1 #!/bin/ksh93 2 # 3 # $Id: idsconfig.sh,v 66c6ff17d001 2013-09-24 00:35:53Z jel+illumos $ 4 # 5 # CDDL HEADER START 6 # 7 # The contents of this file are subject to the terms of the 8 # Common Development and Distribution License (the "License"). 9 # You may not use this file except in compliance with the License. 10 # 11 # You can obtain a copy of the license at usr/src/OPENSOLARIS.LICENSE 12 # or http://www.opensolaris.org/os/licensing. 13 # See the License for the specific language governing permissions 14 # and limitations under the License. 15 # 16 # When distributing Covered Code, include this CDDL HEADER in each 17 # file and include the License file at usr/src/OPENSOLARIS.LICENSE. 18 # If applicable, add the following below this CDDL HEADER, with the 19 # fields enclosed by brackets "[]" replaced with your own identifying 20 # information: Portions Copyright [yyyy] [name of copyright owner] 21 # 22 # CDDL HEADER END 23 # 24 # Copyright (c) 2001, 2010, Oracle and/or its affiliates. All rights reserved. 25 # Portions Copyright 2013 Jens Elkner. 26 27 28 ############################################################################# 29 # Main global vars 30 ############################################################################# 31 LIC='[-?$Id: idsconfig.sh,v 66c6ff17d001 2013-09-24 00:35:53Z jel+illumos $ ] 32 [-copyright?Copyright (c) 2001, 2010, Oracle and/or its affiliates. All rights reserved.] 33 [-copyright?Portions Copyright (c) 2013 Jens Elkner.] 34 [-license?CDDL 1.0]' 35 36 SDIR=${.sh.file%/*} 37 typeset -r FPROG=${.sh.file} 38 typeset -r PROG=${FPROG##*/} 39 40 typeset -T LogObj_t=( 41 typeset -Sh 'Color for info messages' GREEN='38;5;232;48;5;118' #'1;30;102'; 42 typeset -Sh 'Color for warning messages' BLUE='38;5;21;48;5;118' #'1;34;102'; 43 typeset -Sh 'Color for fatal messages' RED='38;5;9;48;5;118' #'1;31;102'; 44 typeset -Sih 'Flag, whether to use colored labels' COLORED=1 45 typeset -Sih 'Flag, whether to print verbose infos' VERBOSE=0 46 47 function log { 48 (( _.COLORED )) && print -u2 "\E[1;$2m${ date +%T; } $1:\E[0m $3" || \ 49 print -u2 "${ date +%T; } ${1}: $3" 50 } 51 typeset -Sfh ' log a message to stderr' log 52 function verbose { 53 (( _.VERBOSE )) && _.log "VERB" ${_.GREEN} " $*" 54 } 55 typeset -Sfh ' log a info message to stderr if verbose messages are enabled' info 56 57 function info { 58 _.log "INFO" ${_.GREEN} "$*" 59 } 60 typeset -Sfh ' log a info message to stderr' info 61 function warn { 62 _.log "WARN" ${_.BLUE} "$*" 63 } 64 typeset -Sfh ' log a warning message to stderr' warn 65 function fatal { 66 _.log "FATAL" ${_.RED} "$*" 67 } 68 typeset -Sfh ' log a fatal error message to stderr' fatal 69 function printMarker { 70 typeset COLOR="$1" L='----------------------------------------------------------------------------' 71 (( _.COLORED )) && print "\E[1;${COLOR:-${_.GREEN}}m${L}\E[0m" || \ 72 print "${L}" 73 } 74 typeset -Sfh ' print a marker line to stdout' printMarker 75 ) 76 LogObj_t Log 77 78 typeset -T ManObj_t=( 79 typeset -Ah 'Variable descriptions. Key: variable name' VAR 80 typeset -Ah 'Function usages. Key: function name' FUNC 81 function addVar { 82 [[ -n ${_.VAR[$1]} ]] && Log.warn "Overwriting previous description for $1" 83 _.VAR[$1]="[+$1?$2]" 84 } 85 typeset -fh 'Add a description (arg2) for the given variable name (arg1)' addVar 86 function addFunc { 87 typeset fname=$1 88 typeset X="$2" 89 [[ -n ${_.FUNC[$fname]} ]] && Log.warn "Overwriting previous usage info for $1()" 90 [[ -z $X ]] && X="$LIC" 91 shift 2 92 while [[ -n $1 ]]; do 93 X+="$1" 94 shift 95 done 96 _.FUNC[$fname]+="$X" 97 } 98 typeset -fh $'Add usage info (arg3 ...) for the given function name (arg1). If implementation details (arg2) is empty, the value of \a$LIC\a gets used instead' addFunc 99 function varUsage { 100 typeset X="" 101 [[ "$1" == '@' || "$1" == '*' ]] && set -- ${!_.VAR[@]} 102 while [[ -n $1 ]]; do 103 X+="${_.VAR[$1]}" 104 shift 105 done 106 print "$X" 107 } 108 typeset -fh $'Get the variable usage info for the named variables (arg2 ...) if available as a concatenated string. See \baddVar()\b' varUsage 109 function funcUsage { 110 printf "${_.FUNC[$1]}" 111 } 112 typeset -fh 'Get the function usage info for the named function (arg1)' funcUsage 113 114 function listVars { 115 typeset ALL="${ print ${!_.VAR[*]} | tr ' ' '\n' | sort -u; }" VNAME 116 for VNAME in $ALL; do 117 [[ $VNAME == OLDENV || $VNAME == LASTENV ]] && continue 118 typeset -n X=$VNAME 119 print "$VNAME=$X" 120 done 121 unset -n X 122 } 123 typeset -fh 'List all registered environment variables and its current value, except for OLDENV and LASTENV.' listVars 124 ) 125 ManObj_t Man 126 127 Man.addFunc showUsage '' '[+NAME?showUsage - show usage info.] 128 [+DESCRIPTION?Shows usage info for the given \afname\a if available.] 129 \n\n\afname\a 130 ' 131 function showUsage { 132 typeset WHAT="$2" 133 if (( Log.VERBOSE )) && [[ ${WHAT} == 'MAIN' ]]; then 134 typeset PRE=${Man.FUNC[$WHAT]%%\[+NOTES*} 135 typeset POST=${.sh.match} 136 typeset E='[+ENVIRONMENT VARIABLES]{'"${ Man.varUsage '*' ; }"'}' 137 getopts -a "${PROG}" "${ print ${PRE}${E}${POST} ; }" OPT --man 138 else 139 getopts -a "${PROG}" "${ print ${Man.FUNC[$WHAT]}; }" OPT --man 140 fi 141 } 142 143 Man.addVar PROG 'String. The basename of this script.' 144 145 Man.addVar INT 'Associative array of Integers. Config relevant int values and flags.' 146 typeset -A -i INT=( ) 147 Man.addVar STR 'Associative array of Strings. Config relevant string values.' 148 typeset -A STR=( ) 149 150 Man.addVar TMPF 'Associative array of Integers. Int values with a more or less a global character in this script.' 151 typeset -A -i TMPF=( ) 152 Man.addVar TMP 'Associative array of Strings. String values whith a more or less a global character in this script.' 153 typeset -A TMP=( ) 154 155 Man.addVar CON_ARGS 'String. Contains connection related options for ldap commands.' 156 typeset CON_ARGS='' 157 158 Man.addVar AUTH_ARGS 'Indexed array of Strings. Contains authentification related options for ldap commands.' 159 typeset -a AUTH_ARGS=( ) 160 161 Man.addVar SSD 'Indexed array of Strings. Contains the Service Search Decsriptors to use.' 162 typeset -a SSD=( ) 163 164 # NOTE: The ACI names are retained to be compatible to previous releases. If one 165 # installs a completely new LDAP infrastructure changing the names should 166 # not be a problem as long as they do not collide with existing aci names! 167 168 Man.addVar SFX_ANYONE_ACI_NAME 'String constant. The name of the suffix ACI rule, which allows anyone to read, search, compare.' 169 typeset -r SFX_ANYONE_ACI_NAME='Anonymous access' 170 171 Man.addVar SFX_SELF_ACI_NAME 'String constant. The name of the suffix ACI rule, which allows a principal to modify its own attributes except those related to password state information and policy.' 172 typeset -r SFX_SELF_ACI_NAME='Limited self entry modification' 173 174 Man.addVar SFX_ADMIN_ACI_NAME 'String constant. The name of the suffix ACI rule, which allows the directory configuration administrator all operations on any entry.' 175 typeset -r SFX_ADMINGRP_ACI_NAME='Configuration Administrator access' 176 177 Man.addVar SFX_ADMINGRP_ACI_NAME 'String constant. The name of the suffix ACI rule, which allows the directory configuration administrator group all operations on any entry.' 178 typeset -r SFX_ADMIN_ACI_NAME='Configuration Administrator Group access' 179 180 Man.addVar PROXY_ACI_NAME 'String constant. The name of the ACI rule, which allows proxies to read passwords.' 181 typeset -r PROXY_ACI_NAME='LDAP Naming Services proxy_password_read' 182 183 Man.addVar HOST_ACI_NAME 'String constant. The name of the ACI rule, which allows host principals to write passwords.' 184 typeset -r HOST_ACI_NAME='LDAP Naming Services host_shadow_write' 185 Man.addVar NON_HOST_ACI_NAME 'String constant. The name of the ACI rule, which denies non-host principals access to shadow data.' 186 typeset -r NON_HOST_ACI_NAME='LDAP Naming Services deny_non_host_shadow_access' 187 188 Man.addVar ADMIN_ACI_NAME 'String constant. The name of the ACI rule, which allows Admin principals access to shadow, rbac and mount data.' 189 typeset -r ADMIN_ACI_NAME='LDAP Naming Services admin_shadow_write' 190 Man.addVar NON_ADMIN_ACI_NAME 'String constant. The name of the ACI rule, which denies non-Admin principals access to shadow data.' 191 typeset -r NON_ADMIN_ACI_NAME='LDAP Naming Services deny_non_admin_shadow_access' 192 Man.addVar USER_ACI_NAME 'String constant. The name of the ACI rule, which prevents a user from changing administrative fields like uidNumber, homeDirectory, shadowExpire, etc..' 193 typeset -r USER_ACI_NAME='LDAP Naming Services deny_write_access' 194 195 Man.addVar VLV_ACI_NAME 'String constant. The name of the global ACI rule, which allows any user to use VLVs for read, search and compare.' 196 typeset -r VLV_ACI_NAME='VLV Request Control' 197 198 Man.addVar SELF_GSS_ACI_NAME 'String constant. The name of the ou=people ACI rule, which allows a via SASL/GSSAPI authenticated user read and search on passwords.' 199 typeset -r SELF_GSS_ACI_NAME='self-read-pwd' 200 201 Man.addVar HOST_GSS_ACI_NAME 'String constant. The name of the ou=people ACI rule, which allows a via SASL/GSSAPI authenticated hosts read and search on passwords.' 202 typeset -r HOST_GSS_ACI_NAME='host-read-pwd' 203 204 Man.addVar AUTH_METHODS 'Indexed array of Strings. A list of currently supported authentication methods.' 205 typeset -a -r AUTH_METHODS=( 206 'none' 'simple' 'sasl/DIGEST-MD5' 207 'tls:simple' 'tls:sasl/DIGEST-MD5' 'sasl/GSSAPI' 208 ) # order is important 209 210 Man.addVar CRED_LEVELS 'Indexed array of Strings. A list of currently supported client credential levels.' 211 typeset -a -r CRED_LEVELS=( 'anonymous' 'proxy' 'proxy anonymous' 'self' ) 212 213 Man.addVar OID2ADEF 'Associative array of Strings. Key: OID, Value: attribute definition. Used to cache attribute definitions of schemas currently installed on the DS. Gets lazy initialized, i.e. populated when needed.' 214 typeset -A OID2ADEF=( ) 215 Man.addVar ANAME2OID 'Associative array of Strings. Key: attribute name (lowercase), Value: OID. Used to cache name to OID mappings. Gets initialized, when \bOID2ADEF\b gets populated.' 216 typeset -A ANAME2OID=( ) 217 218 Man.addVar OID2ODEF 'Associative array of Strings. Key: OID, Value: objectclass definition. Used to cache objectclass definitions of schemas currently installed on the DS. Gets lazy initialized, i.e. populated when needed.' 219 typeset -A OID2ODEF=( ) 220 221 Man.addFunc checkBinary '' '[+NAME?checkBinary - lookup a command.] 222 [+DESCRIPTION?Check the standard path, whether it contains \acmd\a and if not, use \aPATH\a to lookup the command. If found the full path of the command gets printed to stdout, otherwise an error message to stderr.] 223 [+RETURN VALUES]{ 224 [+0?\acmd\a found.] 225 [+1?\acmd\a not found.] 226 } 227 [+SEE ALSO?\bwhence\b(1).] 228 \n\n\acmd\a 229 ' 230 function checkBinary { 231 typeset CMD=$1 X='' 232 for DIR in /usr/bin /usr/sbin ; do 233 [[ -x ${DIR}/${CMD} ]] && print ${DIR}/${CMD} && return 0 234 done 235 X=${ whence -fp ${CMD} ; } 236 [[ -n $X ]] && print "${X}" && return 0 237 X="${.sh.file%/*}/${CMD}" 238 [[ -x ${X} ]] && print "$X" && return 0 239 print -u2 "Missing command '${CMD}'." 240 return 1 241 } 242 243 Man.addFunc init '' '[+NAME?init - initializes variables and options.] 244 [+DESCRIPTION?Does some basic binary-avail checks and initializes certain values of \bSTR[]]\b, \bINT[]]\b and \bTMP[]]\b.] 245 ' 246 function init { 247 typeset X='' KEY VAL TAIL 248 integer ERR=0 249 250 # General commands 251 HOST=${ hostname ; } 252 PING=${ checkBinary ping || (( ERR++ )) ; } 253 STTY=${ checkBinary stty || (( ERR++ )) ; } 254 GETENT=${ checkBinary getent || (( ERR++ )) ; } 255 256 # LDAP COMMANDS 257 LDAPSEARCH=${ checkBinary ldapsearch || (( ERR++ )) ; } 258 LDAPMODIFY=${ checkBinary ldapmodify || (( ERR++ )) ; } 259 LDAPDELETE=${ checkBinary ldapdelete || (( ERR++ )) ; } 260 LDAPCLIENT=${ checkBinary ldapclient || (( ERR++ )) ; } 261 262 if (( ERR )); then 263 Log.fatal 'Please adjust your PATH and try again' 264 return 66 265 fi 266 LDAPSEARCH+=' -r' 267 268 # If DNS domain (resolv.conf) exists use that, otherwise use domainname. 269 if [[ -f /etc/resolv.conf ]]; then 270 while read KEY VAL TAIL ; do 271 if [[ ${KEY} == 'domain' || ${KEY} == 'search' ]]; then 272 X=${VAL} 273 break; 274 fi 275 done < /etc/resolv.conf 276 fi 277 278 # If for any reason the DOMAIN did not get set (error'd resolv.conf) set 279 # X to the domainname or getent command's output. 280 [[ -z $X ]] && X=${ domainname ; } 281 if [[ -z $X ]]; then 282 typeset -a AX=( ${ ${GETENT} hosts ${HOST} 2>/dev/null ; } ) 283 integer I 284 for (( I=${#AX[@]}-1; I > 0; I++ )); do 285 X=${AX[$I]#*.} 286 [[ -n $X ]] && break 287 done 288 fi 289 290 # Actually one needs to init non-null/zero values, only. However, we want 291 # to have the keys registered ... 292 293 # idsconfig specific variables. 294 INT[LDAP_ENABLE_SHADOW_UPDATE]=0 295 INT[NEED_PROXY]=0 # 0 = No Proxy, 1 = Create Proxy. 296 INT[NEED_ADMIN]=0 # 0 = No Admin, 1 = Create Admin. 297 INT[NEED_HOSTACL]=0 # 0 = No Host ACL, 1 = Create Host ACL. 298 INT[EXISTING_PROFILE]=0 299 (( INT[TASK_TIMEOUT] < 10 )) && INT[TASK_TIMEOUT]=60 300 STR[LDAP_PROXYAGENT]='' 301 STR[LDAP_PROXYAGENT_CRED]='' 302 STR[LDAP_ADMINDN]='' 303 STR[LDAP_ADMIN_CRED]='' # enableShadowUpdate flag and Admin credential 304 STR[DS_DB]='' # storage backend name 305 STR[LDAP_SUFFIX]='' # suffix within the backend 306 STR[LDAP_DOMAIN]=${X%%.} # domainname on Server (default value) 307 308 # DS specific information 309 STR[DS_HOST]='' 310 INT[DS_PORT]=389 311 INT[NEED_TIME]=0 312 INT[NEED_SIZE]=0 313 INT[DS_TIMELIMIT]=0 314 INT[DS_SIZELIMIT]=0 315 316 # LDAP PROFILE related defaults 317 STR[LDAP_ROOTDN]='cn=Directory Manager' # Provide common default. 318 STR[LDAP_ROOTPWD]='' # NULL passwd (is invalid) 319 STR[LDAP_PROFILE_NAME]='default' 320 STR[LDAP_BASEDN]='' 321 STR[LDAP_SERVER_LIST]='' 322 STR[LDAP_AUTHMETHOD]='' 323 INT[LDAP_FOLLOWREF]=0 324 INT[NEED_CRYPT]=0 325 INT[NEED_SRVAUTH_PAM]=0 326 INT[NEED_SRVAUTH_KEY]=0 327 INT[NEED_SRVAUTH_CMD]=0 328 (( ! INT[NEED_CRYPT_IMPORT] )) && INT[NEED_CRYPT_IMPORT]=0 329 STR[LDAP_SEARCH_SCOPE]='one' 330 STR[LDAP_SRV_AUTHMETHOD_PAM]='' 331 STR[LDAP_SRV_AUTHMETHOD_KEY]='' 332 STR[LDAP_SRV_AUTHMETHOD_CMD]='' 333 INT[LDAP_SEARCH_TIME_LIMIT]=30 334 STR[LDAP_PREF_SRVLIST]='' 335 INT[LDAP_PROFILE_TTL]=43200 336 STR[LDAP_CRED_LEVEL]='proxy' 337 INT[LDAP_BIND_LIMIT]=10 338 339 # Service Search Descriptors (just make sure it is empty) 340 SSD=( ); unset SSD[0] # unset is required! 341 342 # GSSAPI setup 343 INT[GSSAPI_ENABLE]=0 344 STR[LDAP_KRB_REALM]='' 345 346 # temp settings 347 TMPF[STEP]=0 # internal file counter - see nextFile() 348 TMPF[IS_OPENDJ]=0 # set if detected DS is OpenDS/OpenDJ 349 TMPF[DEL_OLD_PROFILE]=0 # 0 = don't, 1 = delete old profile 350 TMPF[STEP]=1 # simple progress indicator 351 TMPF[GSSAPI_AUTH_MAY_BE_USED]=0 # DS GSSAPI SASL support 352 TMPF[NEED_CREATE_SUFFIX]=0 # see add_suffix() 353 TMPF[NEED_CREATE_BACKEND]=0 # see add_suffix() 354 # schema completion syntax to use: 355 # 0 .. detect, 1 .. force OpenDJ, 2 .. force DSEE 356 (( TMPF[SYNTAX] < 0 || TMPF[SYNTAX] > 2 )) && TMPF[SYNTAX]=0 357 358 TMP[FILE]='' # tmp output filename - see nextFile() 359 TMP[LDAP_ROOTPWF]=${TMP[DIR]}/rootPWD # filename containing the root PW 360 TMP[WARN]='' # set if possibly incompatible DS 361 TMP[SUFFIX_OBJ]='' # suffix objectclass (long name) 362 TMP[SUFFIX_ATT]='' # suffix RDN attr name 363 TMP[SUFFIX_VAL]='' # suffix RDN attr value 364 TMP[DS_DBS_AVAIL]='' # next available suffix DBs 365 TMP[VLV_CMDS]='' # where VLV commands to exec are stored 366 # Set via getopts - just make sure, they are registered 367 [[ -z ${TMP[IN]} ]] && TMP[IN]='' # the config file to read 368 [[ -z ${TMP[OUT]} ]] && TMP[OUT]='' # the config file to write 369 # redirect ldap output to file instead of stdout 370 if (( TMPF[FD] > 2 )); then 371 exec 4>${TMP[DIR]}/ldap.out 372 fi 373 return 0 374 } 375 376 # internal 377 function showProgress { 378 typeset NUM=${ printf "%3d" TMPF[STEP] ; } 379 Log.info "${NUM}. " "$*" 380 (( TMPF[STEP]++ )) 381 } 382 383 # internal 384 function nextFile { 385 typeset CMD="$1" FN="${2// /_}" EXT='cmd' 386 (( TMPF[STEP]++ )) 387 case "$1" in 388 add|modify|delete) CMD="ldap$1" EXT='ldif' ;; 389 sh) EXT='sh' 390 esac 391 TMP[FILE]=${ printf "${TMP[DIR]}/%03d_${CMD}_${FN}.${EXT}" ${TMPF[STEP]} ; } 392 [[ -e ${TMP[FILE]} ]] && Log.warn "Overwriting ${TMP[FILE]} ..." 393 } 394 395 Man.addFunc show_vars '' '[+NAME?show_vars - List of all config variables and their values.] 396 [+DESCRIPTION?Lists all config variables and their current values in a more or less human readable manner.] 397 [+ENVIRONMENT VARIABLES]{' "${ Man.varUsage INT STR SSD ; }" '} 398 ' 399 function show_vars { 400 (( ! VERBOSE )) && return 401 402 typeset S=${!STR[@]} OUT='Current non-NULL values:\n' 403 S=${ print ${S// /$'\n'} | sort ; } 404 for KEY in $S ; do 405 [[ -n ${STR[$KEY]} ]] && \ 406 OUT+=${ printf "%32s = '%s'" ${KEY} "${STR[$KEY]}" ; }'\n' 407 done 408 S=${!INT[@]} 409 S=${ print ${S// /$'\n'} | sort ; } 410 for KEY in $S ; do 411 (( INT[$KEY] )) && \ 412 OUT+=${ printf "%32s = %d" ${KEY} "${INT[$KEY]}" ; }'\n' 413 done 414 if (( ${#SSD[@]} )); then 415 OUT+=${ printf "%32s = (" 'SSD' ; } 416 for S in "${SSD[@]}" ; do 417 OUT+="\t\t${S}\n" 418 done 419 OUT+='\t)\n' 420 fi 421 print -u2 "${OUT}" 422 } 423 424 Man.addFunc save_password '' '[+NAME?save_password - Save password to temporary file.] 425 [+DESCRIPTION?Save the current password from \bSTR[LDAP_ROOTPWD]]\b to the file \bTMP[LDAP_ROOTPWF]]\b.] 426 ' 427 function save_password { 428 print "${STR[LDAP_ROOTPWD]}" >"${TMP[LDAP_ROOTPWF]}" 429 } 430 ############################################################################# 431 # EOG 432 ############################################################################# 433 434 435 ############################################################################# 436 # config file stuff 437 ############################################################################# 438 Man.addFunc create_config_file '' '[+NAME?create_config_file - Write config data to the specified file.] 439 [+DESCRIPTION?Write config data to \afile\a.] 440 [+ENVIRONMENT VARIABLES]{' "${ Man.varUsage INT STR SSD ; }" '} 441 [+RETURN VALUES]{ 442 [+0?file was written successfully.] 443 [+>= 1?otherwise.] 444 } 445 [+SEE ALSO?\bload_config_file()\b.] 446 \n\n\afile\a 447 ' 448 function create_config_file { 449 typeset OUT="$1" KEY 450 [[ -z ${OUT} ]] && return 1 451 452 # Create output file. 453 ( 454 print ' 455 # '"${OUT}"' - This file contains configuration information for 456 # LDAP naming services. Use the '"${PROG}"' tool to load it. 457 # 458 # WARNING: This file was generated by '"${PROG}"', and is intended to 459 # be loaded by '"${PROG}"' as is. DO NOT EDIT THIS FILE! 460 ' 461 print 'typeset -A -i IIN=( )' 462 for KEY in "${!INT[@]}" ; do 463 print "IIN[${KEY}]=${INT[${KEY}]}" 464 done 465 print '\ntypeset -A SIN=( )' 466 for KEY in "${!STR[@]}" ; do 467 printf "SIN[${KEY}]=%q\n" "${STR[${KEY}]}" 468 done 469 print '\ntypeset -a SIN_SSD=( )' 470 for KEY in "${SSD[@]}" ; do 471 [[ -n ${KEY} ]] && printf "SIN_SSD+=( %q )\n" "${KEY}" 472 done 473 print "\n# End of ${OUT}" 474 ) >"${OUT}" 475 } 476 477 # for backward compatibilty, but not documented: old format is bogus and should 478 # not be used anymore. 479 function source_old_config { 480 typeset IN="$1" TODO='' VNAME KEY VAL LINE 481 typeset -A OLDMAP=( 482 [DS_HOST]=IDS_SERVER 483 [DS_PORT]=IDS_PORT 484 [DS_DB]=IDS_DATABASE 485 [DS_TIMELIMIT]=IDS_TIMELIMIT 486 [DS_SIZELIMIT]=IDS_SIZELIMIT 487 [SSD]=LDAP_SERV_SRCH_DES 488 ) 489 490 for KEY in "${!INT[@]}" ; do 491 VNAME=${OLDMAP[${KEY}]} 492 [[ -z ${VNAME} ]] && VNAME=${KEY} 493 typeset -n VAR=${VNAME} 494 INT[${KEY}]=${VAR} 495 TODO+="${VNAME} " 496 done 497 for KEY in "${!STR[@]}" ; do 498 VNAME=${OLDMAP[${KEY}]} 499 [[ -z ${VNAME} ]] && VNAME=${KEY} 500 typeset -n VAR=${VNAME} 501 STR[${KEY}]="${VAR}" 502 TODO+="${VNAME} " 503 done 504 while read LINE; do 505 if [[ ${LINE:0:19} == 'LDAP_SERV_SRCH_DES=' ]]; then 506 VAL="${LINE:19}" 507 [[ -n ${VAL} ]] && SSD+=( "${VAL}" ) 508 fi 509 done < "${IN}" 510 511 # cleanup "global" namespace 512 [[ -n ${TODO} ]] && eval "unset ${TODO}" 513 } 514 515 Man.addFunc load_config_file '' '[+NAME?load_config_file - load an '"${PROG}"' config file.] 516 [+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.] 517 [+ENVIRONMENT VARIABLES]{' "${ Man.varUsage INT STR SSD ; }" '} 518 [+RETURN VALUES]{ 519 [+0?on success.] 520 [+>= 66? \afile\a is not a file or unreadable.] 521 } 522 [+SEE ALSO?\bsave_password()\b, \bshow_vars()\b.] 523 \n\n\afile\a 524 ' 525 function load_config_file { 526 typeset IN="$1" KEY VAL 527 integer I 528 [[ ! -f ${IN} ]] && Log.fatal "${IN} is not a file" && return 66 529 [[ ! -r ${IN} ]] && Log.fatal "${IN} is not readable" && return 67 530 531 # source in the stuff - not nice but less work ;-) 532 . "${IN}" 533 534 KEY=${ typeset -p IIN ; } 535 if [[ -z ${KEY} ]]; then 536 source_old_config "${IN}" || return 68 537 else 538 for KEY in "${!INT[@]}" ; do 539 INT[${KEY}]=${IIN[${KEY}]} 540 done 541 for KEY in "${!STR[@]}" ; do 542 STR[${KEY}]="${SIN[${KEY}]}" 543 done 544 for KEY in "${SIN_SSD[@]}" ; do 545 [[ -n ${KEY} ]] && SSD+=( "${KEY}" ) 546 done 547 # cleanup "global" namespace 548 unset IIN SIN SIN_SSD 549 fi 550 551 TMPF[DEL_OLD_PROFILE]=1 552 save_password 553 show_vars 554 555 return 0 556 } 557 ############################################################################# 558 # End Of config file stuff 559 ############################################################################# 560 561 562 ############################################################################ 563 # Basic [menu] stuff 564 ############################################################################ 565 Man.addFunc display_msg '' '[+NAME?display_msg - display a message] 566 [+DESCRIPTION?Display a message corresponding to the \atag\a passed in.] 567 [+ENVIRONMENT VARIABLES]{'"${ Man.varUsage INT STR TMP ; }" '} 568 \n\n\atag\a 569 ' 570 function display_msg { 571 typeset X 572 integer I 573 574 case "$1" in 575 backup_server) 576 print ' 577 It is strongly recommended that you BACKUP the directory server 578 before running '"${PROG}"'. 579 580 Hit Ctrl-C at any time before the final confirmation to exit.\n' 581 ;; 582 cred_level_menu) 583 print 'The following are the supported credential levels:' 584 for (( I=0; I < ${#CRED_LEVELS[@]}; I++ )); do 585 printf ' %2d %s\n' $((I+1)) "${CRED_LEVELS[I]}" 586 done 587 ;; 588 auth_method_menu) 589 print 'The following are the supported Client Authentication Methods:' 590 for (( I=0; I < ${#AUTH_METHODS[@]}; I++ )); do 591 printf ' %-2d %s\n' $((I+1)) "${AUTH_METHODS[I]}" 592 done 593 ;; 594 srvauth_method_menu) 595 print 'The following are the supported Service Authentication Methods:' 596 # skip 'none' 597 for (( I=1; I < ${#AUTH_METHODS[@]}; I++ )); do 598 printf ' %-2d %s\n' $((I)) "${AUTH_METHODS[I]}" 599 done 600 ;; 601 prompt_ssd_menu) 602 print ' A Add a Service Search Descriptor (SSD) 603 D Delete a SSD 604 M Modify a SSD 605 P Display all SSDs 606 H Help 607 X Clear all SSDs 608 609 Q Exit menu' 610 ;; 611 summary_menu) 612 typeset SUFFIX_INFO='' 613 typeset DB_INFO='' 614 615 if (( TMPF[NEED_CREATE_SUFFIX] )); then 616 SUFFIX_INFO=' 617 Suffix to create : '"${STR[LDAP_SUFFIX]}"'\n' 618 (( TMPF[NEED_CREATE_BACKEND] )) && DB_INFO=' 619 Database to create : '"${STR[DS_DB]}"'\n' 620 fi 621 typeset BASEDN 622 BASEDN="${STR[LDAP_BASEDN]}${STR[SUFFIX_INFO]}${STR[DB_INFO]}" 623 624 print ' Summary of Configuration 625 626 1 Domain to serve : '"${STR[LDAP_DOMAIN]}"' 627 2 Base DN to setup : '"${BASEDN}"' 628 3 Profile name to create : '"${STR[LDAP_PROFILE_NAME]}"' 629 4 Default Server List : '"${STR[LDAP_SERVER_LIST]}"' 630 5 Preferred Server List : '"${STR[LDAP_PREF_SRVLIST]}"' 631 6 Default Search Scope : '"${STR[LDAP_SEARCH_SCOPE]}"' 632 7 Credential Level : '"${STR[LDAP_CRED_LEVEL]}"' 633 8 Authentication Method : '"${STR[LDAP_AUTHMETHOD]}"' 634 9 Enable Follow Referrals : '"${INT[LDAP_FOLLOWREF]}"' 635 10 DS Time Limit : '"${INT[DS_TIMELIMIT]}"' 636 11 DS Size Limit : '"${INT[DS_SIZELIMIT]}"' 637 12 Enable crypt password storage : '"${INT[NEED_CRYPT]}"' 638 13 Service Auth Method pam_ldap : '"${STR[LDAP_SRV_AUTHMETHOD_PAM]}"' 639 14 Service Auth Method keyserv : '"${STR[LDAP_SRV_AUTHMETHOD_KEY]}"' 640 15 Service Auth Method passwd-cmd: '"${STR[LDAP_SRV_AUTHMETHOD_CMD]}"' 641 16 Search Time Limit : '"${INT[LDAP_SEARCH_TIME_LIMIT]}"' 642 17 Profile Time to Live : '"${INT[LDAP_PROFILE_TTL]}"' 643 18 Bind Limit : '"${INT[LDAP_BIND_LIMIT]}"' 644 19 Enable shadow update : '"${INT[LDAP_ENABLE_SHADOW_UPDATE]}"' 645 20 Service Search Descriptors Menu\n' 646 ;; 647 sorry) 648 print ' 649 HELP - No help is available for this topic.\n' 650 ;; 651 create_suffix_help) 652 print ' 653 HELP - Our Base DN is "'"${STR[LDAP_BASEDN]}"'" 654 and we need to create a Directory Suffix, 655 which can be equal to Base DN itself or be any of Base DN parents. 656 All intermediate entries up to suffix will be created on demand.\n' 657 ;; 658 enter_ldbm_db_help) 659 print ' 660 HELP - database backend is an internal database for storage of our suffix data. 661 Backend name must be alphanumeric due to Directory Server restriction. 662 ' 663 ;; 664 backup_help) 665 print ' 666 HELP - Since '"${PROG}"' modifies the directory server configuration, 667 it is strongly recommended that you backup the server prior 668 to running this utility. This is especially true if the server 669 being configured is a production server.\n' 670 ;; 671 port_help) 672 print ' 673 HELP - Enter the port number the directory server is configured to 674 use for LDAP.\n' 675 ;; 676 adminport_help) 677 print ' 678 HELP - Enter the Admin port number the directory server is configured to 679 use for for DS administration commands.\n' 680 ;; 681 domain_help) 682 print ' 683 HELP - This is the DNS domain name this server will be serving. You 684 must provide this name even if the server is not going to be populated 685 with hostnames. Any unqualified hostname stored in the directory 686 will be fully qualified using this DNS domain name.\n' 687 ;; 688 basedn_help) 689 print ' 690 HELP - This parameter defines the default location in the directory tree for 691 the naming services entries. You can override this default by using 692 Service Search Descriptors (SSD). You will be given the option to set up 693 an SSD later on in the setup.\n' 694 ;; 695 profile_help) 696 print ' 697 HELP - Name of the configuration profile with which the clients will be 698 configured. A directory server can store various profiles for multiple 699 groups of clients. The initialization tool, (ldapclient(1M)), assumes 700 "default" unless another is specified.\n' 701 ;; 702 def_srvlist_help) 703 print ' 704 HELP - Provide a list of directory servers to serve clients using this profile. 705 All these servers should contain consistent data and provide similar 706 functionality. This list is not ordered, and clients might change the 707 order given in this list. Note that this is a space separated list of 708 *IP addresses* (not host names). Providing port numbers is optional.\n' 709 ;; 710 pref_srvlist_help) 711 print ' 712 HELP - Provide a list of directory servers to serve this client profile. 713 Unlike the default server list, which is not ordered, the preferred 714 servers must be entered IN THE ORDER you wish to have them contacted. 715 If you do specify a preferred server list, clients will always contact 716 them before attempting to contact any of the servers on the default 717 server list. Note that you must enter the preferred server list as a 718 space-separated list of *IP addresses* (not host names). Providing port 719 numbers is optional.\n' 720 ;; 721 srch_scope_help) 722 print ' 723 HELP - Default search scope to be used for all searches unless they are 724 overwritten using serviceSearchDescriptors. The valid options 725 are "one", which would specify the search will only be performed 726 at the base DN for the given service, or "sub", which would specify 727 the search will be performed through *all* levels below the base DN 728 for the given service. If you use the default DIT layout, "one" should 729 be ok and probably a little bit faster.\n' 730 ;; 731 cred_lvl_help) 732 print ' 733 HELP - This parameter defines what credentials (identity) the clients use to 734 authenticate (bind) to the directory server. This list might contain 735 multiple credential levels and is ordered. If a proxy level is 736 configured, you will also be prompted to enter a bind DN for the 737 proxy agent along with a password. This proxy agent will be created 738 if it does not exist. See also ldapclient(1M).\n' 739 ;; 740 auth_help) 741 print ' 742 HELP - The default authentication (bind) method(s) to be used by all services 743 in the client using this profile. This is a ordered list of 744 authentication methods separated by a ";". The supported methods are 745 provided in a menu. Note that sasl/DIGEST-MD5 binds require passwords 746 to be stored un-encrypted on the server. See also ldapclient(1M).\n' 747 ;; 748 srvauth_help) 749 print ' 750 HELP - The non-default authentication (bind) methods to be used by certain 751 services (for now pam_ldap, keyserv, and passwd-cmd are supported). 752 The authentication methods specified in this attribute overrides 753 the default authentication method defined in the profile. This 754 feature can be used to select stronger authentication methods for 755 services which require increased security.\n' 756 ;; 757 pam_ldap_help) 758 print ' 759 HELP - The authentication (bind) method(s) to be used by pam_ldap when 760 contacting the directory server. This is a ordered list, and, if 761 provided, will override the default authentication method parameter.\n' 762 ;; 763 keyserv_help) 764 print ' 765 HELP - The authentication (bind) method(s) to be used by newkey(1M) and chkey(1) 766 when contacting the directory server. This is a ordered list and if 767 provided will override the default authentication method parameter.\n' 768 ;; 769 passwd-cmd_help) 770 print ' 771 HELP - The authentication (bind) method(s) to be used by passwd(1) command when 772 contacting the directory server. This is a ordered list and if 773 provided will override the default authentication method parameter.\n' 774 ;; 775 referrals_help) 776 print ' 777 HELP - This parameter indicates whether the client should follow 778 ldap referrals if it encounters one during naming lookups.\n' 779 ;; 780 tlim_help) 781 print ' 782 HELP - The server time limit value indicates the maximum amount of time the 783 server would spend on a query from the client before abandoning it. 784 A value of "-1" indicates no limit.\n' 785 ;; 786 slim_help) 787 print ' 788 HELP - The server sizelimit value indicates the maximum number of entries 789 the server would return in respond to a query from the client. A 790 value of "-1" indicates no limit.\n' 791 ;; 792 crypt_help) 793 print ' 794 HELP - By default a DS does not store userPassword attribute values using 795 unix "crypt" formats (like bsdmd5, sunmd5, sha256, sha512, old crypt, 796 etc. - see crypt.conf(4)). If you need to keep your passwords in the 797 crypt formats for backward or pam_unix compatibility, choose "yes". 798 If any other password storage scheme than crypt is used, pam_ldap 799 MUST be used by clients to authenticate users to the system. Note 800 that if you wish to use sasl/*-MD5 in conjunction with pam_ldap, 801 user passwords are/must be stored in the clear text.\n' 802 ;; 803 srchtime_help) 804 print ' 805 HELP - The search time limit the client will enforce for directory lookups.\n' 806 ;; 807 profttl_help) 808 print ' 809 HELP - The time to live value for profile. The client will refresh its 810 cached version of the configuration profile at this TTL interval.\n' 811 ;; 812 bindlim_help) 813 print ' 814 HELP - The time limit for the bind operation to the directory. This 815 value controls the responsiveness of the client in case a server 816 becomes unavailable. The smallest timeout value for a given 817 network architecture/conditions would work best. This is very 818 similar to setting TCP timeout, but only for LDAP bind operation.\n' 819 ;; 820 ssd_help) 821 print ' 822 HELP - Using Service Search Descriptors (SSD), you can override the 823 default configuration for a given service. The SSD can be 824 used to override the default search base DN, the default search 825 scope, and the default search filter to be used for directory 826 lookups. SSD are supported for all services (databases) 827 defined in nsswitch.conf(4). See also ldapclient(1M). 828 829 Note: SSD are powerful tools in defining configuration profiles 830 and provide a great deal of flexibility. However, care 831 must be taken in creating them. If you decide to make use 832 of SSDs, consult the documentation first.\n' 833 ;; 834 ssd_menu_help) 835 print ' 836 HELP - Using this menu SSD can be added, updated, or deleted from 837 the profile. 838 839 A - This option creates a new SSD by prompting for the 840 service name, base DN, and scope. Service name is 841 any valid service as defined in ldap(1). base is 842 either the distinguished name to the container where 843 this service will use, or a relative DN followed 844 by a ",". For more information see ldapclient(1M). 845 D - Delete a previously created SSD. 846 M - Modify a previously created SSD. 847 P - Display a list of all the previously created SSD. 848 X - Delete all of the previously created SSD. 849 850 Q - Exit the menu and continue with the server configuration.\n' 851 ;; 852 enable_shadow_update_help) 853 print ' 854 HELP - Enter "y" to set up the LDAP server for shadow update. 855 The setup will add an administrator identity/credential 856 and modify the necessary access controls for the client 857 to update shadow(4), auth_attr(4), exec_attr(4), prof_attr(4), 858 user_attr(4), and project(4) data on the LDAP server. If 859 sasl/GSSAPI is in use, the Kerberos host principal will be used 860 as the administrator identity. 861 862 Shadow data is used for password aging and account locking. 863 Please refer to the shadow(4) manual page for details.\n' 864 ;; 865 add_admin_cred_help) 866 print ' 867 HELP - Start the setup to add an administrator identity/credential 868 and to modify access controls for the client to update 869 shadow(4), auth_attr(4), exec_attr(4), prof_attr(4), 870 user_attr(4), and project(4) data on the LDAP server. 871 872 Shadow data is used for password aging and account locking. 873 Please refer to the shadow(4) manual page for details.\n' 874 ;; 875 use_host_principal_help) 876 print ' 877 HELP - A profile with a "sasl/GSSAPI" authentication method and a "self" 878 credential level is detected, enter "y" to modify the necessary 879 access controls for allowing the client to update shadow(4) data 880 on the LDAP server. 881 882 Shadow data is used for password aging and account locking. 883 Please refer to the shadow(4) manual page for details.\n' 884 ;; 885 esac 886 } 887 888 Man.addVar ANS 'Variable used to return the user input.' 889 Man.addFunc get_ans '' '[+NAME?get_ans - gets an answer from the user.] 890 [+DESCRIPTION?Read a string from stdin. Arguments:]{ 891 [+prompt?instruction/comment/description/question to show.] 892 [+default?Default value to use if no answer/empty string was read.] 893 } 894 [+ENVIRONMENT VARIABLES]{' "${ Man.varUsage ANS ; }" '} 895 \n\n\aprompt\a [\adefault\a] 896 ' 897 function get_ans { 898 typeset PROMPT="$1 " 899 [[ -n $2 ]] && PROMPT+="[$2] " 900 901 read ANS?"${PROMPT}" 902 [[ -z ${ANS} ]] && ANS="$2" 903 } 904 905 906 Man.addFunc get_ans_req '' '[+NAME?get_ans_req - get a non-empty answer from the user.] 907 [+DESCRIPTION?Read a string from stdin. Does not return until the string is a non-empty string or a \adefault\a value is given. Arguments:]{ 908 [+prompt?instruction/comment/description/question to show.] 909 [+default?Default value to use if no answer/empty string was read.] 910 } 911 [+ENVIRONMENT VARIABLES]{' "${ Man.varUsage ANS ; }" '} 912 [+SEE ALSO?\bget_ans()\b] 913 \n\n\aprompt\a [\adefault\a] 914 ' 915 function get_ans_req { 916 ANS='' 917 while [[ -z ${ANS} ]]; do 918 get_ans "$@" 919 [[ -z ${ANS} ]] && Log.warn 'NULL value not allowed' 920 done 921 } 922 923 Man.addFunc is_numeric '' '[+NAME?is_numeric - check whether arg is a numeric string] 924 [+DESCRIPTION?Check whether the given \astring\a is a numeric string.] 925 [+RETURN VALUES]{ 926 [+0?If the string is a numeric string.] 927 [+1?Otherwise.] 928 } 929 [+SEE ALSO?\bnot_numeric()\b] 930 \n\n\astring\a 931 ' 932 function is_numeric { 933 (( $# != 1 )) && return 1 934 [[ $1 =~ ^[0-9]+$ ]] && return 0 || return 1 935 } 936 937 Man.addFunc not_numeric '' '[+NAME?not_numeric - check whether arg is NOT a numeric string] 938 [+DESCRIPTION?Check whether the given \astring\a is NOT a numeric string. Useful for if and while statements that want to test for non-numeric data.] 939 [+RETURN VALUES]{ 940 [+0?If the string is NOT a numeric string.] 941 [+1?Otherwise.] 942 } 943 [+SEE ALSO?\bis_numeric()\b] 944 \n\n\astring\a 945 ' 946 function not_numeric { 947 is_numeric $1 && return 1 || return 0 948 } 949 950 Man.addVar NUM 'Variable used to return the user input as a number.' 951 integer NUM 952 Man.addFunc get_number '' '[+NAME?get_number - get a number from the user.] 953 [+DESCRIPTION?Read a string from stdin until it represents a number. Arguments:] 954 { 955 [+prompt?instruction/comment/description/question to show.] 956 [+default?Default value to use if no answer/empty string was read.] 957 [+helpTag?Tag of the help message to show, when the string read is h|H|?|help|Help.] 958 } 959 [+ENVIRONMENT VARIABLES]{' "${ Man.varUsage NUM ANS ; }" '} 960 [+SEE ALSO?\bget_ans()\b, \bis_numeric()\b, \bdisplay_msg()\b] 961 \n\n\aprompt\a \adefault\a [\ahelpTag\a] 962 ' 963 function get_number { 964 ANS='' 965 966 get_ans "$1" "$2" 967 968 while not_numeric $ANS ; do 969 case "${ANS}" in 970 [Hh?] | help | Help) display_msg ${3:-sorry} ;; 971 * ) Log.warn "Invalid value: '${ANS}'" ;; 972 esac 973 get_ans 'Enter a numeric value:' "$2" 974 done 975 NUM=${ANS} 976 } 977 978 979 Man.addFunc get_negone_num '' '[+NAME?get_negone_num - get a number >= -1 from the user.] 980 [+DESCRIPTION?Read a string from stdin until it represents a number >= -1. Arguments:] 981 { 982 [+prompt?instruction/comment/description/question to show.] 983 [+default?Default value to use if no answer/empty string was read.] 984 [+helpTag?Tag of the help message to show, when the string read is h|H|?|help|Help.] 985 } 986 [+ENVIRONMENT VARIABLES]{' "${ Man.varUsage NUM ANS ; }" '} 987 [+SEE ALSO?\bget_number()\b] 988 \n\n\aprompt\a \adefault\a [\ahelpTag\a] 989 ' 990 function get_negone_num { 991 while : ; do 992 get_number "$1" "$2" "$3" 993 (( NUM >= -1 )) && break 994 Log.warn 'Invalid number! Enter a number >= -1' 995 done 996 } 997 998 Man.addFunc get_passwd '' '[+NAME?get_passwd - get a password from the user.] 999 [+DESCRIPTION?Read a password from stdin and verify with second until both match. Password gets not echoed to stdout. Arguments:] 1000 { 1001 [+prompt?instruction/comment/description/question to show.] 1002 [+default?Default value to use if no answer/empty string was read.] 1003 } 1004 [+ENVIRONMENT VARIABLES]{' "${ Man.varUsage ANS ; }" '} 1005 [+SEE ALSO?\bget_ans()\b] 1006 \n\n\aprompt\a [\adefault\a] 1007 ' 1008 function get_passwd { 1009 typeset _PASS1='' _PASS2='' 1010 1011 ${STTY} -echo # Turn echo OFF 1012 1013 # continue until passwd and re-entered passwd match 1014 while : ; do 1015 ANS='' 1016 # Don't allow NULL for first try. 1017 while [[ -z ${ANS} ]]; do 1018 get_ans "$@" 1019 [[ -z ${ANS} ]] && \ 1020 print -u2 && Log.warn 'Empty password not allowed' 1021 done 1022 _PASS1="${ANS}" # Store first try. 1023 1024 print 1025 get_ans 'Re-enter password:' 1026 _PASS2="${ANS}" 1027 1028 [[ ${_PASS1} == ${_PASS2} ]] && break 1029 print -u2 && Log.warn "passwords don't match - try again" 1030 done 1031 1032 ${STTY} echo # Turn echo ON 1033 1034 print 1035 } 1036 1037 Man.addFunc get_passwd_nochk '' '[+NAME?get_passwd_nochk - get a password from the user w/o check.] 1038 [+DESCRIPTION?Read a password from stdin. Actually the same as \bget_ans()\b but echoing characters read to stdout is switched off. Arguments:] 1039 { 1040 [+prompt?instruction/comment/description/question to show.] 1041 [+default?Default value to use if no answer/empty string was read.] 1042 } 1043 [+ENVIRONMENT VARIABLES]{' "${ Man.varUsage ANS ; }" '} 1044 [+SEE ALSO?\bget_ans()\b] 1045 \n\n\aprompt\a [\adefault\a] 1046 ' 1047 function get_passwd_nochk { 1048 ${STTY} -echo # Turn echo OFF 1049 get_ans "$@" 1050 ${STTY} echo # Turn echo ON 1051 print 1052 } 1053 1054 Man.addFunc get_menu_choice '' '[+NAME?get_menu_choice - get a valid menu choice number.] 1055 [+DESCRIPTION?Get a menu choice from user. Continue prompting until the choice is in required range. Arguments:] 1056 { 1057 [+prompt?Message text.] 1058 [+min?Min value.] 1059 [+max?Max value.] 1060 [+default?Default value to use if no answer/empty string was read.] 1061 [+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.] 1062 [+menuTag?If given the menu choices for the given menu gest displayed at start and after each help text.] 1063 } 1064 [+RETURN VALUES]{ 1065 [+-1?on error (invalid parameters).] 1066 [+>= \amin\a?the selected value.] 1067 } 1068 [+SEE ALSO?\bis_numeric()\b, \bget_ans()\b, \bdisplay_msg()\b.] 1069 \n\n\aprompt\a \amin\a \amax\a [\adefault\a [\ahelpTag\a]] 1070 ' 1071 function get_menu_choice { 1072 if (( $# < 3 )); then 1073 Log.warn "${.sh.fun}(): Did not get required parameters" 1074 return -1 1075 fi 1076 1077 [[ -n $6 ]] && display_msg "$6" 1078 while : ; do 1079 get_ans "$1" "$4" 1080 if is_numeric ${ANS} && NUM=${ANS} && (( NUM >= $2 )) && (( NUM <= $3 )) 1081 then 1082 return ${NUM} 1083 fi 1084 if [[ -n $5 && ${ANS} == 'h' ]]; then 1085 display_msg "$5" 1086 [[ -n $6 ]] && display_msg "$6" 1087 continue 1088 fi 1089 Log.warn "Invalid choice! Enter a number in the range $2 .. $3" 1090 done 1091 return -1 1092 } 1093 1094 Man.addFunc get_confirm '' '[+NAME?get_confirm - Get confirmation from the user.] 1095 [+DESCRIPTION?Read a string from stdin until it matches Y|y|Yes|yes|YES|N|n|No|no|NO or an empty string. Arguments:] 1096 { 1097 [+prompt?instruction/comment/description/question to show.] 1098 [+default?Default value to use if no answer/empty string was read.] 1099 [+helpTag?Tag of the help message to show, when the string read is h|H|?|help|Help.] 1100 } 1101 [+RETURN VALUES]{ 1102 [+0?for NO] 1103 [+1?for YES] 1104 } 1105 [+SEE ALSO?\bdisplay_msg()\b] 1106 \n\n\aprompt\a \adefault\a [\ahelpTag\a] 1107 ' 1108 function get_confirm { 1109 typeset _ANSWER='' 1110 1111 if [[ -z $2 ]]; then 1112 Log.fatal "INTERNAL ERROR: ${.sh.fun} requires 2 args, 3rd is optional" 1113 exit 2 1114 fi 1115 1116 while : ; do 1117 read _ANSWER?"$1 [$2] " 1118 [[ -z ${_ANSWER} ]] && _ANSWER="$2" 1119 1120 case "${_ANSWER}" in 1121 [Yy] | yes | Yes | YES) return 1 ;; 1122 [Nn] | no | No | NO) return 0 ;; 1123 [Hh?] | help | Help) display_msg ${3:-sorry} ;; 1124 * ) Log.warn 'Please enter y or n' ;; 1125 esac 1126 done 1127 } 1128 1129 Man.addFunc get_confirm_nodef '' '[+NAME?get_confirm_nodef - Get confirmation from the user.] 1130 [+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:] 1131 { 1132 [+prompt?instruction/comment/description/question to show.] 1133 } 1134 [+RETURN VALUES]{ 1135 [+0?for NO] 1136 [+1?for YES] 1137 } 1138 \n\n[\aprompt\a]... 1139 ' 1140 function get_confirm_nodef { 1141 typeset _ANSWER='' 1142 1143 while : ; do 1144 read _ANSWER?"$@ " 1145 case "${_ANSWER}" in 1146 [Yy] | yes | Yes | YES) return 1 ;; 1147 [Nn] | no | No | NO) return 0 ;; 1148 * ) Log.warn 'Please enter y or n' ;; 1149 esac 1150 done 1151 } 1152 ############################################################################ 1153 # End of basic [menu] stuff 1154 ############################################################################ 1155 1156 1157 ###################################################################### 1158 # FUNCTIONS FOR prompt_config_info() START HERE. 1159 ###################################################################### 1160 Man.addFunc get_ids_server '' '[+NAME?get_ids_server - Prompt for DS server name.] 1161 [+DESCRIPTION?Ask the user for the DS hostname, store it into \bSTR[DS_HOST]]\b and adjusts \bCON_ARGS\b.] 1162 [+ENVIRONMENT VARIABLES]{'"${ Man.varUsage CON_ARGS ; }"'} 1163 [+SEE ALSO?\bget_ans()\b, \bget_ids_port()\b, \bping\b(1M).] 1164 ' 1165 function get_ids_server { 1166 typeset SRV="${STR[DS_HOST]}" 1167 typeset OS=${ uname -s ; } 1168 1169 while : ; do 1170 get_ans "Enter the Directory Server's hostname to setup:" \ 1171 "${STR[DS_HOST]}" 1172 SRV="${ANS}" 1173 1174 # Ping server to see if alive. If reachable break out of loop 1175 if [[ ${OS} == 'SunOS' ]]; then 1176 ${PING} "${SRV}" 3 >/dev/null 2>&1 && break 1177 else 1178 # assume Linux 1179 ${PING} -W 3 "${SRV}" >/dev/null 2>&1 && break 1180 fi 1181 Log.warn "Server '${SRV}' is invalid or unreachable" 1182 done 1183 STR[DS_HOST]="${SRV}" 1184 1185 # Set CON_ARGS since values might have changed 1186 CON_ARGS="-h ${SRV} -p ${INT[DS_PORT]}" 1187 } 1188 1189 Man.addFunc get_ids_port '' '[+NAME?get_ids_port - Prompt for DS port number.] 1190 [+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.] 1191 [+ENVIRONMENT VARIABLES]{' "${ Man.varUsage CON_ARGS ; }" '} 1192 [+SEE ALSO?\bget_number()\b, \bget_ids_server()\b, \bldapsearch\b(1).] 1193 \n\n\aarg\a' 1194 function get_ids_port { 1195 typeset ASK='Enter the port number for the directory server (h=help):' 1196 typeset HELP='port_help' KEY='DS_PORT' 1197 1198 integer PORT 1199 while : ; do 1200 # Enter port number 1201 get_number "${ASK}" ${INT[${KEY}]} "${HELP}" 1202 PORT=${ANS} 1203 # Do a simple search to check hostname and LDAP port number 1204 if [[ -z $1 ]]; then 1205 if ${LDAPSEARCH} -h ${STR[DS_HOST]} -p ${PORT} -b '' \ 1206 -s base 'objectclass=*' > /dev/null 2>&1 1207 then 1208 break 1209 fi 1210 Log.warn "Invalid host or port '${STR[DS_HOST]}:${PORT}'" 1211 get_ids_server 1212 else 1213 break 1214 fi 1215 done 1216 INT[${KEY}]=${PORT} 1217 [[ -n $1 ]] && return 1218 1219 # Set CON_ARGS since values might have changed 1220 CON_ARGS="-h ${STR[DS_HOST]} -p ${PORT}" 1221 } 1222 1223 Man.addFunc chk_ids_version '' '[+NAME?chk_ids_version - Read the DS version info.] 1224 [+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.] 1225 [+RETURN VALUES]{ 1226 [+0?on success.] 1227 [+>= 66?on fatal error (got no info).] 1228 } 1229 [+SEE ALSO?\bldapsearch\b(1).] 1230 ' 1231 function chk_ids_version { 1232 typeset PROD='' X LINE 1233 typeset -a SPLIT=( ) 1234 integer MAJOR=0 MINOR=0 1235 ${LDAPSEARCH} ${CON_ARGS} -b cn=monitor -s base 'objectclass=*' \ 1236 2>/dev/null | \ 1237 while read LINE ; do 1238 if [[ ${LINE:0:14} == 'vendorVersion=' ]]; then 1239 # OpenD* 1240 X=${LINE:14} 1241 X=${X//Directory Server } # usually <= 2.3.x 1242 PROD=${X%% *} 1243 SPLIT=( ${.sh.match//./ } ) 1244 MAJOR=${SPLIT[0]} 1245 MINOR=${SPLIT[1]} 1246 break 1247 elif [[ ${LINE:0:8} == 'version=' ]]; then 1248 # *DSEE 1249 X=${LINE:8} 1250 PROD=${X%%/*} 1251 X=${.sh.match#/} 1252 SPLIT=( ${X//./ } ) 1253 MAJOR=${SPLIT[0]} 1254 MINOR=${SPLIT[1]} 1255 break 1256 fi 1257 done 1258 if [[ -z ${PROD} ]] || (( MAJOR == 0 )); then 1259 Log.fatal 'Can not determine the version number of the DS' 1260 return 66 1261 fi 1262 1263 TMP[DS_INFO]="${PROD} ${MAJOR} ${MINOR}" 1264 1265 # for easier maintainance we don't put it into a single expr 1266 X='' 1267 if [[ ${PROD} == 'OpenDS' || ${PROD} == 'OpenDJ' ]]; then 1268 TMPF[IS_OPENDJ]=1 1269 (( MAJOR < 2 )) && X='1' 1270 elif (( MAJOR < 5 || (7 < MAJOR && MAJOR < 11) )); then 1271 # not a supported DSEE version 1272 TMPF[IS_OPENDJ]=0 1273 X='1' 1274 fi 1275 if [[ -n ${X} ]]; then 1276 TMP[WARN]="$PROG only works with DSEE version 5.x, 6.x, 7.x, " 1277 TMP[WARN]+='ODSEE 11g and OpenDS/OpenDJ 2.x' 1278 Log.warn "${TMP[WARN]}" 1279 fi 1280 Log.printMarker 1281 Log.info "Detected DS: ${PROD} ${MAJOR}.${MINOR}" 1282 Log.printMarker 1283 return 0 1284 } 1285 1286 Man.addFunc get_dirmgr_dn '' '[+NAME?get_dirmgr_dn - get the directory manger DN.] 1287 [+DESCRIPTION?Ask the user for the directory manger DN and store it into \bSTR[LDAP_ROOTDN]]\b and adjust \bAUTH_ARGS\b.] 1288 [+ENVIRONMENT VARIABLES]{' "${ Man.varUsage AUTH_ARGS ; }" '} 1289 [+SEE ALSO?\bget_ans()\b.] 1290 ' 1291 function get_dirmgr_dn { 1292 get_ans 'Enter the directory manager DN:' "${STR[LDAP_ROOTDN]}" 1293 STR[LDAP_ROOTDN]="${ANS}" 1294 AUTH_ARGS=( '-D' "${STR[LDAP_ROOTDN]}" '-j' "${TMP[LDAP_ROOTPWF]}" ) 1295 } 1296 1297 Man.addFunc get_dirmgr_pw '' '[+NAME?get_dirmgr_pw - get the Root DN password.] 1298 [+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.] 1299 [+ENVIRONMENT VARIABLES]{' "${ Man.varUsage AUTH_ARGS ; }" '} 1300 [+SEE ALSO?\bget_passwd_nochk()\b, \bsave_password()\b, \bldapsearch\b(1).] 1301 ' 1302 function get_dirmgr_pw { 1303 typeset RES 1304 1305 while : ; do 1306 get_passwd_nochk "Enter passwd for ${STR[LDAP_ROOTDN]} :" 1307 STR[LDAP_ROOTPWD]="${ANS}" # stored for create_config_file(), only 1308 save_password 1309 AUTH_ARGS=( '-D' "${STR[LDAP_ROOTDN]}" '-j' "${TMP[LDAP_ROOTPWF]}" ) 1310 1311 # Verify that ROOTDN and ROOTPWD are valid 1312 RES=${ ${LDAPSEARCH} ${CON_ARGS} "${AUTH_ARGS[@]}" \ 1313 -b '' -s base 'objectclass=*' supportedLDAPVersion 2>&1 ; } 1314 (( $? == 0 )) && break # Both are valid. 1315 1316 if [[ ${RES} =~ (credential|no password) ]]; then 1317 Log.warn 'Password for Root DN is invalid' 1318 else 1319 Log.warn "Root DN '${STR[LDAP_ROOTDN]}' is invalid" 1320 get_dirmgr_dn 1321 fi 1322 done 1323 } 1324 1325 Man.addFunc get_domain '' '[+NAME?get_domain - Ask user for domain to be served.] 1326 [+DESCRIPTION?Ask the user for the Domain that will be served by the LDAP server and store it into \bSTR[LDAP_DOMAIN]]\b.] 1327 [+SEE ALSO?\bget_ans()\b.] 1328 ' 1329 function get_domain { 1330 while : ; do 1331 get_ans 'Enter the domainname to be served (h=help):' \ 1332 "${STR[LDAP_DOMAIN]}" 1333 case "${ANS}" in 1334 [Hh?] | help | Help) display_msg 'domain_help' ; continue ;; 1335 esac 1336 # check, whether it has at least 2 dot separated components 1337 [[ ${ANS} =~ [^.]+\.[^.]+ ]] && break 1338 Log.warn "Invalid domainname '${ANS}'" 1339 done 1340 STR[LDAP_DOMAIN]="${ANS}" 1341 } 1342 1343 Man.addFunc getDSobjectclasses '' '[+NAME?getDSobjectclasses - get all DS schema objectclasses.] 1344 [+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.] 1345 [+RETURN VALUES]{ 1346 [+0?on success.] 1347 [+>0?failed to retrieve attribute definitions.] 1348 } 1349 ' 1350 function getDSobjectclasses { 1351 (( ${#OID2ODEF[@]} )) && return 0 1352 1353 typeset AT OID NAME TAIL 1354 set -o pipefail 1355 ${LDAPSEARCH} ${CON_ARGS} -b cn=schema -s base 'objectclass=*' \ 1356 objectclasses 2>/dev/null | \ 1357 while read AT OID NAME TAIL ; do 1358 [[ ${AT} != 'objectclasses=(' ]] && continue 1359 TAIL=${TAIL%%+( )$')'}; 1360 OID2ODEF["${OID}"]="${TAIL}" 1361 : # always return 0 1362 done 1363 } 1364 1365 Man.addFunc getDSattributes '' '[+NAME?getDSattributes - get all DS schema attributes.] 1366 [+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.] 1367 [+RETURN VALUES]{ 1368 [+0?on success.] 1369 [+>0?failed to retrieve attribute definitions.] 1370 } 1371 ' 1372 function getDSattributes { 1373 (( ${#ANAME2OID[@]} )) && return 0 1374 1375 typeset AT OID NAME TAIL 1376 typeset -l ALIAS 1377 set -o pipefail 1378 ${LDAPSEARCH} ${CON_ARGS} -b cn=schema -s base 'objectclass=*' \ 1379 attributeTypes 2>/dev/null | \ 1380 while read AT OID NAME TAIL ; do 1381 [[ ${AT} != 'attributeTypes=(' ]] && continue 1382 TAIL=${TAIL%%+( )$')'}; 1383 OID2ADEF["${OID}"]="${TAIL}" 1384 # 'name' 1385 if [[ ${TAIL:0:1} == "'" ]]; then 1386 NAME=${TAIL:1} 1387 ALIAS=${NAME%%"'"*} 1388 ANAME2OID["${ALIAS}"]="${OID}" 1389 continue 1390 fi 1391 # ( 'name' 'name 1' ... ) 1392 NAME=${TAIL%%$')'*} 1393 NAME=${NAME##$'('*([[:space:]])} 1394 while [[ ${NAME} == ~(E)"'"([^"'"]+)"'" ]]; do 1395 ALIAS=${.sh.match[1]} 1396 ANAME2OID["${ALIAS}"]="${OID}" 1397 NAME=${NAME:${#ALIAS}+2} 1398 NAME=${NAME#*([[:space:]])} 1399 done 1400 : # always return 0 1401 done 1402 } 1403 1404 Man.addFunc check_attrName '' '[+NAME?check_attrName - validate an attribute name.] 1405 [+DESCRIPTION?Check that \akey\a is a valid attribute name.] 1406 [+RETURN VALUES]{ 1407 [0?key is a valid name.] 1408 [1?key is a invalid name.] 1409 [66?failed to fetch attribute definitions from server.] 1410 } 1411 [+SEE ALSO?\bldapsearch\b(1).] 1412 \n\n\akey\a 1413 ' 1414 function check_attrName { 1415 typeset KEY=$1 1416 1417 if ! getDSattributes ; then 1418 Log.fatal 'Unable to fetch attribute definitions from server' 1419 return 66 1420 fi 1421 [[ -z ${KEY} ]] && return 1 1422 if [[ ${KEY} =~ ^[0-9]+(\.[0-9]+)*$ ]]; then 1423 # OID value 1424 [[ -n ${OID2ADEF[${KEY}]} ]] && return 0 1425 else 1426 # symbol. name 1427 KEY=${ANAME2OID[${KEY}]} 1428 # if have a mapping, than we also have a definition for it - no need 1429 # to double check 1430 [[ -n ${KEY} ]] && return 0 1431 fi 1432 return 1 1433 } 1434 1435 Man.addFunc check_baseDN '' '[+NAME?check_baseDN - check validity of the baseDN name.] 1436 [+DESCRIPTION?Check that \abaseDN\a is a valid base DN.] 1437 [+RETURN VALUES]{ 1438 [+0?baseDN is a valid.] 1439 [+>= 1?baseDN is a invalid.] 1440 } 1441 [+SEE ALSO?\bcheck_attrName()\b.] 1442 [+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.] 1443 \n\n\abaseDN\a 1444 ' 1445 function check_baseDN { 1446 # NOTE: we preserve spaces! And try to report all errors instead of just one 1447 Log.info 'Checking LDAP Base DN ...' 1448 [[ -z $1 ]] && return 0 1449 1450 # NOTE: when fancy debug via trap is enabled, splitting via IFS does not 1451 # work: IFS=',' PAIRS=( $1 ) - so we do it manually 1452 1453 typeset PAIR KEY VAL TAIL="${1}," 1454 typeset -A UNIQ_KEYS=( ) # avoid repeated lookup of the same attrName 1455 integer ERR=0 1456 while [[ -n ${TAIL} ]]; do 1457 TAIL=${TAIL#*,} 1458 PAIR=${.sh.match%,} 1459 VAL=${PAIR#*=} 1460 KEY=${.sh.match%=} 1461 if [[ -z ${KEY} || -z ${VAL} ]]; then 1462 Log.warn "Invalid key=value pair '${PAIR}'" 1463 (( ERR++ )) 1464 continue 1465 fi 1466 UNIQ_KEYS[${KEY}]=1 1467 done 1468 1469 for KEY in "${!UNIQ_KEYS[@]}" ; do 1470 if ! check_attrName "${KEY}" ; then 1471 Log.warn "Unknown attribute name '${KEY}'" 1472 fi 1473 done 1474 return ${ERR} 1475 } 1476 1477 Man.addFunc discover_serv_suffix '' '[+NAME?discover_serv_suffix - query the DS to find suffixes available.] 1478 [+DESCRIPTION?Query the DS to find suffixes available. All suffixes found are stored into the variable \avname\a which must be an indexed array.] 1479 [+RETURN VALUES]{ 1480 [+0?at least one suffix was found.] 1481 [+1?no suffix found.] 1482 } 1483 [+SEE ALSO?\bldapsearch\b(1).] 1484 \n\n\avname\a 1485 ' 1486 function discover_serv_suffix { 1487 typeset LINE 1488 integer NUM_TOP=0 1489 typeset -n DST=$1 1490 DST=( ); unset DST[0] 1491 1492 # Search the server for the TOP of the TREE. Usually none for virgin DS 1493 ${LDAPSEARCH} ${CON_ARGS} -b '' -s base 'objectclass=*' namingContexts \ 1494 2>/dev/null | \ 1495 while read LINE ; do 1496 if [[ -n ${LINE} && ${LINE} != ~(Ei)NetscapeRoot ]]; then 1497 (( NUM_TOP++ )) 1498 DST+=( "${LINE#*=}\n" ) # strip off '^namingContexts=' 1499 fi 1500 done 1501 1502 if (( NUM_TOP == 0 )); then 1503 Log.verbose 'No suffix found in LDAP tree' 1504 return 1 1505 fi 1506 1507 Log.verbose "LDAP_SUFFIX_LIST = ${DST[@]}" 1508 return 0 1509 } 1510 1511 Man.addFunc get_backend '' '[+NAME?get_backend - get the relevant database backend for the Base DN.] 1512 [+DESCRIPTION?Get the relevant database backend name for the Base DN and store it into \bSTR[DS_DB]]\b.] 1513 [+?Prerequisite: \bSTR[LDAP_BASEDN]]\b must be set and valid.] 1514 [+?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.] 1515 [+RETURN VALUES]{ 1516 [+0?on success.] 1517 [+>= 66?an fatal error occured.] 1518 } 1519 [+SEE ALSO?\bldapsearch\b(1).] 1520 ' 1521 function get_backend { 1522 typeset CUR_DN=${STR[LDAP_BASEDN]} PREV_DN='' FILTER 1523 1524 while [[ ${CUR_DN} != ${PREV_DN} ]]; do 1525 typeset -a DB=( ) 1526 Log.verbose "Testing LDAP suffix: ${CUR_DN} ..." 1527 1528 if (( TMPF[IS_OPENDJ] )); then 1529 FILTER='(&(&(objectclass=ds-cfg-backend)(ds-cfg-base-dn='"${CUR_DN}" 1530 FILTER+='))(objectClass=ds-cfg-local-db-backend))' 1531 ${LDAPSEARCH} ${CON_ARGS} "${AUTH_ARGS[@]}" \ 1532 -b 'cn=Backends,cn=config' -s one "${FILTER}" 2>/dev/null | \ 1533 while read LINE ; do 1534 [[ ${LINE:0:18} == 'ds-cfg-backend-id=' && \ 1535 ${LINE: -10} != ',cn=config' ]] && DB+=( "${LINE:18}" ) 1536 done 1537 else 1538 1539 ${LDAPSEARCH} ${CON_ARGS} "${AUTH_ARGS[@]}" \ 1540 -b "cn=${CUR_DN},cn=mapping tree,cn=config" \ 1541 -s base 'nsslapd-state=Backend' 'nsslapd-backend' 2>/dev/null |\ 1542 while read LINE ; do 1543 [[ ${LINE:0:16} == 'nsslapd-backend=' ]] && DB+=( "${LINE:16}" ) 1544 done 1545 fi 1546 1547 if (( ${#DB[@]} == 0 )); then 1548 # not a suffix, or suffix not activated; try next 1549 PREV_DN=${CUR_DN} 1550 CUR_DN="${CUR_DN#*,}" 1551 elif (( ${#DB[@]} == 1 )); then 1552 break 1553 else 1554 Log.fatal "More than one database is configured for '${CUR_DN}'!" \ 1555 "$PROG can not configure suffixes where" \ 1556 'more than one database is used for one suffix' 1557 return 66 1558 fi 1559 done 1560 1561 if (( ${#DB[@]} == 0 )); then 1562 # should not happen, since STR[LDAP_BASEDN] is supposed to be valid 1563 Log.fatal "Could not find a valid backend for '${STR[LDAP_BASEDN]}'" 1564 if (( TMPF[IS_OPENDJ] )); then 1565 Log.fatal 'Check the "Creating a New Database Backend" section in the Administration Guide' 1566 fi 1567 return 67 1568 fi 1569 STR[DS_DB]="${DB[0]}" 1570 1571 Log.verbose "DB backend '${STR[DS_DB]}' selected" 1572 1573 return 0 1574 } 1575 1576 Man.addFunc normalizeDN '' '[+NAME?normalizeDN - normalize a [relative]] distinguished name.] 1577 [+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.] 1578 \n\n\adn\a [\bl\b|\bu\b] 1579 ' 1580 function normalizeDN { 1581 [[ -z $1 ]] && return 1582 typeset VAL=",${1}," 1583 VAL="${VAL//*( ),*( )/,}" 1584 VAL="${VAL//*( )=*( )/=}" 1585 if [[ $2 == 'l' ]]; then 1586 typeset -l LC="${VAL}" 1587 VAL="${LC}" 1588 elif [[ $2 == 'u' ]]; then 1589 typeset -u UC="${VAL}" 1590 VAL="${UC}" 1591 fi 1592 print -- "${VAL:1:${#VAL}-2}" 1593 } 1594 1595 Man.addFunc check_basedn_suffix '' '[+NAME?check_basedn_suffix - check that there is an existing valid suffix to hold current base DN.] 1596 [+DESCRIPTION?Check that there is an existing valid suffix for \bSTR[LDAP_BASEDN]]\b. If one is found, store it into \bSTR[LDAP_SUFFIX]]\b and make a corresponding backend check.] 1597 [+RETURN VALUES]{ 1598 [+0?valid suffix found or new one should be created (\bTMPF[NEED_CREATE_SUFFIX]]\b flag actually indicates that).] 1599 [+>= 66?a fatal error occured.] 1600 } 1601 [+SEE ALSO?\bdiscover_serv_suffix()\b, \bldapsearch\b(1), \bnormalizeDN()\b, \bget_backend()\b.] 1602 ' 1603 function check_basedn_suffix { 1604 Log.info 'Validating LDAP Base DN and Suffix ...' 1605 TMPF[NEED_CREATE_SUFFIX]=0 1606 1607 # check that LDAP Base DN might be added 1608 typeset CUR_DN="${STR[LDAP_BASEDN]}" PREV_DN='' 1609 1610 while [[ ${CUR_DN} != ${PREV_DN} ]]; do 1611 ${LDAPSEARCH} ${CON_ARGS} -b "${CUR_DN}" -s one 'objectclass=*' \ 1612 >/dev/null 2>&1 && break 1613 PREV_DN=${CUR_DN} 1614 CUR_DN="${CUR_DN#*,}" # remove leading component 1615 done 1616 1617 if [[ ${CUR_DN} == ${PREV_DN} ]]; then 1618 Log.info "No valid suffixes were found for Base DN '${STR[LDAP_BASEDN]}'" 1619 TMPF[NEED_CREATE_SUFFIX]=1 1620 return 0 1621 fi 1622 1623 # find out existing suffixes 1624 typeset -a SUFFIXES=( ) 1625 discover_serv_suffix SUFFIXES 1626 1627 # Now looking for relevant suffix for this entry (using lower case - LC). 1628 # STR[LDAP_SUFFIX] will then be used to add necessary base objects via 1629 # add_base_objects(). 1630 typeset LC_DN=,${ normalizeDN "${CUR_DN}" l ; } 1631 typeset SFX LC_SFX 1632 for SFX in "${SUFFIXES[@]}" ; do 1633 Log.verbose "Testing suffix: ${SFX} ..." 1634 # LC_DN ends with ,SFX ? 1635 LC_SFX=,${ normalizeDN "${SFX}" l ; } 1636 if [[ ${LC_DN: -${#LC_SFX}} == ${LC_SFX} ]]; then 1637 STR[LDAP_SUFFIX]="${SFX}" 1638 break 1639 fi 1640 done 1641 1642 if [[ -z ${STR[LDAP_SUFFIX]} ]]; then 1643 # should not happen, since we found the entry 1644 Log.fatal "Could not find a valid suffix for '${STR[LDAP_BASEDN]}'" 1645 return 66 1646 fi 1647 1648 # Getting relevant database (backend) 1649 # DS_DB will then be used to create indexes. 1650 get_backend || return 67 1651 1652 return 0 1653 } 1654 1655 Man.addFunc get_objectclass '' '[+NAME?get_objectclass - get the objectclass for the given attribute name.] 1656 [+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.] 1657 [+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).] 1658 \n\n\aattrName\a 1659 ' 1660 function get_objectclass { 1661 typeset -l ANAME="$1" 1662 1663 case "${ANAME}" in 1664 ou | organizationalunitname | 2.5.4.11) 1665 print 'organizationalUnit' 1666 ;; 1667 dc | domaincomponent | 0.9.2342.19200300.100.1.25) 1668 print 'domain' 1669 ;; 1670 o | organizationname | 2.5.4.10) 1671 print 'organization' 1672 ;; 1673 c | countryname | 2.5.4.6) 1674 print 'country' 1675 ;; 1676 esac 1677 } 1678 1679 function checkSuffixRDN { 1680 typeset RDN="${STR[LDAP_SUFFIX]%%,*}" 1681 TMP[SUFFIX_VAL]="${RDN#*=}" 1682 TMP[SUFFIX_ATT]="${.sh.match%=}" 1683 1684 # find out an objectclass for suffix entry if it is not defined yet 1685 TMP[SUFFIX_OBJ]=${ get_objectclass "${TMP[SUFFIX_ATT]}" ; } 1686 if [[ -z ${TMP[SUFFIX_OBJ]} ]]; then 1687 Log.fatal 'Unable to find an objectclass for' "'${TMP[SUFFIX_ATT]}'" \ 1688 'attribute' 1689 return 1 1690 fi 1691 return 0 1692 } 1693 1694 Man.addFunc prep_create_sfx_entry '' '[+NAME?prep_create_sfx_entry - prepare for the suffix entry creation.] 1695 [+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:]{ 1696 [+TMPF[NEED_CREATE_BACKEND]]?0 .. backend already exists. 1 .. create a backend.] 1697 [+TMP[SUFFIX_ATT]]?The attribute name of the leading RDN of the suffix (ID).] 1698 [+TMP[SUFFIX_VAL]]?The attribute value of the leading RDN of the suffix (ID).] 1699 } 1700 [+RETURN VALUES]{ 1701 [+0?on success.] 1702 [+1?an attribute/consistence related error occured.] 1703 [+>= 66?a fatal error occured.] 1704 } 1705 [+SEE ALSO?\bnormalizeDN()\b, \bdisplay_msg()\b, \badd_suffix()\b, \bldapsearch\b(1)] 1706 ' 1707 function prep_create_sfx_entry { 1708 # check whether suffix corresponds to base dn (i.e. baseDN ends with suffix) 1709 typeset X=,${ normalizeDN "${STR[LDAP_BASEDN]}" l ; } 1710 typeset SFX=,${STR[LDAP_SUFFIX]} 1711 if [[ ${X: -${#SFX}} != ${SFX} ]]; then 1712 Log.warn "Sorry, suffix '${STR[LDAP_SUFFIX]}' is not suitable for" \ 1713 "Base DN '${STR[LDAP_BASEDN]}'" 1714 return 1 1715 fi 1716 1717 checkSuffixRDN || return 66 1718 Log.verbose "Suffix entry object: '${TMP[SUFFIX_OBJ]}'" 1719 1720 TMPF[NEED_CREATE_BACKEND]=0 1721 1722 if (( TMPF[IS_OPENDJ] )); then 1723 # 1st just check, whether any backend matches the suffix 1724 typeset FILTER='(&(objectClass=ds-cfg-local-db-backend)' 1725 FILTER+="(|(ds-cfg-base-dn=${STR[LDAP_SUFFIX]})" 1726 FILTER+="(ds-cfg-base-dn=*,${STR[LDAP_SUFFIX]})))" 1727 X=${ ${LDAPSEARCH} ${CON_ARGS} "${AUTH_ARGS[@]}" \ 1728 -b 'cn=Backends,cn=config' -s one "${FILTER}" 2>/dev/null ; } 1729 if [[ -n ${X} ]]; then 1730 Log.verbose 'Suffix backend already exists' 1731 get_backend || return 66 1732 return 0 1733 fi 1734 else 1735 # DSEE: check the suffix mapping tree ... 1736 # If mapping exists, suffix should work, otherwise DSEE inconsistent 1737 # NOTE: -b 'cn=mapping tree,cn=config' -s one 'cn=\"$1\"' won't work 1738 # in case of 'cn' value in LDAP is not quoted by '"', 1739 # -b 'cn=\"$1\",cn=mapping tree,cn=config' works in all cases 1740 if ${LDAPSEARCH} ${CON_ARGS} "${AUTH_ARGS[@]}" \ 1741 -b "cn=\"${STR[LDAP_SUFFIX]}\",cn=mapping tree,cn=config" \ 1742 -s base 'objectclass=*' dn 2>/dev/null 1743 then 1744 Log.verbose 'Suffix mapping already exists' 1745 get_backend || return 66 1746 return 0 1747 fi 1748 1749 # no suffix mapping, just in case check ldbm backends consistency - 1750 # there are must be NO any databases pointing to STR[LDAP_SUFFIX] 1751 X=${ ${LDAPSEARCH} ${CON_ARGS} "${AUTH_ARGS[@]}" \ 1752 -b 'cn=ldbm database,cn=plugins,cn=config' \ 1753 -s one "nsslapd-suffix=${STR[LDAP_SUFFIX]}" dn 2>/dev/null ; } 1754 if [[ -n ${X} ]]; then 1755 Log.warn 'Sorry, there is no suffix mapping for' \ 1756 "'${STR[LDAP_SUFFIX]}', while ldbm database exists, server" \ 1757 'configuration needs to be fixed manually, look at cn=mapping' \ 1758 'tree,cn=config and cn=ldbm database,cn=plugins,cn=config' 1759 return 1 1760 fi 1761 fi 1762 1763 Log.verbose 'Backend needs to be created ...' 1764 TMPF[NEED_CREATE_BACKEND]=1 1765 return 0 1766 } 1767 1768 Man.addFunc prep_create_sfx_backend '' '[+NAME?prep_create_sfx_backend - prepare for the suffix backend creation.] 1769 [+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.] 1770 [+RETURN VALUES]{ 1771 [+0?backend db name is ok.] 1772 [+1?\bSTR[DS_DB]]\b exists, so \bTMP[DS_DB_AVAIL]]\b contains available name.] 1773 [+2?unable to find any available name.] 1774 } 1775 [+SEE ALSO?\bldapsearch\b(1).] 1776 ' 1777 function prep_create_sfx_backend { 1778 # check if requested name available 1779 [[ ${STR[DS_DB]} == ${TMP[DS_DBS_AVAIL]} ]] && return 0 1780 1781 # get the list of database names start with a requested name 1782 typeset -a DBS=( ) 1783 if (( TMPF[IS_OPENDJ] )); then 1784 typeset FILTER='(&(objectclass=ds-cfg-local-db-backend)' 1785 FILTER+="(ds-cfg-backend-id=${STR[DS_DB]}*))" 1786 ${LDAPSEARCH} ${CON_ARGS} "${AUTH_ARGS[@]}" \ 1787 -b 'cn=Backends,cn=config' -s one "${FILTER}" ds-cfg-backend-id \ 1788 2>/dev/null | \ 1789 while read LINE ; do 1790 [[ -n ${LINE} && ${LINE: -10} != ',cn=config' ]] && \ 1791 DBS+=( "${LINE#ds-cfg-backend-id=}" ) 1792 done 1793 else 1794 ${LDAPSEARCH} ${CON_ARGS} "${AUTH_ARGS[@]}" \ 1795 -b 'cn=ldbm database,cn=plugins,cn=config' \ 1796 -s one "cn=${STR[DS_DB]}*" cn 2>/dev/null | \ 1797 while read LINE ; do 1798 [[ -n ${LINE} && ${LINE: -10} != ',cn=config' ]] && \ 1799 DBS+=( "${LINE#cn=}" ) 1800 done 1801 fi 1802 if (( ${#DBS[@]} == 0 )); then 1803 # no backend name starts with STR[DS_DB] 1804 TMP[DS_DBS_AVAIL]="${STR[DS_DB]}" 1805 return 0 1806 fi 1807 1808 # find a non-existing db name based on a requested name 1809 integer I FOUND 1810 typeset -l NAME DB 1811 for (( I=0 ; I < 10; I++ )); do 1812 (( I )) && NAME="${STR[DS_DB]}${I}" || NAME="${STR[DS_DB]}" 1813 FOUND=0 1814 for DB in "${DBS[@]}" ; do 1815 [[ ${DB} == ${NAME} ]] && FOUND=1 && break 1816 done 1817 if (( ! FOUND )); then 1818 if (( I == 0 )); then 1819 TMP[DS_DBS_AVAIL]="${STR[DS_DB]}" 1820 return 0 # requested name is available 1821 fi 1822 TMP[DS_DBS_AVAIL]="${STR[DS_DB]}${I}" 1823 break 1824 fi 1825 done 1826 1827 if [[ -n ${TMP[DS_DBS_AVAIL]} ]]; then 1828 Log.warn "Database backend '${STR[DS_DB]}' already exists," \ 1829 "however '${TMP[DS_DBS_AVAIL]}' is available" 1830 return 1 1831 fi 1832 1833 Log.warn 'Unable to find any available database backend close to' \ 1834 "'${STR[DS_DB]}'" 1835 return 2 1836 } 1837 1838 Man.addFunc get_suffix '' '[+NAME?get_suffix - Ask user for suffix and related backend.] 1839 [+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.] 1840 [+RETURN VALUES]{ 1841 [+0?on success (user gave a correct suffix).] 1842 [+1?unable to create suffix given by user.] 1843 [+>= 66?a fatal error occured.] 1844 } 1845 [+SEE ALSO?\bget_ans()\b, \bdisplay_msg()\b, \bnormalizeDN()\b, \bprep_create_sfx_entry()\b, \bprep_create_sfx_backend()\b.] 1846 ' 1847 function get_suffix { 1848 while : ; do 1849 get_ans 'Enter suffix to be created (b=back/h=help):' \ 1850 "${STR[LDAP_BASEDN]}" 1851 case "${ANS}" in 1852 [Hh?] | Help | help) 1853 display_msg 'create_suffix_help' 1854 continue 1855 ;; 1856 [Bb] | '<' | Back | back) 1857 return 1 1858 ;; 1859 esac 1860 STR[LDAP_SUFFIX]=${ normalizeDN "${ANS}" l ; } 1861 prep_create_sfx_entry 1862 integer REDO=$? 1863 (( REDO >= 66 )) && return 66 1864 (( REDO )) && continue 1865 1866 if (( TMPF[NEED_CREATE_BACKEND] )); then 1867 TMP[DS_DBS_AVAIL]='' # reset the available db name 1868 REDO=0 1869 while : ; do 1870 X=${TMP[DS_DBS_AVAIL]} 1871 # Basically for OpenDJ we could use 'userRoot' as default aka 1872 # TMP[SUFFIX_VAL], however, users may use it for trying out 1873 # examples from the docs 1874 get_ans 'Enter local database backend name (b=back/h=help):' \ 1875 "${X:-${TMP[SUFFIX_VAL]}}" 1876 case "${ANS}" in 1877 [Hh?]) 1878 display_msg 'enter_ldbm_db_help' 1879 continue 1880 ;; 1881 [Bb] | '<') 1882 REDO=1 1883 break 1884 ;; 1885 esac 1886 STR[DS_DB]="${ANS}" 1887 prep_create_sfx_backend && break 1888 done 1889 1890 (( REDO )) && continue 1891 1892 Log.verbose 'Databse backend name for suffix ${STR[LDAP_SUFFIX]}:' \ 1893 "${DS_DB}" 1894 fi 1895 # eventually everything is prepared 1896 break 1897 done 1898 return 0 1899 } 1900 1901 Man.addFunc domain_2_dc '' '[+NAME?domain_2_dc - Convert a domain name into dc string.] 1902 [+DESCRIPTION?Convert a domain name \adomain\a into a dc=...,dc=... string and print it to stdout.] 1903 \n\n\adomain\a 1904 ' 1905 function domain_2_dc { 1906 [[ -z $1 ]] && return 1907 typeset DC='' SD 1908 for SD in ${1//./ } ; do # removes leading && trailing && double dots 1909 DC+=",dc=${SD}" 1910 done 1911 print -- "${DC:1}" 1912 } 1913 1914 Man.addFunc get_basedn '' '[+NAME?get_basedn - Ask user for Base DN.] 1915 [+DESCRIPTION?Ask the user for the BaseDN to use, check whether to create a suffix and store the value into \bSTR[LDAP_BASEDN]]\b.] 1916 [+RETURN VALUES]{ 1917 [+0?on success.] 1918 [+>= 66?a fatal error occured.] 1919 } 1920 [+SEE ALSO?\bget_ans_req()\b, \bdisplay_msg()\b, \bcheck_baseDN()\b, \bcheck_basedn_suffix()\b, \bget_suffix()\b.] 1921 ' 1922 function get_basedn { 1923 typeset BASEDN=${ domain_2_dc ${STR[LDAP_DOMAIN]} ; } 1924 integer RES 1925 1926 while : ; do 1927 # Get Base DN. 1928 while : ; do 1929 get_ans_req 'Enter LDAP Base DN (h=help):' "${BASEDN}" 1930 case "${ANS}" in 1931 [Hh?] | help | Help) display_msg 'basedn_help' ; continue ;; 1932 esac 1933 check_baseDN "${ANS}" && break 1934 Log.warn "Invalid base DN: '${ANS}'" 1935 done 1936 1937 # Set base DN and check its suffix 1938 STR[LDAP_BASEDN]="${ANS}" 1939 check_basedn_suffix || return 66 1940 1941 # suffix may need to be created, in that case get suffix from user 1942 if (( TMPF[NEED_CREATE_SUFFIX] )); then 1943 get_suffix 1944 RES=$? 1945 (( RES >= 66 )) && return 67 1946 (( RES )) && continue 1947 fi 1948 1949 # suffix is ok, break out of the base dn inquire loop 1950 break 1951 done 1952 return 0 1953 } 1954 1955 Man.addFunc chk_vlv_indexes '' '[+NAME?chk_vlv_indexes - check if server supports VLV.] 1956 [+DESCRIPTION?Checks, whether the DS supports Virtual List Views (VLV).] 1957 [+RETURN VALUES]{ 1958 [+0?if VLVs are supported.] 1959 [+>= 66?if VLVs are not supported.] 1960 } 1961 [+SEE ALSO?\bldapsearch\b(1).] 1962 ' 1963 function chk_vlv_indexes { 1964 typeset ATTR='' 1965 ${LDAPSEARCH} ${CON_ARGS} -b '' -s base 'objectclass=*' \ 1966 supportedControl 2>/dev/null | \ 1967 while read LINE ; do 1968 if [[ ${LINE#*=} == '2.16.840.1.113730.3.4.9' ]]; then 1969 ATTR=${.sh.match%=} 1970 break 1971 fi 1972 done 1973 if [[ -z ${ATTR} ]]; then 1974 Log.fatal 'VLV is not supported on LDAP server' 1975 return 66 1976 fi 1977 return 0 1978 } 1979 1980 Man.addFunc validate_suffix '' '[+NAME?validate_suffix - validate suffix obtained via config file.] 1981 [+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.] 1982 [+RETURN VALUES]{ 1983 [+0?on success.] 1984 [+>= 66?a fatal error occured.] 1985 } 1986 [+SEE ALSO?\bnormalizeDN()\b, \bldapsearch\b(1), \bprep_create_sfx_entry()\b, \bprep_create_sfx_backend()\b.] 1987 ' 1988 function validate_suffix { 1989 # Check STR[LDAP_SUFFIX] is not null 1990 if [[ -z ${STR[LDAP_SUFFIX]} ]]; then 1991 Log.fatal 'Invalid suffix (null suffix)' 1992 return 66 1993 fi 1994 1995 # STR[LDAP_SUFFIX] and STR[LDAP_BASEDN] are consistent ? 1996 typeset X=,${ normalizeDN "${STR[LDAP_BASEDN]}" l ; } 1997 typeset SFX=,${ normalizeDN "${STR[LDAP_SUFFIX]}" l ; } 1998 if [[ ${X: -${#SFX}} != ${SFX} ]]; then 1999 Log.fatal "Invalid suffix '${SFX}' for Base DN '${X}'" 2000 return 67 2001 fi 2002 2003 # STR[LDAP_SUFFIX] does exist ? 2004 if ${LDAPSEARCH} ${CON_ARGS} "${AUTH_ARGS[@]}" -b "${STR[LDAP_SUFFIX]}" \ 2005 -s base 'objectclass=*' >/dev/null 2>&1 2006 then 2007 checkSuffixRDN && return 0 || return 68 2008 fi 2009 2010 # Well, suffix does not exist, try to prepare create it ... 2011 TMPF[NEED_CREATE_SUFFIX]=1 2012 prep_create_sfx_entry || return 69 2013 if (( TMPF[NEED_CREATE_BACKEND] )); then 2014 if [[ -z ${STR[DS_DB]} ]]; then 2015 Log.fatal 'Database backend name is not set. Either set SIN[DS_DB]'\ 2016 'in the input file to a valid, i.e. non-existing DB name, or' \ 2017 'create the backend with the default DS tools' 2018 return 70 2019 fi 2020 # too risky, even if a available backend name could be found 2021 prep_create_sfx_backend || return 71 2022 fi 2023 2024 Log.verbose "Suffix: '${STR[LDAP_SUFFIX]}' Database: '${STR[DS_DB]}'" 2025 return 0 2026 } 2027 2028 Man.addFunc validate_info '' '[+NAME?validate_info - validate basic info obtained via config file.] 2029 [+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.] 2030 [+RETURN VALUES]{ 2031 [+0?on success.] 2032 [+>= 66?a fatal error occured.] 2033 } 2034 [+SEE ALSO?\bldapsearch\b(1), \bchk_vlv_indexes()\b, \bvalidate_suffix()\b.] 2035 ' 2036 function validate_info { 2037 # Set CON_ARGS and AUTH_ARGS for the config file. 2038 CON_ARGS="-h ${STR[DS_HOST]} -p ${INT[DS_PORT]}" 2039 AUTH_ARGS=( '-D' "${STR[LDAP_ROOTDN]}" '-j' "${TMP[LDAP_ROOTPWF]}" ) 2040 chk_ids_version || return 1 # Check DSEE version for compatibility 2041 2042 # Check the Root DN and Root DN passwd.o Same as for get_dirmgr_pw() 2043 RES=${ ${LDAPSEARCH} ${CON_ARGS} "${AUTH_ARGS[@]}" -b '' -s base \ 2044 'objectclass=*' supportedLDAPVersion 2>&1 ; } 2045 if (( $? )); then 2046 if [[ ${RES} =~ credential ]]; then 2047 Log.fatal 'Root DN passwd is invalid' 2048 else 2049 Log.fatal "Invalid Root DN '${STR[LDAP_ROOTDN]}'" 2050 fi 2051 return 66 2052 fi 2053 Log.verbose 'RootDN: ok RootDN passwd: ok' 2054 2055 # Check if the server supports the VLV 2056 chk_vlv_indexes || return 67 2057 Log.verbose 'VLV indexes ... ok' 2058 2059 # Check LDAP suffix 2060 validate_suffix || return 66 2061 Log.verbose 'LDAP suffix ... ok' 2062 return 0 2063 } 2064 2065 Man.addFunc gssapi_setup '' '[+NAME?gssapi_setup - set up GSSAPI if necessary.] 2066 [+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.] 2067 [+SEE ALSO?\bldapsearch\b(1), \bget_ans_req()\b.] 2068 ' 2069 function gssapi_setup { 2070 INT[GSSAPI_ENABLE]=0 2071 TMPF[GSSAPI_AUTH_MAY_BE_USED]=0 2072 2073 ${LDAPSEARCH} ${CON_ARGS} "${AUTH_ARGS[@]}" -b '' -s base 'objectclass=*' \ 2074 supportedSASLMechanisms 2>/dev/null | \ 2075 while read LINE ; do 2076 if [[ ${LINE:0:24} == 'supportedSASLMechanisms=' && \ 2077 ${LINE:24} == 'GSSAPI' ]] 2078 then 2079 TMPF[GSSAPI_AUTH_MAY_BE_USED]=1 2080 break 2081 fi 2082 done 2083 if (( ! TMPF[GSSAPI_AUTH_MAY_BE_USED] )); then 2084 Log.info 'sasl/GSSAPI is not supported by this LDAP server' 2085 return 2086 fi 2087 2088 get_confirm 'GSSAPI is supported. Do you want to set up GSSAPI:(y/n)' 'n' 2089 if (( $? )); then 2090 INT[GSSAPI_ENABLE]=1 2091 # get kerberos realm 2092 typeset -u REALM=${STR[LDAP_DOMAIN]} 2093 get_ans_req "Enter Kerberos Realm:" "${REALM}" 2094 REALM="${ANS}" # make sure, it is upper case 2095 STR[LDAP_KRB_REALM]="${REALM}" 2096 else 2097 Log.info 'GSSAPI is not set up - ' \ 2098 'sasl/GSSAPI bind may not work if it is not set up first' 2099 fi 2100 } 2101 2102 Man.addFunc get_profile_name '' '[+NAME?get_profile_name - Ask user for client profile name.] 2103 [+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.] 2104 [+SEE ALSO?\bget_ans()\b, \bldapsearch\b(1), \bget_confirm()\b, \bget_confirm_nodef()\b, \bdisplay_msg()\b.] 2105 ' 2106 function get_profile_name { 2107 TMPF[DEL_OLD_PROFILE]=0 # Reset since getting new profile name 2108 2109 typeset PNAME="${STR[LDAP_PROFILE_NAME]}" 2110 # Loop until valid profile name, or replace. 2111 while : ; do 2112 get_ans 'Enter the client profile name (h=help):' "${PNAME}" 2113 case "${ANS}" in 2114 [Hh] | help | Help | '?') display_msg 'profile_help' ; continue ;; 2115 esac 2116 2117 # Search to see if profile name already exists. 2118 if ! ${LDAPSEARCH} ${CON_ARGS} "${AUTH_ARGS[@]}" \ 2119 -b "cn=${ANS},ou=profile,${STR[LDAP_BASEDN]}" -s base \ 2120 'objectclass=*' >/dev/null 2>&1 2121 then 2122 break # Unique profile name (does not yet exist) 2123 fi 2124 2125 Log.info 'Profile "'"${ANS}"'" already exists, it is possible to' \ 2126 'enable shadow update now.' "${PROG}" 'will exit after shadow' \ 2127 'update is enabled. You can also continue to overwrite the' \ 2128 'profile or create a new one and be given the chance to enable' \ 2129 'shadow update later.' 2130 2131 get_confirm 'Just enable shadow update (y/n/h)?' 'n' \ 2132 'enable_shadow_update_help' 2133 if (( $? )); then 2134 # set up credentials for shadow update 2135 INT[LDAP_ENABLE_SHADOW_UPDATE]=1 2136 # display alternate messages 2137 INT[EXISTING_PROFILE]=1 2138 break 2139 fi 2140 2141 get_confirm_nodef \ 2142 "Are you sure you want to overwrite profile 'cn=${ANS}'?" 2143 if (( $? )); then 2144 TMPF[DEL_OLD_PROFILE]=1 # Replace old profile name 2145 break 2146 fi 2147 done 2148 2149 STR[LDAP_PROFILE_NAME]="${ANS}" 2150 } 2151 2152 Man.addFunc getACIs '' '[+NAME?getACIs - get ACIs for the current base DN.] 2153 [+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.] 2154 [+RETURN VALUES]{ 2155 [+-1?invalid function usage.] 2156 [+0?on success.] 2157 [+1?an error occured when executing the ldapsearch operation.] 2158 } 2159 \n\n\avname\a [\abaseDN\a] [\ascope\a] 2160 ' 2161 function getACIs { 2162 set -o pipefail # wanna have the exit code of ldapsearch 2163 [[ -z $1 ]] && Log.warn "${.sh.fun}(): invalid function usage" && \ 2164 return -1 2165 typeset -n ACILIST=$1 2166 typeset LINE 2167 typeset DN="$2" ACI='aci' 2168 [[ -z ${DN} ]] && DN="${STR[LDAP_BASEDN]}" 2169 [[ $3 == 'Global' ]] && (( TMPF[IS_OPENDJ] )) && ACI='ds-cfg-global-aci' 2170 2171 ${LDAPSEARCH} ${CON_ARGS} "${AUTH_ARGS[@]}" -b "${DN}" -s base \ 2172 'objectclass=*' ${ACI} 2>/dev/null | \ 2173 while read LINE ; do 2174 # NOTE: the || : is required to always get success (pipe doesn't break) 2175 [[ ${LINE:0:${#ACI}+1} == "${ACI}=" ]] && \ 2176 ACILIST+=( "${LINE:${#ACI}+1}" ) || : 2177 done 2178 if (( $? )); then 2179 Log.fatal "Unable to get aci list for '${STR[LDAP_BASEDN]}'" 2180 return 1 2181 fi 2182 return 0 2183 } 2184 2185 Man.addFunc findACI '' '[+NAME?findACI - find an ACI in the given list.] 2186 [+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.] 2187 [+RETURN VALUES]{ 2188 [+0?no ACI matched.] 2189 [+>=1?the index+1 of the ACI matched.] 2190 } 2191 [+SEE ALSO?\bgetACIs()\b, \bfindACI()\b, \bfind_and_delete_ACI\b()] 2192 \n\n\avname\a \aaciName\a \aregex\a [\ascope\a] 2193 ' 2194 function findACI { 2195 typeset -n LIST=$1 2196 typeset NAME="$2" REGEX="$3" SCOPE="$4" 2197 integer MAX=${#LIST[@]} I 2198 [[ -z ${SCOPE} ]] && SCOPE='BaseDN' 2199 for (( I=0; I < MAX; I++ )); do 2200 if [[ ${LIST[${I}]} =~ ${REGEX} ]]; then 2201 showProgress "${SCOPE} ACI '${NAME}' exists." 2202 return $((I+1)) 2203 fi 2204 done 2205 return 0 2206 } 2207 2208 Man.addFunc allow_proxy_read_pw '' '[+NAME?allow_proxy_read_pw - add Proxy Agent read permission for password.] 2209 [+DESCRIPTION?Add the ACI \bPROXY_ACI_NAME\b for \bSTR[LDAP_PROXYAGENT]]\b to \bSTR[LDAP_BASEDN]]\b if it not already exists.] 2210 [+ENVIRONMENT VARIABLES]{' "${ Man.varUsage PROXY_ACI_NAME ; }" '} 2211 [+RETURN VALUES]{ 2212 [+0?on success (ACI already exists or added successfully).] 2213 [+>= 66?a fatal error occured.] 2214 } 2215 [+SEE ALSO?\bgetACIs()\b, \bldapmodify\b(1).] 2216 ' 2217 function allow_proxy_read_pw { 2218 typeset ACI PATTERN TARGET 2219 2220 typeset -a LIST=( ) 2221 getACIs LIST || return 66 2222 2223 PATTERN='acl[ ]+"?('"${PROXY_ACI_NAME}|${PROXY_ACI_NAME// /_}"')"?' 2224 findACI LIST "${PROXY_ACI_NAME}" "${PATTERN}" || return 0 2225 2226 PATTERN='userPassword' 2227 if (( TMPF[IS_OPENDJ] )); then 2228 PATTERN+=' || authPassword' 2229 # At least in OpenDJ specifying the same target as where the ACI gets 2230 # attached is redundant wrt. the result and causes minimal overhead 2231 TARGET='' 2232 else 2233 TARGET='(target = "ldap:///'"${STR[LDAP_BASEDN]}"'")' 2234 fi 2235 2236 nextFile modify $0 2237 print ' 2238 dn: '"${STR[LDAP_BASEDN]}"' 2239 changetype: modify 2240 add: aci 2241 aci: '"${TARGET}"'(targetattr = "'"${PATTERN}"'") 2242 ( 2243 version 3.0; acl "'"${PROXY_ACI_NAME}"'"; 2244 allow (compare,read,search) userdn = "ldap:///'"${STR[LDAP_PROXYAGENT]}"'"; 2245 ) 2246 ' > ${TMP[FILE]} 2247 2248 if ! ${LDAPMODIFY} ${CON_ARGS} "${AUTH_ARGS[@]}" -f ${TMP[FILE]} \ 2249 >&${TMPF[FD]} 2>&1 2250 then 2251 Log.fatal "Adding '${PROXY_ACI_NAME}' ACI failed" 2252 return 66 2253 fi 2254 2255 showProgress "ACI '${PROXY_ACI_NAME}' added." 2256 return 0 2257 } 2258 2259 Man.addFunc delete_proxy_read_pw '' '[+NAME?delete_proxy_read_pw - delete Proxy Agent read permission for password.] 2260 [+DESCRIPTION?Remove the ACI \bPROXY_ACI_NAME\b from \bSTR[LDAP_BASEDN]]\b if such an ACI exists.] 2261 [+ENVIRONMENT VARIABLES]{' "${ Man.varUsage PROXY_ACI_NAME ; }" '} 2262 [+RETURN VALUES]{ 2263 [+0?on success (no delete required or ACI deleted).] 2264 [+>= 66?a fatal error occured.] 2265 } 2266 [+SEE ALSO?\bgetACIs()\b, \bget_menu_choice()\b, \bldapmodify\b(1).] 2267 ' 2268 function delete_proxy_read_pw { 2269 typeset PROXY_ACI='' ACI PATTERN X 2270 2271 typeset -a LIST=( ) 2272 getACIs LIST || return 66 2273 2274 # Search for ACI_NAME - no findACI() because we need ALL matching ACIs 2275 typeset -a ACILIST=( ) 2276 PATTERN='acl[ ]+"?('"${PROXY_ACI_NAME}|${PROXY_ACI_NAME// /_}"')"?' 2277 for ACI in "${LIST[@]}" ; do 2278 [[ ${ACI} =~ ${PATTERN} ]] && ACILIST+=( "${ACI#*=}" ) 2279 done 2280 2281 # We need to remove proxy agent's read access to user passwords, 2282 # but We do not know the value of the ${LDAP_PROXYAGENT} here, so 2283 # 1. if only one match found, delete it 2284 # 2. if more than one matches found, ask the user which one to delete 2285 if (( ${#ACILIST[@]} == 0 )); then 2286 Log.warn "ACI '${PROXY_ACI_NAME}' does not exist for" \ 2287 "'${STR[LDAP_BASEDN]}'" 2288 return 0 2289 fi 2290 if (( ${#ACILIST[@]} == 1 ));then 2291 PROXY_ACI="${ACI[0]}" 2292 else 2293 X=' 2294 Proxy agent is not allowed to read user passwords when shadow 2295 update is enabled. There are more than one proxy agents found. 2296 Please select the proxy agent currently being used, so that 2297 '"${PROG}"' can remove its read access to user passwords. 2298 2299 The proxy agents are: 2300 ' 2301 # print the list of ACIs for selection 2302 integer I MAX 2303 MAX=${#ACILIST[@]} 2304 for (( I=0; I < MAX; I++ )) ; do 2305 ACI="${ACILIST[${I}]}" 2306 if (( ! Log.VERBOSE )); then 2307 # older versions used 2308 # sed -e 's|.*ldap:///.*ldap:///||' -e 's|";)||' 2309 ACI=${ACI##*ldap:///} 2310 ACI=${ACI%\"*} 2311 fi 2312 X+=${ printf '%2d: %s\n' $((I+1)) "${ACI}" ; } 2313 done 2314 Log.warn "${X}" 2315 2316 # ask the user to pick one 2317 get_menu_choice "Select the proxy agent (1-${MAX}): " 1 ${MAX} 2318 I=$(( $? - 1 )) 2319 PROXY_ACI="${ACILIST[$I]}" 2320 fi 2321 2322 nextFile modify $0 2323 print ' 2324 dn: '"${STR[LDAP_BASEDN]}"' 2325 changetype: modify 2326 delete: aci 2327 aci: '"${PROXY_ACI}"' 2328 ' >${TMP[FILE]} 2329 2330 if ! ${LDAPMODIFY} ${CON_ARGS} "${AUTH_ARGS[@]}" -f ${TMP[FILE]} \ 2331 >&${TMPF[FD]} 2>&1 2332 then 2333 Log.fatal "Remove of '${PROXY_ACI_NAME}' ACI failed" 2334 cat ${TMP[FILE]} 2335 return 66 2336 fi 2337 2338 Log.info "Removed '${PROXY_ACI_NAME}' ACI" 2339 return 0 2340 } 2341 2342 Man.addFunc allow_host_read_write_shadow '' '[+NAME?allow_host_read_write_shadow - add host principal read/write permission for shadow data.] 2343 [+DESCRIPTION?Add the ACI \bHOST_ACI_NAME\b to \bSTR[LDAP_BASEDN]]\b unless it already exists.] 2344 [+ENVIRONMENT VARIABLES]{' "${ Man.varUsage HOST_ACI_NAME ; }" '} 2345 [+RETURN VALUES]{ 2346 [+0?on success (ACI already exists or added successfully).] 2347 [+>= 66?a fatal error occured.] 2348 } 2349 [+SEE ALSO?\bgetACIs()\b, \bldapmodify\b(1).] 2350 ' 2351 function allow_host_read_write_shadow { 2352 typeset ACI PATTERN TARGET='' 2353 2354 typeset -a LIST=( ) 2355 getACIs LIST || return 66 2356 2357 PATTERN='acl[ ]+"?('"${HOST_ACI_NAME}|${HOST_ACI_NAME// /_}"')"?' 2358 findACI LIST "${HOST_ACI_NAME}" "${PATTERN}" || return 0 2359 2360 (( ! TMPF[IS_OPENDJ] )) && \ 2361 TARGET='(target = "ldap:///'"${STR[LDAP_BASEDN]}"'")' # OD: redundant 2362 2363 nextFile modify $0 2364 print ' 2365 dn: '"${STR[LDAP_BASEDN]}"' 2366 changetype: modify 2367 add: aci 2368 aci: '"${TARGET}"'(targetattr = "shadowLastChange || shadowMin || shadowMax || shadowWarning || shadowInactive || shadowExpire || shadowFlag || userPassword || loginShell || homeDirectory || gecos") 2369 ( 2370 version 3.0; acl '"${HOST_ACI_NAME}"'; 2371 allow (write,compare,read,search) authmethod = "sasl GSSAPI" and userdn = "ldap:///cn=*+ipHostNumber=*,ou=Hosts,'"${STR[LDAP_BASEDN]}"'; 2372 ) 2373 ' > ${TMP[FILE]} 2374 2375 if ! ${LDAPMODIFY} ${CON_ARGS} "${AUTH_ARGS[@]}" -f ${TMP[FILE]} \ 2376 >&${TMPF[FD]} 2>&1 2377 then 2378 Log.fatal 'Remove of '${ACI_NAME}' ACI failed' 2379 return 66 2380 fi 2381 2382 showProgress "ACI '${ACI_NAME}' added." 2383 return 0 2384 } 2385 2386 Man.addFunc find_and_delete_ACI '' '[+NAME?find_and_delete_ACI - find and delete an ACI in the given list.] 2387 [+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.] 2388 [+RETURN VALUES]{ 2389 [+0?on success (no ACI matched or ACI removed successfully).] 2390 [+66?an error occured when removing the matched ACI.] 2391 } 2392 [+SEE ALSO?\bgetACIs()\b, \bldapmodify\b(1)] 2393 \n\n\avname\a \aaciName\a \aregex\a 2394 ' 2395 function find_and_delete_ACI { 2396 typeset -n LIST=$1 2397 typeset ACI_NAME="$2" REGEX="$3" DEL='' ACI 2398 2399 for ACI in "${LIST[@]}" ; do 2400 [[ ${ACI} =~ ${REGEX} ]] && DEL="${ACI}" && break 2401 done 2402 [[ -z ${DEL} ]] && return 0 2403 2404 nextFile modify "${0}-${ACI_NAME}" 2405 print ' 2406 dn: '"${STR[LDAP_BASEDN]}"' 2407 changetype: modify 2408 delete: aci 2409 aci: '"${DEL}"' 2410 ' > ${TMP[FILE]} 2411 2412 if ! ${LDAPMODIFY} ${CON_ARGS} "${AUTH_ARGS[@]}" -f ${TMP[FILE]} \ 2413 >&${TMPF[FD]} 2>&1 2414 then 2415 Log.fatal "Remove of '${ACI_NAME}' ACI failed" 2416 return 66 2417 fi 2418 2419 showProgress "ACI '${ACI_NAME}' deleted." 2420 return 0 2421 } 2422 2423 Man.addFunc deny_non_host_shadow_access '' '[+NAME?deny_non_host_shadow_access - add a deny non-host access to shadow data.] 2424 [+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.] 2425 [+ENVIRONMENT VARIABLES]{' "${ Man.varUsage NON_HOST_ACI_NAME NON_ADMIN_ACI_NAME ; }" '} 2426 [+RETURN VALUES]{ 2427 [+0?on success (ACI already exists or added successfully).] 2428 [+>= 66?a fatal error occured.] 2429 } 2430 [+SEE ALSO?\bgetACIs()\b, \bfind_and_delete_ACI()\b, \bldapmodify\b(1).] 2431 ' 2432 function deny_non_host_shadow_access { 2433 typeset ACI PATTERN TARGET='' 2434 2435 typeset -a LIST=( ) 2436 getACIs LIST || return 66 2437 2438 # If an ACI with ${NON_HOST_ACI_NAME} already exists, we are done 2439 PATTERN='acl[ ]+"?('"${NON_HOST_ACI_NAME}|${NON_HOST_ACI_NAME// /_}"')"?' 2440 findACI LIST "${NON_HOST_ACI_NAME}" "${PATTERN}" || return 0 2441 2442 # The deny_non_admin_shadow_access and deny_non_host_shadow_access ACIs 2443 # should be mutually exclusive, so if the former exists, delete it 2444 PATTERN='acl[ ]+"?('"${NON_ADMIN_ACI_NAME}|${NON_ADMIN_ACI_NAME// /_}"')"?' 2445 find_and_delete_ACI LIST "${NON_ADMIN_ACI_NAME}" "${PATTERN}" || return 67 2446 2447 (( ! TMPF[IS_OPENDJ] )) && \ 2448 TARGET='(target = "ldap:///'"${STR[LDAP_BASEDN]}"'")' # OD: redundant 2449 2450 nextFile modify $0 2451 print ' 2452 dn: '"${STR[LDAP_BASEDN]}"' 2453 changetype: modify 2454 add: aci 2455 aci: '"${TARGET}"'(targetattr = "shadowLastChange || shadowMin || shadowMax ||shadowWarning || shadowInactive || shadowExpire || shadowFlag || userPassword") 2456 ( 2457 version 3.0; acl "'"${NON_HOST_ACI_NAME}"'"; 2458 deny (write,read,search,compare) userdn != "ldap:///cn=*+ipHostNumber=*,ou=Hosts,'"${STR[LDAP_BASEDN]}"'"; 2459 ) 2460 ' > ${TMP[FILE]} 2461 2462 if ! ${LDAPMODIFY} ${CON_ARGS} "${AUTH_ARGS[@]}" -f ${TMP[FILE]} \ 2463 >&${TMPF[FD]} 2>&1 2464 then 2465 Log.fatal "Adding ACI '${NON_HOST_ACI_NAME}' failed" 2466 return 67 2467 fi 2468 2469 showProgress "ACI '${NON_HOST_ACI_NAME}' added." 2470 return 0 2471 } 2472 2473 Man.addFunc get_adminDN '' '[+NAME?get_adminDN - ask user for Admin DN.] 2474 [+DESCRIPTION?Ask the user for the Admin DN and store it into \bSTR[LDAP_BASEDN]]\b.] 2475 [+SEE ALSO?\bget_ans()\b.] 2476 ' 2477 function get_adminDN { 2478 get_ans 'Enter DN for the administrator:' \ 2479 "cn=admin,ou=profile,${STR[LDAP_BASEDN]}" 2480 STR[LDAP_ADMINDN]="${ANS}" 2481 } 2482 2483 Man.addFunc get_admin_pw '' '[+NAME?get_admin_pw - ask user for Admin password] 2484 [+DESCRIPTION?Ask the user for the Admin password and store it into \bSTR[LDAP_ADMIN_CRED]]\b.] 2485 [+SEE ALSO?\bget_passwd()\b.] 2486 ' 2487 function get_admin_pw { 2488 get_passwd 'Enter passwd for the administrator:' 2489 STR[LDAP_ADMIN_CRED]="${ANS}" 2490 } 2491 2492 Man.addFunc add_admin '' '[+NAME?add_admin - Add administrator identity.] 2493 [+DESCRIPTION?Add the \bSTR[LDAP_ADMINDN]]\b entry with the password \bSTR[LDAP_ADMIN_CRED]]\b unless it already exists.] 2494 [+RETURN VALUES]{ 2495 [+0?on success (entry already exists or added successfully).] 2496 [+>= 66?a fatal error occured.] 2497 } 2498 [+SEE ALSO?\bldapsearch\b(1), \bldapmodify\b(1).] 2499 ' 2500 function add_admin { 2501 if ${LDAPSEARCH} ${CON_ARGS} "${AUTH_ARGS[@]}" -b "${STR[LDAP_ADMINDN]}" \ 2502 -s base 'objectclass=*' >/dev/null 2>&1 2503 then 2504 showProgress "Administrator identity exists." 2505 return 0 2506 fi 2507 2508 typeset CN="${STR[LDAP_ADMINDN]%%,*}" 2509 CN="${CN#*=}" 2510 2511 nextFile add $0 2512 print ' 2513 dn: '"${STR[LDAP_ADMINDN]}"' 2514 cn: '"${CN}"' 2515 sn: '"${CN}"' 2516 objectclass: top 2517 objectclass: person 2518 userpassword: '"${STR[LDAP_ADMIN_CRED]}"' 2519 ' > ${TMP[FILE]} 2520 2521 if ! ${LDAPMODIFY} -a ${CON_ARGS} "${AUTH_ARGS[@]}" -f ${TMP[FILE]} \ 2522 >&${TMPF[FD]} 2>&1 2523 then 2524 Log.fatal 'Adding administrator identity failed' 2525 return 66 2526 fi 2527 2528 showProgress 'Administrator identity added.' 2529 return 0 2530 } 2531 2532 Man.addFunc allow_admin_read_write_shadow '' '[+NAME?allow_admin_read_write_shadow - add Admin read/write permission for shadow, rbac and mount data.] 2533 [+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.] 2534 [+ENVIRONMENT VARIABLES]{' "${ Man.varUsage ADMIN_ACI_NAME ; }" '} 2535 [+RETURN VALUES]{ 2536 [+0?on success (aci already exists or addition was successfull).] 2537 [+>= 66?a fatal error occured.] 2538 } 2539 [+SEE ALSO?\bgetACIs()\b, \bfind_and_delete_ACI()\b, \bldapmodify\b(1).] 2540 ' 2541 function allow_admin_read_write_shadow { 2542 typeset PERMS='write,compare,read,search,add,delete' \ 2543 ACI RBAC PATTERN TARGET='' 2544 2545 typeset -a LIST=( ) 2546 getACIs LIST || return 66 2547 2548 # If the ACI already exists, we are done 2549 PATTERN='acl[ ]+"?('"${ADMIN_ACI_NAME}|${ADMIN_ACI_NAME// /_}"')"?' 2550 PATTERN+='.*\('"${PERMS}"'\).*'"${STR[LDAP_ADMINDN]}" 2551 findACI LIST "${ADMIN_ACI_NAME}" "${PATTERN}" || return 0 2552 2553 # If an ACI with ${ADMIN_ACI_NAME} and "(write|write,compare,read,search)" 2554 # and ${LDAP_ADMINDN} exists, delete it 2555 PATTERN='acl[ ]+"?('"${ADMIN_ACI_NAME}|${ADMIN_ACI_NAME// /_}"')"?' 2556 PATTERN+='.*\(write\).*'"${STR[LDAP_ADMINDN]}" 2557 find_and_delete_ACI LIST "${ADMIN_ACI_NAME}" "${PATTERN}" || return 67 2558 2559 PATTERN='acl[ ]+"?('"${ADMIN_ACI_NAME}|${ADMIN_ACI_NAME// /_}"')"?' 2560 PATTERN+='.*\(write,compare,read,search\).*'"${STR[LDAP_ADMINDN]}" 2561 find_and_delete_ACI LIST "${ADMIN_ACI_NAME}" "${PATTERN}" || return 68 2562 2563 RBAC='objectClass || uid || uidNumber || gidNumber || cn' 2564 RBAC+=' || SolarisAttrKeyValue || SolarisAttrShortDesc' 2565 RBAC+=' || SolarisAttrLongDesc || SolarisKernelSecurityPolicy' 2566 RBAC+=' || SolarisProfileType || SolarisProfileId || SolarisUserQualifier' 2567 RBAC+=' || SolarisReserved1 || SolarisReserved2' 2568 RBAC+=' || SolarisAttrReserved1 || SolarisAttrReserved2' 2569 RBAC+=' || SolarisProjectID || SolarisProjectName' 2570 RBAC+=' || SolarisProjectAttr || memberUid || memberGid || description' 2571 RBAC+=' || automountKey || automountMapName || automountInformation' 2572 RBAC+=' || ipTnetTemplateName || ipTnetNumber' 2573 2574 (( ! TMPF[IS_OPENDJ] )) && \ 2575 TARGET='(target = "ldap:///'"${STR[LDAP_BASEDN]}"'")' # OD: redundant 2576 2577 nextFile modify $0 2578 print ' 2579 dn: '"${STR[LDAP_BASEDN]}"' 2580 changetype: modify 2581 add: aci 2582 aci: '"${TARGET}"'(targetattr = "shadowLastChange || shadowMin || shadowMax || shadowWarning || shadowInactive || shadowExpire || shadowFlag || userPassword || loginShell || homeDirectory || gecos || '"${RBAC}"'") 2583 ( 2584 version 3.0; acl "'${ADMIN_ACI_NAME}'"; 2585 allow ('"${PERMS}"') userdn = "ldap:///'"${STR[LDAP_ADMINDN]}"'"; 2586 ) 2587 ' > ${TMP[FILE]} 2588 2589 if ! ${LDAPMODIFY} ${CON_ARGS} "${AUTH_ARGS[@]}" -f ${TMP[FILE]} \ 2590 >&${TMPF[FD]} 2>&1 2591 then 2592 Log.fatal "Adding '${ADMIN_ACI_NAME}' ACI failed" 2593 return 69 2594 fi 2595 2596 showProgress "ACI '${ADMIN_ACI_NAME}' added." 2597 return 0 2598 } 2599 2600 Man.addFunc deny_non_admin_shadow_access '' '[+NAME?deny_non_admin_shadow_access - add a deny Admin access to shadow data.] 2601 [+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.] 2602 [+ENVIRONMENT VARIABLES]{' "${ Man.varUsage NON_ADMIN_ACI_NAME NON_HOST_ACI_NAME ; }" '} 2603 [+RETURN VALUES]{ 2604 [+0?on success (ACI already exists or added successfully).] 2605 [+>= 66?a fatal error occured.] 2606 } 2607 [+SEE ALSO?\bgetACIs()\b, \bfind_and_delete_ACI()\b, \bldapmodify\b(1).] 2608 ' 2609 function deny_non_admin_shadow_access { 2610 typeset TARGET='' 2611 typeset -a LIST=( ) 2612 getACIs LIST || return 66 2613 2614 # If the ACI already exists, we are done 2615 PATTERN='acl[ ]+"?('"${NON_ADMIN_ACI_NAME}|${NON_ADMIN_ACI_NAME// /_}"')"?' 2616 findACI LIST "${NON_ADMIN_ACI_NAME}" "${PATTERN}" || return 0 2617 2618 # The deny_non_admin_shadow_access and deny_non_host_shadow_access ACIs 2619 # should be mutually exclusive, so if the latter exists, delete it 2620 PATTERN='acl[ ]+"?('"${NON_HOST_ACI_NAME}|${NON_HOST_ACI_NAME// /_}"')"?' 2621 find_and_delete_ACI LIST "${NON_HOST_ACI_NAME}" "${PATTERN}" || return 67 2622 2623 (( ! TMPF[IS_OPENDJ] )) && \ 2624 TARGET='(target = "ldap:///'"${STR[LDAP_BASEDN]}"'")' # OD: redundant 2625 2626 nextFile modify $0 2627 print ' 2628 dn: '"${STR[LDAP_BASEDN]}"' 2629 changetype: modify 2630 add: aci 2631 aci: '"${TARGET}"'(targetattr = "shadowLastChange || shadowMin || shadowMax || shadowWarning || shadowInactive || shadowExpire || shadowFlag || userPassword") 2632 ( 2633 version 3.0; acl "'"${NON_ADMIN_ACI_NAME}"'"; 2634 deny (write,read,search,compare,add,delete) userdn != "ldap:///'"${STR[LDAP_ADMINDN]}"'"; 2635 ) 2636 ' > ${TMP[FILE]} 2637 2638 if ! ${LDAPMODIFY} ${CON_ARGS} "${AUTH_ARGS[@]}" -f ${TMP[FILE]} \ 2639 >&${TMPF[FD]} 2>&1 2640 then 2641 Log.fatal "Adding '${NON_ADMIN_ACI_NAME}' ACI failed" 2642 ${CAT} ${TMP[FILE]} 2643 return 68 2644 fi 2645 2646 showProgress "ACI '${NON_ADMIN_ACI_NAME}' added." 2647 return 0 2648 } 2649 2650 Man.addFunc setup_shadow_update '' '[+NAME?setup_shadow_update - set up shadow update.] 2651 [+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).] 2652 [+RETURN VALUES]{ 2653 [+0?on success.] 2654 [+>= 66? a fatal error occured.] 2655 } 2656 [+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.] 2657 ' 2658 function setup_shadow_update { 2659 # get content of the profile 2660 integer CN=0 GSSAPI=0 SELF=0 2661 typeset -l LINE 2662 2663 ${LDAPSEARCH} ${CON_ARGS} "${AUTH_ARGS[@]}" \ 2664 -b "cn=${STR[LDAP_PROFILE_NAME]},ou=profile,${STR[LDAP_BASEDN]}" \ 2665 -s base 'objectclass=*' 2>/dev/null | \ 2666 while read LINE ; do 2667 [[ ${LINE:0:3} == 'cn=' ]] && CN=1 2668 [[ ${LINE:0:21} == 'authenticationmethod=' && ${LINE:21} == 'GSSAPI' ]]\ 2669 && GSSAPI=1 2670 [[ ${LINE:0:16} == 'credentiallevel=' && ${LINE:16} == 'self' ]] \ 2671 && SELF=1 2672 done 2673 if (( ! CN )); then 2674 Log.verbose "Profile '${STR[LDAP_PROFILE_NAME]}' does not exist" 2675 return 0 2676 fi 2677 2678 # If authenticationMethod has 'GSSAPI' and credentialLevel 2679 # has 'self', ask to use the host principal for shadow update 2680 if (( TMPF[GSSAPI_AUTH_MAY_BE_USED] && GSSAPI && SELF )); then 2681 INT[NEED_HOSTACL]=1 2682 fi 2683 Log.verbose "NEED_HOSTACL = ${INT[NEED_HOSTACL]}" 2684 2685 if (( INT[NEED_HOSTACL] )); then 2686 get_confirm 'Use host principal for shadow data update (y/n/h)?' 'y' \ 2687 'use_host_principal_help' 2688 if (( $? )); then 2689 delete_proxy_read_pw || return 66 2690 allow_host_read_write_shadow || return 67 2691 deny_non_host_shadow_access || return 68 2692 Log.info 'Shadow update has been enabled' 2693 else 2694 Log.warn 'Shadow update may not work' 2695 fi 2696 return 0 2697 fi 2698 2699 get_confirm 'Add the administrator identity (y/n/h)?' 'y' \ 2700 'add_admin_cred_help' 2701 if (( $? )); then 2702 get_adminDN 2703 get_admin_pw 2704 add_admin || return 69 2705 delete_proxy_read_pw || return 66 2706 allow_admin_read_write_shadow || return 70 2707 deny_non_admin_shadow_access || return 71 2708 Log.info 'Shadow update has been enabled' 2709 else 2710 Log.warn 'No administrator identity specified, shadow update may not' \ 2711 'work' 2712 fi 2713 return 0 2714 } 2715 2716 Man.addFunc get_srv_list '' '[+NAME?get_srv_list - Ask user for default server list.] 2717 [+DESCRIPTION?Ask the user for the default server list and store it into \bSTR[LDAP_SERVER_LIST]]\b.] 2718 [+SEE ALSO?\bget_ans()\b, \bdisplay_msg()\b.] 2719 ' 2720 function get_srv_list { 2721 # If LDAP_SERVER_LIST is NULL, then set, otherwise leave alone 2722 if [[ -z ${STR[LDAP_SERVER_LIST]} ]]; then 2723 ANS=${ ${GETENT} hosts ${STR[DS_HOST]} ; } 2724 STR[LDAP_SERVER_LIST]=${ANS%%[[:space:]]*} 2725 if (( INT[DS_PORT] != 389 )); then 2726 STR[LDAP_SERVER_LIST]="${STR[LDAP_SERVER_LIST]}:${INT[DS_PORT]}" 2727 fi 2728 fi 2729 2730 while : ; do 2731 get_ans 'Default server list (h=help):' "${STR[LDAP_SERVER_LIST]}" 2732 case "${ANS}" in 2733 [Hh] | help | Help | '?') display_msg 'def_srvlist_help' ;; 2734 * ) break ;; 2735 esac 2736 done 2737 STR[LDAP_SERVER_LIST]="${ANS}" 2738 } 2739 2740 Man.addFunc get_pref_srv '' '[+NAME?get_pref_srv - Ask user for preferred server list.] 2741 [+DESCRIPTION?Ask the user for the preferred server list and store it into \bSTR[LDAP_PREF_SRVLIST]]\b (overrides the server list).] 2742 [+SEE ALSO?\bget_ans()\b, \bdisplay_msg()\b.] 2743 ' 2744 function get_pref_srv { 2745 while : ; do 2746 get_ans 'Preferred server list (h=help):' "${STR[LDAP_PREF_SRVLIST]}" 2747 case "${ANS}" in 2748 [Hh] | help | Help | '?') display_msg 'pref_srvlist_help' ;; 2749 * ) break ;; 2750 esac 2751 done 2752 STR[LDAP_PREF_SRVLIST]="${ANS}" 2753 } 2754 2755 Man.addFunc get_search_scope '' '[+NAME?get_search_scope - Ask user for search scope.] 2756 [+DESCRIPTION?Ask the user for the search scope and store it into \bSTR[LDAP_SEARCH_SCOPE]]\b.] 2757 [+RETURN VALUES]{ 2758 [+1?search scope "one" selected.] 2759 [+2?search scope "sub" selected.] 2760 } 2761 [+SEE ALSO?\bget_ans()\b, \bdisplay_msg()\b.] 2762 ' 2763 function get_search_scope { 2764 integer RES 2765 get_menu_choice 'Choose desired search scope (1=one, 2=sub, h=help): ' \ 2766 1 2 1 srch_scope_help 2767 (( $? == 2 )) && { STR[LDAP_SEARCH_SCOPE]='sub' ; return 2 ; } 2768 STR[LDAP_SEARCH_SCOPE]='one' 2769 return 1 2770 } 2771 2772 Man.addFunc get_cred_level '' '[+NAME?get_cred_level - Ask user for credential level.] 2773 [+DESCRIPTION?Ask the user for the client credential level to use and store it into \bSTR[LDAP_CRED_LEVEL]]\b.] 2774 [+ENVIRONMENT VARIABLES]{' "${ Man.varUsage CRED_LEVELS STR ; }" '} 2775 [+RETURN VALUES?The index wrt. \bCRED_LEVELS\b of the selected credential level \b+1\b.] 2776 [+SEE ALSO?\bget_ans()\b, \bdisplay_msg()\b.] 2777 ' 2778 function get_cred_level { 2779 integer RES 2780 typeset PROMPT='' 2781 (( INT[GSSAPI_ENABLE] )) && PROMPT='"self" is needed for GSSAPI profile.\n' 2782 PROMPT+='Choose Credential level (identity) [h=help]:' 2783 2784 get_menu_choice "${PROMPT}" 1 4 1 'cred_lvl_help' 'cred_level_menu' 2785 RES=$? 2786 STR[LDAP_CRED_LEVEL]="${CRED_LEVELS[RES-1]}" 2787 return ${RES} 2788 } 2789 2790 Man.addFunc get_auth '' '[+NAME?get_auth - ask user for authentication methods.] 2791 [+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.] 2792 [+ENVIRONMENT VARIABLES]{' "${ Man.varUsage AUTH_METHODS STR ; }" '} 2793 [+RETURN VALUES]{ 2794 [+0?on success.] 2795 [+-1?if an invalid \atype\a was given.] 2796 } 2797 [+SEE ALSO?\bget_menu_choice()\b, \bget_confirm_nodef()\b.] 2798 \n\n\atype\a 2799 ' 2800 function get_auth { 2801 typeset TYPE=$1 VNAME HELPTAG MENU 2802 integer RES MAX=${#AUTH_METHODS[@]} OFFSET=0 2803 typeset -a SELECTED=( ) 2804 typeset -u UTYPE=${TYPE} 2805 2806 case "${TYPE}" in 2807 client) VNAME=LDAP_AUTHMETHOD ; OFFSET=1 ; UTYPE='DEFAULT' ;; 2808 pam_ldap) VNAME=LDAP_SRV_AUTHMETHOD_PAM ;; 2809 keyserv) VNAME=LDAP_SRV_AUTHMETHOD_KEY ;; 2810 passwd-cmd) VNAME=LDAP_SRV_AUTHMETHOD_CMD ;; 2811 *) print -u2 "${.sh.fun}() bug: invalid type argument" && return -1 ;; 2812 esac 2813 2814 typeset ASK='' VAL='' M='' 2815 if (( OFFSET )); then 2816 # client 2817 (( INT[GSSAPI_ENABLE] )) && \ 2818 ASK='"sasl/GSSAPI" is needed for GSSAPI profile.\n' 2819 ASK+='Choose DEFAULT authentication (bind) method (0=reset, h=help):' 2820 MENU='auth_method_menu' 2821 HELPTAG='auth_help' 2822 else 2823 ASK="Choose ${UTYPE} authentication (bind) method (0=reset, h=help):" 2824 (( MAX-- )) 2825 MENU='srvauth_method_menu' 2826 HELPTAG='srvauth_help' 2827 fi 2828 2829 while : ; do 2830 get_menu_choice "${ASK}" 0 ${MAX} 1 ${HELPTAG} ${MENU} 2831 RES=$? 2832 if (( RES )); then 2833 M=${AUTH_METHODS[RES - OFFSET]} 2834 # avoid doubles 2835 [[ ${VAL} != ~(E)[ ]${M}[ ] ]] && SELECTED+=( ${M} ) 2836 VAL=" ${SELECTED[@]} " 2837 M=${VAL// /\;} 2838 M=${M#\;} 2839 else 2840 typeset -a SELECTED=( ) # reset 2841 VAL='' 2842 M='' 2843 fi 2844 print && Log.info "Current authentication method(s): ${M%\;}\n" 2845 2846 get_confirm_nodef "Do you want to add another ${UTYPE} authentication method (y/n)?" \ 2847 && break 2848 done 2849 M=${M%\;} 2850 if (( ! OFFSET )); then 2851 M=${M//\;/\;${TYPE}:} 2852 if [[ -n ${M} ]]; then 2853 STR[${VNAME}]="${TYPE}:${M}" 2854 else 2855 STR[${VNAME}]='' 2856 INT[NEED_SRVAUTH_${TYPE##*_}]=0 2857 fi 2858 else 2859 STR[${VNAME}]="${M}" 2860 fi 2861 Log.verbose "Selected ${UTYPE} authentication methods: '${STR[${VNAME}]}'" 2862 return 0 2863 } 2864 2865 Man.addFunc get_followref '' '[+NAME?get_followref - Ask user whether or not to follow referrals.] 2866 [+DESCRIPTION?Ask the user whether the client should follow referrals. Result gets stored into \bINT[LDAP_FOLLOWREF]]\b.] 2867 [+SEE ALSO?\bget_confirm()\b, \bldapclient\b(1M).] 2868 ' 2869 function get_followref { 2870 get_confirm 'Do you want the clients to follow referrals (y/n/h)?' 'n' \ 2871 'referrals_help' 2872 INT[LDAP_FOLLOWREF]=$? 2873 } 2874 2875 Man.addFunc get_timelimit '' '[+NAME?get_timelimit - Ask user for search time limit.] 2876 [+DESCRIPTION?Ask the user for the max. time allowed to process a search operation and store it into \bINT[DS_TIMELIMIT]]\b.] 2877 [+RETURN VALUES]{ 2878 [+0?on success.] 2879 [+>= 66?a fatal error occured.] 2880 } 2881 [+SEE ALSO?\bis_numeric()\b, \bget_negone_num()\b.] 2882 ' 2883 function get_timelimit { 2884 typeset NAME='nsslapd-timelimit' VAL='' 2885 (( TMPF[IS_OPENDJ] )) && NAME='ds-cfg-time-limit' # min val = 0 2886 2887 # Get current timeout value from cn=config 2888 ${LDAPSEARCH} ${CON_ARGS} "${AUTH_ARGS[@]}" -b 'cn=config' -s base \ 2889 'objectclass=*' "${NAME}" 2>/dev/null | \ 2890 while read LINE; do 2891 if [[ ${LINE%%=*} == ${NAME} ]]; then 2892 VAL=${LINE#*=} 2893 break 2894 fi 2895 done 2896 if [[ -z ${VAL} ]]; then 2897 Log.fatal 'Unable to check current search processing timeout of the DS' 2898 return 66 2899 fi 2900 VAL=${VAL%%[[:space:]]*} 2901 is_numeric ${VAL} && NUM=${VAL} || NUM=INT[DS_TIMELIMIT] 2902 2903 get_negone_num "Enter the DS search processing time limit in seconds (current=${NUM})" \ 2904 '30' 2905 (( TMPF[IS_OPENDJ] && NUM < 0 )) && NUM=0 # unlimited 2906 2907 INT[DS_TIMELIMIT]=${NUM} 2908 return 0 2909 } 2910 2911 Man.addFunc get_sizelimit '' '[+NAME?get_sizelimit - Ask user for search size limit.] 2912 [+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.] 2913 [+RETURN VALUES]{ 2914 [+0?on success.] 2915 [+>= 66?a fatal error occured.] 2916 } 2917 [+SEE ALSO?\bis_numeric()\b, \bget_negone_num()\b.] 2918 ' 2919 function get_sizelimit { 2920 typeset NAME='nsslapd-sizelimit' VAL='' 2921 (( TMPF[IS_OPENDJ] )) && NAME='ds-cfg-size-limit' # min val = 0 2922 2923 # Get current search sizelimit 2924 ${LDAPSEARCH} ${CON_ARGS} "${AUTH_ARGS[@]}" -b 'cn=config' -s base \ 2925 'objectclass=*' "${NAME}" 2>/dev/null | \ 2926 while read LINE; do 2927 if [[ ${LINE%%=*} == ${NAME} ]]; then 2928 VAL=${LINE#*=} 2929 break 2930 fi 2931 done 2932 if [[ -z ${VAL} ]]; then 2933 Log.fatal 'Unable to check current search entry result limit of the DS' 2934 return 66 2935 fi 2936 is_numeric ${VAL} && NUM=${VAL} || NUM=INT[DS_SIZELIMIT] 2937 2938 get_negone_num "Enter max. entries per search the DS returns (current=${NUM})" '1000' 2939 (( TMPF[IS_OPENDJ] && NUM < 0 )) && NUM=0 # unlimited 2940 INT[DS_SIZELIMIT]=${NUM} 2941 return 0 2942 } 2943 2944 Man.addFunc get_want_crypt '' '[+NAME?get_want_crypt - Ask user whether to store passwords in crypt.] 2945 [+DESCRIPTION?Ask the user whether the default password storage scheme should be set to "CRYPT" and stores the result into \bINT[NEED_CRYPT]]\b.] 2946 [+SEE ALSO?\bget_confirm()\b.] 2947 ' 2948 function get_want_crypt { 2949 get_confirm 'Do you want to store passwords in "CRYPT" formats (y/n/h)?' \ 2950 'n' 'crypt_help' 2951 INT[NEED_CRYPT]=$? 2952 } 2953 2954 Man.addFunc get_srch_time '' '[+NAME?get_srch_time - Ask user for client search time limit.] 2955 [+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.] 2956 [+SEE ALSO?\bget_negone_num()\b, \bldapclient\b(1M).] 2957 ' 2958 function get_srch_time { 2959 get_negone_num 'Client search time limit in seconds (h=help):' \ 2960 ${INT[LDAP_SEARCH_TIME_LIMIT]} 'srchtime_help' 2961 INT[LDAP_SEARCH_TIME_LIMIT]=${NUM} 2962 } 2963 2964 Man.addFunc get_prof_ttl '' '[+NAME?get_prof_ttl - Ask user for client profile time to live (TTL).] 2965 [+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.] 2966 [+SEE ALSO?\bget_negone_num()\b, \bldapclient\b(1M).] 2967 ' 2968 function get_prof_ttl { 2969 get_negone_num 'Client Profile Time To Live in seconds (h=help):' \ 2970 ${INT[LDAP_PROFILE_TTL]} 'profttl_help' 2971 INT[LDAP_PROFILE_TTL]=${NUM} 2972 } 2973 2974 Man.addFunc get_bind_limit '' '[+NAME?get_bind_limit - Ask user for client bind time limit.] 2975 [+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.] 2976 [+SEE ALSO?\bget_negone_num()\b, \bldapclient\b(1M).] 2977 ' 2978 function get_bind_limit { 2979 get_negone_num 'Client bind time limit in seconds (h=help):' \ 2980 ${INT[LDAP_BIND_LIMIT]} 'bindlim_help' 2981 INT[LDAP_BIND_LIMIT]=${NUM} 2982 } 2983 2984 Man.addFunc get_want_shadow_update '' '[+NAME?get_want_shadow_update - Ask user whether to enable shadow update.] 2985 [+DESCRIPTION?Ask the user whether to enable shadow update. Set \bINT[LDAP_ENABLE_SHADOW_UPDATE]]\b to 0 if not desired, to 1 otherwise.] 2986 [+SEE ALSO?\bget_confirm()\b.] 2987 ' 2988 function get_want_shadow_update { 2989 get_confirm 'Do you want to enable shadow update (y/n/h)?' 'n' \ 2990 'enable_shadow_update_help' 2991 INT[LDAP_ENABLE_SHADOW_UPDATE]=$? 2992 } 2993 2994 ###################################################################### 2995 # FUNCTIONS FOR Service Search Descriptor's START HERE. 2996 ###################################################################### 2997 Man.addFunc add_ssd '' '[+NAME?add_ssd - Ask user for SSD to add.] 2998 [+DESCRIPTION?Ask the user for the ID:BASE:SCOPE and add it to \bSSD\b list.] 2999 [+ENVIRONMENT VARIABLES]{' "${ Man.varUsage SSD ; }" '} 3000 [+SEE ALSO?\bget_ans\b, \bget_ans_req()\b.] 3001 ' 3002 function add_ssd { 3003 typeset ID='' BASE='' X 3004 typeset -l SCOPE='' 3005 integer FOUND 3006 3007 while : ; do 3008 get_ans 'Enter the service name (e.g. passwd):' 3009 [[ -z ${ANS} ]] && return 3010 [[ ${ANS} =~ [:] ]] && print 'Invalid service name' && continue 3011 3012 FOUND=0 3013 for X in "${SSD[@]}" ; do 3014 [[ ${X%%:*} == ${ANS} ]] && FOUND=1 && break 3015 done 3016 (( ! FOUND )) && break 3017 Log.warn "An SSD for '${ANS}' already exists:\n${X}\n" 3018 done 3019 ID="${ANS}" 3020 3021 while : ; do 3022 get_ans_req 'Enter the search base (e.g. ou=people,o=inet):' 3023 [[ ${ANS} != ~(E)[:] ]] && break 3024 Log.warn 'Invalid search base' && continue 3025 done 3026 BASE="${ANS}" 3027 3028 while : ; do 3029 get_ans_req "Enter the search scope (one|sub):" 3030 SCOPE=${ANS} 3031 [[ ${SCOPE} == 'one' || ${SCOPE} == 'sub' ]] && break 3032 Log.warn "'${ANS}' is NOT valid - Enter 'one' or 'sub'" 3033 done 3034 3035 SSD+=( "${ID}:${BASE}?${SCOPE}" ) 3036 } 3037 3038 Man.addFunc delete_ssd '' '[+NAME?delete_ssd - Ask user which SSD to delete.] 3039 [+DESCRIPTION?Ask the user for the ID of the SSD to delete and remove it from \bSSD\b list.] 3040 [+ENVIRONMENT VARIABLES]{' "${ Man.varUsage SSD ; }" '} 3041 [+SEE ALSO?\bget_ans()\b.] 3042 ' 3043 function delete_ssd { 3044 get_ans 'Enter the service name of the SSD to delete (e.g. passwd):' 3045 [[ -z ${ANS} ]] && return 3046 3047 typeset X 3048 integer I FOUND=-1 MAX=${#SSD[@]} 3049 3050 for (( I=0; I < MAX ; I++ )); do 3051 X=${SSD[I]} 3052 [[ ${X%%:*} == ${ANS} ]] && FOUND=$I && break 3053 done 3054 if (( FOUND == -1 )); then 3055 Log.warn "Invalid service name: '${ANS}' not present in list" 3056 return 3057 fi 3058 (( FOUND == 0 )) && unset SSD[0] && return 3059 3060 # need to do a little bit more, since ksh allows to remove the 1st entry, 3061 # only (i.e. like "shift 1" for positional params) 3062 for (( I=FOUND; I > 0; I-- )); do 3063 SSD[${I}]="${SSD[I-1]}" 3064 done 3065 unset SSD[0] 3066 } 3067 3068 Man.addFunc modify_ssd '' '[+NAME?modify_ssd - Allow user to modify an SSD.] 3069 [+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.] 3070 [+ENVIRONMENT VARIABLES]{' "${ Man.varUsage SSD ; }" '} 3071 [+SEE ALSO?\bget_ans()\b, \bread\b(1).] 3072 ' 3073 function modify_ssd { 3074 get_ans 'Enter the service name of the SSD to modify:' 3075 [[ -z ${ANS} ]] && return 3076 3077 typeset ID BASE 3078 typeset -l SCOPE 3079 integer I FOUND=-1 MAX=${#SSD[@]} 3080 for (( I=0; I < MAX ; I++ )); do 3081 X=${SSD[I]} 3082 [[ ${X%%:*} == ${ANS} ]] && FOUND=$I && break 3083 done 3084 if (( FOUND == -1 )); then 3085 Log.warn "Invalid service name: '${ANS}'" 3086 return 0 3087 fi 3088 3089 # most users expect emacs edit behavior (don't have a proper profile) 3090 set -o emacs 3091 read -v X?'Current SSD: ' 3092 3093 # now verify 3094 ID=${X%%:*} 3095 BASE=${.sh.match#:} 3096 SCOPE=${BASE#*$'?'} 3097 BASE=${.sh.match%$'?'} 3098 if [[ -z ${ID} || -z ${BASE} || -z ${SCOPE} ]] || \ 3099 [[ ${SCOPE} != 'one' && ${SCOPE} != 'sub' ]] 3100 then 3101 Log.warn 'SSD is invalid. Change skipped' 3102 return 3103 fi 3104 SSD[${FOUND}]="${ID}:${BASE}?${SCOPE}" 3105 } 3106 3107 Man.addFunc reset_ssd '' '[+NAME?reset_ssd - Blank out current list of SSDs.] 3108 [+DESCRIPTION?Clear the \bSSD\b array.] 3109 [+ENVIRONMENT VARIABLES]{' "${ Man.varUsage SSD ; }" '} 3110 ' 3111 function reset_ssd { 3112 SSD=( ) 3113 unset SSD[0] # required otherwise it would contain a single '\n'. Bug? 3114 } 3115 3116 Man.addFunc display_ssd '' '[+NAME?display_ssd - Display current SSD list.] 3117 [+DESCRIPTION?Display the current \bSSD\b list and return, when the user hit the enter key.] 3118 [+ENVIRONMENT VARIABLES]{' "${ Man.varUsage SSD ; }" '} 3119 ' 3120 function display_ssd { 3121 typeset X 3122 print ' 3123 Current Service Search Descriptors: 3124 ==================================' 3125 for X in "${SSD[@]}" ; do 3126 print " ${X}" 3127 done 3128 print '\nHit return to continue.' 3129 read 3130 } 3131 3132 Man.addFunc prompt_ssd '' '[+NAME?prompt_ssd - Get SSDs from user.] 3133 [+DESCRIPTION?Ask the user whether to add/delete/modify/display one or more SSDs currently set and execute the corresponding action.] 3134 [+ENVIRONMENT VARIABLES]{' "${ Man.varUsage ANS ; }" '} 3135 [+SEE ALSO?\bdisplay_msg()\b, \bget_ans()\b, \badd_ssd()\b, \bdelete_ssd()\b, \bmodify_ssd()\b, \bdisplay_ssd()\b, \breset_ssd()\b.] 3136 ' 3137 function prompt_ssd { 3138 if get_confirm 'Do you wish to setup Service Search Descriptors (y/n/h)?' \ 3139 'n' 'ssd_help' 3140 then 3141 return 3142 fi 3143 3144 # Display menu for SSD choices 3145 while : ; do 3146 display_msg prompt_ssd_menu 3147 get_ans 'Enter menu choice:' 'Quit' 3148 case "${ANS}" in 3149 [Qq] | Quit | quit) return ;; 3150 [Aa] | add) add_ssd ;; 3151 [Dd] | delete) delete_ssd ;; 3152 [Mm] | modify) modify_ssd ;; 3153 [Pp] | print | display) display_ssd ;; 3154 [Xx] | reset | clear) reset_ssd ;; 3155 [Hh] | Help | help) 3156 display_msg 'ssd_menu_help ' 3157 print ' Press return to continue.' 3158 read 3159 ;; 3160 *) Log.warn "Invalid choice: '${ANS}' please re-enter from menu!" ;; 3161 esac 3162 done 3163 } 3164 3165 ###################################################################### 3166 # End Of FUNCTIONS FOR Service Search Descriptor's 3167 ###################################################################### 3168 Man.addFunc prompt_config_info '' '[+NAME?prompt_config_info - Ask user for missing config info.] 3169 [+DESCRIPTION?Ask the user for the config info not yet available/read from an '"${PROG}"' config file.] 3170 [+ENVIRONMENT VARIABLES]{' "${ Man.varUsage INT STR SSD ; }" '} 3171 [+RETURN VALUES]{ 3172 [+0?on success (all info aquired).] 3173 [+>= 66?a fatal error occured.] 3174 } 3175 ' 3176 function prompt_config_info { 3177 # Prompt for DS server name 3178 get_ids_server 3179 3180 # Prompt for DS port number 3181 get_ids_port 3182 3183 # Check DS version for compatibility 3184 chk_ids_version || return 66 3185 3186 # Check if the DS supports the VLV 3187 chk_vlv_indexes || return 67 3188 3189 # Get the Directory manager DN and passwd 3190 get_dirmgr_dn 3191 get_dirmgr_pw 3192 3193 get_confirm 'Do you want to modify the DS timelimit for processing searches (y/n/h)?' 'n' 'tlim_help' 3194 INT[NEED_TIME]=$? 3195 if (( INT[NEED_TIME] )); then 3196 get_timelimit || return 70 3197 fi 3198 3199 get_confirm 'Do you want to modify the DS sizelimit - max. entries to return (y/n/h)?' 'n' 'slim_help' 3200 INT[NEED_SIZE]=$? 3201 if (( INT[NEED_SIZE] )); then 3202 get_sizelimit || return 71 3203 fi 3204 3205 3206 # LDAP CLIENT PROFILE SPECIFIC INFORMATION 3207 # (i.e. the fields that show up in the profile) 3208 get_domain 3209 get_basedn || return 68 3210 3211 # Check/Get Kerberos infos 3212 gssapi_setup 3213 3214 get_profile_name 3215 3216 if (( INT[LDAP_ENABLE_SHADOW_UPDATE] ));then 3217 setup_shadow_update || return 69 3218 return 0 3219 fi 3220 3221 get_srv_list 3222 get_pref_srv 3223 get_search_scope 3224 get_followref 3225 3226 # Store passwords in crypt format? 3227 get_want_crypt 3228 3229 # If cred is "anonymous", make auth == "none" 3230 get_cred_level 3231 [[ ${STR[LDAP_CRED_LEVEL]} != anonymous ]] && get_auth 'client' 3232 3233 get_confirm 'Do you want to setup any non-default Authentication Methods (y/n/h)?' 'n' 'srvauth_help' 3234 if (( $? )); then 3235 get_confirm 'Do you want to setup Authentication Methods for "pam_ldap" (y/n/h)?' 'n' 'pam_ldap_help' 3236 INT[NEED_SRVAUTH_PAM]=$? 3237 (( INT[NEED_SRVAUTH_PAM] )) && get_auth 'pam_ldap' 3238 3239 get_confirm 'Do you want to setup Authentication Methods for "keyserv" (y/n/h)?' 'n' 'keyserv_help' 3240 INT[NEED_SRVAUTH_KEY]=$? 3241 (( INT[NEED_SRVAUTH_KEY] )) && get_auth 'keyserv' 3242 3243 get_confirm 'Do you want to setup Authentication Methods for "passwd-cmd (y/n/h)?' 'n' 'passwd-cmd_help' 3244 INT[NEED_SRVAUTH_CMD]=$? 3245 (( INT[NEED_SRVAUTH_CMD] )) && get_auth 'passwd-cmd' 3246 fi 3247 3248 # Get client timeouts 3249 get_srch_time 3250 get_prof_ttl 3251 get_bind_limit 3252 3253 get_want_shadow_update 3254 3255 reset_ssd 3256 prompt_ssd 3257 3258 show_vars 3259 3260 Log.printMarker 3261 return 0 3262 } 3263 ###################################################################### 3264 # End Of FUNCTIONS FOR prompt_config_info() 3265 ###################################################################### 3266 3267 3268 ###################################################################### 3269 # FUNCTIONS FOR display_summary() START HERE. 3270 ###################################################################### 3271 Man.addFunc get_proxyagent '' '[+NAME?get_proxyagent - Ask user for the proxy agent DN and password.] 3272 [+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.] 3273 [+SEE ALSO?\bget_ans()\b.] 3274 ' 3275 function get_proxyagent { 3276 STR[LDAP_PROXYAGENT]="cn=proxyagent,ou=profile,${STR[LDAP_BASEDN]}" 3277 get_ans 'Enter DN for proxy agent:' "${STR[LDAP_PROXYAGENT]}" 3278 STR[LDAP_PROXYAGENT]="${ANS}" 3279 get_passwd 'Enter passwd for proxyagent:' 3280 STR[LDAP_PROXYAGENT_CRED]="${ANS}" 3281 } 3282 3283 Man.addFunc display_summary '' '[+NAME?display_summary - Display a summary of values entered and let the user modify values at will.] 3284 [+DESCRIPTION?Display a summary of all configuration relevant info and let the user re-enter data if needed.] 3285 ' 3286 function display_summary { 3287 # Create lookup table for function names. Needs to be in sync with 3288 # helpTag 'summary_menu' in display_msg()! 3289 typeset -a FN=( 'dummy' ) # dummy for commit and quit 3290 FN+=( 'get_domain' 'get_basedn' 'get_profile_name' ) 3291 FN+=( 'get_srv_list' 'get_pref_srv' 'get_search_scope' 'get_cred_level' ) 3292 FN+=( 'get_auth client' 'get_followref' ) 3293 FN+=( 'get_timelimit' 'get_sizelimit' 'get_want_crypt' ) 3294 FN+=( 'get_auth pam_ldap' 'get_auth keyserv' 'get_auth passwd-cmd' ) 3295 FN+=( 'get_srch_time' 'get_prof_ttl' 'get_bind_limit' ) 3296 FN+=( 'get_want_shadow_update' ) 3297 FN+=( 'prompt_ssd' ) 3298 3299 # Since menu prompt string is long, set here 3300 typeset PROMPT='Enter config value to change: (1-20 0=commit changes)' 3301 integer RES MAX=${#FN[@]} 3302 (( MAX-- )) 3303 3304 while : ; do 3305 # Display menu and get value in range 3306 get_menu_choice "${PROMPT}" '0' ${MAX} '0' '' 'summary_menu' 3307 RES=$? 3308 3309 # Make sure where not exiting 3310 (( RES == 0 )) && break # quit selected 3311 3312 # Call appropriate function from function table 3313 ${FN[RES]} 3314 done 3315 3316 # If credlevel is still proxy see if user wants a change? 3317 if [[ ${STR[LDAP_CRED_LEVEL]:0:5} == 'proxy' ]]; then 3318 if [[ ${STR[LDAP_AUTHMETHOD]} != none ]]; then 3319 INT[NEED_PROXY]=1 3320 get_proxyagent 3321 else 3322 Log.warn 'Since Authentication method is "none",' \ 3323 'credential level will be set to "anonymous"' 3324 STR[LDAP_CRED_LEVEL]='anonymous' 3325 fi 3326 fi 3327 3328 # If shadow update is enabled, set up administrator credential 3329 if (( INT[LDAP_ENABLE_SHADOW_UPDATE] )); then 3330 INT[NEED_ADMIN]=1 3331 if [[ ${STR[LDAP_CRED_LEVEL]} == 'self' && \ 3332 ${STR[LDAP_AUTHMETHOD]} == 'sasl/GSSAPI' ]]; 3333 then 3334 INT[NEED_HOSTACL]=1 3335 INT[NEED_ADMIN]=0 3336 fi 3337 if (( INT[NEED_ADMIN] )); then 3338 get_adminDN 3339 get_admin_pw 3340 fi 3341 fi 3342 3343 show_vars 3344 3345 get_confirm_nodef ' 3346 WARNING: About to start committing changes. (y=continue, n=EXIT)' 3347 if (( ! $? )); then 3348 Log.info 'Terminating setup without making changes at users request' 3349 return 1 3350 fi 3351 3352 Log.printMarker 3353 return 0 3354 } 3355 ###################################################################### 3356 # End Of FUNCTIONS FOR display_summary() 3357 ###################################################################### 3358 3359 Man.addFunc modify_cn '' '[+NAME?modify_cn - modify objectclass "ipNetwork" to RFC 2307bis.] 3360 [+DESCRIPTION?Change the cn from MUST to MAY in ipNetwork objectclass.] 3361 [+RETURN VALUES]{ 3362 [+0?on success.] 3363 [+>= 66?a fatal error occurred.] 3364 } 3365 [+SEE ALSO?\bldapmodify\b(1).] 3366 ' 3367 function modify_cn { 3368 getDSobjectclasses || return 66 3369 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'" 3370 3371 # bis-delta: MUST ( -cn ) MAY ( +cn ) 3372 typeset DEF=${OID2ODEF['1.3.6.1.1.1.2.7']} 3373 if [[ -n ${DEF} ]]; then 3374 DEF=${DEF##*MUST*([[:space:]])} 3375 if [[ ${DEF:0:9} == 'ipNetwork' ]]; then 3376 # assume no need to fix, old definition would start with a '(' 3377 showProgress 'Schema definition of ipNetwork ok (RFC2307bis).' 3378 return 0 3379 fi 3380 fi 3381 3382 nextFile modify $0 3383 print ' 3384 dn: cn=schema 3385 changetype: modify 3386 add: objectclasses 3387 objectclasses: ( 1.3.6.1.1.1.2.7 NAME '"${NEWDEF}"') 3388 ' > ${TMP[FILE]} 3389 3390 if ! ${LDAPMODIFY} ${CON_ARGS} "${AUTH_ARGS[@]}" -f ${TMP[FILE]} \ 3391 >&${TMPF[FD]} 2>&1 3392 then 3393 Log.fatal 'Schema update of ipNetwork to RFC2307bis failed' 3394 return 67 3395 fi 3396 showProgress 'Schema update of ipNetwork to RFC2307bis done.' 3397 OID2ODEF['1.3.6.1.1.1.2.7']="${NEWDEF}" 3398 return 0 3399 } 3400 3401 Man.addFunc modify_timelimit '' '[+NAME?modify_timelimit - Set the DS timelimit.] 3402 [+DESCRIPTION?Set the DS timelimit to \bINT[DS_TIMELIMIT]]\b.] 3403 [+RETURN VALUES]{ 3404 [+0?on success.] 3405 [+>= 66?a fatal error occurred.] 3406 } 3407 [+SEE ALSO?\bldapmodify\b(1).] 3408 ' 3409 function modify_timelimit { 3410 typeset NAME='nsslapd-timelimit' 3411 (( TMPF[IS_OPENDJ] )) && NAME='ds-cfg-time-limit' 3412 3413 nextFile modify $0 3414 print ' 3415 dn: cn=config 3416 changetype: modify 3417 replace: '"${NAME}"' 3418 '"${NAME}"': '"${INT[DS_TIMELIMIT]}"' s 3419 ' > ${TMP[FILE]} 3420 3421 if ! ${LDAPMODIFY} ${CON_ARGS} "${AUTH_ARGS[@]}" -f ${TMP[FILE]} \ 3422 >&${TMPF[FD]} 2>&1 3423 then 3424 Log.fatal 'Update of ${NAME} failed' 3425 return 66 3426 fi 3427 3428 showProgress "Changed ${NAME} to ${INT[DS_TIMELIMIT]} in 'cn=config'." 3429 return 0 3430 } 3431 3432 Man.addFunc modify_sizelimit '' '[+NAME?modify_sizelimit - Set the DS sizelimit.] 3433 [+DESCRIPTION?Set the DS sizelimit to \bINT[DS_SIZELIMIT]]\b.] 3434 [+RETURN VALUES]{ 3435 [+0?on success.] 3436 [+>= 66?a fatal error occurred.] 3437 } 3438 [+SEE ALSO?\bldapmodify\b(1).] 3439 ' 3440 function modify_sizelimit { 3441 typeset NAME='nsslapd-sizelimit' 3442 (( TMPF[IS_OPENDJ] )) && NAME='ds-cfg-size-limit' 3443 3444 nextFile modify $0 3445 print ' 3446 dn: cn=config 3447 changetype: modify 3448 replace: '"${NAME}"' 3449 '"${NAME}"': '"${INT[DS_SIZELIMIT]}"' 3450 ' > ${TMP[FILE]} 3451 3452 if ! ${LDAPMODIFY} ${CON_ARGS} "${AUTH_ARGS[@]}" -f ${TMP[FILE]} \ 3453 >&${TMPF[FD]} 2>&1 3454 then 3455 Log.fatal "Update of ${NAME} failed" 3456 return 66 3457 fi 3458 3459 showProgress "Changed ${NAME} to ${INT[DS_SIZELIMIT]} in 'cn=config'." 3460 return 0 3461 } 3462 3463 Man.addFunc modify_pwd_crypt '' '[+NAME?modify_pwd_crypt - modify the passwd storage scheme to use CRYPT.] 3464 [+DESCRIPTION?Set the default password policy of the DS to CRYPT.] 3465 [+RETURN VALUES]{ 3466 [+0?on success.] 3467 [+>= 66?a fatal error occurred.] 3468 } 3469 [+SEE ALSO?\bldapmodify\b(1).] 3470 ' 3471 function modify_pwd_crypt { 3472 # DS 5.2 moved passwordchangescheme off to a new data structure 3473 typeset -a INFO=( ${TMP[DS_INFO]} ) 3474 typeset DN='cn=config' TARGET='passwordStorageScheme' VALUE='CRYPT' MSG='' 3475 typeset FMT='dn: %s\nchangetype: modify\nreplace: %s\n%s: %s\n\n' 3476 nextFile modify $0 3477 3478 if (( TMPF[IS_OPENDJ] )); then 3479 DN='cn=Default Password Policy,cn=Password Policies,cn=config' 3480 TARGET='ds-cfg-default-password-storage-scheme' 3481 VALUE='cn=CRYPT,cn=Password Storage Schemes,cn=config' 3482 else 3483 # DSEE 3484 integer VER=$(( ${INFO[1]} * 1000 + ${INFO[2]} )) 3485 (( VER >= 5002 )) && DN='cn=Password Policy,cn=config' 3486 fi 3487 printf "${FMT}" "${DN}" "${TARGET}" "${TARGET}" "${VALUE}" >${TMP[FILE]} 3488 MSG="Changed '${TARGET}' to 'CRYPT'" 3489 3490 if (( TMPF[IS_OPENDJ] )); then 3491 if (( INT[NEED_CRYPT_IMPORT] )); then 3492 TARGET='ds-cfg-allow-pre-encoded-passwords' 3493 printf "${FMT}" "${DN}" "${TARGET}" "${TARGET}" 'true'>>${TMP[FILE]} 3494 MSG+="and '${TARGET}' to 'true'" 3495 fi 3496 DN='cn=Password Policy Import,cn=Plugins,cn=config' 3497 TARGET='ds-cfg-default-user-password-storage-scheme' 3498 printf "${FMT}" "${DN}" "${TARGET}" "${TARGET}" "${VALUE}">>${TMP[FILE]} 3499 fi 3500 3501 if ! ${LDAPMODIFY} ${CON_ARGS} "${AUTH_ARGS[@]}" -f ${TMP[FILE]} \ 3502 >&${TMPF[FD]} 2>&1 3503 then 3504 Log.fatal 'Update of passwordStorageScheme failed' 3505 return 66 3506 fi 3507 3508 showProgress "${MSG}." 3509 return 0 3510 } 3511 3512 Man.addFunc update_schema_attr '' '[+NAME?update_schema_attr - Update DS schema to support Naming Services.] 3513 [+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.] 3514 [+RETURN VALUES]{ 3515 [+0?on success.] 3516 [+>= 66?a fatal error occurred.] 3517 } 3518 [+SEE ALSO?\bldapmodify\b(1), \bgetDSattributes()\b.] 3519 ' 3520 function update_schema_attr { 3521 3522 # incorporate former 'keep_backward_compatibility()' 3523 getDSattributes || return 66 3524 typeset MEMBERGID_OID='1.3.6.1.4.1.42.2.27.5.1.30' 3525 typeset DEF=${ANAME2OID['membergid-oid']} 3526 [[ -n ${DEF} ]] && MEMBERGID_OID='memberGid-oid' 3527 typeset RFC822MAILMEMBER_OID='1.3.6.1.4.1.42.2.27.2.1.15' 3528 DEF=${ANAME2OID['rfc822mailmember-oid']} 3529 [[ -n ${DEF} ]] && RFC822MAILMEMBER_OID='rfc822mailMember-oid' 3530 3531 # EE and OD definitions seem to be compatible wrt. to Solaris. However, the 3532 # OD defs usually specifies explicitly the server behavior (what happens, if 3533 # EQUALITY|SUBSTR is not specified => caseIgnore[Substring]Match)) and 3534 # sometimes a different kind of String to use (see RFC 4517): 3535 # IA5 String: ASCII character in the range of 0..127 3536 # Octet String: byte collection (0..255), usually not human readable, 3537 # bytewise comparision 3538 # Directory String: an UTF-8 string 3539 # DN: basically an UTF-8 string, but with some constraints wrt. backslash 3540 # escaping '\0', '"', '+', ',', ';', '<', '>', and '\' 3541 # Boolean String: TRUE|FALSE 3542 typeset -A EE=( ) 3543 typeset -A OD=( ) 3544 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" 3545 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 3546 3547 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" 3548 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 3549 3550 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" 3551 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 3552 3553 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" 3554 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'" 3555 3556 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" 3557 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'" 3558 3559 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" 3560 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'" 3561 3562 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" 3563 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'" 3564 3565 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" 3566 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'" 3567 3568 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" 3569 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'" 3570 3571 EE[${RFC822MAILMEMBER_OID}]="'rfc822mailMember' SYNTAX 1.3.6.1.4.1.1466.115.121.1.26" 3572 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'" 3573 3574 EE['2.16.840.1.113730.3.1.30']="'mgrpRFC822MailMember' SYNTAX 1.3.6.1.4.1.1466.115.121.1.15" 3575 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'" 3576 3577 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" 3578 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 3579 3580 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" 3581 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 3582 3583 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" 3584 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 3585 3586 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" 3587 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 3588 3589 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" 3590 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 3591 3592 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" 3593 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 3594 3595 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" 3596 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 3597 3598 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" 3599 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'" 3600 3601 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" 3602 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 3603 3604 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" 3605 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'" 3606 3607 EE['1.3.6.1.4.1.42.2.27.5.1.25']="'SolarisSearchScope' SYNTAX 1.3.6.1.4.1.1466.115.121.1.15 SINGLE-VALUE" 3608 OD['1.3.6.1.4.1.42.2.27.5.1.25']="'SolarisSearchScope' DESC 'Scope to be used for search operations eg. \"NS_LDAP_SCOPE_BASE\", \"NS_LDAP_SCOPE_ONELEVEL\" or \"NS_LDAP_SCOPE_SUBTREE\"' EQUALITY caseIgnoreIA5Match SYNTAX 1.3.6.1.4.1.1466.115.121.1.26 SINGLE-VALUE X-ORIGIN 'Solaris Specific'" # Directory vs. IA5 String 3609 3610 EE['1.3.6.1.4.1.42.2.27.5.1.26']="'SolarisSearchTimeLimit' SYNTAX 1.3.6.1.4.1.1466.115.121.1.27 SINGLE-VALUE" 3611 OD['1.3.6.1.4.1.42.2.27.5.1.26']="'SolarisSearchTimeLimit' DESC 'Time Limit in seconds for search operations' EQUALITY integerMatch SYNTAX 1.3.6.1.4.1.1466.115.121.1.27 SINGLE-VALUE X-ORIGIN 'Solaris Specific'" 3612 3613 EE['1.3.6.1.4.1.42.2.27.5.1.27']="'SolarisPreferredServer' SYNTAX 1.3.6.1.4.1.1466.115.121.1.15" 3614 OD['1.3.6.1.4.1.42.2.27.5.1.27']="'SolarisPreferredServer' DESC 'Preferred LDAP Server address or network number' EQUALITY caseIgnoreIA5Match SYNTAX 1.3.6.1.4.1.1466.115.121.1.26 X-ORIGIN 'Solaris Specific'" # Directory vs. IA5 String 3615 3616 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" 3617 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 3618 3619 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" 3620 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 3621 3622 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" 3623 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 3624 3625 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" 3626 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 3627 3628 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" 3629 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 3630 3631 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" 3632 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 3633 3634 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" 3635 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 3636 3637 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" 3638 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 3639 3640 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" 3641 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 3642 3643 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" 3644 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'" 3645 3646 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" 3647 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 3648 3649 EE['1.3.6.1.4.1.42.2.27.5.1.13']="'SolarisAttrReserved1' SYNTAX 1.3.6.1.4.1.1466.115.121.1.15 SINGLE-VALUE" 3650 OD['1.3.6.1.4.1.42.2.27.5.1.13']="'SolarisAttrReserved1' DESC 'Reserved for future use' EQUALITY caseIgnoreIA5Match SYNTAX 1.3.6.1.4.1.1466.115.121.1.26 SINGLE-VALUE X-ORIGIN 'Solaris Specific'" # Directory vs. IA5 String 3651 3652 EE['1.3.6.1.4.1.42.2.27.5.1.14']="'SolarisAttrReserved2' SYNTAX 1.3.6.1.4.1.1466.115.121.1.15 SINGLE-VALUE" 3653 OD['1.3.6.1.4.1.42.2.27.5.1.14']="'SolarisAttrReserved2' DESC 'Reserved for future use' EQUALITY caseIgnoreIA5Match SYNTAX 1.3.6.1.4.1.1466.115.121.1.26 SINGLE-VALUE X-ORIGIN 'Solaris Specific'" # Directory vs. IA5 String 3654 3655 EE['1.3.6.1.4.1.42.2.27.5.1.1']="'SolarisProjectID' SYNTAX 1.3.6.1.4.1.1466.115.121.1.27 SINGLE-VALUE" 3656 OD['1.3.6.1.4.1.42.2.27.5.1.1']="'SolarisProjectID' DESC 'Unique ID for a Solaris Project entry' EQUALITY integerMatch SYNTAX 1.3.6.1.4.1.1466.115.121.1.27 SINGLE-VALUE X-ORIGIN 'Solaris Specific'" 3657 3658 EE['1.3.6.1.4.1.42.2.27.5.1.2']="'SolarisProjectName' SYNTAX 1.3.6.1.4.1.1466.115.121.1.26 SINGLE-VALUE" 3659 OD['1.3.6.1.4.1.42.2.27.5.1.2']="'SolarisProjectName' DESC 'Name of a Solaris Project Entry' EQUALITY caseExactIA5Match SYNTAX 1.3.6.1.4.1.1466.115.121.1.26 SINGLE-VALUE X-ORIGIN 'Solaris Specific'" 3660 3661 EE['1.3.6.1.4.1.42.2.27.5.1.3']="'SolarisProjectAttr' SYNTAX 1.3.6.1.4.1.1466.115.121.1.26" 3662 OD['1.3.6.1.4.1.42.2.27.5.1.3']="'SolarisProjectAttr' DESC 'Attributes of a Solaris Project entry' EQUALITY caseExactIA5Match SYNTAX 1.3.6.1.4.1.1466.115.121.1.26 SINGLE-VALUE X-ORIGIN 'Solaris Specific'" 3663 3664 EE[${MEMBERGID_OID}]="'memberGid' SYNTAX 1.3.6.1.4.1.1466.115.121.1.26" 3665 OD[${MEMBERGID_OID}]="'memberGid' DESC 'Posix Group Name' EQUALITY caseExactIA5Match SYNTAX 1.3.6.1.4.1.1466.115.121.1.26 X-ORIGIN 'Solaris Specific'" 3666 3667 EE['1.3.6.1.4.1.11.1.3.1.1.0']="'defaultServerList' DESC 'Default LDAP server host address used by a DUA' SYNTAX 1.3.6.1.4.1.1466.115.121.1.15 SINGLE-VALUE" 3668 OD['1.3.6.1.4.1.11.1.3.1.1.0']="'defaultServerList' DESC 'List of default servers' EQUALITY caseIgnoreMatch SUBSTR caseIgnoreSubstringsMatch SYNTAX 1.3.6.1.4.1.1466.115.121.1.15 SINGLE-VALUE X-ORIGIN 'RFC 4876'" 3669 3670 EE['1.3.6.1.4.1.11.1.3.1.1.1']="'defaultSearchBase' DESC 'Default LDAP base DN used by a DUA' SYNTAX 1.3.6.1.4.1.1466.115.121.1.12 SINGLE-VALUE" 3671 OD['1.3.6.1.4.1.11.1.3.1.1.1']="'defaultSearchBase' DESC 'Default base for searches' EQUALITY distinguishedNameMatch SYNTAX 1.3.6.1.4.1.1466.115.121.1.12 SINGLE-VALUE X-ORIGIN 'RFC 4876'" 3672 3673 EE['1.3.6.1.4.1.11.1.3.1.1.2']="'preferredServerList' DESC 'Preferred LDAP server host addresses to be used by a DUA' SYNTAX 1.3.6.1.4.1.1466.115.121.1.15 SINGLE-VALUE" 3674 OD['1.3.6.1.4.1.11.1.3.1.1.2']="'preferredServerList' DESC 'List of preferred servers' EQUALITY caseIgnoreMatch SUBSTR caseIgnoreSubstringsMatch SYNTAX 1.3.6.1.4.1.1466.115.121.1.15 SINGLE-VALUE X-ORIGIN 'RFC 4876'" 3675 3676 EE['1.3.6.1.4.1.11.1.3.1.1.3']="'searchTimeLimit' DESC 'Maximum time in seconds a DUA should allow for a search to complete' SYNTAX 1.3.6.1.4.1.1466.115.121.1.27 SINGLE-VALUE" 3677 OD['1.3.6.1.4.1.11.1.3.1.1.3']="'searchTimeLimit' DESC 'Maximum time an agent or service allows for a search to complete' EQUALITY integerMatch ORDERING integerOrderingMatch SYNTAX 1.3.6.1.4.1.1466.115.121.1.27 SINGLE-VALUE X-ORIGIN 'RFC 4876'" 3678 3679 EE['1.3.6.1.4.1.11.1.3.1.1.4']="'bindTimeLimit' DESC 'Maximum time in seconds a DUA should allow for the bind operation to complete' SYNTAX 1.3.6.1.4.1.1466.115.121.1.27 SINGLE-VALUE" 3680 OD['1.3.6.1.4.1.11.1.3.1.1.4']="'bindTimeLimit' DESC 'Maximum time an agent or service allows for a bind operation to complete' EQUALITY integerMatch ORDERING integerOrderingMatch SYNTAX 1.3.6.1.4.1.1466.115.121.1.27 SINGLE-VALUE X-ORIGIN 'RFC 4876'" 3681 3682 EE['1.3.6.1.4.1.11.1.3.1.1.5']="'followReferrals' DESC 'Tells DUA if it should follow referrals returned by a DSA search result' SYNTAX 1.3.6.1.4.1.1466.115.121.1.15 SINGLE-VALUE" 3683 OD['1.3.6.1.4.1.11.1.3.1.1.5']="'followReferrals' DESC 'An agent or service does or should follow referrals' EQUALITY booleanMatch SYNTAX 1.3.6.1.4.1.1466.115.121.1.7 SINGLE-VALUE X-ORIGIN 'RFC 4876'" # Directory vs. Boolean String 3684 3685 EE['1.3.6.1.4.1.11.1.3.1.1.6']="'authenticationMethod' DESC 'A keystring which identifies the type of authentication method used to contact the DSA' SYNTAX 1.3.6.1.4.1.1466.115.121.1.15 SINGLE-VALUE" 3686 OD['1.3.6.1.4.1.11.1.3.1.1.6']="'authenticationMethod' DESC 'Identifies the types of authentication methods either used, required, or provided by a service or peer' EQUALITY caseIgnoreMatch SUBSTR caseIgnoreSubstringsMatch SYNTAX 1.3.6.1.4.1.1466.115.121.1.15 SINGLE-VALUE X-ORIGIN 'RFC 4876'" 3687 3688 EE['1.3.6.1.4.1.11.1.3.1.1.7']="'profileTTL' DESC 'Time to live before a client DUA should re-read this configuration profile' SYNTAX 1.3.6.1.4.1.1466.115.121.1.27 SINGLE-VALUE" 3689 OD['1.3.6.1.4.1.11.1.3.1.1.7']="'profileTTL' DESC 'Time to live, in seconds, before a profile is considered stale' EQUALITY integerMatch ORDERING integerOrderingMatch SYNTAX 1.3.6.1.4.1.1466.115.121.1.27 SINGLE-VALUE X-ORIGIN 'RFC 4876'" 3690 3691 EE['1.3.6.1.4.1.11.1.3.1.1.14']="'serviceSearchDescriptor' DESC 'LDAP search descriptor list used by Naming-DUA' SYNTAX 1.3.6.1.4.1.1466.115.121.1.26" 3692 OD['1.3.6.1.4.1.11.1.3.1.1.14']="'serviceSearchDescriptor' DESC 'Specifies search descriptors required, used, or supported by a particular service or agent' EQUALITY caseExactMatch SUBSTR caseExactSubstringsMatch SYNTAX 1.3.6.1.4.1.1466.115.121.1.15 X-ORIGIN 'RFC 4876'" # IA5 vs. Directory String 3693 3694 EE['1.3.6.1.4.1.11.1.3.1.1.9']="'attributeMap' DESC 'Attribute mappings used by a Naming-DUA' SYNTAX 1.3.6.1.4.1.1466.115.121.1.15" 3695 OD['1.3.6.1.4.1.11.1.3.1.1.9']="'attributeMap' DESC 'Attribute 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 3696 3697 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" 3698 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 3699 3700 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" 3701 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 3702 3703 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" 3704 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 3705 3706 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" 3707 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'" 3708 3709 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" 3710 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'" 3711 3712 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" 3713 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'" 3714 3715 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" 3716 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'" 3717 3718 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" 3719 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'" 3720 3721 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" 3722 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'" 3723 3724 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" 3725 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'" 3726 3727 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" 3728 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'" 3729 3730 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" 3731 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'" 3732 3733 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" 3734 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'" 3735 3736 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}" 3737 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'" 3738 3739 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" 3740 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'" 3741 3742 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" 3743 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'" 3744 3745 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}" 3746 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'" 3747 3748 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}" 3749 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'" 3750 3751 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}" 3752 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'" 3753 3754 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" 3755 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'" 3756 3757 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}" 3758 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'" 3759 3760 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" 3761 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'" 3762 3763 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" 3764 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'" 3765 3766 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}" 3767 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'" 3768 3769 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" 3770 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'" 3771 3772 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}" 3773 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'" 3774 3775 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}" 3776 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'" 3777 3778 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}" 3779 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'" 3780 3781 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}" 3782 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'" 3783 3784 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}" 3785 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'" 3786 3787 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" 3788 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'" 3789 3790 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" 3791 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'" 3792 3793 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" 3794 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'" 3795 3796 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" 3797 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'" 3798 3799 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" 3800 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'" 3801 3802 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}" 3803 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'" 3804 3805 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}" 3806 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'" 3807 3808 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}" 3809 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'" 3810 3811 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}" 3812 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'" 3813 3814 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" 3815 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'" 3816 3817 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" 3818 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'" 3819 3820 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" 3821 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'" 3822 3823 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" 3824 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'" 3825 3826 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" 3827 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'" 3828 3829 # obey CLI syntax force option (if any) 3830 if (( TMPF[SYNTAX] == 1 )); then 3831 typeset -n MAP=OD 3832 elif (( TMPF[SYNTAX] == 2 )); then 3833 typeset -n MAP=EE 3834 elif (( TMPF[IS_OPENDJ] )); then 3835 typeset -n MAP=OD 3836 else 3837 typeset -n MAP=EE 3838 fi 3839 3840 typeset TODO='' OID X 3841 for OID in ${!MAP[@]}; do 3842 [[ -n ${OID2ADEF[${OID}]} ]] && continue 3843 X="${MAP[${OID}]}" 3844 TODO+='attributetypes: ( '"${OID} NAME ${X}"' )\n' 3845 OID2ADEF["${OID}"]="${X}" 3846 X=${X:1} 3847 X=${X%%"'"*} # we have no aliases above 3848 ANAME2OID["${X}"]="${OID}" 3849 done 3850 3851 if [[ -z ${TODO} ]]; then 3852 X='Schema contains all required attribute definitions' 3853 else 3854 nextFile add $0 3855 print 'dn: cn=schema\nchangetype: modify\nadd: attributetypes' \ 3856 "\n${TODO}" >${TMP[FILE]} 3857 if ! ${LDAPMODIFY} ${CON_ARGS} "${AUTH_ARGS[@]}" -f ${TMP[FILE]} \ 3858 >&${TMPF[FD]} 2>&1 3859 then 3860 Log.fatal 'Adding attribute definitions to schema failed' 3861 return 67 3862 fi 3863 X='Schema attribute definitions added' 3864 fi 3865 showProgress "${X}." 3866 return 0 3867 } 3868 3869 Man.addFunc update_schema_obj '' '[+NAME?update_schema_obj - Update DS schema to support Naming Services.] 3870 [+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.] 3871 [+RETURN VALUES]{ 3872 [+0?on success.] 3873 [+>= 66?a fatal error occurred.] 3874 } 3875 [+SEE ALSO?\bupdate_schema_attr()\b, \bldapmodify\b(1).] 3876 ' 3877 function update_schema_obj { 3878 getDSobjectclasses || return 66 3879 3880 typeset -A EE=( ) 3881 typeset -A OD=( ) 3882 EE['1.3.6.1.1.1.2.14']="'NisKeyObject' SUP top MUST "'( cn $ nisPublickey $ nisSecretkey ) MAY ( uidNumber $ description )' 3883 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'" 3884 3885 EE['1.3.6.1.1.1.2.15']="'nisDomainObject' SUP top MUST nisDomain" 3886 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'" 3887 3888 EE['1.3.6.1.1.1.2.16']="'automountMap' SUP top MUST automountMapName MAY description" 3889 OD['1.3.6.1.1.1.2.16']="'automountMap' SUP top STRUCTURAL MUST ( automountMapName ) MAY description X-ORIGIN 'draft-howard-rfc2307bis'" 3890 3891 EE['1.3.6.1.1.1.2.17']="'automount' SUP top MUST "'( automountKey $ automountInformation ) MAY description' 3892 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'" 3893 3894 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 )' 3895 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'" 3896 3897 EE['2.16.840.1.113730.3.2.4']="'mailGroup' SUP top MUST mail MAY "'( cn $ mgrpRFC822MailMember )' 3898 OD['2.16.840.1.113730.3.2.4']="'mailGroup' SUP top STRUCTURAL MUST mail MAY "'( cn $ mgrpRFC822MailMember )'" X-ORIGIN 'Solaris Specific'" 3899 3900 EE['1.3.6.1.4.1.42.2.27.1.2.5']="'nisMailAlias' SUP top MUST cn MAY rfc822mailMember" 3901 OD['1.3.6.1.4.1.42.2.27.1.2.5']="'nisMailAlias' SUP top MUST cn MAY rfc822mailMember X-ORIGIN 'Solaris Specific'" 3902 3903 EE['1.3.6.1.4.1.42.2.27.1.2.6']="'nisNetId' SUP top MUST cn MAY "'( nisNetIdUser $ nisNetIdGroup $ nisNetIdHost )' 3904 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'" 3905 3906 EE['1.3.6.1.4.1.42.2.27.5.2.2']="'SolarisAuditUser' SUP top AUXILIARY MAY "'( SolarisAuditAlways $ SolarisAuditNever )' 3907 OD['1.3.6.1.4.1.42.2.27.5.2.2']="'SolarisAuditUser' SUP top AUXILIARY MAY "'( SolarisAuditAlways $ SolarisAuditNever )'" X-ORIGIN 'Solaris Specific'" 3908 3909 EE['1.3.6.1.4.1.42.2.27.5.2.3']="'SolarisUserAttr' SUP top AUXILIARY MAY "'( SolarisUserQualifier $ SolarisAttrReserved1 $ SolarisAttrReserved2 $ SolarisAttrKeyValue )' 3910 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'" 3911 3912 EE['1.3.6.1.4.1.42.2.27.5.2.4']="'SolarisAuthAttr' SUP top MUST cn MAY "'( SolarisAttrReserved1 $ SolarisAttrReserved2 $ SolarisAttrShortDesc $ SolarisAttrLongDesc $ SolarisAttrKeyValue )' 3913 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'" 3914 3915 EE['1.3.6.1.4.1.42.2.27.5.2.5']="'SolarisProfAttr' SUP top MUST cn MAY "'( SolarisAttrReserved1 $ SolarisAttrReserved2 $ SolarisAttrLongDesc $ SolarisAttrKeyValue )' 3916 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'" 3917 3918 EE['1.3.6.1.4.1.42.2.27.5.2.6']="'SolarisExecAttr' SUP top AUXILIARY MAY "'( SolarisKernelSecurityPolicy $ SolarisProfileType $ SolarisAttrReserved1 $ SolarisAttrReserved2 $ SolarisProfileID $ SolarisAttrKeyValue )' 3919 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'" 3920 3921 EE['1.3.6.1.4.1.42.2.27.5.2.1']="'SolarisProject' SUP top MUST "'( SolarisProjectID $ SolarisProjectName ) MAY ( memberUid $ memberGid $ description $ SolarisProjectAttr )' 3922 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'" 3923 3924 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 )' 3925 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'" 3926 3927 EE['1.3.18.0.2.6.2549']="'slpService' DESC 'DUMMY definition' SUP top MUST objectclass" 3928 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'" 3929 3930 EE['1.3.18.0.2.6.254']="'slpServicePrinter' DESC 'Service Location Protocol (SLP) information.' SUP slpService AUXILIARY" 3931 OD['1.3.18.0.2.6.254']="'slpServicePrinter' DESC 'Service Location Protocol (SLP) information.' SUP slpService AUXILIARY X-ORIGIN 'RFC 3712'" 3932 3933 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 )' 3934 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' )" 3935 3936 EE['1.3.18.0.2.6.255']="'printerService' DESC 'Printer information.' SUP printerAbstract STRUCTURAL MAY "'( printer-uri $ printer-xri-supported )' 3937 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'" 3938 3939 EE['1.3.18.0.2.6.257']="'printerServiceAuxClass' DESC 'Printer information.' SUP printerAbstract AUXILIARY MAY "'( printer-uri $ printer-xri-supported )' 3940 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'" 3941 3942 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 )' 3943 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'" 3944 3945 EE['1.3.18.0.2.6.253']="'printerLPR' DESC 'LPR information.' SUP top AUXILIARY MUST printer-name MAY printer-aliases" 3946 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'" 3947 3948 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 )' 3949 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'" 3950 3951 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 )' 3952 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'" 3953 3954 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" 3955 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'" 3956 3957 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" 3958 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'" 3959 3960 # obey CLI syntax force option (if any) 3961 if (( TMPF[SYNTAX] == 1 )); then 3962 typeset -n MAP=OD 3963 elif (( TMPF[SYNTAX] == 2 )); then 3964 typeset -n MAP=EE 3965 elif (( TMPF[IS_OPENDJ] )); then 3966 typeset -n MAP=OD 3967 else 3968 typeset -n MAP=EE 3969 fi 3970 3971 # we need to preserve order, so 2 passes 3972 typeset TODO='' OID 3973 for OID in ${!MAP[@]}; do 3974 [[ -n ${OID2ODEF[${OID}]} ]] && continue 3975 TODO+="${OID} " 3976 done 3977 3978 if [[ -z ${TODO} ]]; then 3979 X='Schema contains all required objectclass defintions' 3980 else 3981 # 2nd pass 3982 typeset X SORTED 3983 SORTED=${ print ${TODO// /$'\n'} | sort -n -t. ; } 3984 TODO='' TODO2='' 3985 for OID in ${SORTED} ; do 3986 X="${MAP[${OID}]}" 3987 OID2ODEF["${OID}"]="${X}" 3988 # 1.3.18.0.2.6.254 requires 1.3.18.0.2.6.2549 3989 # 1.3.18.0.2.6.255,1.3.18.0.2.6.257 require 1.3.18.0.2.6.258 3990 [[ ${OID} == '1.3.18.0.2.6.254' \ 3991 || ${OID} == '1.3.18.0.2.6.255' \ 3992 || ${OID} == '1.3.18.0.2.6.257' ]] \ 3993 && TODO2+='objectclasses: ( '"${OID} NAME ${X}"' )\n' \ 3994 || TODO+='objectclasses: ( '"${OID} NAME ${X}"' )\n' 3995 done 3996 3997 nextFile modify $0 3998 print 'dn: cn=schema\nchangetype: modify\nadd: objectclasses' \ 3999 "\n${TODO}${TODO2}" >${TMP[FILE]} 4000 if ! ${LDAPMODIFY} ${CON_ARGS} "${AUTH_ARGS[@]}" -f ${TMP[FILE]} \ 4001 >&${TMPF[FD]} 2>&1 4002 then 4003 Log.fatal 'Adding objectclass definitions to schema failed' 4004 return 67 4005 fi 4006 X='Schema objectclass definitions added' 4007 fi 4008 4009 showProgress "${X}." 4010 return 0 4011 } 4012 4013 Man.addFunc add_suffix '' '[+NAME?add_suffix - add suffix to DS if needed.] 4014 [+DESCRIPTION?Add a suffix to the DS if needed (\bTMPF[NEED_CREATE_SUFFIX]]\b is set) and optionally create the backend for it (\bTMPF[NEED_CREATE_BACKEND]]\b is set). Suffix entry and backend MUST be prepared by \bprep_create_sfx_entry()\b and \bprep_create_sfx_backend()\b correspondingly to have the following vars set:]{ 4015 [+STR[LDAP_SUFFIX]] ] 4016 [+STR[DS_DB]] ] 4017 [+TMP[SUFFIX_OBJ]] ] 4018 [+TMP[SUFFIX_ATT]] ] 4019 [+TMP[SUFFIX_VAL]] ] 4020 } 4021 [+RETURN VALUES]{ 4022 [+0?on success (suffix not needed or created successfully).] 4023 [+1?otherwise (unable to create suffix).] 4024 } 4025 [+SEE ALSO?\bldapadd\b(1), \bdisplay_msg()\b.] 4026 ' 4027 function add_suffix { 4028 if (( ! TMPF[NEED_CREATE_BACKEND] )); then 4029 showProgress "Database backend exists." 4030 else 4031 nextFile add "${0}-backend" 4032 if (( TMPF[IS_OPENDJ] )); then 4033 print ' 4034 dn: ds-cfg-backend-id='"${STR[DS_DB]}"',cn=Backends,cn=config 4035 ds-cfg-backend-id: '"${STR[DS_DB]}"' 4036 ds-cfg-base-dn: '"${STR[LDAP_SUFFIX]}"' 4037 objectclass: top 4038 objectclass: ds-cfg-backend 4039 objectclass: ds-cfg-local-db-backend 4040 ds-cfg-java-class: org.opends.server.backends.jeb.BackendImpl 4041 ds-cfg-enabled: true 4042 ds-cfg-writability-mode: enabled 4043 ds-cfg-preload-time-limit: 0 ms 4044 ds-cfg-compact-encoding: true 4045 ds-cfg-entries-compressed: false 4046 ds-cfg-index-entry-limit: 4000 4047 ds-cfg-db-directory: db 4048 ds-cfg-db-directory-permissions: 700 4049 ds-cfg-disk-low-threshold: 100 mb 4050 ds-cfg-disk-full-threshold: 20 mb 4051 ds-cfg-db-run-cleaner: true 4052 ds-cfg-db-cleaner-min-utilization: 50 4053 ds-cfg-db-logging-file-handler-on: true 4054 ds-cfg-db-logging-level: CONFIG 4055 ds-cfg-db-log-filecache-size: 100 4056 ds-cfg-db-log-file-max: 10 mb 4057 ds-cfg-db-cache-size: 0 b 4058 ds-cfg-db-cache-percent: 50 4059 ds-cfg-db-evictor-core-threads: 1 4060 ds-cfg-db-evictor-max-threads: 10 4061 ds-cfg-db-evictor-nodes-per-scan: 10 4062 ds-cfg-db-evictor-lru-only: true 4063 ds-cfg-db-evictor-keep-alive: 600 s 4064 ds-cfg-db-txn-no-sync: false 4065 ds-cfg-db-txn-write-no-sync: true 4066 ds-cfg-db-checkpointer-wakeup-interval: 30 s 4067 ds-cfg-db-checkpointer-bytes-interval: 20 mb 4068 ' >${TMP[FILE]} 4069 else 4070 print ' 4071 dn: cn="'"${STR[LDAP_SUFFIX]}"'",cn=mapping tree,cn=config 4072 objectclass: top 4073 objectclass: extensibleObject 4074 objectclass: nsMappingTree 4075 cn: '"${STR[LDAP_SUFFIX]}"' 4076 nsslapd-state: backend 4077 nsslapd-backend: '"${STR[DS_DB]}"' 4078 4079 dn: cn='"${STR[DS_DB]}"',cn=ldbm database,cn=plugins,cn=config 4080 objectclass: top 4081 objectclass: extensibleObject 4082 objectclass: nsBackendInstance 4083 cn: '"${STR[DS_DB]}"' 4084 nsslapd-suffix: '"${STR[LDAP_SUFFIX]}"' 4085 ' >${TMP[FILE]} 4086 fi 4087 4088 if ! ${LDAPMODIFY} -a ${CON_ARGS} "${AUTH_ARGS[@]}" -f ${TMP[FILE]} \ 4089 >&${TMPF[FD]} 2>&1 4090 then 4091 Log.fatal 'Unable to create backend' "'${STR[DS_DB]}'" \ 4092 'with suffix' "'${STR[LDAP_SUFFIX]}'" 'due to server error.' 4093 return 1 4094 fi 4095 showProgress "Database backend created." 4096 fi 4097 4098 if (( ! TMPF[NEED_CREATE_SUFFIX] )); then 4099 showProgress "Suffix exists." 4100 else 4101 nextFile add $0 4102 print ' 4103 dn: '"${STR[LDAP_SUFFIX]}"' 4104 objectclass: top 4105 objectclass: '"${TMP[SUFFIX_OBJ]}"' 4106 '"${TMP[SUFFIX_ATT]}: ${TMP[SUFFIX_VAL]}"' 4107 ' >${TMP[FILE]} 4108 if ! ${LDAPMODIFY} -a ${CON_ARGS} "${AUTH_ARGS[@]}" -f ${TMP[FILE]} \ 4109 >&${TMPF[FD]} 2>&1 4110 then 4111 Log.warn 'Unable to create entry' "'${STR[LDAP_SUFFIX]}'" 'of' \ 4112 "'${TMP[SUFFIX_OBJ]}'" 'class' 4113 return 1 4114 fi 4115 showProgress "Suffix added." 4116 fi 4117 return 0 4118 } 4119 4120 function add_suffix_aci { 4121 typeset PW_STATE PW_STATE_RO ANY_NO_READ SELF_NO_WRITE 4122 typeset ADMIN_DN ADMIN_GROUP_DN 4123 if (( TMPF[IS_OPENDJ] )); then 4124 # TBD: include Password Policy State extended ops ? 4125 4126 # password state information aka ds-pwp-*, pwd* and ds-pta-* WITHOUT 4127 # attrs, which are schema flagged with NO-USER-MODIFICATION 4128 PW_STATE=' ds-pwp-account-disabled || ds-pwp-account-expiration-time 4129 || ds-pwp-last-login-time || ds-pwp-password-changed-by-required-time 4130 || ds-pwp-password-policy-dn || ds-pwp-reset-time || ds-pwp-warned-time 4131 || ds-privilege-name || pwdReset' 4132 # password state information flagged with NO-USER-MODIFICATION 4133 # ds-pwp-password-expiration-time is the same as pwdExpirationTime 4134 # so save some parse time ;-) 4135 PW_STATE_RO=' pwdExpirationTime || pwdChangedTime || pwdGraceUseTime 4136 || pwdFailureTime || pwdHistory || pwdAccountLockedTime' 4137 # People using PassThroughAuthentication probably wish that too: 4138 #PW_STATE_RO+='|| ds-pta-cached-password || ds-pta-cached-password-time' 4139 4140 # passwords + password state 4141 ANY_NO_READ="userPassword || authPassword ||${PW_STATE}||${PW_STATE_RO}" 4142 # aci + search limits aka ds-rlim-* + RW password state/policy 4143 SELF_NO_WRITE='aci || ds-rlim-idle-time-limit 4144 || ds-rlim-lookthrough-limit || ds-rlim-size-limit || ds-rlim-time-limit 4145 || pwdPolicySubentry ||'"${PW_STATE}" 4146 ADMIN_DN='uid=admin,cn=Administrators' 4147 ADMIN_GROUP_DN='cn=Administrators' 4148 ADMIN_DN+=',cn=admin data' 4149 ADMIN_GROUP_DN+=',cn=admin data' 4150 else 4151 # DSEE 4152 # password state information aka passwordObject 4153 PW_STATE=' accountUnlockTime || passwordAllowChangeTime 4154 || passwordExpWarned || passwordExpirationTime || passwordHistory 4155 || passwordRetryCount || retryCountResetTime' 4156 4157 # passwords + password state 4158 ANY_NO_READ="userPassword ||${PW_STATE}" 4159 # aci + search limits + password state/policy 4160 SELF_NO_WRITE='aci || nsIdleTimeout || nsLookThroughLimit 4161 || nsSizeLimit || nsTimeLimit || nsroledn || passwordPolicySubentry 4162 ||'"${PW_STATE}" 4163 ADMIN_DN='uid=admin,ou=Administrators' 4164 ADMIN_GROUP_DN='cn=Configuration Administrators,ou=Groups' 4165 ADMIN_DN+=',ou=TopologyManagement,o=NetscapeRoot' 4166 ADMIN_GROUP_DN+=',ou=TopologyManagement,o=NetscapeRoot"' 4167 fi 4168 4169 typeset -A RULES=( 4170 [SFX_ANYONE_ACI_NAME]='aci: (targetattr != "'"${ANY_NO_READ//$'\n'}"'") 4171 ( 4172 version 3.0; acl "'"${SFX_ANYONE_ACI_NAME}"'"; 4173 allow (read, search, compare) userdn = "ldap:///anyone"; 4174 )' 4175 [SFX_SELF_ACI_NAME]='aci: (targetattr != "'"${SELF_NO_WRITE//$'\n'}"'") 4176 ( 4177 version 3.0; acl "'"${SFX_SELF_ACI_NAME}"'"; 4178 allow (write) userdn = "ldap:///self"; 4179 )' 4180 [SFX_ADMIN_ACI_NAME]='aci: (targetattr = "*") 4181 ( 4182 version 3.0; acl "'"${SFX_ADMIN_ACI_NAME}"'"; 4183 allow (all) userdn = "ldap:///'"${ADMIN_DN}"'"; 4184 )' 4185 [SFX_ADMINGRP_ACI_NAME]='aci: (targetattr ="*") 4186 ( 4187 version 3.0; acl "'"${SFX_ADMINGRP_ACI_NAME}"'"; 4188 allow (all) groupdn = "ldap:///'"${ADMIN_GROUP_DN}"'"; 4189 )' 4190 ) 4191 4192 # Check and set if doesn't already exist 4193 typeset -a LIST=( ) 4194 getACIs LIST "${STR[LDAP_SUFFIX]}" 4195 4196 for ACI in "${!RULES[@]}" ; do 4197 typeset -n NAME=${ACI} 4198 [[ -z ${NAME} ]] && continue # if it has no name, ignore it 4199 PATTERN='acl[ ]+"?'"${NAME}"'"?' 4200 findACI LIST "${NAME}" "${PATTERN}" 'Suffix' || continue # exists 4201 nextFile add "${0}-${NAME}" 4202 print ' 4203 dn: '"${STR[LDAP_SUFFIX]}"' 4204 changetype: modify 4205 add: aci 4206 '"${RULES[${ACI}]}"' 4207 ' >${TMP[FILE]} 4208 if ! ${LDAPMODIFY} -a ${CON_ARGS} "${AUTH_ARGS[@]}" -f ${TMP[FILE]} \ 4209 >&${TMPF[FD]} 2>&1 4210 then 4211 Log.warn "Adding '${NAME}' suffix ACI failed" 4212 return 1 4213 fi 4214 showProgress "Suffix ACI '${NAME}' added." 4215 done 4216 return 0 4217 } 4218 4219 Man.addFunc add_base_objects '' '[+NAME?add_base_objects - add possibly missing, necessary base objects.] 4220 [+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.] 4221 [+RETURN VALUES]{ 4222 [+0?on success (all entries available or created successfully).] 4223 [+>= 66?a fatal error occured.] 4224 } 4225 [+SEE ALSO?\bnormalizeDN()\b, \bldapsearch\b(1), \bldapmodify\b(1).] 4226 ' 4227 function add_base_objects { 4228 # typeset -A STR 4229 # STR[LDAP_BASEDN]=dc=x,dc=my,dc=do,dc=main,dc=de 4230 # STR[LDAP_SUFFIX]=dc=do,dc=main,dc=de 4231 4232 # Convert to lower case for basename. 4233 typeset LC_DN=','${ normalizeDN "${STR[LDAP_BASEDN]}" l ; } 4234 typeset LC_SFX=','${ normalizeDN "${STR[LDAP_SUFFIX]}" l ; } 4235 4236 # first, test that baseDN ends with suffix 4237 if [[ ${LC_DN: -${#LC_SFX}} != ${LC_SFX} ]]; then 4238 # should not happen since check_basedn_suffix() succeeded 4239 Log.fatal "Invalid suffix '${LC_SFX}' for Base DN '${LC_DN}'" 4240 return 66 4241 fi 4242 # Save the stuff before LC_SFX w/o leading ',' -> further called prefix 4243 LC_DN=${LC_DN:1:${#LC_DN}-${#LC_SFX}-1} 4244 4245 # Remove redundant spaces around ',' and '=' first from LDAP_BASEDN 4246 typeset DN=${ normalizeDN ${STR[LDAP_BASEDN]} ; } 4247 4248 # Now LC_DN and DN differ at most in lower vs. uppercase character and 4249 # we can get the prefix of the baseDN by just copying corresponding chars 4250 DN=",${DN:0:${#LC_DN}}" 4251 4252 if [[ ${DN} == ',' ]]; then 4253 X='No need to create a DN component (baseDN equals suffix)' 4254 else 4255 typeset LAST=${ normalizeDN ${STR[LDAP_SUFFIX]} ; } DC KEY VAL CLASS 4256 X="Created DN components for ${DN#,}" 4257 while [[ -n ${DN} ]]; do 4258 # Get trailing key=val (DC) from DN and strip it off 4259 DN=${DN%,*} 4260 DC="${.sh.match#,}" 4261 LAST="${DC},${LAST}" 4262 4263 # Check if entry exists first, if so, skip to next. 4264 ${LDAPSEARCH} ${CON_ARGS} -b "${LAST}" -s base \ 4265 'objectclass=*' >/dev/null 2>&1 && continue 4266 4267 VAL=${DC#*=} 4268 KEY=${.sh.match%=} 4269 # Determine the objectclass for the entry. 4270 CLASS=${ get_objectclass ${KEY} ; } 4271 if [[ -z ${CLASS} ]]; then 4272 Log.fatal "Unable to determine objectClass for '${KEY}'." \ 4273 "Please create the following entry and re-run ${PROG}:" \ 4274 "${KEY}=${VAL},${LAST}" 4275 return 66 4276 fi 4277 4278 nextFile add "${0}-${LAST}" 4279 print ' 4280 dn: '"${LAST}"' 4281 '"${KEY}: ${VAL}"' 4282 objectClass: top 4283 objectClass: '"${CLASS}"' 4284 ' > ${TMP[FILE]} 4285 4286 if ! ${LDAPMODIFY} -a ${CON_ARGS} "${AUTH_ARGS[@]}" -f ${TMP[FILE]}\ 4287 >&${TMPF[FD]} 2>&1 4288 then 4289 Log.fatal "Update of base objects '${DC}' failed" 4290 return 66 4291 fi 4292 done 4293 fi 4294 showProgress "${X}." 4295 return 0 4296 } 4297 4298 Man.addFunc set_nisdomain '' '[+NAME?set_nisdomain - Add the NisDomainObject to the Base DN.] 4299 [+DESCRIPTION?Add a NisDomainObject with nisdomain \bSTR[LDAP_DOMAIN]]\b to the \bSTR[LDAP_BASEDN]]\b unless there is already one.] 4300 [+RETURN VALUES]{ 4301 [+0?on success.] 4302 [+>= 66?a fatal error occured.] 4303 } 4304 [+SEE ALSO?\bldapsearch\b(1), \bldapmodify\b(1).] 4305 ' 4306 function set_nisdomain { 4307 # Check if nisDomain is already set 4308 ${LDAPSEARCH} ${CON_ARGS} "${AUTH_ARGS[@]}" -b "${STR[LDAP_BASEDN]}" \ 4309 -s base 'objectclass=*' 2>/dev/null | \ 4310 while read LINE ; do 4311 if [[ ${LINE} == ~(Ei)^nisDomain= ]]; then 4312 showProgress 'NisDomainObject was already set for' \ 4313 "'${STR[LDAP_BASEDN]}'." 4314 return 0 4315 fi 4316 done 4317 4318 nextFile modify $0 4319 print ' 4320 dn: '"${STR[LDAP_BASEDN]}"' 4321 changetype: modify 4322 objectclass: nisDomainObject 4323 nisdomain: '"${STR[LDAP_DOMAIN]}"' 4324 ' >${TMP[FILE]} 4325 4326 if ! ${LDAPMODIFY} ${CON_ARGS} "${AUTH_ARGS[@]}" -f ${TMP[FILE]} \ 4327 >&${TMPF[FD]} 2>&1 4328 then 4329 Log.fatal "Update of NisDomainObject in '${STR[LDAP_BASEDN]}' failed" 4330 return 66 4331 fi 4332 4333 showProgress "NisDomainObject added to '${STR[LDAP_BASEDN]}'." 4334 return 0 4335 } 4336 4337 Man.addFunc add_new_containers '' '[+NAME?add_new_containers - Add top level containers to the base DN.] 4338 [+DESCRIPTION?Add the Name Service Switch top level containers to the \bSTR[LDAP_BASEDN]]\b unless they already exist.] 4339 [+RETURN VALUES]{ 4340 [+0?on success.] 4341 [+>= 66?a fatal error occured.] 4342 } 4343 [+SEE ALSO?\b/etc/nsswitch.ldap\b, \bldapsearch\b(1), \bldapmodify\b(1).] 4344 ' 4345 function add_new_containers { 4346 typeset OU 4347 4348 for OU in people group rpc protocols networks netgroup \ 4349 aliases hosts services ethers profile printers projects \ 4350 SolarisAuthAttr SolarisProfAttr Timezone ipTnet 4351 do 4352 if ${LDAPSEARCH} ${CON_ARGS} "${AUTH_ARGS[@]}" \ 4353 -b "ou=${OU},${STR[LDAP_BASEDN]}" -s base 'objectclass=*' \ 4354 >/dev/null 2>&1 4355 then 4356 showProgress "'ou=${OU}' exists." 4357 continue 4358 fi 4359 4360 nextFile add "${0}-${OU}" 4361 print ' 4362 dn: ou='"${OU},${STR[LDAP_BASEDN]}"' 4363 ou: '"${OU}"' 4364 objectClass: top 4365 objectClass: organizationalUnit 4366 ' > ${TMP[FILE]} 4367 4368 if ! ${LDAPMODIFY} -a ${CON_ARGS} "${AUTH_ARGS[@]}" -f ${TMP[FILE]} \ 4369 >&${TMPF[FD]} 2>&1 4370 then 4371 Log.fatal "Adding 'ou=${OU}' failed" 4372 return 66 4373 fi 4374 showProgress "'ou=${OU}' added." 4375 done 4376 4377 return 0 4378 } 4379 4380 Man.addFunc add_auto_maps '' '[+NAME?add_auto_maps - Add the automount map entries.] 4381 [+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.] 4382 [+RETURN VALUES]{ 4383 [+0?on success.] 4384 [+>= 66?a fatal error occured.] 4385 } 4386 [+SEE ALSO?\bldapsearch\b(1), \bldapmodify\b(1).] 4387 ' 4388 function add_auto_maps { 4389 # AUTO_MAPS for maps to create 4390 typeset AUTO_MAPS="auto_home auto_direct auto_master auto_shared" MAP 4391 4392 for MAP in ${AUTO_MAPS}; do 4393 # Check if automap already exist 4394 if ${LDAPSEARCH} ${CON_ARGS} "${AUTH_ARGS[@]}" \ 4395 -b "automountMapName=${MAP},${STR[LDAP_BASEDN]}" -s base \ 4396 'objectclass=*' >/dev/null 2>&1 4397 then 4398 showProgress "'${MAP}' automount exists." 4399 continue 4400 fi 4401 4402 nextFile add "${0}-${MAP}" 4403 print ' 4404 dn: automountMapName='"${MAP},${STR[LDAP_BASEDN]}"' 4405 automountMapName: '"${MAP}"' 4406 objectClass: top 4407 objectClass: automountMap 4408 ' > ${TMP[FILE]} 4409 4410 if ! ${LDAPMODIFY} -a ${CON_ARGS} "${AUTH_ARGS[@]}" -f ${TMP[FILE]} \ 4411 >&${TMPF[FD]} 2>&1 4412 then 4413 Log.fatal "Adding '${MAP}' automount map failed" 4414 return 66 4415 fi 4416 showProgress "'${MAP}' automount added." 4417 done 4418 4419 return 0 4420 } 4421 4422 Man.addFunc modify_top_aci '' '[+NAME?modify_top_aci - add a deny self modify ACI.] 4423 [+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.] 4424 [+ENVIRONMENT VARIABLES]{' "${ Man.varUsage USER_ACI_NAME ; }" '} 4425 [+RETURN VALUES]{ 4426 [+0?on success (ACI already exists or added successfully).] 4427 [+>= 66?a fatal error occured.] 4428 } 4429 [+SEE ALSO?\bgetACIs()\b, \bfindACI()\b, \bldapmodify\b(1).] 4430 ' 4431 function modify_top_aci { 4432 typeset PATTERN 4433 4434 typeset -a LIST=( ) 4435 getACIs LIST || return 66 4436 4437 # check, whether ACI already exists 4438 PATTERN='acl[ ]+"?('"${USER_ACI_NAME}|${USER_ACI_NAME// /_}"')"?' 4439 findACI LIST "${USER_ACI_NAME}" "${PATTERN}" || return 0 # exists 4440 4441 # Create LDIF for top level ACI 4442 nextFile modify $0 4443 print ' 4444 dn: '"${STR[LDAP_BASEDN]}"' 4445 changetype: modify 4446 add: aci 4447 aci: (targetattr = "cn || uid || uidNumber || gidNumber || homeDirectory || shadowLastChange || shadowMin || shadowMax || shadowWarning || shadowInactive || shadowExpire || shadowFlag || memberUid || SolarisAttrKeyValue || SolarisAttrReserved1 || SolarisAttrReserved2 || SolarisUserQualifier") 4448 ( 4449 version 3.0; acl "'"${USER_ACI_NAME}"'"; 4450 deny (write) userdn = "ldap:///self"; 4451 ) 4452 ' > ${TMP[FILE]} 4453 4454 if ! ${LDAPMODIFY} ${CON_ARGS} "${AUTH_ARGS[@]}" -f ${TMP[FILE]} \ 4455 >&${TMPF[FD]} 2>&1 4456 then 4457 Log.fatal 'Deny user to change non-password attributes failed' 4458 return 66 4459 fi 4460 4461 showProgress "Self modify for non-password attributes disabled." 4462 return 0 4463 } 4464 4465 Man.addFunc add_vlv_aci '' '[+NAME?add_vlv_aci - Add ACI for VLV.] 4466 [+DESCRIPTION?Add the global ACI to allow everyone read-only access to VLVs.] 4467 [+ENVIRONMENT VARIABLES]{' "${ Man.varUsage VLV_ACI_NAME ; }" '} 4468 [+RETURN VALUES]{ 4469 [+0?on success (ACI added successfully).] 4470 [+>= 66?a fatal error occured.] 4471 } 4472 [+SEE ALSO?\bldapmodify\b(1).] 4473 ' 4474 function add_vlv_aci { 4475 # VLV Request = 2.16.840.1.113730.3.4.9 4476 typeset RULE='version 3.0; acl "'"${VLV_ACI_NAME}"'";\n\t' 4477 RULE+='allow (read,search,compare) userdn = "ldap:///anyone";' 4478 typeset DN='oid=2.16.840.1.113730.3.4.9,cn=features,cn=config' 4479 (( TMPF[IS_OPENDJ] )) && DN='cn=Access Control Handler,cn=config' 4480 4481 typeset -a LIST=( ) 4482 getACIs LIST "${DN}" 'Global' || return 66 4483 4484 PATTERN='acl[ ]+"?('"${VLV_ACI_NAME}|${VLV_ACI_NAME// /_}"')"?' 4485 findACI LIST "${VLV_ACI_NAME}" "${PATTERN}" 'Global' || return 0 4486 4487 nextFile modify $0 4488 if (( TMPF[IS_OPENDJ] )); then 4489 print ' 4490 dn: '"${DN}"' 4491 changetype: modify 4492 add: ds-cfg-global-aci 4493 ds-cfg-global-aci: (targetcontrol = "2.16.840.1.113730.3.4.9")(targetattr != "aci") 4494 ( 4495 '"${RULE}"' 4496 ) 4497 ' > ${TMP[FILE]} 4498 else 4499 print ' 4500 dn: '"${DN}"' 4501 changetype: modify 4502 replace: aci 4503 aci: (targetattr != "aci") 4504 ( 4505 '"${RULE}"' 4506 ) 4507 ' > ${TMP[FILE]} 4508 fi 4509 4510 if ! ${LDAPMODIFY} ${CON_ARGS} "${AUTH_ARGS[@]}" -f ${TMP[FILE]} \ 4511 >&${TMPF[FD]} 2>&1 4512 then 4513 Log.fatal "Adding '${VLV_ACI_NAME}' global ACI failed" 4514 return 66 4515 fi 4516 4517 showProgress "Global ACI '${VLV_ACI_NAME}' added." 4518 return 0 4519 } 4520 4521 Man.addFunc add_proxyagent '' '[+NAME?add_proxyagent - Add proxy agent user to DS.] 4522 [+DESCRIPTION?Add the proxy agent user \bSTR[LDAP_PROXYAGENT]]\b to the DS unless it already exists to allow nameservice access to the server.] 4523 [+RETURN VALUES]{ 4524 [+0?on success (entry already exists or added successfully).] 4525 [+>= 66?a fatal error occured.] 4526 } 4527 [+SEE ALSO?\bldapmodify\b(1).] 4528 ' 4529 function add_proxyagent { 4530 # Check if proxy agent already exists 4531 if ${LDAPSEARCH} ${CON_ARGS} "${AUTH_ARGS[@]}" \ 4532 -b "${STR[LDAP_PROXYAGENT]}" -s base 'objectclass=*' >/dev/null 2>&1 4533 then 4534 showProgress "Proxyagent identity exists." 4535 return 0 4536 fi 4537 4538 # Get cn and sn names from LDAP_PROXYAGENT. 4539 typeset NAME="${STR[LDAP_PROXYAGENT]%%,*}" 4540 NAME=${NAME#*=} 4541 4542 # Add the entry 4543 nextFile add $0 4544 print ' 4545 dn: '"${STR[LDAP_PROXYAGENT]}"' 4546 cn: '"${NAME}"' 4547 sn: '"${NAME}"' 4548 objectclass: top 4549 objectclass: person 4550 userpassword: '"${STR[LDAP_PROXYAGENT_CRED]}"' 4551 ' > ${TMP[FILE]} 4552 4553 if ! ${LDAPMODIFY} -a ${CON_ARGS} "${AUTH_ARGS[@]}" -f ${TMP[FILE]} \ 4554 >&${TMPF[FD]} 2>&1 4555 then 4556 Log.fatal 'Adding proxyagent identity failed' 4557 return 66 4558 fi 4559 4560 showProgress 'Proxyagent identity added.' 4561 return 0 4562 } 4563 4564 Man.addFunc add_entry_by_DN '' '[+NAME?add_entry_by_DN - Add an ldif file by DN.] 4565 [+DESCRIPTION?Add the entries in the given LDIF \afile\a to the DS unless a base entry for \aDN\a already exists.] 4566 [+RETURN VALUES]{ 4567 [+0?on success (\aDN\a already exists or added successfully).] 4568 [+1?on error (failed to add entries).] 4569 } 4570 [+SEE ALSO?\bldapsearch\b(1), \bldapadd\b(1).] 4571 \n\n\aDN\a \afile\a 4572 ' 4573 function add_entry_by_DN { 4574 if ${LDAPSEARCH} ${CON_ARGS} "${AUTH_ARGS[@]}" -b "$1" -s base \ 4575 'objectclass=*' >/dev/null 2>&1 4576 then 4577 showProgress "'${1}' exists." 4578 return 0 4579 fi 4580 if ${LDAPMODIFY} -a ${CON_ARGS} "${AUTH_ARGS[@]}" -f "$2" >&${TMPF[FD]} 2>&1 4581 then 4582 showProgress "'${1}' added." 4583 return 0 4584 fi 4585 Log.fatal "Adding '${1}' failed" 4586 return 1 4587 } 4588 4589 Man.addFunc add_id_mapping_rules '' '[+NAME?add_id_mapping_rules - Add Kerberos principal to DN mapping rules to the DS.] 4590 [+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.] 4591 [+SEE ALSO?\badd_entry_by_DN()\b.] 4592 ' 4593 function add_id_mapping_rules { 4594 Log.info 'Adding Kerberos principal to DN mapping rules ...' 4595 typeset C_DN='cn=GSSAPI,cn=identity mapping,cn=config' OC='nsContainer' 4596 if (( TMPF[IS_OPENDJ] )); then 4597 # we create a new branch instead of reusing the default, possibly unused 4598 # 'cn=Regular Expression,cn=Identity Mappers,cn=config' GSSAPI ID mapper 4599 C_DN='cn=GSSAPI,cn=Identity Mappers,cn=config' 4600 OC='ds-cfg-branch' 4601 fi 4602 4603 nextFile add "${0}-krbPrincipal" 4604 print ' 4605 dn: cn='"${C_DN}"' 4606 objectClass: top 4607 objectClass: '"${OC}"' 4608 cn: GSSAPI 4609 ' > ${TMP[FILE]} 4610 add_entry_by_DN "${C_DN}" ${TMP[FILE]} || return 4611 4612 typeset H_CN="host_auth_${STR[LDAP_KRB_REALM]}" 4613 typeset H_DN="cn=${H_CN}, ${C_DN}" 4614 nextFile add "${0}-krbHostAuth" 4615 if (( TMPF[IS_OPENDJ] )); then 4616 print ' 4617 # For now (2.6.0) OpenDJ supports a single-valued ds-cfg-identity-mapper, only. 4618 # So either you merge this and the next Identity Mapper to something more useful 4619 # together or just enable one of them until it gets fixed. For more information 4620 # see https://bugster.forgerock.org/jira/browse/OPENDJ-521 4621 dn: '"${H_DN}"' 4622 cn: '"${H_CN}"' 4623 objectClass: top 4624 objectClass=ds-cfg-identity-mapper 4625 objectClass=ds-cfg-regular-expression-identity-mapper 4626 ds-cfg-java-class=org.opends.server.extensions.RegularExpressionIdentityMapper 4627 ds-cfg-enabled=false 4628 ds-cfg-match-base-dn=ou=hosts,'"${STR[LDAP_BASEDN]}"' 4629 ds-cfg-match-pattern=host\/(.*).'"${STR[LDAP_DOMAIN]}@${STR[LDAP_KRB_REALM]}"' 4630 ds-cfg-replace-pattern=$1 4631 ds-cfg-match-attribute=cn 4632 ' > ${TMP[FILE]} 4633 else 4634 print ' 4635 dn: '"${H_DN}"' 4636 objectClass: top 4637 objectClass: nsContainer 4638 objectClass: dsIdentityMapping 4639 objectClass: dsPatternMatching 4640 cn: '"${H_CN}"' 4641 dsMatching-pattern: ${Principal} 4642 dsMatching-regexp: host\/(.*).'"${STR[LDAP_DOMAIN]}@${STR[LDAP_KRB_REALM]}"' 4643 dsSearchBaseDN: ou=hosts,'"${STR[LDAP_BASEDN]}"' 4644 dsSearchFilter: (&(objectClass=ipHost)(cn=$1)) 4645 dsSearchScope: one 4646 ' > ${TMP[FILE]} 4647 fi 4648 add_entry_by_DN "${H_DN}" ${TMP[FILE]} 4649 4650 typeset U_CN="user_auth_${STR[LDAP_KRB_REALM]}" 4651 typeset U_DN="cn=${U_CN}, ${C_DN}" 4652 nextFile add "${0}-krbUserAuth" 4653 if (( TMPF[IS_OPENDJ] )); then 4654 print ' 4655 dn: '"${U_DN}"' 4656 cn: '"${U_CN}"' 4657 objectClass: top 4658 objectClass=ds-cfg-identity-mapper 4659 objectClass=ds-cfg-regular-expression-identity-mapper 4660 ds-cfg-java-class=org.opends.server.extensions.RegularExpressionIdentityMapper 4661 ds-cfg-enabled=true 4662 ds-cfg-match-base-dn=ou=People,'"${STR[LDAP_BASEDN]}"' 4663 ds-cfg-match-pattern=(.*)@'"${STR[LDAP_KRB_REALM]}"' 4664 ds-cfg-replace-pattern=$1 4665 ds-cfg-match-attribute=uid 4666 ' > ${TMP[FILE]} 4667 else 4668 print ' 4669 dn: '"${U_DN}"' 4670 objectClass: top 4671 objectClass: nsContainer 4672 objectClass: dsIdentityMapping 4673 objectClass: dsPatternMatching 4674 cn: '"${U_CN}"' 4675 dsMatching-pattern: ${Principal} 4676 dsMatching-regexp: (.*)@'"${STR[LDAP_KRB_REALM]}"' 4677 dsMappedDN: uid=$1,ou=People,'"${STR[LDAP_BASEDN]}"' 4678 ' > ${TMP[FILE]} 4679 fi 4680 add_entry_by_DN "${U_DN}" ${TMP[FILE]} 4681 } 4682 4683 Man.addFunc modify_userpassword_acl_for_gssapi '' '[+NAME?modify_userpassword_acl_for_gssapi - Allow hosts and user to read password(s).] 4684 [+DESCRIPTION?Modify ACL to allow hosts to read all and a user to read its own password only, when sasl/GSSAPI bind is used.] 4685 [+RETURN VALUES]{ 4686 [+0?on success (entries already exist or added successfully).] 4687 [+>= 66?a fatal error occured.] 4688 } 4689 [+SEE ALSO?\badd_entry_by_DN()\b.] 4690 ' 4691 function modify_userpassword_acl_for_gssapi { 4692 typeset P_DN="ou=People,${STR[LDAP_BASEDN]}" 4693 typeset H_DN="ou=Hosts,${STR[LDAP_BASEDN]}" 4694 4695 if ! ${LDAPSEARCH} ${CON_ARGS} "${AUTH_ARGS[@]}" -b "${P_DN}" -s base \ 4696 'objectclass=*' > /dev/null 2>&1 4697 then 4698 Log.verbose "'${P_DN}' does not exist" 4699 4700 nextFile add $0 4701 print ' 4702 dn: '"${P_DN}"' 4703 ou: People 4704 objectClass: top 4705 objectClass: organizationalUnit 4706 ' > ${TMP[FILE]} 4707 4708 add_entry_by_DN "${P_DN}" ${TMP[FILE]} 4709 else 4710 Log.verbose "'${P_DN}' already exists" 4711 fi 4712 typeset TARGET='userPassword' PATTERN 4713 (( TMPF[IS_OPENDJ] )) && TARGET+=' || authPassword' 4714 4715 typeset -A RULES=( 4716 [SELF_GSS_ACI_NAME]='aci: (targetattr = "'"${TARGET}"'") 4717 ( 4718 version 3.0; acl self-read-pwd; 4719 allow (read,search) userdn="ldap:///self" and authmethod="sasl GSSAPI"; 4720 )' 4721 [HOST_GSS_ACI_NAME]='aci: (targetattr = "'"${TARGET}"'") 4722 ( 4723 version 3.0; acl host-read-pwd; 4724 allow (read,search) userdn="ldap:///cn=*+ipHostNumber=*,ou=Hosts,'"${STR[LDAP_BASEDN]}"'" and authmethod="sasl GSSAPI"; 4725 )' 4726 ) 4727 4728 typeset -a LIST=( ) 4729 getACIs LIST "${P_DN}" || return 66 4730 4731 for ACI in "${!RULES[@]}" ; do 4732 typeset -n NAME=${ACI} 4733 [[ -z ${NAME} ]] && continue # if it has no name, ignore it 4734 PATTERN='acl[ ]+"?'"${NAME}"'"?' 4735 findACI LIST "${NAME}" "${PATTERN}" 'ou=people' || continue # exists 4736 nextFile add "${0}-${NAME}" 4737 print ' 4738 dn: '"${P_DN}"' 4739 changetype: modify 4740 add: aci 4741 '"${RULES[${ACI}]}"' 4742 ' > ${TMP[FILE]} 4743 4744 if ! ${LDAPMODIFY} ${CON_ARGS} "${AUTH_ARGS[@]}" -f ${TMP[FILE]} \ 4745 >&${TMPF[FD]} 2>&1 4746 then 4747 Log.warn "Adding '${NAME}' ou=people ACI failed." 4748 return 67 4749 fi 4750 showProgress "ou=people ACI '${NAME}' added." 4751 done 4752 return 0 4753 } 4754 4755 Man.addFunc add_profile '' '[+NAME?add_profile - Add client profile to server.] 4756 [+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:]{ 4757 [+?STR[LDAP_SERVER_LIST]] ] 4758 [+?STR[LDAP_SEARCH_SCOPE]] ] 4759 [+?STR[LDAP_CRED_LEVEL]] ] 4760 [+?STR[LDAP_AUTHMETHOD]] ] 4761 [+?INT[LDAP_FOLLOWREF]] ] 4762 [+?INT[LDAP_SEARCH_TIME_LIMIT]] ] 4763 [+?INT[LDAP_PROFILE_TTL]] ] 4764 [+?INT[LDAP_BIND_LIMIT]] ] 4765 [+?STR[LDAP_PREF_SRVLIST]] ] 4766 [+?STR[LDAP_SRV_AUTHMETHOD_PAM]] ] 4767 [+?STR[LDAP_SRV_AUTHMETHOD_KEY]] ] 4768 [+?STR[LDAP_SRV_AUTHMETHOD_CMD]] ] 4769 [+?SSD] 4770 } 4771 [+ENVIRONMENT VARIABLES]{' "${ Man.varUsage INT STR SSD ; }" '} 4772 [+RETURN VALUES]{ 4773 [+0?on success.] 4774 [+>= 66?fatal error.] 4775 } 4776 [+SEE ALSO?\bldapclient\b(1M), \bldapdelete\b(1), \bldapmodify\b(1).] 4777 ' 4778 function add_profile { 4779 # If profile name already exists, DELETE it, and add new one 4780 if ${LDAPSEARCH} ${CON_ARGS} "${AUTH_ARGS[@]}" \ 4781 -b "cn=${STR[LDAP_PROFILE_NAME]},ou=profile,${STR[LDAP_BASEDN]}" \ 4782 -s base 'objectclass=*' >/dev/null 2>&1 4783 then 4784 if (( ! TMPF[DEL_OLD_PROFILE] )); then 4785 Log.fatal "Adding client profile name '${STR[LDAP_PROFILE_NAME]}'" \ 4786 'failed (entry already exists)' 4787 return 66 4788 fi 4789 4790 nextFile delete $0 4791 print "cn=${STR[LDAP_PROFILE_NAME]},ou=profile,${STR[LDAP_BASEDN]}" \ 4792 >${TMP[FILE]} 4793 if ! ${LDAPDELETE} ${CON_ARGS} "${AUTH_ARGS[@]}" -f ${TMP[FILE]} \ 4794 >&${TMPF[FD]} 2>&1 4795 then 4796 Log.fatal 'Attempt to DELETE old client profile' \ 4797 "'${STR[LDAP_PROFILE_NAME]}' failed" 4798 return 67 4799 fi 4800 fi 4801 4802 # Build the "ldapclient genprofile" command string to execute 4803 typeset -a ARG=( ) 4804 ARG+=( genprofile '-a' "profileName=${STR[LDAP_PROFILE_NAME]}" ) 4805 4806 # Add required argument defaultSearchBase 4807 ARG+=( -a "defaultSearchBase=${STR[LDAP_BASEDN]}" ) 4808 4809 # Add optional parameters 4810 [[ -n ${STR[LDAP_SERVER_LIST]} ]] && \ 4811 ARG+=( -a "defaultServerList=${STR[LDAP_SERVER_LIST]}" ) 4812 [[ -n ${STR[LDAP_SEARCH_SCOPE]} ]] && \ 4813 ARG+=( -a "defaultSearchScope=${STR[LDAP_SEARCH_SCOPE]}" ) 4814 [[ -n ${STR[LDAP_CRED_LEVEL]} ]] && \ 4815 ARG+=( -a "credentialLevel=${STR[LDAP_CRED_LEVEL]}" ) 4816 [[ -n ${STR[LDAP_AUTHMETHOD]} ]] && \ 4817 ARG+=( -a "authenticationMethod=${STR[LDAP_AUTHMETHOD]}" ) 4818 (( INT[LDAP_FOLLOWREF] )) && X='TRUE"' || X='FALSE"' 4819 ARG+=( -a "followReferrals=${X}" ) 4820 (( INT[LDAP_SEARCH_TIME_LIMIT] )) && \ 4821 ARG+=( -a "searchTimeLimit=${INT[LDAP_SEARCH_TIME_LIMIT]}" ) 4822 (( INT[LDAP_PROFILE_TTL] )) && \ 4823 ARG+=( -a "profileTTL=${INT[LDAP_PROFILE_TTL]}" ) 4824 [[ -n ${INT[LDAP_BIND_LIMIT]} ]] && \ 4825 ARG+=( -a "bindTimeLimit=${INT[LDAP_BIND_LIMIT]}" ) 4826 [[ -n ${STR[LDAP_PREF_SRVLIST]} ]] && \ 4827 ARG+=( -a "preferredServerList=${STR[LDAP_PREF_SRVLIST]}" ) 4828 [[ -n ${STR[LDAP_SRV_AUTHMETHOD_PAM]} ]] && \ 4829 ARG+=(-a "serviceAuthenticationMethod=${STR[LDAP_SRV_AUTHMETHOD_PAM]}") 4830 [[ -n ${STR[LDAP_SRV_AUTHMETHOD_KEY]} ]] && \ 4831 ARG+=(-a "serviceAuthenticationMethod=${STR[LDAP_SRV_AUTHMETHOD_KEY]}") 4832 [[ -n ${STR[LDAP_SRV_AUTHMETHOD_CMD]} ]] && \ 4833 ARG+=(-a "serviceAuthenticationMethod=${STR[LDAP_SRV_AUTHMETHOD_CMD]}") 4834 4835 # Add SSDs 4836 typeset X 4837 for X in "${SSD[@]}" ; do 4838 ARG+=( -a "serviceSearchDescriptor=${X}" ) 4839 done 4840 4841 # Execute "ldapclient genprofile" to create profile 4842 nextFile add $0 4843 if ! ${LDAPCLIENT} "${ARG[@]}" >${TMP[FILE]} 2>${TMP[DIR]}/gen_profile.err 4844 then 4845 Log.fatal 'ldapclient genprofile failed' 4846 return 68 4847 fi 4848 4849 # Add the generated profile 4850 if ! ${LDAPMODIFY} -a ${CON_ARGS} "${AUTH_ARGS[@]}" -f ${TMP[FILE]} \ 4851 >&${TMPF[FD]} 2>&1 4852 then 4853 log.fatal 'Attempt to add profile failed!' 4854 return 69 4855 fi 4856 4857 showProgress 'Client profile generated and pushed to the DS.' 4858 return 0 4859 } 4860 4861 Man.addFunc checkTaskCompletion '' '[+NAME?checkTaskCompletion - Wait and check completion of a DS task.] 4862 [+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.] 4863 [+RETURN VALUES]{ 4864 [+0?on success.] 4865 [+1?task failed.] 4866 [+2?timeout has been hit, task not yet finished or has an unknown state.] 4867 } 4868 [+See Also?\bldapsearch\b(1)] 4869 \n\n\adn\a [\atimeout\a] 4870 ' 4871 function checkTaskCompletion { 4872 typeset DN="$1" STATUS='nstaskstatus=' TASKID X 4873 integer TIMEOUT=${2:-${INT[TASK_TIMEOUT]}} RESULT=2 SEEN 4874 (( TIMEOUT < 10 )) && TIMEOUT=60 4875 (( TMPF[IS_OPENDJ] )) && STATUS='ds-task-state=' 4876 TASKID=${DN%%,*} 4877 # Wait for task to finish, display current status. 4878 while (( TIMEOUT > 0 )) ; do 4879 SEEN=0 X='' 4880 ${LDAPSEARCH} ${CON_ARGS} "${AUTH_ARGS[@]}" \ 4881 -b "${DN}" -s base 'objectclass=*' ${STATUS%=} 2>/dev/null | \ 4882 while read LINE ; do 4883 [[ ${LINE%%,*} == ${TASKID} ]] && SEEN=1 && continue 4884 [[ -z ${X} && ${LINE:0:${#STATUS}} == ${STATUS} ]] && \ 4885 X=${LINE:${#STATUS}} 4886 done 4887 (( ! SEEN )) && break # an error occured 4888 print -n '.' 4889 [[ ${X} =~ Finished || ${X} == 'COMPLETED_SUCCESSFULLY' ]] && \ 4890 RESULT=0 && break 4891 [[ ${X:0:12} == 'Index failed' || ${X:016} == 'STOPPED_BY_ERROR' ]] && \ 4892 RESULT=1 && break 4893 sleep 1 4894 (( TIMEOUT-=1 )) 4895 done 4896 # DSEE removes non-recurring tasks automagically, OpenDJ not 4897 (( RESULT < 2 && TMPF[IS_OPENDJ] )) && \ 4898 ${LDAPDELETE} ${CON_ARGS} "${AUTH_ARGS[@]}" "${DN}" >&${TMPF[FD]} 2>&1 4899 return ${RESULT} 4900 } 4901 4902 Man.addFunc rebuildIndex '' '[+NAME?rebuildIndex - Rebuild a single index.] 4903 [+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.] 4904 [+RETURN VALUES]{ 4905 [+0?on success.] 4906 [+1?if a VLV index was specified, but the current DS is != OpenDJ/OpenDS.] 4907 [+2?if the task failed or did not complete within 60 seconds.] 4908 [+66?if scheduling a rebuild task on the DS failed.] 4909 } 4910 [+SEE ALSO?\bcheckTaskCompletion()\b, \bldapsearch\b(1), \bldapmodify\b(1).] 4911 \n\n\aname\a 4912 ' 4913 function rebuildIndex { 4914 typeset IDX=$1 4915 TASKNAME=${IDX}_${ date "+%Y_%m_%d_%H_%M_%S" ; } 4916 4917 if [[ ${IDX:0:4} == 'vlv.' ]] && (( ! TMPF[IS_OPENDJ] )) ; then 4918 Log.warn 'Non-OpenDJ/OpenDS servers do not support VLV rebuildTasks' 4919 return 1 4920 fi 4921 4922 nextFile add "${0}-task-${TASKNAME}" 4923 if (( TMPF[IS_OPENDJ] )); then 4924 # NOTE: rebuildTask requires ldif-import LDAP privilege. 4925 # It's a little bit inefficient: one could schedule a single task at 4926 # the end of the function and use 'rebuildall' or a space separated 4927 # list of all attributes in ds-task-rebuild-index to do it at once. 4928 DN='ds-task-id='"${TASKNAME}"',cn=Scheduled Tasks,cn=Tasks' 4929 print ' 4930 # rebuild-index --index '"${IDX}"' --baseDN '"${STR[LDAP_BASEDN]}"' \ 4931 # '"${CON_ARGS} ${AUTH_ARGS[@]}"' -X 4932 4933 dn: '"${DN}"' 4934 ds-task-id: '"${TASKNAME}"' 4935 objectClass: top 4936 objectClass: ds-task 4937 objectClass: ds-task-rebuild 4938 ds-task-class-name: org.opends.server.tasks.RebuildTask 4939 ds-task-rebuild-base-dn: '"${STR[LDAP_SUFFIX]}"' 4940 ds-task-rebuild-index: '"${IDX}"' 4941 #ds-task-rebuild-tmp-directory: 4942 ' > ${TMP[FILE]} 4943 else 4944 DN='cn='"${TASKNAME}"',cn=index,cn=tasks,cn=config' 4945 print ' 4946 dn: '"${DN}"' 4947 cn: '"${TASKNAME}"' 4948 objectclass: top 4949 objectclass: extensibleObject 4950 nsInstance: '"${STR[DS_DB]}"' 4951 nsIndexAttribute: '"${IDX}"' 4952 ' > ${TMP[FILE]} 4953 fi 4954 4955 if ! ${LDAPMODIFY} -a ${CON_ARGS} "${AUTH_ARGS[@]}" -f ${TMP[FILE]} \ 4956 >&${TMPF[FD]} 2>&1 4957 then 4958 Log.fatal "Adding task for '${IDX}' failed" 4959 return 66 4960 fi 4961 4962 checkTaskCompletion "${DN}" && print "" || { Log.warn 'FAILED'; return 2; } 4963 return 0 4964 } 4965 4966 Man.addFunc add_indexes '' '[+NAME?add_indexes - Add indexes.] 4967 [+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.] 4968 [+RETURN VALUES]{ 4969 [+-1?syntax error (insufficient parameters).] 4970 [+0?on success.] 4971 [+>= 66?fatal error.] 4972 } 4973 [+SEE ALSO?\bldapsearch\b(1), \bldapmodify\b(1).] 4974 \n\n\atypes\a \aattributes\a 4975 ' 4976 function add_indexes { 4977 if [[ -z ${STR[DS_DB]} ]]; then 4978 get_backend || return 66 4979 fi 4980 [[ -z $1 || -z $2 ]] && Log.warn "${.sh.fun}(): syntax error" && return -1 4981 4982 typeset -l TYPES=${1//,/ } # we allow comma separated lists as well 4983 typeset IDX=${2//,/ } 4984 4985 Log.verbose "Processing '${TYPES// /,}' indexes ..." 4986 4987 typeset DN="cn=index,cn=${STR[DS_DB]},cn=ldbm database,cn=plugins,cn=config" 4988 typeset LTYPE="nsIndexType: ${TYPES// /$'\n'nsIndexType: }" 4989 typeset ATTR='cn' OC='extensibleObject' DST TASKNAME LINE X 4990 integer SEEN 4991 if (( TMPF[IS_OPENDJ] )); then 4992 OC='ds-cfg-branch' 4993 DN="cn=Index,ds-cfg-backend-id=${STR[DS_DB]},cn=Backends,cn=config" 4994 ATTR='ds-cfg-attribute' 4995 LTYPE='' 4996 for X in ${TYPES}; do 4997 [[ $X == 'eq' ]] && LTYPE+='ds-cfg-index-type: equality\n' 4998 [[ $X == 'pres' ]] && LTYPE+='ds-cfg-index-type: presence\n' 4999 [[ $X == 'sub' ]] && LTYPE+='ds-cfg-index-type: substring\n' 5000 done 5001 fi 5002 5003 # check, whether Index container exists - if not, create it 5004 if ! ${LDAPSEARCH} ${CON_ARGS} "${AUTH_ARGS[@]}" -b "${DN}" \ 5005 -s base 'objectclass=*' > /dev/null 2>&1 5006 then 5007 nextFile add $0 5008 X=${DN%%,*} 5009 print ' 5010 dn: '"${DN}"' 5011 objectClass: top 5012 objectClass: '"${OC}"' 5013 cn: '"${X#*=}"' 5014 ' >${TMP[FILE]} 5015 if ! ${LDAPMODIFY} -a ${CON_ARGS} "${AUTH_ARGS[@]}" -f ${TMP[FILE]} \ 5016 >&${TMPF[FD]} 2>&1 5017 then 5018 Log.fatal 'Failed to add backend Index base' 5019 return 66 5020 fi 5021 fi 5022 5023 for DST in ${IDX} ; do 5024 # Check if entry exists first. If so, skip to next 5025 if ${LDAPSEARCH} ${CON_ARGS} "${AUTH_ARGS[@]}" \ 5026 -b "${ATTR}=${DST},${DN}" -s base 'objectclass=*' > /dev/null 2>&1 5027 then 5028 showProgress "'${DST}' index exists." 5029 continue 5030 fi 5031 5032 nextFile add "${0}-${DST}" 5033 if (( TMPF[IS_OPENDJ] )); then 5034 X=${LTYPE//'\n'/ } 5035 X=${X//: /:} 5036 print ' 5037 # dsconfig create-local-db-index '"${CON_ARGS} ${AUTH_ARGS[@]}"' -X \ 5038 # '"${X//ds-cfg-/--set }"' \ 5039 # --backend-name '"${STR[DS_DB]}"' --index-name '"${DST}"' 5040 5041 dn: ds-cfg-attribute='"${DST},${DN}"' 5042 ds-cfg-attribute: '"${DST}"' 5043 objectClass: top 5044 objectClass: ds-cfg-local-db-index 5045 '"${LTYPE}"' 5046 ' > ${TMP[FILE]} 5047 else 5048 print ' 5049 dn: cn='"${DST},${DN}"' 5050 objectClass: top 5051 objectClass: nsIndex 5052 cn: '"${DST}"' 5053 nsSystemIndex: false 5054 '"${LTYPE}"' 5055 ' > ${TMP[FILE]} 5056 fi 5057 if ! ${LDAPMODIFY} -a ${CON_ARGS} "${AUTH_ARGS[@]}" -f ${TMP[FILE]} \ 5058 >&${TMPF[FD]} 2>&1 5059 then 5060 Log.fatal "Adding '${DST}' backend index failed" 5061 return 67 5062 fi 5063 showProgress "'${DST}' backend index added." 5064 rebuildIndex ${DST} 5065 done 5066 return 0 5067 } 5068 5069 Man.addFunc add_eq_indexes '' '[+NAME?add_eq_indexes - Add indexes of type pres and eq.] 5070 [+DESCRIPTION?Add eq,pres indexes and schedule corresponding tasks to actually create the indexes unless they already exist.] 5071 [+RETURN VALUES]{ 5072 [+0?on success.] 5073 [+>= 66?fatal error.] 5074 } 5075 [+SEE ALSO?\badd_indexes()\b.] 5076 ' 5077 function add_eq_indexes { 5078 add_indexes "pres eq" \ 5079 "uidNumber ipNetworkNumber gidnumber oncrpcnumber automountKey" 5080 } 5081 5082 Man.addFunc add_sub_indexes '' '[+NAME?add_sub_indexes - Add indexes of type pres, eq and sub.] 5083 [+DESCRIPTION?Add eq,pres,sub indexes and schedule corresponding tasks to actually create the indexes unless they already exist.] 5084 [+RETURN VALUES]{ 5085 [+0?on success.] 5086 [+>= 66?fatal error.] 5087 } 5088 [+SEE ALSO?\badd_indexes()\b.] 5089 ' 5090 function add_sub_indexes { 5091 add_indexes "pres eq sub" \ 5092 "ipHostNumber membernisnetgroup nisnetgrouptriple" 5093 } 5094 5095 Man.addFunc add_vlv_indexes_OpenDJ '' '[+NAME?add_vlv_indexes_OpenDJ - Add VLV indexes to OpenDS/OpenDJ.] 5096 [+DESCRIPTION?OpenDS/OpenDJ specialized part of \badd_vlv_indexes()\b (pulled out for easier maintenance).] 5097 [+SEE ALSO?\badd_vlv_indexes()\b, https://blogs.oracle.com/kanthi/entry/ldap_paged_results_more] 5098 [+NOTES?\bpagedResultsControl\b and \bVLV\b is per default allowed for authenticated users, only. To check, try something like this:]{ 5099 [+?ldapsearch -r -j /tmp/pw -D "cn=Directory Manager" -h ldaphost \] 5100 [+? -b "cn=Access Control Handler,cn=config" "objectclass=*" | \] 5101 [+? egrep "1.2.840.113556.1.4.319|2.16.840.1.113730.3.4.9"] 5102 } 5103 \n\n\avname\a \afile\a 5104 ' 5105 function add_vlv_indexes_OpenDJ { 5106 typeset -n INDEX_TABLE=$1 5107 typeset OUT="$2" ENTRY IDX_NAME CN SCOPE='single-level' BASE FILTER 5108 5109 [[ ${STR[LDAP_SEARCH_SCOPE]} == sub ]] && SCOPE='subordinate-subtree' 5110 BACKEND="cn=VLV Index,ds-cfg-backend-id=${STR[DS_DB]},cn=Backends,cn=config" 5111 5112 # check, whether VLV Index container exists - if not, create it 5113 if ! ${LDAPSEARCH} ${CON_ARGS} "${AUTH_ARGS[@]}" -b "${BACKEND}" \ 5114 -s base 'objectclass=*' > /dev/null 2>&1 5115 then 5116 nextFile add $0 5117 print ' 5118 dn: '"${BACKEND}"' 5119 objectClass: top 5120 objectClass: ds-cfg-branch 5121 cn: VLV Index 5122 ' >${TMP[FILE]} 5123 if ! ${LDAPMODIFY} -a ${CON_ARGS} "${AUTH_ARGS[@]}" -f ${TMP[FILE]} \ 5124 >&${TMPF[FD]} 2>&1 5125 then 5126 Log.fatal 'Adding VLV Index base' 5127 return 66 5128 fi 5129 fi 5130 5131 integer COUNT=0 5132 # create index entries 5133 for ENTRY in "${INDEX_TABLE[@]}" ; do 5134 typeset -a F=( ${ENTRY} ) # split columns 5135 5136 IDX_NAME="${STR[LDAP_DOMAIN]}.get${F[0]}" 5137 BASE="ou=${F[2]},${STR[LDAP_BASEDN]}" 5138 [[ ${F[2]} =~ = ]] && BASE="${BASE:3}" # cut out ou= 5139 FILTER="objectClass=${F[3]}" 5140 [[ ${F[3]:0:1} == '&' ]] && FILTER="&(objectClass=${F[3]:2}" 5141 5142 if ${LDAPSEARCH} ${CON_ARGS} "${AUTH_ARGS[@]}" -s base \ 5143 -b "ds-cfg-name=${IDX_NAME},${BACKEND}" 'objectclass=*' \ 5144 > /dev/null 2>&1 5145 then 5146 showProgress "'${IDX_NAME}' VLV index exists." 5147 continue 5148 fi 5149 5150 nextFile add "${0}-${IDX_NAME}" 5151 print ' 5152 # dsconfig create-local-db-vlv-index '"${CON_ARGS} ${AUTH_ARGS[@]}"' -X \ 5153 # --backend-name "'"${STR[DS_DB]}"'" \ 5154 # --set "base-dn:'"${BASE}"'" \ 5155 # --index-name '"${IDX_NAME}"' --set "sort-order:cn uid" \ 5156 # --set "scope:'"${SCOPE}"'" --set "filter:'"${FILTER}"'" 5157 5158 dn: ds-cfg-name='"${IDX_NAME},${BACKEND}"' 5159 objectClass: top 5160 objectClass: ds-cfg-local-db-vlv-index 5161 ds-cfg-name: '"${IDX_NAME}"' 5162 ds-cfg-base-dn: '"${BASE}"' 5163 ds-cfg-scope: '"${SCOPE}"' 5164 ds-cfg-filter: '"${FILTER}"' 5165 ds-cfg-sort-order: cn uid 5166 aci: (targetattr = "*") 5167 ( 5168 version 3.0; acl "Config"; 5169 allow (read,search,compare) userdn="ldap:///anyone"; 5170 ) 5171 ' > ${TMP[FILE]} 5172 5173 if ! ${LDAPMODIFY} -a ${CON_ARGS} "${AUTH_ARGS[@]}" -f ${TMP[FILE]} \ 5174 >&${TMPF[FD]} 2>&1 5175 then 5176 Log.fatal "Adding '${IDX_NAME}' VLV index failed" 5177 return 67 5178 fi 5179 showProgress "'${IDX_NAME}' VLV index added." 5180 5181 if rebuildIndex "vlv.${IDX_NAME}" ; then 5182 (( COUNT++ )) 5183 else 5184 print "rebuild-index -b '${STR[LDAP_SUFFIX]}' " \ 5185 "--index 'vlv.${IDX_NAME}'" >>${OUT} 5186 fi 5187 done 5188 if (( COUNT )); then 5189 # Bug or feature: Even if scheduled as task, VLV idx rebuild leaves 5190 # the index in degraded state. So: 5191 showProgress 'Rebuilding degraded indexes.' 5192 if ! rebuildIndex rebuilddegraded ; then 5193 print "svcadm disable opendj@VERS@\n" \ 5194 "rebuild-index -b '${STR[LDAP_SUFFIX]}' --rebuildDegraded\n" \ 5195 "svcadm enable opendj@VERS@" 5196 >>${OUT} 5197 fi 5198 fi 5199 } 5200 5201 Man.addFunc add_vlv_indexes_DSEE '' '[+NAME?add_vlv_indexes_DSEE - Add VLV indexes to DSEE.] 5202 [+DESCRIPTION?DSEE specialized part of \badd_vlv_indexes()\b (pulled out for easier maintenance).] 5203 [+SEE ALSO?\b\badd_vlv_indexes()\b.] 5204 \n\n\avname\a \afile\a 5205 ' 5206 function add_vlv_indexes_DSEE { 5207 typeset -n INDEX_TABLE=$1 5208 typeset OUT="$2" ENTRY IDX_NAME CN SCOPE=1 BASE FILTER 5209 5210 typeset INFO=( ${TMP[DS_INFO]} ) 5211 integer MAJOR=${INFO[${#INFO[@]}-2]} 5212 typeset INSTANCE="@serverInstance@" LINE 5213 ${LDAPSEARCH} -v ${CON_ARGS} "${AUTH_ARGS[@]}" -b 'cn=config' \ 5214 -s base 'objectclass=*' nsslapd-instancedir 2>/dev/null | \ 5215 while read LINE ; do 5216 [[ ${LINE:0:20} == 'nsslapd-instancedir=' ]] && LINE=${LINE:20} && break 5217 done 5218 if (( MAJOR < 6 )); then 5219 # DSEE 5.x only. 5220 [[ ${LINE} =~ slapd- ]] && LINE=${LINE#*/} && INSTANCE=${LINE#*-} 5221 else 5222 # 6+ - the instance path 5223 INSTANCE="${LINE}" 5224 fi 5225 [[ ${STR[LDAP_SEARCH_SCOPE]} == sub ]] && SCOPE=2 5226 BACKEND="cn=${STR[DS_DB]},cn=ldbm database,cn=plugins,cn=config" 5227 5228 # create index entries 5229 for ENTRY in "${INDEX_TABLE[@]}" ; do 5230 typeset -a F=( ${ENTRY} ) # split columns 5231 5232 IDX_NAME="${STR[LDAP_DOMAIN]}.get${F[0]}" 5233 CN="${STR[LDAP_DOMAIN]}_${F[1]}_vlv_index" 5234 BASE="ou=${F[2]},${STR[LDAP_BASEDN]}" 5235 [[ ${F[2]} =~ = ]] && BASE="${BASE:3}" # cut out ou= 5236 FILTER="objectClass=${F[3]}" 5237 [[ ${F[3]:0:1} == '&' ]] && FILTER="&(objectClass=${F[3]:2}" 5238 5239 if ${LDAPSEARCH} ${CON_ARGS} "${AUTH_ARGS[@]}" -s base \ 5240 -b "cn=${IDX_NAME},cn=${CN},${BACKEND}" 'objectclass=*' \ 5241 > /dev/null 2>&1 5242 then 5243 showProgress "'${IDX_NAME}' VLV index exists." 5244 continue 5245 fi 5246 5247 nextFile add "${0}-${IDX_NAME}" 5248 print ' 5249 dn: '"cn=${CN},${BACKEND}"' 5250 objectClass: top 5251 objectClass: vlvSearch 5252 cn: '"${CN}"' 5253 vlvbase: '"${BASE}"' 5254 vlvscope: '${SCOPE}' 5255 vlvfilter: ('"${FILTER}"') 5256 aci: (target = "ldap:///'"cn=${CN},${BACKEND}"'") (targetattr = "*") 5257 ( 5258 version 3.0; acl "Config"; 5259 allow (read,search,compare) userdn="ldap:///anyone"; 5260 ) 5261 5262 dn: cn='"${IDX_NAME},cn=${CN},${BACKEND}"' 5263 cn: '"${IDX_NAME}"' 5264 vlvSort: cn uid 5265 objectclass: top 5266 objectclass: vlvIndex 5267 ' > ${TMP[FILE]} 5268 5269 if ! ${LDAPMODIFY} -a ${CON_ARGS} "${AUTH_ARGS[@]}" -f ${TMP[FILE]} \ 5270 >&${TMPF[FD]} 2>&1 5271 then 5272 Log.fatal "Adding '${IDX_NAME}' VLV index failed" 5273 return 66 5274 fi 5275 showProgress "'${IDX_NAME}' VLV index added." 5276 5277 if (( MAJOR < 6 )); then 5278 # DSEE 5.x 5279 print "directoryserver -s '${INSTANCE}' vlvindex" \ 5280 "-n '${STR[DS_DB]}' -T '${IDX_NAME}'" >> ${OUT} 5281 else 5282 # assume DSEE 6+ 5283 print "dsadm reindex -l -t '${IDX_NAME}'" \ 5284 "'${INSTANCE}' '${STR[LDAP_SUFFIX]}'" >> ${OUT} 5285 fi 5286 done 5287 } 5288 5289 Man.addFunc add_vlv_indexes '' '[+NAME?add_vlv_indexes - Add VLV indexes.] 5290 [+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.] 5291 [+RETURN VALUES]{ 5292 [+0?on success.] 5293 [+>= 66?fatal error.] 5294 } 5295 [+SEE ALSO?\bldapsearch\b(1), \bldapmodify\b(1).] 5296 ' 5297 function add_vlv_indexes { 5298 Log.verbose 'Processing VLV indexes ...' 5299 5300 # F[0] F[1] F[2] F[3] 5301 # ${LDAP_DOMAIN}.get ${LDAP_DOMAIN}_%s_vlv_index ou=%s objectClass=$1||&(%s) 5302 typeset -a INDEXES=( 5303 'grent group group posixGroup' 5304 'hostent hosts hosts ipHost' 5305 'netent networks networks ipNetwork' 5306 'pwent passwd people posixAccount' 5307 'rpcent rpc rpc oncRpc' 5308 'spent shadow people shadowAccount' 5309 # Indexes added during NIS to LDAP transition 5310 'auhoent auho automountMapName=auto_home automount' 5311 'soluent solu people SolarisUserAttr' 5312 'authent auth SolarisAuthAttr SolarisAuthAttr' 5313 'execent exec SolarisProfAttr &(SolarisExecAttr)(SolarisKernelSecurityPolicy=*)' 5314 'profent prof SolarisProfAttr &(SolarisProfAttr)(SolarisAttrLongDesc=*)' 5315 'mailent mail aliases mailGroup' 5316 'bootent _boot ethers &(bootableDevice)(bootParameter=*)' 5317 'ethent ethers ethers &(ieee802Device)(macAddress=*)' 5318 'ngrpent netgroup netgroup nisNetgroup' 5319 'ipnent ipn networks &(ipNetwork)(cn=*)' 5320 'maskent mask networks &(ipNetwork)(ipNetmaskNumber=*)' 5321 'prent pr printers printerService' 5322 'ip4ent ip4 hosts &(ipHost)(ipHostNumber=*.*)' 5323 'ip6ent ip6 hosts &(ipHost)(ipHostNumber=*:*)' 5324 ) 5325 5326 # temp file for vlvindex commands 5327 typeset OUT=${TMP[DIR]}/tmp 5328 rm -f ${OUT} && touch ${OUT} 5329 5330 if (( TMPF[IS_OPENDJ] )); then 5331 add_vlv_indexes_OpenDJ INDEXES ${OUT} 5332 else 5333 add_vlv_indexes_DSEE INDEXES ${OUT} 5334 fi 5335 nextFile sh 5336 mv ${OUT} ${TMP[FILE]} 5337 TMP[VLV_CMDS]=${TMP[FILE]} 5338 } 5339 5340 Man.addFunc display_vlv_cmds '' '[+NAME?display_vlv_cmds - Display VLV index commands to run on server.] 5341 [+DESCRIPTION?Display VLV index commands to run on server and save the file to /var/tmp/doIndexVLV-\bSTR[DS_HOST]].sh\b.] 5342 ' 5343 function display_vlv_cmds { 5344 typeset SAV="/var/tmp/doIndexVLV-${STR[DS_HOST]}.sh" 5345 5346 [[ -s ${TMP[VLV_CMDS]} ]] || return 5347 # some problems occured, so the file is not empty 5348 [[ -e ${SAV} ]] && SAV="${SAV%.sh}-$$.sh" 5349 cp -p ${TMP[VLV_CMDS]} "${SAV}" 5350 5351 typeset MSG='NOTE: '"${PROG}"'configured the entries for VLV indexes.' 5352 if (( TMPF[IS_OPENDJ] )); then 5353 MSG+=' 5354 To create the actual VLV indexes, you need to stop the DS on the host 5355 '"'${STR[DS_HOST]}'"', e.g. using "svcadm disable -t opendj@VERS@" 5356 or, if manually started, "ds-stop" and than run the commands shown in the file 5357 '"'${SAV}'"'. Than restart the DS e.g. using 5358 "svcadm enable opendj@VERS@" or "ds-start". 5359 ' 5360 else 5361 typeset INFO=( ${DS_INFO[@]} ) 5362 if (( INFO[1] >= 6 )); then 5363 MSG+=' 5364 Use the dsadm command delivered with the DS on the host '"'${STR[DS_HOST]}'"' 5365 to stop the server. Then, using dsadm, follow the dsadm examples shown in the 5366 file '"'${SAV}'"' to create the actual VLV indexes. 5367 ' 5368 else 5369 MSG+=' 5370 Use the directoryserver(1m) script on the host '"'${STR[DS_HOST]}'"' 5371 to stop the server. Then, using directoryserver, follow the directoryserver 5372 examples shown in the file '"'${SAV}'"' to create the 5373 actual VLV indexes. 5374 ' 5375 fi 5376 fi 5377 Log.info "\n\n${MSG}" 5378 } 5379 5380 Man.addFunc doMain '' '[+NAME?doMain - the main entry point.] 5381 [+DESCRIPTION?The entry point, where the real work starts.] 5382 ' 5383 function doMain { 5384 # Initialize the variables that need to be set to NULL, or some 5385 # other initial value before the rest of the functions can be called 5386 init || return 1 5387 show_vars 5388 5389 # Print extra line to separate from prompt 5390 print 5391 5392 # Either load the user specified config file or prompt user for config info 5393 if [[ -n ${TMP[IN]} ]]; then 5394 load_config_file "${TMP[IN]}" 5395 validate_info || return 1 # Validate basic info in file 5396 else 5397 display_msg 'backup_server' 5398 get_confirm 'Do you wish to continue with server setup (y/n/h)?' \ 5399 'n' 'backup_help' && return 1 5400 5401 # Ask for all required infos 5402 prompt_config_info || return 1 5403 (( INT[LDAP_ENABLE_SHADOW_UPDATE] && INT[EXISTING_PROFILE] )) && \ 5404 return 0 # just enabled shadow update in an existing profile 5405 # Allow user to modify results 5406 integer RES 5407 display_summary 5408 RES=$? 5409 # save the work for now, so that even on exit/errors it can be re-used 5410 [[ -n ${TMP[OUT]} ]] && create_config_file "${TMP[OUT]}" 5411 (( RES )) && return 1 5412 fi 5413 5414 if (( INT[NEED_TIME] )); then 5415 modify_timelimit || return 1 5416 fi 5417 5418 if (( INT[NEED_SIZE] )); then 5419 modify_sizelimit || return 1 5420 fi 5421 5422 # Modify the password storage scheme to support CRYPT 5423 if (( INT[NEED_CRYPT] )); then 5424 modify_pwd_crypt || return 1 5425 fi 5426 5427 # schema modifications 5428 modify_cn || return 1 5429 update_schema_attr || return 1 5430 update_schema_obj || return 1 5431 5432 add_suffix || return 1 5433 5434 # Add missing suffix ACIs 5435 add_suffix_aci || return 1 5436 5437 # Add base objects (if needed) 5438 add_base_objects || return 1 5439 5440 # Update the NisDomainObject. 5441 # The Base DN might of just been created, so this MUST happen after 5442 # the base objects have been added! 5443 set_nisdomain || return 1 5444 5445 # Add top level classes (new containers) 5446 add_new_containers || return 1 5447 5448 add_auto_maps || return 1 5449 5450 modify_top_aci || return 1 5451 5452 add_vlv_aci || return 1 5453 5454 if (( INT[NEED_PROXY] )); then 5455 add_proxyagent || return 1 5456 if (( ! INT[LDAP_ENABLE_SHADOW_UPDATE] )); then 5457 allow_proxy_read_pw || return 1 5458 fi 5459 fi 5460 5461 if (( INT[NEED_ADMIN] )); then 5462 add_admin || return 1 5463 allow_admin_read_write_shadow || return 1 5464 deny_non_admin_shadow_access || return 1 5465 fi 5466 5467 if (( INT[GSSAPI_ENABLE] )); then 5468 add_id_mapping_rules 5469 # do not modify ACI if "sasl/GSSAPI" and "self" are not selected 5470 if [[ ${STR[LDAP_CRED_LEVEL]} == 'self' && \ 5471 ${STR[LDAP_AUTHMETHOD]} == 'sasl/GSSAPI' ]] 5472 then 5473 modify_userpassword_acl_for_gssapi || return 1 5474 else 5475 Log.warn 'ACL for GSSAPI was not set because of incompatibility' \ 5476 'in profile' 5477 fi 5478 fi 5479 5480 if (( INT[NEED_HOSTACL] )); then 5481 allow_host_read_write_shadow || return 66 5482 deny_non_host_shadow_access || return 67 5483 fi 5484 5485 5486 # Generate client profile and add it to the server 5487 add_profile || return 1 5488 5489 # Add Indexes to improve search performance 5490 add_eq_indexes || return 1 5491 add_sub_indexes || return 1 5492 add_vlv_indexes || return 1 5493 5494 # Display setup complete message 5495 Log.printMarker 5496 Log.info 'Setup of DS server' "'${STR[DS_HOST]}'" 'is complete.' 5497 Log.printMarker 5498 5499 # Display VLV index commands to be executed on server (if any) 5500 display_vlv_cmds 5501 5502 # Create final config file if requested 5503 [[ -n ${TMP[OUT]} ]] && create_config_file "${TMP[OUT]}" 5504 return 0 5505 } 5506 5507 Man.addFunc cleanup '' '[+NAME?cleanup - cleanup tempfiles, tty and exit.] 5508 [+DESCRIPTION?Removes \aTMP[DIR]]\a unless \aTMPF[DEBUG]]\a is set and restores the echo for the tty.] 5509 [+ENVIRONMENT VARIABLES]{' "${ Man.varUsage TMP TMPF ; }" '} 5510 ' 5511 function cleanup { 5512 (( TMPF[CLEANUP_DONE] )) && return 5513 if (( TMPF[DEBUG] )); then 5514 typeset X=${ ls -1 ${TMP[DIR]} 2>/dev/null ; } 5515 if [[ -z $X || $X == 'rootPWD' ]]; then 5516 rm -rf ${TMP[DIR]} 5517 else 5518 Log.info "Leaving temp dir ${TMP[DIR]} as is. Remove it manually" 5519 fi 5520 elif (( TMPF[KEEP] )); then 5521 typeset X=${ ls -1 ${TMP[DIR]} 2>/dev/null ; } 5522 if [[ -z $X || $X == 'rootPWD' ]]; then 5523 rm -rf ${TMP[DIR]} 5524 else 5525 Log.info "Keeping setup scripts and files in ${TMP[DIR]}" 5526 fi 5527 elif [[ -d ${TMP[DIR]} ]]; then 5528 rm -rf ${TMP[DIR]} 5529 fi 5530 ${STTY:-/usr/bin/stty} echo 5531 trap - EXIT 1 2 3 6 15 5532 # Might be trapped in the scope of a function which will stop executing 5533 # the function itself but not the script. So: 5534 [[ -z $1 ]] && kill $$ 5535 TMPF[CLEANUP_DONE]=1 5536 } 5537 5538 # debug helper function. Required to be not a 'function'! 5539 showCommand() { 5540 typeset CMD="${.sh.command}" 5541 [[ -z ${CMD} ]] && return 5542 print -n -u2 '\E[0;30;47m' 5543 print -u2 -r -n "DEBUG: ${.sh.fun} '${CMD}'" 5544 print '\E[0m' 5545 } 5546 5547 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.] 5548 [+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.] 5549 [h:help?Print this help and exit.] 5550 [F:functions?Print out a list of names of all currently defined script functions and exit (see \btypeset +f\b).] 5551 [H:usage]:[function?Show the usage info for the function with the given name if available and exit. (see also option \b-F\b).] 5552 [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.] 5553 [d:debug?Enable really verbose debug info.] 5554 [C:no-color?Do not use ANSI escape codes to color the output.] 5555 [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.] 5556 [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.] 5557 [i:in]:[file?Get setup info from the given file.] 5558 [o:out]:[file?Generate a server configuration output file.] 5559 [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.] 5560 [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. 5561 Wrt. Solaris it should not make a difference, whether OpenDJ or DSEE syntax is used. OpenDJ syntax is usually a little bit more precise.] 5562 [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.] 5563 [v:verbose?Verbose mode - makes the script a little bit more chatty.] 5564 [+NOTES?This script should work for the following DS:]{ 5565 [+?Sun DS 5.x] 5566 [+?Sun DSEE 6.x/7.x] 5567 [+?Oracle ODSEE 11g] 5568 [+?Sun OpenDS 2.x] 5569 [+?Forgerock OpenDJ 2.x] 5570 } 5571 ' 5572 5573 TMPF[DEBUG]=0 TMPF[KEEP]=0 TMPF[FD]=4 5574 5575 X="${ print ${Man.FUNC[MAIN]} ; }" 5576 while getopts "${X}" option ; do 5577 case "${option}" in 5578 '?'|h) showUsage ${PROG} MAIN ; exit 0 ;; 5579 F) typeset +f ; exit 0 ;; 5580 T) if [[ ${OPTARG} == '*' || ${OPTARG} == 'ALL' ]]; then 5581 typeset -ft ${ typeset +f ; } 5582 elif [[ ${OPTARG} == 'MAIN' ]]; then 5583 set -x 5584 else 5585 typeset -ft ${OPTARG//,/ } 5586 fi 5587 ;; 5588 H) if [[ ${OPTARG%_t} != $OPTARG ]]; then 5589 $OPTARG --man # self-defined types 5590 else 5591 showUsage "$OPTARG" "$OPTARG" # functions 5592 fi 5593 exit 0 5594 ;; 5595 C) Log.COLORED=0 ;; 5596 d) TMPF[DEBUG]=1 ;; 5597 k) TMPF[KEEP]=1 ;; 5598 I) INT[NEED_CRYPT_IMPORT]=1 ;; 5599 i) TMP[IN]="${OPTARG}" ;; 5600 o) TMP[OUT]="${OPTARG}" ;; 5601 S) TMPF[SYNTAX]=2 ; print DSEE ;; 5602 s) TMPF[SYNTAX]=1 ; print DJ ;; 5603 t) is_numeric "${OPTARG}" && INT[TASK_TIMEOUT]=${OPTARG} ;; 5604 v) Log.VERBOSE=1 ; TMPF[FD]=1 ;; 5605 *) Log.fatal '**Internal ERROR: Supported option missing handler' 5606 exit 1 5607 ;; 5608 esac 5609 done 5610 X=$(( OPTIND - 1 )) 5611 shift $X 5612 (( TMPF[DEBUG] )) && Man.listVars 5613 5614 unset LC_ALL LC_LANG 5615 export LC_CTYPE=C # avoid any surprises 5616 5617 # Create TMP[DIR] 5618 TMP[DIR]=${ mktemp -d /tmp/${PROG}.XXXXX ; } 5619 if [[ -z ${TMP[DIR]} ]]; then 5620 Log.fatal 'Unable to create a safe temporary directory' 5621 exit 1 5622 fi 5623 trap cleanup EXIT 1 2 3 6 15 # and register to cleanup on exit/signal 5624 if (( TMPF[DEBUG] )); then 5625 trap 'typeset _CMD_="${.sh.command}" 5626 print -u2 -n "\E[0;30;47m" 5627 print -u2 -n -r "${.sh.fun:-MAIN}(): ${_CMD_}" 5628 print -u2 "\E[0m" 5629 ' TMPF[DEBUG] 5630 fi 5631 5632 # Prevent new files from being read by group or others 5633 umask 077 5634 5635 doMain 5636 X=$? 5637 5638 cleanup NOKILL 5639 5640 exit ${X} 5641 5642 # vim:ts=4 filetype=sh 5643 # :syntax sync fromstart