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