xref: /titanic_53/usr/src/lib/passwdutil/ldap_attr.c (revision dd1104fbe0f0f41434502f335b9f0b34999f771c)
17c478bd9Sstevel@tonic-gate /*
27c478bd9Sstevel@tonic-gate  * CDDL HEADER START
37c478bd9Sstevel@tonic-gate  *
47c478bd9Sstevel@tonic-gate  * The contents of this file are subject to the terms of the
5*dd1104fbSMichen Chang  * Common Development and Distribution License (the "License").
6*dd1104fbSMichen Chang  * You may not use this file except in compliance with the License.
77c478bd9Sstevel@tonic-gate  *
87c478bd9Sstevel@tonic-gate  * You can obtain a copy of the license at usr/src/OPENSOLARIS.LICENSE
97c478bd9Sstevel@tonic-gate  * or http://www.opensolaris.org/os/licensing.
107c478bd9Sstevel@tonic-gate  * See the License for the specific language governing permissions
117c478bd9Sstevel@tonic-gate  * and limitations under the License.
127c478bd9Sstevel@tonic-gate  *
137c478bd9Sstevel@tonic-gate  * When distributing Covered Code, include this CDDL HEADER in each
147c478bd9Sstevel@tonic-gate  * file and include the License file at usr/src/OPENSOLARIS.LICENSE.
157c478bd9Sstevel@tonic-gate  * If applicable, add the following below this CDDL HEADER, with the
167c478bd9Sstevel@tonic-gate  * fields enclosed by brackets "[]" replaced with your own identifying
177c478bd9Sstevel@tonic-gate  * information: Portions Copyright [yyyy] [name of copyright owner]
187c478bd9Sstevel@tonic-gate  *
197c478bd9Sstevel@tonic-gate  * CDDL HEADER END
207c478bd9Sstevel@tonic-gate  */
217c478bd9Sstevel@tonic-gate /*
22*dd1104fbSMichen Chang  * Copyright 2009 Sun Microsystems, Inc.  All rights reserved.
237c478bd9Sstevel@tonic-gate  * Use is subject to license terms.
247c478bd9Sstevel@tonic-gate  */
257c478bd9Sstevel@tonic-gate 
267c478bd9Sstevel@tonic-gate #include <stdio.h>
277c478bd9Sstevel@tonic-gate #include <errno.h>
287c478bd9Sstevel@tonic-gate #include <stdlib.h>
297c478bd9Sstevel@tonic-gate #include <string.h>
307c478bd9Sstevel@tonic-gate #include <unistd.h>
31*dd1104fbSMichen Chang #include <macros.h>
32*dd1104fbSMichen Chang #include <priv.h>
337c478bd9Sstevel@tonic-gate 
347c478bd9Sstevel@tonic-gate #include "ns_sldap.h"
357c478bd9Sstevel@tonic-gate 
367c478bd9Sstevel@tonic-gate #include <nss_dbdefs.h>
377c478bd9Sstevel@tonic-gate #include <nsswitch.h>
387c478bd9Sstevel@tonic-gate 
397c478bd9Sstevel@tonic-gate #include <pwd.h>
407c478bd9Sstevel@tonic-gate #include <shadow.h>
417c478bd9Sstevel@tonic-gate #include <syslog.h>
427c478bd9Sstevel@tonic-gate 
437c478bd9Sstevel@tonic-gate #include "passwdutil.h"
447c478bd9Sstevel@tonic-gate 
457c478bd9Sstevel@tonic-gate #include "utils.h"
467c478bd9Sstevel@tonic-gate 
47*dd1104fbSMichen Chang #define	MAX_INT_LEN 11	/* 10+1 %d buflen for words/ints [not longs] */
48*dd1104fbSMichen Chang 
49*dd1104fbSMichen Chang #define	STRDUP_OR_RET(to, from) \
50*dd1104fbSMichen Chang 	if ((to = strdup(from)) == NULL) \
51*dd1104fbSMichen Chang 		return (PWU_NOMEM);
52*dd1104fbSMichen Chang 
53*dd1104fbSMichen Chang #define	STRDUP_OR_ERR(to, from, err) \
54*dd1104fbSMichen Chang 	if (((to) = strdup(from)) == NULL) \
55*dd1104fbSMichen Chang 		(err) = PWU_NOMEM;
56*dd1104fbSMichen Chang 
57*dd1104fbSMichen Chang #define	NUM_TO_STR(to, from) \
58*dd1104fbSMichen Chang 	{ \
59*dd1104fbSMichen Chang 		char nb[MAX_INT_LEN]; \
60*dd1104fbSMichen Chang 		if (snprintf(nb, MAX_INT_LEN, "%d", (from)) >= MAX_INT_LEN) \
61*dd1104fbSMichen Chang 			return (PWU_NOMEM); \
62*dd1104fbSMichen Chang 		STRDUP_OR_RET(to, nb); \
63*dd1104fbSMichen Chang 	}
64*dd1104fbSMichen Chang 
65*dd1104fbSMichen Chang #define	NEW_ATTR(p, i, attr, val) \
66*dd1104fbSMichen Chang 	{ \
67*dd1104fbSMichen Chang 		p[i] = new_attr(attr, (val)); \
68*dd1104fbSMichen Chang 		if (p[i] == NULL) \
69*dd1104fbSMichen Chang 			return (PWU_NOMEM); \
70*dd1104fbSMichen Chang 		i++; \
71*dd1104fbSMichen Chang 	}
72*dd1104fbSMichen Chang 
737c478bd9Sstevel@tonic-gate int ldap_getattr(char *name, attrlist *item, pwu_repository_t *rep);
747c478bd9Sstevel@tonic-gate int ldap_getpwnam(char *name, attrlist *items, pwu_repository_t *rep,
757c478bd9Sstevel@tonic-gate     void **buf);
767c478bd9Sstevel@tonic-gate int ldap_update(attrlist *items, pwu_repository_t *rep, void *buf);
777c478bd9Sstevel@tonic-gate int ldap_putpwnam(char *name, char *oldpw, char *dummy,
787c478bd9Sstevel@tonic-gate 	pwu_repository_t *rep, void *buf);
797c478bd9Sstevel@tonic-gate int ldap_user_to_authenticate(char *name, pwu_repository_t *rep,
807c478bd9Sstevel@tonic-gate 	char **auth_user, int *privileged);
817c478bd9Sstevel@tonic-gate 
827c478bd9Sstevel@tonic-gate /*
837c478bd9Sstevel@tonic-gate  * ldap function pointer table, used by passwdutil_init to initialize
847c478bd9Sstevel@tonic-gate  * the global Repository-OPerations table "rops"
857c478bd9Sstevel@tonic-gate  */
867c478bd9Sstevel@tonic-gate struct repops ldap_repops = {
877c478bd9Sstevel@tonic-gate 	NULL,	/* checkhistory */
887c478bd9Sstevel@tonic-gate 	ldap_getattr,
897c478bd9Sstevel@tonic-gate 	ldap_getpwnam,
907c478bd9Sstevel@tonic-gate 	ldap_update,
917c478bd9Sstevel@tonic-gate 	ldap_putpwnam,
927c478bd9Sstevel@tonic-gate 	ldap_user_to_authenticate,
937c478bd9Sstevel@tonic-gate 	NULL,	/* lock */
947c478bd9Sstevel@tonic-gate 	NULL	/* unlock */
957c478bd9Sstevel@tonic-gate };
967c478bd9Sstevel@tonic-gate 
977c478bd9Sstevel@tonic-gate /*
987c478bd9Sstevel@tonic-gate  * structure used to keep state between get/update/put calls
997c478bd9Sstevel@tonic-gate  */
1007c478bd9Sstevel@tonic-gate typedef struct {
1017c478bd9Sstevel@tonic-gate 	char *passwd;			/* encrypted password */
1027c478bd9Sstevel@tonic-gate 	struct passwd *pwd;
103*dd1104fbSMichen Chang 	ns_ldap_attr_t **pattrs;	/* passwd attrs */
104*dd1104fbSMichen Chang 	int npattrs;			/* max attrs */
105*dd1104fbSMichen Chang 	struct spwd *spwd;
106*dd1104fbSMichen Chang 	ns_ldap_attr_t **sattrs;	/* passwd attrs */
107*dd1104fbSMichen Chang 	int nsattrs;			/* max attrs */
108*dd1104fbSMichen Chang 	boolean_t shadow_update_enabled;	/* shadow update configured */
1097c478bd9Sstevel@tonic-gate } ldapbuf_t;
1107c478bd9Sstevel@tonic-gate 
1117c478bd9Sstevel@tonic-gate /*
1127c478bd9Sstevel@tonic-gate  * The following define's are taken from
1137c478bd9Sstevel@tonic-gate  *	usr/src/lib/nsswitch/ldap/common/getpwnam.c
1147c478bd9Sstevel@tonic-gate  */
1157c478bd9Sstevel@tonic-gate 
1167c478bd9Sstevel@tonic-gate /* passwd attributes filters */
1177c478bd9Sstevel@tonic-gate #define	_PWD_CN			"cn"
1187c478bd9Sstevel@tonic-gate #define	_PWD_UID		"uid"
1197c478bd9Sstevel@tonic-gate #define	_PWD_USERPASSWORD	"userpassword"
1207c478bd9Sstevel@tonic-gate #define	_PWD_UIDNUMBER		"uidnumber"
1217c478bd9Sstevel@tonic-gate #define	_PWD_GIDNUMBER		"gidnumber"
1227c478bd9Sstevel@tonic-gate #define	_PWD_GECOS		"gecos"
1237c478bd9Sstevel@tonic-gate #define	_PWD_DESCRIPTION	"description"
1247c478bd9Sstevel@tonic-gate #define	_PWD_HOMEDIRECTORY	"homedirectory"
1257c478bd9Sstevel@tonic-gate #define	_PWD_LOGINSHELL		"loginshell"
1267c478bd9Sstevel@tonic-gate 
127*dd1104fbSMichen Chang #define	_PWD_MAX_ATTR		10	/* 9+NULL */
128*dd1104fbSMichen Chang 
129*dd1104fbSMichen Chang /* shadow attributes filters */
130*dd1104fbSMichen Chang #define	_S_LASTCHANGE		"shadowlastchange"
131*dd1104fbSMichen Chang #define	_S_MIN			"shadowmin"
132*dd1104fbSMichen Chang #define	_S_MAX			"shadowmax"
133*dd1104fbSMichen Chang #define	_S_WARNING		"shadowwarning"
134*dd1104fbSMichen Chang #define	_S_INACTIVE		"shadowinactive"
135*dd1104fbSMichen Chang #define	_S_EXPIRE		"shadowexpire"
136*dd1104fbSMichen Chang #define	_S_FLAG			"shadowflag"
137*dd1104fbSMichen Chang 
138*dd1104fbSMichen Chang #define	_S_MAX_ATTR		8	/* 7+NULL */
139*dd1104fbSMichen Chang 
140*dd1104fbSMichen Chang /*
141*dd1104fbSMichen Chang  * Frees up an ldapbuf_t
142*dd1104fbSMichen Chang  */
143*dd1104fbSMichen Chang 
144*dd1104fbSMichen Chang static void
145*dd1104fbSMichen Chang free_ldapbuf(ldapbuf_t *p)
146*dd1104fbSMichen Chang {
147*dd1104fbSMichen Chang 	int i;
148*dd1104fbSMichen Chang 
149*dd1104fbSMichen Chang 	if (p == NULL)
150*dd1104fbSMichen Chang 		return;
151*dd1104fbSMichen Chang 	if (p->passwd) {
152*dd1104fbSMichen Chang 		(void) memset(p->passwd, 0, strlen(p->passwd));
153*dd1104fbSMichen Chang 		free(p->passwd);
154*dd1104fbSMichen Chang 	}
155*dd1104fbSMichen Chang 	if (p->pwd)
156*dd1104fbSMichen Chang 		free_pwd(p->pwd);
157*dd1104fbSMichen Chang 	if (p->spwd)
158*dd1104fbSMichen Chang 		free_spwd(p->spwd);
159*dd1104fbSMichen Chang 	if (p->pattrs) {
160*dd1104fbSMichen Chang 		for (i = 0; i < p->npattrs; i++) {
161*dd1104fbSMichen Chang 			if (p->pattrs[i] != NULL) {
162*dd1104fbSMichen Chang 				free(p->pattrs[i]->attrvalue[0]);
163*dd1104fbSMichen Chang 				free(p->pattrs[i]);
164*dd1104fbSMichen Chang 			}
165*dd1104fbSMichen Chang 		}
166*dd1104fbSMichen Chang 		free(p->pattrs);
167*dd1104fbSMichen Chang 	}
168*dd1104fbSMichen Chang 	if (p->sattrs) {
169*dd1104fbSMichen Chang 		for (i = 0; i < p->nsattrs; i++) {
170*dd1104fbSMichen Chang 			if (p->sattrs[i] != NULL) {
171*dd1104fbSMichen Chang 				free(p->sattrs[i]->attrvalue[0]);
172*dd1104fbSMichen Chang 				free(p->sattrs[i]);
173*dd1104fbSMichen Chang 			}
174*dd1104fbSMichen Chang 		}
175*dd1104fbSMichen Chang 		free(p->sattrs);
176*dd1104fbSMichen Chang 	}
177*dd1104fbSMichen Chang }
178*dd1104fbSMichen Chang 
1797c478bd9Sstevel@tonic-gate /*
1807c478bd9Sstevel@tonic-gate  * int ldap_user_to_authenticate(user, rep, auth_user, privileged)
1817c478bd9Sstevel@tonic-gate  *
182*dd1104fbSMichen Chang  * If the Shadow Update functionality is enabled, then we check to
183*dd1104fbSMichen Chang  * see if the caller has 0 as the euid or has all zone privs. If so,
184*dd1104fbSMichen Chang  * the caller would be able to modify shadow(4) data stored on the
185*dd1104fbSMichen Chang  * LDAP server. Otherwise, when LDAP Shadow Update is not enabled,
186*dd1104fbSMichen Chang  * we can't determine whether the user is "privileged" in the LDAP
187*dd1104fbSMichen Chang  * sense. The operation should be attempted and will succeed if the
188*dd1104fbSMichen Chang  * user had privileges. For our purposes, we say that the user is
189*dd1104fbSMichen Chang  * privileged if he/she is attempting to change another user's
190*dd1104fbSMichen Chang  * password attributes.
1917c478bd9Sstevel@tonic-gate  */
1927c478bd9Sstevel@tonic-gate int
1937c478bd9Sstevel@tonic-gate ldap_user_to_authenticate(char *user, pwu_repository_t *rep,
1947c478bd9Sstevel@tonic-gate 	char **auth_user, int *privileged)
1957c478bd9Sstevel@tonic-gate {
1967c478bd9Sstevel@tonic-gate 	struct passwd *pw;
1977c478bd9Sstevel@tonic-gate 	uid_t uid;
1987c478bd9Sstevel@tonic-gate 	uid_t priviledged_uid;
1997c478bd9Sstevel@tonic-gate 	int res = PWU_SUCCESS;
2007c478bd9Sstevel@tonic-gate 
2017c478bd9Sstevel@tonic-gate 	if (strcmp(user, "root") == 0)
2027c478bd9Sstevel@tonic-gate 		return (PWU_NOT_FOUND);
2037c478bd9Sstevel@tonic-gate 
2047c478bd9Sstevel@tonic-gate 	if ((pw = getpwnam_from(user, rep, REP_LDAP)) == NULL)
2057c478bd9Sstevel@tonic-gate 		return (PWU_NOT_FOUND);
2067c478bd9Sstevel@tonic-gate 
2077c478bd9Sstevel@tonic-gate 	uid = getuid();
2087c478bd9Sstevel@tonic-gate 
209*dd1104fbSMichen Chang 	/*
210*dd1104fbSMichen Chang 	 * need equivalent of write access to /etc/shadow
211*dd1104fbSMichen Chang 	 * the privilege escalation model is euid == 0 || all zone privs
212*dd1104fbSMichen Chang 	 */
213*dd1104fbSMichen Chang 	if (__ns_ldap_is_shadow_update_enabled()) {
214*dd1104fbSMichen Chang 		boolean_t priv;
215*dd1104fbSMichen Chang 
216*dd1104fbSMichen Chang 		priv = (geteuid() == 0);
217*dd1104fbSMichen Chang 		if (!priv) {
218*dd1104fbSMichen Chang 			priv_set_t *ps = priv_allocset();	/* caller */
219*dd1104fbSMichen Chang 			priv_set_t *zs;				/* zone */
220*dd1104fbSMichen Chang 
221*dd1104fbSMichen Chang 			(void) getppriv(PRIV_EFFECTIVE, ps);
222*dd1104fbSMichen Chang 			zs = priv_str_to_set("zone", ",", NULL);
223*dd1104fbSMichen Chang 			priv = priv_isequalset(ps, zs);
224*dd1104fbSMichen Chang 			priv_freeset(ps);
225*dd1104fbSMichen Chang 			priv_freeset(zs);
226*dd1104fbSMichen Chang 		}
227*dd1104fbSMichen Chang 		/*
228*dd1104fbSMichen Chang 		 * priv can change anyone's password,
229*dd1104fbSMichen Chang 		 * only root isn't prompted.
230*dd1104fbSMichen Chang 		 */
231*dd1104fbSMichen Chang 		*privileged = 0;	/* for proper prompting */
232*dd1104fbSMichen Chang 		if (priv) {
233*dd1104fbSMichen Chang 			if (uid == 0) {
234*dd1104fbSMichen Chang 				*privileged = 1;
235*dd1104fbSMichen Chang 				*auth_user = NULL;
236*dd1104fbSMichen Chang 				return (res);
237*dd1104fbSMichen Chang 			} else if (uid == pw->pw_uid) {
238*dd1104fbSMichen Chang 				STRDUP_OR_ERR(*auth_user, user, res);
239*dd1104fbSMichen Chang 				return (res);
240*dd1104fbSMichen Chang 			}
241*dd1104fbSMichen Chang 		}
242*dd1104fbSMichen Chang 
243*dd1104fbSMichen Chang 		return (PWU_DENIED);
244*dd1104fbSMichen Chang 	}
245*dd1104fbSMichen Chang 
2467c478bd9Sstevel@tonic-gate 	if (uid == pw->pw_uid) {
247*dd1104fbSMichen Chang 		/* changing our own, not privileged */
2487c478bd9Sstevel@tonic-gate 		*privileged = 0;
249*dd1104fbSMichen Chang 		STRDUP_OR_RET(*auth_user, user);
2507c478bd9Sstevel@tonic-gate 	} else {
2517c478bd9Sstevel@tonic-gate 		char pwd_buf[1024];
2527c478bd9Sstevel@tonic-gate 		struct passwd pwr;
2537c478bd9Sstevel@tonic-gate 
2547c478bd9Sstevel@tonic-gate 		*privileged = 1;
2557c478bd9Sstevel@tonic-gate 		/*
2567c478bd9Sstevel@tonic-gate 		 * specific case for root
2577c478bd9Sstevel@tonic-gate 		 * we want 'user' to be authenticated.
2587c478bd9Sstevel@tonic-gate 		 */
2597c478bd9Sstevel@tonic-gate 		if (uid == 0)  {
2607c478bd9Sstevel@tonic-gate 			priviledged_uid = pw->pw_uid;
2617c478bd9Sstevel@tonic-gate 		} else {
2627c478bd9Sstevel@tonic-gate 			priviledged_uid = uid;
2637c478bd9Sstevel@tonic-gate 		}
2647c478bd9Sstevel@tonic-gate 		if (getpwuid_r(priviledged_uid, &pwr, pwd_buf,
2657c478bd9Sstevel@tonic-gate 		    sizeof (pwd_buf)) != NULL) {
266*dd1104fbSMichen Chang 			STRDUP_OR_ERR(*auth_user, pwr.pw_name, res);
2677c478bd9Sstevel@tonic-gate 		} else {
2687c478bd9Sstevel@tonic-gate 			/* hmm. can't find name of current user...??? */
2697c478bd9Sstevel@tonic-gate 
270*dd1104fbSMichen Chang 			if ((*auth_user = malloc(MAX_INT_LEN)) == NULL) {
2717c478bd9Sstevel@tonic-gate 				res = PWU_NOMEM;
2727c478bd9Sstevel@tonic-gate 			} else {
273*dd1104fbSMichen Chang 				(void) snprintf(*auth_user, MAX_INT_LEN, "%d",
2747c478bd9Sstevel@tonic-gate 				    (int)uid);
2757c478bd9Sstevel@tonic-gate 			}
2767c478bd9Sstevel@tonic-gate 		}
2777c478bd9Sstevel@tonic-gate 	}
2787c478bd9Sstevel@tonic-gate 
2797c478bd9Sstevel@tonic-gate 	return (res);
2807c478bd9Sstevel@tonic-gate }
2817c478bd9Sstevel@tonic-gate 
2827c478bd9Sstevel@tonic-gate /*
2837c478bd9Sstevel@tonic-gate  * int ldap_getattr(name, item, rep)
2847c478bd9Sstevel@tonic-gate  *
2857c478bd9Sstevel@tonic-gate  * retrieve attributes specified in "item" for user "name".
2867c478bd9Sstevel@tonic-gate  */
2877c478bd9Sstevel@tonic-gate /*ARGSUSED*/
2887c478bd9Sstevel@tonic-gate int
2897c478bd9Sstevel@tonic-gate ldap_getattr(char *name, attrlist *items, pwu_repository_t *rep)
2907c478bd9Sstevel@tonic-gate {
291*dd1104fbSMichen Chang 	attrlist *w;
2927c478bd9Sstevel@tonic-gate 	int res;
293*dd1104fbSMichen Chang 	ldapbuf_t *ldapbuf;
2947c478bd9Sstevel@tonic-gate 	struct passwd *pw = NULL;
2957c478bd9Sstevel@tonic-gate 	struct spwd *spw = NULL;
2967c478bd9Sstevel@tonic-gate 
297*dd1104fbSMichen Chang 	res = ldap_getpwnam(name, items, rep, (void **)&ldapbuf);
2987c478bd9Sstevel@tonic-gate 	if (res != PWU_SUCCESS)
299*dd1104fbSMichen Chang 		return (res);
3007c478bd9Sstevel@tonic-gate 
301*dd1104fbSMichen Chang 	pw = ldapbuf->pwd;
302*dd1104fbSMichen Chang 	spw = ldapbuf->spwd;
3037c478bd9Sstevel@tonic-gate 
3047c478bd9Sstevel@tonic-gate 	for (w = items; res == PWU_SUCCESS && w != NULL; w = w->next) {
3057c478bd9Sstevel@tonic-gate 		switch (w->type) {
3067c478bd9Sstevel@tonic-gate 		case ATTR_NAME:
307*dd1104fbSMichen Chang 			STRDUP_OR_ERR(w->data.val_s, pw->pw_name, res);
3087c478bd9Sstevel@tonic-gate 			break;
3097c478bd9Sstevel@tonic-gate 		case ATTR_COMMENT:
310*dd1104fbSMichen Chang 			STRDUP_OR_ERR(w->data.val_s, pw->pw_comment, res);
3117c478bd9Sstevel@tonic-gate 			break;
3127c478bd9Sstevel@tonic-gate 		case ATTR_GECOS:
313*dd1104fbSMichen Chang 			STRDUP_OR_ERR(w->data.val_s, pw->pw_gecos, res);
3147c478bd9Sstevel@tonic-gate 			break;
3157c478bd9Sstevel@tonic-gate 		case ATTR_HOMEDIR:
316*dd1104fbSMichen Chang 			STRDUP_OR_ERR(w->data.val_s, pw->pw_dir, res);
3177c478bd9Sstevel@tonic-gate 			break;
3187c478bd9Sstevel@tonic-gate 		case ATTR_SHELL:
319*dd1104fbSMichen Chang 			STRDUP_OR_ERR(w->data.val_s, pw->pw_shell, res);
3207c478bd9Sstevel@tonic-gate 			break;
3217c478bd9Sstevel@tonic-gate 		case ATTR_PASSWD:
3227c478bd9Sstevel@tonic-gate 		case ATTR_PASSWD_SERVER_POLICY:
323*dd1104fbSMichen Chang 			STRDUP_OR_ERR(w->data.val_s, spw->sp_pwdp, res);
3247c478bd9Sstevel@tonic-gate 			break;
3257c478bd9Sstevel@tonic-gate 		case ATTR_AGE:
326*dd1104fbSMichen Chang 			STRDUP_OR_ERR(w->data.val_s, pw->pw_age, res);
3277c478bd9Sstevel@tonic-gate 			break;
3287c478bd9Sstevel@tonic-gate 		case ATTR_REP_NAME:
329*dd1104fbSMichen Chang 			STRDUP_OR_ERR(w->data.val_s, "ldap", res);
3307c478bd9Sstevel@tonic-gate 			break;
3317c478bd9Sstevel@tonic-gate 
3327c478bd9Sstevel@tonic-gate 		/* integer values */
3337c478bd9Sstevel@tonic-gate 		case ATTR_UID:
3347c478bd9Sstevel@tonic-gate 			w->data.val_i = pw->pw_uid;
3357c478bd9Sstevel@tonic-gate 			break;
3367c478bd9Sstevel@tonic-gate 		case ATTR_GID:
3377c478bd9Sstevel@tonic-gate 			w->data.val_i = pw->pw_gid;
3387c478bd9Sstevel@tonic-gate 			break;
3397c478bd9Sstevel@tonic-gate 		case ATTR_LSTCHG:
340*dd1104fbSMichen Chang 			if (ldapbuf->shadow_update_enabled)
341*dd1104fbSMichen Chang 				w->data.val_i = spw->sp_lstchg;
342*dd1104fbSMichen Chang 			else
3437c478bd9Sstevel@tonic-gate 				w->data.val_i = -1;
3447c478bd9Sstevel@tonic-gate 			break;
3457c478bd9Sstevel@tonic-gate 		case ATTR_MIN:
346*dd1104fbSMichen Chang 			if (ldapbuf->shadow_update_enabled)
347*dd1104fbSMichen Chang 				w->data.val_i = spw->sp_min;
348*dd1104fbSMichen Chang 			else
3497c478bd9Sstevel@tonic-gate 				w->data.val_i = -1;
3507c478bd9Sstevel@tonic-gate 			break;
3517c478bd9Sstevel@tonic-gate 		case ATTR_MAX:
352*dd1104fbSMichen Chang 			if (ldapbuf->shadow_update_enabled)
353*dd1104fbSMichen Chang 				w->data.val_i = spw->sp_max;
354*dd1104fbSMichen Chang 			else
3557c478bd9Sstevel@tonic-gate 				w->data.val_i = -1;
3567c478bd9Sstevel@tonic-gate 			break;
3577c478bd9Sstevel@tonic-gate 		case ATTR_WARN:
358*dd1104fbSMichen Chang 			if (ldapbuf->shadow_update_enabled)
359*dd1104fbSMichen Chang 				w->data.val_i = spw->sp_warn;
360*dd1104fbSMichen Chang 			else
3617c478bd9Sstevel@tonic-gate 				w->data.val_i = -1;
3627c478bd9Sstevel@tonic-gate 			break;
3637c478bd9Sstevel@tonic-gate 		case ATTR_INACT:
364*dd1104fbSMichen Chang 			if (ldapbuf->shadow_update_enabled)
365*dd1104fbSMichen Chang 				w->data.val_i = spw->sp_inact;
366*dd1104fbSMichen Chang 			else
3677c478bd9Sstevel@tonic-gate 				w->data.val_i = -1;
3687c478bd9Sstevel@tonic-gate 			break;
3697c478bd9Sstevel@tonic-gate 		case ATTR_EXPIRE:
370*dd1104fbSMichen Chang 			if (ldapbuf->shadow_update_enabled)
371*dd1104fbSMichen Chang 				w->data.val_i = spw->sp_expire;
372*dd1104fbSMichen Chang 			else
3737c478bd9Sstevel@tonic-gate 				w->data.val_i = -1;
3747c478bd9Sstevel@tonic-gate 			break;
3757c478bd9Sstevel@tonic-gate 		case ATTR_FLAG:
376*dd1104fbSMichen Chang 			if (ldapbuf->shadow_update_enabled)
377*dd1104fbSMichen Chang 				w->data.val_i = spw->sp_flag;
378*dd1104fbSMichen Chang 			break;
379*dd1104fbSMichen Chang 		case ATTR_FAILED_LOGINS:
380*dd1104fbSMichen Chang 			w->data.val_i = spw->sp_flag & FAILCOUNT_MASK;
3817c478bd9Sstevel@tonic-gate 			break;
3827c478bd9Sstevel@tonic-gate 		default:
3837c478bd9Sstevel@tonic-gate 			break;
3847c478bd9Sstevel@tonic-gate 		}
3857c478bd9Sstevel@tonic-gate 	}
3867c478bd9Sstevel@tonic-gate 
3877c478bd9Sstevel@tonic-gate out:
388*dd1104fbSMichen Chang 	free_ldapbuf(ldapbuf);
389*dd1104fbSMichen Chang 	free(ldapbuf);
3907c478bd9Sstevel@tonic-gate 	return (res);
3917c478bd9Sstevel@tonic-gate }
3927c478bd9Sstevel@tonic-gate 
3937c478bd9Sstevel@tonic-gate /*
3947c478bd9Sstevel@tonic-gate  * int ldap_getpwnam(name, items, rep, buf)
3957c478bd9Sstevel@tonic-gate  *
3967c478bd9Sstevel@tonic-gate  * There is no need to get the old values from the ldap
3977c478bd9Sstevel@tonic-gate  * server, as the update will update each item individually.
3987c478bd9Sstevel@tonic-gate  * Therefore, we only allocate a buffer that will be used by
3997c478bd9Sstevel@tonic-gate  * _update and _putpwnam to hold the attributes to update.
4007c478bd9Sstevel@tonic-gate  *
4017c478bd9Sstevel@tonic-gate  * Only when we're about to update a password, we need to retrieve
4027c478bd9Sstevel@tonic-gate  * the old password since it contains salt-information.
4037c478bd9Sstevel@tonic-gate  */
4047c478bd9Sstevel@tonic-gate /*ARGSUSED*/
4057c478bd9Sstevel@tonic-gate int
4067c478bd9Sstevel@tonic-gate ldap_getpwnam(char *name, attrlist *items, pwu_repository_t *rep,
4077c478bd9Sstevel@tonic-gate     void **buf)
4087c478bd9Sstevel@tonic-gate {
4097c478bd9Sstevel@tonic-gate 	ldapbuf_t *ldapbuf;
410*dd1104fbSMichen Chang 	int res = PWU_NOMEM;
4117c478bd9Sstevel@tonic-gate 
412*dd1104fbSMichen Chang 	/*
413*dd1104fbSMichen Chang 	 * [sp]attrs is treated as NULL terminated
414*dd1104fbSMichen Chang 	 */
4157c478bd9Sstevel@tonic-gate 
4167c478bd9Sstevel@tonic-gate 	ldapbuf = calloc(1, sizeof (ldapbuf_t));
4177c478bd9Sstevel@tonic-gate 	if (ldapbuf == NULL)
4187c478bd9Sstevel@tonic-gate 		return (PWU_NOMEM);
4197c478bd9Sstevel@tonic-gate 
420*dd1104fbSMichen Chang 	ldapbuf->pattrs = calloc(_PWD_MAX_ATTR, sizeof (ns_ldap_attr_t *));
421*dd1104fbSMichen Chang 	if (ldapbuf->pattrs == NULL)
422*dd1104fbSMichen Chang 		goto out;
423*dd1104fbSMichen Chang 	ldapbuf->npattrs = _PWD_MAX_ATTR;
4247c478bd9Sstevel@tonic-gate 
425*dd1104fbSMichen Chang 	ldapbuf->sattrs = calloc(_S_MAX_ATTR, sizeof (ns_ldap_attr_t *));
426*dd1104fbSMichen Chang 	if (ldapbuf->sattrs == NULL)
427*dd1104fbSMichen Chang 		goto out;
428*dd1104fbSMichen Chang 	ldapbuf->nsattrs = _S_MAX_ATTR;
4297c478bd9Sstevel@tonic-gate 
4307c478bd9Sstevel@tonic-gate 	res = dup_pw(&ldapbuf->pwd, getpwnam_from(name, rep, REP_LDAP));
4317c478bd9Sstevel@tonic-gate 	if (res != PWU_SUCCESS)
432*dd1104fbSMichen Chang 		goto out;
4337c478bd9Sstevel@tonic-gate 
434*dd1104fbSMichen Chang 	res = dup_spw(&ldapbuf->spwd, getspnam_from(name, rep, REP_LDAP));
435*dd1104fbSMichen Chang 	if (res != PWU_SUCCESS)
436*dd1104fbSMichen Chang 		goto out;
437*dd1104fbSMichen Chang 	else {
438*dd1104fbSMichen Chang 		char *spw = ldapbuf->spwd->sp_pwdp;
439*dd1104fbSMichen Chang 		if (spw != NULL && *spw != '\0') {
440*dd1104fbSMichen Chang 			ldapbuf->passwd = strdup(spw);
4417c478bd9Sstevel@tonic-gate 			if (ldapbuf->passwd == NULL)
442*dd1104fbSMichen Chang 				goto out;
443*dd1104fbSMichen Chang 		} else
444*dd1104fbSMichen Chang 			ldapbuf->passwd = NULL;
4457c478bd9Sstevel@tonic-gate 	}
4467c478bd9Sstevel@tonic-gate 
447*dd1104fbSMichen Chang 	/* remember if shadow update is enabled */
448*dd1104fbSMichen Chang 	ldapbuf->shadow_update_enabled = __ns_ldap_is_shadow_update_enabled();
449*dd1104fbSMichen Chang 
450*dd1104fbSMichen Chang 	*buf = (void *)ldapbuf;
451*dd1104fbSMichen Chang 	return (PWU_SUCCESS);
452*dd1104fbSMichen Chang 
453*dd1104fbSMichen Chang out:
454*dd1104fbSMichen Chang 	free_ldapbuf(ldapbuf);
455*dd1104fbSMichen Chang 	free(ldapbuf);
456*dd1104fbSMichen Chang 	return (res);
4577c478bd9Sstevel@tonic-gate }
4587c478bd9Sstevel@tonic-gate 
4597c478bd9Sstevel@tonic-gate /*
4607c478bd9Sstevel@tonic-gate  * new_attr(name, value)
4617c478bd9Sstevel@tonic-gate  *
4627c478bd9Sstevel@tonic-gate  * create a new LDAP attribute to be sent to the server
4637c478bd9Sstevel@tonic-gate  */
4647c478bd9Sstevel@tonic-gate ns_ldap_attr_t *
4657c478bd9Sstevel@tonic-gate new_attr(char *name, char *value)
4667c478bd9Sstevel@tonic-gate {
4677c478bd9Sstevel@tonic-gate 	ns_ldap_attr_t *tmp;
4687c478bd9Sstevel@tonic-gate 
4697c478bd9Sstevel@tonic-gate 	tmp = malloc(sizeof (*tmp));
4707c478bd9Sstevel@tonic-gate 	if (tmp != NULL) {
4717c478bd9Sstevel@tonic-gate 		tmp->attrname = name;
4727c478bd9Sstevel@tonic-gate 		tmp->attrvalue = (char **)calloc(2, sizeof (char *));
4737c478bd9Sstevel@tonic-gate 		if (tmp->attrvalue == NULL) {
4747c478bd9Sstevel@tonic-gate 			free(tmp);
4757c478bd9Sstevel@tonic-gate 			return (NULL);
4767c478bd9Sstevel@tonic-gate 		}
4777c478bd9Sstevel@tonic-gate 		tmp->attrvalue[0] = value;
4787c478bd9Sstevel@tonic-gate 		tmp->value_count = 1;
4797c478bd9Sstevel@tonic-gate 	}
4807c478bd9Sstevel@tonic-gate 
4817c478bd9Sstevel@tonic-gate 	return (tmp);
4827c478bd9Sstevel@tonic-gate }
4837c478bd9Sstevel@tonic-gate 
4847c478bd9Sstevel@tonic-gate /*
485*dd1104fbSMichen Chang  * max_present(list)
486*dd1104fbSMichen Chang  *
487*dd1104fbSMichen Chang  * returns '1' if a ATTR_MAX with value != -1 is present. (in other words:
488*dd1104fbSMichen Chang  * if password aging is to be turned on).
489*dd1104fbSMichen Chang  */
490*dd1104fbSMichen Chang static int
491*dd1104fbSMichen Chang max_present(attrlist *list)
492*dd1104fbSMichen Chang {
493*dd1104fbSMichen Chang 	while (list != NULL)
494*dd1104fbSMichen Chang 		if (list->type == ATTR_MAX && list->data.val_i != -1)
495*dd1104fbSMichen Chang 			return (1);
496*dd1104fbSMichen Chang 		else
497*dd1104fbSMichen Chang 			list = list->next;
498*dd1104fbSMichen Chang 	return (0);
499*dd1104fbSMichen Chang }
500*dd1104fbSMichen Chang 
501*dd1104fbSMichen Chang /*
502*dd1104fbSMichen Chang  * attr_addmod(attrs, idx, item, val)
503*dd1104fbSMichen Chang  *
504*dd1104fbSMichen Chang  * Adds or updates attribute 'item' in ldap_attrs list to value
505*dd1104fbSMichen Chang  * update idx if item is added
506*dd1104fbSMichen Chang  * return:  -1 - PWU_NOMEM/error, 0 - success
507*dd1104fbSMichen Chang  */
508*dd1104fbSMichen Chang static int
509*dd1104fbSMichen Chang attr_addmod(ns_ldap_attr_t **attrs, int *idx, char *item, int value)
510*dd1104fbSMichen Chang {
511*dd1104fbSMichen Chang 	char numbuf[MAX_INT_LEN], *strp;
512*dd1104fbSMichen Chang 	int i;
513*dd1104fbSMichen Chang 
514*dd1104fbSMichen Chang 	/* stringize the value or abort */
515*dd1104fbSMichen Chang 	if (snprintf(numbuf, MAX_INT_LEN, "%d", value) >= MAX_INT_LEN)
516*dd1104fbSMichen Chang 		return (-1);
517*dd1104fbSMichen Chang 
518*dd1104fbSMichen Chang 	/* check for existence and modify existing */
519*dd1104fbSMichen Chang 	for (i = 0; i < *idx; i++) {
520*dd1104fbSMichen Chang 		if (attrs[i] != NULL &&
521*dd1104fbSMichen Chang 		    strcmp(item, attrs[i]->attrname) == 0) {
522*dd1104fbSMichen Chang 			strp = strdup(numbuf);
523*dd1104fbSMichen Chang 			if (strp == NULL)
524*dd1104fbSMichen Chang 				return (-1);
525*dd1104fbSMichen Chang 			free(attrs[i]->attrvalue[0]);
526*dd1104fbSMichen Chang 			attrs[i]->attrvalue[0] = strp;
527*dd1104fbSMichen Chang 			return (0);
528*dd1104fbSMichen Chang 		}
529*dd1104fbSMichen Chang 	}
530*dd1104fbSMichen Chang 	/* else add */
531*dd1104fbSMichen Chang 	strp = strdup(numbuf);
532*dd1104fbSMichen Chang 	if (strp == NULL)
533*dd1104fbSMichen Chang 		return (-1);
534*dd1104fbSMichen Chang 	attrs[*idx] = new_attr(item, strp);
535*dd1104fbSMichen Chang 	if (attrs[*idx] == NULL)
536*dd1104fbSMichen Chang 		return (-1);
537*dd1104fbSMichen Chang 	(*idx)++;
538*dd1104fbSMichen Chang 	return (0);
539*dd1104fbSMichen Chang }
540*dd1104fbSMichen Chang 
541*dd1104fbSMichen Chang /*
5427c478bd9Sstevel@tonic-gate  * ldap_update(items, rep, buf)
5437c478bd9Sstevel@tonic-gate  *
5447c478bd9Sstevel@tonic-gate  * create LDAP attributes in 'buf' for each attribute in 'items'.
5457c478bd9Sstevel@tonic-gate  */
5467c478bd9Sstevel@tonic-gate /*ARGSUSED*/
5477c478bd9Sstevel@tonic-gate int
5487c478bd9Sstevel@tonic-gate ldap_update(attrlist *items, pwu_repository_t *rep, void *buf)
5497c478bd9Sstevel@tonic-gate {
5507c478bd9Sstevel@tonic-gate 	attrlist *p;
5517c478bd9Sstevel@tonic-gate 	ldapbuf_t *ldapbuf = (ldapbuf_t *)buf;
552*dd1104fbSMichen Chang 	struct spwd *spw;
553*dd1104fbSMichen Chang 	ns_ldap_attr_t **pattrs = ldapbuf->pattrs;
554*dd1104fbSMichen Chang 	int pidx = 0;
555*dd1104fbSMichen Chang 	ns_ldap_attr_t **sattrs = ldapbuf->sattrs;
556*dd1104fbSMichen Chang 	int sidx = 0;
5577c478bd9Sstevel@tonic-gate 	char *pwd, *val;
5587c478bd9Sstevel@tonic-gate 	char *salt;
5597c478bd9Sstevel@tonic-gate 	size_t cryptlen;
560*dd1104fbSMichen Chang 	int len;
561*dd1104fbSMichen Chang 	int count;
562*dd1104fbSMichen Chang 	int rc = PWU_SUCCESS;
563*dd1104fbSMichen Chang 	int aging_needed = 0;
564*dd1104fbSMichen Chang 	int aging_set = 0;
565*dd1104fbSMichen Chang 	int disable_aging;
566*dd1104fbSMichen Chang 
567*dd1104fbSMichen Chang 	spw = ldapbuf->spwd;
568*dd1104fbSMichen Chang 
569*dd1104fbSMichen Chang 	/*
570*dd1104fbSMichen Chang 	 * if sp_max==0 and shadow update is enabled:
571*dd1104fbSMichen Chang 	 * disable passwd aging after updating the password
572*dd1104fbSMichen Chang 	 */
573*dd1104fbSMichen Chang 	disable_aging = (spw != NULL && spw->sp_max == 0 &&
574*dd1104fbSMichen Chang 	    ldapbuf->shadow_update_enabled);
5757c478bd9Sstevel@tonic-gate 
5767c478bd9Sstevel@tonic-gate 	for (p = items; p != NULL; p = p->next) {
5777c478bd9Sstevel@tonic-gate 		switch (p->type) {
5787c478bd9Sstevel@tonic-gate 		case ATTR_PASSWD:
579*dd1104fbSMichen Chang 			/*
580*dd1104fbSMichen Chang 			 * There is a special case for ldap: if the
581*dd1104fbSMichen Chang 			 * password is to be deleted (-d to passwd),
582*dd1104fbSMichen Chang 			 * p->data.val_s will be NULL.
583*dd1104fbSMichen Chang 			 */
584*dd1104fbSMichen Chang 			if (p->data.val_s == NULL) {
585*dd1104fbSMichen Chang 				if (!ldapbuf->shadow_update_enabled)
586*dd1104fbSMichen Chang 					return (PWU_CHANGE_NOT_ALLOWED);
587*dd1104fbSMichen Chang 				cryptlen =
588*dd1104fbSMichen Chang 				    sizeof ("{crypt}" NS_LDAP_NO_UNIX_PASSWORD);
589*dd1104fbSMichen Chang 				val = malloc(cryptlen);
590*dd1104fbSMichen Chang 				if (val == NULL)
591*dd1104fbSMichen Chang 					return (PWU_NOMEM);
592*dd1104fbSMichen Chang 				(void) snprintf(val, cryptlen,
593*dd1104fbSMichen Chang 				"{crypt}" NS_LDAP_NO_UNIX_PASSWORD);
594*dd1104fbSMichen Chang 			} else { /* not deleting password */
595*dd1104fbSMichen Chang 				salt = crypt_gensalt(ldapbuf->passwd,
596*dd1104fbSMichen Chang 				    ldapbuf->pwd);
5977c478bd9Sstevel@tonic-gate 
5987c478bd9Sstevel@tonic-gate 				if (salt == NULL) {
5997c478bd9Sstevel@tonic-gate 					if (errno == ENOMEM)
6007c478bd9Sstevel@tonic-gate 						return (PWU_NOMEM);
601*dd1104fbSMichen Chang 
6027c478bd9Sstevel@tonic-gate 					/* algorithm problem? */
6037c478bd9Sstevel@tonic-gate 					syslog(LOG_AUTH | LOG_ALERT,
6047c478bd9Sstevel@tonic-gate 					    "passwdutil: crypt_gensalt "
6057c478bd9Sstevel@tonic-gate 					    "%m");
6067c478bd9Sstevel@tonic-gate 					return (PWU_UPDATE_FAILED);
6077c478bd9Sstevel@tonic-gate 				}
6087c478bd9Sstevel@tonic-gate 
6097c478bd9Sstevel@tonic-gate 				pwd = crypt(p->data.val_s, salt);
6107c478bd9Sstevel@tonic-gate 				free(salt);
6117c478bd9Sstevel@tonic-gate 				cryptlen = strlen(pwd) + sizeof ("{crypt}");
6127c478bd9Sstevel@tonic-gate 				val = malloc(cryptlen);
6137c478bd9Sstevel@tonic-gate 				if (val == NULL)
6147c478bd9Sstevel@tonic-gate 					return (PWU_NOMEM);
615*dd1104fbSMichen Chang 				(void) snprintf(val, cryptlen,
616*dd1104fbSMichen Chang 				    "{crypt}%s", pwd);
617*dd1104fbSMichen Chang 			}
6187c478bd9Sstevel@tonic-gate 
619*dd1104fbSMichen Chang 			/*
620*dd1104fbSMichen Chang 			 * If not managing passwordAccount,
621*dd1104fbSMichen Chang 			 * insert the new password in the
622*dd1104fbSMichen Chang 			 * passwd attr array and break.
623*dd1104fbSMichen Chang 			 */
624*dd1104fbSMichen Chang 			if (!ldapbuf->shadow_update_enabled) {
625*dd1104fbSMichen Chang 				NEW_ATTR(pattrs, pidx,
626*dd1104fbSMichen Chang 				    _PWD_USERPASSWORD, val);
6277c478bd9Sstevel@tonic-gate 				break;
628*dd1104fbSMichen Chang 			}
629*dd1104fbSMichen Chang 
630*dd1104fbSMichen Chang 			/*
631*dd1104fbSMichen Chang 			 * Managing passwordAccount, insert the
632*dd1104fbSMichen Chang 			 * new password, along with lastChange and
633*dd1104fbSMichen Chang 			 * shadowFlag, in the shadow attr array.
634*dd1104fbSMichen Chang 			 */
635*dd1104fbSMichen Chang 			NEW_ATTR(sattrs, sidx, _PWD_USERPASSWORD, val);
636*dd1104fbSMichen Chang 
637*dd1104fbSMichen Chang 			if (attr_addmod(sattrs, &sidx, _S_LASTCHANGE,
638*dd1104fbSMichen Chang 			    DAY_NOW_32) < 0)
639*dd1104fbSMichen Chang 				return (PWU_NOMEM);
640*dd1104fbSMichen Chang 			spw->sp_lstchg = DAY_NOW_32;
641*dd1104fbSMichen Chang 
642*dd1104fbSMichen Chang 			if (attr_addmod(sattrs, &sidx, _S_FLAG,
643*dd1104fbSMichen Chang 			    spw->sp_flag & ~FAILCOUNT_MASK) < 0)
644*dd1104fbSMichen Chang 				return (PWU_NOMEM);
645*dd1104fbSMichen Chang 			spw->sp_flag &= ~FAILCOUNT_MASK; /* reset count */
646*dd1104fbSMichen Chang 			aging_needed = 1;
647*dd1104fbSMichen Chang 			break;
648*dd1104fbSMichen Chang 		case ATTR_PASSWD_SERVER_POLICY:
6497c478bd9Sstevel@tonic-gate 			/*
6507c478bd9Sstevel@tonic-gate 			 * For server policy, don't crypt the password,
6517c478bd9Sstevel@tonic-gate 			 * send the password as is to the server and
6527c478bd9Sstevel@tonic-gate 			 * let the LDAP server do its own password
6537c478bd9Sstevel@tonic-gate 			 * encryption
6547c478bd9Sstevel@tonic-gate 			 */
655*dd1104fbSMichen Chang 			STRDUP_OR_RET(val, p->data.val_s);
6567c478bd9Sstevel@tonic-gate 
657*dd1104fbSMichen Chang 			NEW_ATTR(pattrs, pidx, _PWD_USERPASSWORD, val);
6587c478bd9Sstevel@tonic-gate 			break;
6597c478bd9Sstevel@tonic-gate 		case ATTR_COMMENT:
6607c478bd9Sstevel@tonic-gate 			/* XX correct? */
661*dd1104fbSMichen Chang 			NEW_ATTR(pattrs, pidx, _PWD_DESCRIPTION, p->data.val_s);
6627c478bd9Sstevel@tonic-gate 			break;
6637c478bd9Sstevel@tonic-gate 		case ATTR_GECOS:
664*dd1104fbSMichen Chang 			if (!ldapbuf->shadow_update_enabled) {
665*dd1104fbSMichen Chang 				NEW_ATTR(pattrs, pidx, _PWD_GECOS,
666*dd1104fbSMichen Chang 				    p->data.val_s);
667*dd1104fbSMichen Chang 			} else {
668*dd1104fbSMichen Chang 				NEW_ATTR(sattrs, sidx, _PWD_GECOS,
669*dd1104fbSMichen Chang 				    p->data.val_s);
670*dd1104fbSMichen Chang 			}
6717c478bd9Sstevel@tonic-gate 			break;
6727c478bd9Sstevel@tonic-gate 		case ATTR_HOMEDIR:
673*dd1104fbSMichen Chang 			if (!ldapbuf->shadow_update_enabled) {
674*dd1104fbSMichen Chang 				NEW_ATTR(pattrs, pidx, _PWD_HOMEDIRECTORY,
6757c478bd9Sstevel@tonic-gate 				    p->data.val_s);
676*dd1104fbSMichen Chang 			} else {
677*dd1104fbSMichen Chang 				NEW_ATTR(sattrs, sidx, _PWD_HOMEDIRECTORY,
678*dd1104fbSMichen Chang 				    p->data.val_s);
679*dd1104fbSMichen Chang 			}
6807c478bd9Sstevel@tonic-gate 			break;
6817c478bd9Sstevel@tonic-gate 		case ATTR_SHELL:
682*dd1104fbSMichen Chang 			if (!ldapbuf->shadow_update_enabled) {
683*dd1104fbSMichen Chang 				NEW_ATTR(pattrs, pidx, _PWD_LOGINSHELL,
684*dd1104fbSMichen Chang 				    p->data.val_s);
685*dd1104fbSMichen Chang 			} else {
686*dd1104fbSMichen Chang 				NEW_ATTR(sattrs, sidx, _PWD_LOGINSHELL,
687*dd1104fbSMichen Chang 				    p->data.val_s);
688*dd1104fbSMichen Chang 			}
6897c478bd9Sstevel@tonic-gate 			break;
690*dd1104fbSMichen Chang 		/* We don't update NAME, UID, GID */
6917c478bd9Sstevel@tonic-gate 		case ATTR_NAME:
6927c478bd9Sstevel@tonic-gate 		case ATTR_UID:
6937c478bd9Sstevel@tonic-gate 		case ATTR_GID:
694*dd1104fbSMichen Chang 		/* Unsupported item */
6957c478bd9Sstevel@tonic-gate 		case ATTR_AGE:
696*dd1104fbSMichen Chang 			break;
697*dd1104fbSMichen Chang 		case ATTR_LOCK_ACCOUNT:
698*dd1104fbSMichen Chang 			if (!ldapbuf->shadow_update_enabled)
699*dd1104fbSMichen Chang 				break;	/* not managing passwordAccount */
700*dd1104fbSMichen Chang 			if (spw->sp_pwdp == NULL) {
701*dd1104fbSMichen Chang 				spw->sp_pwdp = LOCKSTRING;
702*dd1104fbSMichen Chang 			} else if (strncmp(spw->sp_pwdp, LOCKSTRING,
703*dd1104fbSMichen Chang 			    sizeof (LOCKSTRING)-1) != 0) {
704*dd1104fbSMichen Chang 				len = sizeof (LOCKSTRING)-1 +
705*dd1104fbSMichen Chang 				    strlen(spw->sp_pwdp) + 1 +
706*dd1104fbSMichen Chang 				    sizeof ("{crypt}");
707*dd1104fbSMichen Chang 				pwd = malloc(len);
708*dd1104fbSMichen Chang 				if (pwd == NULL) {
709*dd1104fbSMichen Chang 					return (PWU_NOMEM);
710*dd1104fbSMichen Chang 				}
711*dd1104fbSMichen Chang 				(void) strlcpy(pwd, "{crypt}", len);
712*dd1104fbSMichen Chang 				(void) strlcat(pwd, LOCKSTRING, len);
713*dd1104fbSMichen Chang 				(void) strlcat(pwd, spw->sp_pwdp, len);
714*dd1104fbSMichen Chang 				free(spw->sp_pwdp);
715*dd1104fbSMichen Chang 				spw->sp_pwdp = pwd;
716*dd1104fbSMichen Chang 				NEW_ATTR(sattrs, sidx, _PWD_USERPASSWORD,
717*dd1104fbSMichen Chang 				    spw->sp_pwdp);
718*dd1104fbSMichen Chang 			}
719*dd1104fbSMichen Chang 			if (attr_addmod(sattrs, &sidx, _S_LASTCHANGE,
720*dd1104fbSMichen Chang 			    DAY_NOW_32) < 0)
721*dd1104fbSMichen Chang 				return (PWU_NOMEM);
722*dd1104fbSMichen Chang 			spw->sp_lstchg = DAY_NOW_32;
723*dd1104fbSMichen Chang 			break;
724*dd1104fbSMichen Chang 
725*dd1104fbSMichen Chang 		case ATTR_UNLOCK_ACCOUNT:
726*dd1104fbSMichen Chang 			if (!ldapbuf->shadow_update_enabled)
727*dd1104fbSMichen Chang 				break;	/* not managing passwordAccount */
728*dd1104fbSMichen Chang 			if (spw->sp_pwdp &&
729*dd1104fbSMichen Chang 			    strncmp(spw->sp_pwdp, LOCKSTRING,
730*dd1104fbSMichen Chang 			    sizeof (LOCKSTRING)-1) == 0) {
731*dd1104fbSMichen Chang 				len = (sizeof ("{crypt}") -
732*dd1104fbSMichen Chang 				    sizeof (LOCKSTRING)) +
733*dd1104fbSMichen Chang 				    strlen(spw->sp_pwdp) + 1;
734*dd1104fbSMichen Chang 				pwd = malloc(len);
735*dd1104fbSMichen Chang 				if (pwd == NULL) {
736*dd1104fbSMichen Chang 					return (PWU_NOMEM);
737*dd1104fbSMichen Chang 				}
738*dd1104fbSMichen Chang 				(void) strlcpy(pwd, "{crypt}", len);
739*dd1104fbSMichen Chang 				(void) strlcat(pwd, spw->sp_pwdp +
740*dd1104fbSMichen Chang 				    sizeof (LOCKSTRING)-1, len);
741*dd1104fbSMichen Chang 				free(spw->sp_pwdp);
742*dd1104fbSMichen Chang 				spw->sp_pwdp = pwd;
743*dd1104fbSMichen Chang 
744*dd1104fbSMichen Chang 				NEW_ATTR(sattrs, sidx, _PWD_USERPASSWORD,
745*dd1104fbSMichen Chang 				    spw->sp_pwdp);
746*dd1104fbSMichen Chang 				if (attr_addmod(sattrs, &sidx, _S_LASTCHANGE,
747*dd1104fbSMichen Chang 				    DAY_NOW_32) < 0)
748*dd1104fbSMichen Chang 					return (PWU_NOMEM);
749*dd1104fbSMichen Chang 				spw->sp_lstchg = DAY_NOW_32;
750*dd1104fbSMichen Chang 			}
751*dd1104fbSMichen Chang 			break;
752*dd1104fbSMichen Chang 
753*dd1104fbSMichen Chang 		case ATTR_NOLOGIN_ACCOUNT:
754*dd1104fbSMichen Chang 			if (!ldapbuf->shadow_update_enabled)
755*dd1104fbSMichen Chang 				break;	/* not managing passwordAccount */
756*dd1104fbSMichen Chang 			free(spw->sp_pwdp);
757*dd1104fbSMichen Chang 			STRDUP_OR_RET(spw->sp_pwdp, "{crypt}" NOLOGINSTRING);
758*dd1104fbSMichen Chang 			NEW_ATTR(sattrs, sidx, _PWD_USERPASSWORD, spw->sp_pwdp);
759*dd1104fbSMichen Chang 			if (attr_addmod(sattrs, &sidx, _S_LASTCHANGE,
760*dd1104fbSMichen Chang 			    DAY_NOW_32) < 0)
761*dd1104fbSMichen Chang 				return (PWU_NOMEM);
762*dd1104fbSMichen Chang 			spw->sp_lstchg = DAY_NOW_32;
763*dd1104fbSMichen Chang 			break;
764*dd1104fbSMichen Chang 
765*dd1104fbSMichen Chang 		case ATTR_EXPIRE_PASSWORD:
766*dd1104fbSMichen Chang 			if (!ldapbuf->shadow_update_enabled)
767*dd1104fbSMichen Chang 				break;	/* not managing passwordAccount */
768*dd1104fbSMichen Chang 			NUM_TO_STR(val, 0);
769*dd1104fbSMichen Chang 			NEW_ATTR(sattrs, sidx, _S_LASTCHANGE, val);
770*dd1104fbSMichen Chang 			break;
771*dd1104fbSMichen Chang 
7727c478bd9Sstevel@tonic-gate 		case ATTR_LSTCHG:
773*dd1104fbSMichen Chang 			if (!ldapbuf->shadow_update_enabled)
774*dd1104fbSMichen Chang 				break;	/* not managing passwordAccount */
775*dd1104fbSMichen Chang 			NUM_TO_STR(val, p->data.val_i);
776*dd1104fbSMichen Chang 			NEW_ATTR(sattrs, sidx, _S_LASTCHANGE, val);
777*dd1104fbSMichen Chang 			break;
778*dd1104fbSMichen Chang 
7797c478bd9Sstevel@tonic-gate 		case ATTR_MIN:
780*dd1104fbSMichen Chang 			if (!ldapbuf->shadow_update_enabled)
781*dd1104fbSMichen Chang 				break;	/* not managing passwordAccount */
782*dd1104fbSMichen Chang 			if (spw->sp_max == -1 && p->data.val_i != -1 &&
783*dd1104fbSMichen Chang 			    max_present(p->next) == 0)
784*dd1104fbSMichen Chang 				return (PWU_AGING_DISABLED);
785*dd1104fbSMichen Chang 			NUM_TO_STR(val, p->data.val_i);
786*dd1104fbSMichen Chang 			NEW_ATTR(sattrs, sidx, _S_MIN, val);
787*dd1104fbSMichen Chang 			aging_set = 1;
788*dd1104fbSMichen Chang 			break;
789*dd1104fbSMichen Chang 
7907c478bd9Sstevel@tonic-gate 		case ATTR_MAX:
791*dd1104fbSMichen Chang 			if (!ldapbuf->shadow_update_enabled)
792*dd1104fbSMichen Chang 				break;	/* not managing passwordAccount */
793*dd1104fbSMichen Chang 			if (p->data.val_i == -1) {
794*dd1104fbSMichen Chang 				/* Turn off aging. Reset min and warn too */
795*dd1104fbSMichen Chang 				spw->sp_max = spw->sp_min = spw->sp_warn = -1;
796*dd1104fbSMichen Chang 				NUM_TO_STR(val, -1);
797*dd1104fbSMichen Chang 				NEW_ATTR(sattrs, sidx, _S_MIN, val);
798*dd1104fbSMichen Chang 				NUM_TO_STR(val, -1);
799*dd1104fbSMichen Chang 				NEW_ATTR(sattrs, sidx, _S_WARNING, val);
800*dd1104fbSMichen Chang 			} else {
801*dd1104fbSMichen Chang 				/* Turn account aging on */
802*dd1104fbSMichen Chang 				if (spw->sp_min == -1) {
803*dd1104fbSMichen Chang 					/*
804*dd1104fbSMichen Chang 					 * minage was not set with command-
805*dd1104fbSMichen Chang 					 * line option: set to zero
806*dd1104fbSMichen Chang 					 */
807*dd1104fbSMichen Chang 					spw->sp_min = 0;
808*dd1104fbSMichen Chang 					NUM_TO_STR(val, 0);
809*dd1104fbSMichen Chang 					NEW_ATTR(sattrs, sidx, _S_MIN,
810*dd1104fbSMichen Chang 					    val);
811*dd1104fbSMichen Chang 				}
812*dd1104fbSMichen Chang 				/*
813*dd1104fbSMichen Chang 				 * If aging was turned off, we update lstchg.
814*dd1104fbSMichen Chang 				 * We take care not to update lstchg if the
815*dd1104fbSMichen Chang 				 * user has no password, otherwise the user
816*dd1104fbSMichen Chang 				 * might not be required to provide a password
817*dd1104fbSMichen Chang 				 * the next time [s]he logs in.
818*dd1104fbSMichen Chang 				 *
819*dd1104fbSMichen Chang 				 * Also, if lstchg != -1 (i.e., not set)
820*dd1104fbSMichen Chang 				 * we keep the old value.
821*dd1104fbSMichen Chang 				 */
822*dd1104fbSMichen Chang 				if (spw->sp_max == -1 &&
823*dd1104fbSMichen Chang 				    spw->sp_pwdp != NULL && *spw->sp_pwdp &&
824*dd1104fbSMichen Chang 				    spw->sp_lstchg == -1) {
825*dd1104fbSMichen Chang 					if (attr_addmod(sattrs, &sidx,
826*dd1104fbSMichen Chang 					    _S_LASTCHANGE,
827*dd1104fbSMichen Chang 					    DAY_NOW_32) < 0)
828*dd1104fbSMichen Chang 						return (PWU_NOMEM);
829*dd1104fbSMichen Chang 					spw->sp_lstchg = DAY_NOW_32;
830*dd1104fbSMichen Chang 				}
831*dd1104fbSMichen Chang 			}
832*dd1104fbSMichen Chang 			NUM_TO_STR(val, p->data.val_i);
833*dd1104fbSMichen Chang 			NEW_ATTR(sattrs, sidx, _S_MAX, val);
834*dd1104fbSMichen Chang 			aging_set = 1;
835*dd1104fbSMichen Chang 			break;
836*dd1104fbSMichen Chang 
8377c478bd9Sstevel@tonic-gate 		case ATTR_WARN:
838*dd1104fbSMichen Chang 			if (!ldapbuf->shadow_update_enabled)
839*dd1104fbSMichen Chang 				break;	/* not managing passwordAccount */
840*dd1104fbSMichen Chang 			if (spw->sp_max == -1 &&
841*dd1104fbSMichen Chang 			    p->data.val_i != -1 && max_present(p->next) == 0)
842*dd1104fbSMichen Chang 				return (PWU_AGING_DISABLED);
843*dd1104fbSMichen Chang 			NUM_TO_STR(val, p->data.val_i);
844*dd1104fbSMichen Chang 			NEW_ATTR(sattrs, sidx, _S_WARNING, val);
845*dd1104fbSMichen Chang 			break;
846*dd1104fbSMichen Chang 
8477c478bd9Sstevel@tonic-gate 		case ATTR_INACT:
848*dd1104fbSMichen Chang 			if (!ldapbuf->shadow_update_enabled)
849*dd1104fbSMichen Chang 				break;	/* not managing passwordAccount */
850*dd1104fbSMichen Chang 			NUM_TO_STR(val, p->data.val_i);
851*dd1104fbSMichen Chang 			NEW_ATTR(sattrs, sidx, _S_INACTIVE, val);
852*dd1104fbSMichen Chang 			break;
853*dd1104fbSMichen Chang 
8547c478bd9Sstevel@tonic-gate 		case ATTR_EXPIRE:
855*dd1104fbSMichen Chang 			if (!ldapbuf->shadow_update_enabled)
856*dd1104fbSMichen Chang 				break;	/* not managing passwordAccount */
857*dd1104fbSMichen Chang 			NUM_TO_STR(val, p->data.val_i);
858*dd1104fbSMichen Chang 			NEW_ATTR(sattrs, sidx, _S_EXPIRE, val);
859*dd1104fbSMichen Chang 			break;
860*dd1104fbSMichen Chang 
8617c478bd9Sstevel@tonic-gate 		case ATTR_FLAG:
862*dd1104fbSMichen Chang 			if (!ldapbuf->shadow_update_enabled)
863*dd1104fbSMichen Chang 				break;	/* not managing passwordAccount */
864*dd1104fbSMichen Chang 			NUM_TO_STR(val, p->data.val_i);
865*dd1104fbSMichen Chang 			NEW_ATTR(sattrs, sidx, _S_FLAG, val);
866*dd1104fbSMichen Chang 			break;
867*dd1104fbSMichen Chang 		case ATTR_INCR_FAILED_LOGINS:
868*dd1104fbSMichen Chang 			if (!ldapbuf->shadow_update_enabled) {
869*dd1104fbSMichen Chang 				rc = PWU_CHANGE_NOT_ALLOWED;
870*dd1104fbSMichen Chang 				break;	/* not managing passwordAccount */
871*dd1104fbSMichen Chang 			}
872*dd1104fbSMichen Chang 			count = (spw->sp_flag & FAILCOUNT_MASK) + 1;
873*dd1104fbSMichen Chang 			spw->sp_flag &= ~FAILCOUNT_MASK;
874*dd1104fbSMichen Chang 			spw->sp_flag |= min(FAILCOUNT_MASK, count);
875*dd1104fbSMichen Chang 			p->data.val_i = count;
876*dd1104fbSMichen Chang 			NUM_TO_STR(val, spw->sp_flag);
877*dd1104fbSMichen Chang 			NEW_ATTR(sattrs, sidx, _S_FLAG, val);
878*dd1104fbSMichen Chang 			break;
879*dd1104fbSMichen Chang 		case ATTR_RST_FAILED_LOGINS:
880*dd1104fbSMichen Chang 			if (!ldapbuf->shadow_update_enabled) {
881*dd1104fbSMichen Chang 				rc = PWU_CHANGE_NOT_ALLOWED;
882*dd1104fbSMichen Chang 				break;	/* not managing passwordAccount */
883*dd1104fbSMichen Chang 			}
884*dd1104fbSMichen Chang 			p->data.val_i = spw->sp_flag & FAILCOUNT_MASK;
885*dd1104fbSMichen Chang 			spw->sp_flag &= ~FAILCOUNT_MASK;
886*dd1104fbSMichen Chang 			NUM_TO_STR(val, spw->sp_flag);
887*dd1104fbSMichen Chang 			NEW_ATTR(sattrs, sidx, _S_FLAG, val);
8887c478bd9Sstevel@tonic-gate 			break;
8897c478bd9Sstevel@tonic-gate 		default:
8907c478bd9Sstevel@tonic-gate 			break;
8917c478bd9Sstevel@tonic-gate 		}
8927c478bd9Sstevel@tonic-gate 	}
8937c478bd9Sstevel@tonic-gate 
894*dd1104fbSMichen Chang 	/*
895*dd1104fbSMichen Chang 	 * If the ldap client is configured with shadow update enabled,
896*dd1104fbSMichen Chang 	 * then what should the new aging values look like?
897*dd1104fbSMichen Chang 	 *
898*dd1104fbSMichen Chang 	 * There are a number of different conditions
899*dd1104fbSMichen Chang 	 *
900*dd1104fbSMichen Chang 	 *  a) aging is already configured: don't touch it
901*dd1104fbSMichen Chang 	 *
902*dd1104fbSMichen Chang 	 *  b) disable_aging is set: disable aging
903*dd1104fbSMichen Chang 	 *
904*dd1104fbSMichen Chang 	 *  c) aging is not configured: turn on default aging;
905*dd1104fbSMichen Chang 	 *
906*dd1104fbSMichen Chang 	 *  b) and c) of course only if aging_needed and !aging_set.
907*dd1104fbSMichen Chang 	 *  (i.e., password changed, and aging values not changed)
908*dd1104fbSMichen Chang 	 */
9097c478bd9Sstevel@tonic-gate 
910*dd1104fbSMichen Chang 	if (ldapbuf->shadow_update_enabled && spw != NULL && spw->sp_max <= 0) {
911*dd1104fbSMichen Chang 		/* a) aging not yet configured */
912*dd1104fbSMichen Chang 		if (aging_needed && !aging_set) {
913*dd1104fbSMichen Chang 			if (disable_aging) {
914*dd1104fbSMichen Chang 				/* b) turn off aging */
915*dd1104fbSMichen Chang 				spw->sp_min = spw->sp_max = spw->sp_warn = -1;
916*dd1104fbSMichen Chang 				if (attr_addmod(sattrs, &sidx, _S_MIN, -1) < 0)
917*dd1104fbSMichen Chang 					return (PWU_NOMEM);
918*dd1104fbSMichen Chang 				if (attr_addmod(sattrs, &sidx, _S_MAX, -1) < 0)
919*dd1104fbSMichen Chang 					return (PWU_NOMEM);
920*dd1104fbSMichen Chang 				if (attr_addmod(sattrs, &sidx, _S_WARNING,
921*dd1104fbSMichen Chang 				    -1) < 0)
922*dd1104fbSMichen Chang 					return (PWU_NOMEM);
923*dd1104fbSMichen Chang 			} else {
924*dd1104fbSMichen Chang 				/* c) */
925*dd1104fbSMichen Chang 				turn_on_default_aging(spw);
926*dd1104fbSMichen Chang 
927*dd1104fbSMichen Chang 				if (attr_addmod(sattrs, &sidx, _S_MIN,
928*dd1104fbSMichen Chang 				    spw->sp_min) < 0)
929*dd1104fbSMichen Chang 					return (PWU_NOMEM);
930*dd1104fbSMichen Chang 				if (attr_addmod(sattrs, &sidx, _S_MAX,
931*dd1104fbSMichen Chang 				    spw->sp_max) < 0)
932*dd1104fbSMichen Chang 					return (PWU_NOMEM);
933*dd1104fbSMichen Chang 				if (attr_addmod(sattrs, &sidx,
934*dd1104fbSMichen Chang 				    _S_WARNING, spw->sp_warn) < 0)
935*dd1104fbSMichen Chang 					return (PWU_NOMEM);
936*dd1104fbSMichen Chang 			}
937*dd1104fbSMichen Chang 		}
938*dd1104fbSMichen Chang 	}
939*dd1104fbSMichen Chang 
940*dd1104fbSMichen Chang 	pattrs[pidx] = NULL;
941*dd1104fbSMichen Chang 	sattrs[sidx] = NULL;
942*dd1104fbSMichen Chang 
943*dd1104fbSMichen Chang 	return (rc);
9447c478bd9Sstevel@tonic-gate }
9457c478bd9Sstevel@tonic-gate 
9467c478bd9Sstevel@tonic-gate /*
9477c478bd9Sstevel@tonic-gate  * ldap_to_pwu_code(error, pwd_status)
9487c478bd9Sstevel@tonic-gate  *
9497c478bd9Sstevel@tonic-gate  * translation from LDAP return values and PWU return values
9507c478bd9Sstevel@tonic-gate  */
9517c478bd9Sstevel@tonic-gate int
9527c478bd9Sstevel@tonic-gate ldap_to_pwu_code(int error, int pwd_status)
9537c478bd9Sstevel@tonic-gate {
9547c478bd9Sstevel@tonic-gate 	switch (error) {
9557c478bd9Sstevel@tonic-gate 	case NS_LDAP_SUCCESS:	return (PWU_SUCCESS);
9567c478bd9Sstevel@tonic-gate 	case NS_LDAP_OP_FAILED:	return (PWU_DENIED);
9577c478bd9Sstevel@tonic-gate 	case NS_LDAP_NOTFOUND:	return (PWU_NOT_FOUND);
9587c478bd9Sstevel@tonic-gate 	case NS_LDAP_MEMORY:	return (PWU_NOMEM);
9597c478bd9Sstevel@tonic-gate 	case NS_LDAP_CONFIG:	return (PWU_NOT_FOUND);
9607c478bd9Sstevel@tonic-gate 	case NS_LDAP_INTERNAL:
9617c478bd9Sstevel@tonic-gate 		switch (pwd_status) {
9627c478bd9Sstevel@tonic-gate 		case NS_PASSWD_EXPIRED:
9637c478bd9Sstevel@tonic-gate 			return (PWU_DENIED);
9647c478bd9Sstevel@tonic-gate 		case NS_PASSWD_CHANGE_NOT_ALLOWED:
9657c478bd9Sstevel@tonic-gate 			return (PWU_CHANGE_NOT_ALLOWED);
9667c478bd9Sstevel@tonic-gate 		case NS_PASSWD_TOO_SHORT:
9677c478bd9Sstevel@tonic-gate 			return (PWU_PWD_TOO_SHORT);
9687c478bd9Sstevel@tonic-gate 		case NS_PASSWD_INVALID_SYNTAX:
9697c478bd9Sstevel@tonic-gate 			return (PWU_PWD_INVALID);
9707c478bd9Sstevel@tonic-gate 		case NS_PASSWD_IN_HISTORY:
9717c478bd9Sstevel@tonic-gate 			return (PWU_PWD_IN_HISTORY);
9727c478bd9Sstevel@tonic-gate 		case NS_PASSWD_WITHIN_MIN_AGE:
9737c478bd9Sstevel@tonic-gate 			return (PWU_WITHIN_MIN_AGE);
9747c478bd9Sstevel@tonic-gate 		default:
9757c478bd9Sstevel@tonic-gate 			return (PWU_SYSTEM_ERROR);
9767c478bd9Sstevel@tonic-gate 		}
9777c478bd9Sstevel@tonic-gate 	default:		return (PWU_SYSTEM_ERROR);
9787c478bd9Sstevel@tonic-gate 	}
9797c478bd9Sstevel@tonic-gate }
9807c478bd9Sstevel@tonic-gate 
9817c478bd9Sstevel@tonic-gate int
9827c478bd9Sstevel@tonic-gate ldap_replaceattr(const char *dn, ns_ldap_attr_t **attrs, const char *binddn,
983*dd1104fbSMichen Chang 	const char *pwd, int *pwd_status, int flags)
9847c478bd9Sstevel@tonic-gate {
9857c478bd9Sstevel@tonic-gate 	int		result = NS_LDAP_OP_FAILED;
9867c478bd9Sstevel@tonic-gate 	int		ldaprc;
9877c478bd9Sstevel@tonic-gate 	int		authstried = 0;
9887c478bd9Sstevel@tonic-gate 	char		**certpath = NULL;
9897c478bd9Sstevel@tonic-gate 	ns_auth_t	**app;
9907c478bd9Sstevel@tonic-gate 	ns_auth_t	**authpp = NULL;
9917c478bd9Sstevel@tonic-gate 	ns_auth_t	*authp = NULL;
9927c478bd9Sstevel@tonic-gate 	ns_cred_t	*credp;
9937c478bd9Sstevel@tonic-gate 	ns_ldap_error_t	*errorp = NULL;
9947c478bd9Sstevel@tonic-gate 
9957c478bd9Sstevel@tonic-gate 	debug("%s: replace_ldapattr()", __FILE__);
9967c478bd9Sstevel@tonic-gate 
9977c478bd9Sstevel@tonic-gate 	if ((credp = (ns_cred_t *)calloc(1, sizeof (ns_cred_t))) == NULL)
998*dd1104fbSMichen Chang 		return (NS_LDAP_MEMORY); /* map to PWU_NOMEM */
9997c478bd9Sstevel@tonic-gate 
1000*dd1104fbSMichen Chang 	/* for admin shadow update, dn and pwd will be set later in libsldap */
1001*dd1104fbSMichen Chang 	if ((flags & NS_LDAP_UPDATE_SHADOW) == 0) {
10027c478bd9Sstevel@tonic-gate 		/* Fill in the user name and password */
10037c478bd9Sstevel@tonic-gate 		if (dn == NULL || pwd == NULL)
10047c478bd9Sstevel@tonic-gate 			goto out;
10057c478bd9Sstevel@tonic-gate 		credp->cred.unix_cred.userID = strdup(binddn);
10067c478bd9Sstevel@tonic-gate 		credp->cred.unix_cred.passwd = strdup(pwd);
1007*dd1104fbSMichen Chang 	}
10087c478bd9Sstevel@tonic-gate 
10097c478bd9Sstevel@tonic-gate 	/* get host certificate path, if one is configured */
10107c478bd9Sstevel@tonic-gate 	ldaprc = __ns_ldap_getParam(NS_LDAP_HOST_CERTPATH_P,
10117c478bd9Sstevel@tonic-gate 	    (void ***)&certpath, &errorp);
10127c478bd9Sstevel@tonic-gate 	if (ldaprc != NS_LDAP_SUCCESS)
10137c478bd9Sstevel@tonic-gate 		goto out;
10147c478bd9Sstevel@tonic-gate 
10157c478bd9Sstevel@tonic-gate 	if (certpath && *certpath)
10167c478bd9Sstevel@tonic-gate 		credp->hostcertpath = *certpath;
10177c478bd9Sstevel@tonic-gate 
10187c478bd9Sstevel@tonic-gate 	/* Load the service specific authentication method */
10197c478bd9Sstevel@tonic-gate 	ldaprc = __ns_ldap_getServiceAuthMethods("passwd-cmd", &authpp,
10207c478bd9Sstevel@tonic-gate 	    &errorp);
10217c478bd9Sstevel@tonic-gate 
10227c478bd9Sstevel@tonic-gate 	if (ldaprc != NS_LDAP_SUCCESS)
10237c478bd9Sstevel@tonic-gate 		goto out;
10247c478bd9Sstevel@tonic-gate 
10257c478bd9Sstevel@tonic-gate 	/*
10267c478bd9Sstevel@tonic-gate 	 * if authpp is null, there is no serviceAuthenticationMethod
10277c478bd9Sstevel@tonic-gate 	 * try default authenticationMethod
10287c478bd9Sstevel@tonic-gate 	 */
10297c478bd9Sstevel@tonic-gate 	if (authpp == NULL) {
10307c478bd9Sstevel@tonic-gate 		ldaprc = __ns_ldap_getParam(NS_LDAP_AUTH_P, (void ***)&authpp,
10317c478bd9Sstevel@tonic-gate 		    &errorp);
10327c478bd9Sstevel@tonic-gate 		if (ldaprc != NS_LDAP_SUCCESS)
10337c478bd9Sstevel@tonic-gate 			goto out;
10347c478bd9Sstevel@tonic-gate 	}
10357c478bd9Sstevel@tonic-gate 
10367c478bd9Sstevel@tonic-gate 	/*
10377c478bd9Sstevel@tonic-gate 	 * if authpp is still null, then can not authenticate, syslog
10387c478bd9Sstevel@tonic-gate 	 * error message and return error
10397c478bd9Sstevel@tonic-gate 	 */
10407c478bd9Sstevel@tonic-gate 	if (authpp == NULL) {
10417c478bd9Sstevel@tonic-gate 		syslog(LOG_ERR,
10427c478bd9Sstevel@tonic-gate 		"passwdutil: no legal LDAP authentication method configured");
10437c478bd9Sstevel@tonic-gate 		result = NS_LDAP_OP_FAILED;
10447c478bd9Sstevel@tonic-gate 		goto out;
10457c478bd9Sstevel@tonic-gate 	}
10467c478bd9Sstevel@tonic-gate 
10477c478bd9Sstevel@tonic-gate 	/*
10487c478bd9Sstevel@tonic-gate 	 * Walk the array and try all authentication methods in order except
10497c478bd9Sstevel@tonic-gate 	 * for "none".
10507c478bd9Sstevel@tonic-gate 	 */
10517c478bd9Sstevel@tonic-gate 	for (app = authpp; *app; app++) {
10527c478bd9Sstevel@tonic-gate 		authp = *app;
10537c478bd9Sstevel@tonic-gate 		/* what about disabling other mechanisms? "tls:sasl/EXTERNAL" */
10547c478bd9Sstevel@tonic-gate 		if (authp->type == NS_LDAP_AUTH_NONE)
10557c478bd9Sstevel@tonic-gate 			continue;
10567c478bd9Sstevel@tonic-gate 		authstried++;
10577c478bd9Sstevel@tonic-gate 		credp->auth.type = authp->type;
10587c478bd9Sstevel@tonic-gate 		credp->auth.tlstype = authp->tlstype;
10597c478bd9Sstevel@tonic-gate 		credp->auth.saslmech = authp->saslmech;
10607c478bd9Sstevel@tonic-gate 		credp->auth.saslopt = authp->saslopt;
10617c478bd9Sstevel@tonic-gate 
10627c478bd9Sstevel@tonic-gate 		ldaprc = __ns_ldap_repAttr("shadow", dn,
10637c478bd9Sstevel@tonic-gate 		    (const ns_ldap_attr_t * const *)attrs,
1064*dd1104fbSMichen Chang 		    credp, flags, &errorp);
10657c478bd9Sstevel@tonic-gate 		if (ldaprc == NS_LDAP_SUCCESS) {
10667c478bd9Sstevel@tonic-gate 			result = NS_LDAP_SUCCESS;
10677c478bd9Sstevel@tonic-gate 			goto out;
10687c478bd9Sstevel@tonic-gate 		}
10697c478bd9Sstevel@tonic-gate 
10707c478bd9Sstevel@tonic-gate 		/*
1071*dd1104fbSMichen Chang 		 * if change not allowed due to configuration, indicate so
1072*dd1104fbSMichen Chang 		 * to the caller
1073*dd1104fbSMichen Chang 		 */
1074*dd1104fbSMichen Chang 		if (ldaprc == NS_LDAP_CONFIG &&
1075*dd1104fbSMichen Chang 		    errorp->status == NS_CONFIG_NOTALLOW) {
1076*dd1104fbSMichen Chang 			result = NS_LDAP_CONFIG;
1077*dd1104fbSMichen Chang 			*pwd_status = NS_PASSWD_CHANGE_NOT_ALLOWED;
1078*dd1104fbSMichen Chang 			goto out;
1079*dd1104fbSMichen Chang 		}
1080*dd1104fbSMichen Chang 
1081*dd1104fbSMichen Chang 		/*
10827c478bd9Sstevel@tonic-gate 		 * other errors might need to be added to this list, for
10837c478bd9Sstevel@tonic-gate 		 * the current supported mechanisms this is sufficient
10847c478bd9Sstevel@tonic-gate 		 */
10857c478bd9Sstevel@tonic-gate 		if ((ldaprc == NS_LDAP_INTERNAL) &&
10867c478bd9Sstevel@tonic-gate 		    (errorp->pwd_mgmt.status == NS_PASSWD_GOOD) &&
10877c478bd9Sstevel@tonic-gate 		    ((errorp->status == LDAP_INAPPROPRIATE_AUTH) ||
10887c478bd9Sstevel@tonic-gate 		    (errorp->status == LDAP_INVALID_CREDENTIALS))) {
10897c478bd9Sstevel@tonic-gate 			result = ldaprc;
10907c478bd9Sstevel@tonic-gate 			goto out;
10917c478bd9Sstevel@tonic-gate 		}
10927c478bd9Sstevel@tonic-gate 
10937c478bd9Sstevel@tonic-gate 		/*
10947c478bd9Sstevel@tonic-gate 		 * If there is error related to password policy,
10957c478bd9Sstevel@tonic-gate 		 * return it to caller
10967c478bd9Sstevel@tonic-gate 		 */
10977c478bd9Sstevel@tonic-gate 		if ((ldaprc == NS_LDAP_INTERNAL) &&
10987c478bd9Sstevel@tonic-gate 		    errorp->pwd_mgmt.status != NS_PASSWD_GOOD) {
10997c478bd9Sstevel@tonic-gate 			*pwd_status = errorp->pwd_mgmt.status;
11007c478bd9Sstevel@tonic-gate 			result = ldaprc;
11017c478bd9Sstevel@tonic-gate 			goto out;
11027c478bd9Sstevel@tonic-gate 		} else
11037c478bd9Sstevel@tonic-gate 			*pwd_status = NS_PASSWD_GOOD;
11047c478bd9Sstevel@tonic-gate 
11057c478bd9Sstevel@tonic-gate 		/* we don't really care about the error, just clean it up */
11067c478bd9Sstevel@tonic-gate 		if (errorp)
11077c478bd9Sstevel@tonic-gate 			(void) __ns_ldap_freeError(&errorp);
11087c478bd9Sstevel@tonic-gate 	}
11097c478bd9Sstevel@tonic-gate 	if (authstried == 0) {
11107c478bd9Sstevel@tonic-gate 		syslog(LOG_ERR,
11117c478bd9Sstevel@tonic-gate 		"passwdutil: no legal LDAP authentication method configured");
11127c478bd9Sstevel@tonic-gate 		result = NS_LDAP_CONFIG;
11137c478bd9Sstevel@tonic-gate 		goto out;
11147c478bd9Sstevel@tonic-gate 	}
1115*dd1104fbSMichen Chang 	result = NS_LDAP_OP_FAILED; /* map to PWU_DENIED */
11167c478bd9Sstevel@tonic-gate 
11177c478bd9Sstevel@tonic-gate out:
11187c478bd9Sstevel@tonic-gate 	if (credp)
11197c478bd9Sstevel@tonic-gate 		(void) __ns_ldap_freeCred(&credp);
11207c478bd9Sstevel@tonic-gate 
11217c478bd9Sstevel@tonic-gate 	if (authpp)
11227c478bd9Sstevel@tonic-gate 		(void) __ns_ldap_freeParam((void ***)&authpp);
11237c478bd9Sstevel@tonic-gate 
11247c478bd9Sstevel@tonic-gate 	if (errorp)
11257c478bd9Sstevel@tonic-gate 		(void) __ns_ldap_freeError(&errorp);
11267c478bd9Sstevel@tonic-gate 
11277c478bd9Sstevel@tonic-gate 	return (result);
11287c478bd9Sstevel@tonic-gate }
11297c478bd9Sstevel@tonic-gate 
11307c478bd9Sstevel@tonic-gate 
11317c478bd9Sstevel@tonic-gate /*
11327c478bd9Sstevel@tonic-gate  * ldap_putpwnam(name, oldpw, dummy, rep, buf)
11337c478bd9Sstevel@tonic-gate  *
11347c478bd9Sstevel@tonic-gate  * update the LDAP server with the attributes contained in 'buf'.
11357c478bd9Sstevel@tonic-gate  * The dummy parameter is a placeholder for NIS+ where the old
11367c478bd9Sstevel@tonic-gate  * RPC password is passwd.
11377c478bd9Sstevel@tonic-gate  */
11387c478bd9Sstevel@tonic-gate /*ARGSUSED*/
11397c478bd9Sstevel@tonic-gate int
11407c478bd9Sstevel@tonic-gate ldap_putpwnam(char *name, char *oldpw, char *dummy,
11417c478bd9Sstevel@tonic-gate 	pwu_repository_t *rep, void *buf)
11427c478bd9Sstevel@tonic-gate {
11437c478bd9Sstevel@tonic-gate 	int res;
11447c478bd9Sstevel@tonic-gate 	char *dn;	/* dn of user whose attributes we are changing */
11457c478bd9Sstevel@tonic-gate 	char *binddn;	/* dn of user who is performing the change */
11467c478bd9Sstevel@tonic-gate 	ns_ldap_error_t *errorp;
11477c478bd9Sstevel@tonic-gate 	ldapbuf_t *ldapbuf = (ldapbuf_t *)buf;
1148*dd1104fbSMichen Chang 	ns_ldap_attr_t **pattrs = ldapbuf->pattrs;
1149*dd1104fbSMichen Chang 	ns_ldap_attr_t **sattrs = ldapbuf->sattrs;
11507c478bd9Sstevel@tonic-gate 	struct passwd *pw;
11517c478bd9Sstevel@tonic-gate 	int pwd_status;
11527c478bd9Sstevel@tonic-gate 	uid_t uid;
11537c478bd9Sstevel@tonic-gate 
11547c478bd9Sstevel@tonic-gate 	if (strcmp(name, "root") == 0)
11557c478bd9Sstevel@tonic-gate 		return (PWU_NOT_FOUND);
11567c478bd9Sstevel@tonic-gate 
11577c478bd9Sstevel@tonic-gate 	/*
1158*dd1104fbSMichen Chang 	 * convert name of user whose attributes we are changing
1159*dd1104fbSMichen Chang 	 * to a distinguished name
1160*dd1104fbSMichen Chang 	 */
1161*dd1104fbSMichen Chang 	res = __ns_ldap_uid2dn(name, &dn, NULL, &errorp);
1162*dd1104fbSMichen Chang 	if (res != NS_LDAP_SUCCESS)
1163*dd1104fbSMichen Chang 		goto out;
1164*dd1104fbSMichen Chang 
1165*dd1104fbSMichen Chang 	/* update shadow via ldap_cachemgr if it is enabled */
1166*dd1104fbSMichen Chang 	if (ldapbuf->shadow_update_enabled &&
1167*dd1104fbSMichen Chang 	    sattrs != NULL && sattrs[0] != NULL) {
1168*dd1104fbSMichen Chang 		/*
1169*dd1104fbSMichen Chang 		 * flag NS_LDAP_UPDATE_SHADOW indicates the shadow update
1170*dd1104fbSMichen Chang 		 * should be done via ldap_cachemgr
1171*dd1104fbSMichen Chang 		 */
1172*dd1104fbSMichen Chang 		res = ldap_replaceattr(dn, sattrs, NULL, NULL, &pwd_status,
1173*dd1104fbSMichen Chang 		    NS_LDAP_UPDATE_SHADOW);
1174*dd1104fbSMichen Chang 		goto out;
1175*dd1104fbSMichen Chang 	}
1176*dd1104fbSMichen Chang 
1177*dd1104fbSMichen Chang 	/*
11787c478bd9Sstevel@tonic-gate 	 * The LDAP server checks whether we are permitted to perform
11797c478bd9Sstevel@tonic-gate 	 * the requested change. We need to send the name of the user
11807c478bd9Sstevel@tonic-gate 	 * who is executing this piece of code, together with his
11817c478bd9Sstevel@tonic-gate 	 * current password to the server.
11827c478bd9Sstevel@tonic-gate 	 * If this is executed by a normal user changing his/her own
11837c478bd9Sstevel@tonic-gate 	 * password, this will simply be the OLD password that is to
11847c478bd9Sstevel@tonic-gate 	 * be changed.
11857c478bd9Sstevel@tonic-gate 	 * Specific case if the user who is executing this piece
11867c478bd9Sstevel@tonic-gate 	 * of code is root. We will then issue the LDAP request
11877c478bd9Sstevel@tonic-gate 	 * with the DN of the user we want to change the passwd of.
11887c478bd9Sstevel@tonic-gate 	 */
11897c478bd9Sstevel@tonic-gate 
11907c478bd9Sstevel@tonic-gate 	/*
11917c478bd9Sstevel@tonic-gate 	 * create a dn for the user who is executing this code
11927c478bd9Sstevel@tonic-gate 	 */
11937c478bd9Sstevel@tonic-gate 	uid = getuid();
11947c478bd9Sstevel@tonic-gate 	if (uid == 0) {
11957c478bd9Sstevel@tonic-gate 		if ((pw = getpwnam_from(name, rep, REP_LDAP)) == NULL) {
11967c478bd9Sstevel@tonic-gate 			res = NS_LDAP_OP_FAILED;
11977c478bd9Sstevel@tonic-gate 			goto out;
11987c478bd9Sstevel@tonic-gate 		}
11997c478bd9Sstevel@tonic-gate 	} else if ((pw = getpwuid_from(uid, rep, REP_LDAP)) == NULL) {
12007c478bd9Sstevel@tonic-gate 		/*
12017c478bd9Sstevel@tonic-gate 		 * User executing this code is not known to the LDAP
12027c478bd9Sstevel@tonic-gate 		 * server. This operation is to be denied
12037c478bd9Sstevel@tonic-gate 		 */
12047c478bd9Sstevel@tonic-gate 		res = NS_LDAP_OP_FAILED;
12057c478bd9Sstevel@tonic-gate 		goto out;
12067c478bd9Sstevel@tonic-gate 	}
12077c478bd9Sstevel@tonic-gate 
12087c478bd9Sstevel@tonic-gate 	res = __ns_ldap_uid2dn(pw->pw_name, &binddn, NULL, &errorp);
12097c478bd9Sstevel@tonic-gate 	if (res != NS_LDAP_SUCCESS)
12107c478bd9Sstevel@tonic-gate 		goto out;
12117c478bd9Sstevel@tonic-gate 
1212*dd1104fbSMichen Chang 	if (pattrs && pattrs[0] != NULL) {
1213*dd1104fbSMichen Chang 		res = ldap_replaceattr(dn, pattrs, binddn, oldpw,
1214*dd1104fbSMichen Chang 		    &pwd_status, 0);
1215*dd1104fbSMichen Chang 	} else
1216*dd1104fbSMichen Chang 		res = NS_LDAP_OP_FAILED;
12177c478bd9Sstevel@tonic-gate 
12187c478bd9Sstevel@tonic-gate out:
1219*dd1104fbSMichen Chang 	free_ldapbuf(ldapbuf);
12207c478bd9Sstevel@tonic-gate 	free(dn);
12217c478bd9Sstevel@tonic-gate 
12227c478bd9Sstevel@tonic-gate 	return (ldap_to_pwu_code(res, pwd_status));
12237c478bd9Sstevel@tonic-gate }
1224