xref: /titanic_50/usr/src/cmd/ldap/ns_ldap/idsconfig.sh (revision dfb96a4f56fb431b915bc67e5d9d5c8d4f4f6679)
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 2007 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
3457    # Set _EXT to use as shortcut.
3458    _EXT="cn=index,cn=${IDS_DATABASE},cn=ldbm database,cn=plugins,cn=config"
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	${EVAL} "${LDAPSEARCH} ${LDAP_ARGS} -b \"cn=${i},${_EXT}\" -s base \
3470	    \"objectclass=*\" > /dev/null 2>&1"
3471	if [ $? -eq 0 ]; then
3472	    # Display index skipped.
3473	    ${ECHO} "      ${i} (eq,pres) skipped already exists"
3474	    continue
3475	fi
3476
3477	# Here doc to create LDIF.
3478	( cat <<EOF
3479dn: cn=${i},${_EXT}
3480objectClass: top
3481objectClass: nsIndex
3482cn: ${i}
3483nsSystemIndex: false
3484nsIndexType: pres
3485nsIndexType: eq
3486EOF
3487) > ${TMPDIR}/index_${i}
3488
3489	# Add the index.
3490	${EVAL} "${LDAPMODIFY} -a ${LDAP_ARGS} -f ${TMPDIR}/index_${i} ${VERB}"
3491	if [ $? -ne 0 ]; then
3492	    ${ECHO} "  ERROR: Adding EQ,PRES index for ${i} failed!"
3493	    cleanup
3494	    exit 1
3495	fi
3496
3497	# Build date for task name.
3498	_YR=`date '+%y'`
3499	_MN=`date '+%m'`
3500	_DY=`date '+%d'`
3501	_H=`date '+%H'`
3502	_M=`date '+%M'`
3503	_S=`date '+%S'`
3504
3505	# Build task name
3506	TASKNAME="${i}_${_YR}_${_MN}_${_DY}_${_H}_${_M}_${_S}"
3507
3508	# Build the task entry to add.
3509	( cat <<EOF
3510dn: cn=${TASKNAME}, cn=index, cn=tasks, cn=config
3511changetype: add
3512objectclass: top
3513objectclass: extensibleObject
3514cn: ${TASKNAME}
3515nsInstance: ${IDS_DATABASE}
3516nsIndexAttribute: ${i}
3517EOF
3518) > ${TMPDIR}/task_${i}
3519
3520	# Add the task.
3521	${EVAL} "${LDAPMODIFY} -a ${LDAP_ARGS} -f ${TMPDIR}/task_${i} ${VERB}"
3522	if [ $? -ne 0 ]; then
3523	    ${ECHO} "  ERROR: Adding task for ${i} failed!"
3524	    cleanup
3525	    exit 1
3526	fi
3527
3528	# Wait for task to finish, display current status.
3529	while :
3530	do
3531	    ${EVAL} "${LDAPSEARCH} ${LDAP_ARGS} \
3532	        -b \"cn=${TASKNAME}, cn=index, cn=tasks, cn=config\" -s base \
3533	        \"objectclass=*\" nstaskstatus > \"${TMPDIR}/istask_${i}\" 2>&1"
3534	    ${GREP} "${TASKNAME}" "${TMPDIR}/istask_${i}" > /dev/null 2>&1
3535	    if [ $? -ne 0 ]; then
3536		break
3537	    fi
3538	    TASK_STATUS=`${GREP} -i nstaskstatus "${TMPDIR}/istask_${i}" |
3539	        head -1 | cut -d: -f2`
3540	    ${ECHO} "      ${i} (eq,pres)  $TASK_STATUS                  \r\c"
3541	    ${ECHO} "$TASK_STATUS" | ${GREP} "Finished" > /dev/null 2>&1
3542	    if [ $? -eq 0 ]; then
3543		break
3544	    fi
3545	    sleep 2
3546	done
3547
3548	# Print newline because of \c.
3549	${ECHO} " "
3550    done
3551}
3552
3553
3554#
3555# add_sub_indexes(): Add indexes to improve search performance.
3556#
3557add_sub_indexes()
3558{
3559    [ $DEBUG -eq 1 ] && ${ECHO} "In add_sub_indexes()"
3560
3561    # Set eq indexes to add.
3562    _INDEXES="ipHostNumber membernisnetgroup nisnetgrouptriple"
3563
3564    # Set _EXT to use as shortcut.
3565    _EXT="cn=index,cn=${IDS_DATABASE},cn=ldbm database,cn=plugins,cn=config"
3566
3567
3568    # Display message to id current step.
3569    ${ECHO} "  ${STEP}. Processing eq,pres,sub indexes:"
3570    STEP=`expr $STEP + 1`
3571
3572    # For loop to create indexes.
3573    for i in ${_INDEXES}; do
3574	[ $DEBUG -eq 1 ] && ${ECHO} "  Adding index for ${i}"
3575
3576	# Check if entry exists first, if so, skip to next.
3577	${EVAL} "${LDAPSEARCH} ${LDAP_ARGS} -b \"cn=${i},${_EXT}\" \
3578	    -s base \"objectclass=*\" > /dev/null 2>&1"
3579	if [ $? -eq 0 ]; then
3580	    # Display index skipped.
3581	    ${ECHO} "      ${i} (eq,pres,sub) skipped already exists"
3582	    continue
3583	fi
3584
3585	# Here doc to create LDIF.
3586	( cat <<EOF
3587dn: cn=${i},${_EXT}
3588objectClass: top
3589objectClass: nsIndex
3590cn: ${i}
3591nsSystemIndex: false
3592nsIndexType: pres
3593nsIndexType: eq
3594nsIndexType: sub
3595EOF
3596) > ${TMPDIR}/index_${i}
3597
3598	# Add the index.
3599	${EVAL} "${LDAPMODIFY} -a ${LDAP_ARGS} -f ${TMPDIR}/index_${i} ${VERB}"
3600	if [ $? -ne 0 ]; then
3601	    ${ECHO} "  ERROR: Adding EQ,PRES,SUB index for ${i} failed!"
3602	    cleanup
3603	    exit 1
3604	fi
3605
3606	# Build date for task name.
3607	_YR=`date '+%y'`
3608	_MN=`date '+%m'`
3609	_DY=`date '+%d'`
3610	_H=`date '+%H'`
3611	_M=`date '+%M'`
3612	_S=`date '+%S'`
3613
3614	# Build task name
3615	TASKNAME="${i}_${_YR}_${_MN}_${_DY}_${_H}_${_M}_${_S}"
3616
3617	# Build the task entry to add.
3618	( cat <<EOF
3619dn: cn=${TASKNAME}, cn=index, cn=tasks, cn=config
3620changetype: add
3621objectclass: top
3622objectclass: extensibleObject
3623cn: ${TASKNAME}
3624nsInstance: ${IDS_DATABASE}
3625nsIndexAttribute: ${i}
3626EOF
3627) > ${TMPDIR}/task_${i}
3628
3629	# Add the task.
3630	${EVAL} "${LDAPMODIFY} -a ${LDAP_ARGS} -f ${TMPDIR}/task_${i} ${VERB}"
3631	if [ $? -ne 0 ]; then
3632	    ${ECHO} "  ERROR: Adding task for ${i} failed!"
3633	    cleanup
3634	    exit 1
3635	fi
3636
3637	# Wait for task to finish, display current status.
3638	while :
3639	do
3640	    ${EVAL} "${LDAPSEARCH} ${LDAP_ARGS} \
3641	        -b \"cn=${TASKNAME}, cn=index, cn=tasks, cn=config\" -s base \
3642	        \"objectclass=*\" nstaskstatus > \"${TMPDIR}/istask_${i}\" 2>&1"
3643	    ${GREP} "${TASKNAME}" "${TMPDIR}/istask_${i}" > /dev/null 2>&1
3644	    if [ $? -ne 0 ]; then
3645		break
3646	    fi
3647	    TASK_STATUS=`${GREP} -i nstaskstatus "${TMPDIR}/istask_${i}" |
3648	        head -1 | cut -d: -f2`
3649	    ${ECHO} "      ${i} (eq,pres,sub)  $TASK_STATUS                  \r\c"
3650	    ${ECHO} "$TASK_STATUS" | ${GREP} "Finished" > /dev/null 2>&1
3651	    if [ $? -eq 0 ]; then
3652		break
3653	    fi
3654	    sleep 2
3655	done
3656
3657	# Print newline because of \c.
3658	${ECHO} " "
3659    done
3660}
3661
3662
3663#
3664# add_vlv_indexes(): Add VLV indexes to improve search performance.
3665#
3666add_vlv_indexes()
3667{
3668    [ $DEBUG -eq 1 ] && ${ECHO} "In add_vlv_indexes()"
3669
3670    # Set eq indexes to add.
3671    # Note semi colon separators because some filters contain colons
3672    _INDEX1="${LDAP_DOMAIN}.getgrent;${LDAP_DOMAIN}_group_vlv_index;ou=group;objectClass=posixGroup"
3673    _INDEX2="${LDAP_DOMAIN}.gethostent;${LDAP_DOMAIN}_hosts_vlv_index;ou=hosts;objectClass=ipHost"
3674    _INDEX3="${LDAP_DOMAIN}.getnetent;${LDAP_DOMAIN}_networks_vlv_index;ou=networks;objectClass=ipNetwork"
3675    _INDEX4="${LDAP_DOMAIN}.getpwent;${LDAP_DOMAIN}_passwd_vlv_index;ou=people;objectClass=posixAccount"
3676    _INDEX5="${LDAP_DOMAIN}.getrpcent;${LDAP_DOMAIN}_rpc_vlv_index;ou=rpc;objectClass=oncRpc"
3677    _INDEX6="${LDAP_DOMAIN}.getspent;${LDAP_DOMAIN}_shadow_vlv_index;ou=people;objectClass=shadowAccount"
3678
3679    # Indexes added during NIS to LDAP transition
3680    _INDEX7="${LDAP_DOMAIN}.getauhoent;${LDAP_DOMAIN}_auho_vlv_index;automountmapname=auto_home;objectClass=automount"
3681    _INDEX8="${LDAP_DOMAIN}.getsoluent;${LDAP_DOMAIN}_solu_vlv_index;ou=people;objectClass=SolarisUserAttr"
3682    _INDEX9="${LDAP_DOMAIN}.getauduent;${LDAP_DOMAIN}_audu_vlv_index;ou=people;objectClass=SolarisAuditUser"
3683    _INDEX10="${LDAP_DOMAIN}.getauthent;${LDAP_DOMAIN}_auth_vlv_index;ou=SolarisAuthAttr;objectClass=SolarisAuthAttr"
3684    _INDEX11="${LDAP_DOMAIN}.getexecent;${LDAP_DOMAIN}_exec_vlv_index;ou=SolarisProfAttr;&(objectClass=SolarisExecAttr)(SolarisKernelSecurityPolicy=*)"
3685    _INDEX12="${LDAP_DOMAIN}.getprofent;${LDAP_DOMAIN}_prof_vlv_index;ou=SolarisProfAttr;&(objectClass=SolarisProfAttr)(SolarisAttrLongDesc=*)"
3686    _INDEX13="${LDAP_DOMAIN}.getmailent;${LDAP_DOMAIN}_mail_vlv_index;ou=aliases;objectClass=mailGroup"
3687    _INDEX14="${LDAP_DOMAIN}.getbootent;${LDAP_DOMAIN}__boot_vlv_index;ou=ethers;&(objectClass=bootableDevice)(bootParameter=*)"
3688    _INDEX15="${LDAP_DOMAIN}.getethent;${LDAP_DOMAIN}_ethers_vlv_index;ou=ethers;&(objectClass=ieee802Device)(macAddress=*)"
3689    _INDEX16="${LDAP_DOMAIN}.getngrpent;${LDAP_DOMAIN}_netgroup_vlv_index;ou=netgroup;objectClass=nisNetgroup"
3690    _INDEX17="${LDAP_DOMAIN}.getipnent;${LDAP_DOMAIN}_ipn_vlv_index;ou=networks;&(objectClass=ipNetwork)(cn=*)"
3691    _INDEX18="${LDAP_DOMAIN}.getmaskent;${LDAP_DOMAIN}_mask_vlv_index;ou=networks;&(objectClass=ipNetwork)(ipNetmaskNumber=*)"
3692    _INDEX19="${LDAP_DOMAIN}.getprent;${LDAP_DOMAIN}_pr_vlv_index;ou=printers;objectClass=printerService"
3693    _INDEX20="${LDAP_DOMAIN}.getip4ent;${LDAP_DOMAIN}_ip4_vlv_index;ou=hosts;&(objectClass=ipHost)(ipHostNumber=*.*)"
3694    _INDEX21="${LDAP_DOMAIN}.getip6ent;${LDAP_DOMAIN}_ip6_vlv_index;ou=hosts;&(objectClass=ipHost)(ipHostNumber=*:*)"
3695
3696    _INDEXES="$_INDEX1 $_INDEX2 $_INDEX3 $_INDEX4 $_INDEX5 $_INDEX6 $_INDEX7 $_INDEX8 $_INDEX9 $_INDEX10 $_INDEX11 $_INDEX12 $_INDEX13 $_INDEX14 $_INDEX15 $_INDEX16 $_INDEX17 $_INDEX18 $_INDEX19 $_INDEX20 $_INDEX21 "
3697
3698
3699    # Set _EXT to use as shortcut.
3700    _EXT="cn=${IDS_DATABASE},cn=ldbm database,cn=plugins,cn=config"
3701
3702
3703    # Display message to id current step.
3704    ${ECHO} "  ${STEP}. Processing VLV indexes:"
3705    STEP=`expr $STEP + 1`
3706
3707    # Reset temp file for vlvindex commands.
3708    [ -f ${TMPDIR}/vlvindex_list ] &&  rm ${TMPDIR}/vlvindex_list
3709    touch ${TMPDIR}/vlvindex_list
3710
3711    # Get the instance name from iDS server.
3712    _INSTANCE="<server-instance>"    # Default to old output.
3713
3714    eval "${LDAPSEARCH} -v ${LDAP_ARGS} -b \"cn=config\" -s base \"objectclass=*\" nsslapd-instancedir | ${GREP} 'nsslapd-instancedir=' | cut -d'=' -f2- > ${TMPDIR}/instance_name 2>&1"
3715
3716    ${GREP} "slapd-" ${TMPDIR}/instance_name > /dev/null 2>&1 # Check if seems right?
3717    if [ $? -eq 0 ]; then # If success, grab name after "slapd-".
3718	_INST_DIR=`cat ${TMPDIR}/instance_name`
3719	_INSTANCE=`basename "${_INST_DIR}" | cut -d'-' -f2-`
3720    fi
3721
3722    # For loop to create indexes.
3723    for p in ${_INDEXES}; do
3724	[ $DEBUG -eq 1 ] && ${ECHO} "  Adding index for ${i}"
3725
3726	# Break p (pair) into i and j parts.
3727        i=`${ECHO} $p | cut -d';' -f1`
3728        j=`${ECHO} $p | cut -d';' -f2`
3729        k=`${ECHO} $p | cut -d';' -f3`
3730        m=`${ECHO} $p | cut -d';' -f4`
3731
3732	# Set _jEXT to use as shortcut.
3733	_jEXT="cn=${j},${_EXT}"
3734
3735	# Check if entry exists first, if so, skip to next.
3736	${LDAPSEARCH} ${SERVER_ARGS} -b "cn=${i},${_jEXT}" -s base "objectclass=*" > /dev/null 2>&1
3737	if [ $? -eq 0 ]; then
3738	    # Display index skipped.
3739	    ${ECHO} "      ${i} vlv_index skipped already exists"
3740	    continue
3741	fi
3742
3743	# Compute the VLV Scope from the LDAP_SEARCH_SCOPE.
3744	# NOTE: A value of "base (0)" does not make sense.
3745        case "$LDAP_SEARCH_SCOPE" in
3746            sub) VLV_SCOPE="2" ;;
3747            *)   VLV_SCOPE="1" ;;
3748        esac
3749
3750	# Here doc to create LDIF.
3751	( cat <<EOF
3752dn: ${_jEXT}
3753objectClass: top
3754objectClass: vlvSearch
3755cn: ${j}
3756vlvbase: ${k},${LDAP_BASEDN}
3757vlvscope: ${VLV_SCOPE}
3758vlvfilter: (${m})
3759aci: (target="ldap:///${_jEXT}")(targetattr="*")(version 3.0; acl "Config";allow(read,search,compare)userdn="ldap:///anyone";)
3760
3761dn: cn=${i},${_jEXT}
3762cn: ${i}
3763vlvSort: cn uid
3764objectclass: top
3765objectclass: vlvIndex
3766EOF
3767) > ${TMPDIR}/vlv_index_${i}
3768
3769	# Add the index.
3770	${EVAL} "${LDAPMODIFY} -a ${LDAP_ARGS} -f ${TMPDIR}/vlv_index_${i} ${VERB}"
3771	if [ $? -ne 0 ]; then
3772	    ${ECHO} "  ERROR: Adding VLV index for ${i} failed!"
3773	    cleanup
3774	    exit 1
3775	fi
3776
3777	# Print message that index was created.
3778	${ECHO} "      ${i} vlv_index   Entry created"
3779
3780	# Add command to list of vlvindex commands to run.
3781	${ECHO} "  directoryserver -s ${_INSTANCE} vlvindex -n ${IDS_DATABASE} -T ${i}" >> ${TMPDIR}/vlvindex_list
3782    done
3783}
3784
3785
3786#
3787# display_vlv_cmds(): Display VLV index commands to run on server.
3788#
3789display_vlv_cmds()
3790{
3791    if [ -s "${TMPDIR}/vlvindex_list" ]; then
3792	display_msg display_vlv_list
3793	cat ${TMPDIR}/vlvindex_list
3794    fi
3795}
3796
3797
3798#
3799# update_schema_attr(): Update Schema to support Naming.
3800#
3801update_schema_attr()
3802{
3803    [ $DEBUG -eq 1 ] && ${ECHO} "In update_schema_attr()"
3804
3805    ( cat <<EOF
3806dn: cn=schema
3807changetype: modify
3808add: attributetypes
3809attributetypes: ( 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' )
3810attributetypes: ( 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' )
3811attributetypes: ( 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' )
3812attributetypes: ( 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 )
3813attributetypes: ( 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 )
3814attributetypes: ( 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 )
3815attributetypes: ( 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' )
3816attributetypes: ( 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' )
3817attributetypes: ( 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' )
3818attributetypes: ( rfc822mailMember-oid NAME 'rfc822mailMember' SYNTAX '1.3.6.1.4.1.1466.115.121.1.26' )
3819attributetypes: ( 2.16.840.1.113730.3.1.30 NAME 'mgrpRFC822MailMember' SYNTAX '1.3.6.1.4.1.1466.115.121.1.15' )
3820attributetypes: ( 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' )
3821attributetypes: ( 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 )
3822attributetypes: ( 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 )
3823attributetypes: ( 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 )
3824attributetypes: ( 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 )
3825attributetypes: ( 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')
3826attributetypes: ( 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')
3827attributetypes: ( 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 )
3828attributetypes: ( 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 )
3829attributetypes: ( 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')
3830attributetypes: ( 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 )
3831attributetypes: ( 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 )
3832attributetypes: ( 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')
3833attributetypes: ( 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 )
3834attributetypes: ( 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 )
3835attributetypes: ( 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 )
3836attributetypes: ( 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 )
3837attributetypes: ( 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 )
3838attributetypes: ( 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 )
3839attributetypes: ( 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 )
3840attributetypes: ( 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 )
3841attributetypes: ( 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 )
3842attributetypes: ( 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 )
3843attributetypes: ( 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 )
3844attributetypes: ( 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 )
3845attributetypes: ( 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 )
3846attributetypes: ( 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 )
3847attributetypes: ( 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 )
3848attributetypes: ( 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' )
3849attributetypes: ( memberGid-oid NAME 'memberGid' SYNTAX '1.3.6.1.4.1.1466.115.121.1.26' )
3850attributetypes: ( 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 )
3851attributetypes: ( 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 )
3852attributetypes: ( 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 )
3853attributetypes: ( 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 )
3854attributetypes: ( 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 )
3855attributetypes: ( 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 )
3856attributetypes: ( 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 )
3857attributetypes: ( 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 )
3858attributetypes: ( 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' )
3859attributetypes: ( 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' )
3860attributetypes: ( 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 )
3861attributetypes: ( 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' )
3862attributetypes: ( 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 )
3863attributetypes: ( 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 )
3864attributetypes: ( 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 )
3865attributetypes:( 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 )
3866attributetypes:( 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 )
3867attributetypes:( 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 )
3868attributetypes:( 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 )
3869attributetypes:( 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 )
3870attributetypes:( 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 )
3871attributetypes:( 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 )
3872attributetypes:( 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 )
3873attributetypes:( 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} )
3874attributetypes:( 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 )
3875attributetypes:( 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 )
3876attributetypes:( 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} )
3877attributetypes:( 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} )
3878attributetypes:( 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} )
3879attributetypes:( 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 )
3880attributetypes:( 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} )
3881attributetypes:( 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 )
3882attributetypes:( 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 )
3883attributetypes:( 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} )
3884attributetypes:( 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 )
3885attributetypes:( 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} )
3886attributetypes:( 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} )
3887attributetypes:( 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} )
3888attributetypes:( 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} )
3889attributetypes:( 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} )
3890attributetypes:( 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 )
3891attributetypes:( 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 )
3892attributetypes:( 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 )
3893attributetypes:( 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 )
3894attributetypes:( 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 )
3895attributetypes:( 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} )
3896attributetypes:( 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} )
3897attributetypes:( 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} )
3898attributetypes:( 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} )
3899attributetypes:( 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 )
3900attributetypes:( 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' )
3901attributetypes: ( 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 )
3902attributetypes:( 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 )
3903attributetypes:( 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 )
3904EOF
3905) > ${TMPDIR}/schema_attr
3906
3907    # Add the entry.
3908    ${EVAL} "${LDAPMODIFY} ${LDAP_ARGS} -f ${TMPDIR}/schema_attr ${VERB}"
3909    if [ $? -ne 0 ]; then
3910	${ECHO} "  ERROR: update of schema attributes failed!"
3911	cleanup
3912	exit 1
3913    fi
3914
3915    # Display message that schema is updated.
3916    ${ECHO} "  ${STEP}. Schema attributes have been updated."
3917    STEP=`expr $STEP + 1`
3918}
3919
3920
3921#
3922# update_schema_obj(): Update the schema objectclass definitions.
3923#
3924update_schema_obj()
3925{
3926    [ $DEBUG -eq 1 ] && ${ECHO} "In update_schema_obj()"
3927
3928    # Add the objectclass definitions.
3929    ( cat <<EOF
3930dn: cn=schema
3931changetype: modify
3932add: objectclasses
3933objectclasses: ( 1.3.6.1.1.1.2.14 NAME 'NisKeyObject' SUP 'top' MUST (objectclass $ cn $ nisPublickey $ nisSecretkey) MAY (uidNumber $ description))
3934
3935dn: cn=schema
3936changetype: modify
3937add: objectclasses
3938objectclasses: ( 1.3.6.1.1.1.2.15 NAME 'nisDomainObject' SUP 'top' MUST (objectclass $ nisDomain) MAY ())
3939
3940dn: cn=schema
3941changetype: modify
3942add: objectclasses
3943objectclasses: ( 1.3.6.1.1.1.2.16 NAME 'automountMap' SUP 'top' MUST (objectclass $ automountMapName) MAY (description))
3944
3945dn: cn=schema
3946changetype: modify
3947add: objectclasses
3948objectclasses: ( 1.3.6.1.1.1.2.17 NAME 'automount' SUP 'top' MUST (objectclass $ automountKey $ automountInformation ) MAY (description))
3949
3950dn: cn=schema
3951changetype: modify
3952add: objectclasses
3953objectclasses: ( 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))
3954
3955dn: cn=schema
3956changetype: modify
3957add: objectclasses
3958objectclasses: ( 2.16.840.1.113730.3.2.4 NAME 'mailGroup' SUP 'top' MUST (objectclass $ mail) MAY (cn $ mgrpRFC822MailMember))
3959
3960dn: cn=schema
3961changetype: modify
3962add: objectclasses
3963objectclasses: ( 1.3.6.1.4.1.42.2.27.1.2.5 NAME 'nisMailAlias' SUP 'top' MUST (objectclass $ cn) MAY (rfc822mailMember))
3964
3965dn: cn=schema
3966changetype: modify
3967add: objectclasses
3968objectclasses: ( 1.3.6.1.4.1.42.2.27.1.2.6 NAME 'nisNetId' SUP 'top' MUST (objectclass $ cn) MAY (nisNetIdUser $ nisNetIdGroup $ nisNetIdHost))
3969
3970dn: cn=schema
3971changetype: modify
3972add: objectclasses
3973objectclasses: ( 1.3.6.1.4.1.42.2.27.5.2.2 NAME 'SolarisAuditUser' SUP 'top' AUXILIARY MUST (objectclass) MAY (SolarisAuditAlways $ SolarisAuditNever))
3974
3975dn: cn=schema
3976changetype: modify
3977add: objectclasses
3978objectclasses: ( 1.3.6.1.4.1.42.2.27.5.2.3 NAME 'SolarisUserAttr' SUP 'top' AUXILIARY MUST (objectclass) MAY (SolarisUserQualifier $ SolarisAttrReserved1 $ SolarisAttrReserved2 $ SolarisAttrKeyValue))
3979
3980dn: cn=schema
3981changetype: modify
3982add: objectclasses
3983objectclasses: ( 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))
3984
3985dn: cn=schema
3986changetype: modify
3987add: objectclasses
3988objectclasses: ( 1.3.6.1.4.1.42.2.27.5.2.5 NAME 'SolarisProfAttr' SUP 'top' MUST (objectclass $ cn) MAY (SolarisAttrReserved1 $ SolarisAttrReserved2 $ SolarisAttrLongDesc $ SolarisAttrKeyValue))
3989
3990dn: cn=schema
3991changetype: modify
3992add: objectclasses
3993objectclasses: ( 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))
3994
3995dn: cn=schema
3996changetype: modify
3997add: objectclasses
3998objectclasses: ( 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))
3999
4000dn: cn=schema
4001changetype: modify
4002add: objectclasses
4003objectclasses: ( 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))
4004
4005dn: cn=schema
4006changetype: modify
4007add: objectclasses
4008objectclasses: ( 1.3.18.0.2.6.2549 NAME 'slpService' DESC 'DUMMY definition' SUP 'top' MUST (objectclass) MAY ())
4009
4010dn: cn=schema
4011changetype: modify
4012add: objectclasses
4013objectclasses: ( 1.3.18.0.2.6.254 NAME 'slpServicePrinter' DESC 'Service Location Protocol (SLP) information.' AUXILIARY SUP 'slpService')
4014
4015dn: cn=schema
4016changetype: modify
4017add: objectclasses
4018objectclasses: ( 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 ))
4019
4020dn: cn=schema
4021changetype: modify
4022add: objectclasses
4023objectclasses: ( 1.3.18.0.2.6.255 NAME 'printerService' DESC 'Printer information.' STRUCTURAL SUP 'printerAbstract' MAY ( printer-uri $ printer-xri-supported ))
4024
4025dn: cn=schema
4026changetype: modify
4027add: objectclasses
4028objectclasses: ( 1.3.18.0.2.6.257 NAME 'printerServiceAuxClass' DESC 'Printer information.' AUXILIARY SUP 'printerAbstract' MAY ( printer-uri $ printer-xri-supported ))
4029
4030dn: cn=schema
4031changetype: modify
4032add: objectclasses
4033objectclasses: ( 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 ))
4034
4035dn: cn=schema
4036changetype: modify
4037add: objectclasses
4038objectclasses: ( 1.3.18.0.2.6.253 NAME 'printerLPR' DESC 'LPR information.' AUXILIARY SUP 'top' MUST ( printer-name ) MAY ( printer-aliases))
4039
4040dn: cn=schema
4041changetype: modify
4042add: objectclasses
4043objectclasses: ( 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))
4044
4045dn: cn=schema
4046changetype: modify
4047add: objectclasses
4048objectclasses:	( 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 ) )
4049
4050dn: cn=schema
4051changetype: modify
4052add: objectclasses
4053objectclasses:  ( 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 ) )
4054
4055dn: cn=schema
4056changetype: modify
4057add: objectclasses
4058objectclasses:	( 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 ) )
4059EOF
4060) > ${TMPDIR}/schema_obj
4061
4062    # Add the entry.
4063    ${EVAL} "${LDAPMODIFY} ${LDAP_ARGS} -f ${TMPDIR}/schema_obj ${VERB}"
4064    if [ $? -ne 0 ]; then
4065	${ECHO} "  ERROR: update of schema objectclass definitions failed!"
4066	cleanup
4067	exit 1
4068    fi
4069
4070    # Display message that schema is updated.
4071    ${ECHO} "  ${STEP}. Schema objectclass definitions have been added."
4072    STEP=`expr $STEP + 1`
4073}
4074
4075
4076#
4077# modify_top_aci(): Modify the ACI for the top entry to disable self modify
4078#                   of user attributes.
4079#
4080modify_top_aci()
4081{
4082    [ $DEBUG -eq 1 ] && ${ECHO} "In modify_top_aci()"
4083
4084    # Set ACI Name
4085    ACI_NAME="LDAP_Naming_Services_deny_write_access"
4086
4087    # Search for ACI_NAME
4088    eval "${LDAPSEARCH} ${LDAP_ARGS} -b \"${LDAP_BASEDN}\" -s base objectclass=* aci > ${TMPDIR}/chk_top_aci 2>&1"
4089    if [ $? -ne 0 ]; then
4090	${ECHO} "Error searching aci for ${LDAP_BASEDN}"
4091	cat ${TMPDIR}/chk_top_aci
4092	cleanup
4093	exit 1
4094    fi
4095    ${GREP} "${ACI_NAME}" ${TMPDIR}/chk_top_aci > /dev/null 2>&1
4096    if [ $? -eq 0 ]; then
4097	${ECHO} "  ${STEP}. Top level ACI ${ACI_NAME} already exists for ${LDAP_BASEDN}."
4098	STEP=`expr $STEP + 1`
4099	return 0
4100    fi
4101
4102    # Crate LDIF for top level ACI.
4103    ( cat <<EOF
4104dn: ${LDAP_BASEDN}
4105changetype: modify
4106add: aci
4107aci: (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";)
4108-
4109EOF
4110) > ${TMPDIR}/top_aci
4111
4112    # Add the entry.
4113    ${EVAL} "${LDAPMODIFY} ${LDAP_ARGS} -f ${TMPDIR}/top_aci ${VERB}"
4114    if [ $? -ne 0 ]; then
4115	${ECHO} "  ERROR: Modify of top level ACI failed! (restricts self modify)"
4116	cleanup
4117	exit 1
4118    fi
4119
4120    # Display message that schema is updated.
4121    ${ECHO} "  ${STEP}. ACI for ${LDAP_BASEDN} modified to disable self modify."
4122    STEP=`expr $STEP + 1`
4123}
4124
4125
4126#
4127# add_vlv_aci(): Add access control information (aci) for VLV.
4128#
4129add_vlv_aci()
4130{
4131    [ $DEBUG -eq 1 ] && ${ECHO} "In add_vlv_aci()"
4132
4133    # Add the VLV ACI.
4134    ( cat <<EOF
4135dn: oid=2.16.840.1.113730.3.4.9,cn=features,cn=config
4136changetype: modify
4137replace: aci
4138aci: (targetattr != "aci") (version 3.0; acl "VLV Request Control"; allow(read,search,compare) userdn = "ldap:///anyone";)
4139EOF
4140) > ${TMPDIR}/vlv_aci
4141
4142    # Add the entry.
4143    ${EVAL} "${LDAPMODIFY} ${LDAP_ARGS} -f ${TMPDIR}/vlv_aci ${VERB}"
4144    if [ $? -ne 0 ]; then
4145	${ECHO} "  ERROR: Add of VLV ACI failed!"
4146	cleanup
4147	exit 1
4148    fi
4149
4150    # Display message that schema is updated.
4151    ${ECHO} "  ${STEP}. Add of VLV Access Control Information (ACI)."
4152    STEP=`expr $STEP + 1`
4153}
4154
4155
4156#
4157# set_nisdomain(): Add the NisDomainObject to the Base DN.
4158#
4159set_nisdomain()
4160{
4161    [ $DEBUG -eq 1 ] && ${ECHO} "In set_nisdomain()"
4162
4163    # Check if nisDomain is already set.
4164    ${EVAL} "${LDAPSEARCH} ${LDAP_ARGS} -b \"${LDAP_BASEDN}\" -s base \
4165	\"objectclass=*\"" > ${TMPDIR}/chk_nisdomain 2>&1
4166    ${EVAL} "${GREP} -i nisDomain ${TMPDIR}/chk_nisdomain ${VERB}"
4167    if [ $? -eq 0 ]; then
4168	${ECHO} "  ${STEP}. NisDomainObject for ${LDAP_BASEDN} was already set."
4169	STEP=`expr $STEP + 1`
4170	return 0
4171    fi
4172
4173    # Add the new top level containers.
4174    ( cat <<EOF
4175dn: ${LDAP_BASEDN}
4176changetype: modify
4177objectclass: nisDomainObject
4178nisdomain: ${LDAP_DOMAIN}
4179EOF
4180) > ${TMPDIR}/nis_domain
4181
4182    # Add the entry.
4183    ${EVAL} "${LDAPMODIFY} ${LDAP_ARGS} -f ${TMPDIR}/nis_domain ${VERB}"
4184    if [ $? -ne 0 ]; then
4185	${ECHO} "  ERROR: update of NisDomainObject in ${LDAP_BASEDN} failed."
4186	cleanup
4187	exit 1
4188    fi
4189
4190    # Display message that schema is updated.
4191    ${ECHO} "  ${STEP}. NisDomainObject added to ${LDAP_BASEDN}."
4192    STEP=`expr $STEP + 1`
4193}
4194
4195
4196#
4197# check_attrName(): Check that the attribute name is valid.
4198#              $1   Key to check.
4199#         Returns   0 : valid name	1 : invalid name
4200#
4201check_attrName()
4202{
4203    [ $DEBUG -eq 1 ] && ${ECHO} "In check_attrName()"
4204    [ $DEBUG -eq 1 ] && ${ECHO} "check_attrName: Input Param = $1"
4205
4206    ${ECHO} $1 | ${EGREP} '^[0-9]+(\.[0-9]+)*$' > /dev/null 2>&1
4207    if [ $? -eq 0 ]; then
4208	${EVAL} "${LDAPSEARCH} ${SERVER_ARGS} -b cn=schema -s base \"objectclass=*\" \
4209			attributeTypes | ${EGREP} -i '^attributetypes[ ]*=[ ]*\([ ]*$1 ' ${VERB}"
4210    else
4211	${EVAL} "${LDAPSEARCH} ${SERVER_ARGS} -b cn=schema -s base \"objectclass=*\" \
4212			attributeTypes | ${EGREP} -i \"'$1'\" ${VERB}"
4213    fi
4214
4215    if [ $? -ne 0 ]; then
4216	return 1
4217    else
4218	return 0
4219    fi
4220}
4221
4222
4223#
4224# get_objectclass():   Determine the objectclass for the given attribute name
4225#              $1   Attribute name to check.
4226#      _ATTR_NAME   Return value, Object Name or NULL if unknown to idsconfig.
4227#
4228#      NOTE: An attribute name can be valid but still we might not be able
4229#            to determine the objectclass from the table.
4230#            In such cases, the user needs to create the necessary object(s).
4231#
4232get_objectclass()
4233{
4234    [ $DEBUG -eq 1 ] && ${ECHO} "In get_objectclass()"
4235    [ $DEBUG -eq 1 ] && ${ECHO} "get_objectclass: Input Param = $1"
4236
4237    # Set return value to NULL string.
4238    _ATTR_NAME=""
4239
4240    # Test key for type:
4241    case `${ECHO} ${1} | tr '[A-Z]' '[a-z]'` in
4242	ou | organizationalunitname | 2.5.4.11) _ATTR_NAME="organizationalUnit" ;;
4243	dc | domaincomponent | 0.9.2342.19200300.100.1.25) _ATTR_NAME="domain" ;;
4244	 o | organizationname | 2.5.4.10) _ATTR_NAME="organization" ;;
4245	 c | countryname | 2.5.4.6) _ATTR_NAME="country" ;;
4246	 *)  _ATTR_NAME="" ;;
4247    esac
4248
4249    [ $DEBUG -eq 1 ] && ${ECHO} "get_objectclass: _ATTR_NAME = $_ATTR_NAME"
4250}
4251
4252
4253#
4254# add_base_objects(): Add any necessary base objects.
4255#
4256add_base_objects()
4257{
4258    [ $DEBUG -eq 1 ] && ${ECHO} "In add_base_objects()"
4259
4260    # Convert to lower case for basename.
4261    format_string "${LDAP_BASEDN}"
4262    LOWER_BASEDN="${FMT_STR}"
4263    format_string "${LDAP_SUFFIX}"
4264    LOWER_SUFFIX="${FMT_STR}"
4265
4266    [ $DEBUG -eq 1 ] && ${ECHO} "LOWER_BASEDN: ${LOWER_BASEDN}"
4267    [ $DEBUG -eq 1 ] && ${ECHO} "LOWER_SUFFIX: ${LOWER_SUFFIX}"
4268
4269    # Create additional components.
4270    if [ "${LOWER_BASEDN}" = "${LOWER_SUFFIX}" ]; then
4271	[ $DEBUG -eq 1 ] && ${ECHO} "Base DN and Suffix equivalent"
4272    else
4273	# first, test that the suffix is valid
4274	dcstmp=`basename "${LOWER_BASEDN}" "${LOWER_SUFFIX}"`
4275	if [ "$dcstmp" = "${LOWER_BASEDN}" ]; then
4276	    # should not happen since check_basedn_suffix() succeeded
4277	    ${ECHO} "Invalid suffix ${LOWER_SUFFIX}"
4278	    ${ECHO} "for Base DN ${LOWER_BASEDN}"
4279	    cleanup
4280	    exit 1
4281	fi
4282	# OK, suffix is valid, start working with LDAP_BASEDN
4283	# field separator is ',' (i.e., space is a valid character)
4284	dcstmp2="`${ECHO} ${LDAP_BASEDN} |
4285		sed -e 's/[ ]*,[ ]*/,/g' -e 's/[ ]*=[ ]*/=/g'`"
4286	dcs=""
4287	# use dcstmp to count the loop, and dcstmp2 to get the correct
4288	# string case
4289	# dcs should be in reverse order, only for these components
4290	# that need to be added
4291	while [ -n "${dcstmp}" ]
4292	do
4293	    i2=`${ECHO} "$dcstmp2" | cut -f1 -d','`
4294	    dk=`${ECHO} $i2 | awk -F= '{print $1}'`
4295	    dc=`${ECHO} $i2 | awk -F= '{print $2}'`
4296	    dcs="$dk=$dc,$dcs";
4297	    dcstmp2=`${ECHO} "$dcstmp2" | cut -f2- -d','`
4298	    dcstmp=`${ECHO} "$dcstmp" | cut -f2- -d','`
4299	    [ $DEBUG -eq 1 ] && \
4300		${ECHO} "dcs: ${dcs}\ndcstmp: ${dcstmp}\ndcstmp2: ${dcstmp2}\n"
4301	done
4302
4303
4304
4305	lastdc=${LDAP_SUFFIX}
4306	dc=`${ECHO} "${dcs}" | cut -f1 -d','`
4307	dcstmp=`${ECHO} "${dcs}" | cut -f2- -d','`
4308	while [ -n "${dc}" ]; do
4309	    # Get Key and component from $dc.
4310	    dk2=`${ECHO} $dc | awk -F= '{print $1}'`
4311	    dc2=`${ECHO} $dc | awk -F= '{print $2}'`
4312
4313	    # At this point, ${dk2} is a valid attribute name
4314
4315	    # Check if entry exists first, if so, skip to next.
4316	    ${LDAPSEARCH} ${SERVER_ARGS} -b "${dk2}=${dc2},$lastdc" -s base "objectclass=*" > /dev/null 2>&1
4317	    if [ $? -eq 0 ]; then
4318	        # Set the $lastdc to new dc.
4319	        lastdc="${dk2}=${dc2},$lastdc"
4320
4321		# Process next component.
4322		dc=`${ECHO} "${dcstmp}" | cut -f1 -d','`
4323		dcstmp=`${ECHO} "${dcstmp}" | cut -f2- -d','`
4324		continue
4325
4326	    fi
4327
4328	    # Determine the objectclass for the entry.
4329            get_objectclass $dk2
4330	    OBJ_Name=${_ATTR_NAME}
4331	    if [ "${OBJ_Name}" = "" ]; then
4332	        ${ECHO} "Cannot determine objectclass for $dk2"
4333	        ${ECHO} "Please create ${dk2}=${dc2},$lastdc entry and rerun idsconfig"
4334	        exit 1
4335	    fi
4336
4337	    # Add the new container.
4338	    ( cat <<EOF
4339dn: ${dk2}=${dc2},$lastdc
4340${dk2}: $dc2
4341objectClass: top
4342objectClass: ${OBJ_Name}
4343EOF
4344) > ${TMPDIR}/base_objects
4345
4346
4347	    # Set the $lastdc to new dc.
4348	    lastdc="${dk2}=${dc2},$lastdc"
4349
4350	    # Add the entry.
4351	    ${EVAL} "${LDAPMODIFY} -a ${LDAP_ARGS} -f ${TMPDIR}/base_objects ${VERB}"
4352	    if [ $? -ne 0 ]; then
4353		${ECHO} "  ERROR: update of base objects ${dc} failed."
4354		cleanup
4355		exit 1
4356	    fi
4357
4358	    # Display message that schema is updated.
4359	    ${ECHO} "  ${STEP}. Created DN component ${dc}."
4360	    STEP=`expr $STEP + 1`
4361
4362	    # Process next component.
4363	    dc=`${ECHO} "${dcstmp}" | cut -f1 -d','`
4364	    dcstmp=`${ECHO} "${dcstmp}" | cut -f2- -d','`
4365	done
4366    fi
4367}
4368
4369
4370#
4371# add_new_containers(): Add the top level classes.
4372#
4373#    $1 = Base DN
4374#
4375add_new_containers()
4376{
4377    [ $DEBUG -eq 1 ] && ${ECHO} "In add_new_containers()"
4378
4379    for ou in people group rpc protocols networks netgroup \
4380	aliases hosts services ethers profile printers projects \
4381	SolarisAuthAttr SolarisProfAttr Timezone ipTnet ; do
4382
4383	# Check if nismaps already exist.
4384	eval "${LDAPSEARCH} ${LDAP_ARGS} -b \"ou=${ou},${LDAP_BASEDN}\" -s base \"objectclass=*\" ${VERB}"
4385	if [ $? -eq 0 ]; then
4386	    continue
4387	fi
4388
4389	# Create TMP file to add.
4390	( cat <<EOF
4391dn: ou=${ou},${LDAP_BASEDN}
4392ou: ${ou}
4393objectClass: top
4394objectClass: organizationalUnit
4395EOF
4396) > ${TMPDIR}/toplevel.${ou}
4397
4398	# Add the entry.
4399	${EVAL} "${LDAPMODIFY} -a ${LDAP_ARGS} -f ${TMPDIR}/toplevel.${ou} ${VERB}"
4400	if [ $? -ne 0 ]; then
4401	    ${ECHO} "  ERROR: Add of ou=${ou} container failed!"
4402	    cleanup
4403	    exit 1
4404	fi
4405    done
4406
4407    # Display message that top level OU containers complete.
4408    ${ECHO} "  ${STEP}. Top level \"ou\" containers complete."
4409    STEP=`expr $STEP + 1`
4410}
4411
4412
4413#
4414# add_auto_maps(): Add the automount map entries.
4415#
4416# auto_home, auto_direct, auto_master, auto_shared
4417#
4418add_auto_maps()
4419{
4420    [ $DEBUG -eq 1 ] && ${ECHO} "In add_auto_maps()"
4421
4422    # Set AUTO_MAPS for maps to create.
4423    AUTO_MAPS="auto_home auto_direct auto_master auto_shared"
4424
4425    for automap in $AUTO_MAPS; do
4426	# Check if automaps already exist.
4427	eval "${LDAPSEARCH} ${LDAP_ARGS} -b \"automountMapName=${automap},${LDAP_BASEDN}\" -s base \"objectclass=*\" ${VERB}"
4428	if [ $? -eq 0 ]; then
4429	    continue
4430	fi
4431
4432	# Create the tmp file to add.
4433	( cat <<EOF
4434dn: automountMapName=${automap},${LDAP_BASEDN}
4435automountMapName: ${automap}
4436objectClass: top
4437objectClass: automountMap
4438EOF
4439) > ${TMPDIR}/automap.${automap}
4440
4441	# Add the entry.
4442	${EVAL} "${LDAPMODIFY} -a ${LDAP_ARGS} -f ${TMPDIR}/automap.${automap} ${VERB}"
4443	if [ $? -ne 0 ]; then
4444	    ${ECHO} "  ERROR: Add of automap ${automap} failed!"
4445	    cleanup
4446	    exit 1
4447	fi
4448    done
4449
4450    # Display message that automount entries are updated.
4451    ${ECHO} "  ${STEP}. automount maps: $AUTO_MAPS processed."
4452    STEP=`expr $STEP + 1`
4453}
4454
4455
4456#
4457# add_proxyagent(): Add entry for nameservice to use to access server.
4458#
4459add_proxyagent()
4460{
4461    [ $DEBUG -eq 1 ] && ${ECHO} "In add_proxyagent()"
4462
4463    # Check if nismaps already exist.
4464    eval "${LDAPSEARCH} ${LDAP_ARGS} -b \"${LDAP_PROXYAGENT}\" -s base \"objectclass=*\" ${VERB}"
4465    if [ $? -eq 0 ]; then
4466	${ECHO} "  ${STEP}. Proxy Agent ${LDAP_PROXYAGENT} already exists."
4467	STEP=`expr $STEP + 1`
4468	return 0
4469    fi
4470
4471    # Get cn and sn names from LDAP_PROXYAGENT.
4472    cn_tmp=`${ECHO} ${LDAP_PROXYAGENT} | cut -f1 -d, | cut -f2 -d=`
4473
4474    # Create the tmp file to add.
4475    ( cat <<EOF
4476dn: ${LDAP_PROXYAGENT}
4477cn: ${cn_tmp}
4478sn: ${cn_tmp}
4479objectclass: top
4480objectclass: person
4481userpassword: ${LDAP_PROXYAGENT_CRED}
4482EOF
4483) > ${TMPDIR}/proxyagent
4484
4485    # Add the entry.
4486    ${EVAL} "${LDAPMODIFY} -a ${LDAP_ARGS} -f ${TMPDIR}/proxyagent ${VERB}"
4487    if [ $? -ne 0 ]; then
4488	${ECHO} "  ERROR: Adding proxyagent failed!"
4489	cleanup
4490	exit 1
4491    fi
4492
4493    # Display message that schema is updated.
4494    ${ECHO} "  ${STEP}. Proxy Agent ${LDAP_PROXYAGENT} added."
4495    STEP=`expr $STEP + 1`
4496}
4497
4498
4499#
4500# allow_proxy_read_pw(): Give Proxy Agent read permission for password.
4501#
4502allow_proxy_read_pw()
4503{
4504    [ $DEBUG -eq 1 ] && ${ECHO} "In allow_proxy_read_pw()"
4505
4506    # Set ACI Name
4507    PROXY_ACI_NAME="LDAP_Naming_Services_proxy_password_read"
4508
4509    # Search for ACI_NAME
4510    eval "${LDAPSEARCH} ${LDAP_ARGS} -b \"${LDAP_BASEDN}\" -s base objectclass=* aci > ${TMPDIR}/chk_proxyread_aci 2>&1"
4511    ${GREP} "${PROXY_ACI_NAME}" ${TMPDIR}/chk_proxyread_aci > /dev/null 2>&1
4512    if [ $? -eq 0 ]; then
4513	${ECHO} "  ${STEP}. Proxy ACI ${PROXY_ACI_NAME=} already exists for ${LDAP_BASEDN}."
4514	STEP=`expr $STEP + 1`
4515	return 0
4516    fi
4517
4518    # Create the tmp file to add.
4519    ( cat <<EOF
4520dn: ${LDAP_BASEDN}
4521changetype: modify
4522add: aci
4523aci: (target="ldap:///${LDAP_BASEDN}")(targetattr="userPassword")(version 3.0; acl ${PROXY_ACI_NAME}; allow (compare,read,search) userdn = "ldap:///${LDAP_PROXYAGENT}";)
4524EOF
4525) > ${TMPDIR}/proxy_read
4526
4527    # Add the entry.
4528    ${EVAL} "${LDAPMODIFY} ${LDAP_ARGS} -f ${TMPDIR}/proxy_read ${VERB}"
4529    if [ $? -ne 0 ]; then
4530	${ECHO} "  ERROR: Allow ${LDAP_PROXYAGENT} to read password failed!"
4531	cleanup
4532	exit 1
4533    fi
4534
4535    # Display message that schema is updated.
4536    ${ECHO} "  ${STEP}. Give ${LDAP_PROXYAGENT} read permission for password."
4537    STEP=`expr $STEP + 1`
4538}
4539
4540
4541#
4542# add_profile(): Add client profile to server.
4543#
4544add_profile()
4545{
4546    [ $DEBUG -eq 1 ] && ${ECHO} "In add_profile()"
4547
4548    # If profile name already exists, DELETE it, and add new one.
4549    eval "${LDAPSEARCH} ${LDAP_ARGS} -b \"cn=${LDAP_PROFILE_NAME},ou=profile,${LDAP_BASEDN}\" -s base \"objectclass=*\" ${VERB}"
4550    if [ $? -eq 0 ]; then
4551	# Create Delete file.
4552	( cat <<EOF
4553cn=${LDAP_PROFILE_NAME},ou=profile,${LDAP_BASEDN}
4554EOF
4555) > ${TMPDIR}/del_profile
4556
4557	# Check if DEL_OLD_PROFILE is set.  (If not ERROR)
4558	if [ $DEL_OLD_PROFILE -eq 0 ]; then
4559	    ${ECHO} "ERROR: Profile name ${LDAP_PROFILE_NAME} exists! Add failed!"
4560	    exit 1
4561	fi
4562
4563	# Delete the OLD profile.
4564	${EVAL} "${LDAPDELETE} ${LDAP_ARGS} -f ${TMPDIR}/del_profile ${VERB}"
4565	if [ $? -ne 0 ]; then
4566	    ${ECHO} "  ERROR: Attempt to DELETE profile failed!"
4567	    cleanup
4568	    exit 1
4569	fi
4570    fi
4571
4572    # Build the "ldapclient genprofile" command string to execute.
4573    GEN_CMD="ldapclient genprofile -a \"profileName=${LDAP_PROFILE_NAME}\""
4574
4575    # Add required argument defaultSearchBase.
4576    GEN_CMD="${GEN_CMD} -a \"defaultSearchBase=${LDAP_BASEDN}\""
4577
4578    # Add optional parameters.
4579    [ -n "$LDAP_SERVER_LIST" ] && \
4580	GEN_CMD="${GEN_CMD} -a \"defaultServerList=${LDAP_SERVER_LIST}\""
4581    [ -n "$LDAP_SEARCH_SCOPE" ] && \
4582	GEN_CMD="${GEN_CMD} -a \"defaultSearchScope=${LDAP_SEARCH_SCOPE}\""
4583    [ -n "$LDAP_CRED_LEVEL" ] && \
4584	GEN_CMD="${GEN_CMD} -a \"credentialLevel=${LDAP_CRED_LEVEL}\""
4585    [ -n "$LDAP_AUTHMETHOD" ] && \
4586	GEN_CMD="${GEN_CMD} -a \"authenticationMethod=${LDAP_AUTHMETHOD}\""
4587    [ -n "$LDAP_FOLLOWREF" ] && \
4588	GEN_CMD="${GEN_CMD} -a \"followReferrals=${LDAP_FOLLOWREF}\""
4589    [ -n "$LDAP_SEARCH_TIME_LIMIT" ] && \
4590	GEN_CMD="${GEN_CMD} -a \"searchTimeLimit=${LDAP_SEARCH_TIME_LIMIT}\""
4591    [ -n "$LDAP_PROFILE_TTL" ] && \
4592	GEN_CMD="${GEN_CMD} -a \"profileTTL=${LDAP_PROFILE_TTL}\""
4593    [ -n "$LDAP_BIND_LIMIT" ] && \
4594	GEN_CMD="${GEN_CMD} -a \"bindTimeLimit=${LDAP_BIND_LIMIT}\""
4595    [ -n "$LDAP_PREF_SRVLIST" ] && \
4596	GEN_CMD="${GEN_CMD} -a \"preferredServerList=${LDAP_PREF_SRVLIST}\""
4597    [ -n "$LDAP_SRV_AUTHMETHOD_PAM" ] && \
4598	GEN_CMD="${GEN_CMD} -a \"serviceAuthenticationMethod=${LDAP_SRV_AUTHMETHOD_PAM}\""
4599    [ -n "$LDAP_SRV_AUTHMETHOD_KEY" ] && \
4600	GEN_CMD="${GEN_CMD} -a \"serviceAuthenticationMethod=${LDAP_SRV_AUTHMETHOD_KEY}\""
4601    [ -n "$LDAP_SRV_AUTHMETHOD_CMD" ] && \
4602	GEN_CMD="${GEN_CMD} -a \"serviceAuthenticationMethod=${LDAP_SRV_AUTHMETHOD_CMD}\""
4603
4604    # Check if there are any service search descriptors to ad.
4605    if [ -s "${SSD_FILE}" ]; then
4606	ssd_2_profile
4607    fi
4608
4609    # Execute "ldapclient genprofile" to create profile.
4610    eval ${GEN_CMD} > ${TMPDIR}/gen_profile 2> ${TMPDIR}/gen_profile_ERR
4611    if [ $? -ne 0 ]; then
4612	${ECHO} "  ERROR: ldapclient genprofile failed!"
4613	cleanup
4614	exit 1
4615    fi
4616
4617    # Add the generated profile..
4618    ${EVAL} "${LDAPMODIFY} -a ${LDAP_ARGS} -f ${TMPDIR}/gen_profile ${VERB}"
4619    if [ $? -ne 0 ]; then
4620	${ECHO} "  ERROR: Attempt to add profile failed!"
4621	cleanup
4622	exit 1
4623    fi
4624
4625    # Display message that schema is updated.
4626    ${ECHO} "  ${STEP}. Generated client profile and loaded on server."
4627    STEP=`expr $STEP + 1`
4628}
4629
4630
4631#
4632# cleanup(): Remove the TMPDIR and all files in it.
4633#
4634cleanup()
4635{
4636    [ $DEBUG -eq 1 ] && ${ECHO} "In cleanup()"
4637
4638    rm -fr ${TMPDIR}
4639}
4640
4641
4642#
4643# 			* * * MAIN * * *
4644#
4645# Description:
4646# This script assumes that the iPlanet Directory Server (iDS) is
4647# installed and that setup has been run.  This script takes the
4648# iDS server from that point and sets up the infrastructure for
4649# LDAP Naming Services.  After running this script, ldapaddent(1M)
4650# or some other tools can be used to populate data.
4651
4652# Initialize the variables that need to be set to NULL, or some
4653# other initial value before the rest of the functions can be called.
4654init
4655
4656# Parse command line arguments.
4657parse_arg $*
4658shift $?
4659
4660# Print extra line to separate from prompt.
4661${ECHO} " "
4662
4663# Either Load the user specified config file
4664# or prompt user for config info.
4665if [ -n "$INPUT_FILE" ]
4666then
4667    load_config_file
4668    INTERACTIVE=0      # Turns off prompts that occur later.
4669    validate_info      # Validate basic info in file.
4670    chk_ids_version    # Check iDS version for compatibility.
4671    gssapi_setup_auto
4672else
4673    # Display BACKUP warning to user.
4674    display_msg backup_server
4675    get_confirm "Do you wish to continue with server setup (y/n/h)?" "n" "backup_help"
4676    if [ $? -eq 0 ]; then    # if No, cleanup and exit.
4677	cleanup ; exit 1
4678    fi
4679
4680    # Prompt for values.
4681    prompt_config_info
4682    display_summary    # Allow user to modify results.
4683    INTERACTIVE=1      # Insures future prompting.
4684fi
4685
4686# Modify slapd.oc.conf to ALLOW cn instead of REQUIRE.
4687modify_cn
4688
4689# Modify timelimit to user value.
4690[ $NEED_TIME -eq 1 ] && modify_timelimit
4691
4692# Modify sizelimit to user value.
4693[ $NEED_SIZE -eq 1 ] && modify_sizelimit
4694
4695# Modify the password storage scheme to support CRYPT.
4696if [ "$NEED_CRYPT" = "TRUE" ]; then
4697    modify_pwd_crypt
4698fi
4699
4700# Update the schema (Attributes, Objectclass Definitions)
4701if [ ${SCHEMA_UPDATED} -eq 0 ]; then
4702        update_schema_attr
4703        update_schema_obj
4704fi
4705
4706# Add suffix together with its root entry (if needed)
4707add_suffix ||
4708{
4709	cleanup
4710	exit 1
4711}
4712
4713# Add base objects (if needed)
4714add_base_objects
4715
4716# Update the NisDomainObject.
4717#   The Base DN might of just been created, so this MUST happen after
4718#   the base objects have been added!
4719set_nisdomain
4720
4721# Add top level classes (new containers)
4722add_new_containers
4723
4724# Add common nismaps.
4725add_auto_maps
4726
4727# Modify top ACI.
4728modify_top_aci
4729
4730# Add Access Control Information for VLV.
4731add_vlv_aci
4732
4733# if Proxy needed, Add Proxy Agent and give read permission for password.
4734if [ $NEED_PROXY -eq 1 ]; then
4735    add_proxyagent
4736    allow_proxy_read_pw
4737fi
4738
4739# Generate client profile and add it to the server.
4740add_profile
4741
4742# Add Indexes to improve Search Performance.
4743add_eq_indexes
4744add_sub_indexes
4745add_vlv_indexes
4746
4747# Display setup complete message
4748display_msg setup_complete
4749
4750# Display VLV index commands to be executed on server.
4751display_vlv_cmds
4752
4753# Create config file if requested.
4754[ -n "$OUTPUT_FILE" ] && create_config_file
4755
4756# Removed the TMPDIR and all files in it.
4757cleanup
4758
4759exit 0
4760# end of MAIN.
4761