1#!/bin/sh
2#
3# ident	"%Z%%M%	%I%	%E% SMI"
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#
25# idsconfig -- script to setup iDS 5.x/6.x for Native LDAP II.
26#
27# Copyright 2006 Sun Microsystems, Inc.  All rights reserved.
28# Use is subject to license terms.
29#
30
31#
32# display_msg(): Displays message corresponding to the tag passed in.
33#
34display_msg()
35{
36    case "$1" in
37    usage) cat <<EOF
38 $PROG: [ -v ] [ -i input file ] [ -o output file ]
39   i <input file>     Get setup info from input file.
40   o <output file>    Generate a server configuration output file.
41   v                  Verbose mode
42EOF
43    ;;
44    backup_server) cat <<EOF
45It is strongly recommended that you BACKUP the directory server
46before running $PROG.
47
48Hit Ctrl-C at any time before the final confirmation to exit.
49
50EOF
51    ;;
52    setup_complete) cat <<EOF
53
54$PROG: Setup of iDS server ${IDS_SERVER} is complete.
55
56EOF
57    ;;
58    display_vlv_list) cat <<EOF
59
60Note: idsconfig has created entries for VLV indexes.  Use the 
61      directoryserver(1m) script on ${IDS_SERVER} to stop
62      the server and then enter the following vlvindex
63      sub-commands to create the actual VLV indexes:
64
65EOF
66    ;;
67    cred_level_menu) cat <<EOF
68The following are the supported credential levels:
69  1  anonymous
70  2  proxy
71  3  proxy anonymous
72  4  self
73  5  self proxy
74  6  self proxy anonymous
75EOF
76    ;;
77    auth_method_menu) cat <<EOF
78The following are the supported Authentication Methods:
79  1  none
80  2  simple
81  3  sasl/DIGEST-MD5
82  4  tls:simple
83  5  tls:sasl/DIGEST-MD5
84  6  sasl/GSSAPI
85EOF
86    ;;
87    srvauth_method_menu) cat <<EOF
88The following are the supported Authentication Methods:
89  1  simple
90  2  sasl/DIGEST-MD5
91  3  tls:simple
92  4  tls:sasl/DIGEST-MD5
93  5  sasl/GSSAPI
94EOF
95    ;;
96    prompt_ssd_menu) cat <<EOF
97  A  Add a Service Search Descriptor
98  D  Delete a SSD
99  M  Modify a SSD
100  P  Display all SSD's
101  H  Help
102  X  Clear all SSD's
103
104  Q  Exit menu
105EOF
106    ;;
107    summary_menu)
108
109	SUFFIX_INFO=
110	DB_INFO=
111
112	[ -n "${NEED_CREATE_SUFFIX}" ] &&
113	{
114		SUFFIX_INFO=`cat <<EOF
115
116         Suffix to create          : $LDAP_SUFFIX
117EOF
118`
119		[ -n "${NEED_CREATE_BACKEND}" ] &&
120			DB_INFO=`cat <<EOF
121
122         Database to create        : $IDS_DATABASE
123EOF
124`
125	}
126
127	cat <<EOF
128              Summary of Configuration
129
130  1  Domain to serve               : $LDAP_DOMAIN
131  2  Base DN to setup              : $LDAP_BASEDN$SUFFIX_INFO$DB_INFO
132  3  Profile name to create        : $LDAP_PROFILE_NAME
133  4  Default Server List           : $LDAP_SERVER_LIST
134  5  Preferred Server List         : $LDAP_PREF_SRVLIST
135  6  Default Search Scope          : $LDAP_SEARCH_SCOPE
136  7  Credential Level              : $LDAP_CRED_LEVEL
137  8  Authentication Method         : $LDAP_AUTHMETHOD
138  9  Enable Follow Referrals       : $LDAP_FOLLOWREF
139 10  iDS Time Limit                : $IDS_TIMELIMIT
140 11  iDS Size Limit                : $IDS_SIZELIMIT
141 12  Enable crypt password storage : $NEED_CRYPT
142 13  Service Auth Method pam_ldap  : $LDAP_SRV_AUTHMETHOD_PAM
143 14  Service Auth Method keyserv   : $LDAP_SRV_AUTHMETHOD_KEY
144 15  Service Auth Method passwd-cmd: $LDAP_SRV_AUTHMETHOD_CMD
145 16  Search Time Limit             : $LDAP_SEARCH_TIME_LIMIT
146 17  Profile Time to Live          : $LDAP_PROFILE_TTL
147 18  Bind Limit                    : $LDAP_BIND_LIMIT
148 19  Service Search Descriptors Menu
149
150EOF
151    ;;
152    sfx_not_suitable) cat <<EOF
153
154Sorry, suffix ${LDAP_SUFFIX} is not suitable for Base DN ${LDAP_BASEDN}
155
156EOF
157    ;;
158    obj_not_found) cat <<EOF
159
160Sorry, ${PROG} can't find an objectclass for "$_ATT" attribute
161
162EOF
163    ;;
164    sfx_config_incons) cat <<EOF
165
166Sorry, there is no suffix mapping for ${LDAP_SUFFIX},
167while ldbm database exists, server configuration needs to be fixed manually,
168look at cn=mapping tree,cn=config and cn=ldbm database,cn=plugins,cn=config
169
170EOF
171    ;;
172    ldbm_db_exist) cat <<EOF
173
174Database "${IDS_DATABASE}" already exists,
175however "${IDS_DATABASE_AVAIL}" name is available
176
177EOF
178    ;;
179    unable_find_db_name) cat <<EOF
180    
181Unable to find any available database name close to "${IDS_DATABASE}"
182
183EOF
184    ;;
185    create_ldbm_db_error) cat <<EOF
186
187ERROR: unable to create suffix ${LDAP_SUFFIX}
188       due to server error that occurred during creation of ldbm database
189
190EOF
191    ;;
192    create_suffix_entry_error) cat <<EOF
193
194ERROR: unable to create entry ${LDAP_SUFFIX} of ${LDAP_SUFFIX_OBJ} class
195
196EOF
197    ;;
198    ldap_suffix_list) cat <<EOF
199
200No valid suffixes (naming contexts) were found for LDAP base DN:
201${LDAP_BASEDN}
202
203Available suffixes are:
204${LDAP_SUFFIX_LIST}
205
206EOF
207    ;;
208    sorry) cat <<EOF
209
210HELP - No help is available for this topic.
211
212EOF
213    ;;
214    create_suffix_help) cat <<EOF
215
216HELP - Our Base DN is ${LDAP_BASEDN}
217       and we need to create a Directory Suffix,
218       which can be equal to Base DN itself or be any of Base DN parents.
219       All intermediate entries up to suffix will be created on demand.
220
221EOF
222    ;;
223    enter_ldbm_db_help) cat <<EOF
224
225HELP - ldbm database is an internal database for storage of our suffix data.
226       Database name must be alphanumeric due to Directory Server restriction.
227
228EOF
229    ;;
230    backup_help) cat <<EOF
231
232HELP - Since idsconfig modifies the directory server configuration,
233       it is strongly recommended that you backup the server prior
234       to running this utility.  This is especially true if the server
235       being configured is a production server.
236
237EOF
238    ;;
239    port_help) cat <<EOF
240
241HELP - Enter the port number the directory server is configured to
242       use for LDAP.
243
244EOF
245    ;;
246    domain_help) cat <<EOF
247
248HELP - This is the DNS domain name this server will be serving.  You
249       must provide this name even if the server is not going to be populated
250       with hostnames.  Any unqualified hostname stored in the directory
251       will be fully qualified using this DNS domain name.
252
253EOF
254    ;;
255    basedn_help) cat <<EOF
256
257HELP - This parameter defines the default location in the directory tree for
258       the naming services entries.  You can override this default by using 
259       serviceSearchDescriptors (SSD). You will be given the option to set up 
260       an SSD later on in the setup.
261
262EOF
263    ;;
264    profile_help) cat <<EOF
265
266HELP - Name of the configuration profile with which the clients will be
267       configured. A directory server can store various profiles for multiple 
268       groups of clients.  The initialization tool, (ldapclient(1M)), assumes 
269       "default" unless another is specified.
270
271EOF
272    ;;
273    def_srvlist_help) cat <<EOF
274
275HELP - Provide a list of directory servers to serve clients using this profile.
276       All these servers should contain consistent data and provide similar 
277       functionality.  This list is not ordered, and clients might change the 
278       order given in this list. Note that this is a space separated list of 
279       *IP addresses* (not host names).  Providing port numbers is optional.
280
281EOF
282    ;;
283    pref_srvlist_help) cat <<EOF
284
285HELP - Provide a list of directory servers to serve this client profile. 
286       Unlike the default server list, which is not ordered, the preferred 
287       servers must be entered IN THE ORDER you wish to have them contacted. 
288       If you do specify a preferred server list, clients will always contact 
289       them before attempting to contact any of the servers on the default 
290       server list. Note that you must enter the preferred server list as a 
291       space-separated list of *IP addresses* (not host names).  Providing port 
292       numbers is optional.
293
294EOF
295    ;;
296    srch_scope_help) cat <<EOF
297
298HELP - Default search scope to be used for all searches unless they are
299       overwritten using serviceSearchDescriptors.  The valid options
300       are "one", which would specify the search will only be performed 
301       at the base DN for the given service, or "sub", which would specify 
302       the search will be performed through *all* levels below the base DN 
303       for the given service.
304
305EOF
306    ;;
307    cred_lvl_help) cat <<EOF
308
309HELP - This parameter defines what credentials the clients use to
310       authenticate to the directory server.  This list might contain
311       multiple credential levels and is ordered.  If a proxy level
312       is configured, you will also be prompted to enter a bind DN
313       for the proxy agent along with a password.  This proxy agent
314       will be created if it does not exist.
315
316EOF
317    ;;
318    auth_help) cat <<EOF
319
320HELP - The default authentication method(s) to be used by all services
321       in the client using this profile.  This is a ordered list of
322       authentication methods separated by a ';'.  The supported methods
323       are provided in a menu.  Note that sasl/DIGEST-MD5 binds require
324       passwords to be stored un-encrypted on the server.
325
326EOF
327    ;;
328    srvauth_help) cat <<EOF
329
330HELP - The authentication methods to be used by a given service.  Currently
331       3 services support this feature: pam_ldap, keyserv, and passwd-cmd.
332       The authentication method specified in this attribute overrides
333       the default authentication method defined in the profile.  This
334       feature can be used to select stronger authentication methods for
335       services which require increased security.
336
337EOF
338    ;;
339    pam_ldap_help) cat <<EOF
340
341HELP - The authentication method(s) to be used by pam_ldap when contacting
342       the directory server.  This is a ordered list, and, if provided, will
343       override the default authentication method parameter.
344
345EOF
346    ;;
347    keyserv_help) cat <<EOF
348
349HELP - The authentication method(s) to be used by newkey(1M) and chkey(1)
350       when contacting the directory server.  This is a ordered list and
351       if provided will override the default authentication method
352       parameter.
353
354EOF
355    ;;
356    passwd-cmd_help) cat <<EOF
357
358HELP - The authentication method(s) to be used by passwd(1) command when
359       contacting the directory server.  This is a ordered list and if
360       provided will override the default authentication method parameter.
361
362EOF
363    ;;
364    referrals_help) cat <<EOF
365
366HELP - This parameter indicates whether the client should follow
367       ldap referrals if it encounters one during naming lookups.
368
369EOF
370    ;;
371    tlim_help) cat <<EOF
372
373HELP - The server time limit value indicates the maximum amount of time the
374       server would spend on a query from the client before abandoning it.
375       A value of '-1' indicates no limit.
376
377EOF
378    ;;
379    slim_help) cat <<EOF
380
381HELP - The server sizelimit value indicates the maximum number of entries
382       the server would return in respond to a query from the client.  A
383       value of '-1' indicates no limit.
384
385EOF
386    ;;
387    crypt_help) cat <<EOF
388
389HELP - By default iDS does not store userPassword attribute values using
390       unix "crypt" format.  If you need to keep your passwords in the crypt
391       format for NIS/NIS+ and pam_unix compatibility, choose 'yes'.  If
392       passwords are stored using any other format than crypt, pam_ldap
393       MUST be used by clients to authenticate users to the system. Note 
394       that if you wish to use sasl/DIGEST-MD5 in conjunction with pam_ldap,
395       user passwords must be stored in the clear format.
396
397EOF
398    ;;
399    srchtime_help) cat <<EOF
400
401HELP - The search time limit the client will enforce for directory
402       lookups.
403
404EOF
405    ;;
406    profttl_help) cat <<EOF
407
408HELP - The time to live value for profile.  The client will refresh its
409       cached version of the configuration profile at this TTL interval.
410
411EOF
412    ;;
413    bindlim_help) cat <<EOF
414
415HELP - The time limit for the bind operation to the directory.  This
416       value controls the responsiveness of the client in case a server
417       becomes unavailable.  The smallest timeout value for a given
418       network architecture/conditions would work best.  This is very
419       similar to setting TCP timeout, but only for LDAP bind operation.
420
421EOF
422    ;;
423    ssd_help) cat <<EOF
424
425HELP - Using Service Search Descriptors (SSD), you can override the
426       default configuration for a given service.  The SSD can be
427       used to override the default search base DN, the default search
428       scope, and the default search filter to be used for directory
429       lookups.  SSD are supported for all services (databases)
430       defined in nsswitch.conf(4).  The default base DN is defined
431       in ldap(1).
432
433       Note: SSD are powerful tools in defining configuration profiles
434             and provide a great deal of flexibility.  However, care
435             must be taken in creating them.  If you decide to make use
436             of SSDs, consult the documentation first.
437
438EOF
439    ;;
440    ssd_menu_help) cat <<EOF
441
442HELP - Using this menu SSD can be added, updated, or deleted from
443       the profile.
444
445       A - This option creates a new SSD by prompting for the
446           service name, base DN, and scope.  Service name is
447           any valid service as defined in ldap(1).  base is
448           either the distinguished name to the container where
449           this service will use, or a relative DN followed
450           by a ','.
451       D - Delete a previously created SSD.
452       M - Modify a previously created SSD.
453       P - Display a list of all the previously created SSD.
454       X - Delete all of the previously created SSD.
455
456       Q - Exit the menu and continue with the server configuration.
457
458EOF
459    ;;
460    ldap_suffix_list_help) cat <<EOF
461
462HELP - No valid suffixes (naming contexts) are available on server 
463       ${IDS_SERVER}:${IDS_PORT}.
464       You must set an LDAP Base DN that can be contained in 
465       an existing suffix.
466
467EOF
468    ;;
469    esac
470}
471
472
473#
474# get_ans(): gets an answer from the user.
475#		$1  instruction/comment/description/question
476#		$2  default value
477#
478get_ans()
479{
480    if [ -z "$2" ]
481    then
482	${ECHO} "$1 \c"
483    else
484	${ECHO} "$1 [$2] \c"
485    fi
486
487    read ANS
488    if [ -z "$ANS" ]
489    then
490	ANS=$2
491    fi
492}
493
494
495#
496# get_ans_req(): gets an answer (required) from the user, NULL value not allowed.
497#		$@  instruction/comment/description/question
498#
499get_ans_req()
500{
501    ANS=""                  # Set ANS to NULL.
502    while [ "$ANS" = "" ]
503    do
504	get_ans "$@"
505	[ "$ANS" = "" ] && ${ECHO} "NULL value not allowed!"
506    done
507}
508
509
510#
511# get_number(): Querys and verifies that number entered is numeric.
512#               Function will repeat prompt user for number value.
513#               $1  Message text.
514#		$2  default value.
515#               $3  Help argument.
516#
517get_number()
518{
519    ANS=""                  # Set ANS to NULL.
520    NUM=""
521
522    get_ans "$1" "$2"
523
524    # Verify that value is numeric.
525    while not_numeric $ANS
526    do
527	case "$ANS" in
528	    [Hh] | help | Help | \?) display_msg ${3:-sorry} ;;
529	    * ) ${ECHO} "Invalid value: \"${ANS}\". \c"
530	     ;;
531	esac
532	# Get a new value.
533	get_ans "Enter a numeric value:" "$2"
534    done
535    NUM=$ANS
536}
537
538
539#
540# get_negone_num(): Only allows a -1 or positive integer.
541#                   Used for values where -1 has special meaning.
542#
543#                   $1 - Prompt message.
544#                   $2 - Default value (require).
545#                   $3 - Optional help argument.
546get_negone_num()
547{
548    while :
549    do
550	get_number "$1" "$2" "$3"
551	if is_negative $ANS
552	then
553	    if [ "$ANS" = "-1" ]; then
554		break  # -1 is OK, so break.
555	    else       # Need to re-enter number.
556		${ECHO} "Invalid number: please enter -1 or positive number."
557	    fi
558	else
559	    break      # Positive number
560	fi
561    done
562}
563
564
565#
566# get_passwd(): Reads a password from the user and verify with second.
567#		$@  instruction/comment/description/question
568#
569get_passwd()
570{
571    [ $DEBUG -eq 1 ] && ${ECHO} "In get_passwd()"
572
573    # Temporary PASSWD variables
574    _PASS1=""
575    _PASS2=""
576
577    /usr/bin/stty -echo     # Turn echo OFF
578
579    # Endless loop that continues until passwd and re-entered passwd
580    # match.
581    while :
582    do
583	ANS=""                  # Set ANS to NULL.
584
585	# Don't allow NULL for first try.
586	while [ "$ANS" = "" ]
587	do
588	    get_ans "$@"
589	    [ "$ANS" = "" ] && ${ECHO} "" && ${ECHO} "NULL passwd not allowed!"
590	done
591	_PASS1=$ANS         # Store first try.
592
593	# Get second try.
594	${ECHO} ""
595	get_ans "Re-enter passwd:"
596	_PASS2=$ANS
597
598	# Test if passwords are identical.
599	if [ "$_PASS1" = "$_PASS2" ]; then
600	    break
601	fi
602
603	# Move cursor down to next line and print ERROR message.
604	${ECHO} ""
605	${ECHO} "ERROR: passwords don't match; try again."
606    done
607
608    /usr/bin/stty echo      # Turn echo ON
609
610    ${ECHO} ""
611}
612
613
614#
615# get_passwd_nochk(): Reads a password from the user w/o check.
616#		$@  instruction/comment/description/question
617#
618get_passwd_nochk()
619{
620    [ $DEBUG -eq 1 ] && ${ECHO} "In get_passwd_nochk()"
621
622    /usr/bin/stty -echo     # Turn echo OFF
623
624    get_ans "$@"
625
626    /usr/bin/stty echo      # Turn echo ON
627
628    ${ECHO} ""
629}
630
631
632#
633# get_menu_choice(): Get a menu choice from user.  Continue prompting
634#                    till the choice is in required range.
635#   $1 .. Message text.
636#   $2 .. min value
637#   $3 .. max value
638#   $4 .. OPTIONAL: default value
639#
640#   Return value:
641#     MN_CH will contain the value selected.
642#
643get_menu_choice()
644{
645    # Check for req parameter.
646    if [ $# -lt 3 ]; then
647	${ECHO} "get_menu_choice(): Did not get required parameters."
648	return 1
649    fi
650
651    while :
652    do
653	get_ans "$1" "$4"
654	MN_CH=$ANS
655	is_negative $MN_CH
656	if [ $? -eq 1 ]; then
657	    if [ $MN_CH -ge $2 ]; then
658		if [ $MN_CH -le $3 ]; then
659		    return
660		fi
661	    fi
662	fi
663	${ECHO} "Invalid choice: $MN_CH"
664    done
665}
666
667
668#
669# get_confirm(): Get confirmation from the user. (Y/Yes or N/No)
670#                $1 - Message
671#                $2 - default value.
672#
673get_confirm()
674{
675    _ANSWER=
676
677    while :
678    do
679	# Display Internal ERROR if $2 not set.
680	if [ -z "$2" ]
681	then
682	    ${ECHO} "INTERNAL ERROR: get_confirm requires 2 args, 3rd is optional."
683	    exit 2
684	fi
685
686	# Display prompt.
687	${ECHO} "$1 [$2] \c"
688
689	# Get the ANSWER.
690	read _ANSWER
691	if [ "$_ANSWER" = "" ] && [ -n "$2" ] ; then
692	    _ANSWER=$2
693	fi
694	case "$_ANSWER" in
695	    [Yy] | yes | Yes | YES) return 1 ;;
696	    [Nn] | no  | No  | NO)  return 0 ;;
697	    [Hh] | help | Help | \?) display_msg ${3:-sorry};;
698	    * ) ${ECHO} "Please enter y or n."  ;;
699	esac
700    done
701}
702
703
704#
705# get_confirm_nodef(): Get confirmation from the user. (Y/Yes or N/No)
706#                      No default value supported.
707#
708get_confirm_nodef()
709{
710    _ANSWER=
711
712    while :
713    do
714	${ECHO} "$@ \c"
715	read _ANSWER
716	case "$_ANSWER" in
717	    [Yy] | yes | Yes | YES) return 1 ;;
718	    [Nn] | no  | No  | NO)  return 0 ;;
719	    * ) ${ECHO} "Please enter y or n."  ;;
720	esac
721    done
722}
723
724
725#
726# is_numeric(): Tells is a string is numeric.
727#    0 = Numeric
728#    1 = NOT Numeric
729#
730is_numeric()
731{
732    # Check for parameter.
733    if [ $# -ne 1 ]; then
734	return 1
735    fi
736
737    # Determine if numeric.
738    expr "$1" + 1 > /dev/null 2>&1
739    if [ $? -ge 2 ]; then
740	return 1
741    fi
742
743    # Made it here, it's Numeric.
744    return 0
745}
746
747
748#
749# not_numeric(): Reverses the return values of is_numeric.  Useful
750#                 for if and while statements that want to test for
751#                 non-numeric data.
752#    0 = NOT Numeric
753#    1 = Numeric
754#
755not_numeric()
756{
757    is_numeric $1
758    if [ $? -eq 0 ]; then
759       return 1
760    else
761       return 0
762    fi
763}
764
765
766#
767# is_negative(): Tells is a Numeric value is less than zero.
768#    0 = Negative Numeric
769#    1 = Positive Numeric
770#    2 = NOT Numeric
771#
772is_negative()
773{
774    # Check for parameter.
775    if [ $# -ne 1 ]; then
776	return 1
777    fi
778
779    # Determine if numeric.  Can't use expr because -0 is
780    # considered positive??
781    if is_numeric $1; then
782	case "$1" in
783	    -*)  return 0 ;;   # Negative Numeric
784	    *)   return 1 ;;   # Positive Numeric
785	esac
786    else
787	return 2
788    fi
789}
790
791
792#
793# check_domainname(): check validity of a domain name.  Currently we check
794#                     that it has at least two components.
795#		$1  the domain name to be checked
796#
797check_domainname()
798{
799    if [ ! -z "$1" ]
800    then
801	t=`expr "$1" : '[^.]\{1,\}[.][^.]\{1,\}'`
802	if [ "$t" = 0 ]
803	then
804	    return 1
805	fi
806    fi
807    return 0
808}
809
810
811#
812# check_baseDN(): check validity of the baseDN name.
813#		$1  the baseDN name to be checked
814#
815#     NOTE: The check_baseDN function does not catch all invalid DN's.
816#           Its purpose is to reduce the number of invalid DN's to
817#           get past the input routine.  The invalid DN's will be
818#           caught by the LDAP server when they are attempted to be
819#           created.
820#
821check_baseDN()
822{
823    ck_DN=$1
824    ${ECHO} "  Checking LDAP Base DN ..."
825    if [ ! -z "$ck_DN" ]; then
826        [ $DEBUG -eq 1 ] && ${ECHO} "Checking baseDN: $ck_DN"
827        # Check for = (assignment operator)
828        ${ECHO} "$ck_DN" | ${GREP} "=" > /dev/null 2>&1
829        if [ $? -ne 0 ]; then
830            [ $DEBUG -eq 1 ] && ${ECHO} "check_baseDN: No '=' in baseDN."
831            return 1
832        fi
833
834        # Check all keys.
835        while :
836        do
837            # Get first key.
838            dkey=`${ECHO} $ck_DN | cut -d'=' -f1`
839
840            # Check that the key string is valid
841	    check_attrName $dkey
842	    if [ $? -ne 0 ]; then
843                [ $DEBUG -eq 1 ] && ${ECHO} "check_baseDN: invalid key=${dkey}"
844                return 1
845            fi
846
847            [ $DEBUG -eq 1 ] && ${ECHO} "check_baseDN: valid key=${dkey}"
848
849            # Remove first key from DN
850            ck_DN=`${ECHO} $ck_DN | cut -s -d',' -f2-`
851
852            # Break loop if nothing left.
853            if [ "$ck_DN" = "" ]; then
854                break
855            fi
856        done
857    fi
858    return 0
859}
860
861
862#
863# domain_2_dc(): Convert a domain name into dc string.
864#    $1  .. Domain name.
865#
866domain_2_dc()
867{
868    _DOM=$1           # Domain parameter.
869    _DOM_2_DC=""      # Return value from function.
870    _FIRST=1          # Flag for first time.
871
872    export _DOM_2_DC  # Make visible for others.
873
874    # Convert "."'s to spaces for "for" loop.
875    domtmp="`${ECHO} ${_DOM} | tr '.' ' '`"
876    for i in $domtmp; do
877	if [ $_FIRST -eq 1 ]; then
878	    _DOM_2_DC="dc=${i}"
879	    _FIRST=0
880	else
881	    _DOM_2_DC="${_DOM_2_DC},dc=${i}"
882	fi
883    done
884}
885
886
887#
888# is_root_user(): Check to see if logged in as root user.
889#
890is_root_user()
891{
892    case `id` in
893	uid=0\(root\)*) return 0 ;;
894	* )             return 1 ;;
895    esac
896}
897
898
899#
900# parse_arg(): Parses the command line arguments and sets the
901#              appropriate variables.
902#
903parse_arg()
904{
905    while getopts "dvhi:o:" ARG
906    do
907	case $ARG in
908	    d)      DEBUG=1;;
909	    v)      VERB="";;
910	    i)      INPUT_FILE=$OPTARG;;
911	    o)      OUTPUT_FILE=$OPTARG;;
912	    \?)	display_msg usage
913		    exit 1;;
914	    *)	${ECHO} "**ERROR: Supported option missing handler!"
915		    display_msg usage
916		    exit 1;;
917	esac
918    done
919    return `expr $OPTIND - 1`
920}
921
922
923#
924# init(): initializes variables and options
925#
926init()
927{
928    # General variables.
929    PROG=`basename $0`	# Program name
930    PID=$$              # Program ID
931    VERB='> /dev/null 2>&1'	# NULL or "> /dev/null"
932    ECHO="/bin/echo"	# print message on screen
933    EVAL="eval"		# eval or echo
934    EGREP="/usr/bin/egrep"
935    GREP="/usr/bin/grep"
936    DEBUG=0             # Set Debug OFF
937    BACKUP=no_ldap	# backup suffix
938    HOST=""		# NULL or <hostname>
939    NAWK="/usr/bin/nawk"
940
941    DOM=""              # Set to NULL
942    # If DNS domain (resolv.conf) exists use that, otherwise use domainname.
943    if [ -f /etc/resolv.conf ]; then
944        DOM=`/usr/xpg4/bin/grep -i -E '^domain|^search' /etc/resolv.conf \
945	    | awk '{ print $2 }' | tail -1`
946    fi
947
948    # If for any reason the DOM did not get set (error'd resolv.conf) set
949    # DOM to the domainname command's output.
950    if [ "$DOM" = "" ]; then
951        DOM=`domainname`	# domain from domainname command.
952    fi
953
954    STEP=1
955    INTERACTIVE=1       # 0 = on, 1 = off (For input file mode)
956    DEL_OLD_PROFILE=0   # 0 (default), 1 = delete old profile.
957
958    # idsconfig specific variables.
959    INPUT_FILE=""
960    OUTPUT_FILE=""
961    NEED_PROXY=0        # 0 = No Proxy, 1 = Create Proxy.
962    LDAP_PROXYAGENT=""
963    LDAP_SUFFIX=""
964    LDAP_DOMAIN=$DOM	# domainname on Server (default value)
965    GEN_CMD=""
966
967    # LDAP COMMANDS
968    LDAPSEARCH="/bin/ldapsearch -r"
969    LDAPMODIFY=/bin/ldapmodify
970    LDAPADD=/bin/ldapadd
971    LDAPDELETE=/bin/ldapdelete
972    LDAP_GEN_PROFILE=/usr/sbin/ldap_gen_profile
973
974    # iDS specific information
975    IDS_SERVER=""
976    IDS_PORT=389
977    NEED_TIME=0
978    NEED_SIZE=0
979    NEED_SRVAUTH_PAM=0
980    NEED_SRVAUTH_KEY=0
981    NEED_SRVAUTH_CMD=0
982    IDS_TIMELIMIT=""
983    IDS_SIZELIMIT=""
984
985    # LDAP PROFILE related defaults
986    LDAP_ROOTDN="cn=Directory Manager"   # Provide common default.
987    LDAP_ROOTPWD=""                      # NULL passwd as default (i.e. invalid)
988    LDAP_PROFILE_NAME="default"
989    LDAP_BASEDN=""
990    LDAP_SERVER_LIST=""
991    LDAP_AUTHMETHOD=""
992    LDAP_FOLLOWREF="FALSE"
993    NEED_CRYPT=""
994    LDAP_SEARCH_SCOPE="one"
995    LDAP_SRV_AUTHMETHOD_PAM=""
996    LDAP_SRV_AUTHMETHOD_KEY=""
997    LDAP_SRV_AUTHMETHOD_CMD=""
998    LDAP_SEARCH_TIME_LIMIT=30
999    LDAP_PREF_SRVLIST=""
1000    LDAP_PROFILE_TTL=43200
1001    LDAP_CRED_LEVEL="proxy"
1002    LDAP_BIND_LIMIT=10
1003
1004    # Prevent new files from being read by group or others.
1005    umask 077
1006
1007    # Service Search Descriptors
1008    LDAP_SERV_SRCH_DES=""
1009
1010    # Set and create TMPDIR.
1011    TMPDIR="/tmp/idsconfig.${PID}"
1012    if mkdir -m 700 ${TMPDIR}
1013    then
1014	# Cleanup on exit.
1015	trap 'rm -rf ${TMPDIR}; /usr/bin/stty echo; exit' 1 2 3 6 15
1016    else
1017	echo "ERROR: unable to create a safe temporary directory."
1018	exit 1
1019    fi
1020    LDAP_ROOTPWF=${TMPDIR}/rootPWD
1021
1022    # Set the SSD file name after setting TMPDIR.
1023    SSD_FILE=${TMPDIR}/ssd_list
1024
1025    # GSSAPI setup
1026    LDAP_KRB_REALM=""
1027    LDAP_GSSAPI_PROFILE=""
1028    SCHEMA_UPDATED=0
1029
1030    export DEBUG VERB ECHO EVAL EGREP GREP STEP TMPDIR
1031    export IDS_SERVER IDS_PORT LDAP_ROOTDN LDAP_ROOTPWD LDAP_SERVER_LIST
1032    export LDAP_BASEDN LDAP_ROOTPWF
1033    export LDAP_DOMAIN LDAP_SUFFIX LDAP_PROXYAGENT LDAP_PROXYAGENT_CRED
1034    export NEED_PROXY
1035    export LDAP_PROFILE_NAME LDAP_BASEDN LDAP_SERVER_LIST
1036    export LDAP_AUTHMETHOD LDAP_FOLLOWREF LDAP_SEARCH_SCOPE LDAP_SEARCH_TIME_LIMIT
1037    export LDAP_PREF_SRVLIST LDAP_PROFILE_TTL LDAP_CRED_LEVEL LDAP_BIND_LIMIT
1038    export NEED_SRVAUTH_PAM NEED_SRVAUTH_KEY NEED_SRVAUTH_CMD
1039    export LDAP_SRV_AUTHMETHOD_PAM LDAP_SRV_AUTHMETHOD_KEY LDAP_SRV_AUTHMETHOD_CMD
1040    export LDAP_SERV_SRCH_DES SSD_FILE
1041    export GEN_CMD LDAP_KRB_REALM LDAP_GSSAPI_PROFILE SCHEMA_UPDATED
1042}
1043
1044
1045#
1046# disp_full_debug(): List of all debug variables usually interested in.
1047#                    Grouped to avoid MASSIVE code duplication.
1048#
1049disp_full_debug()
1050{
1051    [ $DEBUG -eq 1 ] && ${ECHO} "  IDS_SERVER = $IDS_SERVER"
1052    [ $DEBUG -eq 1 ] && ${ECHO} "  IDS_PORT = $IDS_PORT"
1053    [ $DEBUG -eq 1 ] && ${ECHO} "  LDAP_ROOTDN = $LDAP_ROOTDN"
1054    [ $DEBUG -eq 1 ] && ${ECHO} "  LDAP_ROOTPWD = $LDAP_ROOTPWD"
1055    [ $DEBUG -eq 1 ] && ${ECHO} "  LDAP_DOMAIN = $LDAP_DOMAIN"
1056    [ $DEBUG -eq 1 ] && ${ECHO} "  LDAP_SUFFIX = $LDAP_SUFFIX"
1057    [ $DEBUG -eq 1 ] && ${ECHO} "  LDAP_BASEDN = $LDAP_BASEDN"
1058    [ $DEBUG -eq 1 ] && ${ECHO} "  LDAP_PROFILE_NAME = $LDAP_PROFILE_NAME"
1059    [ $DEBUG -eq 1 ] && ${ECHO} "  LDAP_SERVER_LIST = $LDAP_SERVER_LIST"
1060    [ $DEBUG -eq 1 ] && ${ECHO} "  LDAP_PREF_SRVLIST = $LDAP_PREF_SRVLIST"
1061    [ $DEBUG -eq 1 ] && ${ECHO} "  LDAP_SEARCH_SCOPE = $LDAP_SEARCH_SCOPE"
1062    [ $DEBUG -eq 1 ] && ${ECHO} "  LDAP_CRED_LEVEL = $LDAP_CRED_LEVEL"
1063    [ $DEBUG -eq 1 ] && ${ECHO} "  LDAP_AUTHMETHOD = $LDAP_AUTHMETHOD"
1064    [ $DEBUG -eq 1 ] && ${ECHO} "  LDAP_FOLLOWREF = $LDAP_FOLLOWREF"
1065    [ $DEBUG -eq 1 ] && ${ECHO} "  IDS_TIMELIMIT = $IDS_TIMELIMIT"
1066    [ $DEBUG -eq 1 ] && ${ECHO} "  IDS_SIZELIMIT = $IDS_SIZELIMIT"
1067    [ $DEBUG -eq 1 ] && ${ECHO} "  NEED_CRYPT = $NEED_CRYPT"
1068    [ $DEBUG -eq 1 ] && ${ECHO} "  NEED_SRVAUTH_PAM = $NEED_SRVAUTH_PAM"
1069    [ $DEBUG -eq 1 ] && ${ECHO} "  NEED_SRVAUTH_KEY = $NEED_SRVAUTH_KEY"
1070    [ $DEBUG -eq 1 ] && ${ECHO} "  NEED_SRVAUTH_CMD = $NEED_SRVAUTH_CMD"
1071    [ $DEBUG -eq 1 ] && ${ECHO} "  LDAP_SRV_AUTHMETHOD_PAM = $LDAP_SRV_AUTHMETHOD_PAM"
1072    [ $DEBUG -eq 1 ] && ${ECHO} "  LDAP_SRV_AUTHMETHOD_KEY = $LDAP_SRV_AUTHMETHOD_KEY"
1073    [ $DEBUG -eq 1 ] && ${ECHO} "  LDAP_SRV_AUTHMETHOD_CMD = $LDAP_SRV_AUTHMETHOD_CMD"
1074    [ $DEBUG -eq 1 ] && ${ECHO} "  LDAP_SEARCH_TIME_LIMIT = $LDAP_SEARCH_TIME_LIMIT"
1075    [ $DEBUG -eq 1 ] && ${ECHO} "  LDAP_PROFILE_TTL = $LDAP_PROFILE_TTL"
1076    [ $DEBUG -eq 1 ] && ${ECHO} "  LDAP_BIND_LIMIT = $LDAP_BIND_LIMIT"
1077
1078    # Only display proxy stuff if needed.
1079    if [ $NEED_PROXY -eq  1 ]; then
1080	[ $DEBUG -eq 1 ] && ${ECHO} "  LDAP_PROXYAGENT = $LDAP_PROXYAGENT"
1081	[ $DEBUG -eq 1 ] && ${ECHO} "  LDAP_PROXYAGENT_CRED = $LDAP_PROXYAGENT_CRED"
1082	[ $DEBUG -eq 1 ] && ${ECHO} "  NEED_PROXY = $NEED_PROXY"
1083    fi
1084
1085    # Service Search Descriptors are a special case.
1086    [ $DEBUG -eq 1 ] && ${ECHO} "  LDAP_SERV_SRCH_DES = $LDAP_SERV_SRCH_DES"
1087}
1088
1089
1090#
1091# load_config_file(): Loads the config file.
1092#
1093load_config_file()
1094{
1095    [ $DEBUG -eq 1 ] && ${ECHO} "In load_config_file()"
1096
1097    # Remove SSD lines from input file before sourcing.
1098    # The SSD lines must be removed because some forms of the
1099    # data could cause SHELL errors.
1100    ${GREP} -v "LDAP_SERV_SRCH_DES=" ${INPUT_FILE} > ${TMPDIR}/inputfile.noSSD
1101
1102    # Source the input file.
1103    . ${TMPDIR}/inputfile.noSSD
1104
1105    # If LDAP_SUFFIX is no set, try to utilize LDAP_TREETOP since older
1106    # config files use LDAP_TREETOP
1107    LDAP_SUFFIX="${LDAP_SUFFIX:-$LDAP_TREETOP}"
1108
1109    # Save password to temporary file.
1110    save_password
1111
1112    # Create the SSD file.
1113    create_ssd_file
1114
1115    # Display FULL debugging info.
1116    disp_full_debug
1117}
1118
1119#
1120# save_password(): Save password to temporary file.
1121#
1122save_password()
1123{
1124    cat > ${LDAP_ROOTPWF} <<EOF
1125${LDAP_ROOTPWD}
1126EOF
1127}
1128
1129######################################################################
1130# FUNCTIONS  FOR prompt_config_info() START HERE.
1131######################################################################
1132
1133#
1134# get_ids_server(): Prompt for iDS server name.
1135#
1136get_ids_server()
1137{
1138    while :
1139    do
1140	# Prompt for server name.
1141	get_ans "Enter the JES Directory Server's  hostname to setup:" "$IDS_SERVER"
1142	IDS_SERVER="$ANS"
1143
1144	# Ping server to see if live.  If valid break out of loop.
1145	ping $IDS_SERVER > /dev/null 2>&1
1146	if [ $? -eq 0 ]; then
1147	    break
1148	fi
1149
1150	# Invalid server, enter a new name.
1151	${ECHO} "ERROR: Server '${IDS_SERVER}' is invalid or unreachable."
1152	IDS_SERVER=""
1153    done
1154
1155    # Set SERVER_ARGS and LDAP_ARGS since values might of changed.
1156    SERVER_ARGS="-h ${IDS_SERVER} -p ${IDS_PORT}"
1157    LDAP_ARGS="${SERVER_ARGS} ${AUTH_ARGS}"
1158    export SERVER_ARGS
1159
1160}
1161
1162#
1163# get_ids_port(): Prompt for iDS port number.
1164#
1165get_ids_port()
1166{
1167    # Get a valid iDS port number.
1168    while :
1169    do
1170	# Enter port number.
1171	get_number "Enter the port number for iDS (h=help):" "$IDS_PORT" "port_help"
1172	IDS_PORT=$ANS
1173	# Do a simple search to check hostname and port number.
1174	# If search returns SUCCESS, break out, host and port must
1175	# be valid.
1176	${LDAPSEARCH} -h ${IDS_SERVER} -p ${IDS_PORT} -b "" -s base "objectclass=*" > /dev/null 2>&1
1177	if [ $? -eq 0 ]; then
1178	    break
1179	fi
1180
1181	# Invalid host/port pair, Re-enter.
1182	${ECHO} "ERROR: Invalid host or port: ${IDS_SERVER}:${IDS_PORT}, Please re-enter!"
1183	get_ids_server
1184    done
1185
1186    # Set SERVER_ARGS and LDAP_ARGS since values might of changed.
1187    SERVER_ARGS="-h ${IDS_SERVER} -p ${IDS_PORT}"
1188    LDAP_ARGS="${SERVER_ARGS} ${AUTH_ARGS}"
1189    export SERVER_ARGS
1190}
1191
1192
1193#
1194# chk_ids_version(): Read the slapd config file and set variables
1195#
1196chk_ids_version()
1197{
1198    [ $DEBUG -eq 1 ] && ${ECHO} "In chk_ids_version()"
1199
1200    # check iDS version number.
1201    eval "${LDAPSEARCH} ${SERVER_ARGS} -b cn=monitor -s base \"objectclass=*\" version | ${GREP} \"^version=\" | cut -f2 -d'/' | cut -f1 -d' ' > ${TMPDIR}/checkDSver 2>&1"
1202    if [ $? -ne 0 ]; then
1203	${ECHO} "ERROR: Can not determine the version number of iDS!"
1204	exit 1
1205    fi
1206    IDS_VER=`cat ${TMPDIR}/checkDSver`
1207    IDS_MAJVER=`${ECHO} ${IDS_VER} | cut -f1 -d.`
1208    IDS_MINVER=`${ECHO} ${IDS_VER} | cut -f2 -d.`
1209    if [ "${IDS_MAJVER}" != "5" ] && [ "${IDS_MAJVER}" != "6" ]; then
1210	${ECHO} "ERROR: $PROG only works with JES DS version 5.x and 6.x, not ${IDS_VER}."
1211    	exit 1
1212    fi
1213    if [ $DEBUG -eq 1 ]; then
1214	${ECHO} "  IDS_MAJVER = $IDS_MAJVER"
1215	${ECHO} "  IDS_MINVER = $IDS_MINVER"
1216    fi
1217}
1218
1219
1220#
1221# get_dirmgr_dn(): Get the directory manger DN.
1222#
1223get_dirmgr_dn()
1224{
1225    get_ans "Enter the directory manager DN:" "$LDAP_ROOTDN"
1226    LDAP_ROOTDN=$ANS
1227
1228    # Update ENV variables using DN.
1229    AUTH_ARGS="-D \"${LDAP_ROOTDN}\" -j ${LDAP_ROOTPWF}"
1230    LDAP_ARGS="${SERVER_ARGS} ${AUTH_ARGS}"
1231    export AUTH_ARGS LDAP_ARGS
1232}
1233
1234
1235#
1236# get_dirmgr_pw(): Get the Root DN passwd. (Root DN found in slapd.conf)
1237#
1238get_dirmgr_pw()
1239{
1240    while :
1241    do
1242	# Get passwd.
1243	get_passwd_nochk "Enter passwd for ${LDAP_ROOTDN} :"
1244	LDAP_ROOTPWD=$ANS
1245
1246	# Store password in file.
1247	save_password
1248
1249	# Update ENV variables using DN's PW.
1250	AUTH_ARGS="-D \"${LDAP_ROOTDN}\" -j ${LDAP_ROOTPWF}"
1251	LDAP_ARGS="${SERVER_ARGS} ${AUTH_ARGS}"
1252	export AUTH_ARGS LDAP_ARGS
1253
1254	# Verify that ROOTDN and ROOTPWD are valid.
1255	eval "${LDAPSEARCH} ${LDAP_ARGS} -b \"\" -s base \"objectclass=*\" > ${TMPDIR}/checkDN 2>&1"
1256	if [ $? -ne 0 ]; then
1257	    eval "${GREP} credential ${TMPDIR}/checkDN ${VERB}"
1258	    if [ $? -eq 0 ]; then
1259		${ECHO} "ERROR: Root DN passwd is invalid."
1260	    else
1261		${ECHO} "ERROR: Invalid Root DN <${LDAP_ROOTDN}>."
1262		get_dirmgr_dn
1263	    fi
1264	else
1265	    break         # Both are valid.
1266	fi
1267    done
1268
1269
1270}
1271
1272
1273#
1274# get_domain(): Get the Domain that will be served by the LDAP server.
1275#               $1 - Help argument.
1276#
1277get_domain()
1278{
1279    # Use LDAP_DOMAIN as default.
1280    get_ans "Enter the domainname to be served (h=help):" $LDAP_DOMAIN
1281
1282    # Check domainname, and have user re-enter if not valid.
1283    check_domainname $ANS
1284    while [ $? -ne 0 ]
1285    do
1286	case "$ANS" in
1287	    [Hh] | help | Help | \?) display_msg ${1:-sorry} ;;
1288	    * ) ${ECHO} "Invalid domainname: \"${ANS}\"."
1289	     ;;
1290	esac
1291	get_ans "Enter domainname to be served (h=help):" $DOM
1292
1293	check_domainname $ANS
1294    done
1295
1296    # Set the domainname to valid name.
1297    LDAP_DOMAIN=$ANS
1298}
1299
1300
1301#
1302# get_basedn(): Query for the Base DN.
1303#
1304get_basedn()
1305{
1306    # Set the $_DOM_2_DC and assign to LDAP_BASEDN as default.
1307    # Then call get_basedn().  This method remakes the default
1308    # each time just in case the domain changed.
1309    domain_2_dc $LDAP_DOMAIN
1310    LDAP_BASEDN=$_DOM_2_DC
1311
1312    # Get Base DN.
1313    while :
1314    do
1315	get_ans_req "Enter LDAP Base DN (h=help):" "${_DOM_2_DC}"
1316	check_baseDN "$ANS"
1317	while [ $? -ne 0 ]
1318	do
1319	    case "$ANS" in
1320		[Hh] | help | Help | \?) display_msg basedn_help ;;
1321		* ) ${ECHO} "Invalid base DN: \"${ANS}\"."
1322		;;
1323	    esac
1324
1325	    # Re-Enter the BaseDN
1326	    get_ans_req "Enter LDAP Base DN (h=help):" "${_DOM_2_DC}"
1327	    check_baseDN "$ANS"
1328	done
1329
1330	# Set base DN and check its suffix
1331	LDAP_BASEDN=${ANS}
1332	check_basedn_suffix ||
1333	{
1334		cleanup
1335		exit 1
1336	}
1337
1338	# suffix may need to be created, in that case get suffix from user
1339	[ -n "${NEED_CREATE_SUFFIX}" ] &&
1340	{
1341		get_suffix || continue
1342	}
1343
1344	# suffix is ok, break out of the base dn inquire loop
1345	break
1346    done
1347}
1348
1349get_krb_realm() {
1350
1351    # To upper cases
1352    LDAP_KRB_REALM=`${ECHO} ${LDAP_DOMAIN} | ${NAWK} '{ print toupper($0) }'`
1353    get_ans_req "Enter Kerberos Realm:" "$LDAP_KRB_REALM"
1354    # To upper cases
1355    LDAP_KRB_REALM=`${ECHO} ${ANS} | ${NAWK} '{ print toupper($0) }'`
1356}
1357
1358# $1: DN
1359# $2: ldif file
1360add_entry_by_DN() {
1361
1362    ${EVAL} "${LDAPSEARCH} ${LDAP_ARGS} -b \"${1}\" -s base \"objectclass=*\" ${VERB}"
1363    if [ $? -eq 0 ]; then
1364	    ${ECHO} "  ${1} already exists"
1365	    return 0
1366    else
1367	${EVAL} "${LDAPADD} ${LDAP_ARGS} -f ${2} ${VERB}"
1368	if [ $? -eq 0 ]; then
1369		${ECHO} "  ${1} is added"
1370	    	return 0
1371	else
1372		${ECHO} "  ERROR: failed to add ${1}"
1373		return 1
1374	fi
1375    fi
1376
1377}
1378#
1379# Kerberos princiapl to DN mapping rules
1380#
1381# Add rules for host credentails and user credentials
1382#
1383add_id_mapping_rules() {
1384
1385    ${ECHO} "  Adding Kerberos principal to DN mapping rules..."
1386
1387    _C_DN="cn=GSSAPI,cn=identity mapping,cn=config"
1388    ( cat << EOF
1389dn: cn=GSSAPI,cn=identity mapping,cn=config
1390objectClass: top
1391objectClass: nsContainer
1392cn: GSSAPI
1393EOF
1394) > ${TMPDIR}/GSSAPI_container.ldif
1395
1396    add_entry_by_DN "${_C_DN}" "${TMPDIR}/GSSAPI_container.ldif"
1397    if [ $? -ne 0 ];
1398    then
1399    	${RM} ${TMPDIR}/GSSAPI_container.ldif
1400	return
1401    fi
1402
1403    _H_CN="host_auth_${LDAP_KRB_REALM}"
1404    _H_DN="cn=${_H_CN}, ${_C_DN}"
1405    ( cat << EOF
1406dn: ${_H_DN}
1407objectClass: top
1408objectClass: nsContainer
1409objectClass: dsIdentityMapping
1410objectClass: dsPatternMatching
1411cn: ${_H_CN}
1412dsMatching-pattern: \${Principal}
1413dsMatching-regexp: host\/(.*).${LDAP_DOMAIN}@${LDAP_KRB_REALM}
1414dsSearchBaseDN: ou=hosts,${LDAP_BASEDN}
1415dsSearchFilter: (&(objectClass=ipHost)(cn=\$1))
1416dsSearchScope: one
1417
1418EOF
1419) > ${TMPDIR}/${_H_CN}.ldif
1420
1421    add_entry_by_DN "${_H_DN}" "${TMPDIR}/${_H_CN}.ldif"
1422
1423    _U_CN="user_auth_${LDAP_KRB_REALM}"
1424    _U_DN="cn=${_U_CN}, ${_C_DN}"
1425    ( cat << EOF
1426dn: ${_U_DN}
1427objectClass: top
1428objectClass: nsContainer
1429objectClass: dsIdentityMapping
1430objectClass: dsPatternMatching
1431cn: ${_U_CN}
1432dsMatching-pattern: \${Principal}
1433dsMatching-regexp: (.*)@${LDAP_KRB_REALM}
1434dsMappedDN: uid=\$1,ou=People,${LDAP_BASEDN}
1435
1436EOF
1437) > ${TMPDIR}/${_U_CN}.ldif
1438
1439    add_entry_by_DN "${_U_DN}" "${TMPDIR}/${_U_CN}.ldif"
1440
1441}
1442
1443
1444#
1445# Modify ACL to allow root to read all the password and only self can read
1446# its own password when sasl/GSSAPI bind is used
1447#
1448modify_userpassword_acl_for_gssapi() {
1449
1450    _P_DN="ou=People,${LDAP_BASEDN}"
1451    _H_DN="ou=Hosts,${LDAP_BASEDN}"
1452    _P_ACI="self-read-pwd"
1453
1454    ${EVAL} "${LDAPSEARCH} ${LDAP_ARGS} -b \"${_P_DN}\" -s base \"objectclass=*\" > /dev/null 2>&1"
1455    if [ $? -ne 0 ]; then
1456	    ${ECHO} "  ${_P_DN} does not exist"
1457	# Not Found. Create a new entry
1458	( cat << EOF
1459dn: ${_P_DN}
1460ou: People
1461objectClass: top
1462objectClass: organizationalUnit
1463EOF
1464) > ${TMPDIR}/gssapi_people.ldif
1465
1466	add_entry_by_DN "${_P_DN}" "${TMPDIR}/gssapi_people.ldif"
1467    else
1468	${ECHO} "  ${_P_DN} already exists"
1469    fi
1470
1471    ${EVAL} "${LDAPSEARCH} ${LDAP_ARGS} -b \"${_P_DN}\" -s base \"objectclass=*\" aci > ${TMPDIR}/chk_gssapi_aci 2>&1"
1472
1473    if [ $? -eq 0 ]; then
1474	    ${EVAL} "${GREP} ${_P_ACI} ${TMPDIR}/chk_gssapi_aci > /dev/null 2>&1"
1475	    if [ $? -eq 0 ]; then
1476		${ECHO} "  userpassword ACL ${_P_ACI} already exists."
1477		return
1478	    else
1479		${ECHO} "  userpassword ACL ${_P_ACI} not found. Create a new one."
1480	    fi
1481    else
1482	${ECHO} "  Error searching aci for ${_P_DN}"
1483	cat ${TMPDIR}/chk_gssapi_aci
1484	cleanup
1485	exit 1
1486    fi
1487    ( cat << EOF
1488dn: ${_P_DN}
1489changetype: modify
1490add: aci
1491aci: (targetattr="userPassword")(version 3.0; acl self-read-pwd; allow (read,search) userdn="ldap:///self" and authmethod="sasl GSSAPI";)
1492-
1493add: aci
1494aci: (targetattr="userPassword")(version 3.0; acl host-read-pwd; allow (read,search) userdn="ldap:///cn=*+ipHostNumber=*,ou=Hosts,${LDAP_BASEDN}" and authmethod="sasl GSSAPI";)
1495EOF
1496) > ${TMPDIR}/user_gssapi.ldif
1497    LDAP_TYPE_OR_VALUE_EXISTS=20
1498    ${EVAL} "${LDAPMODIFY} ${LDAP_ARGS} -f ${TMPDIR}/user_gssapi.ldif ${VERB}"
1499
1500    case $? in
1501    0)
1502	${ECHO} "  ${_P_DN} uaserpassword ACL is updated."
1503	;;
1504    20)
1505	${ECHO} "  ${_P_DN} uaserpassword ACL already exists."
1506	;;
1507    *)
1508	${ECHO} "  ERROR: update of userpassword ACL for ${_P_DN} failed!"
1509	cleanup
1510	exit 1
1511	;;
1512    esac
1513}
1514#
1515# $1: objectclass or attributetyp
1516# $2: name
1517search_update_schema() {
1518
1519    ATTR="${1}es"
1520
1521    ${EVAL} "${LDAPSEARCH} ${LDAP_ARGS} -b cn=schema -s base \"objectclass=*\" ${ATTR} | ${GREP} -i \"${2}\" ${VERB}"
1522    if [ $? -ne 0 ]; then
1523	${ECHO} "${1} ${2} does not exist."
1524        update_schema_attr
1525        update_schema_obj
1526	SCHEMA_UPDATED=1
1527    else
1528	${ECHO} "${1} ${2} already exists. Schema has been updated"
1529    fi
1530}
1531
1532#
1533# $1: 1 - interactive, 0 - no
1534#
1535create_gssapi_profile() {
1536
1537
1538    if [ ${1} -eq 1 ]; then
1539        echo
1540        echo "You can create a sasl/GSSAPI enabled profile with default values now."
1541        get_confirm "Do you want to create a sasl/GSSAPI default profile ?" "n"
1542
1543        if [ $? -eq 0 ]; then
1544	    return
1545        fi
1546    fi
1547
1548    # Add profile container if it does not exist
1549    eval "${LDAPSEARCH} ${LDAP_ARGS} -b \"ou=profile,${LDAP_BASEDN}\" -s base \"objectclass=*\" > /dev/null 2>&1"
1550    if [ $? -ne 0 ]; then
1551	( cat << EOF
1552dn: ou=profile,${LDAP_BASEDN}
1553ou: profile
1554objectClass: top
1555objectClass: organizationalUnit
1556EOF
1557) > ${TMPDIR}/profile_people.ldif
1558
1559        add_entry_by_DN "ou=profile,${LDAP_BASEDN}" "${TMPDIR}/profile_people.ldif"
1560
1561    fi
1562
1563    search_update_schema "objectclass" "DUAConfigProfile"
1564
1565    _P_NAME="gssapi_${LDAP_KRB_REALM}"
1566    if [ ${1} -eq 1 ]; then
1567    	_P_TMP=${LDAP_PROFILE_NAME}
1568    	LDAP_PROFILE_NAME=${_P_NAME}
1569   	get_profile_name
1570        LDAP_GSSAPI_PROFILE=${LDAP_PROFILE_NAME}
1571    	LDAP_PROFILE_NAME=${_P_TMP}
1572    fi
1573
1574    _P_DN="cn=${LDAP_GSSAPI_PROFILE},ou=profile,${LDAP_BASEDN}"
1575    if [ ${DEL_OLD_PROFILE} -eq 1 ]; then
1576	    DEL_OLD_PROFILE=0
1577	    ${EVAL} "${LDAPDELETE} ${LDAP_ARGS} ${_P_DN} ${VERB}"
1578    fi
1579
1580    _SVR=`getent hosts ${IDS_SERVER} | ${NAWK} '{ print $1 }'`
1581    if [ ${IDS_PORT} -ne 389 ]; then
1582	    _SVR="${_SVR}:${IDS_PORT}"
1583    fi
1584
1585    (cat << EOF
1586dn: ${_P_DN}
1587objectClass: top
1588objectClass: DUAConfigProfile
1589defaultServerList: ${_SVR}
1590defaultSearchBase: ${LDAP_BASEDN}
1591authenticationMethod: sasl/GSSAPI
1592followReferrals: ${LDAP_FOLLOWREF}
1593defaultSearchScope: ${LDAP_SEARCH_SCOPE}
1594searchTimeLimit: ${LDAP_SEARCH_TIME_LIMIT}
1595profileTTL: ${LDAP_PROFILE_TTL}
1596cn: ${LDAP_GSSAPI_PROFILE}
1597credentialLevel: self
1598bindTimeLimit: ${LDAP_BIND_LIMIT}
1599EOF
1600) > ${TMPDIR}/gssapi_profile.ldif
1601
1602    add_entry_by_DN "${_P_DN}" "${TMPDIR}/gssapi_profile.ldif"
1603
1604}
1605#
1606# Set up GSSAPI if necessary
1607#
1608gssapi_setup() {
1609
1610	${EVAL} "${LDAPSEARCH} ${LDAP_ARGS} -b \"\" -s base \"objectclass=*\" supportedSASLMechanisms | ${GREP} GSSAPI ${VERB}"
1611	if [ $? -ne 0 ]; then
1612		${ECHO} "  sasl/GSSAPI is not supported by this LDAP server"
1613		return
1614	fi
1615
1616	get_confirm "GSSAPI is supported. Do you want to set up gssapi:(y/n)" "n"
1617	if [ $? -eq 0 ]; then
1618		${ECHO}
1619		${ECHO} "GSSAPI is not set up."
1620		${ECHO} "sasl/GSSAPI bind may not workif it's not set up before."
1621	else
1622		get_krb_realm
1623		add_id_mapping_rules
1624		modify_userpassword_acl_for_gssapi
1625		create_gssapi_profile 1
1626		${ECHO}
1627		${ECHO} "GSSAPI setup is done."
1628	fi
1629
1630	cat << EOF
1631
1632You can continue to create a profile and
1633configure the LDAP server.
1634Or you can stop now.
1635
1636EOF
1637	get_confirm "Do you want to stop:(y/n)" "n"
1638	if [ $? -eq 1 ]; then
1639		cleanup
1640		exit
1641	fi
1642
1643}
1644gssapi_setup_auto() {
1645	${EVAL} "${LDAPSEARCH} ${LDAP_ARGS} -b \"\" -s base \"objectclass=*\" supportedSASLMechanisms | ${GREP} GSSAPI ${VERB}"
1646	if [ $? -ne 0 ]; then
1647		${ECHO}
1648		${ECHO} "sasl/GSSAPI is not supported by this LDAP server"
1649		${ECHO}
1650		return
1651	fi
1652	if [ -z "${LDAP_KRB_REALM}" ]; then
1653		${ECHO}
1654		${ECHO} "LDAP_KRB_REALM is not set. Skip gssapi setup."
1655		${ECHO} "sasl/GSSAPI bind won't work properly."
1656		${ECHO}
1657		return
1658	fi
1659	if [ -z "${LDAP_GSSAPI_PROFILE}" ]; then
1660		${ECHO}
1661		${ECHO} "LDAP_GSSAPI_PROFILE is not set. Default is gssapi_${LDAP_KRB_REALM}"
1662		${ECHO}
1663		LDAP_GSSAPI_PROFILE="gssapi_${LDAP_KRB_REALM}"
1664	fi
1665	add_id_mapping_rules
1666	modify_userpassword_acl_for_gssapi
1667	create_gssapi_profile 0
1668}
1669# get_profile_name(): Enter the profile name.
1670#
1671get_profile_name()
1672{
1673    # Reset Delete Old Profile since getting new profile name.
1674    DEL_OLD_PROFILE=0
1675
1676    # Loop until valid profile name, or replace.
1677    while :
1678    do
1679	# Prompt for profile name.
1680	get_ans "Enter the profile name (h=help):" "$LDAP_PROFILE_NAME"
1681
1682	# Check for Help.
1683	case "$ANS" in
1684	    [Hh] | help | Help | \?) display_msg profile_help
1685				     continue ;;
1686	    * )  ;;
1687	esac
1688
1689	# Search to see if profile name already exists.
1690	eval "${LDAPSEARCH} ${LDAP_ARGS} -b \"cn=${ANS},ou=profile,${LDAP_BASEDN}\" -s base \"objectclass=*\" ${VERB}"
1691	if [ $? -eq 0 ]; then
1692	    get_confirm_nodef "Are you sure you want to overwire profile cn=${ANS}?"
1693	    if [ $? -eq 1 ]; then
1694		DEL_OLD_PROFILE=1
1695		return 0  # Replace old profile name.
1696	    else
1697		${ECHO} "Please re-enter a new profile name."
1698	    fi
1699	else
1700	    break  # Unique profile name.
1701	fi
1702    done
1703
1704    # Set Profile Name.
1705    LDAP_PROFILE_NAME=$ANS
1706}
1707
1708
1709#
1710# get_srv_list(): Get the default server list.
1711#
1712get_srv_list()
1713{
1714    # If LDAP_SERVER_LIST is NULL, then set, otherwise leave alone.
1715    if [ -z "${LDAP_SERVER_LIST}" ]; then
1716	LDAP_SERVER_LIST=`getent hosts ${IDS_SERVER} | awk '{print $1}'`
1717        if [ ${IDS_PORT} -ne 389 ]; then
1718	    LDAP_SERVER_LIST="${LDAP_SERVER_LIST}:${IDS_PORT}"
1719	fi
1720    fi
1721
1722    # Prompt for new LDAP_SERVER_LIST.
1723    while :
1724    do
1725	get_ans "Default server list (h=help):" $LDAP_SERVER_LIST
1726
1727	# If help continue, otherwise break.
1728	case "$ANS" in
1729	    [Hh] | help | Help | \?) display_msg def_srvlist_help ;;
1730	    * ) break ;;
1731	esac
1732    done
1733    LDAP_SERVER_LIST=$ANS
1734}
1735
1736
1737#
1738# get_pref_srv(): The preferred server list (Overrides the server list)
1739#
1740get_pref_srv()
1741{
1742    while :
1743    do
1744	get_ans "Preferred server list (h=help):" $LDAP_PREF_SRVLIST
1745
1746	# If help continue, otherwise break.
1747	case "$ANS" in
1748	    [Hh] | help | Help | \?) display_msg pref_srvlist_help ;;
1749	    * ) break ;;
1750	esac
1751    done
1752    LDAP_PREF_SRVLIST=$ANS
1753}
1754
1755
1756#
1757# get_search_scope(): Get the search scope from the user.
1758#
1759get_search_scope()
1760{
1761    [ $DEBUG -eq 1 ] && ${ECHO} "In get_search_scope()"
1762
1763    _MENU_CHOICE=0
1764    while :
1765    do
1766	get_ans "Choose desired search scope (one, sub, h=help): " "one"
1767	_MENU_CHOICE=$ANS
1768	case "$_MENU_CHOICE" in
1769	    one) LDAP_SEARCH_SCOPE="one"
1770	       return 1 ;;
1771	    sub) LDAP_SEARCH_SCOPE="sub"
1772	       return 2 ;;
1773	    h) display_msg srch_scope_help ;;
1774	    *) ${ECHO} "Please enter \"one\", \"sub\", or \"h\"." ;;
1775	esac
1776    done
1777
1778}
1779
1780
1781#
1782# get_cred_level(): Function to display menu to user and get the
1783#                  credential level.
1784#
1785get_cred_level()
1786{
1787    [ $DEBUG -eq 1 ] && ${ECHO} "In get_cred_level()"
1788
1789    _MENU_CHOICE=0
1790    display_msg cred_level_menu
1791    while :
1792    do
1793	get_ans "Choose Credential level [h=help]:" "1"
1794	_MENU_CHOICE=$ANS
1795	case "$_MENU_CHOICE" in
1796	    1) LDAP_CRED_LEVEL="anonymous"
1797	       return 1 ;;
1798	    2) LDAP_CRED_LEVEL="proxy"
1799	       return 2 ;;
1800	    3) LDAP_CRED_LEVEL="proxy anonymous"
1801	       return 3 ;;
1802	    4) LDAP_CRED_LEVEL="self"
1803	       SELF_GSSAPI=1
1804	       return 4 ;;
1805	    5) LDAP_CRED_LEVEL="self proxy"
1806	       SELF_GSSAPI=1
1807	       return 5 ;;
1808	    6) LDAP_CRED_LEVEL="self proxy anonymous"
1809	       SELF_GSSAPI=1
1810	       return 6 ;;
1811	    h) display_msg cred_lvl_help ;;
1812	    *) ${ECHO} "Please enter 1, 2, 3, 4, 5 or 6." ;;
1813	esac
1814    done
1815}
1816
1817
1818#
1819# srvauth_menu_handler(): Enter the Service Authentication method.
1820#
1821srvauth_menu_handler()
1822{
1823    # Display Auth menu
1824    display_msg srvauth_method_menu
1825
1826    # Get a Valid choice.
1827    while :
1828    do
1829	# Display appropriate prompt and get answer.
1830	if [ $_FIRST -eq 1 ]; then
1831	    get_ans "Choose Service Authentication Method:" "1"
1832	else
1833	    get_ans "Choose Service Authentication Method (0=reset):"
1834	fi
1835
1836	# Determine choice.
1837	_MENU_CHOICE=$ANS
1838	case "$_MENU_CHOICE" in
1839	    1) _AUTHMETHOD="simple"
1840		break ;;
1841	    2) _AUTHMETHOD="sasl/DIGEST-MD5"
1842		break ;;
1843	    3) _AUTHMETHOD="tls:simple"
1844		break ;;
1845	    4) _AUTHMETHOD="tls:sasl/DIGEST-MD5"
1846		break ;;
1847	    5) _AUTHMETHOD="sasl/GSSAPI"
1848		break ;;
1849	    0) _AUTHMETHOD=""
1850		_FIRST=1
1851		break ;;
1852	    *) ${ECHO} "Please enter 1-5 or 0 to reset." ;;
1853	esac
1854    done
1855}
1856
1857
1858#
1859# auth_menu_handler(): Enter the Authentication method.
1860#
1861auth_menu_handler()
1862{
1863    # Display Auth menu
1864    display_msg auth_method_menu
1865
1866    # Get a Valid choice.
1867    while :
1868    do
1869	# Display appropriate prompt and get answer.
1870	if [ $_FIRST -eq 1 ]; then
1871	    get_ans "Choose Authentication Method (h=help):" "1"
1872	else
1873	    get_ans "Choose Authentication Method (0=reset, h=help):"
1874	fi
1875
1876	# Determine choice.
1877	_MENU_CHOICE=$ANS
1878	case "$_MENU_CHOICE" in
1879	    1) _AUTHMETHOD="none"
1880		break ;;
1881	    2) _AUTHMETHOD="simple"
1882		break ;;
1883	    3) _AUTHMETHOD="sasl/DIGEST-MD5"
1884		break ;;
1885	    4) _AUTHMETHOD="tls:simple"
1886		break ;;
1887	    5) _AUTHMETHOD="tls:sasl/DIGEST-MD5"
1888		break ;;
1889	    6) _AUTHMETHOD="sasl/GSSAPI"
1890		break ;;
1891	    0) _AUTHMETHOD=""
1892		_FIRST=1
1893		break ;;
1894	    h) display_msg auth_help ;;
1895	    *) ${ECHO} "Please enter 1-6, 0=reset, or h=help." ;;
1896	esac
1897    done
1898}
1899
1900
1901#
1902# get_auth(): Enter the Authentication method.
1903#
1904get_auth()
1905{
1906    [ $DEBUG -eq 1 ] && ${ECHO} "In get_auth()"
1907
1908    _FIRST=1          # Flag for first time.
1909    _MENU_CHOICE=0
1910    _AUTHMETHOD=""    # Tmp method.
1911
1912    while :
1913    do
1914	# Call Menu handler
1915	auth_menu_handler
1916
1917	# Add Auth Method to list.
1918        if [ $_FIRST -eq 1 ]; then
1919	    LDAP_AUTHMETHOD="${_AUTHMETHOD}"
1920	    _FIRST=0
1921	else
1922	    LDAP_AUTHMETHOD="${LDAP_AUTHMETHOD};${_AUTHMETHOD}"
1923	fi
1924
1925	# Display current Authentication Method.
1926	${ECHO} ""
1927	${ECHO} "Current authenticationMethod: ${LDAP_AUTHMETHOD}"
1928	${ECHO} ""
1929
1930	# Prompt for another Auth Method, or break out.
1931	get_confirm_nodef "Do you want to add another Authentication Method?"
1932	if [ $? -eq 0 ]; then
1933	    break;
1934	fi
1935    done
1936}
1937
1938
1939#
1940# get_followref(): Whether or not to follow referrals.
1941#
1942get_followref()
1943{
1944    get_confirm "Do you want the clients to follow referrals (y/n/h)?" "n" "referrals_help"
1945    if [ $? -eq 1 ]; then
1946	LDAP_FOLLOWREF="TRUE"
1947    else
1948	LDAP_FOLLOWREF="FALSE"
1949    fi
1950}
1951
1952
1953#
1954# get_timelimit(): Set the time limit. -1 is max time.
1955#
1956get_timelimit()
1957{
1958    # Get current timeout value from cn=config.
1959    eval "${LDAPSEARCH} ${LDAP_ARGS} -b \"cn=config\" -s base \"objectclass=*\" nsslapd-timelimit > ${TMPDIR}/chk_timeout 2>&1"
1960    if [ $? -ne 0 ]; then
1961	${ECHO} "  ERROR: Could not reach LDAP server to check current timeout!"
1962	cleanup
1963	exit 1
1964    fi
1965    CURR_TIMELIMIT=`${GREP} timelimit ${TMPDIR}/chk_timeout | cut -f2 -d=`
1966
1967    get_negone_num "Enter the time limit for iDS (current=${CURR_TIMELIMIT}):" "-1"
1968    IDS_TIMELIMIT=$NUM
1969}
1970
1971
1972#
1973# get_sizelimit(): Set the size limit. -1 is max size.
1974#
1975get_sizelimit()
1976{
1977    # Get current sizelimit value from cn=config.
1978    eval "${LDAPSEARCH} ${LDAP_ARGS} -b \"cn=config\" -s base \"objectclass=*\" nsslapd-sizelimit > ${TMPDIR}/chk_sizelimit 2>&1"
1979    if [ $? -ne 0 ]; then
1980	${ECHO} "  ERROR: Could not reach LDAP server to check current sizelimit!"
1981	cleanup
1982	exit 1
1983    fi
1984    CURR_SIZELIMIT=`${GREP} sizelimit ${TMPDIR}/chk_sizelimit | cut -f2 -d=`
1985
1986    get_negone_num "Enter the size limit for iDS (current=${CURR_SIZELIMIT}):" "-1"
1987    IDS_SIZELIMIT=$NUM
1988}
1989
1990
1991#
1992# get_want_crypt(): Ask user if want to store passwords in crypt?
1993#
1994get_want_crypt()
1995{
1996    get_confirm "Do you want to store passwords in \"crypt\" format (y/n/h)?" "n" "crypt_help"
1997    if [ $? -eq 1 ]; then
1998	NEED_CRYPT="TRUE"
1999    else
2000	NEED_CRYPT="FALSE"
2001    fi
2002}
2003
2004
2005#
2006# get_srv_authMethod_pam(): Get the Service Auth Method for pam_ldap from user.
2007#
2008#  NOTE: This function is base on get_auth().
2009#
2010get_srv_authMethod_pam()
2011{
2012    [ $DEBUG -eq 1 ] && ${ECHO} "In get_srv_authMethod_pam()"
2013
2014    _FIRST=1          # Flag for first time.
2015    _MENU_CHOICE=0
2016    _AUTHMETHOD=""    # Tmp method.
2017
2018    while :
2019    do
2020	# Call Menu handler
2021	srvauth_menu_handler
2022
2023	# Add Auth Method to list.
2024        if [ $_FIRST -eq 1 ]; then
2025	    if [ "$_AUTHMETHOD" = "" ]; then
2026		LDAP_SRV_AUTHMETHOD_PAM=""
2027	    else
2028		LDAP_SRV_AUTHMETHOD_PAM="pam_ldap:${_AUTHMETHOD}"
2029	    fi
2030	    _FIRST=0
2031	else
2032	    LDAP_SRV_AUTHMETHOD_PAM="${LDAP_SRV_AUTHMETHOD_PAM};${_AUTHMETHOD}"
2033	fi
2034
2035	# Display current Authentication Method.
2036	${ECHO} ""
2037	${ECHO} "Current authenticationMethod: ${LDAP_SRV_AUTHMETHOD_PAM}"
2038	${ECHO} ""
2039
2040	# Prompt for another Auth Method, or break out.
2041	get_confirm_nodef "Do you want to add another Authentication Method?"
2042	if [ $? -eq 0 ]; then
2043	    break;
2044	fi
2045    done
2046
2047    # Check in case user reset string and exited loop.
2048    if [ "$LDAP_SRV_AUTHMETHOD_PAM" = "" ]; then
2049	NEED_SRVAUTH_PAM=0
2050    fi
2051}
2052
2053
2054#
2055# get_srv_authMethod_key(): Get the Service Auth Method for keyserv from user.
2056#
2057#  NOTE: This function is base on get_auth().
2058#
2059get_srv_authMethod_key()
2060{
2061    [ $DEBUG -eq 1 ] && ${ECHO} "In get_srv_authMethod_key()"
2062
2063    _FIRST=1          # Flag for first time.
2064    _MENU_CHOICE=0
2065    _AUTHMETHOD=""    # Tmp method.
2066
2067    while :
2068    do
2069	# Call Menu handler
2070	srvauth_menu_handler
2071
2072	# Add Auth Method to list.
2073        if [ $_FIRST -eq 1 ]; then
2074	    if [ "$_AUTHMETHOD" = "" ]; then
2075		LDAP_SRV_AUTHMETHOD_KEY=""
2076	    else
2077		LDAP_SRV_AUTHMETHOD_KEY="keyserv:${_AUTHMETHOD}"
2078	    fi
2079	    _FIRST=0
2080	else
2081	    LDAP_SRV_AUTHMETHOD_KEY="${LDAP_SRV_AUTHMETHOD_KEY};${_AUTHMETHOD}"
2082	fi
2083
2084	# Display current Authentication Method.
2085	${ECHO} ""
2086	${ECHO} "Current authenticationMethod: ${LDAP_SRV_AUTHMETHOD_KEY}"
2087	${ECHO} ""
2088
2089	# Prompt for another Auth Method, or break out.
2090	get_confirm_nodef "Do you want to add another Authentication Method?"
2091	if [ $? -eq 0 ]; then
2092	    break;
2093	fi
2094    done
2095
2096    # Check in case user reset string and exited loop.
2097    if [ "$LDAP_SRV_AUTHMETHOD_KEY" = "" ]; then
2098	NEED_SRVAUTH_KEY=0
2099    fi
2100}
2101
2102
2103#
2104# get_srv_authMethod_cmd(): Get the Service Auth Method for passwd-cmd from user.
2105#
2106#  NOTE: This function is base on get_auth().
2107#
2108get_srv_authMethod_cmd()
2109{
2110    [ $DEBUG -eq 1 ] && ${ECHO} "In get_srv_authMethod_cmd()"
2111
2112    _FIRST=1          # Flag for first time.
2113    _MENU_CHOICE=0
2114    _AUTHMETHOD=""    # Tmp method.
2115
2116    while :
2117    do
2118	# Call Menu handler
2119	srvauth_menu_handler
2120
2121	# Add Auth Method to list.
2122        if [ $_FIRST -eq 1 ]; then
2123	    if [ "$_AUTHMETHOD" = "" ]; then
2124		LDAP_SRV_AUTHMETHOD_CMD=""
2125	    else
2126		LDAP_SRV_AUTHMETHOD_CMD="passwd-cmd:${_AUTHMETHOD}"
2127	    fi
2128	    _FIRST=0
2129	else
2130	    LDAP_SRV_AUTHMETHOD_CMD="${LDAP_SRV_AUTHMETHOD_CMD};${_AUTHMETHOD}"
2131	fi
2132
2133	# Display current Authentication Method.
2134	${ECHO} ""
2135	${ECHO} "Current authenticationMethod: ${LDAP_SRV_AUTHMETHOD_CMD}"
2136	${ECHO} ""
2137
2138	# Prompt for another Auth Method, or break out.
2139	get_confirm_nodef "Do you want to add another Authentication Method?"
2140	if [ $? -eq 0 ]; then
2141	    break;
2142	fi
2143    done
2144
2145    # Check in case user reset string and exited loop.
2146    if [ "$LDAP_SRV_AUTHMETHOD_CMD" = "" ]; then
2147	NEED_SRVAUTH_CMD=0
2148    fi
2149}
2150
2151
2152#
2153# get_srch_time(): Amount of time to search.
2154#
2155get_srch_time()
2156{
2157    get_negone_num "Client search time limit in seconds (h=help):" "$LDAP_SEARCH_TIME_LIMIT" "srchtime_help"
2158    LDAP_SEARCH_TIME_LIMIT=$NUM
2159}
2160
2161
2162#
2163# get_prof_ttl(): The profile time to live (TTL)
2164#
2165get_prof_ttl()
2166{
2167    get_negone_num "Profile Time To Live in seconds (h=help):" "$LDAP_PROFILE_TTL" "profttl_help"
2168    LDAP_PROFILE_TTL=$NUM
2169}
2170
2171
2172#
2173# get_bind_limit(): Bind time limit
2174#
2175get_bind_limit()
2176{
2177    get_negone_num "Bind time limit in seconds (h=help):" "$LDAP_BIND_LIMIT" "bindlim_help"
2178    LDAP_BIND_LIMIT=$NUM
2179}
2180
2181
2182######################################################################
2183# FUNCTIONS  FOR Service Search Descriptor's START HERE.
2184######################################################################
2185
2186
2187#
2188# add_ssd(): Get SSD's from user and add to file.
2189#
2190add_ssd()
2191{
2192    [ $DEBUG -eq 1 ] && ${ECHO} "In add_ssd()"
2193
2194    # Enter the service id.  Loop til unique.
2195    while :
2196    do
2197	get_ans "Enter the service id:"
2198	_SERV_ID=$ANS
2199
2200	# Grep for name existing.
2201	${GREP} -i "^$ANS:" ${SSD_FILE} > /dev/null 2>&1
2202	if [ $? -eq 1 ]; then
2203	    break
2204	fi
2205
2206	# Name exists, print message, let user decide.
2207	${ECHO} "ERROR: Service id ${ANS} already exists."
2208    done
2209
2210    get_ans "Enter the base:"
2211    _BASE=$ANS
2212
2213    # Get the scope and verify that its one or sub.
2214    while :
2215    do
2216	get_ans "Enter the scope:"
2217	_SCOPE=$ANS
2218	case `${ECHO} ${_SCOPE} | tr '[A-Z]' '[a-z]'` in
2219	    one) break ;;
2220	    sub) break ;;
2221	    *)   ${ECHO} "${_SCOPE} is Not valid - Enter 'one' or 'sub'" ;;
2222	esac
2223    done
2224
2225    # Build SSD to add to file.
2226    _SSD="${_SERV_ID}:${_BASE}?${_SCOPE}"
2227
2228    # Add the SSD to the file.
2229    ${ECHO} "${_SSD}" >> ${SSD_FILE}
2230}
2231
2232
2233#
2234# delete_ssd(): Delete a SSD from the list.
2235#
2236delete_ssd()
2237{
2238    [ $DEBUG -eq 1 ] && ${ECHO} "In delete_ssd()"
2239
2240    # Get service id name from user for SSD to delete.
2241    get_ans_req "Enter service id to delete:"
2242
2243    # Make sure service id exists.
2244    ${GREP} "$ANS" ${SSD_FILE} > /dev/null 2>&1
2245    if [ $? -eq 1 ]; then
2246	${ECHO} "Invalid service id: $ANS not present in list."
2247	return
2248    fi
2249
2250    # Create temporary back SSD file.
2251    cp ${SSD_FILE} ${SSD_FILE}.bak
2252    if [ $? -eq 1 ]; then
2253	${ECHO} "ERROR: could not create file: ${SSD_FILE}.bak"
2254	exit 1
2255    fi
2256
2257    # Use ${GREP} to remove the SSD.  Read from temp file
2258    # and write to the orig file.
2259    ${GREP} -v "$ANS" ${SSD_FILE}.bak > ${SSD_FILE}
2260}
2261
2262
2263#
2264# modify_ssd(): Allow user to modify a SSD.
2265#
2266modify_ssd()
2267{
2268    [ $DEBUG -eq 1 ] && ${ECHO} "In modify_ssd()"
2269
2270    # Prompt user for service id.
2271    get_ans_req "Enter service id to modify:"
2272
2273    # Put into temp _LINE.
2274    _LINE=`${GREP} "^$ANS:" ${SSD_FILE}`
2275    if [ "$_LINE" = "" ]; then
2276	${ECHO} "Invalid service id: $ANS"
2277	return
2278    fi
2279
2280    # Display current filter for user to see.
2281    ${ECHO} ""
2282    ${ECHO} "Current SSD: $_LINE"
2283    ${ECHO} ""
2284
2285    # Get the defaults.
2286    _CURR_BASE=`${ECHO} $_LINE | cut -d: -f2 | cut -d'?' -f 1`
2287    _CURR_SCOPE=`${ECHO} $_LINE | cut -d: -f2 | cut -d'?' -f 2`
2288
2289    # Create temporary back SSD file.
2290    cp ${SSD_FILE} ${SSD_FILE}.bak
2291    if [ $? -eq 1 ]; then
2292	${ECHO} "ERROR: could not create file: ${SSD_FILE}.bak"
2293	cleanup
2294	exit 1
2295    fi
2296
2297    # Removed the old line.
2298    ${GREP} -v "^$ANS:" ${SSD_FILE}.bak > ${SSD_FILE} 2>&1
2299
2300    # New Entry
2301    _SERV_ID=$ANS
2302    get_ans_req "Enter the base:" "$_CURR_BASE"
2303    _BASE=$ANS
2304    get_ans_req "Enter the scope:" "$_CURR_SCOPE"
2305    _SCOPE=$ANS
2306
2307    # Build the new SSD.
2308    _SSD="${_SERV_ID}:${_BASE}?${_SCOPE}"
2309
2310    # Add the SSD to the file.
2311    ${ECHO} "${_SSD}" >> ${SSD_FILE}
2312}
2313
2314
2315#
2316# display_ssd(): Display the current SSD list.
2317#
2318display_ssd()
2319{
2320    [ $DEBUG -eq 1 ] && ${ECHO} "In display_ssd()"
2321
2322    ${ECHO} ""
2323    ${ECHO} "Current Service Search Descriptors:"
2324    ${ECHO} "=================================="
2325    cat ${SSD_FILE}
2326    ${ECHO} ""
2327    ${ECHO} "Hit return to continue."
2328    read __A
2329}
2330
2331
2332#
2333# prompt_ssd(): Get SSD's from user.
2334#
2335prompt_ssd()
2336{
2337    [ $DEBUG -eq 1 ] && ${ECHO} "In prompt_ssd()"
2338    # See if user wants SSD's?
2339    get_confirm "Do you wish to setup Service Search Descriptors (y/n/h)?" "n" "ssd_help"
2340    [ "$?" -eq 0 ] && return
2341
2342    # Display menu for SSD choices.
2343    while :
2344    do
2345	display_msg prompt_ssd_menu
2346	get_ans "Enter menu choice:" "Quit"
2347	case "$ANS" in
2348	    [Aa] | add) add_ssd ;;
2349	    [Dd] | delete) delete_ssd ;;
2350	    [Mm] | modify) modify_ssd ;;
2351	    [Pp] | print | display) display_ssd ;;
2352	    [Xx] | reset | clear) reset_ssd_file ;;
2353	    [Hh] | Help | help)	display_msg ssd_menu_help
2354				${ECHO} " Press return to continue."
2355				read __A ;;
2356	    [Qq] | Quit | quit)	return ;;
2357	    *)    ${ECHO} "Invalid choice: $ANS please re-enter from menu." ;;
2358	esac
2359    done
2360}
2361
2362
2363#
2364# reset_ssd_file(): Blank out current SSD file.
2365#
2366reset_ssd_file()
2367{
2368    [ $DEBUG -eq 1 ] && ${ECHO} "In reset_ssd_file()"
2369
2370    rm -f ${SSD_FILE}
2371    touch ${SSD_FILE}
2372}
2373
2374
2375#
2376# create_ssd_file(): Create a temporary file for SSD's.
2377#
2378create_ssd_file()
2379{
2380    [ $DEBUG -eq 1 ] && ${ECHO} "In create_ssd_file()"
2381
2382    # Build a list of SSD's and store in temp file.
2383    ${GREP} "LDAP_SERV_SRCH_DES=" ${INPUT_FILE} | \
2384	sed 's/LDAP_SERV_SRCH_DES=//' \
2385	> ${SSD_FILE}
2386}
2387
2388
2389#
2390# ssd_2_config(): Append the SSD file to the output file.
2391#
2392ssd_2_config()
2393{
2394    [ $DEBUG -eq 1 ] && ${ECHO} "In ssd_2_config()"
2395
2396    # Convert to config file format using sed.
2397    sed -e "s/^/LDAP_SERV_SRCH_DES=/" ${SSD_FILE} >> ${OUTPUT_FILE}
2398}
2399
2400
2401#
2402# ssd_2_profile(): Add SSD's to the GEN_CMD string.
2403#
2404ssd_2_profile()
2405{
2406    [ $DEBUG -eq 1 ] && ${ECHO} "In ssd_2_profile()"
2407
2408    GEN_TMPFILE=${TMPDIR}/ssd_tmpfile
2409    touch ${GEN_TMPFILE}
2410
2411    # Add and convert each SSD to string.
2412    while read SSD_LINE
2413    do
2414	${ECHO} " -a \"serviceSearchDescriptor=${SSD_LINE}\"\c" >> ${GEN_TMPFILE}
2415    done <${SSD_FILE}
2416
2417    # Add SSD's to GEN_CMD.
2418    GEN_CMD="${GEN_CMD} `cat ${GEN_TMPFILE}`"
2419}
2420
2421
2422#
2423# prompt_config_info(): This function prompts the user for the config
2424# info that is not specified in the input file.
2425#
2426prompt_config_info()
2427{
2428    [ $DEBUG -eq 1 ] && ${ECHO} "In prompt_config_info()"
2429
2430    # Prompt for iDS server name.
2431    get_ids_server
2432
2433    # Prompt for iDS port number.
2434    get_ids_port
2435
2436    # Check iDS version for compatibility.
2437    chk_ids_version
2438
2439    # Check if the server supports the VLV.
2440    chk_vlv_indexes
2441
2442    # Get the Directory manager DN and passwd.
2443    get_dirmgr_dn
2444    get_dirmgr_pw
2445
2446    #
2447    # LDAP CLIENT PROFILE SPECIFIC INFORMATION.
2448    #   (i.e. The fields that show up in the profile.)
2449    #
2450    get_domain "domain_help"
2451
2452    get_basedn
2453
2454    gssapi_setup
2455
2456    get_profile_name
2457    get_srv_list
2458    get_pref_srv
2459    get_search_scope
2460
2461    # If cred is "anonymous", make auth == "none"
2462    get_cred_level
2463    if [ "$LDAP_CRED_LEVEL" != "anonymous" ]; then
2464	get_auth
2465    fi
2466
2467    get_followref
2468
2469    # Query user about timelimt.
2470    get_confirm "Do you want to modify the server timelimit value (y/n/h)?" "n" "tlim_help"
2471    NEED_TIME=$?
2472    [ $NEED_TIME -eq 1 ] && get_timelimit
2473
2474    # Query user about sizelimit.
2475    get_confirm "Do you want to modify the server sizelimit value (y/n/h)?" "n" "slim_help"
2476    NEED_SIZE=$?
2477    [ $NEED_SIZE -eq 1 ] && get_sizelimit
2478
2479    # Does the user want to store passwords in crypt format?
2480    get_want_crypt
2481
2482    # Prompt for any Service Authentication Methods?
2483    get_confirm "Do you want to setup a Service Authentication Methods (y/n/h)?" "n" "srvauth_help"
2484    if [ $? -eq 1 ]; then
2485	# Does the user want to set Service Authentication Method for pam_ldap?
2486	get_confirm "Do you want to setup a Service Auth. Method for \"pam_ldap\" (y/n/h)?" "n" "pam_ldap_help"
2487	NEED_SRVAUTH_PAM=$?
2488	[ $NEED_SRVAUTH_PAM -eq 1 ] && get_srv_authMethod_pam
2489
2490	# Does the user want to set Service Authentication Method for keyserv?
2491	get_confirm "Do you want to setup a Service Auth. Method for \"keyserv\" (y/n/h)?" "n" "keyserv_help"
2492	NEED_SRVAUTH_KEY=$?
2493	[ $NEED_SRVAUTH_KEY -eq 1 ] && get_srv_authMethod_key
2494
2495	# Does the user want to set Service Authentication Method for passwd-cmd?
2496	get_confirm "Do you want to setup a Service Auth. Method for \"passwd-cmd\" (y/n/h)?" "n" "passwd-cmd_help"
2497	NEED_SRVAUTH_CMD=$?
2498	[ $NEED_SRVAUTH_CMD -eq 1 ] && get_srv_authMethod_cmd
2499    fi
2500
2501
2502    # Get Timeouts
2503    get_srch_time
2504    get_prof_ttl
2505    get_bind_limit
2506
2507    # Reset the sdd_file and prompt user for SSD.  Will use menus
2508    # to build an SSD File.
2509    reset_ssd_file
2510    prompt_ssd
2511
2512    # Display FULL debugging info.
2513    disp_full_debug
2514
2515    # Extra blank line to separate prompt lines from steps.
2516    ${ECHO} " "
2517}
2518
2519
2520######################################################################
2521# FUNCTIONS  FOR display_summary() START HERE.
2522######################################################################
2523
2524
2525#
2526# get_proxyagent(): Get the proxyagent DN.
2527#
2528get_proxyagent()
2529{
2530    LDAP_PROXYAGENT="cn=proxyagent,ou=profile,${LDAP_BASEDN}"  # default
2531    get_ans "Enter DN for proxy agent:" "$LDAP_PROXYAGENT"
2532    LDAP_PROXYAGENT=$ANS
2533}
2534
2535
2536#
2537# get_proxy_pw(): Get the proxyagent passwd.
2538#
2539get_proxy_pw()
2540{
2541    get_passwd "Enter passwd for proxyagent:"
2542    LDAP_PROXYAGENT_CRED=$ANS
2543}
2544
2545
2546#
2547# display_summary(): Display a summary of values entered and let the
2548#                    user modify values at will.
2549#
2550display_summary()
2551{
2552    [ $DEBUG -eq 1 ] && ${ECHO} "In display_summary()"
2553
2554    # Create lookup table for function names.  First entry is dummy for
2555    # shift.
2556    TBL1="dummy"
2557    TBL2="get_domain get_basedn get_profile_name"
2558    TBL3="get_srv_list get_pref_srv get_search_scope get_cred_level"
2559    TBL4="get_auth get_followref"
2560    TBL5="get_timelimit get_sizelimit get_want_crypt"
2561    TBL6="get_srv_authMethod_pam get_srv_authMethod_key get_srv_authMethod_cmd"
2562    TBL7="get_srch_time get_prof_ttl get_bind_limit"
2563    TBL8="prompt_ssd"
2564    FUNC_TBL="$TBL1 $TBL2 $TBL3 $TBL4 $TBL5 $TBL6 $TBL7 $TBL8"
2565
2566    # Since menu prompt string is long, set here.
2567    _MENU_PROMPT="Enter config value to change: (1-19 0=commit changes)"
2568
2569    # Infinite loop.  Test for 0, and break in loop.
2570    while :
2571    do
2572	# Display menu and get value in range.
2573	display_msg summary_menu
2574	get_menu_choice "${_MENU_PROMPT}" "0" "19" "0"
2575	_CH=$MN_CH
2576
2577	# Make sure where not exiting.
2578	if [ $_CH -eq 0 ]; then
2579	    break       # Break out of loop if 0 selected.
2580	fi
2581
2582	# Call appropriate function from function table.
2583	set $FUNC_TBL
2584	shift $_CH
2585	$1          # Call the appropriate function.
2586    done
2587
2588    # If cred level is still see if user wants a change?
2589    if ${ECHO} "$LDAP_CRED_LEVEL" | ${GREP} "proxy" > /dev/null 2>&1
2590    then
2591	if [ "$LDAP_AUTHMETHOD" != "none" ]; then
2592	    NEED_PROXY=1    # I assume integer test is faster?
2593	    get_proxyagent
2594	    get_proxy_pw
2595	else
2596	    ${ECHO} "WARNING: Since Authentication method is 'none'."
2597	    ${ECHO} "         Credential level will be set to 'anonymous'."
2598	    LDAP_CRED_LEVEL="anonymous"
2599	fi
2600    fi
2601
2602    # Display FULL debugging info.
2603    disp_full_debug
2604
2605    # Final confirmation message. (ARE YOU SURE!)
2606    ${ECHO} " "
2607    get_confirm_nodef "WARNING: About to start committing changes. (y=continue, n=EXIT)"
2608    if [ $? -eq 0 ]; then
2609	${ECHO} "Terminating setup without making changes at users request."
2610	cleanup
2611	exit 1
2612    fi
2613
2614    # Print newline
2615    ${ECHO} " "
2616}
2617
2618
2619#
2620# create_config_file(): Write config data to config file specified.
2621#
2622create_config_file()
2623{
2624    [ $DEBUG -eq 1 ] && ${ECHO} "In create_config_file()"
2625
2626    # If output file exists, delete it.
2627    [ -f $OUTPUT_FILE ] && rm $OUTPUT_FILE
2628
2629    # Create output file.
2630    cat > $OUTPUT_FILE <<EOF
2631#!/bin/sh
2632# $OUTPUT_FILE - This file contains configuration information for
2633#                Native LDAP.  Use the idsconfig tool to load it.
2634#
2635# WARNING: This file was generated by idsconfig, and is intended to
2636#          be loaded by idsconfig as is.  DO NOT EDIT THIS FILE!
2637#
2638IDS_SERVER="$IDS_SERVER"
2639IDS_PORT=$IDS_PORT
2640IDS_TIMELIMIT=$IDS_TIMELIMIT
2641IDS_SIZELIMIT=$IDS_SIZELIMIT
2642LDAP_ROOTDN="$LDAP_ROOTDN"
2643LDAP_ROOTPWD=$LDAP_ROOTPWD
2644LDAP_DOMAIN="$LDAP_DOMAIN"
2645LDAP_SUFFIX="$LDAP_SUFFIX"
2646LDAP_KRB_REALM="$LDAP_KRB_REALM"
2647LDAP_GSSAPI_PROFILE="$LDAP_GSSAPI_PROFILE"
2648
2649# Internal program variables that need to be set.
2650NEED_PROXY=$NEED_PROXY
2651NEED_TIME=$NEED_TIME
2652NEED_SIZE=$NEED_SIZE
2653NEED_CRYPT=$NEED_CRYPT
2654
2655# LDAP PROFILE related defaults
2656LDAP_PROFILE_NAME="$LDAP_PROFILE_NAME"
2657DEL_OLD_PROFILE=1
2658LDAP_BASEDN="$LDAP_BASEDN"
2659LDAP_SERVER_LIST="$LDAP_SERVER_LIST"
2660LDAP_AUTHMETHOD="$LDAP_AUTHMETHOD"
2661LDAP_FOLLOWREF=$LDAP_FOLLOWREF
2662LDAP_SEARCH_SCOPE="$LDAP_SEARCH_SCOPE"
2663NEED_SRVAUTH_PAM=$NEED_SRVAUTH_PAM
2664NEED_SRVAUTH_KEY=$NEED_SRVAUTH_KEY
2665NEED_SRVAUTH_CMD=$NEED_SRVAUTH_CMD
2666LDAP_SRV_AUTHMETHOD_PAM="$LDAP_SRV_AUTHMETHOD_PAM"
2667LDAP_SRV_AUTHMETHOD_KEY="$LDAP_SRV_AUTHMETHOD_KEY"
2668LDAP_SRV_AUTHMETHOD_CMD="$LDAP_SRV_AUTHMETHOD_CMD"
2669LDAP_SEARCH_TIME_LIMIT=$LDAP_SEARCH_TIME_LIMIT
2670LDAP_PREF_SRVLIST="$LDAP_PREF_SRVLIST"
2671LDAP_PROFILE_TTL=$LDAP_PROFILE_TTL
2672LDAP_CRED_LEVEL="$LDAP_CRED_LEVEL"
2673LDAP_BIND_LIMIT=$LDAP_BIND_LIMIT
2674
2675# Proxy Agent
2676LDAP_PROXYAGENT="$LDAP_PROXYAGENT"
2677LDAP_PROXYAGENT_CRED=$LDAP_PROXYAGENT_CRED
2678
2679# Export all the variables (just in case)
2680export IDS_HOME IDS_PORT LDAP_ROOTDN LDAP_ROOTPWD LDAP_SERVER_LIST LDAP_BASEDN
2681export LDAP_DOMAIN LDAP_SUFFIX LDAP_PROXYAGENT LDAP_PROXYAGENT_CRED
2682export NEED_PROXY
2683export LDAP_PROFILE_NAME LDAP_BASEDN LDAP_SERVER_LIST 
2684export LDAP_AUTHMETHOD LDAP_FOLLOWREF LDAP_SEARCH_SCOPE LDAP_SEARCH_TIME_LIMIT
2685export LDAP_PREF_SRVLIST LDAP_PROFILE_TTL LDAP_CRED_LEVEL LDAP_BIND_LIMIT
2686export NEED_SRVAUTH_PAM NEED_SRVAUTH_KEY NEED_SRVAUTH_CMD
2687export LDAP_SRV_AUTHMETHOD_PAM LDAP_SRV_AUTHMETHOD_KEY LDAP_SRV_AUTHMETHOD_CMD
2688export LDAP_SERV_SRCH_DES SSD_FILE LDAP_KRB_REALM LDAP_GSSAPI_PROFILE
2689
2690# Service Search Descriptors start here if present:
2691EOF
2692    # Add service search descriptors.
2693    ssd_2_config "${OUTPUT_FILE}"
2694
2695    # Add LDAP suffix preferences
2696    print_suffix_config >> "${OUTPUT_FILE}"
2697
2698    # Add the end of FILE tag.
2699    ${ECHO} "" >> ${OUTPUT_FILE}
2700    ${ECHO} "# End of $OUTPUT_FILE" >> ${OUTPUT_FILE}
2701}
2702
2703
2704#
2705# chk_vlv_indexes(): Do ldapsearch to see if server supports VLV.
2706#
2707chk_vlv_indexes()
2708{
2709    # Do ldapsearch to see if server supports VLV.
2710    ${LDAPSEARCH} ${SERVER_ARGS} -b "" -s base "objectclass=*" > ${TMPDIR}/checkVLV 2>&1
2711    eval "${GREP} 2.16.840.1.113730.3.4.9 ${TMPDIR}/checkVLV ${VERB}"
2712    if [ $? -ne 0 ]; then
2713	${ECHO} "ERROR: VLV is not supported on LDAP server!"
2714	cleanup
2715	exit 1
2716    fi
2717    [ $DEBUG -eq 1 ] && ${ECHO} "  VLV controls found on LDAP server."
2718}
2719
2720#
2721# get_backend(): this function gets the relevant backend
2722#                (database) for LDAP_BASED.
2723#                Description: set IDS_DATABASE; exit on failure.
2724#                Prerequisite: LDAP_BASEDN and LDAP_SUFFIX are
2725#                valid.
2726#
2727#                backend is retrieved from suffixes and subsuffixes
2728#                defined under "cn=mapping tree,cn=config". The
2729#                nsslapd-state attribute of these suffixes entries
2730#                is filled with either Backend, Disabled or referrals
2731#                related values. We only want those that have a true
2732#                backend database to select the relevant backend.
2733#
2734get_backend()
2735{
2736    [ $DEBUG -eq 1 ] && ${ECHO} "In get_backend()"
2737
2738    cur_suffix=${LDAP_BASEDN}
2739    prev_suffix=
2740    IDS_DATABASE=
2741    while [ "${cur_suffix}" != "${prev_suffix}" ]
2742    do
2743	[ $DEBUG -eq 1 ] && ${ECHO} "testing LDAP suffix: ${cur_suffix}"
2744	eval "${LDAPSEARCH} ${LDAP_ARGS} " \
2745		"-b \"cn=\\\"${cur_suffix}\\\",cn=mapping tree,cn=config\" " \
2746		"-s base nsslapd-state=Backend nsslapd-backend 2>&1 " \
2747		"| ${GREP} 'nsslapd-backend=' " \
2748		"> ${TMPDIR}/ids_database_name 2>&1"
2749	NUM_DBS=`wc -l ${TMPDIR}/ids_database_name | awk '{print $1}'`
2750	case ${NUM_DBS} in
2751	0) # not a suffix, or suffix not activated; try next
2752	    prev_suffix=${cur_suffix}
2753	    cur_suffix=`${ECHO} ${cur_suffix} | cut -f2- -d','`
2754	    ;;
2755	1) # suffix found; get database name
2756	    IDS_DATABASE=`cat ${TMPDIR}/ids_database_name | cut -d= -f2`
2757	    ;;
2758	*) # can not handle more than one database per suffix
2759	    ${ECHO} "ERROR: More than one database is configured "
2760	    ${ECHO} "       for $LDAP_SUFFIX!"
2761	    ${ECHO} "       $PROG can not configure suffixes where "
2762	    ${ECHO} "       more than one database is used for one suffix."
2763	    cleanup
2764	    exit 1
2765	    ;;
2766	esac
2767	if [ -n "${IDS_DATABASE}" ]; then
2768	    break
2769	fi
2770    done
2771
2772    if [ -z "${IDS_DATABASE}" ]; then
2773	# should not happen, since LDAP_BASEDN is supposed to be valid
2774	${ECHO} "Could not find a valid backend for ${LDAP_BASEDN}."
2775	${ECHO} "Exiting."
2776	cleanup
2777	exit 1
2778    fi
2779
2780    [ $DEBUG -eq 1 ] && ${ECHO} "IDS_DATABASE: ${IDS_DATABASE}"
2781}
2782
2783#
2784# validate_suffix(): This function validates ${LDAP_SUFFIX}
2785#                  THIS FUNCTION IS FOR THE LOAD CONFIG FILE OPTION.
2786#
2787validate_suffix()
2788{
2789    [ $DEBUG -eq 1 ] && ${ECHO} "In validate_suffix()"
2790
2791    # Check LDAP_SUFFIX is not null
2792    if [ -z "${LDAP_SUFFIX}" ]; then
2793	${ECHO} "Invalid suffix (null suffix)"
2794	cleanup
2795	exit 1
2796    fi
2797
2798    # Check LDAP_SUFFIX and LDAP_BASEDN are consistent
2799    # Convert to lower case for basename.
2800    format_string "${LDAP_BASEDN}"
2801    LOWER_BASEDN="${FMT_STR}"
2802    format_string "${LDAP_SUFFIX}"
2803    LOWER_SUFFIX="${FMT_STR}"
2804
2805    [ $DEBUG -eq 1 ] && ${ECHO} "LOWER_BASEDN: ${LOWER_BASEDN}"
2806    [ $DEBUG -eq 1 ] && ${ECHO} "LOWER_SUFFIX: ${LOWER_SUFFIX}"
2807
2808    if [ "${LOWER_BASEDN}" != "${LOWER_SUFFIX}" ]; then
2809    	sub_basedn=`basename "${LOWER_BASEDN}" "${LOWER_SUFFIX}"`
2810    	if [ "$sub_basedn" = "${LOWER_BASEDN}" ]; then
2811	    ${ECHO} "Invalid suffix ${LOWER_SUFFIX}"
2812	    ${ECHO} "for Base DN ${LOWER_BASEDN}"
2813	    cleanup
2814	    exit 1
2815	fi
2816    fi
2817
2818    # Check LDAP_SUFFIX does exist
2819    ${EVAL} "${LDAPSEARCH} ${LDAP_ARGS} -b \"${LDAP_SUFFIX}\" -s base \"objectclass=*\" > ${TMPDIR}/checkSuffix 2>&1" && return 0
2820
2821    # Well, suffix does not exist, try to prepare create it ...
2822    NEED_CREATE_SUFFIX=1
2823    prep_create_sfx_entry ||
2824    {
2825	cleanup
2826	exit 1
2827    }
2828    [ -n "${NEED_CREATE_BACKEND}" ] &&
2829    {
2830	# try to use id attr value of the suffix as a database name
2831	IDS_DATABASE=${_VAL}
2832	prep_create_sfx_backend
2833	case $? in
2834	1)	# cann't use the name we want, so we can either exit or use
2835		# some another available name - doing the last ...
2836		IDS_DATABASE=${IDS_DATABASE_AVAIL}
2837		;;
2838	2)	# unable to determine database name
2839		cleanup
2840		exit 1
2841		;;
2842	esac
2843    }
2844
2845    [ $DEBUG -eq 1 ] && ${ECHO} "Suffix $LDAP_SUFFIX, Database $IDS_DATABASE"
2846}
2847
2848#
2849# validate_info(): This function validates the basic info collected
2850#                  So that some problems are caught right away.
2851#                  THIS FUNCTION IS FOR THE LOAD CONFIG FILE OPTION.
2852#
2853validate_info()
2854{
2855    [ $DEBUG -eq 1 ] && ${ECHO} "In validate_info()"
2856
2857    # Set SERVER_ARGS, AUTH_ARGS, and LDAP_ARGS for the config file.
2858    SERVER_ARGS="-h ${IDS_SERVER} -p ${IDS_PORT}"
2859    AUTH_ARGS="-D \"${LDAP_ROOTDN}\" -j ${LDAP_ROOTPWF}"
2860    LDAP_ARGS="${SERVER_ARGS} ${AUTH_ARGS}"
2861    export SERVER_ARGS
2862
2863    # Check the Root DN and Root DN passwd.
2864    # Use eval instead of $EVAL because not part of setup. (validate)
2865    eval "${LDAPSEARCH} ${LDAP_ARGS} -b \"\" -s base \"objectclass=*\" > ${TMPDIR}/checkDN 2>&1"
2866    if [ $? -ne 0 ]; then
2867	eval "${GREP} credential ${TMPDIR}/checkDN ${VERB}"
2868	if [ $? -eq 0 ]; then
2869	    ${ECHO} "ERROR: Root DN passwd is invalid."
2870	else
2871	    ${ECHO} "ERROR2: Invalid Root DN <${LDAP_ROOTDN}>."
2872	fi
2873	cleanup
2874	exit 1
2875    fi
2876    [ $DEBUG -eq 1 ] && ${ECHO} "  RootDN ... OK"
2877    [ $DEBUG -eq 1 ] && ${ECHO} "  RootDN passwd ... OK"
2878
2879    # Check if the server supports the VLV.
2880    chk_vlv_indexes
2881    [ $DEBUG -eq 1 ] && ${ECHO} "  VLV indexes ... OK"
2882
2883    # Check LDAP suffix
2884    validate_suffix
2885    [ $DEBUG -eq 1 ] && ${ECHO} "  LDAP suffix ... OK"
2886}
2887
2888#
2889# format_string(): take a string as argument and set FMT_STR
2890# to be the same string formatted as follow:
2891# - only lower case characters
2892# - no unnecessary spaces around , and =
2893#
2894format_string()
2895{
2896    FMT_STR=`${ECHO} "$1" | tr '[A-Z]' '[a-z]' |
2897	sed -e 's/[ ]*,[ ]*/,/g' -e 's/[ ]*=[ ]*/=/g'`
2898}
2899
2900#
2901# prepare for the suffix entry creation
2902#
2903# input  : LDAP_BASEDN, LDAP_SUFFIX - base dn and suffix;
2904# in/out : LDAP_SUFFIX_OBJ, LDAP_SUFFIX_ACI - initially may come from config.
2905# output : NEED_CREATE_BACKEND - backend for this suffix needs to be created;
2906#          _RDN, _ATT, _VAL - suffix's RDN, id attribute name and its value.
2907# return : 0 - success, otherwise error.
2908#
2909prep_create_sfx_entry()
2910{
2911    [ $DEBUG -eq 1 ] && ${ECHO} "In prep_create_sfx_entry()"
2912
2913    # check whether suffix corresponds to base dn
2914    format_string "${LDAP_BASEDN}"
2915    ${ECHO} ",${FMT_STR}" | ${GREP} ",${LDAP_SUFFIX}$" >/dev/null 2>&1 ||
2916    {
2917	display_msg sfx_not_suitable
2918	return 1
2919    }
2920
2921    # parse LDAP_SUFFIX
2922    _RDN=`${ECHO} "${LDAP_SUFFIX}" | cut -d, -f1`
2923    _ATT=`${ECHO} "${_RDN}" | cut -d= -f1`
2924    _VAL=`${ECHO} "${_RDN}" | cut -d= -f2-`
2925
2926    # find out an objectclass for suffix entry if it is not defined yet
2927    [ -z "${LDAP_SUFFIX_OBJ}" ] &&
2928    {
2929	get_objectclass ${_ATT}
2930	[ -z "${_ATTR_NAME}" ] &&
2931	{
2932		display_msg obj_not_found
2933		return 1
2934	}
2935	LDAP_SUFFIX_OBJ=${_ATTR_NAME}
2936    }
2937    [ $DEBUG -eq 1 ] && ${ECHO} "Suffix entry object is ${LDAP_SUFFIX_OBJ}"
2938
2939    # find out an aci for suffix entry if it is not defined yet
2940    [ -z "${LDAP_SUFFIX_ACI}" ] &&
2941    {
2942	# set Directory Server default aci
2943	LDAP_SUFFIX_ACI=`cat <<EOF
2944aci: (targetattr != "userPassword || passwordHistory || passwordExpirationTime
2945 || passwordExpWarned || passwordRetryCount || retryCountResetTime ||
2946 accountUnlockTime || passwordAllowChangeTime")
2947 (
2948   version 3.0;
2949   acl "Anonymous access";
2950   allow (read, search, compare) userdn = "ldap:///anyone";
2951 )
2952aci: (targetattr != "nsroledn || aci || nsLookThroughLimit || nsSizeLimit ||
2953 nsTimeLimit || nsIdleTimeout || passwordPolicySubentry ||
2954 passwordExpirationTime || passwordExpWarned || passwordRetryCount ||
2955 retryCountResetTime || accountUnlockTime || passwordHistory ||
2956 passwordAllowChangeTime")
2957 (
2958   version 3.0;
2959   acl "Allow self entry modification except for some attributes";
2960   allow (write) userdn = "ldap:///self";
2961 )
2962aci: (targetattr = "*")
2963 (
2964   version 3.0;
2965   acl "Configuration Administrator";
2966   allow (all) userdn = "ldap:///uid=admin,ou=Administrators,
2967                         ou=TopologyManagement,o=NetscapeRoot";
2968 )
2969aci: (targetattr ="*")
2970 (
2971   version 3.0;
2972   acl "Configuration Administrators Group";
2973   allow (all) groupdn = "ldap:///cn=Configuration Administrators,
2974                          ou=Groups,ou=TopologyManagement,o=NetscapeRoot";
2975 )
2976EOF
2977`
2978    }
2979    [ $DEBUG -eq 1 ] && cat <<EOF
2980DEBUG: ACI for ${LDAP_SUFFIX} is
2981${LDAP_SUFFIX_ACI}
2982EOF
2983
2984    NEED_CREATE_BACKEND=
2985
2986    # check the suffix mapping tree ...
2987    # if mapping exists, suffix should work, otherwise DS inconsistent
2988    # NOTE: -b 'cn=mapping tree,cn=config' -s one 'cn=\"$1\"' won't work
2989    #       in case of 'cn' value in LDAP is not quoted by '"',
2990    #       -b 'cn=\"$1\",cn=mapping tree,cn=config' works in all cases
2991    ${EVAL} "${LDAPSEARCH} ${LDAP_ARGS} \
2992	-b 'cn=\"${LDAP_SUFFIX}\",cn=mapping tree,cn=config' \
2993	-s base 'objectclass=*' dn ${VERB}" &&
2994    {
2995	[ $DEBUG -eq 1 ] && ${ECHO} "Suffix mapping already exists"
2996	# get_backend() either gets IDS_DATABASE or exits
2997	get_backend
2998	return 0
2999    }
3000
3001    # no suffix mapping, just in case check ldbm backends consistency -
3002    # there are must be NO any databases pointing to LDAP_SUFFIX
3003    [ -n "`${EVAL} \"${LDAPSEARCH} ${LDAP_ARGS} \
3004	-b 'cn=ldbm database,cn=plugins,cn=config' \
3005	-s one 'nsslapd-suffix=${LDAP_SUFFIX}' dn\" 2>/dev/null`" ] &&
3006    {
3007	display_msg sfx_config_incons
3008	return 1
3009    }
3010
3011    # ok, no suffix mapping, no ldbm database
3012    [ $DEBUG -eq 1 ] && ${ECHO} "DEBUG: backend needs to be created ..."
3013    NEED_CREATE_BACKEND=1
3014    return 0
3015}
3016
3017#
3018# prepare for the suffix backend creation
3019#
3020# input  : IDS_DATABASE - requested ldbm db name (must be not null)
3021# in/out : IDS_DATABASE_AVAIL - available ldbm db name
3022# return : 0 - ldbm db name ok
3023#          1 - IDS_DATABASE exists,
3024#              so IDS_DATABASE_AVAIL contains available name
3025#          2 - unable to find any available name
3026#
3027prep_create_sfx_backend()
3028{
3029    [ $DEBUG -eq 1 ] && ${ECHO} "In prep_create_sfx_backend()"
3030
3031    # check if requested name available
3032    [ "${IDS_DATABASE}" = "${IDS_DATABASE_AVAIL}" ] && return 0
3033
3034    # get the list of database names start with a requested name
3035    _LDBM_DBS=`${EVAL} "${LDAPSEARCH} ${LDAP_ARGS} \
3036	-b 'cn=ldbm database,cn=plugins,cn=config' \
3037	-s one 'cn=${IDS_DATABASE}*' cn"` 2>/dev/null
3038
3039    # find available db name based on a requested name
3040    _i=""; _i_MAX=10
3041    while [ ${_i:-0} -lt ${_i_MAX} ]
3042    do
3043	_name="${IDS_DATABASE}${_i}"
3044	${ECHO} "${_LDBM_DBS}" | ${GREP} -i "^cn=${_name}$" >/dev/null 2>&1 ||
3045	{
3046		IDS_DATABASE_AVAIL="${_name}"
3047		break
3048	}
3049	_i=`expr ${_i:-0} + 1`
3050    done
3051
3052    [ "${IDS_DATABASE}" = "${IDS_DATABASE_AVAIL}" ] && return 0
3053
3054    [ -n "${IDS_DATABASE_AVAIL}" ] &&
3055    {
3056	display_msg ldbm_db_exist
3057	return 1
3058    }
3059
3060    display_msg unable_find_db_name
3061    return 2
3062}
3063
3064#
3065# add suffix if needed,
3066#     suffix entry and backend MUST be prepared by
3067#     prep_create_sfx_entry and prep_create_sfx_backend correspondingly
3068#
3069# input  : NEED_CREATE_SUFFIX, LDAP_SUFFIX, LDAP_SUFFIX_OBJ, _ATT, _VAL
3070#          LDAP_SUFFIX_ACI, NEED_CREATE_BACKEND, IDS_DATABASE
3071# return : 0 - suffix successfully created, otherwise error occured
3072#
3073add_suffix()
3074{
3075    [ $DEBUG -eq 1 ] && ${ECHO} "In add_suffix()"
3076
3077    [ -n "${NEED_CREATE_SUFFIX}" ] || return 0
3078
3079    [ -n "${NEED_CREATE_BACKEND}" ] &&
3080    {
3081	${EVAL} "${LDAPADD} ${LDAP_ARGS} ${VERB}" <<EOF
3082dn: cn="${LDAP_SUFFIX}",cn=mapping tree,cn=config
3083objectclass: top
3084objectclass: extensibleObject
3085objectclass: nsMappingTree
3086cn: ${LDAP_SUFFIX}
3087nsslapd-state: backend
3088nsslapd-backend: ${IDS_DATABASE}
3089
3090dn: cn=${IDS_DATABASE},cn=ldbm database,cn=plugins,cn=config
3091objectclass: top
3092objectclass: extensibleObject
3093objectclass: nsBackendInstance
3094cn: ${IDS_DATABASE}
3095nsslapd-suffix: ${LDAP_SUFFIX}
3096EOF
3097	[ $? -ne 0 ] &&
3098	{
3099		display_msg create_ldbm_db_error
3100		return 1
3101	}
3102
3103	${ECHO} "  ${STEP}. Database ${IDS_DATABASE} successfully created"
3104	STEP=`expr $STEP + 1`
3105    }
3106
3107    ${EVAL} "${LDAPADD} ${LDAP_ARGS} ${VERB}" <<EOF
3108dn: ${LDAP_SUFFIX}
3109objectclass: ${LDAP_SUFFIX_OBJ}
3110${_ATT}: ${_VAL}
3111${LDAP_SUFFIX_ACI}
3112EOF
3113    [ $? -ne 0 ] &&
3114    {
3115	display_msg create_suffix_entry_error
3116	return 1
3117    }
3118
3119    ${ECHO} "  ${STEP}. Suffix ${LDAP_SUFFIX} successfully created"
3120    STEP=`expr $STEP + 1`
3121    return 0
3122}
3123
3124#
3125# interactively get suffix and related info from a user
3126#
3127# input  : LDAP_BASEDN - Base DN
3128# output : LDAP_SUFFIX - Suffix, _ATT, _VAL - id attribute and its value;
3129#          LDAP_SUFFIX_OBJ, LDAP_SUFFIX_ACI - objectclass and aci;
3130#          NEED_CREATE_BACKEND - tells whether backend needs to be created;
3131#          IDS_DATABASE - prepared ldbm db name
3132# return : 0 - user gave a correct suffix
3133#          1 - suffix given by user cann't be created
3134#
3135get_suffix()
3136{
3137    [ $DEBUG -eq 1 ] && ${ECHO} "In get_suffix()"
3138
3139    while :
3140    do
3141	get_ans "Enter suffix to be created (b=back/h=help):" ${LDAP_BASEDN}
3142	case "${ANS}" in
3143	[Hh] | Help | help | \? ) display_msg create_suffix_help ;;
3144	[Bb] | Back | back | \< ) return 1 ;;
3145	* )
3146		format_string "${ANS}"
3147		LDAP_SUFFIX=${FMT_STR}
3148		prep_create_sfx_entry || continue
3149
3150		[ -n "${NEED_CREATE_BACKEND}" ] &&
3151		{
3152		    IDS_DATABASE_AVAIL= # reset the available db name
3153
3154		    reenter_suffix=
3155		    while :
3156		    do
3157			get_ans "Enter ldbm database name (b=back/h=help):" \
3158				${IDS_DATABASE_AVAIL:-${_VAL}}
3159			case "${ANS}" in
3160			[Hh] | \? ) display_msg enter_ldbm_db_help ;;
3161			[Bb] | \< ) reenter_suffix=1; break ;;
3162			* )
3163				IDS_DATABASE="${ANS}"
3164				prep_create_sfx_backend && break
3165			esac
3166		    done
3167		    [ -n "${reenter_suffix}" ] && continue
3168
3169		    [ $DEBUG -eq 1 ] && cat <<EOF
3170DEBUG: backend name for suffix ${LDAP_SUFFIX} will be ${IDS_DATABASE}
3171EOF
3172		}
3173
3174		# eventually everything is prepared
3175		return 0
3176		;;
3177	esac
3178    done
3179}
3180
3181#
3182# print out a script which sets LDAP suffix related preferences
3183#
3184print_suffix_config()
3185{
3186    cat <<EOF2
3187# LDAP suffix related preferences used only if needed
3188IDS_DATABASE="${IDS_DATABASE}" 
3189LDAP_SUFFIX_OBJ="$LDAP_SUFFIX_OBJ"
3190LDAP_SUFFIX_ACI=\`cat <<EOF
3191${LDAP_SUFFIX_ACI}
3192EOF
3193\`
3194export IDS_DATABASE LDAP_SUFFIX_OBJ LDAP_SUFFIX_ACI
3195EOF2
3196}
3197
3198#
3199# check_basedn_suffix(): check that there is an existing
3200# valid suffix to hold current base DN
3201# return:
3202#   0: valid suffix found or new one should be created,
3203#      NEED_CREATE_SUFFIX flag actually indicates that
3204#   1: some error occures
3205#
3206check_basedn_suffix()
3207{
3208    [ $DEBUG -eq 1 ] && ${ECHO} "In check_basedn_suffix()"
3209
3210    NEED_CREATE_SUFFIX=
3211
3212    # find out existing suffixes
3213    discover_serv_suffix
3214
3215    ${ECHO} "  Validating LDAP Base DN and Suffix ..."
3216
3217    # check that LDAP Base DN might be added
3218    cur_ldap_entry=${LDAP_BASEDN}
3219    prev_ldap_entry=
3220    while [ "${cur_ldap_entry}" != "${prev_ldap_entry}" ]
3221    do
3222	[ $DEBUG -eq 1 ] && ${ECHO} "testing LDAP entry: ${cur_ldap_entry}"
3223	${LDAPSEARCH} ${SERVER_ARGS} -b "${cur_ldap_entry}" \
3224		-s one "objectclass=*" > /dev/null 2>&1
3225	if [ $? -eq 0 ]; then
3226	    break
3227	else
3228	    prev_ldap_entry=${cur_ldap_entry}
3229	    cur_ldap_entry=`${ECHO} ${cur_ldap_entry} | cut -f2- -d','`
3230	fi
3231    done
3232
3233    if [ "${cur_ldap_entry}" = "${prev_ldap_entry}" ]; then
3234	${ECHO} "  No valid suffixes were found for Base DN ${LDAP_BASEDN}"
3235
3236	NEED_CREATE_SUFFIX=1
3237	return 0
3238
3239    else
3240	[ $DEBUG -eq 1 ] && ${ECHO} "found valid LDAP entry: ${cur_ldap_entry}"
3241
3242	# Now looking for relevant suffix for this entry.
3243	# LDAP_SUFFIX will then be used to add necessary
3244	# base objects. See add_base_objects().
3245	format_string "${cur_ldap_entry}"
3246	lower_entry="${FMT_STR}"
3247	[ $DEBUG -eq 1 ] && ${ECHO} "final suffix list: ${LDAP_SUFFIX_LIST}"
3248	oIFS=$IFS
3249	[ $DEBUG -eq 1 ] && ${ECHO} "setting IFS to new line"
3250	IFS='
3251'
3252	for suff in ${LDAP_SUFFIX_LIST}
3253	do
3254	    [ $DEBUG -eq 1 ] && ${ECHO} "testing suffix: ${suff}"
3255	    format_string "${suff}"
3256	    lower_suff="${FMT_STR}"
3257	    if [ "${lower_entry}" = "${lower_suff}" ]; then
3258		LDAP_SUFFIX="${suff}"
3259		break
3260	    else
3261		dcstmp=`basename "${lower_entry}" "${lower_suff}"`
3262		if [ "${dcstmp}" = "${lower_entry}" ]; then
3263		    # invalid suffix, try next one
3264		    continue
3265		else
3266		    # valid suffix found
3267		    LDAP_SUFFIX="${suff}"
3268		    break
3269		fi
3270	    fi
3271	done
3272	[ $DEBUG -eq 1 ] && ${ECHO} "setting IFS to original value"
3273	IFS=$oIFS
3274
3275	[ $DEBUG -eq 1 ] && ${ECHO} "LDAP_SUFFIX: ${LDAP_SUFFIX}"
3276
3277	if [ -z "${LDAP_SUFFIX}" ]; then
3278	    # should not happen, since we found the entry
3279	    ${ECHO} "Could not find a valid suffix for ${LDAP_BASEDN}."
3280	    ${ECHO} "Exiting."
3281	    return 1
3282	fi
3283
3284	# Getting relevant database (backend)
3285	# IDS_DATABASE will then be used to create indexes.
3286	get_backend
3287
3288	return 0
3289    fi
3290}
3291
3292#
3293# discover_serv_suffix(): This function queries the server to find
3294#    suffixes available
3295#  return: 0: OK, suffix found
3296#          1: suffix not determined
3297discover_serv_suffix()
3298{
3299    [ $DEBUG -eq 1 ] && ${ECHO} "In discover_serv_suffix()"
3300
3301    # Search the server for the TOP of the TREE.
3302    ${LDAPSEARCH} ${SERVER_ARGS} -b "" -s base "objectclass=*" > ${TMPDIR}/checkTOP 2>&1
3303    ${GREP} -i namingcontexts ${TMPDIR}/checkTOP | \
3304	${GREP} -i -v NetscapeRoot > ${TMPDIR}/treeTOP
3305    NUM_TOP=`wc -l ${TMPDIR}/treeTOP | awk '{print $1}'`
3306    case $NUM_TOP in
3307	0)
3308	    [ $DEBUG -eq 1 ] && ${ECHO} "DEBUG: No suffix found in LDAP tree"
3309	    return 1
3310	    ;;
3311	*)  # build the list of suffixes; take out 'namingContexts=' in
3312	    # each line of ${TMPDIR}/treeTOP
3313	    LDAP_SUFFIX_LIST=`cat ${TMPDIR}/treeTOP |
3314		awk '{ printf("%s\n",substr($0,16,length-15)) }'`
3315	    ;;
3316    esac
3317
3318    [ $DEBUG -eq 1 ] && ${ECHO} "  LDAP_SUFFIX_LIST = $LDAP_SUFFIX_LIST"
3319    return 0
3320}
3321
3322
3323#
3324# modify_cn(): Change the cn from MUST to MAY in ipNetwork.
3325#
3326modify_cn()
3327{
3328    [ $DEBUG -eq 1 ] && ${ECHO} "In modify_cn()"
3329
3330    ( cat <<EOF
3331dn: cn=schema
3332changetype: modify
3333add: objectclasses
3334objectclasses: ( 1.3.6.1.1.1.2.7 NAME 'ipNetwork' DESC 'Standard LDAP objectclass' SUP top STRUCTURAL MUST ( ipNetworkNumber ) MAY ( ipNetmaskNumber $ manager $ cn $ l $ description ) X-ORIGIN 'RFC 2307' ))
3335EOF
3336) > ${TMPDIR}/ipNetwork_cn
3337
3338    # Modify the cn for ipNetwork.
3339    ${EVAL} "${LDAPMODIFY} ${LDAP_ARGS} -f ${TMPDIR}/ipNetwork_cn ${VERB}"
3340    if [ $? -ne 0 ]; then
3341	${ECHO} "  ERROR: update of cn for ipNetwork failed!"
3342	cleanup
3343	exit 1
3344    fi
3345}
3346
3347
3348# modify_timelimit(): Modify timelimit to user value.
3349modify_timelimit()
3350{
3351    [ $DEBUG -eq 1 ] && ${ECHO} "In modify_timelimit()"
3352
3353    # Here doc to modify timelimit.
3354    ( cat <<EOF
3355dn: cn=config
3356changetype: modify
3357replace: nsslapd-timelimit
3358nsslapd-timelimit: ${IDS_TIMELIMIT}
3359EOF
3360) > ${TMPDIR}/ids_timelimit
3361
3362    # Add the entry.
3363    ${EVAL} "${LDAPMODIFY} ${LDAP_ARGS} -f ${TMPDIR}/ids_timelimit ${VERB}"
3364    if [ $? -ne 0 ]; then
3365	${ECHO} "  ERROR: update of nsslapd-timelimit failed!"
3366	cleanup
3367	exit 1
3368    fi
3369
3370    # Display messages for modifications made in patch.
3371    ${ECHO} "  ${STEP}. Changed timelimit to ${IDS_TIMELIMIT} in cn=config."
3372    STEP=`expr $STEP + 1`
3373}
3374
3375
3376# modify_sizelimit(): Modify sizelimit to user value.
3377modify_sizelimit()
3378{
3379    [ $DEBUG -eq 1 ] && ${ECHO} "In modify_sizelimit()"
3380
3381    # Here doc to modify sizelimit.
3382    ( cat <<EOF
3383dn: cn=config
3384changetype: modify
3385replace: nsslapd-sizelimit
3386nsslapd-sizelimit: ${IDS_SIZELIMIT}
3387EOF
3388) > ${TMPDIR}/ids_sizelimit
3389
3390    # Add the entry.
3391    ${EVAL} "${LDAPMODIFY} ${LDAP_ARGS} -f ${TMPDIR}/ids_sizelimit ${VERB}"
3392    if [ $? -ne 0 ]; then
3393	${ECHO} "  ERROR: update of nsslapd-sizelimit failed!"
3394	cleanup
3395	exit 1
3396    fi
3397
3398    # Display messages for modifications made in patch.
3399    ${ECHO} "  ${STEP}. Changed sizelimit to ${IDS_SIZELIMIT} in cn=config."
3400    STEP=`expr $STEP + 1`
3401}
3402
3403
3404# modify_pwd_crypt(): Modify the passwd storage scheme to support CRYPT.
3405modify_pwd_crypt()
3406{
3407    [ $DEBUG -eq 1 ] && ${ECHO} "In modify_pwd_crypt()"
3408
3409    # Here doc to modify passwordstoragescheme.
3410    # IDS 5.2 moved passwordchangesceme off to a new data structure.
3411    if [ $IDS_MAJVER -le 5 ] && [ $IDS_MINVER -le 1 ]; then
3412	( cat <<EOF
3413dn: cn=config
3414changetype: modify
3415replace: passwordstoragescheme
3416passwordstoragescheme: crypt
3417EOF
3418	) > ${TMPDIR}/ids_crypt
3419    else
3420	( cat <<EOF
3421dn: cn=Password Policy,cn=config
3422changetype: modify
3423replace: passwordstoragescheme
3424passwordstoragescheme: crypt
3425EOF
3426	) > ${TMPDIR}/ids_crypt
3427    fi
3428
3429    # Add the entry.
3430    ${EVAL} "${LDAPMODIFY} ${LDAP_ARGS} -f ${TMPDIR}/ids_crypt ${VERB}"
3431    if [ $? -ne 0 ]; then
3432	${ECHO} "  ERROR: update of passwordstoragescheme failed!"
3433	cleanup
3434	exit 1
3435    fi
3436
3437    # Display messages for modifications made in patch.
3438    ${ECHO} "  ${STEP}. Changed passwordstoragescheme to \"crypt\" in cn=config."
3439    STEP=`expr $STEP + 1`
3440}
3441
3442
3443#
3444# add_eq_indexes(): Add indexes to improve search performance.
3445#
3446add_eq_indexes()
3447{
3448    [ $DEBUG -eq 1 ] && ${ECHO} "In add_eq_indexes()"
3449
3450    # Set eq indexes to add.
3451    _INDEXES="uidNumber ipNetworkNumber gidnumber oncrpcnumber automountKey"
3452
3453    if [ -z "${IDS_DATABASE}" ]; then
3454	get_backend
3455    fi
3456    # Set _EXT to use as shortcut.
3457    _EXT="cn=index,cn=${IDS_DATABASE},cn=ldbm database,cn=plugins,cn=config"
3458
3459
3460    # Display message to id current step.
3461    ${ECHO} "  ${STEP}. Processing eq,pres indexes:"
3462    STEP=`expr $STEP + 1`
3463
3464    # For loop to create indexes.
3465    for i in ${_INDEXES}; do
3466	[ $DEBUG -eq 1 ] && ${ECHO} "  Adding index for ${i}"
3467
3468	# Check if entry exists first, if so, skip to next.
3469	${LDAPSEARCH} ${SERVER_ARGS} -b "cn=${i},${_EXT}" -s base "objectclass=*" > /dev/null 2>&1
3470	if [ $? -eq 0 ]; then
3471	    # Display index skipped.
3472	    ${ECHO} "      ${i} (eq,pres) skipped already exists"
3473	    continue
3474	fi
3475
3476	# Here doc to create LDIF.
3477	( cat <<EOF
3478dn: cn=${i},${_EXT}
3479objectClass: top
3480objectClass: nsIndex
3481cn: ${i}
3482nsSystemIndex: false
3483nsIndexType: pres
3484nsIndexType: eq
3485EOF
3486) > ${TMPDIR}/index_${i}
3487
3488	# Add the index.
3489	${EVAL} "${LDAPMODIFY} -a ${LDAP_ARGS} -f ${TMPDIR}/index_${i} ${VERB}"
3490	if [ $? -ne 0 ]; then
3491	    ${ECHO} "  ERROR: Adding EQ,PRES index for ${i} failed!"
3492	    cleanup
3493	    exit 1
3494	fi
3495
3496	# Build date for task name.
3497	_YR=`date '+%y'`
3498	_MN=`date '+%m'`
3499	_DY=`date '+%d'`
3500	_H=`date '+%H'`
3501	_M=`date '+%M'`
3502	_S=`date '+%S'`
3503
3504	# Build task name
3505	TASKNAME="${i}_${_YR}_${_MN}_${_DY}_${_H}_${_M}_${_S}"
3506
3507	# Build the task entry to add.
3508	( cat <<EOF
3509dn: cn=${TASKNAME}, cn=index, cn=tasks, cn=config
3510changetype: add
3511objectclass: top
3512objectclass: extensibleObject
3513cn: ${TASKNAME}
3514nsInstance: ${IDS_DATABASE}
3515nsIndexAttribute: ${i}
3516EOF
3517) > ${TMPDIR}/task_${i}
3518
3519	# Add the task.
3520	${EVAL} "${LDAPMODIFY} -a ${LDAP_ARGS} -f ${TMPDIR}/task_${i} ${VERB}"
3521	if [ $? -ne 0 ]; then
3522	    ${ECHO} "  ERROR: Adding task for ${i} failed!"
3523	    cleanup
3524	    exit 1
3525	fi
3526
3527	# Wait for task to finish, display current status.
3528	while :
3529	do
3530	    eval "${LDAPSEARCH} ${LDAP_ARGS} -b \"cn=index, cn=tasks, cn=config\" -s sub \"objectclass=*\" > ${TMPDIR}/istask_${i} 2>&1"
3531	    ${GREP} ${TASKNAME} ${TMPDIR}/istask_${i} > /dev/null 2>&1
3532	    if [ $? -ne 0 ]; then
3533		break
3534	    fi
3535	    eval "${LDAPSEARCH} ${LDAP_ARGS} -b \"cn=index,cn=tasks,cn=config\" -s one \"objectclass=*\" nstaskstatus | ${GREP} -i nstaskstatus | cut -d\":\" -f2 > ${TMPDIR}/wait_task_${i}"
3536	    TASK_STATUS=`head -1 ${TMPDIR}/wait_task_${i}`
3537	    ${ECHO} "      ${i} (eq,pres)  $TASK_STATUS                  \r\c"
3538	    ${ECHO} "$TASK_STATUS" | ${GREP} "Finished" > /dev/null 2>&1
3539	    if [ $? -eq 0 ]; then
3540		break
3541	    fi
3542	    sleep 2
3543	done
3544
3545	# Print newline because of \c.
3546	${ECHO} " "
3547    done
3548}
3549
3550
3551#
3552# add_sub_indexes(): Add indexes to improve search performance.
3553#
3554add_sub_indexes()
3555{
3556    [ $DEBUG -eq 1 ] && ${ECHO} "In add_sub_indexes()"
3557
3558    # Set eq indexes to add.
3559    _INDEXES="ipHostNumber membernisnetgroup nisnetgrouptriple"
3560
3561    # Set _EXT to use as shortcut.
3562    _EXT="cn=index,cn=${IDS_DATABASE},cn=ldbm database,cn=plugins,cn=config"
3563
3564
3565    # Display message to id current step.
3566    ${ECHO} "  ${STEP}. Processing eq,pres,sub indexes:"
3567    STEP=`expr $STEP + 1`
3568
3569    # For loop to create indexes.
3570    for i in ${_INDEXES}; do
3571	[ $DEBUG -eq 1 ] && ${ECHO} "  Adding index for ${i}"
3572
3573	# Check if entry exists first, if so, skip to next.
3574	${LDAPSEARCH} ${SERVER_ARGS} -b "cn=${i},${_EXT}" -s base "objectclass=*" > /dev/null 2>&1
3575	if [ $? -eq 0 ]; then
3576	    # Display index skipped.
3577	    ${ECHO} "      ${i} (eq,pres,sub) skipped already exists"
3578	    continue
3579	fi
3580
3581	# Here doc to create LDIF.
3582	( cat <<EOF
3583dn: cn=${i},${_EXT}
3584objectClass: top
3585objectClass: nsIndex
3586cn: ${i}
3587nsSystemIndex: false
3588nsIndexType: pres
3589nsIndexType: eq
3590nsIndexType: sub
3591EOF
3592) > ${TMPDIR}/index_${i}
3593
3594	# Add the index.
3595	${EVAL} "${LDAPMODIFY} -a ${LDAP_ARGS} -f ${TMPDIR}/index_${i} ${VERB}"
3596	if [ $? -ne 0 ]; then
3597	    ${ECHO} "  ERROR: Adding EQ,PRES,SUB index for ${i} failed!"
3598	    cleanup
3599	    exit 1
3600	fi
3601
3602	# Build date for task name.
3603	_YR=`date '+%y'`
3604	_MN=`date '+%m'`
3605	_DY=`date '+%d'`
3606	_H=`date '+%H'`
3607	_M=`date '+%M'`
3608	_S=`date '+%S'`
3609
3610	# Build task name
3611	TASKNAME="${i}_${_YR}_${_MN}_${_DY}_${_H}_${_M}_${_S}"
3612
3613	# Build the task entry to add.
3614	( cat <<EOF
3615dn: cn=${TASKNAME}, cn=index, cn=tasks, cn=config
3616changetype: add
3617objectclass: top
3618objectclass: extensibleObject
3619cn: ${TASKNAME}
3620nsInstance: ${IDS_DATABASE}
3621nsIndexAttribute: ${i}
3622EOF
3623) > ${TMPDIR}/task_${i}
3624
3625	# Add the task.
3626	${EVAL} "${LDAPMODIFY} -a ${LDAP_ARGS} -f ${TMPDIR}/task_${i} ${VERB}"
3627	if [ $? -ne 0 ]; then
3628	    ${ECHO} "  ERROR: Adding task for ${i} failed!"
3629	    cleanup
3630	    exit 1
3631	fi
3632
3633	# Wait for task to finish, display current status.
3634	while :
3635	do
3636	    eval "${LDAPSEARCH} ${LDAP_ARGS} -b \"cn=index, cn=tasks, cn=config\" -s sub \"objectclass=*\" > ${TMPDIR}/istask_${i} 2>&1"
3637	    ${GREP} ${TASKNAME} ${TMPDIR}/istask_${i} > /dev/null 2>&1
3638	    if [ $? -ne 0 ]; then
3639		break
3640	    fi
3641	    eval "${LDAPSEARCH} ${LDAP_ARGS} -b \"cn=index,cn=tasks,cn=config\" -s one \"objectclass=*\" nstaskstatus | ${GREP} -i nstaskstatus | cut -d\":\" -f2 > ${TMPDIR}/wait_task_${i}"
3642	    TASK_STATUS=`head -1 ${TMPDIR}/wait_task_${i}`
3643	    ${ECHO} "      ${i} (eq,pres,sub)  $TASK_STATUS                  \r\c"
3644	    ${ECHO} "$TASK_STATUS" | ${GREP} "Finished" > /dev/null 2>&1
3645	    if [ $? -eq 0 ]; then
3646		break
3647	    fi
3648	    sleep 2
3649	done
3650
3651	# Print newline because of \c.
3652	${ECHO} " "
3653    done
3654}
3655
3656
3657#
3658# add_vlv_indexes(): Add VLV indexes to improve search performance.
3659#
3660add_vlv_indexes()
3661{
3662    [ $DEBUG -eq 1 ] && ${ECHO} "In add_vlv_indexes()"
3663
3664    # Set eq indexes to add.
3665    # Note semi colon separators because some filters contain colons
3666    _INDEX1="${LDAP_DOMAIN}.getgrent;${LDAP_DOMAIN}_group_vlv_index;ou=group;objectClass=posixGroup"
3667    _INDEX2="${LDAP_DOMAIN}.gethostent;${LDAP_DOMAIN}_hosts_vlv_index;ou=hosts;objectClass=ipHost"
3668    _INDEX3="${LDAP_DOMAIN}.getnetent;${LDAP_DOMAIN}_networks_vlv_index;ou=networks;objectClass=ipNetwork"
3669    _INDEX4="${LDAP_DOMAIN}.getpwent;${LDAP_DOMAIN}_passwd_vlv_index;ou=people;objectClass=posixAccount"
3670    _INDEX5="${LDAP_DOMAIN}.getrpcent;${LDAP_DOMAIN}_rpc_vlv_index;ou=rpc;objectClass=oncRpc"
3671    _INDEX6="${LDAP_DOMAIN}.getspent;${LDAP_DOMAIN}_shadow_vlv_index;ou=people;objectClass=shadowAccount"
3672
3673    # Indexes added during NIS to LDAP transition
3674    _INDEX7="${LDAP_DOMAIN}.getauhoent;${LDAP_DOMAIN}_auho_vlv_index;automountmapname=auto_home;objectClass=automount"
3675    _INDEX8="${LDAP_DOMAIN}.getsoluent;${LDAP_DOMAIN}_solu_vlv_index;ou=people;objectClass=SolarisUserAttr"
3676    _INDEX9="${LDAP_DOMAIN}.getauduent;${LDAP_DOMAIN}_audu_vlv_index;ou=people;objectClass=SolarisAuditUser"
3677    _INDEX10="${LDAP_DOMAIN}.getauthent;${LDAP_DOMAIN}_auth_vlv_index;ou=SolarisAuthAttr;objectClass=SolarisAuthAttr"
3678    _INDEX11="${LDAP_DOMAIN}.getexecent;${LDAP_DOMAIN}_exec_vlv_index;ou=SolarisProfAttr;&(objectClass=SolarisExecAttr)(SolarisKernelSecurityPolicy=*)"
3679    _INDEX12="${LDAP_DOMAIN}.getprofent;${LDAP_DOMAIN}_prof_vlv_index;ou=SolarisProfAttr;&(objectClass=SolarisProfAttr)(SolarisAttrLongDesc=*)"
3680    _INDEX13="${LDAP_DOMAIN}.getmailent;${LDAP_DOMAIN}_mail_vlv_index;ou=aliases;objectClass=mailGroup"
3681    _INDEX14="${LDAP_DOMAIN}.getbootent;${LDAP_DOMAIN}__boot_vlv_index;ou=ethers;&(objectClass=bootableDevice)(bootParameter=*)"
3682    _INDEX15="${LDAP_DOMAIN}.getethent;${LDAP_DOMAIN}_ethers_vlv_index;ou=ethers;&(objectClass=ieee802Device)(macAddress=*)"
3683    _INDEX16="${LDAP_DOMAIN}.getngrpent;${LDAP_DOMAIN}_netgroup_vlv_index;ou=netgroup;objectClass=nisNetgroup"
3684    _INDEX17="${LDAP_DOMAIN}.getipnent;${LDAP_DOMAIN}_ipn_vlv_index;ou=networks;&(objectClass=ipNetwork)(cn=*)"
3685    _INDEX18="${LDAP_DOMAIN}.getmaskent;${LDAP_DOMAIN}_mask_vlv_index;ou=networks;&(objectClass=ipNetwork)(ipNetmaskNumber=*)"
3686    _INDEX19="${LDAP_DOMAIN}.getprent;${LDAP_DOMAIN}_pr_vlv_index;ou=printers;objectClass=printerService"
3687    _INDEX20="${LDAP_DOMAIN}.getip4ent;${LDAP_DOMAIN}_ip4_vlv_index;ou=hosts;&(objectClass=ipHost)(ipHostNumber=*.*)"
3688    _INDEX21="${LDAP_DOMAIN}.getip6ent;${LDAP_DOMAIN}_ip6_vlv_index;ou=hosts;&(objectClass=ipHost)(ipHostNumber=*:*)"
3689
3690    _INDEXES="$_INDEX1 $_INDEX2 $_INDEX3 $_INDEX4 $_INDEX5 $_INDEX6 $_INDEX7 $_INDEX8 $_INDEX9 $_INDEX10 $_INDEX11 $_INDEX12 $_INDEX13 $_INDEX14 $_INDEX15 $_INDEX16 $_INDEX17 $_INDEX18 $_INDEX19 $_INDEX20 $_INDEX21 "
3691
3692
3693    # Set _EXT to use as shortcut.
3694    _EXT="cn=${IDS_DATABASE},cn=ldbm database,cn=plugins,cn=config"
3695
3696
3697    # Display message to id current step.
3698    ${ECHO} "  ${STEP}. Processing VLV indexes:"
3699    STEP=`expr $STEP + 1`
3700
3701    # Reset temp file for vlvindex commands.
3702    [ -f ${TMPDIR}/vlvindex_list ] &&  rm ${TMPDIR}/vlvindex_list
3703    touch ${TMPDIR}/vlvindex_list
3704
3705    # Get the instance name from iDS server.
3706    _INSTANCE="<server-instance>"    # Default to old output.
3707
3708    eval "${LDAPSEARCH} -v ${LDAP_ARGS} -b \"cn=config\" -s base \"objectclass=*\" nsslapd-instancedir | ${GREP} 'nsslapd-instancedir=' | cut -d'=' -f2- > ${TMPDIR}/instance_name 2>&1"
3709
3710    ${GREP} "slapd-" ${TMPDIR}/instance_name > /dev/null 2>&1 # Check if seems right?
3711    if [ $? -eq 0 ]; then # If success, grab name after "slapd-".
3712	_INST_DIR=`cat ${TMPDIR}/instance_name`
3713	_INSTANCE=`basename "${_INST_DIR}" | cut -d'-' -f2-`
3714    fi
3715
3716    # For loop to create indexes.
3717    for p in ${_INDEXES}; do
3718	[ $DEBUG -eq 1 ] && ${ECHO} "  Adding index for ${i}"
3719
3720	# Break p (pair) into i and j parts.
3721        i=`${ECHO} $p | cut -d';' -f1`
3722        j=`${ECHO} $p | cut -d';' -f2`
3723        k=`${ECHO} $p | cut -d';' -f3`
3724        m=`${ECHO} $p | cut -d';' -f4`
3725
3726	# Set _jEXT to use as shortcut.
3727	_jEXT="cn=${j},${_EXT}"
3728
3729	# Check if entry exists first, if so, skip to next.
3730	${LDAPSEARCH} ${SERVER_ARGS} -b "cn=${i},${_jEXT}" -s base "objectclass=*" > /dev/null 2>&1
3731	if [ $? -eq 0 ]; then
3732	    # Display index skipped.
3733	    ${ECHO} "      ${i} vlv_index skipped already exists"
3734	    continue
3735	fi
3736
3737	# Compute the VLV Scope from the LDAP_SEARCH_SCOPE.
3738	# NOTE: A value of "base (0)" does not make sense.
3739        case "$LDAP_SEARCH_SCOPE" in
3740            sub) VLV_SCOPE="2" ;;
3741            *)   VLV_SCOPE="1" ;;
3742        esac
3743
3744	# Here doc to create LDIF.
3745	( cat <<EOF
3746dn: ${_jEXT}
3747objectClass: top
3748objectClass: vlvSearch
3749cn: ${j}
3750vlvbase: ${k},${LDAP_BASEDN}
3751vlvscope: ${VLV_SCOPE}
3752vlvfilter: (${m})
3753aci: (target="ldap:///${_jEXT}")(targetattr="*")(version 3.0; acl "Config";allow(read,search,compare)userdn="ldap:///anyone";)
3754
3755dn: cn=${i},${_jEXT}
3756cn: ${i}
3757vlvSort: cn uid
3758objectclass: top
3759objectclass: vlvIndex
3760EOF
3761) > ${TMPDIR}/vlv_index_${i}
3762
3763	# Add the index.
3764	${EVAL} "${LDAPMODIFY} -a ${LDAP_ARGS} -f ${TMPDIR}/vlv_index_${i} ${VERB}"
3765	if [ $? -ne 0 ]; then
3766	    ${ECHO} "  ERROR: Adding VLV index for ${i} failed!"
3767	    cleanup
3768	    exit 1
3769	fi
3770
3771	# Print message that index was created.
3772	${ECHO} "      ${i} vlv_index   Entry created"
3773
3774	# Add command to list of vlvindex commands to run.
3775	${ECHO} "  directoryserver -s ${_INSTANCE} vlvindex -n ${IDS_DATABASE} -T ${i}" >> ${TMPDIR}/vlvindex_list
3776    done
3777}
3778
3779
3780#
3781# display_vlv_cmds(): Display VLV index commands to run on server.
3782#
3783display_vlv_cmds()
3784{
3785    if [ -s "${TMPDIR}/vlvindex_list" ]; then
3786	display_msg display_vlv_list
3787	cat ${TMPDIR}/vlvindex_list
3788    fi
3789}
3790
3791
3792#
3793# update_schema_attr(): Update Schema to support Naming.
3794#
3795update_schema_attr()
3796{
3797    [ $DEBUG -eq 1 ] && ${ECHO} "In update_schema_attr()"
3798
3799    ( cat <<EOF
3800dn: cn=schema
3801changetype: modify
3802add: attributetypes
3803attributetypes: ( 1.3.6.1.1.1.1.28 NAME 'nisPublickey' DESC 'NIS public key' SYNTAX '1.3.6.1.4.1.1466.115.121.1.15' )
3804attributetypes: ( 1.3.6.1.1.1.1.29 NAME 'nisSecretkey' DESC 'NIS secret key' SYNTAX '1.3.6.1.4.1.1466.115.121.1.15' )
3805attributetypes: ( 1.3.6.1.1.1.1.30 NAME 'nisDomain' DESC 'NIS domain' SYNTAX '1.3.6.1.4.1.1466.115.121.1.15' )
3806attributetypes: ( 1.3.6.1.1.1.1.31 NAME 'automountMapName' DESC 'automount Map Name' EQUALITY caseExactIA5Match SYNTAX '1.3.6.1.4.1.1466.115.121.1.26' SINGLE-VALUE )
3807attributetypes: ( 1.3.6.1.1.1.1.32 NAME 'automountKey' DESC 'automount Key Value' EQUALITY caseExactIA5Match SYNTAX '1.3.6.1.4.1.1466.115.121.1.26' SINGLE-VALUE )
3808attributetypes: ( 1.3.6.1.1.1.1.33 NAME 'automountInformation' DESC 'automount information' EQUALITY caseExactIA5Match SYNTAX '1.3.6.1.4.1.1466.115.121.1.26' SINGLE-VALUE )
3809attributetypes: ( 1.3.6.1.4.1.42.2.27.1.1.12 NAME 'nisNetIdUser' SYNTAX '1.3.6.1.4.1.1466.115.121.1.26' )
3810attributetypes: ( 1.3.6.1.4.1.42.2.27.1.1.13 NAME 'nisNetIdGroup' SYNTAX '1.3.6.1.4.1.1466.115.121.1.26' )
3811attributetypes: ( 1.3.6.1.4.1.42.2.27.1.1.14 NAME 'nisNetIdHost' SYNTAX '1.3.6.1.4.1.1466.115.121.1.26' )
3812attributetypes: ( rfc822mailMember-oid NAME 'rfc822mailMember' SYNTAX '1.3.6.1.4.1.1466.115.121.1.26' )
3813attributetypes: ( 2.16.840.1.113730.3.1.30 NAME 'mgrpRFC822MailMember' SYNTAX '1.3.6.1.4.1.1466.115.121.1.15' )
3814attributetypes: ( 1.3.6.1.4.1.42.2.27.5.1.15 NAME 'SolarisLDAPServers' SYNTAX '1.3.6.1.4.1.1466.115.121.1.15' )
3815attributetypes: ( 1.3.6.1.4.1.42.2.27.5.1.16 NAME 'SolarisSearchBaseDN' SYNTAX '1.3.6.1.4.1.1466.115.121.1.12' SINGLE-VALUE )
3816attributetypes: ( 1.3.6.1.4.1.42.2.27.5.1.17 NAME 'SolarisCacheTTL' SYNTAX '1.3.6.1.4.1.1466.115.121.1.15' SINGLE-VALUE )
3817attributetypes: ( 1.3.6.1.4.1.42.2.27.5.1.18 NAME 'SolarisBindDN' SYNTAX '1.3.6.1.4.1.1466.115.121.1.12' SINGLE-VALUE )
3818attributetypes: ( 1.3.6.1.4.1.42.2.27.5.1.19 NAME 'SolarisBindPassword' SYNTAX '1.3.6.1.4.1.1466.115.121.1.26' SINGLE-VALUE )
3819attributetypes: ( 1.3.6.1.4.1.42.2.27.5.1.20 NAME 'SolarisAuthMethod' SYNTAX '1.3.6.1.4.1.1466.115.121.1.15')
3820attributetypes: ( 1.3.6.1.4.1.42.2.27.5.1.21 NAME 'SolarisTransportSecurity' SYNTAX '1.3.6.1.4.1.1466.115.121.1.15')
3821attributetypes: ( 1.3.6.1.4.1.42.2.27.5.1.22 NAME 'SolarisCertificatePath' SYNTAX '1.3.6.1.4.1.1466.115.121.1.26' SINGLE-VALUE )
3822attributetypes: ( 1.3.6.1.4.1.42.2.27.5.1.23 NAME 'SolarisCertificatePassword' SYNTAX '1.3.6.1.4.1.1466.115.121.1.26' SINGLE-VALUE )
3823attributetypes: ( 1.3.6.1.4.1.42.2.27.5.1.24 NAME 'SolarisDataSearchDN' SYNTAX '1.3.6.1.4.1.1466.115.121.1.15')
3824attributetypes: ( 1.3.6.1.4.1.42.2.27.5.1.25 NAME 'SolarisSearchScope' SYNTAX '1.3.6.1.4.1.1466.115.121.1.15' SINGLE-VALUE )
3825attributetypes: ( 1.3.6.1.4.1.42.2.27.5.1.26 NAME 'SolarisSearchTimeLimit' SYNTAX '1.3.6.1.4.1.1466.115.121.1.27' SINGLE-VALUE )
3826attributetypes: ( 1.3.6.1.4.1.42.2.27.5.1.27 NAME 'SolarisPreferredServer' SYNTAX '1.3.6.1.4.1.1466.115.121.1.15')
3827attributetypes: ( 1.3.6.1.4.1.42.2.27.5.1.28 NAME 'SolarisPreferredServerOnly' SYNTAX '1.3.6.1.4.1.1466.115.121.1.15' SINGLE-VALUE )
3828attributetypes: ( 1.3.6.1.4.1.42.2.27.5.1.29 NAME 'SolarisSearchReferral' SYNTAX '1.3.6.1.4.1.1466.115.121.1.15' SINGLE-VALUE )
3829attributetypes: ( 1.3.6.1.4.1.42.2.27.5.1.4 NAME 'SolarisAttrKeyValue' SYNTAX '1.3.6.1.4.1.1466.115.121.1.15' SINGLE-VALUE )
3830attributetypes: ( 1.3.6.1.4.1.42.2.27.5.1.5 NAME 'SolarisAuditAlways' SYNTAX '1.3.6.1.4.1.1466.115.121.1.15' SINGLE-VALUE )
3831attributetypes: ( 1.3.6.1.4.1.42.2.27.5.1.6 NAME 'SolarisAuditNever' SYNTAX '1.3.6.1.4.1.1466.115.121.1.15' SINGLE-VALUE )
3832attributetypes: ( 1.3.6.1.4.1.42.2.27.5.1.7 NAME 'SolarisAttrShortDesc' SYNTAX '1.3.6.1.4.1.1466.115.121.1.15' SINGLE-VALUE )
3833attributetypes: ( 1.3.6.1.4.1.42.2.27.5.1.8 NAME 'SolarisAttrLongDesc' SYNTAX '1.3.6.1.4.1.1466.115.121.1.15' SINGLE-VALUE )
3834attributetypes: ( 1.3.6.1.4.1.42.2.27.5.1.9 NAME 'SolarisKernelSecurityPolicy' SYNTAX '1.3.6.1.4.1.1466.115.121.1.15' SINGLE-VALUE )
3835attributetypes: ( 1.3.6.1.4.1.42.2.27.5.1.10 NAME 'SolarisProfileType' SYNTAX '1.3.6.1.4.1.1466.115.121.1.15' SINGLE-VALUE )
3836attributetypes: ( 1.3.6.1.4.1.42.2.27.5.1.11 NAME 'SolarisProfileId' SYNTAX '1.3.6.1.4.1.1466.115.121.1.26' SINGLE-VALUE )
3837attributetypes: ( 1.3.6.1.4.1.42.2.27.5.1.12 NAME 'SolarisUserQualifier' SYNTAX '1.3.6.1.4.1.1466.115.121.1.15' SINGLE-VALUE )
3838attributetypes: ( 1.3.6.1.4.1.42.2.27.5.1.13 NAME 'SolarisAttrReserved1' SYNTAX '1.3.6.1.4.1.1466.115.121.1.15' SINGLE-VALUE )
3839attributetypes: ( 1.3.6.1.4.1.42.2.27.5.1.14 NAME 'SolarisAttrReserved2' SYNTAX '1.3.6.1.4.1.1466.115.121.1.15' SINGLE-VALUE )
3840attributetypes: ( 1.3.6.1.4.1.42.2.27.5.1.1 NAME 'SolarisProjectID' SYNTAX '1.3.6.1.4.1.1466.115.121.1.27' SINGLE-VALUE )
3841attributetypes: ( 1.3.6.1.4.1.42.2.27.5.1.2 NAME 'SolarisProjectName' SYNTAX '1.3.6.1.4.1.1466.115.121.1.26' SINGLE-VALUE )
3842attributetypes: ( 1.3.6.1.4.1.42.2.27.5.1.3 NAME 'SolarisProjectAttr' SYNTAX '1.3.6.1.4.1.1466.115.121.1.26' )
3843attributetypes: ( memberGid-oid NAME 'memberGid' SYNTAX '1.3.6.1.4.1.1466.115.121.1.26' )
3844attributetypes: ( 1.3.6.1.4.1.11.1.3.1.1.0 NAME '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 )
3845attributetypes: ( 1.3.6.1.4.1.11.1.3.1.1.1 NAME 'defaultSearchBase' DESC 'Default LDAP base DN used by a DUA' SYNTAX '1.3.6.1.4.1.1466.115.121.1.12' SINGLE-VALUE )
3846attributetypes: ( 1.3.6.1.4.1.11.1.3.1.1.2 NAME '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 )
3847attributetypes: ( 1.3.6.1.4.1.11.1.3.1.1.3 NAME '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 )
3848attributetypes: ( 1.3.6.1.4.1.11.1.3.1.1.4 NAME '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 )
3849attributetypes: ( 1.3.6.1.4.1.11.1.3.1.1.5 NAME '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 )
3850attributetypes: ( 1.3.6.1.4.1.11.1.3.1.1.6 NAME '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 )
3851attributetypes: ( 1.3.6.1.4.1.11.1.3.1.1.7 NAME '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 )
3852attributetypes: ( 1.3.6.1.4.1.11.1.3.1.1.14 NAME 'serviceSearchDescriptor' DESC 'LDAP search descriptor list used by Naming-DUA' SYNTAX '1.3.6.1.4.1.1466.115.121.1.26' )
3853attributetypes: ( 1.3.6.1.4.1.11.1.3.1.1.9 NAME 'attributeMap' DESC 'Attribute mappings used by a Naming-DUA' SYNTAX '1.3.6.1.4.1.1466.115.121.1.15' )
3854attributetypes: ( 1.3.6.1.4.1.11.1.3.1.1.10 NAME '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 )
3855attributetypes: ( 1.3.6.1.4.1.11.1.3.1.1.11 NAME 'objectclassMap' DESC 'Objectclass mappings used by a Naming-DUA' SYNTAX '1.3.6.1.4.1.1466.115.121.1.15' )
3856attributetypes: ( 1.3.6.1.4.1.11.1.3.1.1.12 NAME 'defaultSearchScope' DESC 'Default search scope used by a DUA' SYNTAX '1.3.6.1.4.1.1466.115.121.1.15' SINGLE-VALUE )
3857attributetypes: ( 1.3.6.1.4.1.11.1.3.1.1.13 NAME '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 )
3858attributetypes: ( 1.3.6.1.4.1.11.1.3.1.1.15 NAME '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 )
3859attributetypes:( 1.3.18.0.2.4.1140 NAME '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 )
3860attributetypes:( 1.3.18.0.2.4.1107 NAME '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 )
3861attributetypes:( 1.3.18.0.2.4.1135 NAME '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 )
3862attributetypes:( 1.3.18.0.2.4.1119 NAME '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 )
3863attributetypes:( 1.3.18.0.2.4.1136 NAME '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 )
3864attributetypes:( 1.3.18.0.2.4.1139 NAME '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 )
3865attributetypes:( 1.3.18.0.2.4.1134 NAME '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 )
3866attributetypes:( 1.3.18.0.2.4.1138 NAME '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 )
3867attributetypes:( 1.3.18.0.2.4.1133 NAME '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} )
3868attributetypes:( 1.3.18.0.2.4.1132 NAME '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 )
3869attributetypes:( 1.3.18.0.2.4.1109 NAME '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 )
3870attributetypes:( 1.3.18.0.2.4.1131 NAME '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} )
3871attributetypes:( 1.3.18.0.2.4.1137 NAME '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} )
3872attributetypes:( 1.3.18.0.2.4.1130 NAME '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} )
3873attributetypes:( 1.3.18.0.2.4.1129 NAME '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 )
3874attributetypes:( 1.3.18.0.2.4.1128 NAME '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} )
3875attributetypes:( 1.3.18.0.2.4.1127 NAME '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 )
3876attributetypes:( 1.3.18.0.2.4.1126 NAME '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 )
3877attributetypes:( 1.3.18.0.2.4.1125 NAME '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} )
3878attributetypes:( 1.3.18.0.2.4.1124 NAME '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 )
3879attributetypes:( 1.3.18.0.2.4.1123 NAME '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} )
3880attributetypes:( 1.3.18.0.2.4.1122 NAME '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} )
3881attributetypes:( 1.3.18.0.2.4.1117 NAME '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} )
3882attributetypes:( 1.3.18.0.2.4.1121 NAME '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} )
3883attributetypes:( 1.3.18.0.2.4.1120 NAME 'printer-print-quality-supported' DESC 'List of print qualities supported for printing documents on this printer.  For example: "draft, normal".  Legal values include; "unknown", "draft", "normal", "high".' EQUALITY caseIgnoreMatch SYNTAX  1.3.6.1.4.1.1466.115.121.1.15{127} )
3884attributetypes:( 1.3.18.0.2.4.1110 NAME 'printer-job-priority-supported' DESC 'Indicates the number of job priority levels supported.  An IPP conformant printer which supports job priority must always support a full range of priorities from "1" to "100" (to ensure consistent behavior), therefore this attribute describes the "granularity".  Legal values of this attribute are from "1" to "100".' EQUALITY integerMatch ORDERING integerOrderingMatch SYNTAX  1.3.6.1.4.1.1466.115.121.1.27  SINGLE-VALUE )
3885attributetypes:( 1.3.18.0.2.4.1118 NAME 'printer-copies-supported' DESC 'The maximum number of copies of a document that may be printed as a single job.  A value of "0" indicates no maximum limit.  A value of "-1" indicates unknown.' EQUALITY integerMatch ORDERING integerOrderingMatch SYNTAX  1.3.6.1.4.1.1466.115.121.1.27  SINGLE-VALUE )
3886attributetypes:( 1.3.18.0.2.4.1111 NAME 'printer-job-k-octets-supported' DESC 'The maximum size in kilobytes (1,024 octets actually) incoming print job that this printer will accept.  A value of "0" indicates no maximum limit.  A value of "-1" indicates unknown.' EQUALITY integerMatch ORDERING integerOrderingMatch SYNTAX  1.3.6.1.4.1.1466.115.121.1.27  SINGLE-VALUE )
3887attributetypes:( 1.3.18.0.2.4.1112 NAME 'printer-current-operator' DESC 'The name of the current human operator responsible for operating this printer.  It is suggested that this string include information that would enable other humans to reach the operator, such as a phone number.' EQUALITY caseIgnoreMatch ORDERING caseIgnoreOrderingMatch SUBSTR caseIgnoreSubstringsMatch SYNTAX  1.3.6.1.4.1.1466.115.121.1.15{127} SINGLE-VALUE )
3888attributetypes:( 1.3.18.0.2.4.1113 NAME 'printer-service-person' DESC 'The name of the current human service person responsible for servicing this printer.  It is suggested that this string include information that would enable other humans to reach the service person, such as a phone number.' EQUALITY caseIgnoreMatch ORDERING caseIgnoreOrderingMatch SUBSTR caseIgnoreSubstringsMatch SYNTAX  1.3.6.1.4.1.1466.115.121.1.15{127}  SINGLE-VALUE )
3889attributetypes:( 1.3.18.0.2.4.1114 NAME 'printer-delivery-orientation-supported' DESC 'The possible delivery orientations of pages as they are printed and ejected from this printer.  Legal values include; "unknown", "face-up", and "face-down".' EQUALITY caseIgnoreMatch SYNTAX  1.3.6.1.4.1.1466.115.121.1.15{127} )
3890attributetypes:( 1.3.18.0.2.4.1115 NAME 'printer-stacking-order-supported' DESC 'The possible stacking order of pages as they are printed and ejected from this printer. Legal values include; "unknown", "first-to-last", "last-to-first".' EQUALITY caseIgnoreMatch SYNTAX  1.3.6.1.4.1.1466.115.121.1.15{127} )
3891attributetypes:( 1.3.18.0.2.4.1116 NAME 'printer-output-features-supported' DESC 'The possible output features supported by this printer. Legal values include; "unknown", "bursting", "decollating", "page-collating", "offset-stacking".' EQUALITY caseIgnoreMatch SYNTAX  1.3.6.1.4.1.1466.115.121.1.15{127} )
3892attributetypes:( 1.3.18.0.2.4.1108 NAME 'printer-aliases' DESC 'Site-specific administrative names of this printer in addition the printer name specified for printer-name.' EQUALITY caseIgnoreMatch ORDERING caseIgnoreOrderingMatch SUBSTR caseIgnoreSubstringsMatch SYNTAX  1.3.6.1.4.1.1466.115.121.1.15{127} )
3893attributetypes:( 1.3.6.1.4.1.42.2.27.5.1.63 NAME 'sun-printer-bsdaddr' DESC 'Sets the server, print queue destination name and whether the client generates protocol extensions. "Solaris" specifies a Solaris print server extension. The value is represented by the following value: server "," destination ", Solaris".' SYNTAX '1.3.6.1.4.1.1466.115.121.1.15' SINGLE-VALUE )
3894attributetypes:( 1.3.6.1.4.1.42.2.27.5.1.64 NAME 'sun-printer-kvp' DESC 'This attribute contains a set of key value pairs which may have meaning to the print subsystem or may be user defined. Each value is represented by the following: key "=" value.' SYNTAX '1.3.6.1.4.1.1466.115.121.1.15' )
3895attributetypes: ( 1.3.6.1.4.1.42.2.27.5.1.57 NAME 'nisplusTimeZone' DESC 'tzone column from NIS+ timezone table' SYNTAX 1.3.6.1.4.1.1466.115.121.1.26 SINGLE-VALUE )
3896attributetypes:( 1.3.6.1.4.1.42.2.27.5.1.67 NAME 'ipTnetTemplateName' DESC 'Trusted Solaris network template template_name' SYNTAX 1.3.6.1.4.1.1466.115.121.1.26 SINGLE-VALUE )
3897attributetypes:( 1.3.6.1.4.1.42.2.27.5.1.68 NAME 'ipTnetNumber' DESC 'Trusted Solaris network template ip_address' SYNTAX 1.3.6.1.4.1.1466.115.121.1.26 SINGLE-VALUE )
3898EOF
3899) > ${TMPDIR}/schema_attr
3900
3901    # Add the entry.
3902    ${EVAL} "${LDAPMODIFY} ${LDAP_ARGS} -f ${TMPDIR}/schema_attr ${VERB}"
3903    if [ $? -ne 0 ]; then
3904	${ECHO} "  ERROR: update of schema attributes failed!"
3905	cleanup
3906	exit 1
3907    fi
3908
3909    # Display message that schema is updated.
3910    ${ECHO} "  ${STEP}. Schema attributes have been updated."
3911    STEP=`expr $STEP + 1`
3912}
3913
3914
3915#
3916# update_schema_obj(): Update the schema objectclass definitions.
3917#
3918update_schema_obj()
3919{
3920    [ $DEBUG -eq 1 ] && ${ECHO} "In update_schema_obj()"
3921
3922    # Add the objectclass definitions.
3923    ( cat <<EOF
3924dn: cn=schema
3925changetype: modify
3926add: objectclasses
3927objectclasses: ( 1.3.6.1.1.1.2.14 NAME 'NisKeyObject' SUP 'top' MUST (objectclass $ cn $ nisPublickey $ nisSecretkey) MAY (uidNumber $ description))
3928
3929dn: cn=schema
3930changetype: modify
3931add: objectclasses
3932objectclasses: ( 1.3.6.1.1.1.2.15 NAME 'nisDomainObject' SUP 'top' MUST (objectclass $ nisDomain) MAY ())
3933
3934dn: cn=schema
3935changetype: modify
3936add: objectclasses
3937objectclasses: ( 1.3.6.1.1.1.2.16 NAME 'automountMap' SUP 'top' MUST (objectclass $ automountMapName) MAY (description))
3938
3939dn: cn=schema
3940changetype: modify
3941add: objectclasses
3942objectclasses: ( 1.3.6.1.1.1.2.17 NAME 'automount' SUP 'top' MUST (objectclass $ automountKey $ automountInformation ) MAY (description))
3943
3944dn: cn=schema
3945changetype: modify
3946add: objectclasses
3947objectclasses: ( 1.3.6.1.4.1.42.2.27.5.2.7 NAME 'SolarisNamingProfile' SUP 'top' MUST (objectclass $ cn $ SolarisLDAPservers $ SolarisSearchBaseDN) MAY (SolarisBindDN $ SolarisBindPassword $ SolarisAuthMethod $ SolarisTransportSecurity $ SolarisCertificatePath $ SolarisCertificatePassword $ SolarisDataSearchDN $ SolarisSearchScope $ SolarisSearchTimeLimit $ SolarisPreferredServer $ SolarisPreferredServerOnly $ SolarisCacheTTL $ SolarisSearchReferral))
3948
3949dn: cn=schema
3950changetype: modify
3951add: objectclasses
3952objectclasses: ( 2.16.840.1.113730.3.2.4 NAME 'mailGroup' SUP 'top' MUST (objectclass $ mail) MAY (cn $ mgrpRFC822MailMember))
3953
3954dn: cn=schema
3955changetype: modify
3956add: objectclasses
3957objectclasses: ( 1.3.6.1.4.1.42.2.27.1.2.5 NAME 'nisMailAlias' SUP 'top' MUST (objectclass $ cn) MAY (rfc822mailMember))
3958
3959dn: cn=schema
3960changetype: modify
3961add: objectclasses
3962objectclasses: ( 1.3.6.1.4.1.42.2.27.1.2.6 NAME 'nisNetId' SUP 'top' MUST (objectclass $ cn) MAY (nisNetIdUser $ nisNetIdGroup $ nisNetIdHost))
3963
3964dn: cn=schema
3965changetype: modify
3966add: objectclasses
3967objectclasses: ( 1.3.6.1.4.1.42.2.27.5.2.2 NAME 'SolarisAuditUser' SUP 'top' AUXILIARY MUST (objectclass) MAY (SolarisAuditAlways $ SolarisAuditNever))
3968
3969dn: cn=schema
3970changetype: modify
3971add: objectclasses
3972objectclasses: ( 1.3.6.1.4.1.42.2.27.5.2.3 NAME 'SolarisUserAttr' SUP 'top' AUXILIARY MUST (objectclass) MAY (SolarisUserQualifier $ SolarisAttrReserved1 $ SolarisAttrReserved2 $ SolarisAttrKeyValue))
3973
3974dn: cn=schema
3975changetype: modify
3976add: objectclasses
3977objectclasses: ( 1.3.6.1.4.1.42.2.27.5.2.4 NAME 'SolarisAuthAttr' SUP 'top' MUST (objectclass $ cn) MAY (SolarisAttrReserved1 $ SolarisAttrReserved2 $ SolarisAttrShortDesc $ SolarisAttrLongDesc $ SolarisAttrKeyValue))
3978
3979dn: cn=schema
3980changetype: modify
3981add: objectclasses
3982objectclasses: ( 1.3.6.1.4.1.42.2.27.5.2.5 NAME 'SolarisProfAttr' SUP 'top' MUST (objectclass $ cn) MAY (SolarisAttrReserved1 $ SolarisAttrReserved2 $ SolarisAttrLongDesc $ SolarisAttrKeyValue))
3983
3984dn: cn=schema
3985changetype: modify
3986add: objectclasses
3987objectclasses: ( 1.3.6.1.4.1.42.2.27.5.2.6 NAME 'SolarisExecAttr' SUP 'top' AUXILIARY MUST (objectclass) MAY (SolarisKernelSecurityPolicy $ SolarisProfileType $ SolarisAttrReserved1 $ SolarisAttrReserved2 $ SolarisProfileID $ SolarisAttrKeyValue))
3988
3989dn: cn=schema
3990changetype: modify
3991add: objectclasses
3992objectclasses: ( 1.3.6.1.4.1.42.2.27.5.2.1 NAME 'SolarisProject' SUP 'top' MUST (objectclass $ SolarisProjectID $ SolarisProjectName) MAY (memberUid $ memberGid $ description $ SolarisProjectAttr))
3993
3994dn: cn=schema
3995changetype: modify
3996add: objectclasses
3997objectclasses: ( 1.3.6.1.4.1.11.1.3.1.2.4 NAME 'DUAConfigProfile' SUP 'top' DESC 'Abstraction of a base configuration for a DUA' MUST (cn) MAY (defaultServerList $ preferredServerList $ defaultSearchBase $ defaultSearchScope $ searchTimeLimit $ bindTimeLimit $ credentialLevel $ authenticationMethod $ followReferrals $ serviceSearchDescriptor $ serviceCredentialLevel $ serviceAuthenticationMethod $ objectclassMap $ attributeMap $ profileTTL))
3998
3999dn: cn=schema
4000changetype: modify
4001add: objectclasses
4002objectclasses: ( 1.3.18.0.2.6.2549 NAME 'slpService' DESC 'DUMMY definition' SUP 'top' MUST (objectclass) MAY ())
4003
4004dn: cn=schema
4005changetype: modify
4006add: objectclasses
4007objectclasses: ( 1.3.18.0.2.6.254 NAME 'slpServicePrinter' DESC 'Service Location Protocol (SLP) information.' AUXILIARY SUP 'slpService')
4008
4009dn: cn=schema
4010changetype: modify
4011add: objectclasses
4012objectclasses: ( 1.3.18.0.2.6.258 NAME 'printerAbstract' DESC 'Printer related information.' ABSTRACT SUP 'top' 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 ))
4013
4014dn: cn=schema
4015changetype: modify
4016add: objectclasses
4017objectclasses: ( 1.3.18.0.2.6.255 NAME 'printerService' DESC 'Printer information.' STRUCTURAL SUP 'printerAbstract' MAY ( printer-uri $ printer-xri-supported ))
4018
4019dn: cn=schema
4020changetype: modify
4021add: objectclasses
4022objectclasses: ( 1.3.18.0.2.6.257 NAME 'printerServiceAuxClass' DESC 'Printer information.' AUXILIARY SUP 'printerAbstract' MAY ( printer-uri $ printer-xri-supported ))
4023
4024dn: cn=schema
4025changetype: modify
4026add: objectclasses
4027objectclasses: ( 1.3.18.0.2.6.256 NAME 'printerIPP' DESC 'Internet Printing Protocol (IPP) information.' AUXILIARY SUP 'top' MAY   ( printer-ipp-versions-supported $ printer-multiple-document-jobs-supported ))
4028
4029dn: cn=schema
4030changetype: modify
4031add: objectclasses
4032objectclasses: ( 1.3.18.0.2.6.253 NAME 'printerLPR' DESC 'LPR information.' AUXILIARY SUP 'top' MUST ( printer-name ) MAY ( printer-aliases))
4033
4034dn: cn=schema
4035changetype: modify
4036add: objectclasses
4037objectclasses: ( 1.3.6.1.4.1.42.2.27.5.2.14 NAME 'sunPrinter' DESC 'Sun printer information' SUP 'top' AUXILIARY MUST (objectclass $ printer-name)  MAY (sun-printer-bsdaddr $ sun-printer-kvp))
4038
4039dn: cn=schema
4040changetype: modify
4041add: objectclasses
4042objectclasses:	( 1.3.6.1.4.1.42.2.27.5.2.12 NAME 'nisplusTimeZoneData' DESC 'NIS+ timezone table data' SUP top STRUCTURAL MUST ( cn ) MAY ( nisplusTimeZone $ description ) )
4043
4044dn: cn=schema
4045changetype: modify
4046add: objectclasses
4047objectclasses:  ( 1.3.6.1.4.1.42.2.27.5.2.8 NAME 'ipTnetTemplate' DESC 'Object class for TSOL network templates' SUP 'top' MUST ( objectclass $ ipTnetTemplateName ) MAY ( SolarisAttrKeyValue ) )
4048
4049dn: cn=schema
4050changetype: modify
4051add: objectclasses
4052objectclasses:	( 1.3.6.1.4.1.42.2.27.5.2.9 NAME 'ipTnetHost' DESC 'Associates an IP address or wildcard with a TSOL template_name' SUP 'top' AUXILIARY MUST ( objectclass $ ipTnetNumber ) )
4053EOF
4054) > ${TMPDIR}/schema_obj
4055
4056    # Add the entry.
4057    ${EVAL} "${LDAPMODIFY} ${LDAP_ARGS} -f ${TMPDIR}/schema_obj ${VERB}"
4058    if [ $? -ne 0 ]; then
4059	${ECHO} "  ERROR: update of schema objectclass definitions failed!"
4060	cleanup
4061	exit 1
4062    fi
4063
4064    # Display message that schema is updated.
4065    ${ECHO} "  ${STEP}. Schema objectclass definitions have been added."
4066    STEP=`expr $STEP + 1`
4067}
4068
4069
4070#
4071# modify_top_aci(): Modify the ACI for the top entry to disable self modify
4072#                   of user attributes.
4073#
4074modify_top_aci()
4075{
4076    [ $DEBUG -eq 1 ] && ${ECHO} "In modify_top_aci()"
4077
4078    # Set ACI Name
4079    ACI_NAME="LDAP_Naming_Services_deny_write_access"
4080
4081    # Search for ACI_NAME
4082    eval "${LDAPSEARCH} ${LDAP_ARGS} -b \"${LDAP_BASEDN}\" -s base objectclass=* aci > ${TMPDIR}/chk_top_aci 2>&1"
4083    if [ $? -ne 0 ]; then
4084	${ECHO} "Error searching aci for ${LDAP_BASEDN}"
4085	cat ${TMPDIR}/chk_top_aci
4086	cleanup
4087	exit 1
4088    fi
4089    ${GREP} "${ACI_NAME}" ${TMPDIR}/chk_top_aci > /dev/null 2>&1
4090    if [ $? -eq 0 ]; then
4091	${ECHO} "  ${STEP}. Top level ACI ${ACI_NAME} already exists for ${LDAP_BASEDN}."
4092	STEP=`expr $STEP + 1`
4093	return 0
4094    fi
4095
4096    # Crate LDIF for top level ACI.
4097    ( cat <<EOF
4098dn: ${LDAP_BASEDN}
4099changetype: modify
4100add: aci
4101aci: (targetattr = "cn||uid||uidNumber||gidNumber||homeDirectory||shadowLastChange||shadowMin||shadowMax||shadowWarning||shadowInactive||shadowExpire||shadowFlag||memberUid")(version 3.0; acl ${ACI_NAME}; deny (write) userdn = "ldap:///self";)
4102-
4103EOF
4104) > ${TMPDIR}/top_aci
4105
4106    # Add the entry.
4107    ${EVAL} "${LDAPMODIFY} ${LDAP_ARGS} -f ${TMPDIR}/top_aci ${VERB}"
4108    if [ $? -ne 0 ]; then
4109	${ECHO} "  ERROR: Modify of top level ACI failed! (restricts self modify)"
4110	cleanup
4111	exit 1
4112    fi
4113
4114    # Display message that schema is updated.
4115    ${ECHO} "  ${STEP}. ACI for ${LDAP_BASEDN} modified to disable self modify."
4116    STEP=`expr $STEP + 1`
4117}
4118
4119
4120#
4121# add_vlv_aci(): Add access control information (aci) for VLV.
4122#
4123add_vlv_aci()
4124{
4125    [ $DEBUG -eq 1 ] && ${ECHO} "In add_vlv_aci()"
4126
4127    # Add the VLV ACI.
4128    ( cat <<EOF
4129dn: oid=2.16.840.1.113730.3.4.9,cn=features,cn=config
4130changetype: modify
4131replace: aci
4132aci: (targetattr != "aci") (version 3.0; acl "VLV Request Control"; allow(read,search,compare) userdn = "ldap:///anyone";)
4133EOF
4134) > ${TMPDIR}/vlv_aci
4135
4136    # Add the entry.
4137    ${EVAL} "${LDAPMODIFY} ${LDAP_ARGS} -f ${TMPDIR}/vlv_aci ${VERB}"
4138    if [ $? -ne 0 ]; then
4139	${ECHO} "  ERROR: Add of VLV ACI failed!"
4140	cleanup
4141	exit 1
4142    fi
4143
4144    # Display message that schema is updated.
4145    ${ECHO} "  ${STEP}. Add of VLV Access Control Information (ACI)."
4146    STEP=`expr $STEP + 1`
4147}
4148
4149
4150#
4151# set_nisdomain(): Add the NisDomainObject to the Base DN.
4152#
4153set_nisdomain()
4154{
4155    [ $DEBUG -eq 1 ] && ${ECHO} "In set_nisdomain()"
4156
4157    # Check if nisDomain is already set.
4158    ${EVAL} "${LDAPSEARCH} ${LDAP_ARGS} -b \"${LDAP_BASEDN}\" -s base \
4159	\"objectclass=*\"" > ${TMPDIR}/chk_nisdomain 2>&1
4160    ${EVAL} "${GREP} -i nisDomain ${TMPDIR}/chk_nisdomain ${VERB}"
4161    if [ $? -eq 0 ]; then
4162	${ECHO} "  ${STEP}. NisDomainObject for ${LDAP_BASEDN} was already set."
4163	STEP=`expr $STEP + 1`
4164	return 0
4165    fi
4166
4167    # Add the new top level containers.
4168    ( cat <<EOF
4169dn: ${LDAP_BASEDN}
4170changetype: modify
4171objectclass: nisDomainObject
4172nisdomain: ${LDAP_DOMAIN}
4173EOF
4174) > ${TMPDIR}/nis_domain
4175
4176    # Add the entry.
4177    ${EVAL} "${LDAPMODIFY} ${LDAP_ARGS} -f ${TMPDIR}/nis_domain ${VERB}"
4178    if [ $? -ne 0 ]; then
4179	${ECHO} "  ERROR: update of NisDomainObject in ${LDAP_BASEDN} failed."
4180	cleanup
4181	exit 1
4182    fi
4183
4184    # Display message that schema is updated.
4185    ${ECHO} "  ${STEP}. NisDomainObject added to ${LDAP_BASEDN}."
4186    STEP=`expr $STEP + 1`
4187}
4188
4189
4190#
4191# check_attrName(): Check that the attribute name is valid.
4192#              $1   Key to check.
4193#         Returns   0 : valid name	1 : invalid name
4194#
4195check_attrName()
4196{
4197    [ $DEBUG -eq 1 ] && ${ECHO} "In check_attrName()"
4198    [ $DEBUG -eq 1 ] && ${ECHO} "check_attrName: Input Param = $1"
4199
4200    ${ECHO} $1 | ${EGREP} '^[0-9]+(\.[0-9]+)*$' > /dev/null 2>&1
4201    if [ $? -eq 0 ]; then
4202	${EVAL} "${LDAPSEARCH} ${SERVER_ARGS} -b cn=schema -s base \"objectclass=*\" \
4203			attributeTypes | ${EGREP} -i '^attributetypes[ ]*=[ ]*\([ ]*$1 ' ${VERB}"
4204    else
4205	${EVAL} "${LDAPSEARCH} ${SERVER_ARGS} -b cn=schema -s base \"objectclass=*\" \
4206			attributeTypes | ${EGREP} -i \"'$1'\" ${VERB}"
4207    fi
4208
4209    if [ $? -ne 0 ]; then
4210	return 1
4211    else
4212	return 0
4213    fi
4214}
4215
4216
4217#
4218# get_objectclass():   Determine the objectclass for the given attribute name
4219#              $1   Attribute name to check.
4220#      _ATTR_NAME   Return value, Object Name or NULL if unknown to idsconfig.
4221#
4222#      NOTE: An attribute name can be valid but still we might not be able
4223#            to determine the objectclass from the table.
4224#            In such cases, the user needs to create the necessary object(s).
4225#
4226get_objectclass()
4227{
4228    [ $DEBUG -eq 1 ] && ${ECHO} "In get_objectclass()"
4229    [ $DEBUG -eq 1 ] && ${ECHO} "get_objectclass: Input Param = $1"
4230
4231    # Set return value to NULL string.
4232    _ATTR_NAME=""
4233
4234    # Test key for type:
4235    case `${ECHO} ${1} | tr '[A-Z]' '[a-z]'` in
4236	ou | organizationalunitname | 2.5.4.11) _ATTR_NAME="organizationalUnit" ;;
4237	dc | domaincomponent | 0.9.2342.19200300.100.1.25) _ATTR_NAME="domain" ;;
4238	 o | organizationname | 2.5.4.10) _ATTR_NAME="organization" ;;
4239	 c | countryname | 2.5.4.6) _ATTR_NAME="country" ;;
4240	 *)  _ATTR_NAME="" ;;
4241    esac
4242
4243    [ $DEBUG -eq 1 ] && ${ECHO} "get_objectclass: _ATTR_NAME = $_ATTR_NAME"
4244}
4245
4246
4247#
4248# add_base_objects(): Add any necessary base objects.
4249#
4250add_base_objects()
4251{
4252    [ $DEBUG -eq 1 ] && ${ECHO} "In add_base_objects()"
4253
4254    # Convert to lower case for basename.
4255    format_string "${LDAP_BASEDN}"
4256    LOWER_BASEDN="${FMT_STR}"
4257    format_string "${LDAP_SUFFIX}"
4258    LOWER_SUFFIX="${FMT_STR}"
4259
4260    [ $DEBUG -eq 1 ] && ${ECHO} "LOWER_BASEDN: ${LOWER_BASEDN}"
4261    [ $DEBUG -eq 1 ] && ${ECHO} "LOWER_SUFFIX: ${LOWER_SUFFIX}"
4262
4263    # Create additional components.
4264    if [ "${LOWER_BASEDN}" = "${LOWER_SUFFIX}" ]; then
4265	[ $DEBUG -eq 1 ] && ${ECHO} "Base DN and Suffix equivalent"
4266    else
4267	# first, test that the suffix is valid
4268	dcstmp=`basename "${LOWER_BASEDN}" "${LOWER_SUFFIX}"`
4269	if [ "$dcstmp" = "${LOWER_BASEDN}" ]; then
4270	    # should not happen since check_basedn_suffix() succeeded
4271	    ${ECHO} "Invalid suffix ${LOWER_SUFFIX}"
4272	    ${ECHO} "for Base DN ${LOWER_BASEDN}"
4273	    cleanup
4274	    exit 1
4275	fi
4276	# OK, suffix is valid, start working with LDAP_BASEDN
4277	# field separator is ',' (i.e., space is a valid character)
4278	dcstmp2="`${ECHO} ${LDAP_BASEDN} |
4279		sed -e 's/[ ]*,[ ]*/,/g' -e 's/[ ]*=[ ]*/=/g'`"
4280	dcs=""
4281	# use dcstmp to count the loop, and dcstmp2 to get the correct
4282	# string case
4283	# dcs should be in reverse order, only for these components
4284	# that need to be added
4285	while [ -n "${dcstmp}" ]
4286	do
4287	    i2=`${ECHO} "$dcstmp2" | cut -f1 -d','`
4288	    dk=`${ECHO} $i2 | awk -F= '{print $1}'`
4289	    dc=`${ECHO} $i2 | awk -F= '{print $2}'`
4290	    dcs="$dk=$dc,$dcs";
4291	    dcstmp2=`${ECHO} "$dcstmp2" | cut -f2- -d','`
4292	    dcstmp=`${ECHO} "$dcstmp" | cut -f2- -d','`
4293	    [ $DEBUG -eq 1 ] && \
4294		${ECHO} "dcs: ${dcs}\ndcstmp: ${dcstmp}\ndcstmp2: ${dcstmp2}\n"
4295	done
4296
4297
4298
4299	lastdc=${LDAP_SUFFIX}
4300	dc=`${ECHO} "${dcs}" | cut -f1 -d','`
4301	dcstmp=`${ECHO} "${dcs}" | cut -f2- -d','`
4302	while [ -n "${dc}" ]; do
4303	    # Get Key and component from $dc.
4304	    dk2=`${ECHO} $dc | awk -F= '{print $1}'`
4305	    dc2=`${ECHO} $dc | awk -F= '{print $2}'`
4306
4307	    # At this point, ${dk2} is a valid attribute name
4308
4309	    # Check if entry exists first, if so, skip to next.
4310	    ${LDAPSEARCH} ${SERVER_ARGS} -b "${dk2}=${dc2},$lastdc" -s base "objectclass=*" > /dev/null 2>&1
4311	    if [ $? -eq 0 ]; then
4312	        # Set the $lastdc to new dc.
4313	        lastdc="${dk2}=${dc2},$lastdc"
4314
4315		# Process next component.
4316		dc=`${ECHO} "${dcstmp}" | cut -f1 -d','`
4317		dcstmp=`${ECHO} "${dcstmp}" | cut -f2- -d','`
4318		continue
4319
4320	    fi
4321
4322	    # Determine the objectclass for the entry.
4323            get_objectclass $dk2
4324	    OBJ_Name=${_ATTR_NAME}
4325	    if [ "${OBJ_Name}" = "" ]; then
4326	        ${ECHO} "Cannot determine objectclass for $dk2"
4327	        ${ECHO} "Please create ${dk2}=${dc2},$lastdc entry and rerun idsconfig"
4328	        exit 1
4329	    fi
4330
4331	    # Add the new container.
4332	    ( cat <<EOF
4333dn: ${dk2}=${dc2},$lastdc
4334${dk2}: $dc2
4335objectClass: top
4336objectClass: ${OBJ_Name}
4337EOF
4338) > ${TMPDIR}/base_objects
4339
4340
4341	    # Set the $lastdc to new dc.
4342	    lastdc="${dk2}=${dc2},$lastdc"
4343
4344	    # Add the entry.
4345	    ${EVAL} "${LDAPMODIFY} -a ${LDAP_ARGS} -f ${TMPDIR}/base_objects ${VERB}"
4346	    if [ $? -ne 0 ]; then
4347		${ECHO} "  ERROR: update of base objects ${dc} failed."
4348		cleanup
4349		exit 1
4350	    fi
4351
4352	    # Display message that schema is updated.
4353	    ${ECHO} "  ${STEP}. Created DN component ${dc}."
4354	    STEP=`expr $STEP + 1`
4355
4356	    # Process next component.
4357	    dc=`${ECHO} "${dcstmp}" | cut -f1 -d','`
4358	    dcstmp=`${ECHO} "${dcstmp}" | cut -f2- -d','`
4359	done
4360    fi
4361}
4362
4363
4364#
4365# add_new_containers(): Add the top level classes.
4366#
4367#    $1 = Base DN
4368#
4369add_new_containers()
4370{
4371    [ $DEBUG -eq 1 ] && ${ECHO} "In add_new_containers()"
4372
4373    for ou in people group rpc protocols networks netgroup \
4374	aliases hosts services ethers profile printers \
4375	SolarisAuthAttr SolarisProfAttr Timezone ipTnet ; do
4376
4377	# Check if nismaps already exist.
4378	eval "${LDAPSEARCH} ${LDAP_ARGS} -b \"ou=${ou},${LDAP_BASEDN}\" -s base \"objectclass=*\" ${VERB}"
4379	if [ $? -eq 0 ]; then
4380	    continue
4381	fi
4382
4383	# Create TMP file to add.
4384	( cat <<EOF
4385dn: ou=${ou},${LDAP_BASEDN}
4386ou: ${ou}
4387objectClass: top
4388objectClass: organizationalUnit
4389EOF
4390) > ${TMPDIR}/toplevel.${ou}
4391
4392	# Add the entry.
4393	${EVAL} "${LDAPMODIFY} -a ${LDAP_ARGS} -f ${TMPDIR}/toplevel.${ou} ${VERB}"
4394	if [ $? -ne 0 ]; then
4395	    ${ECHO} "  ERROR: Add of ou=${ou} container failed!"
4396	    cleanup
4397	    exit 1
4398	fi
4399    done
4400
4401    # Display message that top level OU containers complete.
4402    ${ECHO} "  ${STEP}. Top level \"ou\" containers complete."
4403    STEP=`expr $STEP + 1`
4404}
4405
4406
4407#
4408# add_auto_maps(): Add the automount map entries.
4409#
4410# auto_home, auto_direct, auto_master, auto_shared
4411#
4412add_auto_maps()
4413{
4414    [ $DEBUG -eq 1 ] && ${ECHO} "In add_auto_maps()"
4415
4416    # Set AUTO_MAPS for maps to create.
4417    AUTO_MAPS="auto_home auto_direct auto_master auto_shared"
4418
4419    for automap in $AUTO_MAPS; do
4420	# Check if automaps already exist.
4421	eval "${LDAPSEARCH} ${LDAP_ARGS} -b \"automountMapName=${automap},${LDAP_BASEDN}\" -s base \"objectclass=*\" ${VERB}"
4422	if [ $? -eq 0 ]; then
4423	    continue
4424	fi
4425
4426	# Create the tmp file to add.
4427	( cat <<EOF
4428dn: automountMapName=${automap},${LDAP_BASEDN}
4429automountMapName: ${automap}
4430objectClass: top
4431objectClass: automountMap
4432EOF
4433) > ${TMPDIR}/automap.${automap}
4434
4435	# Add the entry.
4436	${EVAL} "${LDAPMODIFY} -a ${LDAP_ARGS} -f ${TMPDIR}/automap.${automap} ${VERB}"
4437	if [ $? -ne 0 ]; then
4438	    ${ECHO} "  ERROR: Add of automap ${automap} failed!"
4439	    cleanup
4440	    exit 1
4441	fi
4442    done
4443
4444    # Display message that automount entries are updated.
4445    ${ECHO} "  ${STEP}. automount maps: $AUTO_MAPS processed."
4446    STEP=`expr $STEP + 1`
4447}
4448
4449
4450#
4451# add_proxyagent(): Add entry for nameservice to use to access server.
4452#
4453add_proxyagent()
4454{
4455    [ $DEBUG -eq 1 ] && ${ECHO} "In add_proxyagent()"
4456
4457    # Check if nismaps already exist.
4458    eval "${LDAPSEARCH} ${LDAP_ARGS} -b \"${LDAP_PROXYAGENT}\" -s base \"objectclass=*\" ${VERB}"
4459    if [ $? -eq 0 ]; then
4460	${ECHO} "  ${STEP}. Proxy Agent ${LDAP_PROXYAGENT} already exists."
4461	STEP=`expr $STEP + 1`
4462	return 0
4463    fi
4464
4465    # Get cn and sn names from LDAP_PROXYAGENT.
4466    cn_tmp=`${ECHO} ${LDAP_PROXYAGENT} | cut -f1 -d, | cut -f2 -d=`
4467
4468    # Create the tmp file to add.
4469    ( cat <<EOF
4470dn: ${LDAP_PROXYAGENT}
4471cn: ${cn_tmp}
4472sn: ${cn_tmp}
4473objectclass: top
4474objectclass: person
4475userpassword: ${LDAP_PROXYAGENT_CRED}
4476EOF
4477) > ${TMPDIR}/proxyagent
4478
4479    # Add the entry.
4480    ${EVAL} "${LDAPMODIFY} -a ${LDAP_ARGS} -f ${TMPDIR}/proxyagent ${VERB}"
4481    if [ $? -ne 0 ]; then
4482	${ECHO} "  ERROR: Adding proxyagent failed!"
4483	cleanup
4484	exit 1
4485    fi
4486
4487    # Display message that schema is updated.
4488    ${ECHO} "  ${STEP}. Proxy Agent ${LDAP_PROXYAGENT} added."
4489    STEP=`expr $STEP + 1`
4490}
4491
4492
4493#
4494# allow_proxy_read_pw(): Give Proxy Agent read permission for password.
4495#
4496allow_proxy_read_pw()
4497{
4498    [ $DEBUG -eq 1 ] && ${ECHO} "In allow_proxy_read_pw()"
4499
4500    # Set ACI Name
4501    PROXY_ACI_NAME="LDAP_Naming_Services_proxy_password_read"
4502
4503    # Search for ACI_NAME
4504    eval "${LDAPSEARCH} ${LDAP_ARGS} -b \"${LDAP_BASEDN}\" -s base objectclass=* aci > ${TMPDIR}/chk_proxyread_aci 2>&1"
4505    ${GREP} "${PROXY_ACI_NAME}" ${TMPDIR}/chk_proxyread_aci > /dev/null 2>&1
4506    if [ $? -eq 0 ]; then
4507	${ECHO} "  ${STEP}. Proxy ACI ${PROXY_ACI_NAME=} already exists for ${LDAP_BASEDN}."
4508	STEP=`expr $STEP + 1`
4509	return 0
4510    fi
4511
4512    # Create the tmp file to add.
4513    ( cat <<EOF
4514dn: ${LDAP_BASEDN}
4515changetype: modify
4516add: aci
4517aci: (target="ldap:///${LDAP_BASEDN}")(targetattr="userPassword")(version 3.0; acl ${PROXY_ACI_NAME}; allow (compare,read,search) userdn = "ldap:///${LDAP_PROXYAGENT}";)
4518EOF
4519) > ${TMPDIR}/proxy_read
4520
4521    # Add the entry.
4522    ${EVAL} "${LDAPMODIFY} ${LDAP_ARGS} -f ${TMPDIR}/proxy_read ${VERB}"
4523    if [ $? -ne 0 ]; then
4524	${ECHO} "  ERROR: Allow ${LDAP_PROXYAGENT} to read password failed!"
4525	cleanup
4526	exit 1
4527    fi
4528
4529    # Display message that schema is updated.
4530    ${ECHO} "  ${STEP}. Give ${LDAP_PROXYAGENT} read permission for password."
4531    STEP=`expr $STEP + 1`
4532}
4533
4534
4535#
4536# add_profile(): Add client profile to server.
4537#
4538add_profile()
4539{
4540    [ $DEBUG -eq 1 ] && ${ECHO} "In add_profile()"
4541
4542    # If profile name already exists, DELETE it, and add new one.
4543    eval "${LDAPSEARCH} ${LDAP_ARGS} -b \"cn=${LDAP_PROFILE_NAME},ou=profile,${LDAP_BASEDN}\" -s base \"objectclass=*\" ${VERB}"
4544    if [ $? -eq 0 ]; then
4545	# Create Delete file.
4546	( cat <<EOF
4547cn=${LDAP_PROFILE_NAME},ou=profile,${LDAP_BASEDN}
4548EOF
4549) > ${TMPDIR}/del_profile
4550
4551	# Check if DEL_OLD_PROFILE is set.  (If not ERROR)
4552	if [ $DEL_OLD_PROFILE -eq 0 ]; then
4553	    ${ECHO} "ERROR: Profile name ${LDAP_PROFILE_NAME} exists! Add failed!"
4554	    exit 1
4555	fi
4556
4557	# Delete the OLD profile.
4558	${EVAL} "${LDAPDELETE} ${LDAP_ARGS} -f ${TMPDIR}/del_profile ${VERB}"
4559	if [ $? -ne 0 ]; then
4560	    ${ECHO} "  ERROR: Attempt to DELETE profile failed!"
4561	    cleanup
4562	    exit 1
4563	fi
4564    fi
4565
4566    # Build the "ldapclient genprofile" command string to execute.
4567    GEN_CMD="ldapclient genprofile -a \"profileName=${LDAP_PROFILE_NAME}\""
4568
4569    # Add required argument defaultSearchBase.
4570    GEN_CMD="${GEN_CMD} -a \"defaultSearchBase=${LDAP_BASEDN}\""
4571
4572    # Add optional parameters.
4573    [ -n "$LDAP_SERVER_LIST" ] && \
4574	GEN_CMD="${GEN_CMD} -a \"defaultServerList=${LDAP_SERVER_LIST}\""
4575    [ -n "$LDAP_SEARCH_SCOPE" ] && \
4576	GEN_CMD="${GEN_CMD} -a \"defaultSearchScope=${LDAP_SEARCH_SCOPE}\""
4577    [ -n "$LDAP_CRED_LEVEL" ] && \
4578	GEN_CMD="${GEN_CMD} -a \"credentialLevel=${LDAP_CRED_LEVEL}\""
4579    [ -n "$LDAP_AUTHMETHOD" ] && \
4580	GEN_CMD="${GEN_CMD} -a \"authenticationMethod=${LDAP_AUTHMETHOD}\""
4581    [ -n "$LDAP_FOLLOWREF" ] && \
4582	GEN_CMD="${GEN_CMD} -a \"followReferrals=${LDAP_FOLLOWREF}\""
4583    [ -n "$LDAP_SEARCH_TIME_LIMIT" ] && \
4584	GEN_CMD="${GEN_CMD} -a \"searchTimeLimit=${LDAP_SEARCH_TIME_LIMIT}\""
4585    [ -n "$LDAP_PROFILE_TTL" ] && \
4586	GEN_CMD="${GEN_CMD} -a \"profileTTL=${LDAP_PROFILE_TTL}\""
4587    [ -n "$LDAP_BIND_LIMIT" ] && \
4588	GEN_CMD="${GEN_CMD} -a \"bindTimeLimit=${LDAP_BIND_LIMIT}\""
4589    [ -n "$LDAP_PREF_SRVLIST" ] && \
4590	GEN_CMD="${GEN_CMD} -a \"preferredServerList=${LDAP_PREF_SRVLIST}\""
4591    [ -n "$LDAP_SRV_AUTHMETHOD_PAM" ] && \
4592	GEN_CMD="${GEN_CMD} -a \"serviceAuthenticationMethod=${LDAP_SRV_AUTHMETHOD_PAM}\""
4593    [ -n "$LDAP_SRV_AUTHMETHOD_KEY" ] && \
4594	GEN_CMD="${GEN_CMD} -a \"serviceAuthenticationMethod=${LDAP_SRV_AUTHMETHOD_KEY}\""
4595    [ -n "$LDAP_SRV_AUTHMETHOD_CMD" ] && \
4596	GEN_CMD="${GEN_CMD} -a \"serviceAuthenticationMethod=${LDAP_SRV_AUTHMETHOD_CMD}\""
4597
4598    # Check if there are any service search descriptors to ad.
4599    if [ -s "${SSD_FILE}" ]; then
4600	ssd_2_profile
4601    fi
4602
4603    # Execute "ldapclient genprofile" to create profile.
4604    eval ${GEN_CMD} > ${TMPDIR}/gen_profile 2> ${TMPDIR}/gen_profile_ERR
4605    if [ $? -ne 0 ]; then
4606	${ECHO} "  ERROR: ldapclient genprofile failed!"
4607	cleanup
4608	exit 1
4609    fi
4610
4611    # Add the generated profile..
4612    ${EVAL} "${LDAPMODIFY} -a ${LDAP_ARGS} -f ${TMPDIR}/gen_profile ${VERB}"
4613    if [ $? -ne 0 ]; then
4614	${ECHO} "  ERROR: Attempt to add profile failed!"
4615	cleanup
4616	exit 1
4617    fi
4618
4619    # Display message that schema is updated.
4620    ${ECHO} "  ${STEP}. Generated client profile and loaded on server."
4621    STEP=`expr $STEP + 1`
4622}
4623
4624
4625#
4626# cleanup(): Remove the TMPDIR and all files in it.
4627#
4628cleanup()
4629{
4630    [ $DEBUG -eq 1 ] && ${ECHO} "In cleanup()"
4631
4632    rm -fr ${TMPDIR}
4633}
4634
4635
4636#
4637# 			* * * MAIN * * *
4638#
4639# Description:
4640# This script assumes that the iPlanet Directory Server (iDS) is
4641# installed and that setup has been run.  This script takes the
4642# iDS server from that point and sets up the infrastructure for
4643# LDAP Naming Services.  After running this script, ldapaddent(1M)
4644# or some other tools can be used to populate data.
4645
4646# Initialize the variables that need to be set to NULL, or some
4647# other initial value before the rest of the functions can be called.
4648init
4649
4650# Parse command line arguments.
4651parse_arg $*
4652shift $?
4653
4654# Print extra line to separate from prompt.
4655${ECHO} " "
4656
4657# Either Load the user specified config file
4658# or prompt user for config info.
4659if [ -n "$INPUT_FILE" ]
4660then
4661    load_config_file
4662    INTERACTIVE=0      # Turns off prompts that occur later.
4663    validate_info      # Validate basic info in file.
4664    chk_ids_version    # Check iDS version for compatibility.
4665    gssapi_setup_auto
4666else
4667    # Display BACKUP warning to user.
4668    display_msg backup_server
4669    get_confirm "Do you wish to continue with server setup (y/n/h)?" "n" "backup_help"
4670    if [ $? -eq 0 ]; then    # if No, cleanup and exit.
4671	cleanup ; exit 1
4672    fi
4673
4674    # Prompt for values.
4675    prompt_config_info
4676    display_summary    # Allow user to modify results.
4677    INTERACTIVE=1      # Insures future prompting.
4678fi
4679
4680# Modify slapd.oc.conf to ALLOW cn instead of REQUIRE.
4681modify_cn
4682
4683# Modify timelimit to user value.
4684[ $NEED_TIME -eq 1 ] && modify_timelimit
4685
4686# Modify sizelimit to user value.
4687[ $NEED_SIZE -eq 1 ] && modify_sizelimit
4688
4689# Modify the password storage scheme to support CRYPT.
4690if [ "$NEED_CRYPT" = "TRUE" ]; then
4691    modify_pwd_crypt
4692fi
4693
4694# Update the schema (Attributes, Objectclass Definitions)
4695if [ ${SCHEMA_UPDATED} -eq 0 ]; then
4696        update_schema_attr
4697        update_schema_obj
4698fi
4699
4700# Add suffix together with its root entry (if needed)
4701add_suffix ||
4702{
4703	cleanup
4704	exit 1
4705}
4706
4707# Add base objects (if needed)
4708add_base_objects
4709
4710# Update the NisDomainObject.
4711#   The Base DN might of just been created, so this MUST happen after
4712#   the base objects have been added!
4713set_nisdomain
4714
4715# Add top level classes (new containers)
4716add_new_containers
4717
4718# Add common nismaps.
4719add_auto_maps
4720
4721# Modify top ACI.
4722modify_top_aci
4723
4724# Add Access Control Information for VLV.
4725add_vlv_aci
4726
4727# if Proxy needed, Add Proxy Agent and give read permission for password.
4728if [ $NEED_PROXY -eq 1 ]; then
4729    add_proxyagent
4730    allow_proxy_read_pw
4731fi
4732
4733# Generate client profile and add it to the server.
4734add_profile
4735
4736# Add Indexes to improve Search Performance.
4737add_eq_indexes
4738add_sub_indexes
4739add_vlv_indexes
4740
4741# Display setup complete message
4742display_msg setup_complete
4743
4744# Display VLV index commands to be executed on server.
4745display_vlv_cmds
4746
4747# Create config file if requested.
4748[ -n "$OUTPUT_FILE" ] && create_config_file
4749
4750# Removed the TMPDIR and all files in it.
4751cleanup
4752
4753exit 0
4754# end of MAIN.
4755