xref: /illumos-gate/usr/src/lib/passwdutil/ldap_attr.c (revision 33efde4275d24731ef87927237b0ffb0630b6b2d)
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
free_ldapbuf(ldapbuf_t * p)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
ldap_user_to_authenticate(const char * user,pwu_repository_t * rep,char ** auth_user,int * privileged)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
ldap_getattr(const char * name,attrlist * items,pwu_repository_t * rep)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 	free_ldapbuf(ldapbuf);
390 	free(ldapbuf);
391 	return (res);
392 }
393 
394 /*
395  * int ldap_getpwnam(name, items, rep, buf)
396  *
397  * There is no need to get the old values from the ldap
398  * server, as the update will update each item individually.
399  * Therefore, we only allocate a buffer that will be used by
400  * _update and _putpwnam to hold the attributes to update.
401  *
402  * Only when we're about to update a password, we need to retrieve
403  * the old password since it contains salt-information.
404  */
405 /*ARGSUSED*/
406 int
ldap_getpwnam(const char * name,attrlist * items,pwu_repository_t * rep,void ** buf)407 ldap_getpwnam(const char *name, attrlist *items, pwu_repository_t *rep,
408     void **buf)
409 {
410 	ldapbuf_t *ldapbuf;
411 	int res = PWU_NOMEM;
412 
413 	/*
414 	 * [sp]attrs is treated as NULL terminated
415 	 */
416 
417 	ldapbuf = calloc(1, sizeof (ldapbuf_t));
418 	if (ldapbuf == NULL)
419 		return (PWU_NOMEM);
420 
421 	ldapbuf->pattrs = calloc(_PWD_MAX_ATTR, sizeof (ns_ldap_attr_t *));
422 	if (ldapbuf->pattrs == NULL)
423 		goto out;
424 	ldapbuf->npattrs = _PWD_MAX_ATTR;
425 
426 	ldapbuf->sattrs = calloc(_S_MAX_ATTR, sizeof (ns_ldap_attr_t *));
427 	if (ldapbuf->sattrs == NULL)
428 		goto out;
429 	ldapbuf->nsattrs = _S_MAX_ATTR;
430 
431 	res = dup_pw(&ldapbuf->pwd, getpwnam_from(name, rep, REP_LDAP));
432 	if (res != PWU_SUCCESS)
433 		goto out;
434 
435 	res = dup_spw(&ldapbuf->spwd, getspnam_from(name, rep, REP_LDAP));
436 	if (res != PWU_SUCCESS)
437 		goto out;
438 	else {
439 		char *spw = ldapbuf->spwd->sp_pwdp;
440 		if (spw != NULL && *spw != '\0') {
441 			ldapbuf->passwd = strdup(spw);
442 			if (ldapbuf->passwd == NULL)
443 				goto out;
444 		} else
445 			ldapbuf->passwd = NULL;
446 	}
447 
448 	/* remember if shadow update is enabled */
449 	ldapbuf->shadow_update_enabled = __ns_ldap_is_shadow_update_enabled();
450 
451 	*buf = (void *)ldapbuf;
452 	return (PWU_SUCCESS);
453 
454 out:
455 	free_ldapbuf(ldapbuf);
456 	free(ldapbuf);
457 	return (res);
458 }
459 
460 /*
461  * new_attr(name, value)
462  *
463  * create a new LDAP attribute to be sent to the server
464  */
465 ns_ldap_attr_t *
new_attr(char * name,char * value)466 new_attr(char *name, char *value)
467 {
468 	ns_ldap_attr_t *tmp;
469 
470 	tmp = malloc(sizeof (*tmp));
471 	if (tmp != NULL) {
472 		tmp->attrname = name;
473 		tmp->attrvalue = (char **)calloc(2, sizeof (char *));
474 		if (tmp->attrvalue == NULL) {
475 			free(tmp);
476 			return (NULL);
477 		}
478 		tmp->attrvalue[0] = value;
479 		tmp->value_count = 1;
480 	}
481 
482 	return (tmp);
483 }
484 
485 /*
486  * max_present(list)
487  *
488  * returns '1' if a ATTR_MAX with value != -1 is present. (in other words:
489  * if password aging is to be turned on).
490  */
491 static int
max_present(attrlist * list)492 max_present(attrlist *list)
493 {
494 	while (list != NULL)
495 		if (list->type == ATTR_MAX && list->data.val_i != -1)
496 			return (1);
497 		else
498 			list = list->next;
499 	return (0);
500 }
501 
502 /*
503  * attr_addmod(attrs, idx, item, val)
504  *
505  * Adds or updates attribute 'item' in ldap_attrs list to value
506  * update idx if item is added
507  * return:  -1 - PWU_NOMEM/error, 0 - success
508  */
509 static int
attr_addmod(ns_ldap_attr_t ** attrs,int * idx,char * item,int value)510 attr_addmod(ns_ldap_attr_t **attrs, int *idx, char *item, int value)
511 {
512 	char numbuf[MAX_INT_LEN], *strp;
513 	int i;
514 
515 	/* stringize the value or abort */
516 	if (snprintf(numbuf, MAX_INT_LEN, "%d", value) >= MAX_INT_LEN)
517 		return (-1);
518 
519 	/* check for existence and modify existing */
520 	for (i = 0; i < *idx; i++) {
521 		if (attrs[i] != NULL &&
522 		    strcmp(item, attrs[i]->attrname) == 0) {
523 			strp = strdup(numbuf);
524 			if (strp == NULL)
525 				return (-1);
526 			free(attrs[i]->attrvalue[0]);
527 			attrs[i]->attrvalue[0] = strp;
528 			return (0);
529 		}
530 	}
531 	/* else add */
532 	strp = strdup(numbuf);
533 	if (strp == NULL)
534 		return (-1);
535 	attrs[*idx] = new_attr(item, strp);
536 	if (attrs[*idx] == NULL)
537 		return (-1);
538 	(*idx)++;
539 	return (0);
540 }
541 
542 /*
543  * ldap_update(items, rep, buf)
544  *
545  * create LDAP attributes in 'buf' for each attribute in 'items'.
546  */
547 /*ARGSUSED*/
548 int
ldap_update(attrlist * items,pwu_repository_t * rep,void * buf)549 ldap_update(attrlist *items, pwu_repository_t *rep, void *buf)
550 {
551 	attrlist *p;
552 	ldapbuf_t *ldapbuf = (ldapbuf_t *)buf;
553 	struct spwd *spw;
554 	ns_ldap_attr_t **pattrs = ldapbuf->pattrs;
555 	int pidx = 0;
556 	ns_ldap_attr_t **sattrs = ldapbuf->sattrs;
557 	int sidx = 0;
558 	char *pwd, *val;
559 	char *salt;
560 	size_t cryptlen;
561 	int len;
562 	int count;
563 	int rc = PWU_SUCCESS;
564 	int aging_needed = 0;
565 	int aging_set = 0;
566 	int disable_aging;
567 
568 	spw = ldapbuf->spwd;
569 
570 	/*
571 	 * if sp_max==0 and shadow update is enabled:
572 	 * disable passwd aging after updating the password
573 	 */
574 	disable_aging = (spw != NULL && spw->sp_max == 0 &&
575 	    ldapbuf->shadow_update_enabled);
576 
577 	for (p = items; p != NULL; p = p->next) {
578 		switch (p->type) {
579 		case ATTR_PASSWD:
580 			/*
581 			 * There is a special case for ldap: if the
582 			 * password is to be deleted (-d to passwd),
583 			 * p->data.val_s will be NULL.
584 			 */
585 			if (p->data.val_s == NULL) {
586 				if (!ldapbuf->shadow_update_enabled)
587 					return (PWU_CHANGE_NOT_ALLOWED);
588 				cryptlen =
589 				    sizeof ("{crypt}" NS_LDAP_NO_UNIX_PASSWORD);
590 				val = malloc(cryptlen);
591 				if (val == NULL)
592 					return (PWU_NOMEM);
593 				(void) snprintf(val, cryptlen,
594 				"{crypt}" NS_LDAP_NO_UNIX_PASSWORD);
595 			} else { /* not deleting password */
596 				salt = crypt_gensalt(ldapbuf->passwd,
597 				    ldapbuf->pwd);
598 
599 				if (salt == NULL) {
600 					if (errno == ENOMEM)
601 						return (PWU_NOMEM);
602 
603 					/* algorithm problem? */
604 					syslog(LOG_AUTH | LOG_ALERT,
605 					    "passwdutil: crypt_gensalt "
606 					    "%m");
607 					return (PWU_UPDATE_FAILED);
608 				}
609 
610 				pwd = crypt(p->data.val_s, salt);
611 				free(salt);
612 				cryptlen = strlen(pwd) + sizeof ("{crypt}");
613 				val = malloc(cryptlen);
614 				if (val == NULL)
615 					return (PWU_NOMEM);
616 				(void) snprintf(val, cryptlen,
617 				    "{crypt}%s", pwd);
618 			}
619 
620 			/*
621 			 * If not managing passwordAccount,
622 			 * insert the new password in the
623 			 * passwd attr array and break.
624 			 */
625 			if (!ldapbuf->shadow_update_enabled) {
626 				NEW_ATTR(pattrs, pidx,
627 				    _PWD_USERPASSWORD, val);
628 				break;
629 			}
630 
631 			/*
632 			 * Managing passwordAccount, insert the
633 			 * new password, along with lastChange and
634 			 * shadowFlag, in the shadow attr array.
635 			 */
636 			NEW_ATTR(sattrs, sidx, _PWD_USERPASSWORD, val);
637 
638 			if (attr_addmod(sattrs, &sidx, _S_LASTCHANGE,
639 			    DAY_NOW_32) < 0)
640 				return (PWU_NOMEM);
641 			spw->sp_lstchg = DAY_NOW_32;
642 
643 			if (attr_addmod(sattrs, &sidx, _S_FLAG,
644 			    spw->sp_flag & ~FAILCOUNT_MASK) < 0)
645 				return (PWU_NOMEM);
646 			spw->sp_flag &= ~FAILCOUNT_MASK; /* reset count */
647 			aging_needed = 1;
648 			break;
649 		case ATTR_PASSWD_SERVER_POLICY:
650 			/*
651 			 * For server policy, don't crypt the password,
652 			 * send the password as is to the server and
653 			 * let the LDAP server do its own password
654 			 * encryption
655 			 */
656 			STRDUP_OR_RET(val, p->data.val_s);
657 
658 			NEW_ATTR(pattrs, pidx, _PWD_USERPASSWORD, val);
659 			break;
660 		case ATTR_COMMENT:
661 			/* XX correct? */
662 			NEW_ATTR(pattrs, pidx, _PWD_DESCRIPTION, p->data.val_s);
663 			break;
664 		case ATTR_GECOS:
665 			if (!ldapbuf->shadow_update_enabled) {
666 				NEW_ATTR(pattrs, pidx, _PWD_GECOS,
667 				    p->data.val_s);
668 			} else {
669 				NEW_ATTR(sattrs, sidx, _PWD_GECOS,
670 				    p->data.val_s);
671 			}
672 			break;
673 		case ATTR_HOMEDIR:
674 			if (!ldapbuf->shadow_update_enabled) {
675 				NEW_ATTR(pattrs, pidx, _PWD_HOMEDIRECTORY,
676 				    p->data.val_s);
677 			} else {
678 				NEW_ATTR(sattrs, sidx, _PWD_HOMEDIRECTORY,
679 				    p->data.val_s);
680 			}
681 			break;
682 		case ATTR_SHELL:
683 			if (!ldapbuf->shadow_update_enabled) {
684 				NEW_ATTR(pattrs, pidx, _PWD_LOGINSHELL,
685 				    p->data.val_s);
686 			} else {
687 				NEW_ATTR(sattrs, sidx, _PWD_LOGINSHELL,
688 				    p->data.val_s);
689 			}
690 			break;
691 		/* We don't update NAME, UID, GID */
692 		case ATTR_NAME:
693 		case ATTR_UID:
694 		case ATTR_GID:
695 		/* Unsupported item */
696 		case ATTR_AGE:
697 			break;
698 		case ATTR_LOCK_ACCOUNT:
699 			if (!ldapbuf->shadow_update_enabled)
700 				break;	/* not managing passwordAccount */
701 			if (spw->sp_pwdp == NULL) {
702 				spw->sp_pwdp = LOCKSTRING;
703 			} else if ((strncmp(spw->sp_pwdp, LOCKSTRING,
704 			    sizeof (LOCKSTRING)-1) != 0) &&
705 			    (strcmp(spw->sp_pwdp, NOLOGINSTRING) != 0)) {
706 				len = sizeof (LOCKSTRING)-1 +
707 				    strlen(spw->sp_pwdp) + 1 +
708 				    sizeof ("{crypt}");
709 				pwd = malloc(len);
710 				if (pwd == NULL) {
711 					return (PWU_NOMEM);
712 				}
713 				(void) strlcpy(pwd, "{crypt}", len);
714 				(void) strlcat(pwd, LOCKSTRING, len);
715 				(void) strlcat(pwd, spw->sp_pwdp, len);
716 				free(spw->sp_pwdp);
717 				spw->sp_pwdp = pwd;
718 				NEW_ATTR(sattrs, sidx, _PWD_USERPASSWORD,
719 				    spw->sp_pwdp);
720 			}
721 			if (attr_addmod(sattrs, &sidx, _S_LASTCHANGE,
722 			    DAY_NOW_32) < 0)
723 				return (PWU_NOMEM);
724 			spw->sp_lstchg = DAY_NOW_32;
725 			break;
726 
727 		case ATTR_UNLOCK_ACCOUNT:
728 			if (!ldapbuf->shadow_update_enabled)
729 				break;	/* not managing passwordAccount */
730 			if (spw->sp_pwdp &&
731 			    strncmp(spw->sp_pwdp, LOCKSTRING,
732 			    sizeof (LOCKSTRING)-1) == 0) {
733 				len = (sizeof ("{crypt}") -
734 				    sizeof (LOCKSTRING)) +
735 				    strlen(spw->sp_pwdp) + 1;
736 				pwd = malloc(len);
737 				if (pwd == NULL) {
738 					return (PWU_NOMEM);
739 				}
740 				(void) strlcpy(pwd, "{crypt}", len);
741 				(void) strlcat(pwd, spw->sp_pwdp +
742 				    sizeof (LOCKSTRING)-1, len);
743 				free(spw->sp_pwdp);
744 				spw->sp_pwdp = pwd;
745 
746 				NEW_ATTR(sattrs, sidx, _PWD_USERPASSWORD,
747 				    spw->sp_pwdp);
748 				if (attr_addmod(sattrs, &sidx, _S_LASTCHANGE,
749 				    DAY_NOW_32) < 0)
750 					return (PWU_NOMEM);
751 				spw->sp_lstchg = DAY_NOW_32;
752 			}
753 			break;
754 
755 		case ATTR_NOLOGIN_ACCOUNT:
756 			if (!ldapbuf->shadow_update_enabled)
757 				break;	/* not managing passwordAccount */
758 			free(spw->sp_pwdp);
759 			STRDUP_OR_RET(spw->sp_pwdp, "{crypt}" NOLOGINSTRING);
760 			NEW_ATTR(sattrs, sidx, _PWD_USERPASSWORD, spw->sp_pwdp);
761 			if (attr_addmod(sattrs, &sidx, _S_LASTCHANGE,
762 			    DAY_NOW_32) < 0)
763 				return (PWU_NOMEM);
764 			spw->sp_lstchg = DAY_NOW_32;
765 			break;
766 
767 		case ATTR_EXPIRE_PASSWORD:
768 			if (!ldapbuf->shadow_update_enabled)
769 				break;	/* not managing passwordAccount */
770 			NUM_TO_STR(val, 0);
771 			NEW_ATTR(sattrs, sidx, _S_LASTCHANGE, val);
772 			break;
773 
774 		case ATTR_LSTCHG:
775 			if (!ldapbuf->shadow_update_enabled)
776 				break;	/* not managing passwordAccount */
777 			NUM_TO_STR(val, p->data.val_i);
778 			NEW_ATTR(sattrs, sidx, _S_LASTCHANGE, val);
779 			break;
780 
781 		case ATTR_MIN:
782 			if (!ldapbuf->shadow_update_enabled)
783 				break;	/* not managing passwordAccount */
784 			if (spw->sp_max == -1 && p->data.val_i != -1 &&
785 			    max_present(p->next) == 0)
786 				return (PWU_AGING_DISABLED);
787 			NUM_TO_STR(val, p->data.val_i);
788 			NEW_ATTR(sattrs, sidx, _S_MIN, val);
789 			aging_set = 1;
790 			break;
791 
792 		case ATTR_MAX:
793 			if (!ldapbuf->shadow_update_enabled)
794 				break;	/* not managing passwordAccount */
795 			if (p->data.val_i == -1) {
796 				/* Turn off aging. Reset min and warn too */
797 				spw->sp_max = spw->sp_min = spw->sp_warn = -1;
798 				NUM_TO_STR(val, -1);
799 				NEW_ATTR(sattrs, sidx, _S_MIN, val);
800 				NUM_TO_STR(val, -1);
801 				NEW_ATTR(sattrs, sidx, _S_WARNING, val);
802 			} else {
803 				/* Turn account aging on */
804 				if (spw->sp_min == -1) {
805 					/*
806 					 * minage was not set with command-
807 					 * line option: set to zero
808 					 */
809 					spw->sp_min = 0;
810 					NUM_TO_STR(val, 0);
811 					NEW_ATTR(sattrs, sidx, _S_MIN,
812 					    val);
813 				}
814 				/*
815 				 * If aging was turned off, we update lstchg.
816 				 * We take care not to update lstchg if the
817 				 * user has no password, otherwise the user
818 				 * might not be required to provide a password
819 				 * the next time they log in.
820 				 *
821 				 * Also, if lstchg != -1 (i.e., not set)
822 				 * we keep the old value.
823 				 */
824 				if (spw->sp_max == -1 &&
825 				    spw->sp_pwdp != NULL && *spw->sp_pwdp &&
826 				    spw->sp_lstchg == -1) {
827 					if (attr_addmod(sattrs, &sidx,
828 					    _S_LASTCHANGE,
829 					    DAY_NOW_32) < 0)
830 						return (PWU_NOMEM);
831 					spw->sp_lstchg = DAY_NOW_32;
832 				}
833 			}
834 			NUM_TO_STR(val, p->data.val_i);
835 			NEW_ATTR(sattrs, sidx, _S_MAX, val);
836 			aging_set = 1;
837 			break;
838 
839 		case ATTR_WARN:
840 			if (!ldapbuf->shadow_update_enabled)
841 				break;	/* not managing passwordAccount */
842 			if (spw->sp_max == -1 &&
843 			    p->data.val_i != -1 && max_present(p->next) == 0)
844 				return (PWU_AGING_DISABLED);
845 			NUM_TO_STR(val, p->data.val_i);
846 			NEW_ATTR(sattrs, sidx, _S_WARNING, val);
847 			break;
848 
849 		case ATTR_INACT:
850 			if (!ldapbuf->shadow_update_enabled)
851 				break;	/* not managing passwordAccount */
852 			NUM_TO_STR(val, p->data.val_i);
853 			NEW_ATTR(sattrs, sidx, _S_INACTIVE, val);
854 			break;
855 
856 		case ATTR_EXPIRE:
857 			if (!ldapbuf->shadow_update_enabled)
858 				break;	/* not managing passwordAccount */
859 			NUM_TO_STR(val, p->data.val_i);
860 			NEW_ATTR(sattrs, sidx, _S_EXPIRE, val);
861 			break;
862 
863 		case ATTR_FLAG:
864 			if (!ldapbuf->shadow_update_enabled)
865 				break;	/* not managing passwordAccount */
866 			NUM_TO_STR(val, p->data.val_i);
867 			NEW_ATTR(sattrs, sidx, _S_FLAG, val);
868 			break;
869 		case ATTR_INCR_FAILED_LOGINS:
870 			if (!ldapbuf->shadow_update_enabled) {
871 				rc = PWU_CHANGE_NOT_ALLOWED;
872 				break;	/* not managing passwordAccount */
873 			}
874 			count = (spw->sp_flag & FAILCOUNT_MASK) + 1;
875 			spw->sp_flag &= ~FAILCOUNT_MASK;
876 			spw->sp_flag |= min(FAILCOUNT_MASK, count);
877 			p->data.val_i = count;
878 			NUM_TO_STR(val, spw->sp_flag);
879 			NEW_ATTR(sattrs, sidx, _S_FLAG, val);
880 			break;
881 		case ATTR_RST_FAILED_LOGINS:
882 			if (!ldapbuf->shadow_update_enabled) {
883 				rc = PWU_CHANGE_NOT_ALLOWED;
884 				break;	/* not managing passwordAccount */
885 			}
886 			p->data.val_i = spw->sp_flag & FAILCOUNT_MASK;
887 			spw->sp_flag &= ~FAILCOUNT_MASK;
888 			NUM_TO_STR(val, spw->sp_flag);
889 			NEW_ATTR(sattrs, sidx, _S_FLAG, val);
890 			break;
891 		default:
892 			break;
893 		}
894 	}
895 
896 	/*
897 	 * If the ldap client is configured with shadow update enabled,
898 	 * then what should the new aging values look like?
899 	 *
900 	 * There are a number of different conditions
901 	 *
902 	 *  a) aging is already configured: don't touch it
903 	 *
904 	 *  b) disable_aging is set: disable aging
905 	 *
906 	 *  c) aging is not configured: turn on default aging;
907 	 *
908 	 *  b) and c) of course only if aging_needed and !aging_set.
909 	 *  (i.e., password changed, and aging values not changed)
910 	 */
911 
912 	if (ldapbuf->shadow_update_enabled && spw != NULL && spw->sp_max <= 0) {
913 		/* a) aging not yet configured */
914 		if (aging_needed && !aging_set) {
915 			if (disable_aging) {
916 				/* b) turn off aging */
917 				spw->sp_min = spw->sp_max = spw->sp_warn = -1;
918 				if (attr_addmod(sattrs, &sidx, _S_MIN, -1) < 0)
919 					return (PWU_NOMEM);
920 				if (attr_addmod(sattrs, &sidx, _S_MAX, -1) < 0)
921 					return (PWU_NOMEM);
922 				if (attr_addmod(sattrs, &sidx, _S_WARNING,
923 				    -1) < 0)
924 					return (PWU_NOMEM);
925 			} else {
926 				/* c) */
927 				turn_on_default_aging(spw);
928 
929 				if (attr_addmod(sattrs, &sidx, _S_MIN,
930 				    spw->sp_min) < 0)
931 					return (PWU_NOMEM);
932 				if (attr_addmod(sattrs, &sidx, _S_MAX,
933 				    spw->sp_max) < 0)
934 					return (PWU_NOMEM);
935 				if (attr_addmod(sattrs, &sidx,
936 				    _S_WARNING, spw->sp_warn) < 0)
937 					return (PWU_NOMEM);
938 			}
939 		}
940 	}
941 
942 	pattrs[pidx] = NULL;
943 	sattrs[sidx] = NULL;
944 
945 	return (rc);
946 }
947 
948 /*
949  * ldap_to_pwu_code(error, pwd_status)
950  *
951  * translation from LDAP return values and PWU return values
952  */
953 int
ldap_to_pwu_code(int error,int pwd_status)954 ldap_to_pwu_code(int error, int pwd_status)
955 {
956 	switch (error) {
957 	case NS_LDAP_SUCCESS:	return (PWU_SUCCESS);
958 	case NS_LDAP_OP_FAILED:	return (PWU_DENIED);
959 	case NS_LDAP_NOTFOUND:	return (PWU_NOT_FOUND);
960 	case NS_LDAP_MEMORY:	return (PWU_NOMEM);
961 	case NS_LDAP_CONFIG:	return (PWU_NOT_FOUND);
962 	case NS_LDAP_INTERNAL:
963 		switch (pwd_status) {
964 		case NS_PASSWD_EXPIRED:
965 			return (PWU_DENIED);
966 		case NS_PASSWD_CHANGE_NOT_ALLOWED:
967 			return (PWU_CHANGE_NOT_ALLOWED);
968 		case NS_PASSWD_TOO_SHORT:
969 			return (PWU_PWD_TOO_SHORT);
970 		case NS_PASSWD_INVALID_SYNTAX:
971 			return (PWU_PWD_INVALID);
972 		case NS_PASSWD_IN_HISTORY:
973 			return (PWU_PWD_IN_HISTORY);
974 		case NS_PASSWD_WITHIN_MIN_AGE:
975 			return (PWU_WITHIN_MIN_AGE);
976 		default:
977 			return (PWU_SYSTEM_ERROR);
978 		}
979 	default:		return (PWU_SYSTEM_ERROR);
980 	}
981 }
982 
983 int
ldap_replaceattr(const char * dn,ns_ldap_attr_t ** attrs,const char * binddn,const char * pwd,int * pwd_status,int flags)984 ldap_replaceattr(const char *dn, ns_ldap_attr_t **attrs, const char *binddn,
985 	const char *pwd, int *pwd_status, int flags)
986 {
987 	int		result = NS_LDAP_OP_FAILED;
988 	int		ldaprc;
989 	int		authstried = 0;
990 	char		**certpath = NULL;
991 	ns_auth_t	**app;
992 	ns_auth_t	**authpp = NULL;
993 	ns_auth_t	*authp = NULL;
994 	ns_cred_t	*credp;
995 	ns_ldap_error_t	*errorp = NULL;
996 
997 	debug("%s: replace_ldapattr()", __FILE__);
998 
999 	if ((credp = (ns_cred_t *)calloc(1, sizeof (ns_cred_t))) == NULL)
1000 		return (NS_LDAP_MEMORY); /* map to PWU_NOMEM */
1001 
1002 	/* for admin shadow update, dn and pwd will be set later in libsldap */
1003 	if ((flags & NS_LDAP_UPDATE_SHADOW) == 0) {
1004 		/* Fill in the user name and password */
1005 		if (dn == NULL || pwd == NULL)
1006 			goto out;
1007 		credp->cred.unix_cred.userID = strdup(binddn);
1008 		credp->cred.unix_cred.passwd = strdup(pwd);
1009 	}
1010 
1011 	/* get host certificate path, if one is configured */
1012 	ldaprc = __ns_ldap_getParam(NS_LDAP_HOST_CERTPATH_P,
1013 	    (void ***)&certpath, &errorp);
1014 	if (ldaprc != NS_LDAP_SUCCESS)
1015 		goto out;
1016 
1017 	if (certpath && *certpath)
1018 		credp->hostcertpath = *certpath;
1019 
1020 	/* Load the service specific authentication method */
1021 	ldaprc = __ns_ldap_getServiceAuthMethods("passwd-cmd", &authpp,
1022 	    &errorp);
1023 
1024 	if (ldaprc != NS_LDAP_SUCCESS)
1025 		goto out;
1026 
1027 	/*
1028 	 * if authpp is null, there is no serviceAuthenticationMethod
1029 	 * try default authenticationMethod
1030 	 */
1031 	if (authpp == NULL) {
1032 		ldaprc = __ns_ldap_getParam(NS_LDAP_AUTH_P, (void ***)&authpp,
1033 		    &errorp);
1034 		if (ldaprc != NS_LDAP_SUCCESS)
1035 			goto out;
1036 	}
1037 
1038 	/*
1039 	 * if authpp is still null, then can not authenticate, syslog
1040 	 * error message and return error
1041 	 */
1042 	if (authpp == NULL) {
1043 		syslog(LOG_ERR,
1044 		"passwdutil: no legal LDAP authentication method configured");
1045 		result = NS_LDAP_OP_FAILED;
1046 		goto out;
1047 	}
1048 
1049 	/*
1050 	 * Walk the array and try all authentication methods in order except
1051 	 * for "none".
1052 	 */
1053 	for (app = authpp; *app; app++) {
1054 		authp = *app;
1055 		/* what about disabling other mechanisms? "tls:sasl/EXTERNAL" */
1056 		if (authp->type == NS_LDAP_AUTH_NONE)
1057 			continue;
1058 		authstried++;
1059 		credp->auth.type = authp->type;
1060 		credp->auth.tlstype = authp->tlstype;
1061 		credp->auth.saslmech = authp->saslmech;
1062 		credp->auth.saslopt = authp->saslopt;
1063 
1064 		ldaprc = __ns_ldap_repAttr("shadow", dn,
1065 		    (const ns_ldap_attr_t * const *)attrs,
1066 		    credp, flags, &errorp);
1067 		if (ldaprc == NS_LDAP_SUCCESS) {
1068 			result = NS_LDAP_SUCCESS;
1069 			goto out;
1070 		}
1071 
1072 		/*
1073 		 * if change not allowed due to configuration, indicate so
1074 		 * to the caller
1075 		 */
1076 		if (ldaprc == NS_LDAP_CONFIG &&
1077 		    errorp->status == NS_CONFIG_NOTALLOW) {
1078 			result = NS_LDAP_CONFIG;
1079 			*pwd_status = NS_PASSWD_CHANGE_NOT_ALLOWED;
1080 			goto out;
1081 		}
1082 
1083 		/*
1084 		 * other errors might need to be added to this list, for
1085 		 * the current supported mechanisms this is sufficient
1086 		 */
1087 		if ((ldaprc == NS_LDAP_INTERNAL) &&
1088 		    (errorp->pwd_mgmt.status == NS_PASSWD_GOOD) &&
1089 		    ((errorp->status == LDAP_INAPPROPRIATE_AUTH) ||
1090 		    (errorp->status == LDAP_INVALID_CREDENTIALS))) {
1091 			result = ldaprc;
1092 			goto out;
1093 		}
1094 
1095 		/*
1096 		 * If there is error related to password policy,
1097 		 * return it to caller
1098 		 */
1099 		if ((ldaprc == NS_LDAP_INTERNAL) &&
1100 		    errorp->pwd_mgmt.status != NS_PASSWD_GOOD) {
1101 			*pwd_status = errorp->pwd_mgmt.status;
1102 			result = ldaprc;
1103 			goto out;
1104 		} else
1105 			*pwd_status = NS_PASSWD_GOOD;
1106 
1107 		/* we don't really care about the error, just clean it up */
1108 		if (errorp)
1109 			(void) __ns_ldap_freeError(&errorp);
1110 	}
1111 	if (authstried == 0) {
1112 		syslog(LOG_ERR,
1113 		"passwdutil: no legal LDAP authentication method configured");
1114 		result = NS_LDAP_CONFIG;
1115 		goto out;
1116 	}
1117 	result = NS_LDAP_OP_FAILED; /* map to PWU_DENIED */
1118 
1119 out:
1120 	if (credp)
1121 		(void) __ns_ldap_freeCred(&credp);
1122 
1123 	if (authpp)
1124 		(void) __ns_ldap_freeParam((void ***)&authpp);
1125 
1126 	if (errorp)
1127 		(void) __ns_ldap_freeError(&errorp);
1128 
1129 	return (result);
1130 }
1131 
1132 
1133 /*
1134  * ldap_putpwnam(name, oldpw, rep, buf)
1135  *
1136  * update the LDAP server with the attributes contained in 'buf'.
1137  */
1138 /*ARGSUSED*/
1139 int
ldap_putpwnam(const char * name,const char * oldpw,pwu_repository_t * rep,void * buf)1140 ldap_putpwnam(const char *name, const char *oldpw, pwu_repository_t *rep,
1141     void *buf)
1142 {
1143 	int res;
1144 	char *dn;	/* dn of user whose attributes we are changing */
1145 	char *binddn;	/* dn of user who is performing the change */
1146 	ns_ldap_error_t *errorp;
1147 	ldapbuf_t *ldapbuf = (ldapbuf_t *)buf;
1148 	ns_ldap_attr_t **pattrs = ldapbuf->pattrs;
1149 	ns_ldap_attr_t **sattrs = ldapbuf->sattrs;
1150 	struct passwd *pw;
1151 	int pwd_status;
1152 	uid_t uid;
1153 
1154 	if (strcmp(name, "root") == 0)
1155 		return (PWU_NOT_FOUND);
1156 
1157 	/*
1158 	 * convert name of user whose attributes we are changing
1159 	 * to a distinguished name
1160 	 */
1161 	res = __ns_ldap_uid2dn(name, &dn, NULL, &errorp);
1162 	if (res != NS_LDAP_SUCCESS)
1163 		goto out;
1164 
1165 	/* update shadow via ldap_cachemgr if it is enabled */
1166 	if (ldapbuf->shadow_update_enabled &&
1167 	    sattrs != NULL && sattrs[0] != NULL) {
1168 		/*
1169 		 * flag NS_LDAP_UPDATE_SHADOW indicates the shadow update
1170 		 * should be done via ldap_cachemgr
1171 		 */
1172 		res = ldap_replaceattr(dn, sattrs, NULL, NULL, &pwd_status,
1173 		    NS_LDAP_UPDATE_SHADOW);
1174 		goto out;
1175 	}
1176 
1177 	/*
1178 	 * The LDAP server checks whether we are permitted to perform
1179 	 * the requested change. We need to send the name of the user
1180 	 * who is executing this piece of code, together with his
1181 	 * current password to the server.
1182 	 * If this is executed by a normal user changing his/her own
1183 	 * password, this will simply be the OLD password that is to
1184 	 * be changed.
1185 	 * Specific case if the user who is executing this piece
1186 	 * of code is root. We will then issue the LDAP request
1187 	 * with the DN of the user we want to change the passwd of.
1188 	 */
1189 
1190 	/*
1191 	 * create a dn for the user who is executing this code
1192 	 */
1193 	uid = getuid();
1194 	if (uid == 0) {
1195 		if ((pw = getpwnam_from(name, rep, REP_LDAP)) == NULL) {
1196 			res = NS_LDAP_OP_FAILED;
1197 			goto out;
1198 		}
1199 	} else if ((pw = getpwuid_from(uid, rep, REP_LDAP)) == NULL) {
1200 		/*
1201 		 * User executing this code is not known to the LDAP
1202 		 * server. This operation is to be denied
1203 		 */
1204 		res = NS_LDAP_OP_FAILED;
1205 		goto out;
1206 	}
1207 
1208 	res = __ns_ldap_uid2dn(pw->pw_name, &binddn, NULL, &errorp);
1209 	if (res != NS_LDAP_SUCCESS)
1210 		goto out;
1211 
1212 	if (pattrs && pattrs[0] != NULL) {
1213 		res = ldap_replaceattr(dn, pattrs, binddn, oldpw,
1214 		    &pwd_status, 0);
1215 	} else
1216 		res = NS_LDAP_OP_FAILED;
1217 
1218 out:
1219 	free_ldapbuf(ldapbuf);
1220 	free(dn);
1221 
1222 	return (ldap_to_pwu_code(res, pwd_status));
1223 }
1224