Mercurial > illumos > illumos-gate
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, + ¶mVal, 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(¶mVal); + return (rc); + } + credp->cred.unix_cred.userID = strdup((char *)*paramVal); + (void) __ns_ldap_freeParam(¶mVal); + 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, + ¶mVal, 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(¶mVal); + 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(¶m); 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