xref: /onnv-gate/usr/src/lib/passwdutil/ldap_attr.c (revision 11262:b7ebfbf2359e)
10Sstevel@tonic-gate /*
20Sstevel@tonic-gate  * CDDL HEADER START
30Sstevel@tonic-gate  *
40Sstevel@tonic-gate  * The contents of this file are subject to the terms of the
58821SMichen.Chang@Sun.COM  * Common Development and Distribution License (the "License").
68821SMichen.Chang@Sun.COM  * You may not use this file except in compliance with the License.
70Sstevel@tonic-gate  *
80Sstevel@tonic-gate  * You can obtain a copy of the license at usr/src/OPENSOLARIS.LICENSE
90Sstevel@tonic-gate  * or http://www.opensolaris.org/os/licensing.
100Sstevel@tonic-gate  * See the License for the specific language governing permissions
110Sstevel@tonic-gate  * and limitations under the License.
120Sstevel@tonic-gate  *
130Sstevel@tonic-gate  * When distributing Covered Code, include this CDDL HEADER in each
140Sstevel@tonic-gate  * file and include the License file at usr/src/OPENSOLARIS.LICENSE.
150Sstevel@tonic-gate  * If applicable, add the following below this CDDL HEADER, with the
160Sstevel@tonic-gate  * fields enclosed by brackets "[]" replaced with your own identifying
170Sstevel@tonic-gate  * information: Portions Copyright [yyyy] [name of copyright owner]
180Sstevel@tonic-gate  *
190Sstevel@tonic-gate  * CDDL HEADER END
200Sstevel@tonic-gate  */
210Sstevel@tonic-gate /*
228821SMichen.Chang@Sun.COM  * Copyright 2009 Sun Microsystems, Inc.  All rights reserved.
230Sstevel@tonic-gate  * Use is subject to license terms.
240Sstevel@tonic-gate  */
250Sstevel@tonic-gate 
260Sstevel@tonic-gate #include <stdio.h>
270Sstevel@tonic-gate #include <errno.h>
280Sstevel@tonic-gate #include <stdlib.h>
290Sstevel@tonic-gate #include <string.h>
300Sstevel@tonic-gate #include <unistd.h>
318821SMichen.Chang@Sun.COM #include <macros.h>
328821SMichen.Chang@Sun.COM #include <priv.h>
330Sstevel@tonic-gate 
340Sstevel@tonic-gate #include "ns_sldap.h"
350Sstevel@tonic-gate 
360Sstevel@tonic-gate #include <nss_dbdefs.h>
370Sstevel@tonic-gate #include <nsswitch.h>
380Sstevel@tonic-gate 
390Sstevel@tonic-gate #include <pwd.h>
400Sstevel@tonic-gate #include <shadow.h>
410Sstevel@tonic-gate #include <syslog.h>
420Sstevel@tonic-gate 
430Sstevel@tonic-gate #include "passwdutil.h"
440Sstevel@tonic-gate 
450Sstevel@tonic-gate #include "utils.h"
460Sstevel@tonic-gate 
478821SMichen.Chang@Sun.COM #define	MAX_INT_LEN 11	/* 10+1 %d buflen for words/ints [not longs] */
488821SMichen.Chang@Sun.COM 
498821SMichen.Chang@Sun.COM #define	STRDUP_OR_RET(to, from) \
508821SMichen.Chang@Sun.COM 	if ((to = strdup(from)) == NULL) \
518821SMichen.Chang@Sun.COM 		return (PWU_NOMEM);
528821SMichen.Chang@Sun.COM 
538821SMichen.Chang@Sun.COM #define	STRDUP_OR_ERR(to, from, err) \
548821SMichen.Chang@Sun.COM 	if (((to) = strdup(from)) == NULL) \
558821SMichen.Chang@Sun.COM 		(err) = PWU_NOMEM;
568821SMichen.Chang@Sun.COM 
578821SMichen.Chang@Sun.COM #define	NUM_TO_STR(to, from) \
588821SMichen.Chang@Sun.COM 	{ \
598821SMichen.Chang@Sun.COM 		char nb[MAX_INT_LEN]; \
608821SMichen.Chang@Sun.COM 		if (snprintf(nb, MAX_INT_LEN, "%d", (from)) >= MAX_INT_LEN) \
618821SMichen.Chang@Sun.COM 			return (PWU_NOMEM); \
628821SMichen.Chang@Sun.COM 		STRDUP_OR_RET(to, nb); \
638821SMichen.Chang@Sun.COM 	}
648821SMichen.Chang@Sun.COM 
658821SMichen.Chang@Sun.COM #define	NEW_ATTR(p, i, attr, val) \
668821SMichen.Chang@Sun.COM 	{ \
678821SMichen.Chang@Sun.COM 		p[i] = new_attr(attr, (val)); \
688821SMichen.Chang@Sun.COM 		if (p[i] == NULL) \
698821SMichen.Chang@Sun.COM 			return (PWU_NOMEM); \
708821SMichen.Chang@Sun.COM 		i++; \
718821SMichen.Chang@Sun.COM 	}
728821SMichen.Chang@Sun.COM 
730Sstevel@tonic-gate int ldap_getattr(char *name, attrlist *item, pwu_repository_t *rep);
740Sstevel@tonic-gate int ldap_getpwnam(char *name, attrlist *items, pwu_repository_t *rep,
750Sstevel@tonic-gate     void **buf);
760Sstevel@tonic-gate int ldap_update(attrlist *items, pwu_repository_t *rep, void *buf);
77*11262SRajagopal.Andra@Sun.COM int ldap_putpwnam(char *name, char *oldpw, pwu_repository_t *rep, void *buf);
780Sstevel@tonic-gate int ldap_user_to_authenticate(char *name, pwu_repository_t *rep,
790Sstevel@tonic-gate 	char **auth_user, int *privileged);
800Sstevel@tonic-gate 
810Sstevel@tonic-gate /*
820Sstevel@tonic-gate  * ldap function pointer table, used by passwdutil_init to initialize
830Sstevel@tonic-gate  * the global Repository-OPerations table "rops"
840Sstevel@tonic-gate  */
850Sstevel@tonic-gate struct repops ldap_repops = {
860Sstevel@tonic-gate 	NULL,	/* checkhistory */
870Sstevel@tonic-gate 	ldap_getattr,
880Sstevel@tonic-gate 	ldap_getpwnam,
890Sstevel@tonic-gate 	ldap_update,
900Sstevel@tonic-gate 	ldap_putpwnam,
910Sstevel@tonic-gate 	ldap_user_to_authenticate,
920Sstevel@tonic-gate 	NULL,	/* lock */
930Sstevel@tonic-gate 	NULL	/* unlock */
940Sstevel@tonic-gate };
950Sstevel@tonic-gate 
960Sstevel@tonic-gate /*
970Sstevel@tonic-gate  * structure used to keep state between get/update/put calls
980Sstevel@tonic-gate  */
990Sstevel@tonic-gate typedef struct {
1008821SMichen.Chang@Sun.COM 	char *passwd;			/* encrypted password */
1010Sstevel@tonic-gate 	struct passwd *pwd;
1028821SMichen.Chang@Sun.COM 	ns_ldap_attr_t **pattrs;	/* passwd attrs */
1038821SMichen.Chang@Sun.COM 	int npattrs;			/* max attrs */
1048821SMichen.Chang@Sun.COM 	struct spwd *spwd;
1058821SMichen.Chang@Sun.COM 	ns_ldap_attr_t **sattrs;	/* passwd attrs */
1068821SMichen.Chang@Sun.COM 	int nsattrs;			/* max attrs */
1078821SMichen.Chang@Sun.COM 	boolean_t shadow_update_enabled;	/* shadow update configured */
1080Sstevel@tonic-gate } ldapbuf_t;
1090Sstevel@tonic-gate 
1100Sstevel@tonic-gate /*
1110Sstevel@tonic-gate  * The following define's are taken from
1120Sstevel@tonic-gate  *	usr/src/lib/nsswitch/ldap/common/getpwnam.c
1130Sstevel@tonic-gate  */
1140Sstevel@tonic-gate 
1150Sstevel@tonic-gate /* passwd attributes filters */
1160Sstevel@tonic-gate #define	_PWD_CN			"cn"
1170Sstevel@tonic-gate #define	_PWD_UID		"uid"
1180Sstevel@tonic-gate #define	_PWD_USERPASSWORD	"userpassword"
1190Sstevel@tonic-gate #define	_PWD_UIDNUMBER		"uidnumber"
1200Sstevel@tonic-gate #define	_PWD_GIDNUMBER		"gidnumber"
1210Sstevel@tonic-gate #define	_PWD_GECOS		"gecos"
1220Sstevel@tonic-gate #define	_PWD_DESCRIPTION	"description"
1230Sstevel@tonic-gate #define	_PWD_HOMEDIRECTORY	"homedirectory"
1240Sstevel@tonic-gate #define	_PWD_LOGINSHELL		"loginshell"
1250Sstevel@tonic-gate 
1268821SMichen.Chang@Sun.COM #define	_PWD_MAX_ATTR		10	/* 9+NULL */
1278821SMichen.Chang@Sun.COM 
1288821SMichen.Chang@Sun.COM /* shadow attributes filters */
1298821SMichen.Chang@Sun.COM #define	_S_LASTCHANGE		"shadowlastchange"
1308821SMichen.Chang@Sun.COM #define	_S_MIN			"shadowmin"
1318821SMichen.Chang@Sun.COM #define	_S_MAX			"shadowmax"
1328821SMichen.Chang@Sun.COM #define	_S_WARNING		"shadowwarning"
1338821SMichen.Chang@Sun.COM #define	_S_INACTIVE		"shadowinactive"
1348821SMichen.Chang@Sun.COM #define	_S_EXPIRE		"shadowexpire"
1358821SMichen.Chang@Sun.COM #define	_S_FLAG			"shadowflag"
1368821SMichen.Chang@Sun.COM 
1378821SMichen.Chang@Sun.COM #define	_S_MAX_ATTR		8	/* 7+NULL */
1388821SMichen.Chang@Sun.COM 
1398821SMichen.Chang@Sun.COM /*
1408821SMichen.Chang@Sun.COM  * Frees up an ldapbuf_t
1418821SMichen.Chang@Sun.COM  */
1428821SMichen.Chang@Sun.COM 
1438821SMichen.Chang@Sun.COM static void
free_ldapbuf(ldapbuf_t * p)1448821SMichen.Chang@Sun.COM free_ldapbuf(ldapbuf_t *p)
1458821SMichen.Chang@Sun.COM {
1468821SMichen.Chang@Sun.COM 	int i;
1478821SMichen.Chang@Sun.COM 
1488821SMichen.Chang@Sun.COM 	if (p == NULL)
1498821SMichen.Chang@Sun.COM 		return;
1508821SMichen.Chang@Sun.COM 	if (p->passwd) {
1518821SMichen.Chang@Sun.COM 		(void) memset(p->passwd, 0, strlen(p->passwd));
1528821SMichen.Chang@Sun.COM 		free(p->passwd);
1538821SMichen.Chang@Sun.COM 	}
1548821SMichen.Chang@Sun.COM 	if (p->pwd)
1558821SMichen.Chang@Sun.COM 		free_pwd(p->pwd);
1568821SMichen.Chang@Sun.COM 	if (p->spwd)
1578821SMichen.Chang@Sun.COM 		free_spwd(p->spwd);
1588821SMichen.Chang@Sun.COM 	if (p->pattrs) {
1598821SMichen.Chang@Sun.COM 		for (i = 0; i < p->npattrs; i++) {
1608821SMichen.Chang@Sun.COM 			if (p->pattrs[i] != NULL) {
1618821SMichen.Chang@Sun.COM 				free(p->pattrs[i]->attrvalue[0]);
1628821SMichen.Chang@Sun.COM 				free(p->pattrs[i]);
1638821SMichen.Chang@Sun.COM 			}
1648821SMichen.Chang@Sun.COM 		}
1658821SMichen.Chang@Sun.COM 		free(p->pattrs);
1668821SMichen.Chang@Sun.COM 	}
1678821SMichen.Chang@Sun.COM 	if (p->sattrs) {
1688821SMichen.Chang@Sun.COM 		for (i = 0; i < p->nsattrs; i++) {
1698821SMichen.Chang@Sun.COM 			if (p->sattrs[i] != NULL) {
1708821SMichen.Chang@Sun.COM 				free(p->sattrs[i]->attrvalue[0]);
1718821SMichen.Chang@Sun.COM 				free(p->sattrs[i]);
1728821SMichen.Chang@Sun.COM 			}
1738821SMichen.Chang@Sun.COM 		}
1748821SMichen.Chang@Sun.COM 		free(p->sattrs);
1758821SMichen.Chang@Sun.COM 	}
1768821SMichen.Chang@Sun.COM }
1778821SMichen.Chang@Sun.COM 
1780Sstevel@tonic-gate /*
1790Sstevel@tonic-gate  * int ldap_user_to_authenticate(user, rep, auth_user, privileged)
1800Sstevel@tonic-gate  *
1818821SMichen.Chang@Sun.COM  * If the Shadow Update functionality is enabled, then we check to
1828821SMichen.Chang@Sun.COM  * see if the caller has 0 as the euid or has all zone privs. If so,
1838821SMichen.Chang@Sun.COM  * the caller would be able to modify shadow(4) data stored on the
1848821SMichen.Chang@Sun.COM  * LDAP server. Otherwise, when LDAP Shadow Update is not enabled,
1858821SMichen.Chang@Sun.COM  * we can't determine whether the user is "privileged" in the LDAP
1868821SMichen.Chang@Sun.COM  * sense. The operation should be attempted and will succeed if the
1878821SMichen.Chang@Sun.COM  * user had privileges. For our purposes, we say that the user is
1888821SMichen.Chang@Sun.COM  * privileged if he/she is attempting to change another user's
1898821SMichen.Chang@Sun.COM  * password attributes.
1900Sstevel@tonic-gate  */
1910Sstevel@tonic-gate int
ldap_user_to_authenticate(char * user,pwu_repository_t * rep,char ** auth_user,int * privileged)1920Sstevel@tonic-gate ldap_user_to_authenticate(char *user, pwu_repository_t *rep,
1930Sstevel@tonic-gate 	char **auth_user, int *privileged)
1940Sstevel@tonic-gate {
1950Sstevel@tonic-gate 	struct passwd *pw;
1960Sstevel@tonic-gate 	uid_t uid;
1970Sstevel@tonic-gate 	uid_t priviledged_uid;
1980Sstevel@tonic-gate 	int res = PWU_SUCCESS;
1990Sstevel@tonic-gate 
2000Sstevel@tonic-gate 	if (strcmp(user, "root") == 0)
2010Sstevel@tonic-gate 		return (PWU_NOT_FOUND);
2020Sstevel@tonic-gate 
2030Sstevel@tonic-gate 	if ((pw = getpwnam_from(user, rep, REP_LDAP)) == NULL)
2040Sstevel@tonic-gate 		return (PWU_NOT_FOUND);
2050Sstevel@tonic-gate 
2060Sstevel@tonic-gate 	uid = getuid();
2070Sstevel@tonic-gate 
2088821SMichen.Chang@Sun.COM 	/*
2098821SMichen.Chang@Sun.COM 	 * need equivalent of write access to /etc/shadow
2108821SMichen.Chang@Sun.COM 	 * the privilege escalation model is euid == 0 || all zone privs
2118821SMichen.Chang@Sun.COM 	 */
2128821SMichen.Chang@Sun.COM 	if (__ns_ldap_is_shadow_update_enabled()) {
2138821SMichen.Chang@Sun.COM 		boolean_t priv;
2148821SMichen.Chang@Sun.COM 
2158821SMichen.Chang@Sun.COM 		priv = (geteuid() == 0);
2168821SMichen.Chang@Sun.COM 		if (!priv) {
2178821SMichen.Chang@Sun.COM 			priv_set_t *ps = priv_allocset();	/* caller */
2188821SMichen.Chang@Sun.COM 			priv_set_t *zs;				/* zone */
2198821SMichen.Chang@Sun.COM 
2208821SMichen.Chang@Sun.COM 			(void) getppriv(PRIV_EFFECTIVE, ps);
2218821SMichen.Chang@Sun.COM 			zs = priv_str_to_set("zone", ",", NULL);
2228821SMichen.Chang@Sun.COM 			priv = priv_isequalset(ps, zs);
2238821SMichen.Chang@Sun.COM 			priv_freeset(ps);
2248821SMichen.Chang@Sun.COM 			priv_freeset(zs);
2258821SMichen.Chang@Sun.COM 		}
2268821SMichen.Chang@Sun.COM 		/*
2278821SMichen.Chang@Sun.COM 		 * priv can change anyone's password,
2288821SMichen.Chang@Sun.COM 		 * only root isn't prompted.
2298821SMichen.Chang@Sun.COM 		 */
2308821SMichen.Chang@Sun.COM 		*privileged = 0;	/* for proper prompting */
2318821SMichen.Chang@Sun.COM 		if (priv) {
2328821SMichen.Chang@Sun.COM 			if (uid == 0) {
2338821SMichen.Chang@Sun.COM 				*privileged = 1;
2348821SMichen.Chang@Sun.COM 				*auth_user = NULL;
2358821SMichen.Chang@Sun.COM 				return (res);
2368821SMichen.Chang@Sun.COM 			} else if (uid == pw->pw_uid) {
2378821SMichen.Chang@Sun.COM 				STRDUP_OR_ERR(*auth_user, user, res);
2388821SMichen.Chang@Sun.COM 				return (res);
2398821SMichen.Chang@Sun.COM 			}
2408821SMichen.Chang@Sun.COM 		}
2418821SMichen.Chang@Sun.COM 
2428821SMichen.Chang@Sun.COM 		return (PWU_DENIED);
2438821SMichen.Chang@Sun.COM 	}
2448821SMichen.Chang@Sun.COM 
2450Sstevel@tonic-gate 	if (uid == pw->pw_uid) {
2468821SMichen.Chang@Sun.COM 		/* changing our own, not privileged */
2470Sstevel@tonic-gate 		*privileged = 0;
2488821SMichen.Chang@Sun.COM 		STRDUP_OR_RET(*auth_user, user);
2490Sstevel@tonic-gate 	} else {
2500Sstevel@tonic-gate 		char pwd_buf[1024];
2510Sstevel@tonic-gate 		struct passwd pwr;
2520Sstevel@tonic-gate 
2530Sstevel@tonic-gate 		*privileged = 1;
2540Sstevel@tonic-gate 		/*
2550Sstevel@tonic-gate 		 * specific case for root
2560Sstevel@tonic-gate 		 * we want 'user' to be authenticated.
2570Sstevel@tonic-gate 		 */
2580Sstevel@tonic-gate 		if (uid == 0)  {
2590Sstevel@tonic-gate 			priviledged_uid = pw->pw_uid;
2600Sstevel@tonic-gate 		} else {
2610Sstevel@tonic-gate 			priviledged_uid = uid;
2620Sstevel@tonic-gate 		}
2630Sstevel@tonic-gate 		if (getpwuid_r(priviledged_uid, &pwr, pwd_buf,
2648821SMichen.Chang@Sun.COM 		    sizeof (pwd_buf)) != NULL) {
2658821SMichen.Chang@Sun.COM 			STRDUP_OR_ERR(*auth_user, pwr.pw_name, res);
2660Sstevel@tonic-gate 		} else {
2670Sstevel@tonic-gate 			/* hmm. can't find name of current user...??? */
2680Sstevel@tonic-gate 
2698821SMichen.Chang@Sun.COM 			if ((*auth_user = malloc(MAX_INT_LEN)) == NULL) {
2700Sstevel@tonic-gate 				res = PWU_NOMEM;
2710Sstevel@tonic-gate 			} else {
2728821SMichen.Chang@Sun.COM 				(void) snprintf(*auth_user, MAX_INT_LEN, "%d",
2730Sstevel@tonic-gate 				    (int)uid);
2740Sstevel@tonic-gate 			}
2750Sstevel@tonic-gate 		}
2760Sstevel@tonic-gate 	}
2770Sstevel@tonic-gate 
2780Sstevel@tonic-gate 	return (res);
2790Sstevel@tonic-gate }
2800Sstevel@tonic-gate 
2810Sstevel@tonic-gate /*
2820Sstevel@tonic-gate  * int ldap_getattr(name, item, rep)
2830Sstevel@tonic-gate  *
2840Sstevel@tonic-gate  * retrieve attributes specified in "item" for user "name".
2850Sstevel@tonic-gate  */
2860Sstevel@tonic-gate /*ARGSUSED*/
2870Sstevel@tonic-gate int
ldap_getattr(char * name,attrlist * items,pwu_repository_t * rep)2880Sstevel@tonic-gate ldap_getattr(char *name, attrlist *items, pwu_repository_t *rep)
2890Sstevel@tonic-gate {
2908821SMichen.Chang@Sun.COM 	attrlist *w;
2910Sstevel@tonic-gate 	int res;
2928821SMichen.Chang@Sun.COM 	ldapbuf_t *ldapbuf;
2930Sstevel@tonic-gate 	struct passwd *pw = NULL;
2940Sstevel@tonic-gate 	struct spwd *spw = NULL;
2950Sstevel@tonic-gate 
2968821SMichen.Chang@Sun.COM 	res = ldap_getpwnam(name, items, rep, (void **)&ldapbuf);
2978821SMichen.Chang@Sun.COM 	if (res != PWU_SUCCESS)
2988821SMichen.Chang@Sun.COM 		return (res);
2990Sstevel@tonic-gate 
3008821SMichen.Chang@Sun.COM 	pw = ldapbuf->pwd;
3018821SMichen.Chang@Sun.COM 	spw = ldapbuf->spwd;
3020Sstevel@tonic-gate 
3030Sstevel@tonic-gate 	for (w = items; res == PWU_SUCCESS && w != NULL; w = w->next) {
3040Sstevel@tonic-gate 		switch (w->type) {
3050Sstevel@tonic-gate 		case ATTR_NAME:
3068821SMichen.Chang@Sun.COM 			STRDUP_OR_ERR(w->data.val_s, pw->pw_name, res);
3070Sstevel@tonic-gate 			break;
3080Sstevel@tonic-gate 		case ATTR_COMMENT:
3098821SMichen.Chang@Sun.COM 			STRDUP_OR_ERR(w->data.val_s, pw->pw_comment, res);
3100Sstevel@tonic-gate 			break;
3110Sstevel@tonic-gate 		case ATTR_GECOS:
3128821SMichen.Chang@Sun.COM 			STRDUP_OR_ERR(w->data.val_s, pw->pw_gecos, res);
3130Sstevel@tonic-gate 			break;
3140Sstevel@tonic-gate 		case ATTR_HOMEDIR:
3158821SMichen.Chang@Sun.COM 			STRDUP_OR_ERR(w->data.val_s, pw->pw_dir, res);
3160Sstevel@tonic-gate 			break;
3170Sstevel@tonic-gate 		case ATTR_SHELL:
3188821SMichen.Chang@Sun.COM 			STRDUP_OR_ERR(w->data.val_s, pw->pw_shell, res);
3190Sstevel@tonic-gate 			break;
3200Sstevel@tonic-gate 		case ATTR_PASSWD:
3210Sstevel@tonic-gate 		case ATTR_PASSWD_SERVER_POLICY:
3228821SMichen.Chang@Sun.COM 			STRDUP_OR_ERR(w->data.val_s, spw->sp_pwdp, res);
3230Sstevel@tonic-gate 			break;
3240Sstevel@tonic-gate 		case ATTR_AGE:
3258821SMichen.Chang@Sun.COM 			STRDUP_OR_ERR(w->data.val_s, pw->pw_age, res);
3260Sstevel@tonic-gate 			break;
3270Sstevel@tonic-gate 		case ATTR_REP_NAME:
3288821SMichen.Chang@Sun.COM 			STRDUP_OR_ERR(w->data.val_s, "ldap", res);
3290Sstevel@tonic-gate 			break;
3300Sstevel@tonic-gate 
3310Sstevel@tonic-gate 		/* integer values */
3320Sstevel@tonic-gate 		case ATTR_UID:
3330Sstevel@tonic-gate 			w->data.val_i = pw->pw_uid;
3340Sstevel@tonic-gate 			break;
3350Sstevel@tonic-gate 		case ATTR_GID:
3360Sstevel@tonic-gate 			w->data.val_i = pw->pw_gid;
3370Sstevel@tonic-gate 			break;
3380Sstevel@tonic-gate 		case ATTR_LSTCHG:
3398821SMichen.Chang@Sun.COM 			if (ldapbuf->shadow_update_enabled)
3408821SMichen.Chang@Sun.COM 				w->data.val_i = spw->sp_lstchg;
3418821SMichen.Chang@Sun.COM 			else
3428821SMichen.Chang@Sun.COM 				w->data.val_i = -1;
3430Sstevel@tonic-gate 			break;
3440Sstevel@tonic-gate 		case ATTR_MIN:
3458821SMichen.Chang@Sun.COM 			if (ldapbuf->shadow_update_enabled)
3468821SMichen.Chang@Sun.COM 				w->data.val_i = spw->sp_min;
3478821SMichen.Chang@Sun.COM 			else
3488821SMichen.Chang@Sun.COM 				w->data.val_i = -1;
3490Sstevel@tonic-gate 			break;
3500Sstevel@tonic-gate 		case ATTR_MAX:
3518821SMichen.Chang@Sun.COM 			if (ldapbuf->shadow_update_enabled)
3528821SMichen.Chang@Sun.COM 				w->data.val_i = spw->sp_max;
3538821SMichen.Chang@Sun.COM 			else
3548821SMichen.Chang@Sun.COM 				w->data.val_i = -1;
3550Sstevel@tonic-gate 			break;
3560Sstevel@tonic-gate 		case ATTR_WARN:
3578821SMichen.Chang@Sun.COM 			if (ldapbuf->shadow_update_enabled)
3588821SMichen.Chang@Sun.COM 				w->data.val_i = spw->sp_warn;
3598821SMichen.Chang@Sun.COM 			else
3608821SMichen.Chang@Sun.COM 				w->data.val_i = -1;
3610Sstevel@tonic-gate 			break;
3620Sstevel@tonic-gate 		case ATTR_INACT:
3638821SMichen.Chang@Sun.COM 			if (ldapbuf->shadow_update_enabled)
3648821SMichen.Chang@Sun.COM 				w->data.val_i = spw->sp_inact;
3658821SMichen.Chang@Sun.COM 			else
3668821SMichen.Chang@Sun.COM 				w->data.val_i = -1;
3670Sstevel@tonic-gate 			break;
3680Sstevel@tonic-gate 		case ATTR_EXPIRE:
3698821SMichen.Chang@Sun.COM 			if (ldapbuf->shadow_update_enabled)
3708821SMichen.Chang@Sun.COM 				w->data.val_i = spw->sp_expire;
3718821SMichen.Chang@Sun.COM 			else
3728821SMichen.Chang@Sun.COM 				w->data.val_i = -1;
3730Sstevel@tonic-gate 			break;
3740Sstevel@tonic-gate 		case ATTR_FLAG:
3758821SMichen.Chang@Sun.COM 			if (ldapbuf->shadow_update_enabled)
3768821SMichen.Chang@Sun.COM 				w->data.val_i = spw->sp_flag;
3778821SMichen.Chang@Sun.COM 			break;
3788821SMichen.Chang@Sun.COM 		case ATTR_FAILED_LOGINS:
3798821SMichen.Chang@Sun.COM 			w->data.val_i = spw->sp_flag & FAILCOUNT_MASK;
3800Sstevel@tonic-gate 			break;
3810Sstevel@tonic-gate 		default:
3820Sstevel@tonic-gate 			break;
3830Sstevel@tonic-gate 		}
3840Sstevel@tonic-gate 	}
3850Sstevel@tonic-gate 
3860Sstevel@tonic-gate out:
3878821SMichen.Chang@Sun.COM 	free_ldapbuf(ldapbuf);
3888821SMichen.Chang@Sun.COM 	free(ldapbuf);
3890Sstevel@tonic-gate 	return (res);
3900Sstevel@tonic-gate }
3910Sstevel@tonic-gate 
3920Sstevel@tonic-gate /*
3930Sstevel@tonic-gate  * int ldap_getpwnam(name, items, rep, buf)
3940Sstevel@tonic-gate  *
3950Sstevel@tonic-gate  * There is no need to get the old values from the ldap
3960Sstevel@tonic-gate  * server, as the update will update each item individually.
3970Sstevel@tonic-gate  * Therefore, we only allocate a buffer that will be used by
3980Sstevel@tonic-gate  * _update and _putpwnam to hold the attributes to update.
3990Sstevel@tonic-gate  *
4000Sstevel@tonic-gate  * Only when we're about to update a password, we need to retrieve
4010Sstevel@tonic-gate  * the old password since it contains salt-information.
4020Sstevel@tonic-gate  */
4030Sstevel@tonic-gate /*ARGSUSED*/
4040Sstevel@tonic-gate int
ldap_getpwnam(char * name,attrlist * items,pwu_repository_t * rep,void ** buf)4050Sstevel@tonic-gate ldap_getpwnam(char *name, attrlist *items, pwu_repository_t *rep,
4060Sstevel@tonic-gate     void **buf)
4070Sstevel@tonic-gate {
4080Sstevel@tonic-gate 	ldapbuf_t *ldapbuf;
4098821SMichen.Chang@Sun.COM 	int res = PWU_NOMEM;
4100Sstevel@tonic-gate 
4118821SMichen.Chang@Sun.COM 	/*
4128821SMichen.Chang@Sun.COM 	 * [sp]attrs is treated as NULL terminated
4138821SMichen.Chang@Sun.COM 	 */
4140Sstevel@tonic-gate 
4150Sstevel@tonic-gate 	ldapbuf = calloc(1, sizeof (ldapbuf_t));
4160Sstevel@tonic-gate 	if (ldapbuf == NULL)
4170Sstevel@tonic-gate 		return (PWU_NOMEM);
4180Sstevel@tonic-gate 
4198821SMichen.Chang@Sun.COM 	ldapbuf->pattrs = calloc(_PWD_MAX_ATTR, sizeof (ns_ldap_attr_t *));
4208821SMichen.Chang@Sun.COM 	if (ldapbuf->pattrs == NULL)
4218821SMichen.Chang@Sun.COM 		goto out;
4228821SMichen.Chang@Sun.COM 	ldapbuf->npattrs = _PWD_MAX_ATTR;
4230Sstevel@tonic-gate 
4248821SMichen.Chang@Sun.COM 	ldapbuf->sattrs = calloc(_S_MAX_ATTR, sizeof (ns_ldap_attr_t *));
4258821SMichen.Chang@Sun.COM 	if (ldapbuf->sattrs == NULL)
4268821SMichen.Chang@Sun.COM 		goto out;
4278821SMichen.Chang@Sun.COM 	ldapbuf->nsattrs = _S_MAX_ATTR;
4280Sstevel@tonic-gate 
4298821SMichen.Chang@Sun.COM 	res = dup_pw(&ldapbuf->pwd, getpwnam_from(name, rep, REP_LDAP));
4308821SMichen.Chang@Sun.COM 	if (res != PWU_SUCCESS)
4318821SMichen.Chang@Sun.COM 		goto out;
4320Sstevel@tonic-gate 
4338821SMichen.Chang@Sun.COM 	res = dup_spw(&ldapbuf->spwd, getspnam_from(name, rep, REP_LDAP));
4348821SMichen.Chang@Sun.COM 	if (res != PWU_SUCCESS)
4358821SMichen.Chang@Sun.COM 		goto out;
4368821SMichen.Chang@Sun.COM 	else {
4378821SMichen.Chang@Sun.COM 		char *spw = ldapbuf->spwd->sp_pwdp;
4388821SMichen.Chang@Sun.COM 		if (spw != NULL && *spw != '\0') {
4398821SMichen.Chang@Sun.COM 			ldapbuf->passwd = strdup(spw);
4400Sstevel@tonic-gate 			if (ldapbuf->passwd == NULL)
4418821SMichen.Chang@Sun.COM 				goto out;
4428821SMichen.Chang@Sun.COM 		} else
4438821SMichen.Chang@Sun.COM 			ldapbuf->passwd = NULL;
4440Sstevel@tonic-gate 	}
4450Sstevel@tonic-gate 
4468821SMichen.Chang@Sun.COM 	/* remember if shadow update is enabled */
4478821SMichen.Chang@Sun.COM 	ldapbuf->shadow_update_enabled = __ns_ldap_is_shadow_update_enabled();
4488821SMichen.Chang@Sun.COM 
4498821SMichen.Chang@Sun.COM 	*buf = (void *)ldapbuf;
4508821SMichen.Chang@Sun.COM 	return (PWU_SUCCESS);
4518821SMichen.Chang@Sun.COM 
4528821SMichen.Chang@Sun.COM out:
4538821SMichen.Chang@Sun.COM 	free_ldapbuf(ldapbuf);
4548821SMichen.Chang@Sun.COM 	free(ldapbuf);
4558821SMichen.Chang@Sun.COM 	return (res);
4560Sstevel@tonic-gate }
4570Sstevel@tonic-gate 
4580Sstevel@tonic-gate /*
4590Sstevel@tonic-gate  * new_attr(name, value)
4600Sstevel@tonic-gate  *
4610Sstevel@tonic-gate  * create a new LDAP attribute to be sent to the server
4620Sstevel@tonic-gate  */
4630Sstevel@tonic-gate ns_ldap_attr_t *
new_attr(char * name,char * value)4640Sstevel@tonic-gate new_attr(char *name, char *value)
4650Sstevel@tonic-gate {
4660Sstevel@tonic-gate 	ns_ldap_attr_t *tmp;
4670Sstevel@tonic-gate 
4680Sstevel@tonic-gate 	tmp = malloc(sizeof (*tmp));
4690Sstevel@tonic-gate 	if (tmp != NULL) {
4700Sstevel@tonic-gate 		tmp->attrname = name;
4710Sstevel@tonic-gate 		tmp->attrvalue = (char **)calloc(2, sizeof (char *));
4720Sstevel@tonic-gate 		if (tmp->attrvalue == NULL) {
4730Sstevel@tonic-gate 			free(tmp);
4740Sstevel@tonic-gate 			return (NULL);
4750Sstevel@tonic-gate 		}
4760Sstevel@tonic-gate 		tmp->attrvalue[0] = value;
4770Sstevel@tonic-gate 		tmp->value_count = 1;
4780Sstevel@tonic-gate 	}
4790Sstevel@tonic-gate 
4800Sstevel@tonic-gate 	return (tmp);
4810Sstevel@tonic-gate }
4820Sstevel@tonic-gate 
4830Sstevel@tonic-gate /*
4848821SMichen.Chang@Sun.COM  * max_present(list)
4858821SMichen.Chang@Sun.COM  *
4868821SMichen.Chang@Sun.COM  * returns '1' if a ATTR_MAX with value != -1 is present. (in other words:
4878821SMichen.Chang@Sun.COM  * if password aging is to be turned on).
4888821SMichen.Chang@Sun.COM  */
4898821SMichen.Chang@Sun.COM static int
max_present(attrlist * list)4908821SMichen.Chang@Sun.COM max_present(attrlist *list)
4918821SMichen.Chang@Sun.COM {
4928821SMichen.Chang@Sun.COM 	while (list != NULL)
4938821SMichen.Chang@Sun.COM 		if (list->type == ATTR_MAX && list->data.val_i != -1)
4948821SMichen.Chang@Sun.COM 			return (1);
4958821SMichen.Chang@Sun.COM 		else
4968821SMichen.Chang@Sun.COM 			list = list->next;
4978821SMichen.Chang@Sun.COM 	return (0);
4988821SMichen.Chang@Sun.COM }
4998821SMichen.Chang@Sun.COM 
5008821SMichen.Chang@Sun.COM /*
5018821SMichen.Chang@Sun.COM  * attr_addmod(attrs, idx, item, val)
5028821SMichen.Chang@Sun.COM  *
5038821SMichen.Chang@Sun.COM  * Adds or updates attribute 'item' in ldap_attrs list to value
5048821SMichen.Chang@Sun.COM  * update idx if item is added
5058821SMichen.Chang@Sun.COM  * return:  -1 - PWU_NOMEM/error, 0 - success
5068821SMichen.Chang@Sun.COM  */
5078821SMichen.Chang@Sun.COM static int
attr_addmod(ns_ldap_attr_t ** attrs,int * idx,char * item,int value)5088821SMichen.Chang@Sun.COM attr_addmod(ns_ldap_attr_t **attrs, int *idx, char *item, int value)
5098821SMichen.Chang@Sun.COM {
5108821SMichen.Chang@Sun.COM 	char numbuf[MAX_INT_LEN], *strp;
5118821SMichen.Chang@Sun.COM 	int i;
5128821SMichen.Chang@Sun.COM 
5138821SMichen.Chang@Sun.COM 	/* stringize the value or abort */
5148821SMichen.Chang@Sun.COM 	if (snprintf(numbuf, MAX_INT_LEN, "%d", value) >= MAX_INT_LEN)
5158821SMichen.Chang@Sun.COM 		return (-1);
5168821SMichen.Chang@Sun.COM 
5178821SMichen.Chang@Sun.COM 	/* check for existence and modify existing */
5188821SMichen.Chang@Sun.COM 	for (i = 0; i < *idx; i++) {
5198821SMichen.Chang@Sun.COM 		if (attrs[i] != NULL &&
5208821SMichen.Chang@Sun.COM 		    strcmp(item, attrs[i]->attrname) == 0) {
5218821SMichen.Chang@Sun.COM 			strp = strdup(numbuf);
5228821SMichen.Chang@Sun.COM 			if (strp == NULL)
5238821SMichen.Chang@Sun.COM 				return (-1);
5248821SMichen.Chang@Sun.COM 			free(attrs[i]->attrvalue[0]);
5258821SMichen.Chang@Sun.COM 			attrs[i]->attrvalue[0] = strp;
5268821SMichen.Chang@Sun.COM 			return (0);
5278821SMichen.Chang@Sun.COM 		}
5288821SMichen.Chang@Sun.COM 	}
5298821SMichen.Chang@Sun.COM 	/* else add */
5308821SMichen.Chang@Sun.COM 	strp = strdup(numbuf);
5318821SMichen.Chang@Sun.COM 	if (strp == NULL)
5328821SMichen.Chang@Sun.COM 		return (-1);
5338821SMichen.Chang@Sun.COM 	attrs[*idx] = new_attr(item, strp);
5348821SMichen.Chang@Sun.COM 	if (attrs[*idx] == NULL)
5358821SMichen.Chang@Sun.COM 		return (-1);
5368821SMichen.Chang@Sun.COM 	(*idx)++;
5378821SMichen.Chang@Sun.COM 	return (0);
5388821SMichen.Chang@Sun.COM }
5398821SMichen.Chang@Sun.COM 
5408821SMichen.Chang@Sun.COM /*
5410Sstevel@tonic-gate  * ldap_update(items, rep, buf)
5420Sstevel@tonic-gate  *
5430Sstevel@tonic-gate  * create LDAP attributes in 'buf' for each attribute in 'items'.
5440Sstevel@tonic-gate  */
5450Sstevel@tonic-gate /*ARGSUSED*/
5460Sstevel@tonic-gate int
ldap_update(attrlist * items,pwu_repository_t * rep,void * buf)5470Sstevel@tonic-gate ldap_update(attrlist *items, pwu_repository_t *rep, void *buf)
5480Sstevel@tonic-gate {
5490Sstevel@tonic-gate 	attrlist *p;
5500Sstevel@tonic-gate 	ldapbuf_t *ldapbuf = (ldapbuf_t *)buf;
5518821SMichen.Chang@Sun.COM 	struct spwd *spw;
5528821SMichen.Chang@Sun.COM 	ns_ldap_attr_t **pattrs = ldapbuf->pattrs;
5538821SMichen.Chang@Sun.COM 	int pidx = 0;
5548821SMichen.Chang@Sun.COM 	ns_ldap_attr_t **sattrs = ldapbuf->sattrs;
5558821SMichen.Chang@Sun.COM 	int sidx = 0;
5560Sstevel@tonic-gate 	char *pwd, *val;
5570Sstevel@tonic-gate 	char *salt;
5580Sstevel@tonic-gate 	size_t cryptlen;
5598821SMichen.Chang@Sun.COM 	int len;
5608821SMichen.Chang@Sun.COM 	int count;
5618821SMichen.Chang@Sun.COM 	int rc = PWU_SUCCESS;
5628821SMichen.Chang@Sun.COM 	int aging_needed = 0;
5638821SMichen.Chang@Sun.COM 	int aging_set = 0;
5648821SMichen.Chang@Sun.COM 	int disable_aging;
5658821SMichen.Chang@Sun.COM 
5668821SMichen.Chang@Sun.COM 	spw = ldapbuf->spwd;
5678821SMichen.Chang@Sun.COM 
5688821SMichen.Chang@Sun.COM 	/*
5698821SMichen.Chang@Sun.COM 	 * if sp_max==0 and shadow update is enabled:
5708821SMichen.Chang@Sun.COM 	 * disable passwd aging after updating the password
5718821SMichen.Chang@Sun.COM 	 */
5728821SMichen.Chang@Sun.COM 	disable_aging = (spw != NULL && spw->sp_max == 0 &&
5738821SMichen.Chang@Sun.COM 	    ldapbuf->shadow_update_enabled);
5740Sstevel@tonic-gate 
5750Sstevel@tonic-gate 	for (p = items; p != NULL; p = p->next) {
5760Sstevel@tonic-gate 		switch (p->type) {
5770Sstevel@tonic-gate 		case ATTR_PASSWD:
5788821SMichen.Chang@Sun.COM 			/*
5798821SMichen.Chang@Sun.COM 			 * There is a special case for ldap: if the
5808821SMichen.Chang@Sun.COM 			 * password is to be deleted (-d to passwd),
5818821SMichen.Chang@Sun.COM 			 * p->data.val_s will be NULL.
5828821SMichen.Chang@Sun.COM 			 */
5838821SMichen.Chang@Sun.COM 			if (p->data.val_s == NULL) {
5848821SMichen.Chang@Sun.COM 				if (!ldapbuf->shadow_update_enabled)
5858821SMichen.Chang@Sun.COM 					return (PWU_CHANGE_NOT_ALLOWED);
5868821SMichen.Chang@Sun.COM 				cryptlen =
5878821SMichen.Chang@Sun.COM 				    sizeof ("{crypt}" NS_LDAP_NO_UNIX_PASSWORD);
5888821SMichen.Chang@Sun.COM 				val = malloc(cryptlen);
5898821SMichen.Chang@Sun.COM 				if (val == NULL)
5908821SMichen.Chang@Sun.COM 					return (PWU_NOMEM);
5918821SMichen.Chang@Sun.COM 				(void) snprintf(val, cryptlen,
5928821SMichen.Chang@Sun.COM 				"{crypt}" NS_LDAP_NO_UNIX_PASSWORD);
5938821SMichen.Chang@Sun.COM 			} else { /* not deleting password */
5948821SMichen.Chang@Sun.COM 				salt = crypt_gensalt(ldapbuf->passwd,
5958821SMichen.Chang@Sun.COM 				    ldapbuf->pwd);
5960Sstevel@tonic-gate 
5978821SMichen.Chang@Sun.COM 				if (salt == NULL) {
5988821SMichen.Chang@Sun.COM 					if (errno == ENOMEM)
5998821SMichen.Chang@Sun.COM 						return (PWU_NOMEM);
6008821SMichen.Chang@Sun.COM 
6010Sstevel@tonic-gate 					/* algorithm problem? */
6020Sstevel@tonic-gate 					syslog(LOG_AUTH | LOG_ALERT,
6030Sstevel@tonic-gate 					    "passwdutil: crypt_gensalt "
6040Sstevel@tonic-gate 					    "%m");
6050Sstevel@tonic-gate 					return (PWU_UPDATE_FAILED);
6060Sstevel@tonic-gate 				}
6078821SMichen.Chang@Sun.COM 
6088821SMichen.Chang@Sun.COM 				pwd = crypt(p->data.val_s, salt);
6098821SMichen.Chang@Sun.COM 				free(salt);
6108821SMichen.Chang@Sun.COM 				cryptlen = strlen(pwd) + sizeof ("{crypt}");
6118821SMichen.Chang@Sun.COM 				val = malloc(cryptlen);
6128821SMichen.Chang@Sun.COM 				if (val == NULL)
6138821SMichen.Chang@Sun.COM 					return (PWU_NOMEM);
6148821SMichen.Chang@Sun.COM 				(void) snprintf(val, cryptlen,
6158821SMichen.Chang@Sun.COM 				    "{crypt}%s", pwd);
6168821SMichen.Chang@Sun.COM 			}
6178821SMichen.Chang@Sun.COM 
6188821SMichen.Chang@Sun.COM 			/*
6198821SMichen.Chang@Sun.COM 			 * If not managing passwordAccount,
6208821SMichen.Chang@Sun.COM 			 * insert the new password in the
6218821SMichen.Chang@Sun.COM 			 * passwd attr array and break.
6228821SMichen.Chang@Sun.COM 			 */
6238821SMichen.Chang@Sun.COM 			if (!ldapbuf->shadow_update_enabled) {
6248821SMichen.Chang@Sun.COM 				NEW_ATTR(pattrs, pidx,
6258821SMichen.Chang@Sun.COM 				    _PWD_USERPASSWORD, val);
6268821SMichen.Chang@Sun.COM 				break;
6270Sstevel@tonic-gate 			}
6280Sstevel@tonic-gate 
6298821SMichen.Chang@Sun.COM 			/*
6308821SMichen.Chang@Sun.COM 			 * Managing passwordAccount, insert the
6318821SMichen.Chang@Sun.COM 			 * new password, along with lastChange and
6328821SMichen.Chang@Sun.COM 			 * shadowFlag, in the shadow attr array.
6338821SMichen.Chang@Sun.COM 			 */
6348821SMichen.Chang@Sun.COM 			NEW_ATTR(sattrs, sidx, _PWD_USERPASSWORD, val);
6358821SMichen.Chang@Sun.COM 
6368821SMichen.Chang@Sun.COM 			if (attr_addmod(sattrs, &sidx, _S_LASTCHANGE,
6378821SMichen.Chang@Sun.COM 			    DAY_NOW_32) < 0)
6380Sstevel@tonic-gate 				return (PWU_NOMEM);
6398821SMichen.Chang@Sun.COM 			spw->sp_lstchg = DAY_NOW_32;
6400Sstevel@tonic-gate 
6418821SMichen.Chang@Sun.COM 			if (attr_addmod(sattrs, &sidx, _S_FLAG,
6428821SMichen.Chang@Sun.COM 			    spw->sp_flag & ~FAILCOUNT_MASK) < 0)
6438821SMichen.Chang@Sun.COM 				return (PWU_NOMEM);
6448821SMichen.Chang@Sun.COM 			spw->sp_flag &= ~FAILCOUNT_MASK; /* reset count */
6458821SMichen.Chang@Sun.COM 			aging_needed = 1;
6460Sstevel@tonic-gate 			break;
6470Sstevel@tonic-gate 		case ATTR_PASSWD_SERVER_POLICY:
6488821SMichen.Chang@Sun.COM 			/*
6498821SMichen.Chang@Sun.COM 			 * For server policy, don't crypt the password,
6508821SMichen.Chang@Sun.COM 			 * send the password as is to the server and
6518821SMichen.Chang@Sun.COM 			 * let the LDAP server do its own password
6528821SMichen.Chang@Sun.COM 			 * encryption
6538821SMichen.Chang@Sun.COM 			 */
6548821SMichen.Chang@Sun.COM 			STRDUP_OR_RET(val, p->data.val_s);
6550Sstevel@tonic-gate 
6568821SMichen.Chang@Sun.COM 			NEW_ATTR(pattrs, pidx, _PWD_USERPASSWORD, val);
6570Sstevel@tonic-gate 			break;
6580Sstevel@tonic-gate 		case ATTR_COMMENT:
6590Sstevel@tonic-gate 			/* XX correct? */
6608821SMichen.Chang@Sun.COM 			NEW_ATTR(pattrs, pidx, _PWD_DESCRIPTION, p->data.val_s);
6610Sstevel@tonic-gate 			break;
6620Sstevel@tonic-gate 		case ATTR_GECOS:
6638821SMichen.Chang@Sun.COM 			if (!ldapbuf->shadow_update_enabled) {
6648821SMichen.Chang@Sun.COM 				NEW_ATTR(pattrs, pidx, _PWD_GECOS,
6658821SMichen.Chang@Sun.COM 				    p->data.val_s);
6668821SMichen.Chang@Sun.COM 			} else {
6678821SMichen.Chang@Sun.COM 				NEW_ATTR(sattrs, sidx, _PWD_GECOS,
6688821SMichen.Chang@Sun.COM 				    p->data.val_s);
6698821SMichen.Chang@Sun.COM 			}
6700Sstevel@tonic-gate 			break;
6710Sstevel@tonic-gate 		case ATTR_HOMEDIR:
6728821SMichen.Chang@Sun.COM 			if (!ldapbuf->shadow_update_enabled) {
6738821SMichen.Chang@Sun.COM 				NEW_ATTR(pattrs, pidx, _PWD_HOMEDIRECTORY,
6748821SMichen.Chang@Sun.COM 				    p->data.val_s);
6758821SMichen.Chang@Sun.COM 			} else {
6768821SMichen.Chang@Sun.COM 				NEW_ATTR(sattrs, sidx, _PWD_HOMEDIRECTORY,
6778821SMichen.Chang@Sun.COM 				    p->data.val_s);
6788821SMichen.Chang@Sun.COM 			}
6790Sstevel@tonic-gate 			break;
6800Sstevel@tonic-gate 		case ATTR_SHELL:
6818821SMichen.Chang@Sun.COM 			if (!ldapbuf->shadow_update_enabled) {
6828821SMichen.Chang@Sun.COM 				NEW_ATTR(pattrs, pidx, _PWD_LOGINSHELL,
6838821SMichen.Chang@Sun.COM 				    p->data.val_s);
6848821SMichen.Chang@Sun.COM 			} else {
6858821SMichen.Chang@Sun.COM 				NEW_ATTR(sattrs, sidx, _PWD_LOGINSHELL,
6868821SMichen.Chang@Sun.COM 				    p->data.val_s);
6878821SMichen.Chang@Sun.COM 			}
6880Sstevel@tonic-gate 			break;
6898821SMichen.Chang@Sun.COM 		/* We don't update NAME, UID, GID */
6900Sstevel@tonic-gate 		case ATTR_NAME:
6910Sstevel@tonic-gate 		case ATTR_UID:
6920Sstevel@tonic-gate 		case ATTR_GID:
6938821SMichen.Chang@Sun.COM 		/* Unsupported item */
6940Sstevel@tonic-gate 		case ATTR_AGE:
6958821SMichen.Chang@Sun.COM 			break;
6968821SMichen.Chang@Sun.COM 		case ATTR_LOCK_ACCOUNT:
6978821SMichen.Chang@Sun.COM 			if (!ldapbuf->shadow_update_enabled)
6988821SMichen.Chang@Sun.COM 				break;	/* not managing passwordAccount */
6998821SMichen.Chang@Sun.COM 			if (spw->sp_pwdp == NULL) {
7008821SMichen.Chang@Sun.COM 				spw->sp_pwdp = LOCKSTRING;
7019043Sgww@eng.sun.com 			} else if ((strncmp(spw->sp_pwdp, LOCKSTRING,
7029043Sgww@eng.sun.com 			    sizeof (LOCKSTRING)-1) != 0) &&
7039043Sgww@eng.sun.com 			    (strcmp(spw->sp_pwdp, NOLOGINSTRING) != 0)) {
7048821SMichen.Chang@Sun.COM 				len = sizeof (LOCKSTRING)-1 +
7058821SMichen.Chang@Sun.COM 				    strlen(spw->sp_pwdp) + 1 +
7068821SMichen.Chang@Sun.COM 				    sizeof ("{crypt}");
7078821SMichen.Chang@Sun.COM 				pwd = malloc(len);
7088821SMichen.Chang@Sun.COM 				if (pwd == NULL) {
7098821SMichen.Chang@Sun.COM 					return (PWU_NOMEM);
7108821SMichen.Chang@Sun.COM 				}
7118821SMichen.Chang@Sun.COM 				(void) strlcpy(pwd, "{crypt}", len);
7128821SMichen.Chang@Sun.COM 				(void) strlcat(pwd, LOCKSTRING, len);
7138821SMichen.Chang@Sun.COM 				(void) strlcat(pwd, spw->sp_pwdp, len);
7148821SMichen.Chang@Sun.COM 				free(spw->sp_pwdp);
7158821SMichen.Chang@Sun.COM 				spw->sp_pwdp = pwd;
7168821SMichen.Chang@Sun.COM 				NEW_ATTR(sattrs, sidx, _PWD_USERPASSWORD,
7178821SMichen.Chang@Sun.COM 				    spw->sp_pwdp);
7188821SMichen.Chang@Sun.COM 			}
7198821SMichen.Chang@Sun.COM 			if (attr_addmod(sattrs, &sidx, _S_LASTCHANGE,
7208821SMichen.Chang@Sun.COM 			    DAY_NOW_32) < 0)
7218821SMichen.Chang@Sun.COM 				return (PWU_NOMEM);
7228821SMichen.Chang@Sun.COM 			spw->sp_lstchg = DAY_NOW_32;
7238821SMichen.Chang@Sun.COM 			break;
7248821SMichen.Chang@Sun.COM 
7258821SMichen.Chang@Sun.COM 		case ATTR_UNLOCK_ACCOUNT:
7268821SMichen.Chang@Sun.COM 			if (!ldapbuf->shadow_update_enabled)
7278821SMichen.Chang@Sun.COM 				break;	/* not managing passwordAccount */
7288821SMichen.Chang@Sun.COM 			if (spw->sp_pwdp &&
7298821SMichen.Chang@Sun.COM 			    strncmp(spw->sp_pwdp, LOCKSTRING,
7308821SMichen.Chang@Sun.COM 			    sizeof (LOCKSTRING)-1) == 0) {
7318821SMichen.Chang@Sun.COM 				len = (sizeof ("{crypt}") -
7328821SMichen.Chang@Sun.COM 				    sizeof (LOCKSTRING)) +
7338821SMichen.Chang@Sun.COM 				    strlen(spw->sp_pwdp) + 1;
7348821SMichen.Chang@Sun.COM 				pwd = malloc(len);
7358821SMichen.Chang@Sun.COM 				if (pwd == NULL) {
7368821SMichen.Chang@Sun.COM 					return (PWU_NOMEM);
7378821SMichen.Chang@Sun.COM 				}
7388821SMichen.Chang@Sun.COM 				(void) strlcpy(pwd, "{crypt}", len);
7398821SMichen.Chang@Sun.COM 				(void) strlcat(pwd, spw->sp_pwdp +
7408821SMichen.Chang@Sun.COM 				    sizeof (LOCKSTRING)-1, len);
7418821SMichen.Chang@Sun.COM 				free(spw->sp_pwdp);
7428821SMichen.Chang@Sun.COM 				spw->sp_pwdp = pwd;
7438821SMichen.Chang@Sun.COM 
7448821SMichen.Chang@Sun.COM 				NEW_ATTR(sattrs, sidx, _PWD_USERPASSWORD,
7458821SMichen.Chang@Sun.COM 				    spw->sp_pwdp);
7468821SMichen.Chang@Sun.COM 				if (attr_addmod(sattrs, &sidx, _S_LASTCHANGE,
7478821SMichen.Chang@Sun.COM 				    DAY_NOW_32) < 0)
7488821SMichen.Chang@Sun.COM 					return (PWU_NOMEM);
7498821SMichen.Chang@Sun.COM 				spw->sp_lstchg = DAY_NOW_32;
7508821SMichen.Chang@Sun.COM 			}
7518821SMichen.Chang@Sun.COM 			break;
7528821SMichen.Chang@Sun.COM 
7538821SMichen.Chang@Sun.COM 		case ATTR_NOLOGIN_ACCOUNT:
7548821SMichen.Chang@Sun.COM 			if (!ldapbuf->shadow_update_enabled)
7558821SMichen.Chang@Sun.COM 				break;	/* not managing passwordAccount */
7568821SMichen.Chang@Sun.COM 			free(spw->sp_pwdp);
7578821SMichen.Chang@Sun.COM 			STRDUP_OR_RET(spw->sp_pwdp, "{crypt}" NOLOGINSTRING);
7588821SMichen.Chang@Sun.COM 			NEW_ATTR(sattrs, sidx, _PWD_USERPASSWORD, spw->sp_pwdp);
7598821SMichen.Chang@Sun.COM 			if (attr_addmod(sattrs, &sidx, _S_LASTCHANGE,
7608821SMichen.Chang@Sun.COM 			    DAY_NOW_32) < 0)
7618821SMichen.Chang@Sun.COM 				return (PWU_NOMEM);
7628821SMichen.Chang@Sun.COM 			spw->sp_lstchg = DAY_NOW_32;
7638821SMichen.Chang@Sun.COM 			break;
7648821SMichen.Chang@Sun.COM 
7658821SMichen.Chang@Sun.COM 		case ATTR_EXPIRE_PASSWORD:
7668821SMichen.Chang@Sun.COM 			if (!ldapbuf->shadow_update_enabled)
7678821SMichen.Chang@Sun.COM 				break;	/* not managing passwordAccount */
7688821SMichen.Chang@Sun.COM 			NUM_TO_STR(val, 0);
7698821SMichen.Chang@Sun.COM 			NEW_ATTR(sattrs, sidx, _S_LASTCHANGE, val);
7708821SMichen.Chang@Sun.COM 			break;
7718821SMichen.Chang@Sun.COM 
7720Sstevel@tonic-gate 		case ATTR_LSTCHG:
7738821SMichen.Chang@Sun.COM 			if (!ldapbuf->shadow_update_enabled)
7748821SMichen.Chang@Sun.COM 				break;	/* not managing passwordAccount */
7758821SMichen.Chang@Sun.COM 			NUM_TO_STR(val, p->data.val_i);
7768821SMichen.Chang@Sun.COM 			NEW_ATTR(sattrs, sidx, _S_LASTCHANGE, val);
7778821SMichen.Chang@Sun.COM 			break;
7788821SMichen.Chang@Sun.COM 
7790Sstevel@tonic-gate 		case ATTR_MIN:
7808821SMichen.Chang@Sun.COM 			if (!ldapbuf->shadow_update_enabled)
7818821SMichen.Chang@Sun.COM 				break;	/* not managing passwordAccount */
7828821SMichen.Chang@Sun.COM 			if (spw->sp_max == -1 && p->data.val_i != -1 &&
7838821SMichen.Chang@Sun.COM 			    max_present(p->next) == 0)
7848821SMichen.Chang@Sun.COM 				return (PWU_AGING_DISABLED);
7858821SMichen.Chang@Sun.COM 			NUM_TO_STR(val, p->data.val_i);
7868821SMichen.Chang@Sun.COM 			NEW_ATTR(sattrs, sidx, _S_MIN, val);
7878821SMichen.Chang@Sun.COM 			aging_set = 1;
7888821SMichen.Chang@Sun.COM 			break;
7898821SMichen.Chang@Sun.COM 
7900Sstevel@tonic-gate 		case ATTR_MAX:
7918821SMichen.Chang@Sun.COM 			if (!ldapbuf->shadow_update_enabled)
7928821SMichen.Chang@Sun.COM 				break;	/* not managing passwordAccount */
7938821SMichen.Chang@Sun.COM 			if (p->data.val_i == -1) {
7948821SMichen.Chang@Sun.COM 				/* Turn off aging. Reset min and warn too */
7958821SMichen.Chang@Sun.COM 				spw->sp_max = spw->sp_min = spw->sp_warn = -1;
7968821SMichen.Chang@Sun.COM 				NUM_TO_STR(val, -1);
7978821SMichen.Chang@Sun.COM 				NEW_ATTR(sattrs, sidx, _S_MIN, val);
7988821SMichen.Chang@Sun.COM 				NUM_TO_STR(val, -1);
7998821SMichen.Chang@Sun.COM 				NEW_ATTR(sattrs, sidx, _S_WARNING, val);
8008821SMichen.Chang@Sun.COM 			} else {
8018821SMichen.Chang@Sun.COM 				/* Turn account aging on */
8028821SMichen.Chang@Sun.COM 				if (spw->sp_min == -1) {
8038821SMichen.Chang@Sun.COM 					/*
8048821SMichen.Chang@Sun.COM 					 * minage was not set with command-
8058821SMichen.Chang@Sun.COM 					 * line option: set to zero
8068821SMichen.Chang@Sun.COM 					 */
8078821SMichen.Chang@Sun.COM 					spw->sp_min = 0;
8088821SMichen.Chang@Sun.COM 					NUM_TO_STR(val, 0);
8098821SMichen.Chang@Sun.COM 					NEW_ATTR(sattrs, sidx, _S_MIN,
8108821SMichen.Chang@Sun.COM 					    val);
8118821SMichen.Chang@Sun.COM 				}
8128821SMichen.Chang@Sun.COM 				/*
8138821SMichen.Chang@Sun.COM 				 * If aging was turned off, we update lstchg.
8148821SMichen.Chang@Sun.COM 				 * We take care not to update lstchg if the
8158821SMichen.Chang@Sun.COM 				 * user has no password, otherwise the user
8168821SMichen.Chang@Sun.COM 				 * might not be required to provide a password
8178821SMichen.Chang@Sun.COM 				 * the next time [s]he logs in.
8188821SMichen.Chang@Sun.COM 				 *
8198821SMichen.Chang@Sun.COM 				 * Also, if lstchg != -1 (i.e., not set)
8208821SMichen.Chang@Sun.COM 				 * we keep the old value.
8218821SMichen.Chang@Sun.COM 				 */
8228821SMichen.Chang@Sun.COM 				if (spw->sp_max == -1 &&
8238821SMichen.Chang@Sun.COM 				    spw->sp_pwdp != NULL && *spw->sp_pwdp &&
8248821SMichen.Chang@Sun.COM 				    spw->sp_lstchg == -1) {
8258821SMichen.Chang@Sun.COM 					if (attr_addmod(sattrs, &sidx,
8268821SMichen.Chang@Sun.COM 					    _S_LASTCHANGE,
8278821SMichen.Chang@Sun.COM 					    DAY_NOW_32) < 0)
8288821SMichen.Chang@Sun.COM 						return (PWU_NOMEM);
8298821SMichen.Chang@Sun.COM 					spw->sp_lstchg = DAY_NOW_32;
8308821SMichen.Chang@Sun.COM 				}
8318821SMichen.Chang@Sun.COM 			}
8328821SMichen.Chang@Sun.COM 			NUM_TO_STR(val, p->data.val_i);
8338821SMichen.Chang@Sun.COM 			NEW_ATTR(sattrs, sidx, _S_MAX, val);
8348821SMichen.Chang@Sun.COM 			aging_set = 1;
8358821SMichen.Chang@Sun.COM 			break;
8368821SMichen.Chang@Sun.COM 
8370Sstevel@tonic-gate 		case ATTR_WARN:
8388821SMichen.Chang@Sun.COM 			if (!ldapbuf->shadow_update_enabled)
8398821SMichen.Chang@Sun.COM 				break;	/* not managing passwordAccount */
8408821SMichen.Chang@Sun.COM 			if (spw->sp_max == -1 &&
8418821SMichen.Chang@Sun.COM 			    p->data.val_i != -1 && max_present(p->next) == 0)
8428821SMichen.Chang@Sun.COM 				return (PWU_AGING_DISABLED);
8438821SMichen.Chang@Sun.COM 			NUM_TO_STR(val, p->data.val_i);
8448821SMichen.Chang@Sun.COM 			NEW_ATTR(sattrs, sidx, _S_WARNING, val);
8458821SMichen.Chang@Sun.COM 			break;
8468821SMichen.Chang@Sun.COM 
8470Sstevel@tonic-gate 		case ATTR_INACT:
8488821SMichen.Chang@Sun.COM 			if (!ldapbuf->shadow_update_enabled)
8498821SMichen.Chang@Sun.COM 				break;	/* not managing passwordAccount */
8508821SMichen.Chang@Sun.COM 			NUM_TO_STR(val, p->data.val_i);
8518821SMichen.Chang@Sun.COM 			NEW_ATTR(sattrs, sidx, _S_INACTIVE, val);
8528821SMichen.Chang@Sun.COM 			break;
8538821SMichen.Chang@Sun.COM 
8540Sstevel@tonic-gate 		case ATTR_EXPIRE:
8558821SMichen.Chang@Sun.COM 			if (!ldapbuf->shadow_update_enabled)
8568821SMichen.Chang@Sun.COM 				break;	/* not managing passwordAccount */
8578821SMichen.Chang@Sun.COM 			NUM_TO_STR(val, p->data.val_i);
8588821SMichen.Chang@Sun.COM 			NEW_ATTR(sattrs, sidx, _S_EXPIRE, val);
8598821SMichen.Chang@Sun.COM 			break;
8608821SMichen.Chang@Sun.COM 
8610Sstevel@tonic-gate 		case ATTR_FLAG:
8628821SMichen.Chang@Sun.COM 			if (!ldapbuf->shadow_update_enabled)
8638821SMichen.Chang@Sun.COM 				break;	/* not managing passwordAccount */
8648821SMichen.Chang@Sun.COM 			NUM_TO_STR(val, p->data.val_i);
8658821SMichen.Chang@Sun.COM 			NEW_ATTR(sattrs, sidx, _S_FLAG, val);
8668821SMichen.Chang@Sun.COM 			break;
8678821SMichen.Chang@Sun.COM 		case ATTR_INCR_FAILED_LOGINS:
8688821SMichen.Chang@Sun.COM 			if (!ldapbuf->shadow_update_enabled) {
8698821SMichen.Chang@Sun.COM 				rc = PWU_CHANGE_NOT_ALLOWED;
8708821SMichen.Chang@Sun.COM 				break;	/* not managing passwordAccount */
8718821SMichen.Chang@Sun.COM 			}
8728821SMichen.Chang@Sun.COM 			count = (spw->sp_flag & FAILCOUNT_MASK) + 1;
8738821SMichen.Chang@Sun.COM 			spw->sp_flag &= ~FAILCOUNT_MASK;
8748821SMichen.Chang@Sun.COM 			spw->sp_flag |= min(FAILCOUNT_MASK, count);
8758821SMichen.Chang@Sun.COM 			p->data.val_i = count;
8768821SMichen.Chang@Sun.COM 			NUM_TO_STR(val, spw->sp_flag);
8778821SMichen.Chang@Sun.COM 			NEW_ATTR(sattrs, sidx, _S_FLAG, val);
8788821SMichen.Chang@Sun.COM 			break;
8798821SMichen.Chang@Sun.COM 		case ATTR_RST_FAILED_LOGINS:
8808821SMichen.Chang@Sun.COM 			if (!ldapbuf->shadow_update_enabled) {
8818821SMichen.Chang@Sun.COM 				rc = PWU_CHANGE_NOT_ALLOWED;
8828821SMichen.Chang@Sun.COM 				break;	/* not managing passwordAccount */
8838821SMichen.Chang@Sun.COM 			}
8848821SMichen.Chang@Sun.COM 			p->data.val_i = spw->sp_flag & FAILCOUNT_MASK;
8858821SMichen.Chang@Sun.COM 			spw->sp_flag &= ~FAILCOUNT_MASK;
8868821SMichen.Chang@Sun.COM 			NUM_TO_STR(val, spw->sp_flag);
8878821SMichen.Chang@Sun.COM 			NEW_ATTR(sattrs, sidx, _S_FLAG, val);
8880Sstevel@tonic-gate 			break;
8890Sstevel@tonic-gate 		default:
8900Sstevel@tonic-gate 			break;
8910Sstevel@tonic-gate 		}
8920Sstevel@tonic-gate 	}
8930Sstevel@tonic-gate 
8948821SMichen.Chang@Sun.COM 	/*
8958821SMichen.Chang@Sun.COM 	 * If the ldap client is configured with shadow update enabled,
8968821SMichen.Chang@Sun.COM 	 * then what should the new aging values look like?
8978821SMichen.Chang@Sun.COM 	 *
8988821SMichen.Chang@Sun.COM 	 * There are a number of different conditions
8998821SMichen.Chang@Sun.COM 	 *
9008821SMichen.Chang@Sun.COM 	 *  a) aging is already configured: don't touch it
9018821SMichen.Chang@Sun.COM 	 *
9028821SMichen.Chang@Sun.COM 	 *  b) disable_aging is set: disable aging
9038821SMichen.Chang@Sun.COM 	 *
9048821SMichen.Chang@Sun.COM 	 *  c) aging is not configured: turn on default aging;
9058821SMichen.Chang@Sun.COM 	 *
9068821SMichen.Chang@Sun.COM 	 *  b) and c) of course only if aging_needed and !aging_set.
9078821SMichen.Chang@Sun.COM 	 *  (i.e., password changed, and aging values not changed)
9088821SMichen.Chang@Sun.COM 	 */
9090Sstevel@tonic-gate 
9108821SMichen.Chang@Sun.COM 	if (ldapbuf->shadow_update_enabled && spw != NULL && spw->sp_max <= 0) {
9118821SMichen.Chang@Sun.COM 		/* a) aging not yet configured */
9128821SMichen.Chang@Sun.COM 		if (aging_needed && !aging_set) {
9138821SMichen.Chang@Sun.COM 			if (disable_aging) {
9148821SMichen.Chang@Sun.COM 				/* b) turn off aging */
9158821SMichen.Chang@Sun.COM 				spw->sp_min = spw->sp_max = spw->sp_warn = -1;
9168821SMichen.Chang@Sun.COM 				if (attr_addmod(sattrs, &sidx, _S_MIN, -1) < 0)
9178821SMichen.Chang@Sun.COM 					return (PWU_NOMEM);
9188821SMichen.Chang@Sun.COM 				if (attr_addmod(sattrs, &sidx, _S_MAX, -1) < 0)
9198821SMichen.Chang@Sun.COM 					return (PWU_NOMEM);
9208821SMichen.Chang@Sun.COM 				if (attr_addmod(sattrs, &sidx, _S_WARNING,
9218821SMichen.Chang@Sun.COM 				    -1) < 0)
9228821SMichen.Chang@Sun.COM 					return (PWU_NOMEM);
9238821SMichen.Chang@Sun.COM 			} else {
9248821SMichen.Chang@Sun.COM 				/* c) */
9258821SMichen.Chang@Sun.COM 				turn_on_default_aging(spw);
9268821SMichen.Chang@Sun.COM 
9278821SMichen.Chang@Sun.COM 				if (attr_addmod(sattrs, &sidx, _S_MIN,
9288821SMichen.Chang@Sun.COM 				    spw->sp_min) < 0)
9298821SMichen.Chang@Sun.COM 					return (PWU_NOMEM);
9308821SMichen.Chang@Sun.COM 				if (attr_addmod(sattrs, &sidx, _S_MAX,
9318821SMichen.Chang@Sun.COM 				    spw->sp_max) < 0)
9328821SMichen.Chang@Sun.COM 					return (PWU_NOMEM);
9338821SMichen.Chang@Sun.COM 				if (attr_addmod(sattrs, &sidx,
9348821SMichen.Chang@Sun.COM 				    _S_WARNING, spw->sp_warn) < 0)
9358821SMichen.Chang@Sun.COM 					return (PWU_NOMEM);
9368821SMichen.Chang@Sun.COM 			}
9378821SMichen.Chang@Sun.COM 		}
9388821SMichen.Chang@Sun.COM 	}
9398821SMichen.Chang@Sun.COM 
9408821SMichen.Chang@Sun.COM 	pattrs[pidx] = NULL;
9418821SMichen.Chang@Sun.COM 	sattrs[sidx] = NULL;
9428821SMichen.Chang@Sun.COM 
9438821SMichen.Chang@Sun.COM 	return (rc);
9440Sstevel@tonic-gate }
9450Sstevel@tonic-gate 
9460Sstevel@tonic-gate /*
9470Sstevel@tonic-gate  * ldap_to_pwu_code(error, pwd_status)
9480Sstevel@tonic-gate  *
9490Sstevel@tonic-gate  * translation from LDAP return values and PWU return values
9500Sstevel@tonic-gate  */
9510Sstevel@tonic-gate int
ldap_to_pwu_code(int error,int pwd_status)9520Sstevel@tonic-gate ldap_to_pwu_code(int error, int pwd_status)
9530Sstevel@tonic-gate {
9540Sstevel@tonic-gate 	switch (error) {
9550Sstevel@tonic-gate 	case NS_LDAP_SUCCESS:	return (PWU_SUCCESS);
9560Sstevel@tonic-gate 	case NS_LDAP_OP_FAILED:	return (PWU_DENIED);
9570Sstevel@tonic-gate 	case NS_LDAP_NOTFOUND:	return (PWU_NOT_FOUND);
9580Sstevel@tonic-gate 	case NS_LDAP_MEMORY:	return (PWU_NOMEM);
9590Sstevel@tonic-gate 	case NS_LDAP_CONFIG:	return (PWU_NOT_FOUND);
9600Sstevel@tonic-gate 	case NS_LDAP_INTERNAL:
9610Sstevel@tonic-gate 		switch (pwd_status) {
9620Sstevel@tonic-gate 		case NS_PASSWD_EXPIRED:
9630Sstevel@tonic-gate 			return (PWU_DENIED);
9640Sstevel@tonic-gate 		case NS_PASSWD_CHANGE_NOT_ALLOWED:
9650Sstevel@tonic-gate 			return (PWU_CHANGE_NOT_ALLOWED);
9660Sstevel@tonic-gate 		case NS_PASSWD_TOO_SHORT:
9670Sstevel@tonic-gate 			return (PWU_PWD_TOO_SHORT);
9680Sstevel@tonic-gate 		case NS_PASSWD_INVALID_SYNTAX:
9690Sstevel@tonic-gate 			return (PWU_PWD_INVALID);
9700Sstevel@tonic-gate 		case NS_PASSWD_IN_HISTORY:
9710Sstevel@tonic-gate 			return (PWU_PWD_IN_HISTORY);
9720Sstevel@tonic-gate 		case NS_PASSWD_WITHIN_MIN_AGE:
9730Sstevel@tonic-gate 			return (PWU_WITHIN_MIN_AGE);
9740Sstevel@tonic-gate 		default:
9750Sstevel@tonic-gate 			return (PWU_SYSTEM_ERROR);
9760Sstevel@tonic-gate 		}
9770Sstevel@tonic-gate 	default:		return (PWU_SYSTEM_ERROR);
9780Sstevel@tonic-gate 	}
9790Sstevel@tonic-gate }
9800Sstevel@tonic-gate 
9810Sstevel@tonic-gate int
ldap_replaceattr(const char * dn,ns_ldap_attr_t ** attrs,const char * binddn,const char * pwd,int * pwd_status,int flags)9820Sstevel@tonic-gate ldap_replaceattr(const char *dn, ns_ldap_attr_t **attrs, const char *binddn,
9838821SMichen.Chang@Sun.COM 	const char *pwd, int *pwd_status, int flags)
9840Sstevel@tonic-gate {
9850Sstevel@tonic-gate 	int		result = NS_LDAP_OP_FAILED;
9860Sstevel@tonic-gate 	int		ldaprc;
9870Sstevel@tonic-gate 	int		authstried = 0;
9880Sstevel@tonic-gate 	char		**certpath = NULL;
9890Sstevel@tonic-gate 	ns_auth_t	**app;
9900Sstevel@tonic-gate 	ns_auth_t	**authpp = NULL;
9910Sstevel@tonic-gate 	ns_auth_t	*authp = NULL;
9920Sstevel@tonic-gate 	ns_cred_t	*credp;
9930Sstevel@tonic-gate 	ns_ldap_error_t	*errorp = NULL;
9940Sstevel@tonic-gate 
9950Sstevel@tonic-gate 	debug("%s: replace_ldapattr()", __FILE__);
9960Sstevel@tonic-gate 
9970Sstevel@tonic-gate 	if ((credp = (ns_cred_t *)calloc(1, sizeof (ns_cred_t))) == NULL)
9988821SMichen.Chang@Sun.COM 		return (NS_LDAP_MEMORY); /* map to PWU_NOMEM */
9990Sstevel@tonic-gate 
10008821SMichen.Chang@Sun.COM 	/* for admin shadow update, dn and pwd will be set later in libsldap */
10018821SMichen.Chang@Sun.COM 	if ((flags & NS_LDAP_UPDATE_SHADOW) == 0) {
10028821SMichen.Chang@Sun.COM 		/* Fill in the user name and password */
10038821SMichen.Chang@Sun.COM 		if (dn == NULL || pwd == NULL)
10048821SMichen.Chang@Sun.COM 			goto out;
10058821SMichen.Chang@Sun.COM 		credp->cred.unix_cred.userID = strdup(binddn);
10068821SMichen.Chang@Sun.COM 		credp->cred.unix_cred.passwd = strdup(pwd);
10078821SMichen.Chang@Sun.COM 	}
10080Sstevel@tonic-gate 
10090Sstevel@tonic-gate 	/* get host certificate path, if one is configured */
10100Sstevel@tonic-gate 	ldaprc = __ns_ldap_getParam(NS_LDAP_HOST_CERTPATH_P,
10118821SMichen.Chang@Sun.COM 	    (void ***)&certpath, &errorp);
10120Sstevel@tonic-gate 	if (ldaprc != NS_LDAP_SUCCESS)
10130Sstevel@tonic-gate 		goto out;
10140Sstevel@tonic-gate 
10150Sstevel@tonic-gate 	if (certpath && *certpath)
10160Sstevel@tonic-gate 		credp->hostcertpath = *certpath;
10170Sstevel@tonic-gate 
10180Sstevel@tonic-gate 	/* Load the service specific authentication method */
10190Sstevel@tonic-gate 	ldaprc = __ns_ldap_getServiceAuthMethods("passwd-cmd", &authpp,
10208821SMichen.Chang@Sun.COM 	    &errorp);
10210Sstevel@tonic-gate 
10220Sstevel@tonic-gate 	if (ldaprc != NS_LDAP_SUCCESS)
10230Sstevel@tonic-gate 		goto out;
10240Sstevel@tonic-gate 
10250Sstevel@tonic-gate 	/*
10260Sstevel@tonic-gate 	 * if authpp is null, there is no serviceAuthenticationMethod
10270Sstevel@tonic-gate 	 * try default authenticationMethod
10280Sstevel@tonic-gate 	 */
10290Sstevel@tonic-gate 	if (authpp == NULL) {
10300Sstevel@tonic-gate 		ldaprc = __ns_ldap_getParam(NS_LDAP_AUTH_P, (void ***)&authpp,
10318821SMichen.Chang@Sun.COM 		    &errorp);
10320Sstevel@tonic-gate 		if (ldaprc != NS_LDAP_SUCCESS)
10330Sstevel@tonic-gate 			goto out;
10340Sstevel@tonic-gate 	}
10350Sstevel@tonic-gate 
10360Sstevel@tonic-gate 	/*
10370Sstevel@tonic-gate 	 * if authpp is still null, then can not authenticate, syslog
10380Sstevel@tonic-gate 	 * error message and return error
10390Sstevel@tonic-gate 	 */
10400Sstevel@tonic-gate 	if (authpp == NULL) {
10410Sstevel@tonic-gate 		syslog(LOG_ERR,
10420Sstevel@tonic-gate 		"passwdutil: no legal LDAP authentication method configured");
10430Sstevel@tonic-gate 		result = NS_LDAP_OP_FAILED;
10440Sstevel@tonic-gate 		goto out;
10450Sstevel@tonic-gate 	}
10460Sstevel@tonic-gate 
10470Sstevel@tonic-gate 	/*
10480Sstevel@tonic-gate 	 * Walk the array and try all authentication methods in order except
10490Sstevel@tonic-gate 	 * for "none".
10500Sstevel@tonic-gate 	 */
10510Sstevel@tonic-gate 	for (app = authpp; *app; app++) {
10520Sstevel@tonic-gate 		authp = *app;
10530Sstevel@tonic-gate 		/* what about disabling other mechanisms? "tls:sasl/EXTERNAL" */
10540Sstevel@tonic-gate 		if (authp->type == NS_LDAP_AUTH_NONE)
10550Sstevel@tonic-gate 			continue;
10560Sstevel@tonic-gate 		authstried++;
10570Sstevel@tonic-gate 		credp->auth.type = authp->type;
10580Sstevel@tonic-gate 		credp->auth.tlstype = authp->tlstype;
10590Sstevel@tonic-gate 		credp->auth.saslmech = authp->saslmech;
10600Sstevel@tonic-gate 		credp->auth.saslopt = authp->saslopt;
10610Sstevel@tonic-gate 
10620Sstevel@tonic-gate 		ldaprc = __ns_ldap_repAttr("shadow", dn,
10638821SMichen.Chang@Sun.COM 		    (const ns_ldap_attr_t * const *)attrs,
10648821SMichen.Chang@Sun.COM 		    credp, flags, &errorp);
10650Sstevel@tonic-gate 		if (ldaprc == NS_LDAP_SUCCESS) {
10660Sstevel@tonic-gate 			result = NS_LDAP_SUCCESS;
10670Sstevel@tonic-gate 			goto out;
10680Sstevel@tonic-gate 		}
10690Sstevel@tonic-gate 
10700Sstevel@tonic-gate 		/*
10718821SMichen.Chang@Sun.COM 		 * if change not allowed due to configuration, indicate so
10728821SMichen.Chang@Sun.COM 		 * to the caller
10738821SMichen.Chang@Sun.COM 		 */
10748821SMichen.Chang@Sun.COM 		if (ldaprc == NS_LDAP_CONFIG &&
10758821SMichen.Chang@Sun.COM 		    errorp->status == NS_CONFIG_NOTALLOW) {
10768821SMichen.Chang@Sun.COM 			result = NS_LDAP_CONFIG;
10778821SMichen.Chang@Sun.COM 			*pwd_status = NS_PASSWD_CHANGE_NOT_ALLOWED;
10788821SMichen.Chang@Sun.COM 			goto out;
10798821SMichen.Chang@Sun.COM 		}
10808821SMichen.Chang@Sun.COM 
10818821SMichen.Chang@Sun.COM 		/*
10820Sstevel@tonic-gate 		 * other errors might need to be added to this list, for
10830Sstevel@tonic-gate 		 * the current supported mechanisms this is sufficient
10840Sstevel@tonic-gate 		 */
10850Sstevel@tonic-gate 		if ((ldaprc == NS_LDAP_INTERNAL) &&
10868821SMichen.Chang@Sun.COM 		    (errorp->pwd_mgmt.status == NS_PASSWD_GOOD) &&
10878821SMichen.Chang@Sun.COM 		    ((errorp->status == LDAP_INAPPROPRIATE_AUTH) ||
10888821SMichen.Chang@Sun.COM 		    (errorp->status == LDAP_INVALID_CREDENTIALS))) {
10890Sstevel@tonic-gate 			result = ldaprc;
10900Sstevel@tonic-gate 			goto out;
10910Sstevel@tonic-gate 		}
10920Sstevel@tonic-gate 
10930Sstevel@tonic-gate 		/*
10940Sstevel@tonic-gate 		 * If there is error related to password policy,
10950Sstevel@tonic-gate 		 * return it to caller
10960Sstevel@tonic-gate 		 */
10970Sstevel@tonic-gate 		if ((ldaprc == NS_LDAP_INTERNAL) &&
10988821SMichen.Chang@Sun.COM 		    errorp->pwd_mgmt.status != NS_PASSWD_GOOD) {
10990Sstevel@tonic-gate 			*pwd_status = errorp->pwd_mgmt.status;
11000Sstevel@tonic-gate 			result = ldaprc;
11010Sstevel@tonic-gate 			goto out;
11020Sstevel@tonic-gate 		} else
11030Sstevel@tonic-gate 			*pwd_status = NS_PASSWD_GOOD;
11040Sstevel@tonic-gate 
11050Sstevel@tonic-gate 		/* we don't really care about the error, just clean it up */
11060Sstevel@tonic-gate 		if (errorp)
11070Sstevel@tonic-gate 			(void) __ns_ldap_freeError(&errorp);
11080Sstevel@tonic-gate 	}
11090Sstevel@tonic-gate 	if (authstried == 0) {
11100Sstevel@tonic-gate 		syslog(LOG_ERR,
11110Sstevel@tonic-gate 		"passwdutil: no legal LDAP authentication method configured");
11120Sstevel@tonic-gate 		result = NS_LDAP_CONFIG;
11130Sstevel@tonic-gate 		goto out;
11140Sstevel@tonic-gate 	}
11158821SMichen.Chang@Sun.COM 	result = NS_LDAP_OP_FAILED; /* map to PWU_DENIED */
11160Sstevel@tonic-gate 
11170Sstevel@tonic-gate out:
11180Sstevel@tonic-gate 	if (credp)
11190Sstevel@tonic-gate 		(void) __ns_ldap_freeCred(&credp);
11200Sstevel@tonic-gate 
11210Sstevel@tonic-gate 	if (authpp)
11220Sstevel@tonic-gate 		(void) __ns_ldap_freeParam((void ***)&authpp);
11230Sstevel@tonic-gate 
11240Sstevel@tonic-gate 	if (errorp)
11250Sstevel@tonic-gate 		(void) __ns_ldap_freeError(&errorp);
11260Sstevel@tonic-gate 
11270Sstevel@tonic-gate 	return (result);
11280Sstevel@tonic-gate }
11290Sstevel@tonic-gate 
11300Sstevel@tonic-gate 
11310Sstevel@tonic-gate /*
1132*11262SRajagopal.Andra@Sun.COM  * ldap_putpwnam(name, oldpw, rep, buf)
11330Sstevel@tonic-gate  *
11340Sstevel@tonic-gate  * update the LDAP server with the attributes contained in 'buf'.
11350Sstevel@tonic-gate  */
11360Sstevel@tonic-gate /*ARGSUSED*/
11370Sstevel@tonic-gate int
ldap_putpwnam(char * name,char * oldpw,pwu_repository_t * rep,void * buf)1138*11262SRajagopal.Andra@Sun.COM ldap_putpwnam(char *name, char *oldpw, pwu_repository_t *rep, void *buf)
11390Sstevel@tonic-gate {
11400Sstevel@tonic-gate 	int res;
11410Sstevel@tonic-gate 	char *dn;	/* dn of user whose attributes we are changing */
11420Sstevel@tonic-gate 	char *binddn;	/* dn of user who is performing the change */
11430Sstevel@tonic-gate 	ns_ldap_error_t *errorp;
11440Sstevel@tonic-gate 	ldapbuf_t *ldapbuf = (ldapbuf_t *)buf;
11458821SMichen.Chang@Sun.COM 	ns_ldap_attr_t **pattrs = ldapbuf->pattrs;
11468821SMichen.Chang@Sun.COM 	ns_ldap_attr_t **sattrs = ldapbuf->sattrs;
11470Sstevel@tonic-gate 	struct passwd *pw;
11480Sstevel@tonic-gate 	int pwd_status;
11490Sstevel@tonic-gate 	uid_t uid;
11500Sstevel@tonic-gate 
11510Sstevel@tonic-gate 	if (strcmp(name, "root") == 0)
11520Sstevel@tonic-gate 		return (PWU_NOT_FOUND);
11530Sstevel@tonic-gate 
11540Sstevel@tonic-gate 	/*
11558821SMichen.Chang@Sun.COM 	 * convert name of user whose attributes we are changing
11568821SMichen.Chang@Sun.COM 	 * to a distinguished name
11578821SMichen.Chang@Sun.COM 	 */
11588821SMichen.Chang@Sun.COM 	res = __ns_ldap_uid2dn(name, &dn, NULL, &errorp);
11598821SMichen.Chang@Sun.COM 	if (res != NS_LDAP_SUCCESS)
11608821SMichen.Chang@Sun.COM 		goto out;
11618821SMichen.Chang@Sun.COM 
11628821SMichen.Chang@Sun.COM 	/* update shadow via ldap_cachemgr if it is enabled */
11638821SMichen.Chang@Sun.COM 	if (ldapbuf->shadow_update_enabled &&
11648821SMichen.Chang@Sun.COM 	    sattrs != NULL && sattrs[0] != NULL) {
11658821SMichen.Chang@Sun.COM 		/*
11668821SMichen.Chang@Sun.COM 		 * flag NS_LDAP_UPDATE_SHADOW indicates the shadow update
11678821SMichen.Chang@Sun.COM 		 * should be done via ldap_cachemgr
11688821SMichen.Chang@Sun.COM 		 */
11698821SMichen.Chang@Sun.COM 		res = ldap_replaceattr(dn, sattrs, NULL, NULL, &pwd_status,
11708821SMichen.Chang@Sun.COM 		    NS_LDAP_UPDATE_SHADOW);
11718821SMichen.Chang@Sun.COM 		goto out;
11728821SMichen.Chang@Sun.COM 	}
11738821SMichen.Chang@Sun.COM 
11748821SMichen.Chang@Sun.COM 	/*
11750Sstevel@tonic-gate 	 * The LDAP server checks whether we are permitted to perform
11760Sstevel@tonic-gate 	 * the requested change. We need to send the name of the user
11770Sstevel@tonic-gate 	 * who is executing this piece of code, together with his
11780Sstevel@tonic-gate 	 * current password to the server.
11790Sstevel@tonic-gate 	 * If this is executed by a normal user changing his/her own
11800Sstevel@tonic-gate 	 * password, this will simply be the OLD password that is to
11810Sstevel@tonic-gate 	 * be changed.
11820Sstevel@tonic-gate 	 * Specific case if the user who is executing this piece
11830Sstevel@tonic-gate 	 * of code is root. We will then issue the LDAP request
11840Sstevel@tonic-gate 	 * with the DN of the user we want to change the passwd of.
11850Sstevel@tonic-gate 	 */
11860Sstevel@tonic-gate 
11870Sstevel@tonic-gate 	/*
11880Sstevel@tonic-gate 	 * create a dn for the user who is executing this code
11890Sstevel@tonic-gate 	 */
11900Sstevel@tonic-gate 	uid = getuid();
11910Sstevel@tonic-gate 	if (uid == 0) {
11920Sstevel@tonic-gate 		if ((pw = getpwnam_from(name, rep, REP_LDAP)) == NULL) {
11930Sstevel@tonic-gate 			res = NS_LDAP_OP_FAILED;
11940Sstevel@tonic-gate 			goto out;
11950Sstevel@tonic-gate 		}
11960Sstevel@tonic-gate 	} else if ((pw = getpwuid_from(uid, rep, REP_LDAP)) == NULL) {
11970Sstevel@tonic-gate 		/*
11980Sstevel@tonic-gate 		 * User executing this code is not known to the LDAP
11990Sstevel@tonic-gate 		 * server. This operation is to be denied
12000Sstevel@tonic-gate 		 */
12010Sstevel@tonic-gate 		res = NS_LDAP_OP_FAILED;
12020Sstevel@tonic-gate 		goto out;
12030Sstevel@tonic-gate 	}
12040Sstevel@tonic-gate 
12050Sstevel@tonic-gate 	res = __ns_ldap_uid2dn(pw->pw_name, &binddn, NULL, &errorp);
12060Sstevel@tonic-gate 	if (res != NS_LDAP_SUCCESS)
12070Sstevel@tonic-gate 		goto out;
12080Sstevel@tonic-gate 
12098821SMichen.Chang@Sun.COM 	if (pattrs && pattrs[0] != NULL) {
12108821SMichen.Chang@Sun.COM 		res = ldap_replaceattr(dn, pattrs, binddn, oldpw,
12118821SMichen.Chang@Sun.COM 		    &pwd_status, 0);
12128821SMichen.Chang@Sun.COM 	} else
12138821SMichen.Chang@Sun.COM 		res = NS_LDAP_OP_FAILED;
12140Sstevel@tonic-gate 
12150Sstevel@tonic-gate out:
12168821SMichen.Chang@Sun.COM 	free_ldapbuf(ldapbuf);
12170Sstevel@tonic-gate 	free(dn);
12180Sstevel@tonic-gate 
12190Sstevel@tonic-gate 	return (ldap_to_pwu_code(res, pwd_status));
12200Sstevel@tonic-gate }
1221