xref: /illumos-gate/usr/src/lib/passwdutil/ldap_attr.c (revision b6805bf78d2bbbeeaea8909a05623587b42d58b3)
1 /*
2  * CDDL HEADER START
3  *
4  * The contents of this file are subject to the terms of the
5  * Common Development and Distribution License (the "License").
6  * You may not use this file except in compliance with the License.
7  *
8  * You can obtain a copy of the license at usr/src/OPENSOLARIS.LICENSE
9  * or http://www.opensolaris.org/os/licensing.
10  * See the License for the specific language governing permissions
11  * and limitations under the License.
12  *
13  * When distributing Covered Code, include this CDDL HEADER in each
14  * file and include the License file at usr/src/OPENSOLARIS.LICENSE.
15  * If applicable, add the following below this CDDL HEADER, with the
16  * fields enclosed by brackets "[]" replaced with your own identifying
17  * information: Portions Copyright [yyyy] [name of copyright owner]
18  *
19  * CDDL HEADER END
20  */
21 /*
22  * Copyright 2009 Sun Microsystems, Inc.  All rights reserved.
23  * Use is subject to license terms.
24  */
25 
26 #include <stdio.h>
27 #include <errno.h>
28 #include <stdlib.h>
29 #include <string.h>
30 #include <unistd.h>
31 #include <macros.h>
32 #include <priv.h>
33 
34 #include "ns_sldap.h"
35 
36 #include <nss_dbdefs.h>
37 #include <nsswitch.h>
38 
39 #include <pwd.h>
40 #include <shadow.h>
41 #include <syslog.h>
42 
43 #include "passwdutil.h"
44 
45 #include "utils.h"
46 
47 #define	MAX_INT_LEN 11	/* 10+1 %d buflen for words/ints [not longs] */
48 
49 #define	STRDUP_OR_RET(to, from) \
50 	if ((to = strdup(from)) == NULL) \
51 		return (PWU_NOMEM);
52 
53 #define	STRDUP_OR_ERR(to, from, err) \
54 	if (((to) = strdup(from)) == NULL) \
55 		(err) = PWU_NOMEM;
56 
57 #define	NUM_TO_STR(to, from) \
58 	{ \
59 		char nb[MAX_INT_LEN]; \
60 		if (snprintf(nb, MAX_INT_LEN, "%d", (from)) >= MAX_INT_LEN) \
61 			return (PWU_NOMEM); \
62 		STRDUP_OR_RET(to, nb); \
63 	}
64 
65 #define	NEW_ATTR(p, i, attr, val) \
66 	{ \
67 		p[i] = new_attr(attr, (val)); \
68 		if (p[i] == NULL) \
69 			return (PWU_NOMEM); \
70 		i++; \
71 	}
72 
73 int ldap_getattr(char *name, attrlist *item, pwu_repository_t *rep);
74 int ldap_getpwnam(char *name, attrlist *items, pwu_repository_t *rep,
75     void **buf);
76 int ldap_update(attrlist *items, pwu_repository_t *rep, void *buf);
77 int ldap_putpwnam(char *name, char *oldpw, pwu_repository_t *rep, void *buf);
78 int ldap_user_to_authenticate(char *name, pwu_repository_t *rep,
79 	char **auth_user, int *privileged);
80 
81 /*
82  * ldap function pointer table, used by passwdutil_init to initialize
83  * the global Repository-OPerations table "rops"
84  */
85 struct repops ldap_repops = {
86 	NULL,	/* checkhistory */
87 	ldap_getattr,
88 	ldap_getpwnam,
89 	ldap_update,
90 	ldap_putpwnam,
91 	ldap_user_to_authenticate,
92 	NULL,	/* lock */
93 	NULL	/* unlock */
94 };
95 
96 /*
97  * structure used to keep state between get/update/put calls
98  */
99 typedef struct {
100 	char *passwd;			/* encrypted password */
101 	struct passwd *pwd;
102 	ns_ldap_attr_t **pattrs;	/* passwd attrs */
103 	int npattrs;			/* max attrs */
104 	struct spwd *spwd;
105 	ns_ldap_attr_t **sattrs;	/* passwd attrs */
106 	int nsattrs;			/* max attrs */
107 	boolean_t shadow_update_enabled;	/* shadow update configured */
108 } ldapbuf_t;
109 
110 /*
111  * The following define's are taken from
112  *	usr/src/lib/nsswitch/ldap/common/getpwnam.c
113  */
114 
115 /* passwd attributes filters */
116 #define	_PWD_CN			"cn"
117 #define	_PWD_UID		"uid"
118 #define	_PWD_USERPASSWORD	"userpassword"
119 #define	_PWD_UIDNUMBER		"uidnumber"
120 #define	_PWD_GIDNUMBER		"gidnumber"
121 #define	_PWD_GECOS		"gecos"
122 #define	_PWD_DESCRIPTION	"description"
123 #define	_PWD_HOMEDIRECTORY	"homedirectory"
124 #define	_PWD_LOGINSHELL		"loginshell"
125 
126 #define	_PWD_MAX_ATTR		10	/* 9+NULL */
127 
128 /* shadow attributes filters */
129 #define	_S_LASTCHANGE		"shadowlastchange"
130 #define	_S_MIN			"shadowmin"
131 #define	_S_MAX			"shadowmax"
132 #define	_S_WARNING		"shadowwarning"
133 #define	_S_INACTIVE		"shadowinactive"
134 #define	_S_EXPIRE		"shadowexpire"
135 #define	_S_FLAG			"shadowflag"
136 
137 #define	_S_MAX_ATTR		8	/* 7+NULL */
138 
139 /*
140  * Frees up an ldapbuf_t
141  */
142 
143 static void
144 free_ldapbuf(ldapbuf_t *p)
145 {
146 	int i;
147 
148 	if (p == NULL)
149 		return;
150 	if (p->passwd) {
151 		(void) memset(p->passwd, 0, strlen(p->passwd));
152 		free(p->passwd);
153 	}
154 	if (p->pwd)
155 		free_pwd(p->pwd);
156 	if (p->spwd)
157 		free_spwd(p->spwd);
158 	if (p->pattrs) {
159 		for (i = 0; i < p->npattrs; i++) {
160 			if (p->pattrs[i] != NULL) {
161 				free(p->pattrs[i]->attrvalue[0]);
162 				free(p->pattrs[i]);
163 			}
164 		}
165 		free(p->pattrs);
166 	}
167 	if (p->sattrs) {
168 		for (i = 0; i < p->nsattrs; i++) {
169 			if (p->sattrs[i] != NULL) {
170 				free(p->sattrs[i]->attrvalue[0]);
171 				free(p->sattrs[i]);
172 			}
173 		}
174 		free(p->sattrs);
175 	}
176 }
177 
178 /*
179  * int ldap_user_to_authenticate(user, rep, auth_user, privileged)
180  *
181  * If the Shadow Update functionality is enabled, then we check to
182  * see if the caller has 0 as the euid or has all zone privs. If so,
183  * the caller would be able to modify shadow(4) data stored on the
184  * LDAP server. Otherwise, when LDAP Shadow Update is not enabled,
185  * we can't determine whether the user is "privileged" in the LDAP
186  * sense. The operation should be attempted and will succeed if the
187  * user had privileges. For our purposes, we say that the user is
188  * privileged if he/she is attempting to change another user's
189  * password attributes.
190  */
191 int
192 ldap_user_to_authenticate(char *user, pwu_repository_t *rep,
193 	char **auth_user, int *privileged)
194 {
195 	struct passwd *pw;
196 	uid_t uid;
197 	uid_t priviledged_uid;
198 	int res = PWU_SUCCESS;
199 
200 	if (strcmp(user, "root") == 0)
201 		return (PWU_NOT_FOUND);
202 
203 	if ((pw = getpwnam_from(user, rep, REP_LDAP)) == NULL)
204 		return (PWU_NOT_FOUND);
205 
206 	uid = getuid();
207 
208 	/*
209 	 * need equivalent of write access to /etc/shadow
210 	 * the privilege escalation model is euid == 0 || all zone privs
211 	 */
212 	if (__ns_ldap_is_shadow_update_enabled()) {
213 		boolean_t priv;
214 
215 		priv = (geteuid() == 0);
216 		if (!priv) {
217 			priv_set_t *ps = priv_allocset();	/* caller */
218 			priv_set_t *zs;				/* zone */
219 
220 			(void) getppriv(PRIV_EFFECTIVE, ps);
221 			zs = priv_str_to_set("zone", ",", NULL);
222 			priv = priv_isequalset(ps, zs);
223 			priv_freeset(ps);
224 			priv_freeset(zs);
225 		}
226 		/*
227 		 * priv can change anyone's password,
228 		 * only root isn't prompted.
229 		 */
230 		*privileged = 0;	/* for proper prompting */
231 		if (priv) {
232 			if (uid == 0) {
233 				*privileged = 1;
234 				*auth_user = NULL;
235 				return (res);
236 			} else if (uid == pw->pw_uid) {
237 				STRDUP_OR_ERR(*auth_user, user, res);
238 				return (res);
239 			}
240 		}
241 
242 		return (PWU_DENIED);
243 	}
244 
245 	if (uid == pw->pw_uid) {
246 		/* changing our own, not privileged */
247 		*privileged = 0;
248 		STRDUP_OR_RET(*auth_user, user);
249 	} else {
250 		char pwd_buf[1024];
251 		struct passwd pwr;
252 
253 		*privileged = 1;
254 		/*
255 		 * specific case for root
256 		 * we want 'user' to be authenticated.
257 		 */
258 		if (uid == 0)  {
259 			priviledged_uid = pw->pw_uid;
260 		} else {
261 			priviledged_uid = uid;
262 		}
263 		if (getpwuid_r(priviledged_uid, &pwr, pwd_buf,
264 		    sizeof (pwd_buf)) != NULL) {
265 			STRDUP_OR_ERR(*auth_user, pwr.pw_name, res);
266 		} else {
267 			/* hmm. can't find name of current user...??? */
268 
269 			if ((*auth_user = malloc(MAX_INT_LEN)) == NULL) {
270 				res = PWU_NOMEM;
271 			} else {
272 				(void) snprintf(*auth_user, MAX_INT_LEN, "%d",
273 				    (int)uid);
274 			}
275 		}
276 	}
277 
278 	return (res);
279 }
280 
281 /*
282  * int ldap_getattr(name, item, rep)
283  *
284  * retrieve attributes specified in "item" for user "name".
285  */
286 /*ARGSUSED*/
287 int
288 ldap_getattr(char *name, attrlist *items, pwu_repository_t *rep)
289 {
290 	attrlist *w;
291 	int res;
292 	ldapbuf_t *ldapbuf;
293 	struct passwd *pw = NULL;
294 	struct spwd *spw = NULL;
295 
296 	res = ldap_getpwnam(name, items, rep, (void **)&ldapbuf);
297 	if (res != PWU_SUCCESS)
298 		return (res);
299 
300 	pw = ldapbuf->pwd;
301 	spw = ldapbuf->spwd;
302 
303 	for (w = items; res == PWU_SUCCESS && w != NULL; w = w->next) {
304 		switch (w->type) {
305 		case ATTR_NAME:
306 			STRDUP_OR_ERR(w->data.val_s, pw->pw_name, res);
307 			break;
308 		case ATTR_COMMENT:
309 			STRDUP_OR_ERR(w->data.val_s, pw->pw_comment, res);
310 			break;
311 		case ATTR_GECOS:
312 			STRDUP_OR_ERR(w->data.val_s, pw->pw_gecos, res);
313 			break;
314 		case ATTR_HOMEDIR:
315 			STRDUP_OR_ERR(w->data.val_s, pw->pw_dir, res);
316 			break;
317 		case ATTR_SHELL:
318 			STRDUP_OR_ERR(w->data.val_s, pw->pw_shell, res);
319 			break;
320 		case ATTR_PASSWD:
321 		case ATTR_PASSWD_SERVER_POLICY:
322 			STRDUP_OR_ERR(w->data.val_s, spw->sp_pwdp, res);
323 			break;
324 		case ATTR_AGE:
325 			STRDUP_OR_ERR(w->data.val_s, pw->pw_age, res);
326 			break;
327 		case ATTR_REP_NAME:
328 			STRDUP_OR_ERR(w->data.val_s, "ldap", res);
329 			break;
330 
331 		/* integer values */
332 		case ATTR_UID:
333 			w->data.val_i = pw->pw_uid;
334 			break;
335 		case ATTR_GID:
336 			w->data.val_i = pw->pw_gid;
337 			break;
338 		case ATTR_LSTCHG:
339 			if (ldapbuf->shadow_update_enabled)
340 				w->data.val_i = spw->sp_lstchg;
341 			else
342 				w->data.val_i = -1;
343 			break;
344 		case ATTR_MIN:
345 			if (ldapbuf->shadow_update_enabled)
346 				w->data.val_i = spw->sp_min;
347 			else
348 				w->data.val_i = -1;
349 			break;
350 		case ATTR_MAX:
351 			if (ldapbuf->shadow_update_enabled)
352 				w->data.val_i = spw->sp_max;
353 			else
354 				w->data.val_i = -1;
355 			break;
356 		case ATTR_WARN:
357 			if (ldapbuf->shadow_update_enabled)
358 				w->data.val_i = spw->sp_warn;
359 			else
360 				w->data.val_i = -1;
361 			break;
362 		case ATTR_INACT:
363 			if (ldapbuf->shadow_update_enabled)
364 				w->data.val_i = spw->sp_inact;
365 			else
366 				w->data.val_i = -1;
367 			break;
368 		case ATTR_EXPIRE:
369 			if (ldapbuf->shadow_update_enabled)
370 				w->data.val_i = spw->sp_expire;
371 			else
372 				w->data.val_i = -1;
373 			break;
374 		case ATTR_FLAG:
375 			if (ldapbuf->shadow_update_enabled)
376 				w->data.val_i = spw->sp_flag;
377 			break;
378 		case ATTR_FAILED_LOGINS:
379 			w->data.val_i = spw->sp_flag & FAILCOUNT_MASK;
380 			break;
381 		default:
382 			break;
383 		}
384 	}
385 
386 out:
387 	free_ldapbuf(ldapbuf);
388 	free(ldapbuf);
389 	return (res);
390 }
391 
392 /*
393  * int ldap_getpwnam(name, items, rep, buf)
394  *
395  * There is no need to get the old values from the ldap
396  * server, as the update will update each item individually.
397  * Therefore, we only allocate a buffer that will be used by
398  * _update and _putpwnam to hold the attributes to update.
399  *
400  * Only when we're about to update a password, we need to retrieve
401  * the old password since it contains salt-information.
402  */
403 /*ARGSUSED*/
404 int
405 ldap_getpwnam(char *name, attrlist *items, pwu_repository_t *rep,
406     void **buf)
407 {
408 	ldapbuf_t *ldapbuf;
409 	int res = PWU_NOMEM;
410 
411 	/*
412 	 * [sp]attrs is treated as NULL terminated
413 	 */
414 
415 	ldapbuf = calloc(1, sizeof (ldapbuf_t));
416 	if (ldapbuf == NULL)
417 		return (PWU_NOMEM);
418 
419 	ldapbuf->pattrs = calloc(_PWD_MAX_ATTR, sizeof (ns_ldap_attr_t *));
420 	if (ldapbuf->pattrs == NULL)
421 		goto out;
422 	ldapbuf->npattrs = _PWD_MAX_ATTR;
423 
424 	ldapbuf->sattrs = calloc(_S_MAX_ATTR, sizeof (ns_ldap_attr_t *));
425 	if (ldapbuf->sattrs == NULL)
426 		goto out;
427 	ldapbuf->nsattrs = _S_MAX_ATTR;
428 
429 	res = dup_pw(&ldapbuf->pwd, getpwnam_from(name, rep, REP_LDAP));
430 	if (res != PWU_SUCCESS)
431 		goto out;
432 
433 	res = dup_spw(&ldapbuf->spwd, getspnam_from(name, rep, REP_LDAP));
434 	if (res != PWU_SUCCESS)
435 		goto out;
436 	else {
437 		char *spw = ldapbuf->spwd->sp_pwdp;
438 		if (spw != NULL && *spw != '\0') {
439 			ldapbuf->passwd = strdup(spw);
440 			if (ldapbuf->passwd == NULL)
441 				goto out;
442 		} else
443 			ldapbuf->passwd = NULL;
444 	}
445 
446 	/* remember if shadow update is enabled */
447 	ldapbuf->shadow_update_enabled = __ns_ldap_is_shadow_update_enabled();
448 
449 	*buf = (void *)ldapbuf;
450 	return (PWU_SUCCESS);
451 
452 out:
453 	free_ldapbuf(ldapbuf);
454 	free(ldapbuf);
455 	return (res);
456 }
457 
458 /*
459  * new_attr(name, value)
460  *
461  * create a new LDAP attribute to be sent to the server
462  */
463 ns_ldap_attr_t *
464 new_attr(char *name, char *value)
465 {
466 	ns_ldap_attr_t *tmp;
467 
468 	tmp = malloc(sizeof (*tmp));
469 	if (tmp != NULL) {
470 		tmp->attrname = name;
471 		tmp->attrvalue = (char **)calloc(2, sizeof (char *));
472 		if (tmp->attrvalue == NULL) {
473 			free(tmp);
474 			return (NULL);
475 		}
476 		tmp->attrvalue[0] = value;
477 		tmp->value_count = 1;
478 	}
479 
480 	return (tmp);
481 }
482 
483 /*
484  * max_present(list)
485  *
486  * returns '1' if a ATTR_MAX with value != -1 is present. (in other words:
487  * if password aging is to be turned on).
488  */
489 static int
490 max_present(attrlist *list)
491 {
492 	while (list != NULL)
493 		if (list->type == ATTR_MAX && list->data.val_i != -1)
494 			return (1);
495 		else
496 			list = list->next;
497 	return (0);
498 }
499 
500 /*
501  * attr_addmod(attrs, idx, item, val)
502  *
503  * Adds or updates attribute 'item' in ldap_attrs list to value
504  * update idx if item is added
505  * return:  -1 - PWU_NOMEM/error, 0 - success
506  */
507 static int
508 attr_addmod(ns_ldap_attr_t **attrs, int *idx, char *item, int value)
509 {
510 	char numbuf[MAX_INT_LEN], *strp;
511 	int i;
512 
513 	/* stringize the value or abort */
514 	if (snprintf(numbuf, MAX_INT_LEN, "%d", value) >= MAX_INT_LEN)
515 		return (-1);
516 
517 	/* check for existence and modify existing */
518 	for (i = 0; i < *idx; i++) {
519 		if (attrs[i] != NULL &&
520 		    strcmp(item, attrs[i]->attrname) == 0) {
521 			strp = strdup(numbuf);
522 			if (strp == NULL)
523 				return (-1);
524 			free(attrs[i]->attrvalue[0]);
525 			attrs[i]->attrvalue[0] = strp;
526 			return (0);
527 		}
528 	}
529 	/* else add */
530 	strp = strdup(numbuf);
531 	if (strp == NULL)
532 		return (-1);
533 	attrs[*idx] = new_attr(item, strp);
534 	if (attrs[*idx] == NULL)
535 		return (-1);
536 	(*idx)++;
537 	return (0);
538 }
539 
540 /*
541  * ldap_update(items, rep, buf)
542  *
543  * create LDAP attributes in 'buf' for each attribute in 'items'.
544  */
545 /*ARGSUSED*/
546 int
547 ldap_update(attrlist *items, pwu_repository_t *rep, void *buf)
548 {
549 	attrlist *p;
550 	ldapbuf_t *ldapbuf = (ldapbuf_t *)buf;
551 	struct spwd *spw;
552 	ns_ldap_attr_t **pattrs = ldapbuf->pattrs;
553 	int pidx = 0;
554 	ns_ldap_attr_t **sattrs = ldapbuf->sattrs;
555 	int sidx = 0;
556 	char *pwd, *val;
557 	char *salt;
558 	size_t cryptlen;
559 	int len;
560 	int count;
561 	int rc = PWU_SUCCESS;
562 	int aging_needed = 0;
563 	int aging_set = 0;
564 	int disable_aging;
565 
566 	spw = ldapbuf->spwd;
567 
568 	/*
569 	 * if sp_max==0 and shadow update is enabled:
570 	 * disable passwd aging after updating the password
571 	 */
572 	disable_aging = (spw != NULL && spw->sp_max == 0 &&
573 	    ldapbuf->shadow_update_enabled);
574 
575 	for (p = items; p != NULL; p = p->next) {
576 		switch (p->type) {
577 		case ATTR_PASSWD:
578 			/*
579 			 * There is a special case for ldap: if the
580 			 * password is to be deleted (-d to passwd),
581 			 * p->data.val_s will be NULL.
582 			 */
583 			if (p->data.val_s == NULL) {
584 				if (!ldapbuf->shadow_update_enabled)
585 					return (PWU_CHANGE_NOT_ALLOWED);
586 				cryptlen =
587 				    sizeof ("{crypt}" NS_LDAP_NO_UNIX_PASSWORD);
588 				val = malloc(cryptlen);
589 				if (val == NULL)
590 					return (PWU_NOMEM);
591 				(void) snprintf(val, cryptlen,
592 				"{crypt}" NS_LDAP_NO_UNIX_PASSWORD);
593 			} else { /* not deleting password */
594 				salt = crypt_gensalt(ldapbuf->passwd,
595 				    ldapbuf->pwd);
596 
597 				if (salt == NULL) {
598 					if (errno == ENOMEM)
599 						return (PWU_NOMEM);
600 
601 					/* algorithm problem? */
602 					syslog(LOG_AUTH | LOG_ALERT,
603 					    "passwdutil: crypt_gensalt "
604 					    "%m");
605 					return (PWU_UPDATE_FAILED);
606 				}
607 
608 				pwd = crypt(p->data.val_s, salt);
609 				free(salt);
610 				cryptlen = strlen(pwd) + sizeof ("{crypt}");
611 				val = malloc(cryptlen);
612 				if (val == NULL)
613 					return (PWU_NOMEM);
614 				(void) snprintf(val, cryptlen,
615 				    "{crypt}%s", pwd);
616 			}
617 
618 			/*
619 			 * If not managing passwordAccount,
620 			 * insert the new password in the
621 			 * passwd attr array and break.
622 			 */
623 			if (!ldapbuf->shadow_update_enabled) {
624 				NEW_ATTR(pattrs, pidx,
625 				    _PWD_USERPASSWORD, val);
626 				break;
627 			}
628 
629 			/*
630 			 * Managing passwordAccount, insert the
631 			 * new password, along with lastChange and
632 			 * shadowFlag, in the shadow attr array.
633 			 */
634 			NEW_ATTR(sattrs, sidx, _PWD_USERPASSWORD, val);
635 
636 			if (attr_addmod(sattrs, &sidx, _S_LASTCHANGE,
637 			    DAY_NOW_32) < 0)
638 				return (PWU_NOMEM);
639 			spw->sp_lstchg = DAY_NOW_32;
640 
641 			if (attr_addmod(sattrs, &sidx, _S_FLAG,
642 			    spw->sp_flag & ~FAILCOUNT_MASK) < 0)
643 				return (PWU_NOMEM);
644 			spw->sp_flag &= ~FAILCOUNT_MASK; /* reset count */
645 			aging_needed = 1;
646 			break;
647 		case ATTR_PASSWD_SERVER_POLICY:
648 			/*
649 			 * For server policy, don't crypt the password,
650 			 * send the password as is to the server and
651 			 * let the LDAP server do its own password
652 			 * encryption
653 			 */
654 			STRDUP_OR_RET(val, p->data.val_s);
655 
656 			NEW_ATTR(pattrs, pidx, _PWD_USERPASSWORD, val);
657 			break;
658 		case ATTR_COMMENT:
659 			/* XX correct? */
660 			NEW_ATTR(pattrs, pidx, _PWD_DESCRIPTION, p->data.val_s);
661 			break;
662 		case ATTR_GECOS:
663 			if (!ldapbuf->shadow_update_enabled) {
664 				NEW_ATTR(pattrs, pidx, _PWD_GECOS,
665 				    p->data.val_s);
666 			} else {
667 				NEW_ATTR(sattrs, sidx, _PWD_GECOS,
668 				    p->data.val_s);
669 			}
670 			break;
671 		case ATTR_HOMEDIR:
672 			if (!ldapbuf->shadow_update_enabled) {
673 				NEW_ATTR(pattrs, pidx, _PWD_HOMEDIRECTORY,
674 				    p->data.val_s);
675 			} else {
676 				NEW_ATTR(sattrs, sidx, _PWD_HOMEDIRECTORY,
677 				    p->data.val_s);
678 			}
679 			break;
680 		case ATTR_SHELL:
681 			if (!ldapbuf->shadow_update_enabled) {
682 				NEW_ATTR(pattrs, pidx, _PWD_LOGINSHELL,
683 				    p->data.val_s);
684 			} else {
685 				NEW_ATTR(sattrs, sidx, _PWD_LOGINSHELL,
686 				    p->data.val_s);
687 			}
688 			break;
689 		/* We don't update NAME, UID, GID */
690 		case ATTR_NAME:
691 		case ATTR_UID:
692 		case ATTR_GID:
693 		/* Unsupported item */
694 		case ATTR_AGE:
695 			break;
696 		case ATTR_LOCK_ACCOUNT:
697 			if (!ldapbuf->shadow_update_enabled)
698 				break;	/* not managing passwordAccount */
699 			if (spw->sp_pwdp == NULL) {
700 				spw->sp_pwdp = LOCKSTRING;
701 			} else if ((strncmp(spw->sp_pwdp, LOCKSTRING,
702 			    sizeof (LOCKSTRING)-1) != 0) &&
703 			    (strcmp(spw->sp_pwdp, NOLOGINSTRING) != 0)) {
704 				len = sizeof (LOCKSTRING)-1 +
705 				    strlen(spw->sp_pwdp) + 1 +
706 				    sizeof ("{crypt}");
707 				pwd = malloc(len);
708 				if (pwd == NULL) {
709 					return (PWU_NOMEM);
710 				}
711 				(void) strlcpy(pwd, "{crypt}", len);
712 				(void) strlcat(pwd, LOCKSTRING, len);
713 				(void) strlcat(pwd, spw->sp_pwdp, len);
714 				free(spw->sp_pwdp);
715 				spw->sp_pwdp = pwd;
716 				NEW_ATTR(sattrs, sidx, _PWD_USERPASSWORD,
717 				    spw->sp_pwdp);
718 			}
719 			if (attr_addmod(sattrs, &sidx, _S_LASTCHANGE,
720 			    DAY_NOW_32) < 0)
721 				return (PWU_NOMEM);
722 			spw->sp_lstchg = DAY_NOW_32;
723 			break;
724 
725 		case ATTR_UNLOCK_ACCOUNT:
726 			if (!ldapbuf->shadow_update_enabled)
727 				break;	/* not managing passwordAccount */
728 			if (spw->sp_pwdp &&
729 			    strncmp(spw->sp_pwdp, LOCKSTRING,
730 			    sizeof (LOCKSTRING)-1) == 0) {
731 				len = (sizeof ("{crypt}") -
732 				    sizeof (LOCKSTRING)) +
733 				    strlen(spw->sp_pwdp) + 1;
734 				pwd = malloc(len);
735 				if (pwd == NULL) {
736 					return (PWU_NOMEM);
737 				}
738 				(void) strlcpy(pwd, "{crypt}", len);
739 				(void) strlcat(pwd, spw->sp_pwdp +
740 				    sizeof (LOCKSTRING)-1, len);
741 				free(spw->sp_pwdp);
742 				spw->sp_pwdp = pwd;
743 
744 				NEW_ATTR(sattrs, sidx, _PWD_USERPASSWORD,
745 				    spw->sp_pwdp);
746 				if (attr_addmod(sattrs, &sidx, _S_LASTCHANGE,
747 				    DAY_NOW_32) < 0)
748 					return (PWU_NOMEM);
749 				spw->sp_lstchg = DAY_NOW_32;
750 			}
751 			break;
752 
753 		case ATTR_NOLOGIN_ACCOUNT:
754 			if (!ldapbuf->shadow_update_enabled)
755 				break;	/* not managing passwordAccount */
756 			free(spw->sp_pwdp);
757 			STRDUP_OR_RET(spw->sp_pwdp, "{crypt}" NOLOGINSTRING);
758 			NEW_ATTR(sattrs, sidx, _PWD_USERPASSWORD, spw->sp_pwdp);
759 			if (attr_addmod(sattrs, &sidx, _S_LASTCHANGE,
760 			    DAY_NOW_32) < 0)
761 				return (PWU_NOMEM);
762 			spw->sp_lstchg = DAY_NOW_32;
763 			break;
764 
765 		case ATTR_EXPIRE_PASSWORD:
766 			if (!ldapbuf->shadow_update_enabled)
767 				break;	/* not managing passwordAccount */
768 			NUM_TO_STR(val, 0);
769 			NEW_ATTR(sattrs, sidx, _S_LASTCHANGE, val);
770 			break;
771 
772 		case ATTR_LSTCHG:
773 			if (!ldapbuf->shadow_update_enabled)
774 				break;	/* not managing passwordAccount */
775 			NUM_TO_STR(val, p->data.val_i);
776 			NEW_ATTR(sattrs, sidx, _S_LASTCHANGE, val);
777 			break;
778 
779 		case ATTR_MIN:
780 			if (!ldapbuf->shadow_update_enabled)
781 				break;	/* not managing passwordAccount */
782 			if (spw->sp_max == -1 && p->data.val_i != -1 &&
783 			    max_present(p->next) == 0)
784 				return (PWU_AGING_DISABLED);
785 			NUM_TO_STR(val, p->data.val_i);
786 			NEW_ATTR(sattrs, sidx, _S_MIN, val);
787 			aging_set = 1;
788 			break;
789 
790 		case ATTR_MAX:
791 			if (!ldapbuf->shadow_update_enabled)
792 				break;	/* not managing passwordAccount */
793 			if (p->data.val_i == -1) {
794 				/* Turn off aging. Reset min and warn too */
795 				spw->sp_max = spw->sp_min = spw->sp_warn = -1;
796 				NUM_TO_STR(val, -1);
797 				NEW_ATTR(sattrs, sidx, _S_MIN, val);
798 				NUM_TO_STR(val, -1);
799 				NEW_ATTR(sattrs, sidx, _S_WARNING, val);
800 			} else {
801 				/* Turn account aging on */
802 				if (spw->sp_min == -1) {
803 					/*
804 					 * minage was not set with command-
805 					 * line option: set to zero
806 					 */
807 					spw->sp_min = 0;
808 					NUM_TO_STR(val, 0);
809 					NEW_ATTR(sattrs, sidx, _S_MIN,
810 					    val);
811 				}
812 				/*
813 				 * If aging was turned off, we update lstchg.
814 				 * We take care not to update lstchg if the
815 				 * user has no password, otherwise the user
816 				 * might not be required to provide a password
817 				 * the next time [s]he logs in.
818 				 *
819 				 * Also, if lstchg != -1 (i.e., not set)
820 				 * we keep the old value.
821 				 */
822 				if (spw->sp_max == -1 &&
823 				    spw->sp_pwdp != NULL && *spw->sp_pwdp &&
824 				    spw->sp_lstchg == -1) {
825 					if (attr_addmod(sattrs, &sidx,
826 					    _S_LASTCHANGE,
827 					    DAY_NOW_32) < 0)
828 						return (PWU_NOMEM);
829 					spw->sp_lstchg = DAY_NOW_32;
830 				}
831 			}
832 			NUM_TO_STR(val, p->data.val_i);
833 			NEW_ATTR(sattrs, sidx, _S_MAX, val);
834 			aging_set = 1;
835 			break;
836 
837 		case ATTR_WARN:
838 			if (!ldapbuf->shadow_update_enabled)
839 				break;	/* not managing passwordAccount */
840 			if (spw->sp_max == -1 &&
841 			    p->data.val_i != -1 && max_present(p->next) == 0)
842 				return (PWU_AGING_DISABLED);
843 			NUM_TO_STR(val, p->data.val_i);
844 			NEW_ATTR(sattrs, sidx, _S_WARNING, val);
845 			break;
846 
847 		case ATTR_INACT:
848 			if (!ldapbuf->shadow_update_enabled)
849 				break;	/* not managing passwordAccount */
850 			NUM_TO_STR(val, p->data.val_i);
851 			NEW_ATTR(sattrs, sidx, _S_INACTIVE, val);
852 			break;
853 
854 		case ATTR_EXPIRE:
855 			if (!ldapbuf->shadow_update_enabled)
856 				break;	/* not managing passwordAccount */
857 			NUM_TO_STR(val, p->data.val_i);
858 			NEW_ATTR(sattrs, sidx, _S_EXPIRE, val);
859 			break;
860 
861 		case ATTR_FLAG:
862 			if (!ldapbuf->shadow_update_enabled)
863 				break;	/* not managing passwordAccount */
864 			NUM_TO_STR(val, p->data.val_i);
865 			NEW_ATTR(sattrs, sidx, _S_FLAG, val);
866 			break;
867 		case ATTR_INCR_FAILED_LOGINS:
868 			if (!ldapbuf->shadow_update_enabled) {
869 				rc = PWU_CHANGE_NOT_ALLOWED;
870 				break;	/* not managing passwordAccount */
871 			}
872 			count = (spw->sp_flag & FAILCOUNT_MASK) + 1;
873 			spw->sp_flag &= ~FAILCOUNT_MASK;
874 			spw->sp_flag |= min(FAILCOUNT_MASK, count);
875 			p->data.val_i = count;
876 			NUM_TO_STR(val, spw->sp_flag);
877 			NEW_ATTR(sattrs, sidx, _S_FLAG, val);
878 			break;
879 		case ATTR_RST_FAILED_LOGINS:
880 			if (!ldapbuf->shadow_update_enabled) {
881 				rc = PWU_CHANGE_NOT_ALLOWED;
882 				break;	/* not managing passwordAccount */
883 			}
884 			p->data.val_i = spw->sp_flag & FAILCOUNT_MASK;
885 			spw->sp_flag &= ~FAILCOUNT_MASK;
886 			NUM_TO_STR(val, spw->sp_flag);
887 			NEW_ATTR(sattrs, sidx, _S_FLAG, val);
888 			break;
889 		default:
890 			break;
891 		}
892 	}
893 
894 	/*
895 	 * If the ldap client is configured with shadow update enabled,
896 	 * then what should the new aging values look like?
897 	 *
898 	 * There are a number of different conditions
899 	 *
900 	 *  a) aging is already configured: don't touch it
901 	 *
902 	 *  b) disable_aging is set: disable aging
903 	 *
904 	 *  c) aging is not configured: turn on default aging;
905 	 *
906 	 *  b) and c) of course only if aging_needed and !aging_set.
907 	 *  (i.e., password changed, and aging values not changed)
908 	 */
909 
910 	if (ldapbuf->shadow_update_enabled && spw != NULL && spw->sp_max <= 0) {
911 		/* a) aging not yet configured */
912 		if (aging_needed && !aging_set) {
913 			if (disable_aging) {
914 				/* b) turn off aging */
915 				spw->sp_min = spw->sp_max = spw->sp_warn = -1;
916 				if (attr_addmod(sattrs, &sidx, _S_MIN, -1) < 0)
917 					return (PWU_NOMEM);
918 				if (attr_addmod(sattrs, &sidx, _S_MAX, -1) < 0)
919 					return (PWU_NOMEM);
920 				if (attr_addmod(sattrs, &sidx, _S_WARNING,
921 				    -1) < 0)
922 					return (PWU_NOMEM);
923 			} else {
924 				/* c) */
925 				turn_on_default_aging(spw);
926 
927 				if (attr_addmod(sattrs, &sidx, _S_MIN,
928 				    spw->sp_min) < 0)
929 					return (PWU_NOMEM);
930 				if (attr_addmod(sattrs, &sidx, _S_MAX,
931 				    spw->sp_max) < 0)
932 					return (PWU_NOMEM);
933 				if (attr_addmod(sattrs, &sidx,
934 				    _S_WARNING, spw->sp_warn) < 0)
935 					return (PWU_NOMEM);
936 			}
937 		}
938 	}
939 
940 	pattrs[pidx] = NULL;
941 	sattrs[sidx] = NULL;
942 
943 	return (rc);
944 }
945 
946 /*
947  * ldap_to_pwu_code(error, pwd_status)
948  *
949  * translation from LDAP return values and PWU return values
950  */
951 int
952 ldap_to_pwu_code(int error, int pwd_status)
953 {
954 	switch (error) {
955 	case NS_LDAP_SUCCESS:	return (PWU_SUCCESS);
956 	case NS_LDAP_OP_FAILED:	return (PWU_DENIED);
957 	case NS_LDAP_NOTFOUND:	return (PWU_NOT_FOUND);
958 	case NS_LDAP_MEMORY:	return (PWU_NOMEM);
959 	case NS_LDAP_CONFIG:	return (PWU_NOT_FOUND);
960 	case NS_LDAP_INTERNAL:
961 		switch (pwd_status) {
962 		case NS_PASSWD_EXPIRED:
963 			return (PWU_DENIED);
964 		case NS_PASSWD_CHANGE_NOT_ALLOWED:
965 			return (PWU_CHANGE_NOT_ALLOWED);
966 		case NS_PASSWD_TOO_SHORT:
967 			return (PWU_PWD_TOO_SHORT);
968 		case NS_PASSWD_INVALID_SYNTAX:
969 			return (PWU_PWD_INVALID);
970 		case NS_PASSWD_IN_HISTORY:
971 			return (PWU_PWD_IN_HISTORY);
972 		case NS_PASSWD_WITHIN_MIN_AGE:
973 			return (PWU_WITHIN_MIN_AGE);
974 		default:
975 			return (PWU_SYSTEM_ERROR);
976 		}
977 	default:		return (PWU_SYSTEM_ERROR);
978 	}
979 }
980 
981 int
982 ldap_replaceattr(const char *dn, ns_ldap_attr_t **attrs, const char *binddn,
983 	const char *pwd, int *pwd_status, int flags)
984 {
985 	int		result = NS_LDAP_OP_FAILED;
986 	int		ldaprc;
987 	int		authstried = 0;
988 	char		**certpath = NULL;
989 	ns_auth_t	**app;
990 	ns_auth_t	**authpp = NULL;
991 	ns_auth_t	*authp = NULL;
992 	ns_cred_t	*credp;
993 	ns_ldap_error_t	*errorp = NULL;
994 
995 	debug("%s: replace_ldapattr()", __FILE__);
996 
997 	if ((credp = (ns_cred_t *)calloc(1, sizeof (ns_cred_t))) == NULL)
998 		return (NS_LDAP_MEMORY); /* map to PWU_NOMEM */
999 
1000 	/* for admin shadow update, dn and pwd will be set later in libsldap */
1001 	if ((flags & NS_LDAP_UPDATE_SHADOW) == 0) {
1002 		/* Fill in the user name and password */
1003 		if (dn == NULL || pwd == NULL)
1004 			goto out;
1005 		credp->cred.unix_cred.userID = strdup(binddn);
1006 		credp->cred.unix_cred.passwd = strdup(pwd);
1007 	}
1008 
1009 	/* get host certificate path, if one is configured */
1010 	ldaprc = __ns_ldap_getParam(NS_LDAP_HOST_CERTPATH_P,
1011 	    (void ***)&certpath, &errorp);
1012 	if (ldaprc != NS_LDAP_SUCCESS)
1013 		goto out;
1014 
1015 	if (certpath && *certpath)
1016 		credp->hostcertpath = *certpath;
1017 
1018 	/* Load the service specific authentication method */
1019 	ldaprc = __ns_ldap_getServiceAuthMethods("passwd-cmd", &authpp,
1020 	    &errorp);
1021 
1022 	if (ldaprc != NS_LDAP_SUCCESS)
1023 		goto out;
1024 
1025 	/*
1026 	 * if authpp is null, there is no serviceAuthenticationMethod
1027 	 * try default authenticationMethod
1028 	 */
1029 	if (authpp == NULL) {
1030 		ldaprc = __ns_ldap_getParam(NS_LDAP_AUTH_P, (void ***)&authpp,
1031 		    &errorp);
1032 		if (ldaprc != NS_LDAP_SUCCESS)
1033 			goto out;
1034 	}
1035 
1036 	/*
1037 	 * if authpp is still null, then can not authenticate, syslog
1038 	 * error message and return error
1039 	 */
1040 	if (authpp == NULL) {
1041 		syslog(LOG_ERR,
1042 		"passwdutil: no legal LDAP authentication method configured");
1043 		result = NS_LDAP_OP_FAILED;
1044 		goto out;
1045 	}
1046 
1047 	/*
1048 	 * Walk the array and try all authentication methods in order except
1049 	 * for "none".
1050 	 */
1051 	for (app = authpp; *app; app++) {
1052 		authp = *app;
1053 		/* what about disabling other mechanisms? "tls:sasl/EXTERNAL" */
1054 		if (authp->type == NS_LDAP_AUTH_NONE)
1055 			continue;
1056 		authstried++;
1057 		credp->auth.type = authp->type;
1058 		credp->auth.tlstype = authp->tlstype;
1059 		credp->auth.saslmech = authp->saslmech;
1060 		credp->auth.saslopt = authp->saslopt;
1061 
1062 		ldaprc = __ns_ldap_repAttr("shadow", dn,
1063 		    (const ns_ldap_attr_t * const *)attrs,
1064 		    credp, flags, &errorp);
1065 		if (ldaprc == NS_LDAP_SUCCESS) {
1066 			result = NS_LDAP_SUCCESS;
1067 			goto out;
1068 		}
1069 
1070 		/*
1071 		 * if change not allowed due to configuration, indicate so
1072 		 * to the caller
1073 		 */
1074 		if (ldaprc == NS_LDAP_CONFIG &&
1075 		    errorp->status == NS_CONFIG_NOTALLOW) {
1076 			result = NS_LDAP_CONFIG;
1077 			*pwd_status = NS_PASSWD_CHANGE_NOT_ALLOWED;
1078 			goto out;
1079 		}
1080 
1081 		/*
1082 		 * other errors might need to be added to this list, for
1083 		 * the current supported mechanisms this is sufficient
1084 		 */
1085 		if ((ldaprc == NS_LDAP_INTERNAL) &&
1086 		    (errorp->pwd_mgmt.status == NS_PASSWD_GOOD) &&
1087 		    ((errorp->status == LDAP_INAPPROPRIATE_AUTH) ||
1088 		    (errorp->status == LDAP_INVALID_CREDENTIALS))) {
1089 			result = ldaprc;
1090 			goto out;
1091 		}
1092 
1093 		/*
1094 		 * If there is error related to password policy,
1095 		 * return it to caller
1096 		 */
1097 		if ((ldaprc == NS_LDAP_INTERNAL) &&
1098 		    errorp->pwd_mgmt.status != NS_PASSWD_GOOD) {
1099 			*pwd_status = errorp->pwd_mgmt.status;
1100 			result = ldaprc;
1101 			goto out;
1102 		} else
1103 			*pwd_status = NS_PASSWD_GOOD;
1104 
1105 		/* we don't really care about the error, just clean it up */
1106 		if (errorp)
1107 			(void) __ns_ldap_freeError(&errorp);
1108 	}
1109 	if (authstried == 0) {
1110 		syslog(LOG_ERR,
1111 		"passwdutil: no legal LDAP authentication method configured");
1112 		result = NS_LDAP_CONFIG;
1113 		goto out;
1114 	}
1115 	result = NS_LDAP_OP_FAILED; /* map to PWU_DENIED */
1116 
1117 out:
1118 	if (credp)
1119 		(void) __ns_ldap_freeCred(&credp);
1120 
1121 	if (authpp)
1122 		(void) __ns_ldap_freeParam((void ***)&authpp);
1123 
1124 	if (errorp)
1125 		(void) __ns_ldap_freeError(&errorp);
1126 
1127 	return (result);
1128 }
1129 
1130 
1131 /*
1132  * ldap_putpwnam(name, oldpw, rep, buf)
1133  *
1134  * update the LDAP server with the attributes contained in 'buf'.
1135  */
1136 /*ARGSUSED*/
1137 int
1138 ldap_putpwnam(char *name, char *oldpw, pwu_repository_t *rep, void *buf)
1139 {
1140 	int res;
1141 	char *dn;	/* dn of user whose attributes we are changing */
1142 	char *binddn;	/* dn of user who is performing the change */
1143 	ns_ldap_error_t *errorp;
1144 	ldapbuf_t *ldapbuf = (ldapbuf_t *)buf;
1145 	ns_ldap_attr_t **pattrs = ldapbuf->pattrs;
1146 	ns_ldap_attr_t **sattrs = ldapbuf->sattrs;
1147 	struct passwd *pw;
1148 	int pwd_status;
1149 	uid_t uid;
1150 
1151 	if (strcmp(name, "root") == 0)
1152 		return (PWU_NOT_FOUND);
1153 
1154 	/*
1155 	 * convert name of user whose attributes we are changing
1156 	 * to a distinguished name
1157 	 */
1158 	res = __ns_ldap_uid2dn(name, &dn, NULL, &errorp);
1159 	if (res != NS_LDAP_SUCCESS)
1160 		goto out;
1161 
1162 	/* update shadow via ldap_cachemgr if it is enabled */
1163 	if (ldapbuf->shadow_update_enabled &&
1164 	    sattrs != NULL && sattrs[0] != NULL) {
1165 		/*
1166 		 * flag NS_LDAP_UPDATE_SHADOW indicates the shadow update
1167 		 * should be done via ldap_cachemgr
1168 		 */
1169 		res = ldap_replaceattr(dn, sattrs, NULL, NULL, &pwd_status,
1170 		    NS_LDAP_UPDATE_SHADOW);
1171 		goto out;
1172 	}
1173 
1174 	/*
1175 	 * The LDAP server checks whether we are permitted to perform
1176 	 * the requested change. We need to send the name of the user
1177 	 * who is executing this piece of code, together with his
1178 	 * current password to the server.
1179 	 * If this is executed by a normal user changing his/her own
1180 	 * password, this will simply be the OLD password that is to
1181 	 * be changed.
1182 	 * Specific case if the user who is executing this piece
1183 	 * of code is root. We will then issue the LDAP request
1184 	 * with the DN of the user we want to change the passwd of.
1185 	 */
1186 
1187 	/*
1188 	 * create a dn for the user who is executing this code
1189 	 */
1190 	uid = getuid();
1191 	if (uid == 0) {
1192 		if ((pw = getpwnam_from(name, rep, REP_LDAP)) == NULL) {
1193 			res = NS_LDAP_OP_FAILED;
1194 			goto out;
1195 		}
1196 	} else if ((pw = getpwuid_from(uid, rep, REP_LDAP)) == NULL) {
1197 		/*
1198 		 * User executing this code is not known to the LDAP
1199 		 * server. This operation is to be denied
1200 		 */
1201 		res = NS_LDAP_OP_FAILED;
1202 		goto out;
1203 	}
1204 
1205 	res = __ns_ldap_uid2dn(pw->pw_name, &binddn, NULL, &errorp);
1206 	if (res != NS_LDAP_SUCCESS)
1207 		goto out;
1208 
1209 	if (pattrs && pattrs[0] != NULL) {
1210 		res = ldap_replaceattr(dn, pattrs, binddn, oldpw,
1211 		    &pwd_status, 0);
1212 	} else
1213 		res = NS_LDAP_OP_FAILED;
1214 
1215 out:
1216 	free_ldapbuf(ldapbuf);
1217 	free(dn);
1218 
1219 	return (ldap_to_pwu_code(res, pwd_status));
1220 }
1221