changeset 8821:7eb613e55044

PSARC 2008/745 nss_ldap shadowAccount support 6715171 nss_ldap and passwdutil do not support all shadowAccount attributes 6797378 'ldapaddent -d passwd' does not print 'x' for the password field 6783712 libsldap fails to set correct version number for V1 profile
author Michen Chang <Michen.Chang@Sun.COM>
date Fri, 13 Feb 2009 18:18:56 -0800
parents b666ace44e61
children 486d6b3deddc
files usr/src/cmd/ldap/ns_ldap/idsconfig.sh usr/src/cmd/ldap/ns_ldap/ldapaddent.c usr/src/cmd/ldap/ns_ldap/ldapclient.c usr/src/cmd/ldapcachemgr/cachemgr.c usr/src/cmd/passwd/passwd.c usr/src/lib/libsldap/common/mapfile-vers usr/src/lib/libsldap/common/ns_cache_door.h usr/src/lib/libsldap/common/ns_config.c usr/src/lib/libsldap/common/ns_confmgr.c usr/src/lib/libsldap/common/ns_internal.h usr/src/lib/libsldap/common/ns_sldap.h usr/src/lib/libsldap/common/ns_writes.c usr/src/lib/nsswitch/ldap/common/getspent.c usr/src/lib/passwdutil/__failed_count.c usr/src/lib/passwdutil/ldap_attr.c usr/src/lib/passwdutil/passwdutil.h
diffstat 16 files changed, 2567 insertions(+), 396 deletions(-) [+]
line wrap: on
line diff
--- a/usr/src/cmd/ldap/ns_ldap/idsconfig.sh	Fri Feb 13 18:28:50 2009 -0800
+++ b/usr/src/cmd/ldap/ns_ldap/idsconfig.sh	Fri Feb 13 18:18:56 2009 -0800
@@ -1,7 +1,5 @@
 #!/bin/sh
 #
-# ident	"%Z%%M%	%I%	%E% SMI"
-#
 # CDDL HEADER START
 #
 # The contents of this file are subject to the terms of the
@@ -24,7 +22,7 @@
 #
 # idsconfig -- script to setup iDS 5.x/6.x for Native LDAP II.
 #
-# Copyright 2008 Sun Microsystems, Inc.  All rights reserved.
+# Copyright 2009 Sun Microsystems, Inc.  All rights reserved.
 # Use is subject to license terms.
 #
 
@@ -150,7 +148,8 @@
  16  Search Time Limit             : $LDAP_SEARCH_TIME_LIMIT
  17  Profile Time to Live          : $LDAP_PROFILE_TTL
  18  Bind Limit                    : $LDAP_BIND_LIMIT
- 19  Service Search Descriptors Menu
+ 19  Enable shadow update          : $LDAP_ENABLE_SHADOW_UPDATE
+ 20  Service Search Descriptors Menu
 
 EOF
     ;;
@@ -471,6 +470,43 @@
 
 EOF
     ;;
+    enable_shadow_update_help) cat <<EOF
+
+HELP - Enter 'y' to set up the LDAP server for shadow update.
+       The setup will add an administrator identity/credential
+       and modify the necessary access controls for the client
+       to update shadow(4) data on the LDAP server. If sasl/GSSAPI
+       is in use, the Kerberos host principal will be used as the
+       administrator identity.
+
+       Shadow data is used for password aging and account locking.
+       Please refer to the shadow(4) manual page for details.
+
+EOF
+    ;;
+    add_admin_cred_help) cat <<EOF
+
+HELP - Start the setup to add an administrator identity/credential
+       and to modify access controls for the client to update
+       shadow(4) data on the LDAP server.
+
+       Shadow data is used for password aging and account locking.
+       Please refer to the shadow(4) manual page for details.
+
+EOF
+    ;;
+    use_host_principal_help) cat <<EOF
+
+HELP - A profile with a 'sasl/GSSAPI' authentication method and a 'self'
+       credential level is detected, enter 'y' to modify the necessary
+       access controls for allowing the client to update shadow(4) data
+       on the LDAP server.
+
+       Shadow data is used for password aging and account locking.
+       Please refer to the shadow(4) manual page for details.
+
+EOF
+    ;;
     esac
 }
 
@@ -942,6 +978,7 @@
     BACKUP=no_ldap	# backup suffix
     HOST=""		# NULL or <hostname>
     NAWK="/usr/bin/nawk"
+    RM="/usr/bin/rm"
 
     DOM=""              # Set to NULL
     # If DNS domain (resolv.conf) exists use that, otherwise use domainname.
@@ -963,8 +1000,13 @@
     # idsconfig specific variables.
     INPUT_FILE=""
     OUTPUT_FILE=""
-    NEED_PROXY=0        # 0 = No Proxy, 1 = Create Proxy.
+    LDAP_ENABLE_SHADOW_UPDATE="FALSE"
+    NEED_PROXY=0        # 0 = No Proxy,    1 = Create Proxy.
+    NEED_ADMIN=0        # 0 = No Admin,    1 = Create Admin.
+    NEED_HOSTACL=0      # 0 = No Host ACL, 1 = Create Host ACL.
+    EXISTING_PROFILE=0
     LDAP_PROXYAGENT=""
+    LDAP_ADMINDN=""
     LDAP_SUFFIX=""
     LDAP_DOMAIN=$DOM	# domainname on Server (default value)
     GEN_CMD=""
@@ -1037,6 +1079,8 @@
     export LDAP_BASEDN LDAP_ROOTPWF
     export LDAP_DOMAIN LDAP_SUFFIX LDAP_PROXYAGENT LDAP_PROXYAGENT_CRED
     export NEED_PROXY
+    export LDAP_ENABLE_SHADOW_UPDATE LDAP_ADMINDN LDAP_ADMIN_CRED
+    export NEED_ADMIN NEED_HOSTACL EXISTING_PROFILE
     export LDAP_PROFILE_NAME LDAP_BASEDN LDAP_SERVER_LIST 
     export LDAP_AUTHMETHOD LDAP_FOLLOWREF LDAP_SEARCH_SCOPE LDAP_SEARCH_TIME_LIMIT
     export LDAP_PREF_SRVLIST LDAP_PROFILE_TTL LDAP_CRED_LEVEL LDAP_BIND_LIMIT
@@ -1079,12 +1123,21 @@
     [ $DEBUG -eq 1 ] && ${ECHO} "  LDAP_SEARCH_TIME_LIMIT = $LDAP_SEARCH_TIME_LIMIT"
     [ $DEBUG -eq 1 ] && ${ECHO} "  LDAP_PROFILE_TTL = $LDAP_PROFILE_TTL"
     [ $DEBUG -eq 1 ] && ${ECHO} "  LDAP_BIND_LIMIT = $LDAP_BIND_LIMIT"
+    [ $DEBUG -eq 1 ] && ${ECHO} "  LDAP_ENABLE_SHADOW_UPDATE = $LDAP_ENABLE_SHADOW_UPDATE"
 
     # Only display proxy stuff if needed.
+    [ $DEBUG -eq 1 ] && ${ECHO} "  NEED_PROXY = $NEED_PROXY"
     if [ $NEED_PROXY -eq  1 ]; then
 	[ $DEBUG -eq 1 ] && ${ECHO} "  LDAP_PROXYAGENT = $LDAP_PROXYAGENT"
 	[ $DEBUG -eq 1 ] && ${ECHO} "  LDAP_PROXYAGENT_CRED = $LDAP_PROXYAGENT_CRED"
-	[ $DEBUG -eq 1 ] && ${ECHO} "  NEED_PROXY = $NEED_PROXY"
+    fi
+
+    # Only display admin credential if needed.
+    [ $DEBUG -eq 1 ] && ${ECHO} "  NEED_ADMIN = $NEED_ADMIN"
+    [ $DEBUG -eq 1 ] && ${ECHO} "  NEED_HOSTACL = $NEED_HOSTACL"
+    if [ $NEED_ADMIN -eq  1 ]; then
+	[ $DEBUG -eq 1 ] && ${ECHO} "  LDAP_ADMINDN = $LDAP_ADMINDN"
+	[ $DEBUG -eq 1 ] && ${ECHO} "  LDAP_ADMIN_CRED = $LDAP_ADMIN_CRED"
     fi
 
     # Service Search Descriptors are a special case.
@@ -1351,6 +1404,20 @@
     done
 }
 
+#
+# get_want_shadow_update(): Ask user if want to enable shadow update?
+#
+get_want_shadow_update()
+{
+    MSG="Do you want to enable shadow update (y/n/h)?"
+    get_confirm "$MSG" "n" "enable_shadow_update_help"
+    if [ $? -eq 1 ]; then
+	LDAP_ENABLE_SHADOW_UPDATE="TRUE"
+    else
+	LDAP_ENABLE_SHADOW_UPDATE="FALSE"
+    fi
+}
+
 get_krb_realm() {
 
     # To upper cases
@@ -1612,8 +1679,11 @@
 #
 gssapi_setup() {
 
+	# assume sasl/GSSAPI is supported by the ldap server and may be used
+	GSSAPI_AUTH_MAY_BE_USED=1
 	${EVAL} "${LDAPSEARCH} ${LDAP_ARGS} -b \"\" -s base \"objectclass=*\" supportedSASLMechanisms | ${GREP} GSSAPI ${VERB}"
 	if [ $? -ne 0 ]; then
+		GSSAPI_AUTH_MAY_BE_USED=0
 		${ECHO} "  sasl/GSSAPI is not supported by this LDAP server"
 		return
 	fi
@@ -1622,7 +1692,7 @@
 	if [ $? -eq 0 ]; then
 		${ECHO}
 		${ECHO} "GSSAPI is not set up."
-		${ECHO} "sasl/GSSAPI bind may not workif it's not set up before."
+		${ECHO} "sasl/GSSAPI bind may not work if it's not set up first."
 	else
 		get_krb_realm
 		add_id_mapping_rules
@@ -1647,6 +1717,7 @@
 
 }
 gssapi_setup_auto() {
+	GSSAPI_AUTH_MAY_BE_USED=0
 	${EVAL} "${LDAPSEARCH} ${LDAP_ARGS} -b \"\" -s base \"objectclass=*\" supportedSASLMechanisms | ${GREP} GSSAPI ${VERB}"
 	if [ $? -ne 0 ]; then
 		${ECHO}
@@ -1661,6 +1732,7 @@
 		${ECHO}
 		return
 	fi
+	GSSAPI_AUTH_MAY_BE_USED=1
 	if [ -z "${LDAP_GSSAPI_PROFILE}" ]; then
 		${ECHO}
 		${ECHO} "LDAP_GSSAPI_PROFILE is not set. Default is gssapi_${LDAP_KRB_REALM}"
@@ -1694,7 +1766,30 @@
 	# Search to see if profile name already exists.
 	eval "${LDAPSEARCH} ${LDAP_ARGS} -b \"cn=${ANS},ou=profile,${LDAP_BASEDN}\" -s base \"objectclass=*\" ${VERB}"
 	if [ $? -eq 0 ]; then
-	    get_confirm_nodef "Are you sure you want to overwire profile cn=${ANS}?"
+
+	    cat << EOF
+
+Profile '${ANS}' already exists, it is possible to enable
+shadow update now. idsconfig will exit after shadow update
+is enabled. You can also continue to overwrite the profile 
+or create a new one and be given the chance to enable
+shadow update later.
+
+EOF
+
+	    MSG="Just enable shadow update (y/n/h)?"
+	    get_confirm "$MSG" "n" "enable_shadow_update_help"
+	    if [ $? -eq 1 ]; then
+	        [ $DEBUG -eq 1 ] && ${ECHO} "set up shadow update"
+	        LDAP_ENABLE_SHADOW_UPDATE=TRUE
+		# display alternate messages
+		EXISTING_PROFILE=1
+	        # Set Profile Name.
+	        LDAP_PROFILE_NAME=$ANS
+	        return 0  # set up credentials for shadow update.
+	    fi
+
+	    get_confirm_nodef "Are you sure you want to overwrite profile cn=${ANS}?"
 	    if [ $? -eq 1 ]; then
 		DEL_OLD_PROFILE=1
 		return 0  # Replace old profile name.
@@ -2423,6 +2518,248 @@
     GEN_CMD="${GEN_CMD} `cat ${GEN_TMPFILE}`"
 }
 
+#
+# get_adminDN(): Get the admin DN.
+#
+get_adminDN()
+{
+    LDAP_ADMINDN="cn=admin,ou=profile,${LDAP_BASEDN}"  # default
+    get_ans "Enter DN for the administrator:" "$LDAP_ADMINDN"
+    LDAP_ADMINDN=$ANS
+    [ $DEBUG -eq 1 ] && ${ECHO} "LDAP_ADMINDN = $LDAP_ADMINDN"
+}
+
+#
+# get_admin_pw(): Get the admin passwd.
+#
+get_admin_pw()
+{
+    get_passwd "Enter passwd for the administrator:"
+    LDAP_ADMIN_CRED=$ANS
+    [ $DEBUG -eq 1 ] && ${ECHO} "LDAP_ADMIN_CRED = $LDAP_ADMIN_CRED"
+}
+
+#
+# add_admin(): Add an admin entry for nameservice for updating shadow data.
+#
+add_admin()
+{
+    [ $DEBUG -eq 1 ] && ${ECHO} "In add_admin()"
+
+    # Check if the admin user already exists.
+    eval "${LDAPSEARCH} ${LDAP_ARGS} -b \"${LDAP_ADMINDN}\" -s base \"objectclass=*\" ${VERB}"
+    if [ $? -eq 0 ]; then
+	MSG="Administrator ${LDAP_ADMINDN} already exists."
+	if [ $EXISTING_PROFILE -eq 1 ]; then
+	    ${ECHO} "  NOT ADDED: $MSG"
+	else
+	    ${ECHO} "  ${STEP}. $MSG"
+	    STEP=`expr $STEP + 1`	
+	fi
+	return 0
+    fi
+
+    # Get cn and sn names from LDAP_ADMINDN.
+    cn_tmp=`${ECHO} ${LDAP_ADMINDN} | cut -f1 -d, | cut -f2 -d=`
+
+    # Create the tmp file to add.
+    ( cat <<EOF
+dn: ${LDAP_ADMINDN}
+cn: ${cn_tmp}
+sn: ${cn_tmp}
+objectclass: top
+objectclass: person
+userpassword: ${LDAP_ADMIN_CRED}
+EOF
+) > ${TMPDIR}/admin
+    
+    # Add the entry.
+    ${EVAL} "${LDAPMODIFY} -a ${LDAP_ARGS} -f ${TMPDIR}/admin ${VERB}"
+    if [ $? -ne 0 ]; then
+	${ECHO} "  ERROR: Adding administrator identity failed!"
+	cleanup
+	exit 1
+    fi
+
+    ${RM} -f ${TMPDIR}/admin
+
+    # Display message that the administrator identity is added.
+    MSG="Administrator identity ${LDAP_ADMINDN}"
+    if [ $EXISTING_PROFILE -eq 1 ]; then
+	${ECHO} "  ADDED: $MSG."
+    else
+	${ECHO} "  ${STEP}. $MSG added."
+	STEP=`expr $STEP + 1`
+    fi
+}
+
+#
+# allow_admin_write_shadow(): Give Admin write permission for shadow data.
+#
+allow_admin_write_shadow()
+{
+    [ $DEBUG -eq 1 ] && ${ECHO} "In allow_admin_write_shadow()"
+
+    # Set ACI Name
+    ADMIN_ACI_NAME="LDAP_Naming_Services_admin_shadow_write"
+
+    # Search for ACI_NAME
+    eval "${LDAPSEARCH} ${LDAP_ARGS} -b \"${LDAP_BASEDN}\" \
+    -s base objectclass=* aci > ${TMPDIR}/chk_adminwrite_aci 2>&1"
+    ${GREP} "${ADMIN_ACI_NAME}" ${TMPDIR}/chk_adminwrite_aci > /dev/null 2>&1
+    if [ $? -eq 0 ]; then
+	MSG="Admin ACI ${ADMIN_ACI_NAME} already exists for ${LDAP_BASEDN}."
+	if [ $EXISTING_PROFILE -eq 1 ]; then
+	    ${ECHO} "  NOT SET: $MSG"
+	else
+	    ${ECHO} "  ${STEP}. $MSG"
+	    STEP=`expr $STEP + 1`	
+	fi
+	return 0
+    fi
+
+    # Create the tmp file to add.
+    ( cat <<EOF
+dn: ${LDAP_BASEDN}
+changetype: modify
+add: aci
+aci: (target="ldap:///${LDAP_BASEDN}")(targetattr="shadowLastChange||shadowMin||shadowMax||shadowWarning||shadowInactive||shadowExpire||shadowFlag||userPassword||loginShell||homeDirectory||gecos")(version 3.0; acl ${ADMIN_ACI_NAME}; allow (write) userdn = "ldap:///${LDAP_ADMINDN}";)
+EOF
+) > ${TMPDIR}/admin_write
+    
+    # Add the entry.
+    ${EVAL} "${LDAPMODIFY} ${LDAP_ARGS} -f ${TMPDIR}/admin_write ${VERB}"
+    if [ $? -ne 0 ]; then
+	${ECHO} "  ERROR: Allow ${LDAP_ADMINDN} to write shadow data failed!"
+	cleanup
+	exit 1
+    fi
+
+    ${RM} -f ${TMPDIR}/admin_write
+    # Display message that the administrator ACL is set.
+    MSG="Give ${LDAP_ADMINDN} write permission for shadow."
+    if [ $EXISTING_PROFILE -eq 1 ]; then
+	${ECHO} "  ACI SET: $MSG"
+    else
+	${ECHO} "  ${STEP}. $MSG"
+	STEP=`expr $STEP + 1`
+    fi
+}
+
+#
+# allow_host_write_shadow(): Give host principal write permission
+# for shadow data.
+#
+allow_host_write_shadow()
+{
+    [ $DEBUG -eq 1 ] && ${ECHO} "In allow_host_write_shadow()"
+
+    # Set ACI Name
+    HOST_ACI_NAME="LDAP_Naming_Services_host_shadow_write"
+
+    # Search for ACI_NAME
+    eval "${LDAPSEARCH} ${LDAP_ARGS} -b \"${LDAP_BASEDN}\" -s base objectclass=* aci > ${TMPDIR}/chk_hostwrite_aci 2>&1"
+    ${GREP} "${HOST_ACI_NAME}" ${TMPDIR}/chk_hostwrite_aci > /dev/null 2>&1
+    if [ $? -eq 0 ]; then
+	MSG="Host ACI ${HOST_ACI_NAME} already exists for ${LDAP_BASEDN}."
+	if [ $EXISTING_PROFILE -eq 1 ]; then
+	    ${ECHO} "  NOT ADDED: $MSG"
+	else
+	    ${ECHO} "  ${STEP}. $MSG"
+	    STEP=`expr $STEP + 1`
+	fi
+	return 0
+    fi
+
+    # Create the tmp file to add.
+    ( cat <<EOF
+dn: ${LDAP_BASEDN}
+changetype: modify
+add: aci
+aci: (target="ldap:///${LDAP_BASEDN}")(targetattr="shadowLastChange||shadowMin||shadowMax||shadowWarning||shadowInactive||shadowExpire||shadowFlag||userPassword||loginShell||homeDirectory||gecos")(version 3.0; acl ${HOST_ACI_NAME}; allow (read, write) authmethod="sasl GSSAPI" and userdn = "ldap:///cn=*+ipHostNumber=*,ou=Hosts,${LDAP_BASEDN}";)
+EOF
+) > ${TMPDIR}/host_write
+    
+    # Add the entry.
+    ${EVAL} "${LDAPMODIFY} ${LDAP_ARGS} -f ${TMPDIR}/host_write ${VERB}"
+    if [ $? -ne 0 ]; then
+	${ECHO} "  ERROR: Allow Host Principal to write shadow data failed!"
+	cleanup
+	exit 1
+    fi
+
+    ${RM} -f ${TMPDIR}/host_write
+    MSG="Give host principal write permission for shadow."
+    if [ $EXISTING_PROFILE -eq 1 ]; then
+	${ECHO} "  ACI SET: $MSG"
+    else
+	${ECHO} "  ${STEP}. $MSG"
+	STEP=`expr $STEP + 1`
+    fi
+}
+
+#
+# Set up shadow update
+#
+setup_shadow_update() {
+    [ $DEBUG -eq 1 ] && ${ECHO} "In setup_shadow_update()"
+
+    # get content of the profile
+    PROFILE_OUT=${TMPDIR}/prof_tmpfile
+    ${EVAL} "${LDAPSEARCH} ${LDAP_ARGS} -b \"cn=${LDAP_PROFILE_NAME},ou=profile,${LDAP_BASEDN}\" -s base \"objectclass=*\" > $PROFILE_OUT 2>&1"
+    ${GREP} -i cn $PROFILE_OUT >/dev/null 2>&1
+    if [ $? -ne 0 ]; then
+	[ $DEBUG -eq 1 ] && ${ECHO} "Profile ${LDAP_PROFILE_NAME} does not exist"
+	${RM} ${PROFILE_OUT}
+	return
+    fi
+
+    # Search to see if authenticationMethod has 'GSSAPI' and
+    # credentialLevel has 'self'. If so, ask to use the
+    # host principal for shadow update
+    if [ $GSSAPI_AUTH_MAY_BE_USED -eq 1 ]; then
+	if ${GREP} authenticationMethod $PROFILE_OUT | ${GREP} GSSAPI >/dev/null 2>&1
+	then
+	    if ${GREP} credentialLevel $PROFILE_OUT | ${GREP} self >/dev/null 2>&1
+	    then
+		NEED_HOSTACL=1
+	    fi
+	fi
+	${RM} ${PROFILE_OUT}
+	[ $DEBUG -eq 1 ] && ${ECHO} "NEED_HOSTACL = $NEED_HOSTACL"
+
+	if [ $NEED_HOSTACL -eq 1 ]; then
+	    MSG="Use host principal for shadow data update (y/n/h)?"
+	    get_confirm "$MSG" "y" "use_host_principal_help"
+	    if [ $? -eq 1 ]; then
+		allow_host_write_shadow
+		modify_top_aci
+	        ${ECHO} ""
+		${ECHO} "  Shadow update has been enabled."
+	    else
+	        ${ECHO} ""
+    		${ECHO} "  Shadow update may not work."
+	    fi
+	    return
+	fi
+    fi
+
+    MSG="Add the administrator identity (y/n/h)?"
+    get_confirm "$MSG" "y" "add_admin_cred_help"
+    if [ $? -eq 1 ]; then
+	get_adminDN
+	get_admin_pw
+	add_admin
+	allow_admin_write_shadow
+	modify_top_aci
+        ${ECHO} ""
+	${ECHO} "  Shadow update has been enabled."
+	return
+    fi
+
+    ${ECHO} "  No administrator identity specified, shadow update may not work."
+}
+
 
 #
 # prompt_config_info(): This function prompts the user for the config
@@ -2459,6 +2796,12 @@
     gssapi_setup
 
     get_profile_name
+
+    if [ "$LDAP_ENABLE_SHADOW_UPDATE" = "TRUE" ];then
+	setup_shadow_update
+	exit 0
+    fi
+
     get_srv_list
     get_pref_srv
     get_search_scope
@@ -2509,6 +2852,9 @@
     get_prof_ttl
     get_bind_limit
 
+    # Ask whether to enable shadow update
+    get_want_shadow_update
+
     # Reset the sdd_file and prompt user for SSD.  Will use menus
     # to build an SSD File.
     reset_ssd_file
@@ -2547,7 +2893,6 @@
     LDAP_PROXYAGENT_CRED=$ANS
 }
 
-
 #
 # display_summary(): Display a summary of values entered and let the 
 #                    user modify values at will.
@@ -2565,18 +2910,19 @@
     TBL5="get_timelimit get_sizelimit get_want_crypt"
     TBL6="get_srv_authMethod_pam get_srv_authMethod_key get_srv_authMethod_cmd"
     TBL7="get_srch_time get_prof_ttl get_bind_limit"
-    TBL8="prompt_ssd"
-    FUNC_TBL="$TBL1 $TBL2 $TBL3 $TBL4 $TBL5 $TBL6 $TBL7 $TBL8"
+    TBL8="get_want_shadow_update"
+    TBL9="prompt_ssd"
+    FUNC_TBL="$TBL1 $TBL2 $TBL3 $TBL4 $TBL5 $TBL6 $TBL7 $TBL8 $TBL9"
 
     # Since menu prompt string is long, set here.
-    _MENU_PROMPT="Enter config value to change: (1-19 0=commit changes)"
+    _MENU_PROMPT="Enter config value to change: (1-20 0=commit changes)"
 
     # Infinite loop.  Test for 0, and break in loop.
     while :
     do
 	# Display menu and get value in range.
 	display_msg summary_menu
-	get_menu_choice "${_MENU_PROMPT}" "0" "19" "0"
+	get_menu_choice "${_MENU_PROMPT}" "0" "20" "0"
 	_CH=$MN_CH
 	
 	# Make sure where not exiting.
@@ -2604,6 +2950,23 @@
 	fi
     fi
 
+    # If shadow update is enabled, set up administrator credential
+    if [ "$LDAP_ENABLE_SHADOW_UPDATE" = "TRUE" ]; then
+	NEED_ADMIN=1
+	if ${ECHO} "$LDAP_CRED_LEVEL" | ${GREP} "self" > /dev/null 2>&1; then
+	    if ${ECHO} "$LDAP_AUTHMETHOD" | ${GREP} "GSSAPI" > /dev/null 2>&1; then
+		NEED_HOSTACL=1
+		NEED_ADMIN=0
+	    fi
+	fi
+        [ $DEBUG -eq 1 ] && ${ECHO} "NEED_HOSTACL = $NEED_HOSTACL"
+        [ $DEBUG -eq 1 ] && ${ECHO} "NEED_ADMIN   = $NEED_ADMIN"
+	if [ $NEED_ADMIN -eq 1 ]; then
+	    get_adminDN
+	    get_admin_pw
+	fi
+    fi
+
     # Display FULL debugging info.
     disp_full_debug
 
@@ -2656,6 +3019,9 @@
 NEED_TIME=$NEED_TIME
 NEED_SIZE=$NEED_SIZE
 NEED_CRYPT=$NEED_CRYPT
+NEED_ADMIN=$NEED_ADMIN
+NEED_HOSTACL=$NEED_HOSTACL
+EXISTING_PROFILE=$EXISTING_PROFILE
 
 # LDAP PROFILE related defaults
 LDAP_PROFILE_NAME="$LDAP_PROFILE_NAME"
@@ -2681,10 +3047,17 @@
 LDAP_PROXYAGENT="$LDAP_PROXYAGENT"
 LDAP_PROXYAGENT_CRED=$LDAP_PROXYAGENT_CRED
 
+# enableShadowUpdate flag and Administrator credential
+LDAP_ENABLE_SHADOW_UPDATE=$LDAP_ENABLE_SHADOW_UPDATE
+LDAP_ADMINDN="$LDAP_ADMINDN"
+LDAP_ADMIN_CRED=$LDAP_ADMIN_CRED
+
 # Export all the variables (just in case)
 export IDS_HOME IDS_PORT LDAP_ROOTDN LDAP_ROOTPWD LDAP_SERVER_LIST LDAP_BASEDN
 export LDAP_DOMAIN LDAP_SUFFIX LDAP_PROXYAGENT LDAP_PROXYAGENT_CRED
 export NEED_PROXY
+export LDAP_ENABLE_SHADOW_UPDATE LDAP_ADMINDN LDAP_ADMIN_CRED
+export NEED_ADMIN NEED_HOSTACL EXISTING_PROFILE
 export LDAP_PROFILE_NAME LDAP_BASEDN LDAP_SERVER_LIST 
 export LDAP_AUTHMETHOD LDAP_FOLLOWREF LDAP_SEARCH_SCOPE LDAP_SEARCH_TIME_LIMIT
 export LDAP_PREF_SRVLIST LDAP_PROFILE_TTL LDAP_CRED_LEVEL LDAP_BIND_LIMIT
@@ -4113,11 +4486,38 @@
 	cleanup
 	exit 1
     fi
+
+    # Display "already exists" message if necessary. For shadow update,
+    # check also if the deny self-write to userPassword has been done.
+    # If not, more to do, don't display the message.
+    MSG="Top level ACI ${ACI_NAME} already exists for ${LDAP_BASEDN}."
     ${GREP} "${ACI_NAME}" ${TMPDIR}/chk_top_aci > /dev/null 2>&1
     if [ $? -eq 0 ]; then
-	${ECHO} "  ${STEP}. Top level ACI ${ACI_NAME} already exists for ${LDAP_BASEDN}."
-	STEP=`expr $STEP + 1`
-	return 0
+	if [ "$LDAP_ENABLE_SHADOW_UPDATE" != "TRUE" ];then
+	    ${ECHO} "  ${STEP}. $MSG"
+	    STEP=`expr $STEP + 1`
+	    return 0
+	else
+	    ${GREP} "${ACI_NAME}" ${TMPDIR}/chk_top_aci | ${GREP} -i  \
+	    userPassword > /dev/null 2>&1
+	    if [ $? -eq 0 ]; then
+		# userPassword is already on the deny list, no more to do
+		if [ $EXISTING_PROFILE -eq 1 ];then
+		    ${ECHO} "  NOT SET: $MSG"
+		else
+		    ${ECHO} "  ${STEP}. $MSG"
+		    STEP=`expr $STEP + 1`
+		fi
+	        return 0
+	    fi
+	fi
+    fi
+
+    # if shadow update is enabled, also deny self-write to userPassword
+    if [ "$LDAP_ENABLE_SHADOW_UPDATE" = "TRUE" ];then
+	PWD_SELF_CHANGE="userPassword||"
+    else
+	PWD_SELF_CHANGE=""
     fi
 
     # Crate LDIF for top level ACI.
@@ -4125,7 +4525,7 @@
 dn: ${LDAP_BASEDN}
 changetype: modify
 add: aci
-aci: (targetattr = "cn||uid||uidNumber||gidNumber||homeDirectory||shadowLastChange||shadowMin||shadowMax||shadowWarning||shadowInactive||shadowExpire||shadowFlag||memberUid||SolarisAuditAlways||SolarisAuditNever||SolarisAttrKeyValue||SolarisAttrReserved1||SolarisAttrReserved2||SolarisUserQualifier")(version 3.0; acl ${ACI_NAME}; deny (write) userdn = "ldap:///self";)
+aci: (targetattr = "${PWD_SELF_CHANGE}cn||uid||uidNumber||gidNumber||homeDirectory||shadowLastChange||shadowMin||shadowMax||shadowWarning||shadowInactive||shadowExpire||shadowFlag||memberUid||SolarisAuditAlways||SolarisAuditNever||SolarisAttrKeyValue||SolarisAttrReserved1||SolarisAttrReserved2||SolarisUserQualifier")(version 3.0; acl ${ACI_NAME}; deny (write) userdn = "ldap:///self";)
 -
 EOF
 ) > ${TMPDIR}/top_aci
@@ -4139,11 +4539,15 @@
     fi
 
     # Display message that schema is updated.
-    ${ECHO} "  ${STEP}. ACI for ${LDAP_BASEDN} modified to disable self modify."
-    STEP=`expr $STEP + 1`
+    MSG="ACI for ${LDAP_BASEDN} modified to disable self modify."
+    if [ $EXISTING_PROFILE -eq 1 ];then
+	${ECHO} "  ACI SET: $MSG"
+    else
+	${ECHO} "  ${STEP}. $MSG"
+	STEP=`expr $STEP + 1`
+    fi
 }
 
-
 #
 # add_vlv_aci(): Add access control information (aci) for VLV.
 #
@@ -4516,7 +4920,6 @@
     STEP=`expr $STEP + 1`
 }
 
-
 #
 # allow_proxy_read_pw(): Give Proxy Agent read permission for password.
 #
@@ -4558,7 +4961,6 @@
     STEP=`expr $STEP + 1`
 }
 
-
 #
 # add_profile(): Add client profile to server.
 #
@@ -4757,6 +5159,18 @@
     allow_proxy_read_pw
 fi
 
+# If admin needed for shadow update, Add the administrator identity and
+# give write permission for shadow.
+if [ $NEED_ADMIN -eq 1 ]; then
+    add_admin
+    allow_admin_write_shadow
+fi
+
+# if use host principal for shadow update, give write permission for shadow.
+if [ $NEED_HOSTACL -eq 1 ]; then
+    allow_host_write_shadow
+fi
+
 # Generate client profile and add it to the server.
 add_profile
 
--- a/usr/src/cmd/ldap/ns_ldap/ldapaddent.c	Fri Feb 13 18:28:50 2009 -0800
+++ b/usr/src/cmd/ldap/ns_ldap/ldapaddent.c	Fri Feb 13 18:18:56 2009 -0800
@@ -19,7 +19,7 @@
  * CDDL HEADER END
  */
 /*
- * Copyright 2008 Sun Microsystems, Inc.  All rights reserved.
+ * Copyright 2009 Sun Microsystems, Inc.  All rights reserved.
  * Use is subject to license terms.
  */
 
@@ -3119,7 +3119,6 @@
 dump_passwd(ns_ldap_result_t *res)
 {
 	char    **value = NULL;
-	char	pnam[256];
 
 	value = __ns_ldap_getAttr(res->entry, "uid");
 	if (value == NULL)
@@ -3127,15 +3126,13 @@
 	else
 		(void) fprintf(stdout, "%s:", value[0]);
 	value = __ns_ldap_getAttr(res->entry, "userPassword");
-	if (value == NULL)
-		(void) fprintf(stdout, "*:");
-	else {
-		(void) strcpy(pnam, value[0]);
-		if (strncasecmp(value[0], "{crypt}", 7) == 0)
-		(void) fprintf(stdout, "%s:", (pnam+7));
-		else
-			(void) fprintf(stdout, "*:");
-	}
+
+	/*
+	 * Don't print the encrypted password, Use x to
+	 * indicate it is in the shadow database.
+	 */
+	(void) fprintf(stdout, "x:");
+
 	value = __ns_ldap_getAttr(res->entry, "uidNumber");
 	if (value && value[0])
 		(void) fprintf(stdout, "%s:", value[0]);
@@ -3315,8 +3312,16 @@
 		(void) snprintf(pname, sizeof (pname), "{crypt}%s",
 		    ecol[1].ec_value.ec_value_val);
 		data.sp_pwdp = strdup(pname);
-	} else
-		data.sp_pwdp = NULL;
+	} else {
+		/*
+		 * no password (e.g., deleted by "passwd -d"):
+		 * use the special value NS_LDAP_NO_UNIX_PASSWORD
+		 * instead.
+		 */
+		(void) snprintf(pname, sizeof (pname), "{crypt}%s",
+		    NS_LDAP_NO_UNIX_PASSWORD);
+		data.sp_pwdp = strdup(pname);
+	}
 
 	if (ecol[2].ec_value.ec_value_val != NULL &&
 	    ecol[2].ec_value.ec_value_val[0] != '\0') {
@@ -3450,9 +3455,12 @@
 		(void) fprintf(stdout, "*:");
 	else {
 		(void) strcpy(pnam, value[0]);
-		if (strncasecmp(value[0], "{crypt}", 7) == 0)
-		(void) fprintf(stdout, "%s:", (pnam+7));
-		else
+		if (strncasecmp(value[0], "{crypt}", 7) == 0) {
+			if (strcmp(pnam + 7, NS_LDAP_NO_UNIX_PASSWORD) == 0)
+				(void) fprintf(stdout, ":");
+			else
+				(void) fprintf(stdout, "%s:", (pnam+7));
+		} else
 			(void) fprintf(stdout, "*:");
 	}
 	value = __ns_ldap_getAttr(res->entry, "shadowLastChange");
@@ -3471,12 +3479,31 @@
 	else
 		(void) fprintf(stdout, "%s:", value[0]);
 
-	/* ignore shadowWarning, shadowInactive, shadowExpire, shadowFlag */
-	(void) fprintf(stdout, ":::\n");
-
+	value = __ns_ldap_getAttr(res->entry, "shadowWarning");
+	if (value == NULL)
+		(void) fprintf(stdout, ":");
+	else
+		(void) fprintf(stdout, "%s:", value[0]);
+
+	value = __ns_ldap_getAttr(res->entry, "shadowInactive");
+	if (value == NULL)
+		(void) fprintf(stdout, ":");
+	else
+		(void) fprintf(stdout, "%s:", value[0]);
+
+	value = __ns_ldap_getAttr(res->entry, "shadowExpire");
+	if (value == NULL)
+		(void) fprintf(stdout, ":");
+	else
+		(void) fprintf(stdout, "%s:", value[0]);
+
+	value = __ns_ldap_getAttr(res->entry, "shadowFlag");
+	if (value == NULL || value[0] == NULL || strcmp(value[0], "0") == 0)
+		(void) fprintf(stdout, "\n");
+	else
+		(void) fprintf(stdout, "%s\n", value[0]);
 }
 
-
 static int
 genent_bootparams(char *line, int (*cback)())
 {
--- a/usr/src/cmd/ldap/ns_ldap/ldapclient.c	Fri Feb 13 18:28:50 2009 -0800
+++ b/usr/src/cmd/ldap/ns_ldap/ldapclient.c	Fri Feb 13 18:18:56 2009 -0800
@@ -19,12 +19,10 @@
  * CDDL HEADER END
  */
 /*
- * Copyright 2008 Sun Microsystems, Inc.  All rights reserved.
+ * Copyright 2009 Sun Microsystems, Inc.  All rights reserved.
  * Use is subject to license terms.
  */
 
-#pragma ident	"%Z%%M%	%I%	%E% SMI"
-
 /*
  * ldapclient command. To make (initiailize) or uninitialize a machines as
  * and LDAP client.  This command MUST be run as root (or it will simply exit).
@@ -87,6 +85,12 @@
  *	proxyPassword
  *		Client password not needed for authentication "none".
  *		(formerly -w)
+ *	adminDN
+ *		Administrator DN for updating naming data.
+ *	adminPassword
+ *		Administrator password
+ *	enableShadowUpdate
+ *		Allow Administrator to change shadow data in LDAP
  *	searchTimeLimit
  *		Timeout value. (formerly -o)
  *	serviceSearchDescriptor
@@ -274,6 +278,9 @@
 	char		*profileTTL;
 	char		*proxyDN;
 	char		*proxyPassword;
+	char		*enableShadowUpdate;
+	char		*adminDN;
+	char		*adminPassword;
 	char		*bindDN;
 	char		*bindPasswd;
 	char		*defaultSearchScope;
@@ -294,6 +301,7 @@
 static void usage(void);
 
 static int credCheck(clientopts_t *arglist);
+static int adminCredCheck(clientopts_t *arglist);
 static int clientSetParam(clientopts_t *optlist, int paramFlag, char *attrVal);
 static int parseParam(char *param, char **paramVal);
 static void dumpargs(clientopts_t *arglist);
@@ -369,7 +377,7 @@
 
 	optind = 1;
 	while (optind < argc) {
-		option = getopt(argc, argv, "vqa:ID:w:j:y:");
+		option = getopt(argc, argv, "vqa:ID:w:j:y:z:");
 
 		switch (option) {
 		case 'v':
@@ -397,6 +405,14 @@
 				    "-a proxyPassword is ignored.\n"));
 				break;
 			}
+			if (paramFlag == NS_LDAP_ADMIN_BINDPASSWD_P &&
+			    optlist->adminPassword != NULL) {
+				(void) fprintf(stderr,
+				    gettext("The -a adminPassword option is "
+				    "mutually exclusive of -z. "
+				    "-a adminPassword is ignored.\n"));
+				break;
+			}
 			retcode = clientSetParam(optlist, paramFlag, attrVal);
 			if (retcode != CLIENT_SUCCESS) {
 				CLIENT_FPRINTF(
@@ -444,13 +460,24 @@
 				    gettext("The -a proxyPassword option is "
 				    "mutually exclusive of -y. "
 				    "-a proxyPassword is ignored.\n"));
-				free(optlist->proxyPassword);
 			}
 			optlist->proxyPassword = readPwd(optarg);
 			if (optlist->proxyPassword == NULL) {
 				exit(CLIENT_ERR_FAIL);
 			}
 			break;
+		case 'z':
+			if (optlist->adminPassword != NULL) {
+				(void) fprintf(stderr,
+				    gettext("The -a adminPassword option is "
+				    "mutually exclusive of -z. "
+				    "-a adminPassword is ignored.\n"));
+			}
+			optlist->adminPassword = readPwd(optarg);
+			if (optlist->adminPassword == NULL) {
+				exit(CLIENT_ERR_FAIL);
+			}
+			break;
 		case EOF:
 			if (strcmp(argv[optind], "init") == 0) {
 				op_init = 1;
@@ -530,6 +557,23 @@
 		exit(CLIENT_ERR_FAIL);
 	}
 
+/*
+ * if init or manual, and if adminDN is specified then enableShadowUpdate
+ * must be set to TRUE.
+ */
+	if ((op_init || op_manual) &&
+	    (!optlist->enableShadowUpdate ||
+	    strcasecmp(optlist->enableShadowUpdate, "TRUE") != 0) &&
+	    (optlist->adminDN || optlist->adminPassword)) {
+		CLIENT_FPUTS(
+		    gettext("ldapclient: adminDN and adminPassword must not "
+		    "be specified if enableShadowUpdate is not set to TRUE \n"),
+		    stderr);
+		usage();
+		clientopts_free(optlist);
+		exit(CLIENT_ERR_FAIL);
+	}
+
 /* Only one verb can be specified */
 	if ((op_init + op_manual + op_mod + op_uninit +
 	    op_list + op_genprofile) != 1) {
@@ -807,6 +851,9 @@
 	LDAP_SET_PARAM(arglist->defaultSearchBase, NS_LDAP_SEARCH_BASEDN_P);
 	LDAP_SET_PARAM(arglist->credentialLevel, NS_LDAP_CREDENTIAL_LEVEL_P);
 	LDAP_SET_PARAM(arglist->proxyDN, NS_LDAP_BINDDN_P);
+	LDAP_SET_PARAM(arglist->enableShadowUpdate,
+	    NS_LDAP_ENABLE_SHADOW_UPDATE_P);
+	LDAP_SET_PARAM(arglist->adminDN, NS_LDAP_ADMIN_BINDDN_P);
 	LDAP_SET_PARAM(arglist->searchTimeLimit, NS_LDAP_SEARCH_TIME_P);
 	LDAP_SET_PARAM(arglist->preferredServerList, NS_LDAP_SERVER_PREF_P);
 	LDAP_SET_PARAM(arglist->profileName, NS_LDAP_PROFILE_P);
@@ -814,6 +861,7 @@
 	LDAP_SET_PARAM(arglist->defaultSearchScope, NS_LDAP_SEARCH_SCOPE_P);
 	LDAP_SET_PARAM(arglist->bindTimeLimit, NS_LDAP_BIND_TIME_P);
 	LDAP_SET_PARAM(arglist->proxyPassword, NS_LDAP_BINDPASSWD_P);
+	LDAP_SET_PARAM(arglist->adminPassword, NS_LDAP_ADMIN_BINDPASSWD_P);
 	LDAP_SET_PARAM(arglist->defaultServerList, NS_LDAP_SERVERS_P);
 	LDAP_SET_PARAM(arglist->certificatePath, NS_LDAP_HOST_CERTPATH_P);
 
@@ -854,6 +902,8 @@
 	}
 
 	retcode = credCheck(arglist);
+	if (retcode == CLIENT_SUCCESS)
+		retcode = adminCredCheck(arglist);
 	if (retcode != CLIENT_SUCCESS) {
 		CLIENT_FPUTS(
 		    gettext("Error in setting up credentials\n"),
@@ -1091,6 +1141,7 @@
 	LDAP_SET_PARAM(arglist->defaultSearchBase, NS_LDAP_SEARCH_BASEDN_P);
 	LDAP_SET_PARAM(arglist->credentialLevel, NS_LDAP_CREDENTIAL_LEVEL_P);
 	LDAP_SET_PARAM(arglist->proxyDN, NS_LDAP_BINDDN_P);
+	LDAP_SET_PARAM(arglist->adminDN, NS_LDAP_ADMIN_BINDDN_P);
 	LDAP_SET_PARAM(arglist->profileTTL, NS_LDAP_CACHETTL_P);
 	LDAP_SET_PARAM(arglist->searchTimeLimit, NS_LDAP_SEARCH_TIME_P);
 	LDAP_SET_PARAM(arglist->preferredServerList, NS_LDAP_SERVER_PREF_P);
@@ -1099,7 +1150,10 @@
 	LDAP_SET_PARAM(arglist->defaultSearchScope, NS_LDAP_SEARCH_SCOPE_P);
 	LDAP_SET_PARAM(arglist->bindTimeLimit, NS_LDAP_BIND_TIME_P);
 	LDAP_SET_PARAM(arglist->proxyPassword, NS_LDAP_BINDPASSWD_P);
+	LDAP_SET_PARAM(arglist->adminPassword, NS_LDAP_ADMIN_BINDPASSWD_P);
 	LDAP_SET_PARAM(arglist->defaultServerList, NS_LDAP_SERVERS_P);
+	LDAP_SET_PARAM(arglist->enableShadowUpdate,
+	    NS_LDAP_ENABLE_SHADOW_UPDATE_P);
 	LDAP_SET_PARAM(arglist->certificatePath, NS_LDAP_HOST_CERTPATH_P);
 
 	for (counter = 0;
@@ -1144,6 +1198,8 @@
 	}
 
 	retcode = credCheck(arglist);
+	if (retcode == CLIENT_SUCCESS)
+		retcode = adminCredCheck(arglist);
 	if (retcode != CLIENT_SUCCESS) {
 		CLIENT_FPUTS(
 		    gettext("Error in setting up credentials\n"),
@@ -1300,6 +1356,10 @@
 	/* *** Check for invalid args *** */
 	LDAP_CHECK_INVALID(arglist->proxyDN, "proxyDN");
 	LDAP_CHECK_INVALID(arglist->proxyPassword, "proxyPassword");
+	LDAP_CHECK_INVALID(arglist->enableShadowUpdate,
+	    "enableShadowUpdate");
+	LDAP_CHECK_INVALID(arglist->adminDN, "adminDN");
+	LDAP_CHECK_INVALID(arglist->adminPassword, "adminPassword");
 	LDAP_CHECK_INVALID(arglist->certificatePath, "certificatePath");
 	LDAP_CHECK_INVALID(arglist->domainName, "domainName");
 	LDAP_CHECK_INVALID(arglist->bindDN, "bind DN");
@@ -1472,7 +1532,7 @@
 		cfg.SA_CRED = "proxy";
 		/*
 		 * We don't want to force users to always specify authentication
-		 * method when we can infer it. If users wants SSL, he/she would
+		 * method when we can infer it. If users want SSL, he/she would
 		 * have to specify appropriate -a though.
 		 */
 		auth.type = NS_LDAP_AUTH_SIMPLE;
@@ -1558,6 +1618,43 @@
 		}
 	}
 
+	if (arglist->enableShadowUpdate != NULL) {
+		LDAP_SET_PARAM(arglist->enableShadowUpdate,
+		    NS_LDAP_ENABLE_SHADOW_UPDATE_P);
+	}
+
+	if (arglist->enableShadowUpdate &&
+	    strcasecmp(arglist->enableShadowUpdate, "TRUE") == 0 &&
+	    arglist->adminDN != NULL && arglist->adminPassword == NULL) {
+		arglist->adminPassword = getpassphrase("admin Bind Password:");
+		if (arglist->adminPassword == NULL) {
+			CLIENT_FPUTS(gettext("Get password failed\n"), stderr);
+
+			if (gStartLdap == START_RESET)
+				(void) start_service(LDAP_FMRI, B_TRUE);
+
+			return (CLIENT_ERR_CREDENTIAL);
+		}
+	}
+	if (arglist->adminDN != NULL && arglist->adminPassword != NULL) {
+		if (__ns_ldap_setParam(NS_LDAP_ADMIN_BINDDN_P,
+		    arglist->adminDN, &errorp) != NS_LDAP_SUCCESS) {
+			if (errorp != NULL) {
+				CLIENT_FPRINTF(stderr, "%s\n", errorp->message);
+				(void) __ns_ldap_freeError(&errorp);
+			}
+			return (CLIENT_ERR_CREDENTIAL);
+		}
+		if (__ns_ldap_setParam(NS_LDAP_ADMIN_BINDPASSWD_P,
+		    arglist->adminPassword, &errorp) != NS_LDAP_SUCCESS) {
+			if (errorp != NULL) {
+				CLIENT_FPRINTF(stderr, "%s\n", errorp->message);
+				(void) __ns_ldap_freeError(&errorp);
+			}
+			return (CLIENT_ERR_CREDENTIAL);
+		}
+	}
+
 	if (arglist->authenticationMethod != NULL) {
 		if (__ns_ldap_getParam(NS_LDAP_AUTH_P,
 		    (void ***)&authMethod, &errorp) != NS_LDAP_SUCCESS) {
@@ -1641,6 +1738,8 @@
 	}
 
 	retcode = credCheck(arglist);
+	if (retcode == CLIENT_SUCCESS)
+		retcode = adminCredCheck(arglist);
 	if (retcode != CLIENT_SUCCESS) {
 		CLIENT_FPUTS(
 		    gettext("Error in setting up credentials\n"), stderr);
@@ -1850,7 +1949,8 @@
 		CLIENT_FPRINTF(stderr,
 		    gettext("\n       %s [-v | -q] [-a authenticationMethod]"
 		    " [-D bindDN]\n\t[-w bindPassword] [-j passswdFile]"
-		    " [-y proxyPasswordFile] init [<args>]\n"),
+		    " [-y proxyPasswordFile]\n\t"
+		    "[-z adminPasswordFile] init [<args>]\n"),
 		    cmd);
 
 		CLIENT_FPUTS(
@@ -2319,6 +2419,189 @@
 }
 
 /*
+ * adminCredCheck is called to check if the admin credential is required
+ * for this configuration. This means that if enableShadowUpdate is set
+ * to TRUE then credential info is required (adminDN and adminPassword).
+ * One exception is that if there is a 'self' credentialLevel and
+ * 'sasl/GSSAPI' authenticationMethod (i.e., possibly using Kerberos
+ * host credential) then adminDN and adminPassword are not required.
+ */
+static int
+adminCredCheck(clientopts_t *arglist)
+{
+	int counter;
+	int **enabled = NULL;
+	int **credLevel = NULL;
+	char **adminDN = NULL;
+	char **adminPassword = NULL;
+	ns_auth_t **authMethod = NULL;
+	ns_ldap_error_t *errorp = NULL;
+	int credSelf, authSASLgss;
+	int retcode, rc;
+
+	/* If shadow update not enabled, then no need to check */
+	retcode = __ns_ldap_getParam(NS_LDAP_ENABLE_SHADOW_UPDATE_P,
+	    (void ***)&enabled, &errorp);
+	if (retcode != 0) {
+		CLIENT_FPRINTF(stderr,
+		    gettext("Error %d while trying to retrieve "
+		    "enableShadowUpdate\n"), retcode);
+		rc = CLIENT_ERR_FAIL;
+		goto out;
+	}
+	if (enabled == NULL ||
+	    *enabled[0] != NS_LDAP_ENABLE_SHADOW_UPDATE_TRUE) {
+		if (mode_verbose)
+			CLIENT_FPUTS(
+			    gettext("Shadow Update is not enabled, "
+			    "no adminDN/adminPassword is required.\n"), stderr);
+		rc = CLIENT_SUCCESS;
+		goto out;
+	}
+
+	/* get credentialLevel */
+	retcode = __ns_ldap_getParam(NS_LDAP_CREDENTIAL_LEVEL_P,
+	    (void ***)&credLevel, &errorp);
+	if (retcode != 0) {
+		CLIENT_FPRINTF(stderr,
+		    gettext("Error %d while trying to retrieve credLevel\n"),
+		    retcode);
+		rc = CLIENT_ERR_FAIL;
+		goto out;
+	}
+
+	/* get AuthenticationMethod */
+	retcode = __ns_ldap_getParam(NS_LDAP_AUTH_P,
+	    (void ***)&authMethod, &errorp);
+	if (retcode != 0) {
+		CLIENT_FPRINTF(stderr,
+		    gettext("Error %d while trying to retrieve authMethod\n"),
+		    retcode);
+		rc = CLIENT_ERR_FAIL;
+		goto out;
+	}
+
+	/* get adminDN */
+	retcode = __ns_ldap_getParam(NS_LDAP_ADMIN_BINDDN_P,
+	    (void ***)&adminDN, &errorp);
+	if (retcode != 0) {
+		CLIENT_FPRINTF(stderr,
+		    gettext("Error %d while trying to retrieve adminDN\n"),
+		    retcode);
+		rc = CLIENT_ERR_FAIL;
+		goto out;
+	}
+
+	/* get adminPassword */
+	retcode = __ns_ldap_getParam(NS_LDAP_ADMIN_BINDPASSWD_P,
+	    (void ***)&adminPassword, &errorp);
+	if (retcode != 0) {
+		CLIENT_FPRINTF(stderr,
+		    gettext("Error %d while trying to retrieve "
+		    "adminPassword\n"), retcode);
+		rc = CLIENT_ERR_FAIL;
+		goto out;
+	}
+
+	if (mode_verbose) {
+		CLIENT_FPRINTF(stderr,
+		    gettext("admin DN: %s\n"),
+		    (adminDN && adminDN[0]) ? adminDN[0] : "NULL");
+		CLIENT_FPRINTF(stderr,
+		    gettext("admin password: %s\n"),
+		    (adminPassword && adminPassword[0]) ?
+		    adminPassword[0] : "NULL");
+	}
+
+	credSelf = 0;	/* flag to indicate if we have a credLevel of self */
+	for (counter = 0; credLevel && credLevel[counter] != NULL; counter++) {
+		if (mode_verbose)
+			CLIENT_FPRINTF(stderr,
+			    gettext("Credential level: %d\n"),
+			    *credLevel[counter]);
+		if (*credLevel[counter] == NS_LDAP_CRED_SELF) {
+			credSelf = 1;
+			break;
+		}
+	}
+
+	authSASLgss = 0;	/* flag for authMethod of SASL/gssapi */
+	for (counter = 0;
+	    authMethod && authMethod[counter] != NULL;
+	    counter++) {
+
+		if (mode_verbose)
+			CLIENT_FPRINTF(stderr,
+			    gettext("Authentication sasl mechanism: %d\n"),
+			    authMethod[counter]->saslmech);
+		if (authMethod[counter]->saslmech == NS_LDAP_SASL_GSSAPI) {
+			authSASLgss = 1;
+			break;
+		}
+	}
+
+	/* First, if we don't need adminDN/adminPassword then just return ok */
+	if (credSelf && authSASLgss) {
+		if (mode_verbose)
+			CLIENT_FPUTS(
+			    gettext("A credential Level of self and an "
+			    "authentication method of sasl/GSSAPI is "
+			    "configured, no adminDN/adminPassword "
+			    "is required.\n"), stderr);
+		rc = CLIENT_SUCCESS;
+		goto out;
+	}
+
+	/* Now let's check if we have the cred stuff we need */
+	if (adminDN == NULL || adminDN[0] == '\0') {
+		CLIENT_FPUTS(
+		    gettext("Shadow Update is enabled, but "
+		    "no adminDN is configured.\n"), stderr);
+		rc = CLIENT_ERR_CREDENTIAL;
+		goto out;
+	}
+
+	/* If we need adminPassword (prompt) */
+	if (adminPassword == NULL || adminPassword[0] == '\0') {
+		CLIENT_FPUTS(
+		    gettext("Shadow Update requires adminPassword\n"),
+		    stderr);
+		arglist->adminPassword = getpassphrase("admin Password:");
+		if (arglist->adminPassword == NULL) {
+			CLIENT_FPUTS(gettext("Unable to get admin password\n"),
+			    stderr);
+			rc = CLIENT_ERR_CREDENTIAL;
+			goto out;
+		}
+		LDAP_SET_PARAM(arglist->adminPassword,
+		    NS_LDAP_ADMIN_BINDPASSWD_P);
+		if (retcode != 0) {
+			CLIENT_FPUTS(
+			    gettext("setParam adminPassword failed.\n"),
+			    stderr);
+			rc = CLIENT_ERR_CREDENTIAL;
+			goto out;
+		}
+	}
+
+	rc = CLIENT_SUCCESS;
+
+	out:
+	if (enabled != NULL)
+		(void) __ns_ldap_freeParam((void ***)&enabled);
+	if (credLevel != NULL)
+		(void) __ns_ldap_freeParam((void ***)&credLevel);
+	if (authMethod != NULL)
+		(void) __ns_ldap_freeParam((void ***)&authMethod);
+	if (adminDN != NULL)
+		(void) __ns_ldap_freeParam((void ***)&adminDN);
+	if (adminPassword != NULL)
+		(void) __ns_ldap_freeParam((void ***)&adminPassword);
+
+	return (rc);
+}
+
+/*
  * try to restore the previous name space on this machine
  */
 static int
@@ -3043,6 +3326,8 @@
 	arg_count += list->serviceCredentialLevel->count;
 	arg_count += list->domainName ? 1 : 0;
 	arg_count += list->proxyDN ? 1 : 0;
+	arg_count += list->enableShadowUpdate ? 1 : 0;
+	arg_count += list->adminDN ? 1 : 0;
 	arg_count += list->profileTTL ? 1 : 0;
 	arg_count += list->objectclassMap->count;
 	arg_count += list->searchTimeLimit ? 1 : 0;
@@ -3054,6 +3339,7 @@
 	arg_count += list->serviceSearchDescriptor->count;
 	arg_count += list->bindTimeLimit ? 1 : 0;
 	arg_count += list->proxyPassword ? 1 : 0;
+	arg_count += list->adminPassword ? 1 : 0;
 	arg_count += list->defaultServerList ? 1 : 0;
 	arg_count += list->certificatePath ? 1 : 0;
 
@@ -3075,6 +3361,8 @@
 	    list->serviceCredentialLevel);
 	CLIENT_PRINT("\tdomainName: ", list->domainName);
 	CLIENT_PRINT("\tproxyDN: ", list->proxyDN);
+	CLIENT_PRINT("\tadminDN: ", list->adminDN);
+	CLIENT_PRINT("\tenableShadowUpdate: ", list->enableShadowUpdate);
 	CLIENT_PRINT("\tprofileTTL: ", list->profileTTL);
 	multival_list("\tobjectclassMap: ", list->objectclassMap);
 	CLIENT_PRINT("\tsearchTimeLimit: ", list->searchTimeLimit);
@@ -3087,6 +3375,7 @@
 	    list->serviceSearchDescriptor);
 	CLIENT_PRINT("\tbindTimeLimit: ", list->bindTimeLimit);
 	CLIENT_PRINT("\tproxyPassword: ", list->proxyPassword);
+	CLIENT_PRINT("\tadminPassword: ", list->adminPassword);
 	CLIENT_PRINT("\tdefaultServerList: ", list->defaultServerList);
 	CLIENT_PRINT("\tcertificatePath: ", list->certificatePath);
 }
@@ -3119,6 +3408,9 @@
 	{"serviceAuthenticationMethod", NS_LDAP_SERVICE_AUTH_METHOD_P},
 	{"serviceCredentialLevel", NS_LDAP_SERVICE_CRED_LEVEL_P},
 	{"domainName", LOCAL_DOMAIN_P},
+	{"enableShadowUpdate", NS_LDAP_ENABLE_SHADOW_UPDATE_P},
+	{"adminDN", NS_LDAP_ADMIN_BINDDN_P},
+	{"adminPassword", NS_LDAP_ADMIN_BINDPASSWD_P},
 	{NULL, 0}
 };
 
@@ -3229,6 +3521,16 @@
 		optlist->proxyDN = attrVal;
 		break;
 
+	case NS_LDAP_ENABLE_SHADOW_UPDATE_P:
+		CLIENT_OPT_CHECK(paramFlag, optlist->enableShadowUpdate);
+		optlist->enableShadowUpdate = attrVal;
+		break;
+
+	case NS_LDAP_ADMIN_BINDDN_P:
+		CLIENT_OPT_CHECK(paramFlag, optlist->adminDN);
+		optlist->adminDN = attrVal;
+		break;
+
 	case NS_LDAP_CACHETTL_P:
 		CLIENT_OPT_CHECK(paramFlag, optlist->profileTTL);
 		optlist->profileTTL = attrVal;
@@ -3319,6 +3621,11 @@
 		optlist->proxyPassword = attrVal;
 		break;
 
+	case NS_LDAP_ADMIN_BINDPASSWD_P:
+		CLIENT_OPT_CHECK(paramFlag, optlist->adminPassword);
+		optlist->adminPassword = attrVal;
+		break;
+
 	case NS_LDAP_HOST_CERTPATH_P:
 		CLIENT_OPT_CHECK(paramFlag, optlist->certificatePath);
 		optlist->certificatePath = attrVal;
--- a/usr/src/cmd/ldapcachemgr/cachemgr.c	Fri Feb 13 18:28:50 2009 -0800
+++ b/usr/src/cmd/ldapcachemgr/cachemgr.c	Fri Feb 13 18:18:56 2009 -0800
@@ -19,12 +19,10 @@
  * CDDL HEADER END
  */
 /*
- * Copyright 2008 Sun Microsystems, Inc.  All rights reserved.
+ * Copyright 2009 Sun Microsystems, Inc.  All rights reserved.
  * Use is subject to license terms.
  */
 
-#pragma ident	"%Z%%M%	%I%	%E% SMI"
-
 /*
  * Simple doors ldap cache daemon
  */
@@ -55,6 +53,8 @@
 
 #include <alloca.h>
 #include <ucontext.h>
+#include <stddef.h>	/* offsetof */
+#include <priv.h>
 
 #include "getxby_door.h"
 #include "cachemgr.h"
@@ -72,6 +72,8 @@
 static  int client_setadmin(admin_t *ptr);
 static int client_showstats(admin_t *ptr);
 static int is_root(int free_uc, char *dc_str, ucred_t **uc);
+static int is_root_or_all_privs(char *dc_str, ucred_t **ucp);
+static void admin_modify(LineBuf *config_info, ldap_call_t *in);
 
 #ifdef SLP
 int			use_slp = 0;
@@ -833,6 +835,11 @@
 				ucred_free(uc);
 			state = ALLOCATE;
 			break;
+		case ADMINMODIFY:
+			admin_modify(&configInfo, ptr);
+
+			state = GETSIZE;
+			break;
 		case GETSTATUSCHANGE:
 			/*
 			 * Process the request and proceed with the default
@@ -1354,16 +1361,18 @@
 
 		if (current_admin.debug_level >= DBG_CANT_FIND)
 			logit("%s call failed(cred): caller pid %ld, uid %u, "
-			    "euid %u\n", dc_str, ucred_getpid(*ucp),
-			    ucred_getruid(*ucp), ucred_geteuid(*ucp));
+			    "euid %u (if uid or euid is %u, it may be "
+			    "unavailable)\n", dc_str, ucred_getpid(*ucp),
+			    ucred_getruid(*ucp), ucred_geteuid(*ucp), -1);
 
 		rc = 0;
 	} else {
 
 		if (current_admin.debug_level >= DBG_ALL)
-			logit("ldap_cachemgr received %s call from pid %ld, "
-			    "uid %u, euid %u\n", dc_str, ucred_getpid(*ucp),
-			    ucred_getruid(*ucp), ucred_geteuid(*ucp));
+			logit("received %s call from pid %ld, uid %u, euid %u "
+			    "(if uid or euid is %u, it may be unavailable)\n",
+			    dc_str, ucred_getpid(*ucp), ucred_getruid(*ucp),
+			    ucred_geteuid(*ucp), -1);
 		rc = 1;
 	}
 
@@ -1458,3 +1467,376 @@
 	return (match);
 
 }
+
+/*
+ * new_attr(name, value)
+ *
+ * create a new LDAP attribute to be sent to the server
+ */
+static ns_ldap_attr_t *
+new_attr(char *name, char *value)
+{
+	ns_ldap_attr_t *tmp;
+
+	tmp = malloc(sizeof (*tmp));
+	if (tmp != NULL) {
+		tmp->attrname = name;
+		tmp->attrvalue = (char **)calloc(2, sizeof (char *));
+		if (tmp->attrvalue == NULL) {
+			free(tmp);
+			return (NULL);
+		}
+		tmp->attrvalue[0] = value;
+		tmp->value_count = 1;
+	}
+
+	return (tmp);
+}
+
+/*
+ * Convert the flatten ldap attributes in a ns_ldap_attr_t back
+ * to an ns_ldap_attr_t array.
+ *
+ * strlist->ldap_offsets[] contains offsets to strings:
+ * "dn", <dn value>, <attr 1>, <attrval 1>, ... <attr n>, <attrval n>
+ * where n is (strlist->ldap_count/2 -1).
+ * The output ns_ldap_attr_t array has a size of (strlist->ldap_count/2)
+ * the first (strlist->ldap_count/2 -1) contains all the attribute data,
+ * the last one is a NULL pointer. DN will be extracted out and pointed
+ * to by *dn.
+ */
+static ns_ldap_attr_t **
+str2attrs(ldap_strlist_t *strlist, char **dn)
+{
+	int		c;
+	int		i;
+	int		j;
+	ns_ldap_attr_t	**ret;
+
+	c = strlist->ldap_count;
+	ret = calloc(c/2, sizeof (ns_ldap_attr_t *));
+	if (ret == NULL)
+		return (NULL);
+	*dn = (char *)strlist + strlist->ldap_offsets[1];
+
+	/*
+	 * skip the first 'dn'/<dn value> pair, for all other attr type/value
+	 * pairs, get pointers to the attr type (offset [i]) and attr value
+	 * (offset [i+1]) and put in ns_ldap_attr_t at ret[j]
+	 */
+	for (i = 2, j = 0; i < c; i = i + 2, j++) {
+		ret[j] = new_attr((char *)strlist + strlist->ldap_offsets[i],
+		    (char *)strlist + strlist->ldap_offsets[i + 1]);
+	}
+	return (ret);
+}
+
+static int
+get_admin_dn(ns_cred_t *credp, int *status, ns_ldap_error_t **errorp)
+{
+	void	**paramVal = NULL;
+	int	rc;
+
+	/* get bind DN for shadow update */
+	rc = __ns_ldap_getParam(NS_LDAP_ADMIN_BINDDN_P,
+	    &paramVal, errorp);
+	if (rc != NS_LDAP_SUCCESS)
+		return (rc);
+
+	if (paramVal == NULL || *paramVal == NULL) {
+		rc = NS_LDAP_CONFIG;
+		*status = NS_CONFIG_NOTALLOW;
+		if (paramVal != NULL)
+			(void) __ns_ldap_freeParam(&paramVal);
+		return (rc);
+	}
+	credp->cred.unix_cred.userID = strdup((char *)*paramVal);
+	(void) __ns_ldap_freeParam(&paramVal);
+	if (credp->cred.unix_cred.userID == NULL)
+		return (NS_LDAP_MEMORY);
+
+	return (NS_LDAP_SUCCESS);
+}
+
+/*
+ * admin_modify() does a privileged modify within the ldap_cachemgr daemon
+ * process using the admin DN/password configured with parameters
+ * NS_LDAP_ADMIN_BINDDN and NS_LDAP_ADMIN_BINDPASSWD. It will only
+ * be done if NS_LDAP_ENABLE_SHADOW_UPDATE is set to TRUE.
+ *
+ * The input ldap_call_t (*in) contains LDAP shadowAccount attributes to
+ * be modified. The data is a flatten ns_ldap_attr_t arrary stored in
+ * the strlist element of the input ldap_call_t.
+ * The output will be in LineBuf (*config_info), an ldap_admin_mod_result_t
+ * structure that contains error code, error status, and error message.
+ */
+static void
+admin_modify(LineBuf *config_info, ldap_call_t *in)
+{
+	int		rc = NS_LDAP_SUCCESS;
+	int		authstried = 0;
+	int		shadow_enabled = 0;
+	char		*dn = NULL;
+	char		**certpath = NULL;
+	char		**enable_shadow = NULL;
+	ns_auth_t	**app;
+	ns_auth_t	**authpp = NULL;
+	ns_auth_t	*authp = NULL;
+	ns_cred_t	*credp = NULL;
+	char		buffer[MAXERROR];
+	const int	rlen = offsetof(ldap_admin_mod_result_t, msg);
+	int		mlen = 0;
+	const int	msgmax = MAXERROR - rlen;
+	int		status = 0;
+	ucred_t		*uc = NULL;
+	ldap_strlist_t	*strlist;
+	ns_ldap_attr_t	**attrs = NULL;
+	ns_ldap_error_t *error = NULL;
+	ldap_admin_mod_result_t *result;
+
+	(void) memset((char *)config_info, 0, sizeof (LineBuf));
+
+	/* only root or an ALL privs user can do admin modify */
+	if (is_root_or_all_privs("ADMINMODIFY", &uc) == 0) {
+		mlen = snprintf(buffer, msgmax, "%s",
+		    gettext("shadow update by a non-root and no ALL privilege "
+		    "user not allowed"));
+		rc = NS_LDAP_CONFIG;
+		goto out;
+	}
+
+	/* check to see if shadow update is enabled */
+	rc = __ns_ldap_getParam(NS_LDAP_ENABLE_SHADOW_UPDATE_P,
+	    (void ***)&enable_shadow, &error);
+	if (rc != NS_LDAP_SUCCESS)
+		goto out;
+	if (enable_shadow != NULL && *enable_shadow != NULL) {
+		shadow_enabled = (*(int *)enable_shadow[0] ==
+		    NS_LDAP_ENABLE_SHADOW_UPDATE_TRUE);
+	}
+	if (enable_shadow != NULL)
+		(void) __ns_ldap_freeParam((void ***)&enable_shadow);
+	if (shadow_enabled == 0) {
+		rc = NS_LDAP_CONFIG;
+		status = NS_CONFIG_NOTALLOW;
+		mlen = snprintf(buffer, msgmax, "%s",
+		    gettext("shadow update not enabled"));
+		goto out;
+	}
+
+	/* convert attributes in string buffer into an ldap attribute array */
+	strlist = &in->ldap_u.strlist;
+	attrs = str2attrs(strlist, &dn);
+	if (attrs == NULL || *attrs == NULL || dn == NULL || *dn == '\0') {
+		rc = NS_LDAP_INVALID_PARAM;
+		goto out;
+	}
+
+	if ((credp = (ns_cred_t *)calloc(1, sizeof (ns_cred_t))) == NULL) {
+		rc = NS_LDAP_MEMORY;
+		goto out;
+	}
+
+	/* get host certificate path, if one is configured */
+	rc = __ns_ldap_getParam(NS_LDAP_HOST_CERTPATH_P,
+	    (void ***)&certpath, &error);
+	if (rc != NS_LDAP_SUCCESS)
+		goto out;
+	if (certpath != NULL && *certpath != NULL) {
+		credp->hostcertpath = strdup(*certpath);
+		if (credp->hostcertpath == NULL)
+			rc = NS_LDAP_MEMORY;
+	}
+	if (certpath != NULL)
+		(void) __ns_ldap_freeParam((void ***)&certpath);
+	if (rc != NS_LDAP_SUCCESS)
+		goto out;
+
+	/* Load the service specific authentication method */
+	rc = __ns_ldap_getServiceAuthMethods("passwd-cmd", &authpp,
+	    &error);
+	if (rc != NS_LDAP_SUCCESS) {
+		if (credp->hostcertpath != NULL)
+			free(credp->hostcertpath);
+		goto out;
+	}
+
+	/*
+	 * if authpp is null, there is no serviceAuthenticationMethod
+	 * try default authenticationMethod
+	 */
+	if (authpp == NULL) {
+		rc = __ns_ldap_getParam(NS_LDAP_AUTH_P, (void ***)&authpp,
+		    &error);
+		if (rc != NS_LDAP_SUCCESS)
+			goto out;
+	}
+
+	/*
+	 * if authpp is still null, then can not authenticate, syslog
+	 * error message and return error
+	 */
+	if (authpp == NULL) {
+		rc = NS_LDAP_CONFIG;
+		mlen = snprintf(buffer, msgmax, "%s",
+		    gettext("No legal LDAP authentication method configured"));
+		goto out;
+	}
+
+	/*
+	 * Walk the array and try all authentication methods in order except
+	 * for "none".
+	 */
+	for (app = authpp; *app; app++) {
+		authp = *app;
+		if (authp->type == NS_LDAP_AUTH_NONE)
+			continue;
+		authstried++;
+		credp->auth.type = authp->type;
+		credp->auth.tlstype = authp->tlstype;
+		credp->auth.saslmech = authp->saslmech;
+		credp->auth.saslopt = authp->saslopt;
+
+		/*
+		 * For GSSAPI, host credential will be used. No admin
+		 * DN is needed. For other authentication methods,
+		 * we need to set admin.
+		 */
+		if (credp->auth.saslmech != NS_LDAP_SASL_GSSAPI) {
+			if ((rc = get_admin_dn(credp, &status,
+			    &error)) != NS_LDAP_SUCCESS) {
+				if (error != NULL)
+					goto out;
+				if (status == NS_CONFIG_NOTALLOW) {
+					mlen = snprintf(buffer, msgmax, "%s",
+					    gettext("Admin bind DN not "
+					    "configured"));
+					goto out;
+				}
+			}
+		}
+
+		rc = __ns_ldap_repAttr(NS_ADMIN_SHADOW_UPDATE, dn,
+		    (const ns_ldap_attr_t * const *)attrs,
+		    credp, 0, &error);
+		if (rc == NS_LDAP_SUCCESS)
+			goto out;
+
+		/*
+		 * Other errors might need to be added to this list, for
+		 * the current supported mechanisms this is sufficient.
+		 */
+		if (rc == NS_LDAP_INTERNAL &&
+		    error->pwd_mgmt.status == NS_PASSWD_GOOD &&
+		    (error->status == LDAP_INAPPROPRIATE_AUTH ||
+		    error->status == LDAP_INVALID_CREDENTIALS))
+			goto out;
+
+		/*
+		 * If there is error related to password policy,
+		 * return it to caller.
+		 */
+		if (rc == NS_LDAP_INTERNAL &&
+		    error->pwd_mgmt.status != NS_PASSWD_GOOD) {
+			rc = NS_LDAP_CONFIG;
+			status = NS_CONFIG_NOTALLOW;
+			(void) __ns_ldap_freeError(&error);
+			mlen = snprintf(buffer, msgmax, "%s",
+			    gettext("update failed due to "
+			    "password policy on server (%d)"),
+			    error->pwd_mgmt.status);
+			goto out;
+		}
+
+		/* we don't really care about the error, just clean it up */
+		if (error)
+			(void) __ns_ldap_freeError(&error);
+	}
+	if (authstried == 0) {
+		rc = NS_LDAP_CONFIG;
+		mlen = snprintf(buffer, msgmax, "%s",
+		    gettext("No legal LDAP authentication method configured"));
+		goto out;
+	}
+
+	rc = NS_LDAP_OP_FAILED;
+
+out:
+	if (credp != NULL)
+		(void) __ns_ldap_freeCred(&credp);
+
+	if (authpp != NULL)
+		(void) __ns_ldap_freeParam((void ***)&authpp);
+
+	if (error != NULL) {
+		mlen = snprintf(buffer, msgmax, "%s", error->message);
+		status = error->status;
+		(void) __ns_ldap_freeError(&error);
+	}
+
+	if (attrs != NULL) {
+		int i;
+		for (i = 0; attrs[i]; i++) {
+			free(attrs[i]->attrvalue);
+			free(attrs[i]);
+		}
+	}
+
+	config_info->len = rlen + mlen + 1;
+	config_info->str = malloc(config_info->len);
+	if (config_info->str == NULL) {
+		config_info->len = 0;
+		return;
+	}
+	result = (ldap_admin_mod_result_t *)config_info->str;
+	result->ns_err = rc;
+	result->status = status;
+	if (mlen != 0) {
+		result->msg_size = mlen + 1;
+		(void) strcpy(config_info->str + rlen, buffer);
+	}
+}
+
+/*
+ * Check to see if the door client's euid is 0 or if it has ALL zone privilege.
+ * return - 0 No or error
+ *          1 Yes
+ */
+static int
+is_root_or_all_privs(char *dc_str, ucred_t **ucp)
+{
+	const priv_set_t *ps;	/* door client */
+	priv_set_t *zs;		/* zone */
+	int rc = 0;
+
+	*ucp = NULL;
+
+	/* no more to do if door client's euid is 0 */
+	if (is_root(0, dc_str, ucp) == 1) {
+		ucred_free(*ucp);
+		return (1);
+	}
+
+	/* error if couldn't get the ucred_t */
+	if (*ucp == NULL)
+		return (0);
+
+	if ((ps = ucred_getprivset(*ucp, PRIV_EFFECTIVE)) != NULL) {
+		zs = priv_str_to_set("zone", ",", NULL);
+		if (priv_isequalset(ps, zs))
+			rc = 1; /* has all zone privs */
+		else {
+			if (current_admin.debug_level >= DBG_CANT_FIND)
+				logit("%s call failed (no all zone privs): "
+				    "caller pid %ld, uid %u, euid %u "
+				    "(if uid or euid is %u, it may "
+				    "be unavailable)\n", dc_str,
+				    ucred_getpid(*ucp), ucred_getruid(*ucp),
+				    ucred_geteuid(*ucp), -1);
+		}
+		priv_freeset(zs);
+	}
+
+	ucred_free(*ucp);
+	return (rc);
+}
--- a/usr/src/cmd/passwd/passwd.c	Fri Feb 13 18:28:50 2009 -0800
+++ b/usr/src/cmd/passwd/passwd.c	Fri Feb 13 18:18:56 2009 -0800
@@ -2,9 +2,8 @@
  * CDDL HEADER START
  *
  * The contents of this file are subject to the terms of the
- * Common Development and Distribution License, Version 1.0 only
- * (the "License").  You may not use this file except in compliance
- * with the License.
+ * Common Development and Distribution License (the "License").
+ * You may not use this file except in compliance with the License.
  *
  * You can obtain a copy of the license at usr/src/OPENSOLARIS.LICENSE
  * or http://www.opensolaris.org/os/licensing.
@@ -20,7 +19,7 @@
  * CDDL HEADER END
  */
 /*
- * Copyright 2005 Sun Microsystems, Inc.  All rights reserved.
+ * Copyright 2009 Sun Microsystems, Inc.  All rights reserved.
  * Use is subject to license terms.
  */
 
@@ -30,8 +29,6 @@
 /*	Copyright (c) 1987, 1988 Microsoft Corporation	*/
 /*	  All Rights Reserved	*/
 
-#pragma ident	"%Z%%M%	%I%	%E% SMI"
-
 /*
  * passwd is a program whose sole purpose is to manage
  * the password file, map, or table. It allows system administrator
@@ -207,7 +204,7 @@
 
 static	int		get_namelist(pwu_repository_t, char ***, int *);
 static	int		get_namelist_files(char ***, int *);
-static	int		get_namelist_nisplus(char ***, int *);
+static	int		get_namelist_local(char ***, int *);
 static	int		get_attr(char *, pwu_repository_t *, attrlist **);
 static	void		display_attr(char *, attrlist *);
 static	void		free_attr(attrlist *);
@@ -318,7 +315,7 @@
 
 		if (num_user == 0) {
 			(void) fprintf(stderr, "%s: %s\n", prognamep,
-					gettext(MSG_FF));
+			    gettext(MSG_FF));
 			passwd_exit(FATAL);
 		}
 		i = 0;
@@ -737,11 +734,13 @@
 
 			/*
 			 * Delete the password - only privileged processes
-			 * can execute this for FILES
+			 * can execute this for FILES or LDAP
 			 */
-			if (IS_FILES(repository) == FALSE) {
+			if (IS_FILES(repository) == FALSE &&
+			    IS_LDAP(repository) == FALSE) {
 				(void) fprintf(stderr, gettext(
-				    "-d only applies to files repository\n"));
+				    "-d only applies to files "
+				    "or ldap repository\n"));
 				rusage();	/* exit */
 				retval = BADSYN;
 				return (FAIL);
@@ -767,9 +766,11 @@
 				repository = __REPFILES;
 
 			if (IS_FILES(repository) == FALSE &&
+			    IS_LDAP(repository) == FALSE &&
 			    IS_NISPLUS(repository) == FALSE) {
 				(void) fprintf(stderr, gettext(
-			"-N only applies to files or nisplus repository\n"));
+				    "-N only applies to files, ldap or  "
+				    "nisplus repository\n"));
 				rusage();	/* exit */
 				retval = BADOPT;
 				return (FAIL);
@@ -777,10 +778,10 @@
 
 			/*
 			 * Only privileged processes can execute this
-			 * for FILES
+			 * for FILES or LDAP
 			 */
-			if (IS_FILES(repository) &&
-				((retval = ckuid()) != SUCCESS))
+			if ((IS_FILES(repository) || IS_LDAP(repository)) &&
+			    ((retval = ckuid()) != SUCCESS))
 				return (FAIL);
 			if (flag & (MUTEXFLAG|NONAGEFLAG)) {
 				rusage();	/* exit */
@@ -798,9 +799,11 @@
 				repository = __REPFILES;
 
 			if (IS_FILES(repository) == FALSE &&
+			    IS_LDAP(repository) == FALSE &&
 			    IS_NISPLUS(repository) == FALSE) {
 				(void) fprintf(stderr, gettext(
-			"-l only applies to files or nisplus repository\n"));
+				    "-l only applies to files, ldap or "
+				    "nisplus repository\n"));
 				rusage();	/* exit */
 				retval = BADOPT;
 				return (FAIL);
@@ -808,10 +811,10 @@
 
 			/*
 			 * Only privileged processes can execute this
-			 * for FILES
+			 * for FILES or LDAP
 			 */
-			if (IS_FILES(repository) &&
-				((retval = ckuid()) != SUCCESS))
+			if ((IS_FILES(repository) || IS_LDAP(repository)) &&
+			    ((retval = ckuid()) != SUCCESS))
 				return (FAIL);
 			if (flag & (MUTEXFLAG|NONAGEFLAG)) {
 				rusage();	/* exit */
@@ -829,9 +832,11 @@
 				repository = __REPFILES;
 
 			if (IS_FILES(repository) == FALSE &&
+			    IS_LDAP(repository) == FALSE &&
 			    IS_NISPLUS(repository) == FALSE) {
 				(void) fprintf(stderr, gettext(
-			"-u only applies to files or nisplus repository\n"));
+				    "-u only applies to files, ldap or "
+				    "nisplus repository\n"));
 				rusage();	/* exit */
 				retval = BADOPT;
 				return (FAIL);
@@ -839,10 +844,10 @@
 
 			/*
 			 * Only privileged processes can execute this
-			 * for FILES
+			 * for FILES or LDAP
 			 */
-			if (IS_FILES(repository) &&
-				((retval = ckuid()) != SUCCESS))
+			if ((IS_FILES(repository) || IS_LDAP(repository)) &&
+			    ((retval = ckuid()) != SUCCESS))
 				return (FAIL);
 			if (flag & (MUTEXFLAG|NONAGEFLAG)) {
 				rusage();	/* exit */
@@ -861,9 +866,11 @@
 				repository = __REPFILES;
 
 			if (IS_FILES(repository) == FALSE &&
+			    IS_LDAP(repository) == FALSE &&
 			    IS_NISPLUS(repository) == FALSE) {
 				(void) fprintf(stderr, gettext(
-			"-x only applies to files or nisplus repository\n"));
+				    "-x only applies to files, ldap or "
+				    "nisplus repository\n"));
 				rusage();	/* exit */
 				retval = BADSYN;
 				return (FAIL);
@@ -871,9 +878,10 @@
 
 			/*
 			 * Only privileged process can execute this
-			 * for FILES
+			 * for FILES or LDAP
 			 */
-			if (IS_FILES(repository) && (ckuid() != SUCCESS)) {
+			if ((IS_FILES(repository) || IS_LDAP(repository)) &&
+			    (ckuid() != SUCCESS)) {
 				retval = NOPERM;
 				return (FAIL);
 			}
@@ -886,7 +894,7 @@
 			    (maxdate = strtol(optarg, &char_p, 10)) < -1 ||
 			    *char_p != '\0') {
 				(void) fprintf(stderr, "%s: %s -x\n",
-					prognamep, gettext(MSG_NV));
+				    prognamep, gettext(MSG_NV));
 				retval = BADSYN;
 				return (FAIL);
 			}
@@ -900,9 +908,11 @@
 				repository = __REPFILES;
 
 			if (IS_FILES(repository) == FALSE &&
+			    IS_LDAP(repository) == FALSE &&
 			    IS_NISPLUS(repository) == FALSE) {
 				(void) fprintf(stderr, gettext(
-			"-n only applies to files or nisplus repository\n"));
+				    "-n only applies to files, ldap or "
+				    "nisplus repository\n"));
 				rusage();	/* exit */
 				retval = BADSYN;
 				return (FAIL);
@@ -910,10 +920,10 @@
 
 			/*
 			 * Only privileged process can execute this
-			 * for FILES
+			 * for FILES or LDAP
 			 */
-			if (IS_FILES(repository) &&
-				((retval = ckuid()) != SUCCESS))
+			if ((IS_FILES(repository) || IS_LDAP(repository)) &&
+			    ((retval = ckuid()) != SUCCESS))
 				return (FAIL);
 			if (flag & (SAFLAG|NFLAG|NONAGEFLAG)) {
 				retval = BADOPT;
@@ -924,7 +934,7 @@
 			    (strtol(optarg, &char_p, 10)) < 0 ||
 			    *char_p != '\0') {
 				(void) fprintf(stderr, "%s: %s -n\n",
-					prognamep, gettext(MSG_NV));
+				    prognamep, gettext(MSG_NV));
 				retval = BADSYN;
 				return (FAIL);
 			}
@@ -938,9 +948,11 @@
 				repository = __REPFILES;
 
 			if (IS_FILES(repository) == FALSE &&
+			    IS_LDAP(repository) == FALSE &&
 			    IS_NISPLUS(repository) == FALSE) {
 				(void) fprintf(stderr, gettext(
-			"-w only applies to files or nisplus repository\n"));
+				    "-w only applies to files, ldap or "
+				    "nisplus repository\n"));
 				rusage();	/* exit */
 				retval = BADSYN;
 				return (FAIL);
@@ -948,9 +960,10 @@
 
 			/*
 			 * Only privileged process can execute this
-			 * for FILES
+			 * for FILES or LDAP
 			 */
-			if (IS_FILES(repository) && (ckuid() != SUCCESS)) {
+			if ((IS_FILES(repository) || IS_LDAP(repository)) &&
+			    (ckuid() != SUCCESS)) {
 				retval = NOPERM;
 				return (FAIL);
 			}
@@ -963,7 +976,7 @@
 			    (strtol(optarg, &char_p, 10)) < 0 ||
 			    *char_p != '\0') {
 				(void) fprintf(stderr, "%s: %s -w\n",
-					prognamep, gettext(MSG_NV));
+				    prognamep, gettext(MSG_NV));
 				retval = BADSYN;
 				return (FAIL);
 			}
@@ -979,9 +992,11 @@
 
 			/* display password attributes */
 			if (IS_FILES(repository) == FALSE &&
+			    IS_LDAP(repository) == FALSE &&
 			    IS_NISPLUS(repository) == FALSE) {
 				(void) fprintf(stderr, gettext(
-			"-s only applies to files or nisplus repository\n"));
+				    "-s only applies to files, ldap or "
+				    "nisplus repository\n"));
 				rusage();	/* exit */
 				retval = BADSYN;
 				return (FAIL);
@@ -989,10 +1004,10 @@
 
 			/*
 			 * Only privileged process can execute this
-			 * for FILES
+			 * for FILES or LDAP
 			 */
-			if (IS_FILES(repository) &&
-				((retval = ckuid()) != SUCCESS))
+			if ((IS_FILES(repository) || IS_LDAP(repository)) &&
+			    ((retval = ckuid()) != SUCCESS))
 				return (FAIL);
 			if (flag && (flag != AFLAG)) {
 				retval = BADOPT;
@@ -1008,9 +1023,11 @@
 				repository = __REPFILES;
 
 			if (IS_FILES(repository) == FALSE &&
+			    IS_LDAP(repository) == FALSE &&
 			    IS_NISPLUS(repository) == FALSE) {
 				(void) fprintf(stderr, gettext(
-			"-a only applies to files or nisplus repository\n"));
+				    "-a only applies to files, ldap or "
+				    "nisplus repository\n"));
 				rusage();	/* exit */
 				retval = BADSYN;
 				return (FAIL);
@@ -1018,10 +1035,10 @@
 
 			/*
 			 * Only privileged process can execute this
-			 * for FILES
+			 * for FILES or LDAP
 			 */
-			if (IS_FILES(repository) &&
-				((retval = ckuid()) != SUCCESS))
+			if ((IS_FILES(repository) || IS_LDAP(repository)) &&
+			    ((retval = ckuid()) != SUCCESS))
 				return (FAIL);
 			if (flag && (flag != SFLAG)) {
 				retval = BADOPT;
@@ -1037,9 +1054,11 @@
 				repository = __REPFILES;
 
 			if (IS_FILES(repository) == FALSE &&
+			    IS_LDAP(repository) == FALSE &&
 			    IS_NISPLUS(repository) == FALSE) {
 				(void) fprintf(stderr, gettext(
-			"-f only applies to files or nisplus repository\n"));
+				    "-f only applies to files, ldap or "
+				    "nisplus repository\n"));
 				rusage();	/* exit */
 				retval = BADSYN;
 				return (FAIL);
@@ -1047,10 +1066,10 @@
 
 			/*
 			 * Only privileged process can execute this
-			 * for FILES
+			 * for FILES or LDAP
 			 */
-			if (IS_FILES(repository) &&
-				((retval = ckuid()) != SUCCESS))
+			if ((IS_FILES(repository) || IS_LDAP(repository)) &&
+			    ((retval = ckuid()) != SUCCESS))
 				return (FAIL);
 			if (flag & (SAFLAG|FFLAG|NONAGEFLAG)) {
 				retval = BADOPT;
@@ -1208,7 +1227,7 @@
 	 */
 	if ((maxdate == -1) && (flag & NFLAG)) {
 		(void) fprintf(stderr, "%s: %s -n\n",
-				prognamep, gettext(MSG_NV));
+		    prognamep, gettext(MSG_NV));
 		retval = BADOPT;
 		return (FAIL);
 	}
@@ -1400,47 +1419,49 @@
 }
 
 /*
- * get_namelist_nisplus
+ * get_namelist_local
  *
  */
 
 /*
- * Our private version of the switch frontend for getspent.  We want to
- * search just the nisplus sp file, so we want to bypass normal nsswitch.conf
- * based processing.  This implementation compatible with version 2 of the
- * name service switch.
+ * Our private version of the switch frontend for getspent.  We want
+ * to search just the nisplus or ldap sp file, so we want to bypass
+ * normal nsswitch.conf based processing.  This implementation
+ * compatible with version 2 of the name service switch.
  */
 #define	NSS_NISPLUS_ONLY	"nisplus"
+#define	NSS_LDAP_ONLY		"ldap"
 
 extern int str2spwd(const char *, int, void *, char *, int);
 
 static DEFINE_NSS_DB_ROOT(db_root);
 static DEFINE_NSS_GETENT(context);
 
+static char *local_config;
 static void
-_np_nss_initf_shadow(nss_db_params_t *p)
+_lc_nss_initf_shadow(nss_db_params_t *p)
 {
 	p->name	= NSS_DBNAM_SHADOW;
 	p->config_name    = NSS_DBNAM_PASSWD;	/* Use config for "passwd" */
-	p->default_config = NSS_NISPLUS_ONLY;   /* Use nisplus only */
+	p->default_config = local_config;   	/* Use ldap or nisplus only */
 	p->flags = NSS_USE_DEFAULT_CONFIG;
 }
 
 static void
-_np_setspent(void)
+_lc_setspent(void)
 {
-	nss_setent(&db_root, _np_nss_initf_shadow, &context);
+	nss_setent(&db_root, _lc_nss_initf_shadow, &context);
 }
 
 static void
-_np_endspent(void)
+_lc_endspent(void)
 {
-	nss_endent(&db_root, _np_nss_initf_shadow, &context);
+	nss_endent(&db_root, _lc_nss_initf_shadow, &context);
 	nss_delete(&db_root);
 }
 
 static struct spwd *
-_np_getspent_r(struct spwd *result, char *buffer, int buflen)
+_lc_getspent_r(struct spwd *result, char *buffer, int buflen)
 {
 	nss_XbyY_args_t arg;
 	char		*nam;
@@ -1450,11 +1471,11 @@
 	do {
 		NSS_XbyY_INIT(&arg, result, buffer, buflen, str2spwd);
 			/* No key to fill in */
-		(void) nss_getent(&db_root, _np_nss_initf_shadow, &context,
+		(void) nss_getent(&db_root, _lc_nss_initf_shadow, &context,
 		    &arg);
 	} while (arg.returnval != 0 &&
-			(nam = ((struct spwd *)arg.returnval)->sp_namp) != 0 &&
-			(*nam == '+' || *nam == '-'));
+	    (nam = ((struct spwd *)arg.returnval)->sp_namp) != 0 &&
+	    (*nam == '+' || *nam == '-'));
 
 	return (struct spwd *)NSS_XbyY_FINI(&arg);
 }
@@ -1462,17 +1483,17 @@
 static nss_XbyY_buf_t *buffer;
 
 static struct spwd *
-_np_getspent(void)
+_lc_getspent(void)
 {
 	nss_XbyY_buf_t	*b;
 
 	b = NSS_XbyY_ALLOC(&buffer, sizeof (struct spwd), NSS_BUFLEN_SHADOW);
 
-	return (b == 0 ? 0 : _np_getspent_r(b->result, b->buffer, b->buflen));
+	return (b == 0 ? 0 : _lc_getspent_r(b->result, b->buffer, b->buflen));
 }
 
 int
-get_namelist_nisplus(char ***namelist_p, int *num_user)
+get_namelist_local(char ***namelist_p, int *num_user)
 {
 	int nuser = 0;
 	int alloced = 100;
@@ -1483,22 +1504,22 @@
 	if ((nl = calloc(alloced, sizeof (*nl))) == NULL)
 		return (FMERR);
 
-	(void) _np_setspent();
-	while ((p = _np_getspent()) != NULL) {
+	(void) _lc_setspent();
+	while ((p = _lc_getspent()) != NULL) {
 		if ((nl[nuser] = strdup(p->sp_namp)) == NULL) {
-			_np_endspent();
+			_lc_endspent();
 			return (FMERR);
 		}
 		if (++nuser == alloced) {
 			alloced += 100;
 			nl = realloc(nl, alloced * (sizeof (*nl)));
 			if (nl == NULL) {
-				_np_endspent();
+				_lc_endspent();
 				return (FMERR);
 			}
 		}
 	}
-	(void) _np_endspent();
+	(void) _lc_endspent();
 	nl[nuser] = NULL;
 
 	*namelist_p = nl;
@@ -1510,9 +1531,13 @@
 int
 get_namelist(pwu_repository_t repository, char ***namelist, int *num_user)
 {
-	if (IS_NISPLUS(repository))
-		return (get_namelist_nisplus(namelist, num_user));
-	else if (IS_FILES(repository))
+	if (IS_LDAP(repository)) {
+		local_config = NSS_LDAP_ONLY;
+		return (get_namelist_local(namelist, num_user));
+	} else if (IS_NISPLUS(repository)) {
+		local_config = NSS_NISPLUS_ONLY;
+		return (get_namelist_local(namelist, num_user));
+	} else if (IS_FILES(repository))
 		return (get_namelist_files(namelist, num_user));
 
 	rusage();
@@ -1607,7 +1632,7 @@
 		return (PAM_CONV_ERR);
 
 	*response = (struct pam_response *)calloc(num_msg,
-						sizeof (struct pam_response));
+	    sizeof (struct pam_response));
 	if (*response == NULL)
 		return (PAM_BUF_ERR);
 
@@ -1644,7 +1669,7 @@
 				(void) fputs(m->msg, stdout);
 			}
 			r->resp = (char *)calloc(PAM_MAX_RESP_SIZE,
-							sizeof (char));
+			    sizeof (char));
 			if (r->resp == NULL) {
 				/* free responses */
 				r = *response;
@@ -1793,5 +1818,9 @@
 	    "[-w warn]\n");
 	MSG("\t\t[-x max] name\n");
 	MSG("\tpasswd -r ldap [-egh] [name]\n");
+	MSG("\tpasswd -r ldap -sa\n");
+	MSG("\tpasswd -r ldap -s [name]\n");
+	MSG("\tpasswd -r ldap [-l|-N|-u] [-f] [-n min] [-w warn] "
+	    "[-x max] name\n");
 #undef MSG
 }
--- a/usr/src/lib/libsldap/common/mapfile-vers	Fri Feb 13 18:28:50 2009 -0800
+++ b/usr/src/lib/libsldap/common/mapfile-vers	Fri Feb 13 18:18:56 2009 -0800
@@ -22,19 +22,6 @@
 # Copyright 2009 Sun Microsystems, Inc.  All rights reserved.
 # Use is subject to license terms.
 #
-
-#
-# MAPFILE HEADER START
-#
-# WARNING:  STOP NOW.  DO NOT MODIFY THIS FILE.
-# Object versioning must comply with the rules detailed in
-#
-#	usr/src/lib/README.mapfiles
-#
-# You should not be making modifications here until you've read the most current
-# copy of that file. If you need help, contact a gatekeeper for guidance.
-#
-# MAPFILE HEADER END
 #
 
 # There really should be only one SUNWprivate version.
@@ -42,22 +29,23 @@
 
 SUNWprivate_1.1 {
     global:
-	__ns_ldap_initStandalone;
+	__ns_ldap_cancelStandalone;
+	__ns_ldap_check_all_preq;
+	__ns_ldap_check_dns_preq;
+	__ns_ldap_check_gssapi_preq;
+	__ns_ldap_getAcctMgmt;
+	__ns_ldap_getAttrStruct;
 	__ns_ldap_getConnectionInfoFromDUA;
 	__ns_ldap_getRootDSE;
+	__ns_ldap_initAuth;
+	__ns_ldap_initStandalone;
+	__ns_ldap_is_shadow_update_enabled;
 	__ns_ldap_pingOfflineServers;
-	__ns_ldap_cancelStandalone;
-	__ns_ldap_initAuth;
-	__ns_ldap_getAcctMgmt;
-	__s_api_get_canonical_name;
-	__ns_ldap_getAttrStruct;
 	__ns_ldap_self_gssapi_config;
 	__ns_ldap_self_gssapi_only_set;
-	__ns_ldap_check_dns_preq;
-	__ns_ldap_check_gssapi_preq;
-	__ns_ldap_check_all_preq;
+	__s_api_get_canonical_name;
+	__s_api_hostname2ip;
 	__s_api_ip2hostname;
-	__s_api_hostname2ip;
 } SUNWprivate_1.0;
 
 SUNWprivate_1.0 {
--- a/usr/src/lib/libsldap/common/ns_cache_door.h	Fri Feb 13 18:28:50 2009 -0800
+++ b/usr/src/lib/libsldap/common/ns_cache_door.h	Fri Feb 13 18:18:56 2009 -0800
@@ -19,15 +19,13 @@
  * CDDL HEADER END
  */
 /*
- * Copyright 2008 Sun Microsystems, Inc.  All rights reserved.
+ * Copyright 2009 Sun Microsystems, Inc.  All rights reserved.
  * Use is subject to license terms.
  */
 
 #ifndef	_NS_CACHE_DOOR_H
 #define	_NS_CACHE_DOOR_H
 
-#pragma ident	"%Z%%M%	%I%	%E% SMI"
-
 /*
  * Definitions for client side of doors-based ldap caching
  */
@@ -96,6 +94,13 @@
 	char 		config_str[sizeof (int)]; /* real size is data_size */
 } ldap_config_out_t;
 
+typedef struct ldap_admin_mod_result {
+	uint32_t	ns_err;		/* ns_ldap error code */
+	uint32_t	status;		/* error status */
+	uint32_t	msg_size;	/* length of error message */
+	char 		msg[sizeof (int)]; /* real size is msg_size */
+} ldap_admin_mod_result_t;
+
 /*
  * structure returned by server for all calls
  */
@@ -116,6 +121,7 @@
 		ldap_strlist_t	strlist;
 		ldap_config_out_t config_str;
 		ldap_get_change_out_t changes;
+		ldap_admin_mod_result_t admin_result;
 	} ldap_u;
 
 } ldap_return_t;
@@ -187,6 +193,8 @@
 #define	GETCACHESTAT	24
 	/* Configuration change or server status change notification */
 #define	GETSTATUSCHANGE	25
+	/* perform admin modify via ldap_cachemgr */
+#define	ADMINMODIFY	26
 
 /*
  * GETLDAPSERVER request flags
--- a/usr/src/lib/libsldap/common/ns_config.c	Fri Feb 13 18:28:50 2009 -0800
+++ b/usr/src/lib/libsldap/common/ns_config.c	Fri Feb 13 18:18:56 2009 -0800
@@ -19,12 +19,10 @@
  * CDDL HEADER END
  */
 /*
- * Copyright 2008 Sun Microsystems, Inc.  All rights reserved.
+ * Copyright 2009 Sun Microsystems, Inc.  All rights reserved.
  * Use is subject to license terms.
  */
 
-#pragma ident	"%Z%%M%	%I%	%E% SMI"
-
 /*
  * libsldap - library side configuration components
  * Routines to manage the config structure
@@ -202,6 +200,12 @@
 	{ -1, NULL },
 };
 
+static ns_enum_map ns_shadow_update_enum[] = {
+	{ ENUM2INT(NS_LDAP_ENABLE_SHADOW_UPDATE_FALSE), "FALSE" },
+	{ ENUM2INT(NS_LDAP_ENABLE_SHADOW_UPDATE_TRUE), "TRUE" },
+	{ -1, NULL },
+};
+
 static int	ns_def_auth_v1[] = {
 	ENUM2INT(NS_LDAP_EA_NONE),
 	0
@@ -392,11 +396,31 @@
 		NULL,	/* not defined in the Profile */
 		{ CHARPTR, 0, NULL },
 		__s_val_binddn, NULL },
+
 	{"NS_LDAP_BINDPASSWD", NS_LDAP_BINDPASSWD_P,
 		CREDCONFIG,	CHARPTR,	TRUE,	NS_LDAP_V2,
 		NULL,	/* not defined in the Profile */
 		{ CHARPTR, 0, NULL },
 		__s_val_bindpw, NULL },
+
+	{"NS_LDAP_ENABLE_SHADOW_UPDATE", NS_LDAP_ENABLE_SHADOW_UPDATE_P,
+		CREDCONFIG,	INT,	TRUE,	NS_LDAP_V2,
+		NULL,	/* not defined in the Profile */
+		{ INT, 0, INT2VOIDPTR(NS_LDAP_ENABLE_SHADOW_UPDATE_FALSE) },
+		NULL, ns_shadow_update_enum },
+
+	{"NS_LDAP_ADMIN_BINDDN", NS_LDAP_ADMIN_BINDDN_P,
+		CREDCONFIG,	CHARPTR,	TRUE,	NS_LDAP_V2,
+		NULL,	/* not defined in the Profile */
+		{ CHARPTR, 0, NULL },
+		__s_val_binddn, NULL },
+
+	{"NS_LDAP_ADMIN_BINDPASSWD", NS_LDAP_ADMIN_BINDPASSWD_P,
+		CREDCONFIG,	CHARPTR,	TRUE,	NS_LDAP_V2,
+		NULL,	/* not defined in the Profile */
+		{ CHARPTR, 0, NULL },
+		__s_val_bindpw, NULL },
+
 	{"NS_LDAP_EXP", NS_LDAP_EXP_P,
 		SERVERCONFIG,	TIMET,		TRUE,	NS_LDAP_V2,
 		NULL,	/* initialized by code to time+NS_LDAP_CACHETTL */
@@ -601,6 +625,9 @@
 	case NS_LDAP_PREF_ONLY_P:
 		mapp = &ns_pref_enum[0];
 		break;
+	case NS_LDAP_ENABLE_SHADOW_UPDATE_P:
+		mapp = &ns_shadow_update_enum[0];
+		break;
 	case NS_LDAP_CREDENTIAL_LEVEL_P:
 		if (ptr->version == NS_LDAP_V1)
 			return (-1);
@@ -713,6 +740,21 @@
 	return ("Unknown SearchRef_t type specified");
 }
 
+char *
+__s_get_shadowupdate_name(enableShadowUpdate_t type)
+{
+	register ns_enum_map	*mapp;
+
+	mapp = &ns_shadow_update_enum[0];
+
+	for (; mapp->name != NULL; mapp++) {
+		if (type == INT2SHADOWUPDATENUM(mapp->value)) {
+			return (mapp->name);
+		}
+	}
+	return ("Unknown enableShadowUpdate_t type specified");
+}
+
 static char *
 __s_get_credlvl_name(ns_config_t *ptr, CredLevel_t type)
 {
@@ -1486,6 +1528,8 @@
 	case NS_LDAP_CERT_NICKNAME_P:
 	case NS_LDAP_BINDDN_P:
 	case NS_LDAP_BINDPASSWD_P:
+	case NS_LDAP_ADMIN_BINDDN_P:
+	case NS_LDAP_ADMIN_BINDPASSWD_P:
 	case NS_LDAP_DOMAIN_P:
 	case NS_LDAP_SEARCH_BASEDN_P:
 	case NS_LDAP_SEARCH_TIME_P:
@@ -1648,6 +1692,7 @@
 		case NS_LDAP_PREF_ONLY_P:
 		case NS_LDAP_SEARCH_REF_P:
 		case NS_LDAP_SEARCH_SCOPE_P:
+		case NS_LDAP_ENABLE_SHADOW_UPDATE_P:
 			i = __s_get_enum_value(ptr, cp, def->index);
 			if (i < 0) {
 				(void) snprintf(errstr, sizeof (errstr),
@@ -2615,7 +2660,8 @@
 	 *
 	 * Init NS_LDAP_EXP_P here when CACHETTL is updated
 	 */
-	if (type == NS_LDAP_BINDPASSWD_P) {
+	if (type == NS_LDAP_BINDPASSWD_P ||
+	    type == NS_LDAP_ADMIN_BINDPASSWD_P) {
 		cp = conf.ns_pc;
 		cp2 = evalue((char *)cp);
 		conf.ns_pc = cp2;
@@ -3219,6 +3265,11 @@
 			    __s_get_scope_name(cfg,
 			    (ScopeType_t)ptr->ns_i));
 			break;
+		case NS_LDAP_ENABLE_SHADOW_UPDATE_P:
+			(void) strlcat(buf,
+			    __s_get_shadowupdate_name(
+			    (enableShadowUpdate_t)ptr->ns_i), bufsz);
+			break;
 		default:
 			(void) snprintf(ibuf, sizeof (ibuf),
 			    "%d", ptr->ns_i);
@@ -3730,15 +3781,21 @@
 __s_val_binddn(ParamIndexType i, ns_default_config *def,
 		ns_param_t *param, char *errbuf)
 {
+	char *dntype;
+
 	if (param && param->ns_ptype == CHARPTR &&
-	    i == NS_LDAP_BINDDN_P &&
+	    (i == NS_LDAP_BINDDN_P || i == NS_LDAP_ADMIN_BINDDN_P) &&
 	    ((param->ns_pc == NULL) ||
 	    ((*(param->ns_pc) != '\0') &&
 	    (strchr(param->ns_pc, '=') != NULL)))) {
 		return (NS_SUCCESS);
 	}
+	if (i == NS_LDAP_BINDDN_P)
+		dntype = "proxy";
+	else
+		dntype = "update";
 	(void) snprintf(errbuf, MAXERROR,
-	    gettext("NULL or invalid proxy bind DN"));
+	    gettext("NULL or invalid %s bind DN"), dntype);
 	return (NS_PARSE_ERR);
 }
 
@@ -3751,14 +3808,20 @@
 __s_val_bindpw(ParamIndexType i, ns_default_config *def,
 		ns_param_t *param, char *errbuf)
 {
+	char *pwtype;
+
 	if (param && param->ns_ptype == CHARPTR &&
-	    i == NS_LDAP_BINDPASSWD_P &&
+	    (i == NS_LDAP_BINDPASSWD_P || i == NS_LDAP_ADMIN_BINDPASSWD_P) &&
 	    ((param->ns_pc == NULL) ||
 	    (*(param->ns_pc) != '\0'))) {
 		return (NS_SUCCESS);
 	}
+	if (i == NS_LDAP_BINDPASSWD_P)
+		pwtype = "proxy";
+	else
+		pwtype = "admin";
 	(void) snprintf(errbuf, MAXERROR,
-	    gettext("NULL proxy bind password"));
+	    gettext("NULL %s bind password"), pwtype);
 	return (NS_PARSE_ERR);
 }
 
--- a/usr/src/lib/libsldap/common/ns_confmgr.c	Fri Feb 13 18:28:50 2009 -0800
+++ b/usr/src/lib/libsldap/common/ns_confmgr.c	Fri Feb 13 18:18:56 2009 -0800
@@ -19,12 +19,10 @@
  * CDDL HEADER END
  */
 /*
- * Copyright 2008 Sun Microsystems, Inc.  All rights reserved.
+ * Copyright 2009 Sun Microsystems, Inc.  All rights reserved.
  * Use is subject to license terms.
  */
 
-#pragma ident	"%Z%%M%	%I%	%E% SMI"
-
 /* libsldap - cachemgr side configuration components */
 
 #include <stdio.h>
@@ -332,6 +330,15 @@
 					__s_api_destroy_config(configStruct);
 					return (NULL);
 				}
+			} else if (strcasecmp(attrVal,
+			    _PROFILE1_OBJECTCLASS) == 0) {
+				if (__ns_ldap_setParamValue(configStruct,
+				    NS_LDAP_FILE_VERSION_P,
+				    NS_LDAP_VERSION_1,
+				    errorp) != NS_LDAP_SUCCESS) {
+					__s_api_destroy_config(configStruct);
+					return (NULL);
+				}
 			}
 			continue;
 		}
@@ -490,6 +497,10 @@
 	}
 	(void) memset((char *)configinfo, 0, sizeof (LineBuf));
 	for (i = 0; i <= NS_LDAP_MAX_PIT_P; i++) {
+		/* the credential for shadow update is not to be exposed */
+		if (i == NS_LDAP_ADMIN_BINDDN_P ||
+		    i == NS_LDAP_ADMIN_BINDPASSWD_P)
+			continue;
 		str = __s_api_strValue(ptr, string, sizeof (string), i,
 		    NS_DOOR_FMT);
 		if (str == NULL)
@@ -628,10 +639,15 @@
 		if (str == NULL)
 			continue;
 		/*
-		 * don't dump binddn, bind password, or cert path as they
-		 * are not part of version 2 profiles
+		 * don't dump binddn, bind password, admin binddn, admin
+		 * bind password, enableShadowUpdate flag, or cert path
+		 * as they are not part of version 2 profiles
 		 */
-		if ((i != NS_LDAP_BINDDN_P) && (i != NS_LDAP_BINDPASSWD_P) &&
+		if ((i != NS_LDAP_BINDDN_P) &&
+		    (i != NS_LDAP_BINDPASSWD_P) &&
+		    (i != NS_LDAP_ADMIN_BINDDN_P) &&
+		    (i != NS_LDAP_ADMIN_BINDPASSWD_P) &&
+		    (i != NS_LDAP_ENABLE_SHADOW_UPDATE_P) &&
 		    (i != NS_LDAP_HOST_CERTPATH_P))
 			(void) fprintf(fp, "%s\n", str);
 		if (str != (char *)&string[0]) {
@@ -922,6 +938,7 @@
 		}
 	}
 	if (ptr->version != NS_LDAP_V1) {
+		ParamIndexType i;
 		if (curr_ptr->paramList[NS_LDAP_BINDDN_P].ns_ptype == CHARPTR) {
 			(void) __ns_ldap_setParamValue(ptr, NS_LDAP_BINDDN_P,
 			    curr_ptr->paramList[NS_LDAP_BINDDN_P].ns_pc,
@@ -934,6 +951,28 @@
 			    curr_ptr->paramList[NS_LDAP_BINDPASSWD_P].ns_pc,
 			    &error);
 		}
+		i = NS_LDAP_ENABLE_SHADOW_UPDATE_P;
+		if (curr_ptr->paramList[i].ns_ptype == INT) {
+			char *val;
+			val = __s_get_shadowupdate_name(
+			    curr_ptr->paramList[i].ns_i);
+			(void) __ns_ldap_setParamValue(ptr, i, val, &error);
+		}
+		if (curr_ptr->paramList[NS_LDAP_ADMIN_BINDDN_P].ns_ptype ==
+		    CHARPTR) {
+			(void) __ns_ldap_setParamValue(ptr,
+			    NS_LDAP_ADMIN_BINDDN_P,
+			    curr_ptr->paramList[NS_LDAP_ADMIN_BINDDN_P].ns_pc,
+			    &error);
+		}
+		if (curr_ptr->paramList[NS_LDAP_ADMIN_BINDPASSWD_P].ns_ptype ==
+		    CHARPTR) {
+			(void) __ns_ldap_setParamValue(ptr,
+			    NS_LDAP_ADMIN_BINDPASSWD_P,
+			    curr_ptr->
+			    paramList[NS_LDAP_ADMIN_BINDPASSWD_P].ns_pc,
+			    &error);
+		}
 		if (curr_ptr->paramList[NS_LDAP_HOST_CERTPATH_P].ns_ptype ==
 		    CHARPTR) {
 			(void) __ns_ldap_setParamValue(ptr,
@@ -1065,6 +1104,11 @@
 		if ((i == NS_LDAP_CACHETTL_P) && (ptr->version == NS_LDAP_V1))
 			continue;
 
+		/* the credential for shadow update is not to be exposed */
+		if (i == NS_LDAP_ADMIN_BINDDN_P ||
+		    i == NS_LDAP_ADMIN_BINDPASSWD_P)
+			continue;
+
 		str = __s_api_strValue(ptr, string, BUFSIZ, i, NS_FILE_FMT);
 		if (str == NULL)
 			continue;
--- a/usr/src/lib/libsldap/common/ns_internal.h	Fri Feb 13 18:28:50 2009 -0800
+++ b/usr/src/lib/libsldap/common/ns_internal.h	Fri Feb 13 18:18:56 2009 -0800
@@ -19,7 +19,7 @@
  * CDDL HEADER END
  */
 /*
- * Copyright 2008 Sun Microsystems, Inc.  All rights reserved.
+ * Copyright 2009 Sun Microsystems, Inc.  All rights reserved.
  * Use is subject to license terms.
  */
 
@@ -27,8 +27,6 @@
 #ifndef	_NS_INTERNAL_H
 #define	_NS_INTERNAL_H
 
-#pragma ident	"%Z%%M%	%I%	%E% SMI"
-
 #ifdef __cplusplus
 extern "C" {
 #endif
@@ -118,6 +116,11 @@
 /* max rdn length in conversion routines used by __ns_ldap_addTypedEntry() */
 #define	RDNSIZE			256
 
+/*
+ * special service used by ldap_cachemgr to indicate a shadow update
+ * is to be done with the credential of the administrator identity
+ */
+#define	NS_ADMIN_SHADOW_UPDATE	"shadow__admin_update"
 
 /* Phase 1 profile information */
 #define	_PROFILE1_OBJECTCLASS	"SolarisNamingProfile"
@@ -316,6 +319,7 @@
 #define	INT2SECENUM(x)		((TlsType_t)(x))
 #define	INT2PREFONLYENUM(x)	((PrefOnly_t)(x))
 #define	INT2CREDLEVELENUM(x)	((CredLevel_t)(x))
+#define	INT2SHADOWUPDATENUM(x)	((enableShadowUpdate_t)(x))
 
 #define	INT2LDAPRETURN(x)	((ns_ldap_return_code)(x))
 #define	INT2CONFIGRETURN(x)	((ns_ldap_config_return_code)(x))
@@ -736,6 +740,7 @@
 char *__s_get_scope_name(ns_config_t *ptr, ScopeType_t type);
 char *__s_get_pref_name(PrefOnly_t type);
 char *__s_get_searchref_name(ns_config_t *ptr, SearchRef_t type);
+char *__s_get_shadowupdate_name(enableShadowUpdate_t type);
 char *__s_get_hostcertpath(void);
 void __s_api_free_sessionPool();
 int __s_api_requestServer(const char *request, const char *server,
--- a/usr/src/lib/libsldap/common/ns_sldap.h	Fri Feb 13 18:28:50 2009 -0800
+++ b/usr/src/lib/libsldap/common/ns_sldap.h	Fri Feb 13 18:18:56 2009 -0800
@@ -19,7 +19,7 @@
  * CDDL HEADER END
  */
 /*
- * Copyright 2008 Sun Microsystems, Inc.  All rights reserved.
+ * Copyright 2009 Sun Microsystems, Inc.  All rights reserved.
  * Use is subject to license terms.
  */
 
@@ -84,6 +84,13 @@
 #define	NS_LDAP_NOT_CVT_DN	0x2000
 
 /*
+ * NS_LDAP_UPDATE_SHADOW is for a privileged caller of the
+ * __ns_ldap_repAttr() to update the shadow database on the
+ * LDAP server.
+ */
+#define	NS_LDAP_UPDATE_SHADOW	0x4000
+
+/*
  * Authentication Information
  */
 typedef enum CredLevel {
@@ -126,6 +133,11 @@
 	NS_LDAP_PREF_TRUE	= 1
 } PrefOnly_t;
 
+typedef enum enableShadowUpdate {
+	NS_LDAP_ENABLE_SHADOW_UPDATE_FALSE	= 0,
+	NS_LDAP_ENABLE_SHADOW_UPDATE_TRUE	= 1
+} enableShadowUpdate_t;
+
 typedef struct UnixCred {
 	char	*userID;	/* Unix ID number */
 	char	*passwd;	/* password */
@@ -199,12 +211,15 @@
 	NS_LDAP_SERVICE_AUTH_METHOD_P	= 25,
 	NS_LDAP_SERVICE_CRED_LEVEL_P	= 26,
 	NS_LDAP_HOST_CERTPATH_P		= 27,
+	NS_LDAP_ENABLE_SHADOW_UPDATE_P	= 28,
+	NS_LDAP_ADMIN_BINDDN_P		= 29,
+	NS_LDAP_ADMIN_BINDPASSWD_P	= 30,
 /*
  * The following entry (max ParamIndexType) is an internal
  * placeholder.  It must be the last (and highest value)
  * entry in this eNum.  Please update accordingly.
  */
-	NS_LDAP_MAX_PIT_P		= 28
+	NS_LDAP_MAX_PIT_P		= 31
 
 } ParamIndexType;
 
@@ -484,6 +499,11 @@
 	char		*mappedOC;	/* mapped objectclass */
 } ns_ldap_objectclass_map_t;
 
+/*
+ * Value of the userPassword attribute representing NO Unix password
+ */
+#define	NS_LDAP_NO_UNIX_PASSWORD	"<NO UNIX PASSWORD>"
+
 /* Opaque handle for batch API */
 typedef struct ns_ldap_list_batch ns_ldap_list_batch_t;
 
@@ -872,6 +892,9 @@
 int __ns_ldap_getAcctMgmt(
 	const char *user,
 	AcctUsableResponse_t *acctResp);
+
+boolean_t __ns_ldap_is_shadow_update_enabled();
+
 void
 __ns_ldap_self_gssapi_only_set(
 	int flag);
--- a/usr/src/lib/libsldap/common/ns_writes.c	Fri Feb 13 18:28:50 2009 -0800
+++ b/usr/src/lib/libsldap/common/ns_writes.c	Fri Feb 13 18:18:56 2009 -0800
@@ -19,12 +19,10 @@
  * CDDL HEADER END
  */
 /*
- * Copyright 2008 Sun Microsystems, Inc.  All rights reserved.
+ * Copyright 2009 Sun Microsystems, Inc.  All rights reserved.
  * Use is subject to license terms.
  */
 
-#pragma ident	"%Z%%M%	%I%	%E% SMI"
-
 #include <stdio.h>
 #include <sys/types.h>
 #include <stdlib.h>
@@ -38,10 +36,13 @@
 #include <lber.h>
 #include <ldap.h>
 #include <syslog.h>
+#include <stddef.h>
+#include <sys/mman.h>
 
 #include "ns_sldap.h"
 #include "ns_internal.h"
 #include "ns_connmgmt.h"
+#include "ns_cache_door.h"
 
 /* Additional headers for addTypedEntry Conversion routines */
 #include <pwd.h>
@@ -60,7 +61,8 @@
 #include <sys/tsol/tndb.h>
 #include <tsol/label.h>
 
-
+static int send_to_cachemgr(const char *,
+    ns_ldap_attr_t **, ns_ldap_error_t **);
 /*
  * If the rdn is a mapped attr:
  * 	return NS_LDAP_SUCCESS and a new_dn.
@@ -1038,8 +1040,8 @@
 				errmsg = NULL;
 			}
 
-			(void) sprintf(errstr,
-			    gettext(ldap_err2string(Errno)));
+			(void) snprintf(errstr, sizeof (errstr),
+			    "%s", ldap_err2string(Errno));
 			err = strdup(errstr);
 			if (pwd_status != NS_PASSWD_GOOD) {
 				MKERROR_PWD_MGMT(*errorp, Errno, err,
@@ -1157,6 +1159,89 @@
 	return (rc);
 }
 
+/* Retrieve the admin bind password from the configuration, if allowed. */
+static int
+get_admin_passwd(ns_cred_t *cred, ns_ldap_error_t **errorp)
+{
+	void	**paramVal = NULL;
+	int	rc, ldaprc;
+	char	*modparamVal = NULL;
+
+	/*
+	 * For GSSAPI/Kerberos, host credential is used, no need to get
+	 * admin bind password
+	 */
+	if (cred->auth.saslmech == NS_LDAP_SASL_GSSAPI)
+		return (NS_LDAP_SUCCESS);
+
+	/*
+	 * Retrieve admin bind password.
+	 * The admin bind password is available
+	 * only in the ldap_cachemgr process as
+	 * they are not exposed outside of that
+	 * process.
+	 */
+	paramVal = NULL;
+	if ((ldaprc = __ns_ldap_getParam(NS_LDAP_ADMIN_BINDPASSWD_P,
+	    &paramVal, errorp)) != NS_LDAP_SUCCESS)
+		return (ldaprc);
+	if (paramVal == NULL || *paramVal == NULL) {
+		rc = NS_LDAP_CONFIG;
+		*errorp = __s_api_make_error(NS_CONFIG_NODEFAULT,
+		    gettext("Admin bind password not configured"));
+		if (*errorp == NULL)
+			rc = NS_LDAP_MEMORY;
+		return (rc);
+	}
+	modparamVal = dvalue((char *)*paramVal);
+	(void) memset(*paramVal, 0, strlen((char *)*paramVal));
+	(void) __ns_ldap_freeParam(&paramVal);
+	if (modparamVal == NULL || *((char *)modparamVal) == '\0') {
+		if (modparamVal != NULL)
+			free(modparamVal);
+		rc = NS_LDAP_CONFIG;
+		*errorp = __s_api_make_error(NS_CONFIG_SYNTAX,
+		    gettext("bind password not valid"));
+		if (*errorp == NULL)
+			rc = NS_LDAP_MEMORY;
+		return (rc);
+	}
+
+	cred->cred.unix_cred.passwd = modparamVal;
+	return (NS_LDAP_SUCCESS);
+}
+
+boolean_t
+__ns_ldap_is_shadow_update_enabled() {
+
+	int **enable_shadow = NULL;
+
+	if (__ns_ldap_getParam(NS_LDAP_ENABLE_SHADOW_UPDATE_P,
+	    (void ***)&enable_shadow, NULL) != NS_LDAP_SUCCESS) {
+		return (B_FALSE);
+	}
+	if ((enable_shadow != NULL && *enable_shadow != NULL) &&
+	    (*enable_shadow[0] == NS_LDAP_ENABLE_SHADOW_UPDATE_TRUE)) {
+		(void) __ns_ldap_freeParam((void ***)&enable_shadow);
+		return (B_TRUE);
+	}
+	if (enable_shadow != NULL)
+		(void) __ns_ldap_freeParam((void ***)&enable_shadow);
+	return (B_FALSE);
+}
+
+/*
+ * __ns_ldap_repAttr modifies ldap attributes of the 'dn' entry stored
+ * on the LDAP server. 'service' indicates the type of database entries
+ * to modify. When the Native LDAP client is configured with 'shadow update
+ * enabled', Shadowshadow(4) entries can only be modified by privileged users.
+ * Such users use the NS_LDAP_UPDATE_SHADOW flag to indicate the call is
+ * for such a shadow(4) update, which would be forwarded to ldap_cachemgr
+ * for performing the LDAP modify operation. ldap_cachemgr would call
+ * this function again and use the special service NS_ADMIN_SHADOW_UPDATE
+ * to identify itself, so that admin credential would be obtained and
+ * the actual LDAP modify operation be done.
+ */
 /*ARGSUSED*/
 int
 __ns_ldap_repAttr(
@@ -1169,6 +1254,8 @@
 {
 	LDAPMod		**mods;
 	int		rc = 0;
+	boolean_t	priv;
+	boolean_t	shadow_update_enabled = B_FALSE;
 
 #ifdef DEBUG
 	(void) fprintf(stderr, "__ns_ldap_repAttr START\n");
@@ -1176,13 +1263,59 @@
 	*errorp = NULL;
 
 	/* Sanity check */
-	if ((attr == NULL) || (*attr == NULL) ||
-	    (dn == NULL) || (cred == NULL))
+	if (attr == NULL || *attr == NULL || dn == NULL)
 		return (NS_LDAP_INVALID_PARAM);
+
+	/* Privileged shadow modify? */
+	if ((flags & NS_LDAP_UPDATE_SHADOW) != 0 &&
+	    strcmp(service, "shadow") == 0) {
+
+		/* Shadow update enabled ? If not, error out */
+		shadow_update_enabled = __ns_ldap_is_shadow_update_enabled();
+		if (!shadow_update_enabled) {
+			*errorp = __s_api_make_error(NS_CONFIG_NOTALLOW,
+			    gettext("Shadow Update is not enabled"));
+			return (NS_LDAP_CONFIG);
+		}
+
+		/* privileged shadow modify requires euid 0 or all zone privs */
+		priv = (geteuid() == 0);
+		if (!priv) {
+			priv_set_t *ps = priv_allocset();	/* caller */
+			priv_set_t *zs;				/* zone */
+
+			(void) getppriv(PRIV_EFFECTIVE, ps);
+			zs = priv_str_to_set("zone", ",", NULL);
+			priv = priv_isequalset(ps, zs);
+			priv_freeset(ps);
+			priv_freeset(zs);
+		}
+		if (!priv)
+			return (NS_LDAP_OP_FAILED);
+
+		rc = send_to_cachemgr(dn, (ns_ldap_attr_t **)attr, errorp);
+		return (rc);
+	}
+
+	if (cred == NULL)
+		return (NS_LDAP_INVALID_PARAM);
+
+	/*
+	 * If service is NS_ADMIN_SHADOW_UPDATE, the caller should be
+	 * ldap_cachemgr. We need to get the admin cred to do work.
+	 * If the caller is not ldap_cachemgr, but use the service
+	 * NS_ADMIN_SHADOW_UPDATE, get_admin_passwd() will fail,
+	 * as the admin cred is not available to the caller.
+	 */
+	if (strcmp(service, NS_ADMIN_SHADOW_UPDATE) == 0) {
+		if ((rc = get_admin_passwd((ns_cred_t *)cred, errorp)) !=
+		    NS_LDAP_SUCCESS)
+			return (rc);
+	}
+
 	mods = __s_api_makeModList(service, attr, LDAP_MOD_REPLACE, flags);
-	if (mods == NULL) {
+	if (mods == NULL)
 		return (NS_LDAP_MEMORY);
-	}
 
 	rc = write_state_machine(LDAP_REQ_MODIFY,
 	    (char *)dn, mods, cred, flags, errorp);
@@ -1191,7 +1324,6 @@
 	return (rc);
 }
 
-
 /*ARGSUSED*/
 int
 __ns_ldap_addEntry(
@@ -3793,3 +3925,157 @@
 	(void) __ns_ldap_freeParam(&param);
 	return (NS_LDAP_SUCCESS);
 }
+
+/*
+ * Flatten the input ns_ldap_attr_t list, 'attr', and convert it into an
+ * ldap_strlist_t structure in buffer 'buf', to be used by ldap_cachemgr.
+ * The output contains a count, a list of offsets, which show where the
+ * corresponding copied attribute type and attribute value are located.
+ * For example, for dn=aaaa, userpassword=bbbb, shadowlastchange=cccc,
+ * the output is the ldap_strlist_t structure with: ldap_count = 6,
+ * (buf + ldap_offsets[0]) -> "dn"
+ * (buf + ldap_offsets[1]) -> "aaaa"
+ * (buf + ldap_offsets[2]) -> "userPassword"
+ * (buf + ldap_offsets[3]) -> "bbbb"
+ * (buf + ldap_offsets[4]) -> "shadowlastchange"
+ * (buf + ldap_offsets[5]) -> "cccc"
+ * and all the string data shown above copied into the buffer after
+ * the offset array. The total length of the data will be the return
+ * value, or -1 if error.
+ */
+static int
+attr2list(const char *dn, ns_ldap_attr_t **attr,
+    char *buf, int bufsize)
+{
+	int		c = 0;
+	char		*ap;
+	int		ao;
+	ldap_strlist_t	*al = (ldap_strlist_t *)buf;
+	ns_ldap_attr_t	*a = (ns_ldap_attr_t *)*attr;
+	ns_ldap_attr_t	**aptr = (ns_ldap_attr_t **)attr;
+
+	/* bufsize > strlen(dn) + strlen("dn") + 1 ('\0') */
+	if ((strlen(dn) + 2 + 1) >= bufsize)
+		return (-1);
+
+	/* count number of attributes */
+	while (*aptr++)
+		c++;
+	al->ldap_count = 2 + c * 2;
+	ao = sizeof (al->ldap_count) + sizeof (al->ldap_offsets[0]) *
+	    al->ldap_count;
+	if (ao > bufsize)
+		return (-1);
+	al->ldap_offsets[0] = ao;
+	ap = buf + ao;
+	ao += 3;
+
+	/* copy entry DN */
+	if (ao > bufsize)
+		return (-1);
+	(void) strlcpy(ap, "dn", bufsize);
+	ap += 3;
+
+	al->ldap_offsets[1] = ao;
+	ao += strlen(dn) + 1;
+	if (ao > bufsize)
+		return (-1);
+	(void) strlcpy(ap, dn, bufsize);
+	ap = buf + ao;
+
+	aptr = attr;
+	for (c = 2; c < al->ldap_count; c++, aptr++) {
+		a = *aptr;
+		if (a->attrname == NULL || a->attrvalue == NULL ||
+		    a->value_count != 1 || a->attrvalue[0] == NULL)
+			return (-1);
+		al->ldap_offsets[c] = ao;
+		ao += strlen(a->attrname) + 1;
+		if (ao > bufsize)
+			return (-1);
+		(void) strlcpy(ap, a->attrname, bufsize);
+		ap = buf + ao;
+
+		c++;
+		al->ldap_offsets[c] = ao;
+		ao += strlen(a->attrvalue[0]) + 1;
+		(void) strlcpy(ap, a->attrvalue[0], bufsize);
+		ap = buf + ao;
+	};
+
+	return (ao);
+}
+
+/*
+ * Send a modify request to the ldap_cachemgr daemon
+ * which will use the admin credential to perform the
+ * operation.
+ */
+
+static int
+send_to_cachemgr(
+	const char *dn,
+	ns_ldap_attr_t **attr,
+	ns_ldap_error_t **errorp)
+{
+	union {
+		ldap_data_t	s_d;
+		char		s_b[DOORBUFFERSIZE];
+	} space;
+
+	ldap_data_t		*sptr;
+	int			ndata;
+	int			adata;
+	int			len;
+	int			rc;
+	char			errstr[MAXERROR];
+	ldap_admin_mod_result_t	*admin_result;
+
+	*errorp = NULL;
+	(void) memset(space.s_b, 0, DOORBUFFERSIZE);
+	len = attr2list(dn, attr, (char *)&space.s_d.ldap_call.ldap_u.strlist,
+	    sizeof (space) - offsetof(ldap_return_t, ldap_u));
+	if (len <= 0)
+		return (NS_LDAP_INVALID_PARAM);
+
+	adata = sizeof (ldap_call_t) + len;
+	ndata = sizeof (space);
+	space.s_d.ldap_call.ldap_callnumber = ADMINMODIFY;
+	sptr = &space.s_d;
+
+	switch (__ns_ldap_trydoorcall(&sptr, &ndata, &adata)) {
+	case NS_CACHE_SUCCESS:
+		break;
+	case NS_CACHE_NOTFOUND:
+		(void) snprintf(errstr, sizeof (errstr),
+		    gettext("Door call ADMINMODIFY to "
+		    "ldap_cachemgr failed - error: %d"),
+		    space.s_d.ldap_ret.ldap_errno);
+		MKERROR(LOG_WARNING, *errorp, NS_CONFIG_CACHEMGR,
+		    strdup(errstr), NULL);
+		return (NS_LDAP_OP_FAILED);
+		break;
+	default:
+		return (NS_LDAP_OP_FAILED);
+	}
+
+	admin_result = &sptr->ldap_ret.ldap_u.admin_result;
+	if (admin_result->ns_err == NS_LDAP_SUCCESS)
+		rc = NS_LDAP_SUCCESS;
+	else {
+		rc = admin_result->ns_err;
+		if (admin_result->msg_size == 0)
+			*errorp = __s_api_make_error(admin_result->status,
+			    NULL);
+		else
+			*errorp = __s_api_make_error(admin_result->status,
+			    admin_result->msg);
+	}
+
+	/* clean up the door call */
+	if (sptr != &space.s_d) {
+		(void) munmap((char *)sptr, ndata);
+	}
+
+	return (rc);
+}
--- a/usr/src/lib/nsswitch/ldap/common/getspent.c	Fri Feb 13 18:28:50 2009 -0800
+++ b/usr/src/lib/nsswitch/ldap/common/getspent.c	Fri Feb 13 18:18:56 2009 -0800
@@ -19,11 +19,10 @@
  * CDDL HEADER END
  */
 /*
- * Copyright 2008 Sun Microsystems, Inc.  All rights reserved.
+ * Copyright 2009 Sun Microsystems, Inc.  All rights reserved.
  * Use is subject to license terms.
  */
 
-
 #include <shadow.h>
 #include <stdlib.h>
 #include "ldap_common.h"
@@ -31,6 +30,12 @@
 /* shadow attributes filters */
 #define	_S_UID			"uid"
 #define	_S_USERPASSWORD		"userpassword"
+#define	_S_LASTCHANGE		"shadowlastchange"
+#define	_S_MIN			"shadowmin"
+#define	_S_MAX			"shadowmax"
+#define	_S_WARNING		"shadowwarning"
+#define	_S_INACTIVE		"shadowinactive"
+#define	_S_EXPIRE		"shadowexpire"
 #define	_S_FLAG			"shadowflag"
 
 #define	_F_GETSPNAM		"(&(objectClass=shadowAccount)(uid=%s))"
@@ -39,6 +44,12 @@
 static const char *sp_attrs[] = {
 	_S_UID,
 	_S_USERPASSWORD,
+	_S_LASTCHANGE,
+	_S_MIN,
+	_S_MAX,
+	_S_WARNING,
+	_S_INACTIVE,
+	_S_EXPIRE,
 	_S_FLAG,
 	(char *)NULL
 };
@@ -59,11 +70,15 @@
 {
 	int		nss_result;
 	int		buflen = 0;
+	int		shadow_update_enabled;
 	unsigned long	len = 0L;
 	char		*tmp, *buffer = NULL;
 	char		*pw_passwd = NULL;
 	ns_ldap_result_t	*result = be->result;
-	char		**uid, **passwd, **flag, *flag_str;
+	char		**uid, **passwd, **last, **smin, **smax;
+	char		**warning, **inactive, **expire, **flag;
+	char		*last_str, *min_str, *max_str, *warning_str;
+	char		*inactive_str, *expire_str, *flag_str;
 
 	if (result == NULL)
 		return (NSS_STR_PARSE_PARSE);
@@ -94,11 +109,15 @@
 		goto result_spd2str;
 	} else {
 		if ((tmp = strstr(passwd[0], "{crypt}")) != NULL ||
-			(tmp = strstr(passwd[0], "{CRYPT}")) != NULL) {
+		    (tmp = strstr(passwd[0], "{CRYPT}")) != NULL) {
 			if (tmp != passwd[0])
 				pw_passwd = NOPWDRTR;
-			else
+			else {
 				pw_passwd = tmp + strlen("{crypt}");
+				if (strcmp(pw_passwd,
+				    NS_LDAP_NO_UNIX_PASSWORD) == 0)
+					*pw_passwd = '\0';
+			}
 		} else {
 		/* mark password as not retrievable */
 			pw_passwd = NOPWDRTR;
@@ -107,19 +126,66 @@
 	len += strlen(pw_passwd);
 
 	/*
-	 * Ignore the following password aging related attributes:
+	 * If shadow update is not enabled, ignore the following
+	 * password aging related attributes:
 	 * -- shadowlastchange
 	 * -- shadowmin
 	 * -- shadowmax
 	 * -- shadowwarning
 	 * -- shadowinactive
 	 * -- shadowexpire
-	 * This is because the LDAP naming service does not
-	 * really support the password aging fields defined
-	 * in the shadow structure. These fields, sp_lstchg,
+	 * When shadow update is not enabled, the LDAP naming
+	 * service does not support the password aging fields
+	 * defined in the shadow structure. These fields, sp_lstchg,
 	 * sp_min, sp_max, sp_warn, sp_inact, and sp_expire,
 	 * will be set to -1 by the front end marshaller.
 	 */
+
+	shadow_update_enabled = __ns_ldap_is_shadow_update_enabled();
+	if (shadow_update_enabled) {
+		last = __ns_ldap_getAttr(result->entry, _S_LASTCHANGE);
+		if (last == NULL || last[0] == NULL)
+			last_str = _NO_VALUE;
+		else
+			last_str = last[0];
+		len += strlen(last_str);
+
+		smin = __ns_ldap_getAttr(result->entry, _S_MIN);
+		if (smin == NULL || smin[0] == NULL)
+			min_str = _NO_VALUE;
+		else
+			min_str = smin[0];
+		len += strlen(min_str);
+
+		smax = __ns_ldap_getAttr(result->entry, _S_MAX);
+		if (smax == NULL || smax[0] == NULL)
+			max_str = _NO_VALUE;
+		else
+			max_str = smax[0];
+		len += strlen(max_str);
+
+		warning = __ns_ldap_getAttr(result->entry, _S_WARNING);
+		if (warning == NULL || warning[0] == NULL)
+			warning_str = _NO_VALUE;
+		else
+			warning_str = warning[0];
+		len += strlen(warning_str);
+
+		inactive = __ns_ldap_getAttr(result->entry, _S_INACTIVE);
+		if (inactive == NULL || inactive[0] == NULL)
+			inactive_str = _NO_VALUE;
+		else
+			inactive_str = inactive[0];
+		len += strlen(inactive_str);
+
+		expire = __ns_ldap_getAttr(result->entry, _S_EXPIRE);
+		if (expire == NULL || expire[0] == NULL)
+			expire_str = _NO_VALUE;
+		else
+			expire_str = expire[0];
+		len += strlen(expire_str);
+	}
+
 	flag = __ns_ldap_getAttr(result->entry, _S_FLAG);
 	if (flag == NULL || flag[0] == NULL)
 		flag_str = _NO_VALUE;
@@ -144,8 +210,14 @@
 	} else
 		buffer = argp->buf.buffer;
 
-	(void) snprintf(buffer, len, "%s:%s:::::::%s",
-			uid[0], pw_passwd, flag_str);
+	if (shadow_update_enabled) {
+		(void) snprintf(buffer, len, "%s:%s:%s:%s:%s:%s:%s:%s:%s",
+		    uid[0], pw_passwd, last_str, min_str, max_str, warning_str,
+		    inactive_str, expire_str, flag_str);
+	} else {
+		(void) snprintf(buffer, len, "%s:%s:::::::%s",
+		    uid[0], pw_passwd, flag_str);
+	}
 
 	/* The front end marhsaller doesn't need the trailing null */
 	if (argp->buf.result != NULL)
@@ -185,7 +257,7 @@
 		return ((nss_status_t)NSS_NOTFOUND);
 
 	return (_nss_ldap_lookup(be, argp, _SHADOW, searchfilter, NULL,
-		_merge_SSD_filter, userdata));
+	    _merge_SSD_filter, userdata));
 }
 
 static ldap_backend_op_t sp_ops[] = {
@@ -210,6 +282,6 @@
 {
 
 	return ((nss_backend_t *)_nss_ldap_constr(sp_ops,
-		sizeof (sp_ops)/sizeof (sp_ops[0]),
-		_SHADOW, sp_attrs, _nss_ldap_shadow2str));
+	    sizeof (sp_ops)/sizeof (sp_ops[0]),
+	    _SHADOW, sp_attrs, _nss_ldap_shadow2str));
 }
--- a/usr/src/lib/passwdutil/__failed_count.c	Fri Feb 13 18:28:50 2009 -0800
+++ b/usr/src/lib/passwdutil/__failed_count.c	Fri Feb 13 18:18:56 2009 -0800
@@ -19,12 +19,10 @@
  * CDDL HEADER END
  */
 /*
- * Copyright 2008 Sun Microsystems, Inc.  All rights reserved.
+ * Copyright 2009 Sun Microsystems, Inc.  All rights reserved.
  * Use is subject to license terms.
  */
 
-#pragma ident	"%Z%%M%	%I%	%E% SMI"
-
 #include <string.h>
 #include <syslog.h>
 #include "passwdutil.h"
@@ -35,23 +33,31 @@
 	int ret;
 	void *buf;
 	attrlist items[1];
-	repops_t *ops = rops[REP_FILES];
+	int repnum = name_to_int(repname);
+	repops_t *ops;
 
-	/* account locking only defined for files */
-	if (strcmp(repname, "files") != 0)
+	/* account locking only defined for files and ldap */
+	if ((repnum != REP_FILES) &&
+	    (repnum != REP_LDAP)) {
 		return (PWU_SUCCESS);
+	}
 
-	if ((ret = ops->lock()) != PWU_SUCCESS)
+	ops = rops[repnum];
+	if ((ops->lock != NULL) &&
+	    (ret = ops->lock()) != PWU_SUCCESS) {
 		return (ret);
+	}
 
 	items[0].type = ATTR_INCR_FAILED_LOGINS;
 	items[0].next = NULL;
-	if ((ret = ops->getpwnam(username, items, NULL, &buf)) != PWU_SUCCESS)
+	if ((ret = ops->getpwnam(username, items, NULL, &buf)) != PWU_SUCCESS) {
 		goto out;
+	}
 
 	/* We increment the failed count by one */
-	if ((ret = ops->update(items, NULL, buf)) != PWU_SUCCESS)
+	if ((ret = ops->update(items, NULL, buf)) != PWU_SUCCESS) {
 		goto out;
+	}
 
 	/* Did we just exceed "max_failures" ? */
 	if (items[0].data.val_i >= max_failures) {
@@ -69,7 +75,9 @@
 		ret = PWU_ACCOUNT_LOCKED;
 
 out:
-	ops->unlock();
+	if (ops->unlock != NULL) {
+		ops->unlock();
+	}
 
 	return (ret);
 }
@@ -84,14 +92,20 @@
 	int ret;
 	void *buf;
 	attrlist items[1];
-	repops_t *ops = rops[REP_FILES];
+	int repnum = name_to_int(repname);
+	repops_t *ops;
 
-	/* account locking only defined for files */
-	if (strcmp(repname, "files") != 0)
+	/* account locking only defined for files and ldap */
+	if ((repnum != REP_FILES) &&
+	    (repnum != REP_LDAP)) {
 		return (PWU_SUCCESS);
+	}
 
-	if ((ret = ops->lock()) != PWU_SUCCESS)
+	ops = rops[repnum];
+	if ((ops->lock != NULL) &&
+	    (ret = ops->lock()) != PWU_SUCCESS) {
 		return (ret);
+	}
 
 	items[0].type = ATTR_RST_FAILED_LOGINS;
 	items[0].next = NULL;
@@ -101,7 +115,9 @@
 		goto out;
 	ret = ops->putpwnam(username, NULL, NULL, NULL, buf);
 out:
-	ops->unlock();
+	if (ops->unlock != NULL) {
+		ops->unlock();
+	}
 
 	return (ret != PWU_SUCCESS ? ret : items[0].data.val_i);
 }
--- a/usr/src/lib/passwdutil/ldap_attr.c	Fri Feb 13 18:28:50 2009 -0800
+++ b/usr/src/lib/passwdutil/ldap_attr.c	Fri Feb 13 18:18:56 2009 -0800
@@ -2,9 +2,8 @@
  * CDDL HEADER START
  *
  * The contents of this file are subject to the terms of the
- * Common Development and Distribution License, Version 1.0 only
- * (the "License").  You may not use this file except in compliance
- * with the License.
+ * Common Development and Distribution License (the "License").
+ * You may not use this file except in compliance with the License.
  *
  * You can obtain a copy of the license at usr/src/OPENSOLARIS.LICENSE
  * or http://www.opensolaris.org/os/licensing.
@@ -20,17 +19,17 @@
  * CDDL HEADER END
  */
 /*
- * Copyright 2004 Sun Microsystems, Inc.  All rights reserved.
+ * Copyright 2009 Sun Microsystems, Inc.  All rights reserved.
  * Use is subject to license terms.
  */
 
-#pragma ident	"%Z%%M%	%I%	%E% SMI"
-
 #include <stdio.h>
 #include <errno.h>
 #include <stdlib.h>
 #include <string.h>
 #include <unistd.h>
+#include <macros.h>
+#include <priv.h>
 
 #include "ns_sldap.h"
 
@@ -45,6 +44,32 @@
 
 #include "utils.h"
 
+#define	MAX_INT_LEN 11	/* 10+1 %d buflen for words/ints [not longs] */
+
+#define	STRDUP_OR_RET(to, from) \
+	if ((to = strdup(from)) == NULL) \
+		return (PWU_NOMEM);
+
+#define	STRDUP_OR_ERR(to, from, err) \
+	if (((to) = strdup(from)) == NULL) \
+		(err) = PWU_NOMEM;
+
+#define	NUM_TO_STR(to, from) \
+	{ \
+		char nb[MAX_INT_LEN]; \
+		if (snprintf(nb, MAX_INT_LEN, "%d", (from)) >= MAX_INT_LEN) \
+			return (PWU_NOMEM); \
+		STRDUP_OR_RET(to, nb); \
+	}
+
+#define	NEW_ATTR(p, i, attr, val) \
+	{ \
+		p[i] = new_attr(attr, (val)); \
+		if (p[i] == NULL) \
+			return (PWU_NOMEM); \
+		i++; \
+	}
+
 int ldap_getattr(char *name, attrlist *item, pwu_repository_t *rep);
 int ldap_getpwnam(char *name, attrlist *items, pwu_repository_t *rep,
     void **buf);
@@ -73,9 +98,14 @@
  * structure used to keep state between get/update/put calls
  */
 typedef struct {
-	char *passwd;		/* encrypted password */
+	char *passwd;			/* encrypted password */
 	struct passwd *pwd;
-	ns_ldap_attr_t **attrs;
+	ns_ldap_attr_t **pattrs;	/* passwd attrs */
+	int npattrs;			/* max attrs */
+	struct spwd *spwd;
+	ns_ldap_attr_t **sattrs;	/* passwd attrs */
+	int nsattrs;			/* max attrs */
+	boolean_t shadow_update_enabled;	/* shadow update configured */
 } ldapbuf_t;
 
 /*
@@ -94,15 +124,70 @@
 #define	_PWD_HOMEDIRECTORY	"homedirectory"
 #define	_PWD_LOGINSHELL		"loginshell"
 
+#define	_PWD_MAX_ATTR		10	/* 9+NULL */
+
+/* shadow attributes filters */
+#define	_S_LASTCHANGE		"shadowlastchange"
+#define	_S_MIN			"shadowmin"
+#define	_S_MAX			"shadowmax"
+#define	_S_WARNING		"shadowwarning"
+#define	_S_INACTIVE		"shadowinactive"
+#define	_S_EXPIRE		"shadowexpire"
+#define	_S_FLAG			"shadowflag"
+
+#define	_S_MAX_ATTR		8	/* 7+NULL */
+
+/*
+ * Frees up an ldapbuf_t
+ */
+
+static void
+free_ldapbuf(ldapbuf_t *p)
+{
+	int i;
+
+	if (p == NULL)
+		return;
+	if (p->passwd) {
+		(void) memset(p->passwd, 0, strlen(p->passwd));
+		free(p->passwd);
+	}
+	if (p->pwd)
+		free_pwd(p->pwd);
+	if (p->spwd)
+		free_spwd(p->spwd);
+	if (p->pattrs) {
+		for (i = 0; i < p->npattrs; i++) {
+			if (p->pattrs[i] != NULL) {
+				free(p->pattrs[i]->attrvalue[0]);
+				free(p->pattrs[i]);
+			}
+		}
+		free(p->pattrs);
+	}
+	if (p->sattrs) {
+		for (i = 0; i < p->nsattrs; i++) {
+			if (p->sattrs[i] != NULL) {
+				free(p->sattrs[i]->attrvalue[0]);
+				free(p->sattrs[i]);
+			}
+		}
+		free(p->sattrs);
+	}
+}
+
 /*
  * int ldap_user_to_authenticate(user, rep, auth_user, privileged)
  *
- * We can't determine whether the user is "privileged" in the LDAP
- * sense. The operation should be attempted and will succeed if
- * the user had privileges.
- *
- * For our purposes, we say that the user is privileged if he/she
- * is attempting to change another user's password attributes.
+ * If the Shadow Update functionality is enabled, then we check to
+ * see if the caller has 0 as the euid or has all zone privs. If so,
+ * the caller would be able to modify shadow(4) data stored on the
+ * LDAP server. Otherwise, when LDAP Shadow Update is not enabled,
+ * we can't determine whether the user is "privileged" in the LDAP
+ * sense. The operation should be attempted and will succeed if the
+ * user had privileges. For our purposes, we say that the user is
+ * privileged if he/she is attempting to change another user's
+ * password attributes.
  */
 int
 ldap_user_to_authenticate(char *user, pwu_repository_t *rep,
@@ -121,11 +206,47 @@
 
 	uid = getuid();
 
+	/*
+	 * need equivalent of write access to /etc/shadow
+	 * the privilege escalation model is euid == 0 || all zone privs
+	 */
+	if (__ns_ldap_is_shadow_update_enabled()) {
+		boolean_t priv;
+
+		priv = (geteuid() == 0);
+		if (!priv) {
+			priv_set_t *ps = priv_allocset();	/* caller */
+			priv_set_t *zs;				/* zone */
+
+			(void) getppriv(PRIV_EFFECTIVE, ps);
+			zs = priv_str_to_set("zone", ",", NULL);
+			priv = priv_isequalset(ps, zs);
+			priv_freeset(ps);
+			priv_freeset(zs);
+		}
+		/*
+		 * priv can change anyone's password,
+		 * only root isn't prompted.
+		 */
+		*privileged = 0;	/* for proper prompting */
+		if (priv) {
+			if (uid == 0) {
+				*privileged = 1;
+				*auth_user = NULL;
+				return (res);
+			} else if (uid == pw->pw_uid) {
+				STRDUP_OR_ERR(*auth_user, user, res);
+				return (res);
+			}
+		}
+
+		return (PWU_DENIED);
+	}
+
 	if (uid == pw->pw_uid) {
-		/* changing out own, not privileged */
+		/* changing our own, not privileged */
 		*privileged = 0;
-		if ((*auth_user = strdup(user)) == NULL)
-			res = PWU_NOMEM;
+		STRDUP_OR_RET(*auth_user, user);
 	} else {
 		char pwd_buf[1024];
 		struct passwd pwr;
@@ -141,17 +262,15 @@
 			priviledged_uid = uid;
 		}
 		if (getpwuid_r(priviledged_uid, &pwr, pwd_buf,
-				sizeof (pwd_buf)) != NULL) {
-			if ((*auth_user = strdup(pwr.pw_name)) ==  NULL)
-				res = PWU_NOMEM;
+		    sizeof (pwd_buf)) != NULL) {
+			STRDUP_OR_ERR(*auth_user, pwr.pw_name, res);
 		} else {
 			/* hmm. can't find name of current user...??? */
 
-#define	MAX_UID_LEN 11	/* UID's larger than 2^32 won't fit... */
-			if ((*auth_user = malloc(MAX_UID_LEN)) == NULL) {
+			if ((*auth_user = malloc(MAX_INT_LEN)) == NULL) {
 				res = PWU_NOMEM;
 			} else {
-				(void) snprintf(*auth_user, MAX_UID_LEN, "%d",
+				(void) snprintf(*auth_user, MAX_INT_LEN, "%d",
 				    (int)uid);
 			}
 		}
@@ -169,70 +288,45 @@
 int
 ldap_getattr(char *name, attrlist *items, pwu_repository_t *rep)
 {
+	attrlist *w;
 	int res;
+	ldapbuf_t *ldapbuf;
 	struct passwd *pw = NULL;
 	struct spwd *spw = NULL;
-	attrlist *w;
-
-	int need_shadow = 0;	/* Need shadow info from LDAP server */
-	int need_normal = 0;	/* Need non-shadow info from LDAP server */
 
-	/* We need the "shadow" map for the password only */
-	for (w = items; w != NULL; w = w->next) {
-		if (w->type == ATTR_PASSWD ||
-			w->type == ATTR_PASSWD_SERVER_POLICY)
-			need_shadow = 1;
-		else
-			need_normal = 1;
-	}
+	res = ldap_getpwnam(name, items, rep, (void **)&ldapbuf);
+	if (res != PWU_SUCCESS)
+		return (res);
 
-	if (need_normal) {
-		res = dup_pw(&pw, getpwnam_from(name, rep, REP_LDAP));
-		if (res != PWU_SUCCESS)
-			goto out;
-	}
-
-	if (need_shadow) {
-		res = dup_spw(&spw, getspnam_from(name, rep, REP_LDAP));
-		if (res != PWU_SUCCESS) {
-			goto out;
-		}
-	}
+	pw = ldapbuf->pwd;
+	spw = ldapbuf->spwd;
 
 	for (w = items; res == PWU_SUCCESS && w != NULL; w = w->next) {
 		switch (w->type) {
 		case ATTR_NAME:
-			if ((w->data.val_s = strdup(pw->pw_name)) == NULL)
-				res = PWU_NOMEM;
+			STRDUP_OR_ERR(w->data.val_s, pw->pw_name, res);
 			break;
 		case ATTR_COMMENT:
-			if ((w->data.val_s = strdup(pw->pw_comment)) == NULL)
-				res = PWU_NOMEM;
+			STRDUP_OR_ERR(w->data.val_s, pw->pw_comment, res);
 			break;
 		case ATTR_GECOS:
-			if ((w->data.val_s = strdup(pw->pw_gecos)) == NULL)
-				res = PWU_NOMEM;
+			STRDUP_OR_ERR(w->data.val_s, pw->pw_gecos, res);
 			break;
 		case ATTR_HOMEDIR:
-			if ((w->data.val_s = strdup(pw->pw_dir)) == NULL)
-				res = PWU_NOMEM;
+			STRDUP_OR_ERR(w->data.val_s, pw->pw_dir, res);
 			break;
 		case ATTR_SHELL:
-			if ((w->data.val_s = strdup(pw->pw_shell)) == NULL)
-				res = PWU_NOMEM;
+			STRDUP_OR_ERR(w->data.val_s, pw->pw_shell, res);
 			break;
 		case ATTR_PASSWD:
 		case ATTR_PASSWD_SERVER_POLICY:
-			if ((w->data.val_s = strdup(spw->sp_pwdp)) == NULL)
-				res = PWU_NOMEM;
+			STRDUP_OR_ERR(w->data.val_s, spw->sp_pwdp, res);
 			break;
 		case ATTR_AGE:
-			if ((w->data.val_s = strdup(pw->pw_age)) == NULL)
-				res = PWU_NOMEM;
+			STRDUP_OR_ERR(w->data.val_s, pw->pw_age, res);
 			break;
 		case ATTR_REP_NAME:
-			if ((w->data.val_s = strdup("ldap")) == NULL)
-				res = PWU_NOMEM;
+			STRDUP_OR_ERR(w->data.val_s, "ldap", res);
 			break;
 
 		/* integer values */
@@ -243,24 +337,47 @@
 			w->data.val_i = pw->pw_gid;
 			break;
 		case ATTR_LSTCHG:
-			w->data.val_i = -1;
+			if (ldapbuf->shadow_update_enabled)
+				w->data.val_i = spw->sp_lstchg;
+			else
+				w->data.val_i = -1;
 			break;
 		case ATTR_MIN:
-			w->data.val_i = -1;
+			if (ldapbuf->shadow_update_enabled)
+				w->data.val_i = spw->sp_min;
+			else
+				w->data.val_i = -1;
 			break;
 		case ATTR_MAX:
-			w->data.val_i = -1;
+			if (ldapbuf->shadow_update_enabled)
+				w->data.val_i = spw->sp_max;
+			else
+				w->data.val_i = -1;
 			break;
 		case ATTR_WARN:
-			w->data.val_i = -1;
+			if (ldapbuf->shadow_update_enabled)
+				w->data.val_i = spw->sp_warn;
+			else
+				w->data.val_i = -1;
 			break;
 		case ATTR_INACT:
-			w->data.val_i = -1;
+			if (ldapbuf->shadow_update_enabled)
+				w->data.val_i = spw->sp_inact;
+			else
+				w->data.val_i = -1;
 			break;
 		case ATTR_EXPIRE:
-			w->data.val_i = -1;
+			if (ldapbuf->shadow_update_enabled)
+				w->data.val_i = spw->sp_expire;
+			else
+				w->data.val_i = -1;
 			break;
 		case ATTR_FLAG:
+			if (ldapbuf->shadow_update_enabled)
+				w->data.val_i = spw->sp_flag;
+			break;
+		case ATTR_FAILED_LOGINS:
+			w->data.val_i = spw->sp_flag & FAILCOUNT_MASK;
 			break;
 		default:
 			break;
@@ -268,11 +385,8 @@
 	}
 
 out:
-	if (pw)
-		free_pwd(pw);
-	if (spw)
-		free_spwd(spw);
-
+	free_ldapbuf(ldapbuf);
+	free(ldapbuf);
 	return (res);
 }
 
@@ -292,45 +406,54 @@
 ldap_getpwnam(char *name, attrlist *items, pwu_repository_t *rep,
     void **buf)
 {
-	attrlist *p;
-	int nr_items;
-	int need_pwd = 0;
 	ldapbuf_t *ldapbuf;
-	int res;
+	int res = PWU_NOMEM;
 
-	for (nr_items = 0, p = items; p != NULL; p = p->next) {
-		nr_items++;
-		if (p->type == ATTR_PASSWD ||
-		    p->type == ATTR_PASSWD_SERVER_POLICY)
-			need_pwd = 1;
-	}
-
+	/*
+	 * [sp]attrs is treated as NULL terminated
+	 */
 
 	ldapbuf = calloc(1, sizeof (ldapbuf_t));
 	if (ldapbuf == NULL)
 		return (PWU_NOMEM);
 
-	ldapbuf->attrs = calloc(nr_items, sizeof (ns_ldap_attr_t *));
-	if (ldapbuf->attrs == NULL)
-		return (PWU_NOMEM);
+	ldapbuf->pattrs = calloc(_PWD_MAX_ATTR, sizeof (ns_ldap_attr_t *));
+	if (ldapbuf->pattrs == NULL)
+		goto out;
+	ldapbuf->npattrs = _PWD_MAX_ATTR;
 
-	if (need_pwd) {
-		struct spwd *spw;
+	ldapbuf->sattrs = calloc(_S_MAX_ATTR, sizeof (ns_ldap_attr_t *));
+	if (ldapbuf->sattrs == NULL)
+		goto out;
+	ldapbuf->nsattrs = _S_MAX_ATTR;
 
-		res = dup_pw(&ldapbuf->pwd, getpwnam_from(name, rep, REP_LDAP));
-		if (res != PWU_SUCCESS)
-			return (res);
+	res = dup_pw(&ldapbuf->pwd, getpwnam_from(name, rep, REP_LDAP));
+	if (res != PWU_SUCCESS)
+		goto out;
 
-		spw  = getspnam_from(name, rep, REP_LDAP);
-		if (spw) {
-			ldapbuf->passwd = strdup(spw->sp_pwdp);
+	res = dup_spw(&ldapbuf->spwd, getspnam_from(name, rep, REP_LDAP));
+	if (res != PWU_SUCCESS)
+		goto out;
+	else {
+		char *spw = ldapbuf->spwd->sp_pwdp;
+		if (spw != NULL && *spw != '\0') {
+			ldapbuf->passwd = strdup(spw);
 			if (ldapbuf->passwd == NULL)
-				return (PWU_NOMEM);
-		}
+				goto out;
+		} else
+			ldapbuf->passwd = NULL;
 	}
 
-	*buf = ldapbuf;
-	return (0);
+	/* remember if shadow update is enabled */
+	ldapbuf->shadow_update_enabled = __ns_ldap_is_shadow_update_enabled();
+
+	*buf = (void *)ldapbuf;
+	return (PWU_SUCCESS);
+
+out:
+	free_ldapbuf(ldapbuf);
+	free(ldapbuf);
+	return (res);
 }
 
 /*
@@ -359,6 +482,63 @@
 }
 
 /*
+ * max_present(list)
+ *
+ * returns '1' if a ATTR_MAX with value != -1 is present. (in other words:
+ * if password aging is to be turned on).
+ */
+static int
+max_present(attrlist *list)
+{
+	while (list != NULL)
+		if (list->type == ATTR_MAX && list->data.val_i != -1)
+			return (1);
+		else
+			list = list->next;
+	return (0);
+}
+
+/*
+ * attr_addmod(attrs, idx, item, val)
+ *
+ * Adds or updates attribute 'item' in ldap_attrs list to value
+ * update idx if item is added
+ * return:  -1 - PWU_NOMEM/error, 0 - success
+ */
+static int
+attr_addmod(ns_ldap_attr_t **attrs, int *idx, char *item, int value)
+{
+	char numbuf[MAX_INT_LEN], *strp;
+	int i;
+
+	/* stringize the value or abort */
+	if (snprintf(numbuf, MAX_INT_LEN, "%d", value) >= MAX_INT_LEN)
+		return (-1);
+
+	/* check for existence and modify existing */
+	for (i = 0; i < *idx; i++) {
+		if (attrs[i] != NULL &&
+		    strcmp(item, attrs[i]->attrname) == 0) {
+			strp = strdup(numbuf);
+			if (strp == NULL)
+				return (-1);
+			free(attrs[i]->attrvalue[0]);
+			attrs[i]->attrvalue[0] = strp;
+			return (0);
+		}
+	}
+	/* else add */
+	strp = strdup(numbuf);
+	if (strp == NULL)
+		return (-1);
+	attrs[*idx] = new_attr(item, strp);
+	if (attrs[*idx] == NULL)
+		return (-1);
+	(*idx)++;
+	return (0);
+}
+
+/*
  * ldap_update(items, rep, buf)
  *
  * create LDAP attributes in 'buf' for each attribute in 'items'.
@@ -368,91 +548,399 @@
 ldap_update(attrlist *items, pwu_repository_t *rep, void *buf)
 {
 	attrlist *p;
-	int idx = 0;
 	ldapbuf_t *ldapbuf = (ldapbuf_t *)buf;
-	ns_ldap_attr_t **attrs = ldapbuf->attrs;
+	struct spwd *spw;
+	ns_ldap_attr_t **pattrs = ldapbuf->pattrs;
+	int pidx = 0;
+	ns_ldap_attr_t **sattrs = ldapbuf->sattrs;
+	int sidx = 0;
 	char *pwd, *val;
 	char *salt;
 	size_t cryptlen;
+	int len;
+	int count;
+	int rc = PWU_SUCCESS;
+	int aging_needed = 0;
+	int aging_set = 0;
+	int disable_aging;
+
+	spw = ldapbuf->spwd;
+
+	/*
+	 * if sp_max==0 and shadow update is enabled:
+	 * disable passwd aging after updating the password
+	 */
+	disable_aging = (spw != NULL && spw->sp_max == 0 &&
+	    ldapbuf->shadow_update_enabled);
 
 	for (p = items; p != NULL; p = p->next) {
 		switch (p->type) {
 		case ATTR_PASSWD:
-			salt = crypt_gensalt(ldapbuf->passwd, ldapbuf->pwd);
+			/*
+			 * There is a special case for ldap: if the
+			 * password is to be deleted (-d to passwd),
+			 * p->data.val_s will be NULL.
+			 */
+			if (p->data.val_s == NULL) {
+				if (!ldapbuf->shadow_update_enabled)
+					return (PWU_CHANGE_NOT_ALLOWED);
+				cryptlen =
+				    sizeof ("{crypt}" NS_LDAP_NO_UNIX_PASSWORD);
+				val = malloc(cryptlen);
+				if (val == NULL)
+					return (PWU_NOMEM);
+				(void) snprintf(val, cryptlen,
+				"{crypt}" NS_LDAP_NO_UNIX_PASSWORD);
+			} else { /* not deleting password */
+				salt = crypt_gensalt(ldapbuf->passwd,
+				    ldapbuf->pwd);
 
-			if (salt == NULL) {
-				if (errno == ENOMEM)
-					return (PWU_NOMEM);
-				else {
+				if (salt == NULL) {
+					if (errno == ENOMEM)
+						return (PWU_NOMEM);
+
 					/* algorithm problem? */
 					syslog(LOG_AUTH | LOG_ALERT,
 					    "passwdutil: crypt_gensalt "
 					    "%m");
 					return (PWU_UPDATE_FAILED);
 				}
+
+				pwd = crypt(p->data.val_s, salt);
+				free(salt);
+				cryptlen = strlen(pwd) + sizeof ("{crypt}");
+				val = malloc(cryptlen);
+				if (val == NULL)
+					return (PWU_NOMEM);
+				(void) snprintf(val, cryptlen,
+				    "{crypt}%s", pwd);
+			}
+
+			/*
+			 * If not managing passwordAccount,
+			 * insert the new password in the
+			 * passwd attr array and break.
+			 */
+			if (!ldapbuf->shadow_update_enabled) {
+				NEW_ATTR(pattrs, pidx,
+				    _PWD_USERPASSWORD, val);
+				break;
 			}
 
-			pwd = crypt(p->data.val_s, salt);
-			free(salt);
-			cryptlen = strlen(pwd) + sizeof ("{crypt}");
-			val = malloc(cryptlen);
-			if (val == NULL)
+			/*
+			 * Managing passwordAccount, insert the
+			 * new password, along with lastChange and
+			 * shadowFlag, in the shadow attr array.
+			 */
+			NEW_ATTR(sattrs, sidx, _PWD_USERPASSWORD, val);
+
+			if (attr_addmod(sattrs, &sidx, _S_LASTCHANGE,
+			    DAY_NOW_32) < 0)
 				return (PWU_NOMEM);
-			(void) snprintf(val, cryptlen, "{crypt}%s", pwd);
+			spw->sp_lstchg = DAY_NOW_32;
 
-			attrs[idx] = new_attr(_PWD_USERPASSWORD, val);
+			if (attr_addmod(sattrs, &sidx, _S_FLAG,
+			    spw->sp_flag & ~FAILCOUNT_MASK) < 0)
+				return (PWU_NOMEM);
+			spw->sp_flag &= ~FAILCOUNT_MASK; /* reset count */
+			aging_needed = 1;
 			break;
-		/*
-		 * For server policy, don't crypt the password,
-		 * send the password as is to the server and
-		 * let the LDAP server do its own password
-		 * encryption
-		 */
 		case ATTR_PASSWD_SERVER_POLICY:
-			val = strdup(p->data.val_s);
-			if (val == NULL)
-				return (PWU_NOMEM);
+			/*
+			 * For server policy, don't crypt the password,
+			 * send the password as is to the server and
+			 * let the LDAP server do its own password
+			 * encryption
+			 */
+			STRDUP_OR_RET(val, p->data.val_s);
 
-			attrs[idx] = new_attr(_PWD_USERPASSWORD, val);
+			NEW_ATTR(pattrs, pidx, _PWD_USERPASSWORD, val);
 			break;
 		case ATTR_COMMENT:
 			/* XX correct? */
-			attrs[idx] = new_attr(_PWD_DESCRIPTION, p->data.val_s);
+			NEW_ATTR(pattrs, pidx, _PWD_DESCRIPTION, p->data.val_s);
 			break;
 		case ATTR_GECOS:
-			attrs[idx] = new_attr(_PWD_GECOS, p->data.val_s);
+			if (!ldapbuf->shadow_update_enabled) {
+				NEW_ATTR(pattrs, pidx, _PWD_GECOS,
+				    p->data.val_s);
+			} else {
+				NEW_ATTR(sattrs, sidx, _PWD_GECOS,
+				    p->data.val_s);
+			}
 			break;
 		case ATTR_HOMEDIR:
-			attrs[idx] = new_attr(_PWD_HOMEDIRECTORY,
-						p->data.val_s);
+			if (!ldapbuf->shadow_update_enabled) {
+				NEW_ATTR(pattrs, pidx, _PWD_HOMEDIRECTORY,
+				    p->data.val_s);
+			} else {
+				NEW_ATTR(sattrs, sidx, _PWD_HOMEDIRECTORY,
+				    p->data.val_s);
+			}
 			break;
 		case ATTR_SHELL:
-			attrs[idx] = new_attr(_PWD_LOGINSHELL, p->data.val_s);
+			if (!ldapbuf->shadow_update_enabled) {
+				NEW_ATTR(pattrs, pidx, _PWD_LOGINSHELL,
+				    p->data.val_s);
+			} else {
+				NEW_ATTR(sattrs, sidx, _PWD_LOGINSHELL,
+				    p->data.val_s);
+			}
 			break;
-		/* Unsupported items are below this line */
+		/* We don't update NAME, UID, GID */
 		case ATTR_NAME:
 		case ATTR_UID:
 		case ATTR_GID:
+		/* Unsupported item */
 		case ATTR_AGE:
+			break;
+		case ATTR_LOCK_ACCOUNT:
+			if (!ldapbuf->shadow_update_enabled)
+				break;	/* not managing passwordAccount */
+			if (spw->sp_pwdp == NULL) {
+				spw->sp_pwdp = LOCKSTRING;
+			} else if (strncmp(spw->sp_pwdp, LOCKSTRING,
+			    sizeof (LOCKSTRING)-1) != 0) {
+				len = sizeof (LOCKSTRING)-1 +
+				    strlen(spw->sp_pwdp) + 1 +
+				    sizeof ("{crypt}");
+				pwd = malloc(len);
+				if (pwd == NULL) {
+					return (PWU_NOMEM);
+				}
+				(void) strlcpy(pwd, "{crypt}", len);
+				(void) strlcat(pwd, LOCKSTRING, len);
+				(void) strlcat(pwd, spw->sp_pwdp, len);
+				free(spw->sp_pwdp);
+				spw->sp_pwdp = pwd;
+				NEW_ATTR(sattrs, sidx, _PWD_USERPASSWORD,
+				    spw->sp_pwdp);
+			}
+			if (attr_addmod(sattrs, &sidx, _S_LASTCHANGE,
+			    DAY_NOW_32) < 0)
+				return (PWU_NOMEM);
+			spw->sp_lstchg = DAY_NOW_32;
+			break;
+
+		case ATTR_UNLOCK_ACCOUNT:
+			if (!ldapbuf->shadow_update_enabled)
+				break;	/* not managing passwordAccount */
+			if (spw->sp_pwdp &&
+			    strncmp(spw->sp_pwdp, LOCKSTRING,
+			    sizeof (LOCKSTRING)-1) == 0) {
+				len = (sizeof ("{crypt}") -
+				    sizeof (LOCKSTRING)) +
+				    strlen(spw->sp_pwdp) + 1;
+				pwd = malloc(len);
+				if (pwd == NULL) {
+					return (PWU_NOMEM);
+				}
+				(void) strlcpy(pwd, "{crypt}", len);
+				(void) strlcat(pwd, spw->sp_pwdp +
+				    sizeof (LOCKSTRING)-1, len);
+				free(spw->sp_pwdp);
+				spw->sp_pwdp = pwd;
+
+				NEW_ATTR(sattrs, sidx, _PWD_USERPASSWORD,
+				    spw->sp_pwdp);
+				if (attr_addmod(sattrs, &sidx, _S_LASTCHANGE,
+				    DAY_NOW_32) < 0)
+					return (PWU_NOMEM);
+				spw->sp_lstchg = DAY_NOW_32;
+			}
+			break;
+
+		case ATTR_NOLOGIN_ACCOUNT:
+			if (!ldapbuf->shadow_update_enabled)
+				break;	/* not managing passwordAccount */
+			free(spw->sp_pwdp);
+			STRDUP_OR_RET(spw->sp_pwdp, "{crypt}" NOLOGINSTRING);
+			NEW_ATTR(sattrs, sidx, _PWD_USERPASSWORD, spw->sp_pwdp);
+			if (attr_addmod(sattrs, &sidx, _S_LASTCHANGE,
+			    DAY_NOW_32) < 0)
+				return (PWU_NOMEM);
+			spw->sp_lstchg = DAY_NOW_32;
+			break;
+
+		case ATTR_EXPIRE_PASSWORD:
+			if (!ldapbuf->shadow_update_enabled)
+				break;	/* not managing passwordAccount */
+			NUM_TO_STR(val, 0);
+			NEW_ATTR(sattrs, sidx, _S_LASTCHANGE, val);
+			break;
+
 		case ATTR_LSTCHG:
+			if (!ldapbuf->shadow_update_enabled)
+				break;	/* not managing passwordAccount */
+			NUM_TO_STR(val, p->data.val_i);
+			NEW_ATTR(sattrs, sidx, _S_LASTCHANGE, val);
+			break;
+
 		case ATTR_MIN:
+			if (!ldapbuf->shadow_update_enabled)
+				break;	/* not managing passwordAccount */
+			if (spw->sp_max == -1 && p->data.val_i != -1 &&
+			    max_present(p->next) == 0)
+				return (PWU_AGING_DISABLED);
+			NUM_TO_STR(val, p->data.val_i);
+			NEW_ATTR(sattrs, sidx, _S_MIN, val);
+			aging_set = 1;
+			break;
+
 		case ATTR_MAX:
+			if (!ldapbuf->shadow_update_enabled)
+				break;	/* not managing passwordAccount */
+			if (p->data.val_i == -1) {
+				/* Turn off aging. Reset min and warn too */
+				spw->sp_max = spw->sp_min = spw->sp_warn = -1;
+				NUM_TO_STR(val, -1);
+				NEW_ATTR(sattrs, sidx, _S_MIN, val);
+				NUM_TO_STR(val, -1);
+				NEW_ATTR(sattrs, sidx, _S_WARNING, val);
+			} else {
+				/* Turn account aging on */
+				if (spw->sp_min == -1) {
+					/*
+					 * minage was not set with command-
+					 * line option: set to zero
+					 */
+					spw->sp_min = 0;
+					NUM_TO_STR(val, 0);
+					NEW_ATTR(sattrs, sidx, _S_MIN,
+					    val);
+				}
+				/*
+				 * If aging was turned off, we update lstchg.
+				 * We take care not to update lstchg if the
+				 * user has no password, otherwise the user
+				 * might not be required to provide a password
+				 * the next time [s]he logs in.
+				 *
+				 * Also, if lstchg != -1 (i.e., not set)
+				 * we keep the old value.
+				 */
+				if (spw->sp_max == -1 &&
+				    spw->sp_pwdp != NULL && *spw->sp_pwdp &&
+				    spw->sp_lstchg == -1) {
+					if (attr_addmod(sattrs, &sidx,
+					    _S_LASTCHANGE,
+					    DAY_NOW_32) < 0)
+						return (PWU_NOMEM);
+					spw->sp_lstchg = DAY_NOW_32;
+				}
+			}
+			NUM_TO_STR(val, p->data.val_i);
+			NEW_ATTR(sattrs, sidx, _S_MAX, val);
+			aging_set = 1;
+			break;
+
 		case ATTR_WARN:
+			if (!ldapbuf->shadow_update_enabled)
+				break;	/* not managing passwordAccount */
+			if (spw->sp_max == -1 &&
+			    p->data.val_i != -1 && max_present(p->next) == 0)
+				return (PWU_AGING_DISABLED);
+			NUM_TO_STR(val, p->data.val_i);
+			NEW_ATTR(sattrs, sidx, _S_WARNING, val);
+			break;
+
 		case ATTR_INACT:
+			if (!ldapbuf->shadow_update_enabled)
+				break;	/* not managing passwordAccount */
+			NUM_TO_STR(val, p->data.val_i);
+			NEW_ATTR(sattrs, sidx, _S_INACTIVE, val);
+			break;
+
 		case ATTR_EXPIRE:
+			if (!ldapbuf->shadow_update_enabled)
+				break;	/* not managing passwordAccount */
+			NUM_TO_STR(val, p->data.val_i);
+			NEW_ATTR(sattrs, sidx, _S_EXPIRE, val);
+			break;
+
 		case ATTR_FLAG:
+			if (!ldapbuf->shadow_update_enabled)
+				break;	/* not managing passwordAccount */
+			NUM_TO_STR(val, p->data.val_i);
+			NEW_ATTR(sattrs, sidx, _S_FLAG, val);
+			break;
+		case ATTR_INCR_FAILED_LOGINS:
+			if (!ldapbuf->shadow_update_enabled) {
+				rc = PWU_CHANGE_NOT_ALLOWED;
+				break;	/* not managing passwordAccount */
+			}
+			count = (spw->sp_flag & FAILCOUNT_MASK) + 1;
+			spw->sp_flag &= ~FAILCOUNT_MASK;
+			spw->sp_flag |= min(FAILCOUNT_MASK, count);
+			p->data.val_i = count;
+			NUM_TO_STR(val, spw->sp_flag);
+			NEW_ATTR(sattrs, sidx, _S_FLAG, val);
+			break;
+		case ATTR_RST_FAILED_LOGINS:
+			if (!ldapbuf->shadow_update_enabled) {
+				rc = PWU_CHANGE_NOT_ALLOWED;
+				break;	/* not managing passwordAccount */
+			}
+			p->data.val_i = spw->sp_flag & FAILCOUNT_MASK;
+			spw->sp_flag &= ~FAILCOUNT_MASK;
+			NUM_TO_STR(val, spw->sp_flag);
+			NEW_ATTR(sattrs, sidx, _S_FLAG, val);
 			break;
 		default:
 			break;
 		}
-		if (attrs[idx] == NULL)
-			return (PWU_NOMEM);
-		idx++;
 	}
 
-	attrs[idx] = NULL;
+	/*
+	 * If the ldap client is configured with shadow update enabled,
+	 * then what should the new aging values look like?
+	 *
+	 * There are a number of different conditions
+	 *
+	 *  a) aging is already configured: don't touch it
+	 *
+	 *  b) disable_aging is set: disable aging
+	 *
+	 *  c) aging is not configured: turn on default aging;
+	 *
+	 *  b) and c) of course only if aging_needed and !aging_set.
+	 *  (i.e., password changed, and aging values not changed)
+	 */
 
-	return (PWU_SUCCESS);
+	if (ldapbuf->shadow_update_enabled && spw != NULL && spw->sp_max <= 0) {
+		/* a) aging not yet configured */
+		if (aging_needed && !aging_set) {
+			if (disable_aging) {
+				/* b) turn off aging */
+				spw->sp_min = spw->sp_max = spw->sp_warn = -1;
+				if (attr_addmod(sattrs, &sidx, _S_MIN, -1) < 0)
+					return (PWU_NOMEM);
+				if (attr_addmod(sattrs, &sidx, _S_MAX, -1) < 0)
+					return (PWU_NOMEM);
+				if (attr_addmod(sattrs, &sidx, _S_WARNING,
+				    -1) < 0)
+					return (PWU_NOMEM);
+			} else {
+				/* c) */
+				turn_on_default_aging(spw);
+
+				if (attr_addmod(sattrs, &sidx, _S_MIN,
+				    spw->sp_min) < 0)
+					return (PWU_NOMEM);
+				if (attr_addmod(sattrs, &sidx, _S_MAX,
+				    spw->sp_max) < 0)
+					return (PWU_NOMEM);
+				if (attr_addmod(sattrs, &sidx,
+				    _S_WARNING, spw->sp_warn) < 0)
+					return (PWU_NOMEM);
+			}
+		}
+	}
+
+	pattrs[pidx] = NULL;
+	sattrs[sidx] = NULL;
+
+	return (rc);
 }
 
 /*
@@ -492,7 +980,7 @@
 
 int
 ldap_replaceattr(const char *dn, ns_ldap_attr_t **attrs, const char *binddn,
-	const char *pwd, int *pwd_status)
+	const char *pwd, int *pwd_status, int flags)
 {
 	int		result = NS_LDAP_OP_FAILED;
 	int		ldaprc;
@@ -507,18 +995,20 @@
 	debug("%s: replace_ldapattr()", __FILE__);
 
 	if ((credp = (ns_cred_t *)calloc(1, sizeof (ns_cred_t))) == NULL)
-		return (PWU_NOMEM);
+		return (NS_LDAP_MEMORY); /* map to PWU_NOMEM */
 
-	/* Fill in the user name and password */
-	if (dn == NULL || pwd == NULL)
-		goto out;
-
-	credp->cred.unix_cred.userID = strdup(binddn);
-	credp->cred.unix_cred.passwd = strdup(pwd);
+	/* for admin shadow update, dn and pwd will be set later in libsldap */
+	if ((flags & NS_LDAP_UPDATE_SHADOW) == 0) {
+		/* Fill in the user name and password */
+		if (dn == NULL || pwd == NULL)
+			goto out;
+		credp->cred.unix_cred.userID = strdup(binddn);
+		credp->cred.unix_cred.passwd = strdup(pwd);
+	}
 
 	/* get host certificate path, if one is configured */
 	ldaprc = __ns_ldap_getParam(NS_LDAP_HOST_CERTPATH_P,
-		(void ***)&certpath, &errorp);
+	    (void ***)&certpath, &errorp);
 	if (ldaprc != NS_LDAP_SUCCESS)
 		goto out;
 
@@ -527,7 +1017,7 @@
 
 	/* Load the service specific authentication method */
 	ldaprc = __ns_ldap_getServiceAuthMethods("passwd-cmd", &authpp,
-		&errorp);
+	    &errorp);
 
 	if (ldaprc != NS_LDAP_SUCCESS)
 		goto out;
@@ -538,7 +1028,7 @@
 	 */
 	if (authpp == NULL) {
 		ldaprc = __ns_ldap_getParam(NS_LDAP_AUTH_P, (void ***)&authpp,
-			&errorp);
+		    &errorp);
 		if (ldaprc != NS_LDAP_SUCCESS)
 			goto out;
 	}
@@ -570,21 +1060,32 @@
 		credp->auth.saslopt = authp->saslopt;
 
 		ldaprc = __ns_ldap_repAttr("shadow", dn,
-			(const ns_ldap_attr_t * const *)attrs,
-			credp, 0, &errorp);
+		    (const ns_ldap_attr_t * const *)attrs,
+		    credp, flags, &errorp);
 		if (ldaprc == NS_LDAP_SUCCESS) {
 			result = NS_LDAP_SUCCESS;
 			goto out;
 		}
 
 		/*
+		 * if change not allowed due to configuration, indicate so
+		 * to the caller
+		 */
+		if (ldaprc == NS_LDAP_CONFIG &&
+		    errorp->status == NS_CONFIG_NOTALLOW) {
+			result = NS_LDAP_CONFIG;
+			*pwd_status = NS_PASSWD_CHANGE_NOT_ALLOWED;
+			goto out;
+		}
+
+		/*
 		 * other errors might need to be added to this list, for
 		 * the current supported mechanisms this is sufficient
 		 */
 		if ((ldaprc == NS_LDAP_INTERNAL) &&
-			(errorp->pwd_mgmt.status == NS_PASSWD_GOOD) &&
-			((errorp->status == LDAP_INAPPROPRIATE_AUTH) ||
-			(errorp->status == LDAP_INVALID_CREDENTIALS))) {
+		    (errorp->pwd_mgmt.status == NS_PASSWD_GOOD) &&
+		    ((errorp->status == LDAP_INAPPROPRIATE_AUTH) ||
+		    (errorp->status == LDAP_INVALID_CREDENTIALS))) {
 			result = ldaprc;
 			goto out;
 		}
@@ -594,7 +1095,7 @@
 		 * return it to caller
 		 */
 		if ((ldaprc == NS_LDAP_INTERNAL) &&
-			errorp->pwd_mgmt.status != NS_PASSWD_GOOD) {
+		    errorp->pwd_mgmt.status != NS_PASSWD_GOOD) {
 			*pwd_status = errorp->pwd_mgmt.status;
 			result = ldaprc;
 			goto out;
@@ -611,7 +1112,7 @@
 		result = NS_LDAP_CONFIG;
 		goto out;
 	}
-	result = PWU_DENIED;
+	result = NS_LDAP_OP_FAILED; /* map to PWU_DENIED */
 
 out:
 	if (credp)
@@ -627,7 +1128,6 @@
 }
 
 
-
 /*
  * ldap_putpwnam(name, oldpw, dummy, rep, buf)
  *
@@ -645,7 +1145,8 @@
 	char *binddn;	/* dn of user who is performing the change */
 	ns_ldap_error_t *errorp;
 	ldapbuf_t *ldapbuf = (ldapbuf_t *)buf;
-	ns_ldap_attr_t **attrs = ldapbuf->attrs;
+	ns_ldap_attr_t **pattrs = ldapbuf->pattrs;
+	ns_ldap_attr_t **sattrs = ldapbuf->sattrs;
 	struct passwd *pw;
 	int pwd_status;
 	uid_t uid;
@@ -654,6 +1155,26 @@
 		return (PWU_NOT_FOUND);
 
 	/*
+	 * convert name of user whose attributes we are changing
+	 * to a distinguished name
+	 */
+	res = __ns_ldap_uid2dn(name, &dn, NULL, &errorp);
+	if (res != NS_LDAP_SUCCESS)
+		goto out;
+
+	/* update shadow via ldap_cachemgr if it is enabled */
+	if (ldapbuf->shadow_update_enabled &&
+	    sattrs != NULL && sattrs[0] != NULL) {
+		/*
+		 * flag NS_LDAP_UPDATE_SHADOW indicates the shadow update
+		 * should be done via ldap_cachemgr
+		 */
+		res = ldap_replaceattr(dn, sattrs, NULL, NULL, &pwd_status,
+		    NS_LDAP_UPDATE_SHADOW);
+		goto out;
+	}
+
+	/*
 	 * The LDAP server checks whether we are permitted to perform
 	 * the requested change. We need to send the name of the user
 	 * who is executing this piece of code, together with his
@@ -667,14 +1188,6 @@
 	 */
 
 	/*
-	 * convert name of user whose attributes we are changing
-	 * to a distinguished name
-	 */
-	res = __ns_ldap_uid2dn(name, &dn, NULL, &errorp);
-	if (res != NS_LDAP_SUCCESS)
-		goto out;
-
-	/*
 	 * create a dn for the user who is executing this code
 	 */
 	uid = getuid();
@@ -696,21 +1209,14 @@
 	if (res != NS_LDAP_SUCCESS)
 		goto out;
 
-	res = ldap_replaceattr(dn, attrs, binddn, oldpw,
-			&pwd_status);
+	if (pattrs && pattrs[0] != NULL) {
+		res = ldap_replaceattr(dn, pattrs, binddn, oldpw,
+		    &pwd_status, 0);
+	} else
+		res = NS_LDAP_OP_FAILED;
 
 out:
-	while (*attrs) {
-		free((*attrs)->attrvalue[0]);
-		free(*attrs);
-		attrs++;
-	}
-	if (ldapbuf->passwd) {
-		(void) memset(ldapbuf->passwd, 0, strlen(ldapbuf->passwd));
-		free(ldapbuf->passwd);
-	}
-	if (ldapbuf->pwd)
-		free_pwd(ldapbuf->pwd);
+	free_ldapbuf(ldapbuf);
 	free(dn);
 
 	return (ldap_to_pwu_code(res, pwd_status));
--- a/usr/src/lib/passwdutil/passwdutil.h	Fri Feb 13 18:28:50 2009 -0800
+++ b/usr/src/lib/passwdutil/passwdutil.h	Fri Feb 13 18:18:56 2009 -0800
@@ -19,7 +19,7 @@
  * CDDL HEADER END
  */
 /*
- * Copyright 2008 Sun Microsystems, Inc.  All rights reserved.
+ * Copyright 2009 Sun Microsystems, Inc.  All rights reserved.
  * Use is subject to license terms.
  */
 
@@ -166,6 +166,7 @@
 struct passwd *getpwnam_from(const char *, pwu_repository_t *, int);
 struct passwd *getpwuid_from(uid_t, pwu_repository_t *, int);
 struct spwd *getspnam_from(const char *, pwu_repository_t *, int);
+int name_to_int(char *);
 
 /*
  * __set_authtok_attr.c