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