diff options
author | Michen Chang <Michen.Chang@Sun.COM> | 2009-02-13 18:18:56 -0800 |
---|---|---|
committer | Michen Chang <Michen.Chang@Sun.COM> | 2009-02-13 18:18:56 -0800 |
commit | dd1104fbe0f0f41434502f335b9f0b34999f771c (patch) | |
tree | 57c95d64bc40cebf2b145329e1f49e811352d502 /usr/src/lib | |
parent | d68ef20e3fe871e73146fc684d29d335521dcd99 (diff) | |
download | illumos-joyent-dd1104fbe0f0f41434502f335b9f0b34999f771c.tar.gz |
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
Diffstat (limited to 'usr/src/lib')
-rw-r--r-- | usr/src/lib/libsldap/common/mapfile-vers | 34 | ||||
-rw-r--r-- | usr/src/lib/libsldap/common/ns_cache_door.h | 14 | ||||
-rw-r--r-- | usr/src/lib/libsldap/common/ns_config.c | 79 | ||||
-rw-r--r-- | usr/src/lib/libsldap/common/ns_confmgr.c | 56 | ||||
-rw-r--r-- | usr/src/lib/libsldap/common/ns_internal.h | 11 | ||||
-rw-r--r-- | usr/src/lib/libsldap/common/ns_sldap.h | 27 | ||||
-rw-r--r-- | usr/src/lib/libsldap/common/ns_writes.c | 308 | ||||
-rw-r--r-- | usr/src/lib/nsswitch/ldap/common/getspent.c | 100 | ||||
-rw-r--r-- | usr/src/lib/passwdutil/__failed_count.c | 46 | ||||
-rw-r--r-- | usr/src/lib/passwdutil/ldap_attr.c | 860 | ||||
-rw-r--r-- | usr/src/lib/passwdutil/passwdutil.h | 3 |
11 files changed, 1275 insertions, 263 deletions
diff --git a/usr/src/lib/libsldap/common/mapfile-vers b/usr/src/lib/libsldap/common/mapfile-vers index f58a9328d4..e043513adf 100644 --- a/usr/src/lib/libsldap/common/mapfile-vers +++ b/usr/src/lib/libsldap/common/mapfile-vers @@ -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_getConnectionInfoFromDUA; - __ns_ldap_getRootDSE; - __ns_ldap_pingOfflineServers; __ns_ldap_cancelStandalone; - __ns_ldap_initAuth; + __ns_ldap_check_all_preq; + __ns_ldap_check_dns_preq; + __ns_ldap_check_gssapi_preq; __ns_ldap_getAcctMgmt; - __s_api_get_canonical_name; __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_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_ip2hostname; + __s_api_get_canonical_name; __s_api_hostname2ip; + __s_api_ip2hostname; } SUNWprivate_1.0; SUNWprivate_1.0 { diff --git a/usr/src/lib/libsldap/common/ns_cache_door.h b/usr/src/lib/libsldap/common/ns_cache_door.h index 391394cbe7..52599534af 100644 --- a/usr/src/lib/libsldap/common/ns_cache_door.h +++ b/usr/src/lib/libsldap/common/ns_cache_door.h @@ -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 @@ typedef struct ldap_config_out { 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 @@ typedef struct { 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 @@ typedef union { #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 diff --git a/usr/src/lib/libsldap/common/ns_config.c b/usr/src/lib/libsldap/common/ns_config.c index 2046a46bad..1a7a21bfee 100644 --- a/usr/src/lib/libsldap/common/ns_config.c +++ b/usr/src/lib/libsldap/common/ns_config.c @@ -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 @@ static ns_enum_map ns_pref_enum[] = { { -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 @@ static ns_default_config defconfig[] = { 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 @@ __s_get_enum_value(ns_config_t *ptr, char *value, ParamIndexType i) 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 @@ __s_get_searchref_name(ns_config_t *ptr, SearchRef_t type) 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 @@ verify_value(ns_config_t *cfg, char *name, char *value, char *errstr) 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 @@ __ns_ldap_setParamValue(ns_config_t *ptr, const ParamIndexType type, 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 @@ __ns_ldap_setParamValue(ns_config_t *ptr, const ParamIndexType type, * * 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_api_strValue(ns_config_t *cfg, char *str, __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 @@ static int __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 @@ static int __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); } diff --git a/usr/src/lib/libsldap/common/ns_confmgr.c b/usr/src/lib/libsldap/common/ns_confmgr.c index 1fd58aac6f..a96f186ded 100644 --- a/usr/src/lib/libsldap/common/ns_confmgr.c +++ b/usr/src/lib/libsldap/common/ns_confmgr.c @@ -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_create_config_door_str(char *config, ns_ldap_error_t **errorp) __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 @@ __ns_ldap_LoadDoorInfo(LineBuf *configinfo, char *domainname, ns_config_t *new) } (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 @@ __ns_ldap_DumpLdif(char *filename) 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 @@ __ns_ldap_make_config(ns_ldap_result_t *result) } } 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 @@ __ns_ldap_make_config(ns_ldap_result_t *result) 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 @@ __ns_ldap_print_config(int verbose) 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; diff --git a/usr/src/lib/libsldap/common/ns_internal.h b/usr/src/lib/libsldap/common/ns_internal.h index a7dbf4e6de..9adba43565 100644 --- a/usr/src/lib/libsldap/common/ns_internal.h +++ b/usr/src/lib/libsldap/common/ns_internal.h @@ -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 @@ extern "C" { /* 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 @@ typedef struct ns_enum_map { #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_security_name(ns_config_t *ptr, TlsType_t type); 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, diff --git a/usr/src/lib/libsldap/common/ns_sldap.h b/usr/src/lib/libsldap/common/ns_sldap.h index af288c5cf2..f8bb420019 100644 --- a/usr/src/lib/libsldap/common/ns_sldap.h +++ b/usr/src/lib/libsldap/common/ns_sldap.h @@ -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 @@ typedef enum ScopeType { #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 @@ typedef enum PrefOnly { 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 @@ typedef enum { 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 @@ typedef struct ns_ldap_objectclass_map { 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_getParamType( 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); diff --git a/usr/src/lib/libsldap/common/ns_writes.c b/usr/src/lib/libsldap/common/ns_writes.c index 3abbefd829..14727d8a6b 100644 --- a/usr/src/lib/libsldap/common/ns_writes.c +++ b/usr/src/lib/libsldap/common/ns_writes.c @@ -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 @@ write_state_machine( 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 @@ __ns_ldap_delAttr( 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 @@ __ns_ldap_repAttr( { 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 @@ __ns_ldap_repAttr( *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 @@ __ns_ldap_repAttr( return (rc); } - /*ARGSUSED*/ int __ns_ldap_addEntry( @@ -3793,3 +3925,157 @@ __s_api_append_default_basedn( (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); +} diff --git a/usr/src/lib/nsswitch/ldap/common/getspent.c b/usr/src/lib/nsswitch/ldap/common/getspent.c index a533300a08..bcdba2998c 100644 --- a/usr/src/lib/nsswitch/ldap/common/getspent.c +++ b/usr/src/lib/nsswitch/ldap/common/getspent.c @@ -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 @@ _nss_ldap_shadow2str(ldap_backend_ptr be, nss_XbyY_args_t *argp) { 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 @@ _nss_ldap_shadow2str(ldap_backend_ptr be, nss_XbyY_args_t *argp) 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 @@ _nss_ldap_shadow2str(ldap_backend_ptr be, nss_XbyY_args_t *argp) 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 @@ _nss_ldap_shadow2str(ldap_backend_ptr be, nss_XbyY_args_t *argp) } 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 @@ getbynam(ldap_backend_ptr be, void *a) 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 @@ _nss_ldap_shadow_constr(const char *dummy1, const char *dummy2, { 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)); } diff --git a/usr/src/lib/passwdutil/__failed_count.c b/usr/src/lib/passwdutil/__failed_count.c index 777408d3b5..a72a2fba26 100644 --- a/usr/src/lib/passwdutil/__failed_count.c +++ b/usr/src/lib/passwdutil/__failed_count.c @@ -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 @@ __incr_failed_count(char *username, char *repname, int max_failures) 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 @@ __incr_failed_count(char *username, char *repname, int max_failures) ret = PWU_ACCOUNT_LOCKED; out: - ops->unlock(); + if (ops->unlock != NULL) { + ops->unlock(); + } return (ret); } @@ -84,14 +92,20 @@ __rst_failed_count(char *username, char *repname) 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 @@ __rst_failed_count(char *username, char *repname) 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); } diff --git a/usr/src/lib/passwdutil/ldap_attr.c b/usr/src/lib/passwdutil/ldap_attr.c index 7cf40be7a1..7a29614bf3 100644 --- a/usr/src/lib/passwdutil/ldap_attr.c +++ b/usr/src/lib/passwdutil/ldap_attr.c @@ -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 @@ struct repops ldap_repops = { * 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 @@ typedef struct { #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 @@ ldap_user_to_authenticate(char *user, pwu_repository_t *rep, 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 @@ ldap_user_to_authenticate(char *user, pwu_repository_t *rep, 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 @@ ldap_user_to_authenticate(char *user, pwu_repository_t *rep, 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 */ + res = ldap_getpwnam(name, items, rep, (void **)&ldapbuf); + if (res != PWU_SUCCESS) + return (res); - /* 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; - } - - 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 @@ ldap_getattr(char *name, attrlist *items, pwu_repository_t *rep) 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 @@ ldap_getattr(char *name, attrlist *items, pwu_repository_t *rep) } out: - if (pw) - free_pwd(pw); - if (spw) - free_spwd(spw); - + free_ldapbuf(ldapbuf); + free(ldapbuf); return (res); } @@ -292,45 +406,54 @@ int 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; - - 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; - } + int res = PWU_NOMEM; + /* + * [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 @@ new_attr(char *name, char *value) } /* + * 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 @@ int 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); - - if (salt == NULL) { - if (errno == ENOMEM) + /* + * 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); - else { + (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); + /* 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); - - attrs[idx] = new_attr(_PWD_USERPASSWORD, val); + /* + * 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); + + 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 @@ ldap_to_pwu_code(int error, int pwd_status) 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 @@ ldap_replaceattr(const char *dn, ns_ldap_attr_t **attrs, const char *binddn, debug("%s: replace_ldapattr()", __FILE__); if ((credp = (ns_cred_t *)calloc(1, sizeof (ns_cred_t))) == NULL) - return (PWU_NOMEM); - - /* Fill in the user name and password */ - if (dn == NULL || pwd == NULL) - goto out; + return (NS_LDAP_MEMORY); /* map to PWU_NOMEM */ - 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 @@ ldap_replaceattr(const char *dn, ns_ldap_attr_t **attrs, const char *binddn, /* 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 @@ ldap_replaceattr(const char *dn, ns_ldap_attr_t **attrs, const char *binddn, */ 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 @@ ldap_replaceattr(const char *dn, ns_ldap_attr_t **attrs, const char *binddn, 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 @@ ldap_replaceattr(const char *dn, ns_ldap_attr_t **attrs, const char *binddn, * 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 @@ ldap_replaceattr(const char *dn, ns_ldap_attr_t **attrs, const char *binddn, 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 @@ out: } - /* * ldap_putpwnam(name, oldpw, dummy, rep, buf) * @@ -645,7 +1145,8 @@ ldap_putpwnam(char *name, char *oldpw, char *dummy, 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 @@ ldap_putpwnam(char *name, char *oldpw, char *dummy, 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 @@ ldap_putpwnam(char *name, char *oldpw, char *dummy, */ /* - * 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 @@ ldap_putpwnam(char *name, char *oldpw, char *dummy, 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)); diff --git a/usr/src/lib/passwdutil/passwdutil.h b/usr/src/lib/passwdutil/passwdutil.h index 65216e81e2..7634fce552 100644 --- a/usr/src/lib/passwdutil/passwdutil.h +++ b/usr/src/lib/passwdutil/passwdutil.h @@ -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 @@ int get_ns(pwu_repository_t *, int); 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 |