summaryrefslogtreecommitdiff
path: root/usr/src/lib/passwdutil/ldap_attr.c
diff options
context:
space:
mode:
authorstevel@tonic-gate <none@none>2005-06-14 00:00:00 -0700
committerstevel@tonic-gate <none@none>2005-06-14 00:00:00 -0700
commit7c478bd95313f5f23a4c958a745db2134aa03244 (patch)
treec871e58545497667cbb4b0a4f2daf204743e1fe7 /usr/src/lib/passwdutil/ldap_attr.c
downloadillumos-joyent-7c478bd95313f5f23a4c958a745db2134aa03244.tar.gz
OpenSolaris Launch
Diffstat (limited to 'usr/src/lib/passwdutil/ldap_attr.c')
-rw-r--r--usr/src/lib/passwdutil/ldap_attr.c717
1 files changed, 717 insertions, 0 deletions
diff --git a/usr/src/lib/passwdutil/ldap_attr.c b/usr/src/lib/passwdutil/ldap_attr.c
new file mode 100644
index 0000000000..7cf40be7a1
--- /dev/null
+++ b/usr/src/lib/passwdutil/ldap_attr.c
@@ -0,0 +1,717 @@
+/*
+ * CDDL HEADER START
+ *
+ * The contents of this file are subject to the terms of the
+ * Common Development and Distribution License, Version 1.0 only
+ * (the "License"). You may not use this file except in compliance
+ * with the License.
+ *
+ * You can obtain a copy of the license at usr/src/OPENSOLARIS.LICENSE
+ * or http://www.opensolaris.org/os/licensing.
+ * See the License for the specific language governing permissions
+ * and limitations under the License.
+ *
+ * When distributing Covered Code, include this CDDL HEADER in each
+ * file and include the License file at usr/src/OPENSOLARIS.LICENSE.
+ * If applicable, add the following below this CDDL HEADER, with the
+ * fields enclosed by brackets "[]" replaced with your own identifying
+ * information: Portions Copyright [yyyy] [name of copyright owner]
+ *
+ * CDDL HEADER END
+ */
+/*
+ * Copyright 2004 Sun Microsystems, Inc. All rights reserved.
+ * Use is subject to license terms.
+ */
+
+#pragma ident "%Z%%M% %I% %E% SMI"
+
+#include <stdio.h>
+#include <errno.h>
+#include <stdlib.h>
+#include <string.h>
+#include <unistd.h>
+
+#include "ns_sldap.h"
+
+#include <nss_dbdefs.h>
+#include <nsswitch.h>
+
+#include <pwd.h>
+#include <shadow.h>
+#include <syslog.h>
+
+#include "passwdutil.h"
+
+#include "utils.h"
+
+int ldap_getattr(char *name, attrlist *item, pwu_repository_t *rep);
+int ldap_getpwnam(char *name, attrlist *items, pwu_repository_t *rep,
+ void **buf);
+int ldap_update(attrlist *items, pwu_repository_t *rep, void *buf);
+int ldap_putpwnam(char *name, char *oldpw, char *dummy,
+ pwu_repository_t *rep, void *buf);
+int ldap_user_to_authenticate(char *name, pwu_repository_t *rep,
+ char **auth_user, int *privileged);
+
+/*
+ * ldap function pointer table, used by passwdutil_init to initialize
+ * the global Repository-OPerations table "rops"
+ */
+struct repops ldap_repops = {
+ NULL, /* checkhistory */
+ ldap_getattr,
+ ldap_getpwnam,
+ ldap_update,
+ ldap_putpwnam,
+ ldap_user_to_authenticate,
+ NULL, /* lock */
+ NULL /* unlock */
+};
+
+/*
+ * structure used to keep state between get/update/put calls
+ */
+typedef struct {
+ char *passwd; /* encrypted password */
+ struct passwd *pwd;
+ ns_ldap_attr_t **attrs;
+} ldapbuf_t;
+
+/*
+ * The following define's are taken from
+ * usr/src/lib/nsswitch/ldap/common/getpwnam.c
+ */
+
+/* passwd attributes filters */
+#define _PWD_CN "cn"
+#define _PWD_UID "uid"
+#define _PWD_USERPASSWORD "userpassword"
+#define _PWD_UIDNUMBER "uidnumber"
+#define _PWD_GIDNUMBER "gidnumber"
+#define _PWD_GECOS "gecos"
+#define _PWD_DESCRIPTION "description"
+#define _PWD_HOMEDIRECTORY "homedirectory"
+#define _PWD_LOGINSHELL "loginshell"
+
+/*
+ * int ldap_user_to_authenticate(user, rep, auth_user, privileged)
+ *
+ * We can't determine whether the user is "privileged" in the LDAP
+ * sense. The operation should be attempted and will succeed if
+ * the user had privileges.
+ *
+ * For our purposes, we say that the user is privileged if he/she
+ * is attempting to change another user's password attributes.
+ */
+int
+ldap_user_to_authenticate(char *user, pwu_repository_t *rep,
+ char **auth_user, int *privileged)
+{
+ struct passwd *pw;
+ uid_t uid;
+ uid_t priviledged_uid;
+ int res = PWU_SUCCESS;
+
+ if (strcmp(user, "root") == 0)
+ return (PWU_NOT_FOUND);
+
+ if ((pw = getpwnam_from(user, rep, REP_LDAP)) == NULL)
+ return (PWU_NOT_FOUND);
+
+ uid = getuid();
+
+ if (uid == pw->pw_uid) {
+ /* changing out own, not privileged */
+ *privileged = 0;
+ if ((*auth_user = strdup(user)) == NULL)
+ res = PWU_NOMEM;
+ } else {
+ char pwd_buf[1024];
+ struct passwd pwr;
+
+ *privileged = 1;
+ /*
+ * specific case for root
+ * we want 'user' to be authenticated.
+ */
+ if (uid == 0) {
+ priviledged_uid = pw->pw_uid;
+ } else {
+ priviledged_uid = uid;
+ }
+ if (getpwuid_r(priviledged_uid, &pwr, pwd_buf,
+ sizeof (pwd_buf)) != NULL) {
+ if ((*auth_user = strdup(pwr.pw_name)) == NULL)
+ res = PWU_NOMEM;
+ } else {
+ /* hmm. can't find name of current user...??? */
+
+#define MAX_UID_LEN 11 /* UID's larger than 2^32 won't fit... */
+ if ((*auth_user = malloc(MAX_UID_LEN)) == NULL) {
+ res = PWU_NOMEM;
+ } else {
+ (void) snprintf(*auth_user, MAX_UID_LEN, "%d",
+ (int)uid);
+ }
+ }
+ }
+
+ return (res);
+}
+
+/*
+ * int ldap_getattr(name, item, rep)
+ *
+ * retrieve attributes specified in "item" for user "name".
+ */
+/*ARGSUSED*/
+int
+ldap_getattr(char *name, attrlist *items, pwu_repository_t *rep)
+{
+ int res;
+ struct passwd *pw = NULL;
+ struct spwd *spw = NULL;
+ attrlist *w;
+
+ int need_shadow = 0; /* Need shadow info from LDAP server */
+ int need_normal = 0; /* Need non-shadow info from LDAP server */
+
+ /* We need the "shadow" map for the password only */
+ for (w = items; w != NULL; w = w->next) {
+ if (w->type == ATTR_PASSWD ||
+ w->type == ATTR_PASSWD_SERVER_POLICY)
+ need_shadow = 1;
+ else
+ need_normal = 1;
+ }
+
+ if (need_normal) {
+ res = dup_pw(&pw, getpwnam_from(name, rep, REP_LDAP));
+ if (res != PWU_SUCCESS)
+ goto out;
+ }
+
+ if (need_shadow) {
+ res = dup_spw(&spw, getspnam_from(name, rep, REP_LDAP));
+ if (res != PWU_SUCCESS) {
+ goto out;
+ }
+ }
+
+ for (w = items; res == PWU_SUCCESS && w != NULL; w = w->next) {
+ switch (w->type) {
+ case ATTR_NAME:
+ if ((w->data.val_s = strdup(pw->pw_name)) == NULL)
+ res = PWU_NOMEM;
+ break;
+ case ATTR_COMMENT:
+ if ((w->data.val_s = strdup(pw->pw_comment)) == NULL)
+ res = PWU_NOMEM;
+ break;
+ case ATTR_GECOS:
+ if ((w->data.val_s = strdup(pw->pw_gecos)) == NULL)
+ res = PWU_NOMEM;
+ break;
+ case ATTR_HOMEDIR:
+ if ((w->data.val_s = strdup(pw->pw_dir)) == NULL)
+ res = PWU_NOMEM;
+ break;
+ case ATTR_SHELL:
+ if ((w->data.val_s = strdup(pw->pw_shell)) == NULL)
+ res = PWU_NOMEM;
+ break;
+ case ATTR_PASSWD:
+ case ATTR_PASSWD_SERVER_POLICY:
+ if ((w->data.val_s = strdup(spw->sp_pwdp)) == NULL)
+ res = PWU_NOMEM;
+ break;
+ case ATTR_AGE:
+ if ((w->data.val_s = strdup(pw->pw_age)) == NULL)
+ res = PWU_NOMEM;
+ break;
+ case ATTR_REP_NAME:
+ if ((w->data.val_s = strdup("ldap")) == NULL)
+ res = PWU_NOMEM;
+ break;
+
+ /* integer values */
+ case ATTR_UID:
+ w->data.val_i = pw->pw_uid;
+ break;
+ case ATTR_GID:
+ w->data.val_i = pw->pw_gid;
+ break;
+ case ATTR_LSTCHG:
+ w->data.val_i = -1;
+ break;
+ case ATTR_MIN:
+ w->data.val_i = -1;
+ break;
+ case ATTR_MAX:
+ w->data.val_i = -1;
+ break;
+ case ATTR_WARN:
+ w->data.val_i = -1;
+ break;
+ case ATTR_INACT:
+ w->data.val_i = -1;
+ break;
+ case ATTR_EXPIRE:
+ w->data.val_i = -1;
+ break;
+ case ATTR_FLAG:
+ break;
+ default:
+ break;
+ }
+ }
+
+out:
+ if (pw)
+ free_pwd(pw);
+ if (spw)
+ free_spwd(spw);
+
+ return (res);
+}
+
+/*
+ * int ldap_getpwnam(name, items, rep, buf)
+ *
+ * There is no need to get the old values from the ldap
+ * server, as the update will update each item individually.
+ * Therefore, we only allocate a buffer that will be used by
+ * _update and _putpwnam to hold the attributes to update.
+ *
+ * Only when we're about to update a password, we need to retrieve
+ * the old password since it contains salt-information.
+ */
+/*ARGSUSED*/
+int
+ldap_getpwnam(char *name, attrlist *items, pwu_repository_t *rep,
+ void **buf)
+{
+ attrlist *p;
+ int nr_items;
+ int need_pwd = 0;
+ ldapbuf_t *ldapbuf;
+ int res;
+
+ for (nr_items = 0, p = items; p != NULL; p = p->next) {
+ nr_items++;
+ if (p->type == ATTR_PASSWD ||
+ p->type == ATTR_PASSWD_SERVER_POLICY)
+ need_pwd = 1;
+ }
+
+
+ ldapbuf = calloc(1, sizeof (ldapbuf_t));
+ if (ldapbuf == NULL)
+ return (PWU_NOMEM);
+
+ ldapbuf->attrs = calloc(nr_items, sizeof (ns_ldap_attr_t *));
+ if (ldapbuf->attrs == NULL)
+ return (PWU_NOMEM);
+
+ if (need_pwd) {
+ struct spwd *spw;
+
+ res = dup_pw(&ldapbuf->pwd, getpwnam_from(name, rep, REP_LDAP));
+ if (res != PWU_SUCCESS)
+ return (res);
+
+ spw = getspnam_from(name, rep, REP_LDAP);
+ if (spw) {
+ ldapbuf->passwd = strdup(spw->sp_pwdp);
+ if (ldapbuf->passwd == NULL)
+ return (PWU_NOMEM);
+ }
+ }
+
+ *buf = ldapbuf;
+ return (0);
+}
+
+/*
+ * new_attr(name, value)
+ *
+ * create a new LDAP attribute to be sent to the server
+ */
+ns_ldap_attr_t *
+new_attr(char *name, char *value)
+{
+ ns_ldap_attr_t *tmp;
+
+ tmp = malloc(sizeof (*tmp));
+ if (tmp != NULL) {
+ tmp->attrname = name;
+ tmp->attrvalue = (char **)calloc(2, sizeof (char *));
+ if (tmp->attrvalue == NULL) {
+ free(tmp);
+ return (NULL);
+ }
+ tmp->attrvalue[0] = value;
+ tmp->value_count = 1;
+ }
+
+ return (tmp);
+}
+
+/*
+ * ldap_update(items, rep, buf)
+ *
+ * create LDAP attributes in 'buf' for each attribute in 'items'.
+ */
+/*ARGSUSED*/
+int
+ldap_update(attrlist *items, pwu_repository_t *rep, void *buf)
+{
+ attrlist *p;
+ int idx = 0;
+ ldapbuf_t *ldapbuf = (ldapbuf_t *)buf;
+ ns_ldap_attr_t **attrs = ldapbuf->attrs;
+ char *pwd, *val;
+ char *salt;
+ size_t cryptlen;
+
+ for (p = items; p != NULL; p = p->next) {
+ switch (p->type) {
+ case ATTR_PASSWD:
+ salt = crypt_gensalt(ldapbuf->passwd, ldapbuf->pwd);
+
+ if (salt == NULL) {
+ if (errno == ENOMEM)
+ return (PWU_NOMEM);
+ else {
+ /* algorithm problem? */
+ syslog(LOG_AUTH | LOG_ALERT,
+ "passwdutil: crypt_gensalt "
+ "%m");
+ return (PWU_UPDATE_FAILED);
+ }
+ }
+
+ pwd = crypt(p->data.val_s, salt);
+ free(salt);
+ cryptlen = strlen(pwd) + sizeof ("{crypt}");
+ val = malloc(cryptlen);
+ if (val == NULL)
+ return (PWU_NOMEM);
+ (void) snprintf(val, cryptlen, "{crypt}%s", pwd);
+
+ attrs[idx] = new_attr(_PWD_USERPASSWORD, val);
+ break;
+ /*
+ * For server policy, don't crypt the password,
+ * send the password as is to the server and
+ * let the LDAP server do its own password
+ * encryption
+ */
+ case ATTR_PASSWD_SERVER_POLICY:
+ val = strdup(p->data.val_s);
+ if (val == NULL)
+ return (PWU_NOMEM);
+
+ attrs[idx] = new_attr(_PWD_USERPASSWORD, val);
+ break;
+ case ATTR_COMMENT:
+ /* XX correct? */
+ attrs[idx] = new_attr(_PWD_DESCRIPTION, p->data.val_s);
+ break;
+ case ATTR_GECOS:
+ attrs[idx] = new_attr(_PWD_GECOS, p->data.val_s);
+ break;
+ case ATTR_HOMEDIR:
+ attrs[idx] = new_attr(_PWD_HOMEDIRECTORY,
+ p->data.val_s);
+ break;
+ case ATTR_SHELL:
+ attrs[idx] = new_attr(_PWD_LOGINSHELL, p->data.val_s);
+ break;
+ /* Unsupported items are below this line */
+ case ATTR_NAME:
+ case ATTR_UID:
+ case ATTR_GID:
+ case ATTR_AGE:
+ case ATTR_LSTCHG:
+ case ATTR_MIN:
+ case ATTR_MAX:
+ case ATTR_WARN:
+ case ATTR_INACT:
+ case ATTR_EXPIRE:
+ case ATTR_FLAG:
+ break;
+ default:
+ break;
+ }
+ if (attrs[idx] == NULL)
+ return (PWU_NOMEM);
+ idx++;
+ }
+
+ attrs[idx] = NULL;
+
+ return (PWU_SUCCESS);
+}
+
+/*
+ * ldap_to_pwu_code(error, pwd_status)
+ *
+ * translation from LDAP return values and PWU return values
+ */
+int
+ldap_to_pwu_code(int error, int pwd_status)
+{
+ switch (error) {
+ case NS_LDAP_SUCCESS: return (PWU_SUCCESS);
+ case NS_LDAP_OP_FAILED: return (PWU_DENIED);
+ case NS_LDAP_NOTFOUND: return (PWU_NOT_FOUND);
+ case NS_LDAP_MEMORY: return (PWU_NOMEM);
+ case NS_LDAP_CONFIG: return (PWU_NOT_FOUND);
+ case NS_LDAP_INTERNAL:
+ switch (pwd_status) {
+ case NS_PASSWD_EXPIRED:
+ return (PWU_DENIED);
+ case NS_PASSWD_CHANGE_NOT_ALLOWED:
+ return (PWU_CHANGE_NOT_ALLOWED);
+ case NS_PASSWD_TOO_SHORT:
+ return (PWU_PWD_TOO_SHORT);
+ case NS_PASSWD_INVALID_SYNTAX:
+ return (PWU_PWD_INVALID);
+ case NS_PASSWD_IN_HISTORY:
+ return (PWU_PWD_IN_HISTORY);
+ case NS_PASSWD_WITHIN_MIN_AGE:
+ return (PWU_WITHIN_MIN_AGE);
+ default:
+ return (PWU_SYSTEM_ERROR);
+ }
+ default: return (PWU_SYSTEM_ERROR);
+ }
+}
+
+int
+ldap_replaceattr(const char *dn, ns_ldap_attr_t **attrs, const char *binddn,
+ const char *pwd, int *pwd_status)
+{
+ int result = NS_LDAP_OP_FAILED;
+ int ldaprc;
+ int authstried = 0;
+ char **certpath = NULL;
+ ns_auth_t **app;
+ ns_auth_t **authpp = NULL;
+ ns_auth_t *authp = NULL;
+ ns_cred_t *credp;
+ ns_ldap_error_t *errorp = NULL;
+
+ debug("%s: replace_ldapattr()", __FILE__);
+
+ if ((credp = (ns_cred_t *)calloc(1, sizeof (ns_cred_t))) == NULL)
+ return (PWU_NOMEM);
+
+ /* Fill in the user name and password */
+ if (dn == NULL || pwd == NULL)
+ goto out;
+
+ credp->cred.unix_cred.userID = strdup(binddn);
+ credp->cred.unix_cred.passwd = strdup(pwd);
+
+ /* get host certificate path, if one is configured */
+ ldaprc = __ns_ldap_getParam(NS_LDAP_HOST_CERTPATH_P,
+ (void ***)&certpath, &errorp);
+ if (ldaprc != NS_LDAP_SUCCESS)
+ goto out;
+
+ if (certpath && *certpath)
+ credp->hostcertpath = *certpath;
+
+ /* Load the service specific authentication method */
+ ldaprc = __ns_ldap_getServiceAuthMethods("passwd-cmd", &authpp,
+ &errorp);
+
+ if (ldaprc != NS_LDAP_SUCCESS)
+ goto out;
+
+ /*
+ * if authpp is null, there is no serviceAuthenticationMethod
+ * try default authenticationMethod
+ */
+ if (authpp == NULL) {
+ ldaprc = __ns_ldap_getParam(NS_LDAP_AUTH_P, (void ***)&authpp,
+ &errorp);
+ if (ldaprc != NS_LDAP_SUCCESS)
+ goto out;
+ }
+
+ /*
+ * if authpp is still null, then can not authenticate, syslog
+ * error message and return error
+ */
+ if (authpp == NULL) {
+ syslog(LOG_ERR,
+ "passwdutil: no legal LDAP authentication method configured");
+ result = NS_LDAP_OP_FAILED;
+ goto out;
+ }
+
+ /*
+ * Walk the array and try all authentication methods in order except
+ * for "none".
+ */
+ for (app = authpp; *app; app++) {
+ authp = *app;
+ /* what about disabling other mechanisms? "tls:sasl/EXTERNAL" */
+ if (authp->type == NS_LDAP_AUTH_NONE)
+ continue;
+ authstried++;
+ credp->auth.type = authp->type;
+ credp->auth.tlstype = authp->tlstype;
+ credp->auth.saslmech = authp->saslmech;
+ credp->auth.saslopt = authp->saslopt;
+
+ ldaprc = __ns_ldap_repAttr("shadow", dn,
+ (const ns_ldap_attr_t * const *)attrs,
+ credp, 0, &errorp);
+ if (ldaprc == NS_LDAP_SUCCESS) {
+ result = NS_LDAP_SUCCESS;
+ goto out;
+ }
+
+ /*
+ * other errors might need to be added to this list, for
+ * the current supported mechanisms this is sufficient
+ */
+ if ((ldaprc == NS_LDAP_INTERNAL) &&
+ (errorp->pwd_mgmt.status == NS_PASSWD_GOOD) &&
+ ((errorp->status == LDAP_INAPPROPRIATE_AUTH) ||
+ (errorp->status == LDAP_INVALID_CREDENTIALS))) {
+ result = ldaprc;
+ goto out;
+ }
+
+ /*
+ * If there is error related to password policy,
+ * return it to caller
+ */
+ if ((ldaprc == NS_LDAP_INTERNAL) &&
+ errorp->pwd_mgmt.status != NS_PASSWD_GOOD) {
+ *pwd_status = errorp->pwd_mgmt.status;
+ result = ldaprc;
+ goto out;
+ } else
+ *pwd_status = NS_PASSWD_GOOD;
+
+ /* we don't really care about the error, just clean it up */
+ if (errorp)
+ (void) __ns_ldap_freeError(&errorp);
+ }
+ if (authstried == 0) {
+ syslog(LOG_ERR,
+ "passwdutil: no legal LDAP authentication method configured");
+ result = NS_LDAP_CONFIG;
+ goto out;
+ }
+ result = PWU_DENIED;
+
+out:
+ if (credp)
+ (void) __ns_ldap_freeCred(&credp);
+
+ if (authpp)
+ (void) __ns_ldap_freeParam((void ***)&authpp);
+
+ if (errorp)
+ (void) __ns_ldap_freeError(&errorp);
+
+ return (result);
+}
+
+
+
+/*
+ * ldap_putpwnam(name, oldpw, dummy, rep, buf)
+ *
+ * update the LDAP server with the attributes contained in 'buf'.
+ * The dummy parameter is a placeholder for NIS+ where the old
+ * RPC password is passwd.
+ */
+/*ARGSUSED*/
+int
+ldap_putpwnam(char *name, char *oldpw, char *dummy,
+ pwu_repository_t *rep, void *buf)
+{
+ int res;
+ char *dn; /* dn of user whose attributes we are changing */
+ char *binddn; /* dn of user who is performing the change */
+ ns_ldap_error_t *errorp;
+ ldapbuf_t *ldapbuf = (ldapbuf_t *)buf;
+ ns_ldap_attr_t **attrs = ldapbuf->attrs;
+ struct passwd *pw;
+ int pwd_status;
+ uid_t uid;
+
+ if (strcmp(name, "root") == 0)
+ return (PWU_NOT_FOUND);
+
+ /*
+ * The LDAP server checks whether we are permitted to perform
+ * the requested change. We need to send the name of the user
+ * who is executing this piece of code, together with his
+ * current password to the server.
+ * If this is executed by a normal user changing his/her own
+ * password, this will simply be the OLD password that is to
+ * be changed.
+ * Specific case if the user who is executing this piece
+ * of code is root. We will then issue the LDAP request
+ * with the DN of the user we want to change the passwd of.
+ */
+
+ /*
+ * convert name of user whose attributes we are changing
+ * to a distinguished name
+ */
+ res = __ns_ldap_uid2dn(name, &dn, NULL, &errorp);
+ if (res != NS_LDAP_SUCCESS)
+ goto out;
+
+ /*
+ * create a dn for the user who is executing this code
+ */
+ uid = getuid();
+ if (uid == 0) {
+ if ((pw = getpwnam_from(name, rep, REP_LDAP)) == NULL) {
+ res = NS_LDAP_OP_FAILED;
+ goto out;
+ }
+ } else if ((pw = getpwuid_from(uid, rep, REP_LDAP)) == NULL) {
+ /*
+ * User executing this code is not known to the LDAP
+ * server. This operation is to be denied
+ */
+ res = NS_LDAP_OP_FAILED;
+ goto out;
+ }
+
+ res = __ns_ldap_uid2dn(pw->pw_name, &binddn, NULL, &errorp);
+ if (res != NS_LDAP_SUCCESS)
+ goto out;
+
+ res = ldap_replaceattr(dn, attrs, binddn, oldpw,
+ &pwd_status);
+
+out:
+ while (*attrs) {
+ free((*attrs)->attrvalue[0]);
+ free(*attrs);
+ attrs++;
+ }
+ if (ldapbuf->passwd) {
+ (void) memset(ldapbuf->passwd, 0, strlen(ldapbuf->passwd));
+ free(ldapbuf->passwd);
+ }
+ if (ldapbuf->pwd)
+ free_pwd(ldapbuf->pwd);
+ free(dn);
+
+ return (ldap_to_pwu_code(res, pwd_status));
+}