xref: /titanic_50/usr/src/lib/passwdutil/ldap_attr.c (revision 711890bc9379ceea66272dc8d4981812224ea86e)
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, Version 1.0 only
6  * (the "License").  You may not use this file except in compliance
7  * with the License.
8  *
9  * You can obtain a copy of the license at usr/src/OPENSOLARIS.LICENSE
10  * or http://www.opensolaris.org/os/licensing.
11  * See the License for the specific language governing permissions
12  * and limitations under the License.
13  *
14  * When distributing Covered Code, include this CDDL HEADER in each
15  * file and include the License file at usr/src/OPENSOLARIS.LICENSE.
16  * If applicable, add the following below this CDDL HEADER, with the
17  * fields enclosed by brackets "[]" replaced with your own identifying
18  * information: Portions Copyright [yyyy] [name of copyright owner]
19  *
20  * CDDL HEADER END
21  */
22 /*
23  * Copyright 2004 Sun Microsystems, Inc.  All rights reserved.
24  * Use is subject to license terms.
25  */
26 
27 #pragma ident	"%Z%%M%	%I%	%E% SMI"
28 
29 #include <stdio.h>
30 #include <errno.h>
31 #include <stdlib.h>
32 #include <string.h>
33 #include <unistd.h>
34 
35 #include "ns_sldap.h"
36 
37 #include <nss_dbdefs.h>
38 #include <nsswitch.h>
39 
40 #include <pwd.h>
41 #include <shadow.h>
42 #include <syslog.h>
43 
44 #include "passwdutil.h"
45 
46 #include "utils.h"
47 
48 int ldap_getattr(char *name, attrlist *item, pwu_repository_t *rep);
49 int ldap_getpwnam(char *name, attrlist *items, pwu_repository_t *rep,
50     void **buf);
51 int ldap_update(attrlist *items, pwu_repository_t *rep, void *buf);
52 int ldap_putpwnam(char *name, char *oldpw, char *dummy,
53 	pwu_repository_t *rep, void *buf);
54 int ldap_user_to_authenticate(char *name, pwu_repository_t *rep,
55 	char **auth_user, int *privileged);
56 
57 /*
58  * ldap function pointer table, used by passwdutil_init to initialize
59  * the global Repository-OPerations table "rops"
60  */
61 struct repops ldap_repops = {
62 	NULL,	/* checkhistory */
63 	ldap_getattr,
64 	ldap_getpwnam,
65 	ldap_update,
66 	ldap_putpwnam,
67 	ldap_user_to_authenticate,
68 	NULL,	/* lock */
69 	NULL	/* unlock */
70 };
71 
72 /*
73  * structure used to keep state between get/update/put calls
74  */
75 typedef struct {
76 	char *passwd;		/* encrypted password */
77 	struct passwd *pwd;
78 	ns_ldap_attr_t **attrs;
79 } ldapbuf_t;
80 
81 /*
82  * The following define's are taken from
83  *	usr/src/lib/nsswitch/ldap/common/getpwnam.c
84  */
85 
86 /* passwd attributes filters */
87 #define	_PWD_CN			"cn"
88 #define	_PWD_UID		"uid"
89 #define	_PWD_USERPASSWORD	"userpassword"
90 #define	_PWD_UIDNUMBER		"uidnumber"
91 #define	_PWD_GIDNUMBER		"gidnumber"
92 #define	_PWD_GECOS		"gecos"
93 #define	_PWD_DESCRIPTION	"description"
94 #define	_PWD_HOMEDIRECTORY	"homedirectory"
95 #define	_PWD_LOGINSHELL		"loginshell"
96 
97 /*
98  * int ldap_user_to_authenticate(user, rep, auth_user, privileged)
99  *
100  * We can't determine whether the user is "privileged" in the LDAP
101  * sense. The operation should be attempted and will succeed if
102  * the user had privileges.
103  *
104  * For our purposes, we say that the user is privileged if he/she
105  * is attempting to change another user's password attributes.
106  */
107 int
108 ldap_user_to_authenticate(char *user, pwu_repository_t *rep,
109 	char **auth_user, int *privileged)
110 {
111 	struct passwd *pw;
112 	uid_t uid;
113 	uid_t priviledged_uid;
114 	int res = PWU_SUCCESS;
115 
116 	if (strcmp(user, "root") == 0)
117 		return (PWU_NOT_FOUND);
118 
119 	if ((pw = getpwnam_from(user, rep, REP_LDAP)) == NULL)
120 		return (PWU_NOT_FOUND);
121 
122 	uid = getuid();
123 
124 	if (uid == pw->pw_uid) {
125 		/* changing out own, not privileged */
126 		*privileged = 0;
127 		if ((*auth_user = strdup(user)) == NULL)
128 			res = PWU_NOMEM;
129 	} else {
130 		char pwd_buf[1024];
131 		struct passwd pwr;
132 
133 		*privileged = 1;
134 		/*
135 		 * specific case for root
136 		 * we want 'user' to be authenticated.
137 		 */
138 		if (uid == 0)  {
139 			priviledged_uid = pw->pw_uid;
140 		} else {
141 			priviledged_uid = uid;
142 		}
143 		if (getpwuid_r(priviledged_uid, &pwr, pwd_buf,
144 				sizeof (pwd_buf)) != NULL) {
145 			if ((*auth_user = strdup(pwr.pw_name)) ==  NULL)
146 				res = PWU_NOMEM;
147 		} else {
148 			/* hmm. can't find name of current user...??? */
149 
150 #define	MAX_UID_LEN 11	/* UID's larger than 2^32 won't fit... */
151 			if ((*auth_user = malloc(MAX_UID_LEN)) == NULL) {
152 				res = PWU_NOMEM;
153 			} else {
154 				(void) snprintf(*auth_user, MAX_UID_LEN, "%d",
155 				    (int)uid);
156 			}
157 		}
158 	}
159 
160 	return (res);
161 }
162 
163 /*
164  * int ldap_getattr(name, item, rep)
165  *
166  * retrieve attributes specified in "item" for user "name".
167  */
168 /*ARGSUSED*/
169 int
170 ldap_getattr(char *name, attrlist *items, pwu_repository_t *rep)
171 {
172 	int res;
173 	struct passwd *pw = NULL;
174 	struct spwd *spw = NULL;
175 	attrlist *w;
176 
177 	int need_shadow = 0;	/* Need shadow info from LDAP server */
178 	int need_normal = 0;	/* Need non-shadow info from LDAP server */
179 
180 	/* We need the "shadow" map for the password only */
181 	for (w = items; w != NULL; w = w->next) {
182 		if (w->type == ATTR_PASSWD ||
183 			w->type == ATTR_PASSWD_SERVER_POLICY)
184 			need_shadow = 1;
185 		else
186 			need_normal = 1;
187 	}
188 
189 	if (need_normal) {
190 		res = dup_pw(&pw, getpwnam_from(name, rep, REP_LDAP));
191 		if (res != PWU_SUCCESS)
192 			goto out;
193 	}
194 
195 	if (need_shadow) {
196 		res = dup_spw(&spw, getspnam_from(name, rep, REP_LDAP));
197 		if (res != PWU_SUCCESS) {
198 			goto out;
199 		}
200 	}
201 
202 	for (w = items; res == PWU_SUCCESS && w != NULL; w = w->next) {
203 		switch (w->type) {
204 		case ATTR_NAME:
205 			if ((w->data.val_s = strdup(pw->pw_name)) == NULL)
206 				res = PWU_NOMEM;
207 			break;
208 		case ATTR_COMMENT:
209 			if ((w->data.val_s = strdup(pw->pw_comment)) == NULL)
210 				res = PWU_NOMEM;
211 			break;
212 		case ATTR_GECOS:
213 			if ((w->data.val_s = strdup(pw->pw_gecos)) == NULL)
214 				res = PWU_NOMEM;
215 			break;
216 		case ATTR_HOMEDIR:
217 			if ((w->data.val_s = strdup(pw->pw_dir)) == NULL)
218 				res = PWU_NOMEM;
219 			break;
220 		case ATTR_SHELL:
221 			if ((w->data.val_s = strdup(pw->pw_shell)) == NULL)
222 				res = PWU_NOMEM;
223 			break;
224 		case ATTR_PASSWD:
225 		case ATTR_PASSWD_SERVER_POLICY:
226 			if ((w->data.val_s = strdup(spw->sp_pwdp)) == NULL)
227 				res = PWU_NOMEM;
228 			break;
229 		case ATTR_AGE:
230 			if ((w->data.val_s = strdup(pw->pw_age)) == NULL)
231 				res = PWU_NOMEM;
232 			break;
233 		case ATTR_REP_NAME:
234 			if ((w->data.val_s = strdup("ldap")) == NULL)
235 				res = PWU_NOMEM;
236 			break;
237 
238 		/* integer values */
239 		case ATTR_UID:
240 			w->data.val_i = pw->pw_uid;
241 			break;
242 		case ATTR_GID:
243 			w->data.val_i = pw->pw_gid;
244 			break;
245 		case ATTR_LSTCHG:
246 			w->data.val_i = -1;
247 			break;
248 		case ATTR_MIN:
249 			w->data.val_i = -1;
250 			break;
251 		case ATTR_MAX:
252 			w->data.val_i = -1;
253 			break;
254 		case ATTR_WARN:
255 			w->data.val_i = -1;
256 			break;
257 		case ATTR_INACT:
258 			w->data.val_i = -1;
259 			break;
260 		case ATTR_EXPIRE:
261 			w->data.val_i = -1;
262 			break;
263 		case ATTR_FLAG:
264 			break;
265 		default:
266 			break;
267 		}
268 	}
269 
270 out:
271 	if (pw)
272 		free_pwd(pw);
273 	if (spw)
274 		free_spwd(spw);
275 
276 	return (res);
277 }
278 
279 /*
280  * int ldap_getpwnam(name, items, rep, buf)
281  *
282  * There is no need to get the old values from the ldap
283  * server, as the update will update each item individually.
284  * Therefore, we only allocate a buffer that will be used by
285  * _update and _putpwnam to hold the attributes to update.
286  *
287  * Only when we're about to update a password, we need to retrieve
288  * the old password since it contains salt-information.
289  */
290 /*ARGSUSED*/
291 int
292 ldap_getpwnam(char *name, attrlist *items, pwu_repository_t *rep,
293     void **buf)
294 {
295 	attrlist *p;
296 	int nr_items;
297 	int need_pwd = 0;
298 	ldapbuf_t *ldapbuf;
299 	int res;
300 
301 	for (nr_items = 0, p = items; p != NULL; p = p->next) {
302 		nr_items++;
303 		if (p->type == ATTR_PASSWD ||
304 		    p->type == ATTR_PASSWD_SERVER_POLICY)
305 			need_pwd = 1;
306 	}
307 
308 
309 	ldapbuf = calloc(1, sizeof (ldapbuf_t));
310 	if (ldapbuf == NULL)
311 		return (PWU_NOMEM);
312 
313 	ldapbuf->attrs = calloc(nr_items, sizeof (ns_ldap_attr_t *));
314 	if (ldapbuf->attrs == NULL)
315 		return (PWU_NOMEM);
316 
317 	if (need_pwd) {
318 		struct spwd *spw;
319 
320 		res = dup_pw(&ldapbuf->pwd, getpwnam_from(name, rep, REP_LDAP));
321 		if (res != PWU_SUCCESS)
322 			return (res);
323 
324 		spw  = getspnam_from(name, rep, REP_LDAP);
325 		if (spw) {
326 			ldapbuf->passwd = strdup(spw->sp_pwdp);
327 			if (ldapbuf->passwd == NULL)
328 				return (PWU_NOMEM);
329 		}
330 	}
331 
332 	*buf = ldapbuf;
333 	return (0);
334 }
335 
336 /*
337  * new_attr(name, value)
338  *
339  * create a new LDAP attribute to be sent to the server
340  */
341 ns_ldap_attr_t *
342 new_attr(char *name, char *value)
343 {
344 	ns_ldap_attr_t *tmp;
345 
346 	tmp = malloc(sizeof (*tmp));
347 	if (tmp != NULL) {
348 		tmp->attrname = name;
349 		tmp->attrvalue = (char **)calloc(2, sizeof (char *));
350 		if (tmp->attrvalue == NULL) {
351 			free(tmp);
352 			return (NULL);
353 		}
354 		tmp->attrvalue[0] = value;
355 		tmp->value_count = 1;
356 	}
357 
358 	return (tmp);
359 }
360 
361 /*
362  * ldap_update(items, rep, buf)
363  *
364  * create LDAP attributes in 'buf' for each attribute in 'items'.
365  */
366 /*ARGSUSED*/
367 int
368 ldap_update(attrlist *items, pwu_repository_t *rep, void *buf)
369 {
370 	attrlist *p;
371 	int idx = 0;
372 	ldapbuf_t *ldapbuf = (ldapbuf_t *)buf;
373 	ns_ldap_attr_t **attrs = ldapbuf->attrs;
374 	char *pwd, *val;
375 	char *salt;
376 	size_t cryptlen;
377 
378 	for (p = items; p != NULL; p = p->next) {
379 		switch (p->type) {
380 		case ATTR_PASSWD:
381 			salt = crypt_gensalt(ldapbuf->passwd, ldapbuf->pwd);
382 
383 			if (salt == NULL) {
384 				if (errno == ENOMEM)
385 					return (PWU_NOMEM);
386 				else {
387 					/* algorithm problem? */
388 					syslog(LOG_AUTH | LOG_ALERT,
389 					    "passwdutil: crypt_gensalt "
390 					    "%m");
391 					return (PWU_UPDATE_FAILED);
392 				}
393 			}
394 
395 			pwd = crypt(p->data.val_s, salt);
396 			free(salt);
397 			cryptlen = strlen(pwd) + sizeof ("{crypt}");
398 			val = malloc(cryptlen);
399 			if (val == NULL)
400 				return (PWU_NOMEM);
401 			(void) snprintf(val, cryptlen, "{crypt}%s", pwd);
402 
403 			attrs[idx] = new_attr(_PWD_USERPASSWORD, val);
404 			break;
405 		/*
406 		 * For server policy, don't crypt the password,
407 		 * send the password as is to the server and
408 		 * let the LDAP server do its own password
409 		 * encryption
410 		 */
411 		case ATTR_PASSWD_SERVER_POLICY:
412 			val = strdup(p->data.val_s);
413 			if (val == NULL)
414 				return (PWU_NOMEM);
415 
416 			attrs[idx] = new_attr(_PWD_USERPASSWORD, val);
417 			break;
418 		case ATTR_COMMENT:
419 			/* XX correct? */
420 			attrs[idx] = new_attr(_PWD_DESCRIPTION, p->data.val_s);
421 			break;
422 		case ATTR_GECOS:
423 			attrs[idx] = new_attr(_PWD_GECOS, p->data.val_s);
424 			break;
425 		case ATTR_HOMEDIR:
426 			attrs[idx] = new_attr(_PWD_HOMEDIRECTORY,
427 						p->data.val_s);
428 			break;
429 		case ATTR_SHELL:
430 			attrs[idx] = new_attr(_PWD_LOGINSHELL, p->data.val_s);
431 			break;
432 		/* Unsupported items are below this line */
433 		case ATTR_NAME:
434 		case ATTR_UID:
435 		case ATTR_GID:
436 		case ATTR_AGE:
437 		case ATTR_LSTCHG:
438 		case ATTR_MIN:
439 		case ATTR_MAX:
440 		case ATTR_WARN:
441 		case ATTR_INACT:
442 		case ATTR_EXPIRE:
443 		case ATTR_FLAG:
444 			break;
445 		default:
446 			break;
447 		}
448 		if (attrs[idx] == NULL)
449 			return (PWU_NOMEM);
450 		idx++;
451 	}
452 
453 	attrs[idx] = NULL;
454 
455 	return (PWU_SUCCESS);
456 }
457 
458 /*
459  * ldap_to_pwu_code(error, pwd_status)
460  *
461  * translation from LDAP return values and PWU return values
462  */
463 int
464 ldap_to_pwu_code(int error, int pwd_status)
465 {
466 	switch (error) {
467 	case NS_LDAP_SUCCESS:	return (PWU_SUCCESS);
468 	case NS_LDAP_OP_FAILED:	return (PWU_DENIED);
469 	case NS_LDAP_NOTFOUND:	return (PWU_NOT_FOUND);
470 	case NS_LDAP_MEMORY:	return (PWU_NOMEM);
471 	case NS_LDAP_CONFIG:	return (PWU_NOT_FOUND);
472 	case NS_LDAP_INTERNAL:
473 		switch (pwd_status) {
474 		case NS_PASSWD_EXPIRED:
475 			return (PWU_DENIED);
476 		case NS_PASSWD_CHANGE_NOT_ALLOWED:
477 			return (PWU_CHANGE_NOT_ALLOWED);
478 		case NS_PASSWD_TOO_SHORT:
479 			return (PWU_PWD_TOO_SHORT);
480 		case NS_PASSWD_INVALID_SYNTAX:
481 			return (PWU_PWD_INVALID);
482 		case NS_PASSWD_IN_HISTORY:
483 			return (PWU_PWD_IN_HISTORY);
484 		case NS_PASSWD_WITHIN_MIN_AGE:
485 			return (PWU_WITHIN_MIN_AGE);
486 		default:
487 			return (PWU_SYSTEM_ERROR);
488 		}
489 	default:		return (PWU_SYSTEM_ERROR);
490 	}
491 }
492 
493 int
494 ldap_replaceattr(const char *dn, ns_ldap_attr_t **attrs, const char *binddn,
495 	const char *pwd, int *pwd_status)
496 {
497 	int		result = NS_LDAP_OP_FAILED;
498 	int		ldaprc;
499 	int		authstried = 0;
500 	char		**certpath = NULL;
501 	ns_auth_t	**app;
502 	ns_auth_t	**authpp = NULL;
503 	ns_auth_t	*authp = NULL;
504 	ns_cred_t	*credp;
505 	ns_ldap_error_t	*errorp = NULL;
506 
507 	debug("%s: replace_ldapattr()", __FILE__);
508 
509 	if ((credp = (ns_cred_t *)calloc(1, sizeof (ns_cred_t))) == NULL)
510 		return (PWU_NOMEM);
511 
512 	/* Fill in the user name and password */
513 	if (dn == NULL || pwd == NULL)
514 		goto out;
515 
516 	credp->cred.unix_cred.userID = strdup(binddn);
517 	credp->cred.unix_cred.passwd = strdup(pwd);
518 
519 	/* get host certificate path, if one is configured */
520 	ldaprc = __ns_ldap_getParam(NS_LDAP_HOST_CERTPATH_P,
521 		(void ***)&certpath, &errorp);
522 	if (ldaprc != NS_LDAP_SUCCESS)
523 		goto out;
524 
525 	if (certpath && *certpath)
526 		credp->hostcertpath = *certpath;
527 
528 	/* Load the service specific authentication method */
529 	ldaprc = __ns_ldap_getServiceAuthMethods("passwd-cmd", &authpp,
530 		&errorp);
531 
532 	if (ldaprc != NS_LDAP_SUCCESS)
533 		goto out;
534 
535 	/*
536 	 * if authpp is null, there is no serviceAuthenticationMethod
537 	 * try default authenticationMethod
538 	 */
539 	if (authpp == NULL) {
540 		ldaprc = __ns_ldap_getParam(NS_LDAP_AUTH_P, (void ***)&authpp,
541 			&errorp);
542 		if (ldaprc != NS_LDAP_SUCCESS)
543 			goto out;
544 	}
545 
546 	/*
547 	 * if authpp is still null, then can not authenticate, syslog
548 	 * error message and return error
549 	 */
550 	if (authpp == NULL) {
551 		syslog(LOG_ERR,
552 		"passwdutil: no legal LDAP authentication method configured");
553 		result = NS_LDAP_OP_FAILED;
554 		goto out;
555 	}
556 
557 	/*
558 	 * Walk the array and try all authentication methods in order except
559 	 * for "none".
560 	 */
561 	for (app = authpp; *app; app++) {
562 		authp = *app;
563 		/* what about disabling other mechanisms? "tls:sasl/EXTERNAL" */
564 		if (authp->type == NS_LDAP_AUTH_NONE)
565 			continue;
566 		authstried++;
567 		credp->auth.type = authp->type;
568 		credp->auth.tlstype = authp->tlstype;
569 		credp->auth.saslmech = authp->saslmech;
570 		credp->auth.saslopt = authp->saslopt;
571 
572 		ldaprc = __ns_ldap_repAttr("shadow", dn,
573 			(const ns_ldap_attr_t * const *)attrs,
574 			credp, 0, &errorp);
575 		if (ldaprc == NS_LDAP_SUCCESS) {
576 			result = NS_LDAP_SUCCESS;
577 			goto out;
578 		}
579 
580 		/*
581 		 * other errors might need to be added to this list, for
582 		 * the current supported mechanisms this is sufficient
583 		 */
584 		if ((ldaprc == NS_LDAP_INTERNAL) &&
585 			(errorp->pwd_mgmt.status == NS_PASSWD_GOOD) &&
586 			((errorp->status == LDAP_INAPPROPRIATE_AUTH) ||
587 			(errorp->status == LDAP_INVALID_CREDENTIALS))) {
588 			result = ldaprc;
589 			goto out;
590 		}
591 
592 		/*
593 		 * If there is error related to password policy,
594 		 * return it to caller
595 		 */
596 		if ((ldaprc == NS_LDAP_INTERNAL) &&
597 			errorp->pwd_mgmt.status != NS_PASSWD_GOOD) {
598 			*pwd_status = errorp->pwd_mgmt.status;
599 			result = ldaprc;
600 			goto out;
601 		} else
602 			*pwd_status = NS_PASSWD_GOOD;
603 
604 		/* we don't really care about the error, just clean it up */
605 		if (errorp)
606 			(void) __ns_ldap_freeError(&errorp);
607 	}
608 	if (authstried == 0) {
609 		syslog(LOG_ERR,
610 		"passwdutil: no legal LDAP authentication method configured");
611 		result = NS_LDAP_CONFIG;
612 		goto out;
613 	}
614 	result = PWU_DENIED;
615 
616 out:
617 	if (credp)
618 		(void) __ns_ldap_freeCred(&credp);
619 
620 	if (authpp)
621 		(void) __ns_ldap_freeParam((void ***)&authpp);
622 
623 	if (errorp)
624 		(void) __ns_ldap_freeError(&errorp);
625 
626 	return (result);
627 }
628 
629 
630 
631 /*
632  * ldap_putpwnam(name, oldpw, dummy, rep, buf)
633  *
634  * update the LDAP server with the attributes contained in 'buf'.
635  * The dummy parameter is a placeholder for NIS+ where the old
636  * RPC password is passwd.
637  */
638 /*ARGSUSED*/
639 int
640 ldap_putpwnam(char *name, char *oldpw, char *dummy,
641 	pwu_repository_t *rep, void *buf)
642 {
643 	int res;
644 	char *dn;	/* dn of user whose attributes we are changing */
645 	char *binddn;	/* dn of user who is performing the change */
646 	ns_ldap_error_t *errorp;
647 	ldapbuf_t *ldapbuf = (ldapbuf_t *)buf;
648 	ns_ldap_attr_t **attrs = ldapbuf->attrs;
649 	struct passwd *pw;
650 	int pwd_status;
651 	uid_t uid;
652 
653 	if (strcmp(name, "root") == 0)
654 		return (PWU_NOT_FOUND);
655 
656 	/*
657 	 * The LDAP server checks whether we are permitted to perform
658 	 * the requested change. We need to send the name of the user
659 	 * who is executing this piece of code, together with his
660 	 * current password to the server.
661 	 * If this is executed by a normal user changing his/her own
662 	 * password, this will simply be the OLD password that is to
663 	 * be changed.
664 	 * Specific case if the user who is executing this piece
665 	 * of code is root. We will then issue the LDAP request
666 	 * with the DN of the user we want to change the passwd of.
667 	 */
668 
669 	/*
670 	 * convert name of user whose attributes we are changing
671 	 * to a distinguished name
672 	 */
673 	res = __ns_ldap_uid2dn(name, &dn, NULL, &errorp);
674 	if (res != NS_LDAP_SUCCESS)
675 		goto out;
676 
677 	/*
678 	 * create a dn for the user who is executing this code
679 	 */
680 	uid = getuid();
681 	if (uid == 0) {
682 		if ((pw = getpwnam_from(name, rep, REP_LDAP)) == NULL) {
683 			res = NS_LDAP_OP_FAILED;
684 			goto out;
685 		}
686 	} else if ((pw = getpwuid_from(uid, rep, REP_LDAP)) == NULL) {
687 		/*
688 		 * User executing this code is not known to the LDAP
689 		 * server. This operation is to be denied
690 		 */
691 		res = NS_LDAP_OP_FAILED;
692 		goto out;
693 	}
694 
695 	res = __ns_ldap_uid2dn(pw->pw_name, &binddn, NULL, &errorp);
696 	if (res != NS_LDAP_SUCCESS)
697 		goto out;
698 
699 	res = ldap_replaceattr(dn, attrs, binddn, oldpw,
700 			&pwd_status);
701 
702 out:
703 	while (*attrs) {
704 		free((*attrs)->attrvalue[0]);
705 		free(*attrs);
706 		attrs++;
707 	}
708 	if (ldapbuf->passwd) {
709 		(void) memset(ldapbuf->passwd, 0, strlen(ldapbuf->passwd));
710 		free(ldapbuf->passwd);
711 	}
712 	if (ldapbuf->pwd)
713 		free_pwd(ldapbuf->pwd);
714 	free(dn);
715 
716 	return (ldap_to_pwu_code(res, pwd_status));
717 }
718