summaryrefslogtreecommitdiff
path: root/usr/src/lib/passwdutil/ldap_attr.c
diff options
context:
space:
mode:
authorMichen Chang <Michen.Chang@Sun.COM>2009-02-13 18:18:56 -0800
committerMichen Chang <Michen.Chang@Sun.COM>2009-02-13 18:18:56 -0800
commitdd1104fbe0f0f41434502f335b9f0b34999f771c (patch)
tree57c95d64bc40cebf2b145329e1f49e811352d502 /usr/src/lib/passwdutil/ldap_attr.c
parentd68ef20e3fe871e73146fc684d29d335521dcd99 (diff)
downloadillumos-joyent-dd1104fbe0f0f41434502f335b9f0b34999f771c.tar.gz
PSARC 2008/745 nss_ldap shadowAccount support
6715171 nss_ldap and passwdutil do not support all shadowAccount attributes 6797378 'ldapaddent -d passwd' does not print 'x' for the password field 6783712 libsldap fails to set correct version number for V1 profile
Diffstat (limited to 'usr/src/lib/passwdutil/ldap_attr.c')
-rw-r--r--usr/src/lib/passwdutil/ldap_attr.c860
1 files changed, 683 insertions, 177 deletions
diff --git a/usr/src/lib/passwdutil/ldap_attr.c b/usr/src/lib/passwdutil/ldap_attr.c
index 7cf40be7a1..7a29614bf3 100644
--- a/usr/src/lib/passwdutil/ldap_attr.c
+++ b/usr/src/lib/passwdutil/ldap_attr.c
@@ -2,9 +2,8 @@
* 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.
+ * Common Development and Distribution License (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.
@@ -20,17 +19,17 @@
* CDDL HEADER END
*/
/*
- * Copyright 2004 Sun Microsystems, Inc. All rights reserved.
+ * Copyright 2009 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 <macros.h>
+#include <priv.h>
#include "ns_sldap.h"
@@ -45,6 +44,32 @@
#include "utils.h"
+#define MAX_INT_LEN 11 /* 10+1 %d buflen for words/ints [not longs] */
+
+#define STRDUP_OR_RET(to, from) \
+ if ((to = strdup(from)) == NULL) \
+ return (PWU_NOMEM);
+
+#define STRDUP_OR_ERR(to, from, err) \
+ if (((to) = strdup(from)) == NULL) \
+ (err) = PWU_NOMEM;
+
+#define NUM_TO_STR(to, from) \
+ { \
+ char nb[MAX_INT_LEN]; \
+ if (snprintf(nb, MAX_INT_LEN, "%d", (from)) >= MAX_INT_LEN) \
+ return (PWU_NOMEM); \
+ STRDUP_OR_RET(to, nb); \
+ }
+
+#define NEW_ATTR(p, i, attr, val) \
+ { \
+ p[i] = new_attr(attr, (val)); \
+ if (p[i] == NULL) \
+ return (PWU_NOMEM); \
+ i++; \
+ }
+
int ldap_getattr(char *name, attrlist *item, pwu_repository_t *rep);
int ldap_getpwnam(char *name, attrlist *items, pwu_repository_t *rep,
void **buf);
@@ -73,9 +98,14 @@ struct repops ldap_repops = {
* structure used to keep state between get/update/put calls
*/
typedef struct {
- char *passwd; /* encrypted password */
+ char *passwd; /* encrypted password */
struct passwd *pwd;
- ns_ldap_attr_t **attrs;
+ ns_ldap_attr_t **pattrs; /* passwd attrs */
+ int npattrs; /* max attrs */
+ struct spwd *spwd;
+ ns_ldap_attr_t **sattrs; /* passwd attrs */
+ int nsattrs; /* max attrs */
+ boolean_t shadow_update_enabled; /* shadow update configured */
} ldapbuf_t;
/*
@@ -94,15 +124,70 @@ typedef struct {
#define _PWD_HOMEDIRECTORY "homedirectory"
#define _PWD_LOGINSHELL "loginshell"
+#define _PWD_MAX_ATTR 10 /* 9+NULL */
+
+/* shadow attributes filters */
+#define _S_LASTCHANGE "shadowlastchange"
+#define _S_MIN "shadowmin"
+#define _S_MAX "shadowmax"
+#define _S_WARNING "shadowwarning"
+#define _S_INACTIVE "shadowinactive"
+#define _S_EXPIRE "shadowexpire"
+#define _S_FLAG "shadowflag"
+
+#define _S_MAX_ATTR 8 /* 7+NULL */
+
+/*
+ * Frees up an ldapbuf_t
+ */
+
+static void
+free_ldapbuf(ldapbuf_t *p)
+{
+ int i;
+
+ if (p == NULL)
+ return;
+ if (p->passwd) {
+ (void) memset(p->passwd, 0, strlen(p->passwd));
+ free(p->passwd);
+ }
+ if (p->pwd)
+ free_pwd(p->pwd);
+ if (p->spwd)
+ free_spwd(p->spwd);
+ if (p->pattrs) {
+ for (i = 0; i < p->npattrs; i++) {
+ if (p->pattrs[i] != NULL) {
+ free(p->pattrs[i]->attrvalue[0]);
+ free(p->pattrs[i]);
+ }
+ }
+ free(p->pattrs);
+ }
+ if (p->sattrs) {
+ for (i = 0; i < p->nsattrs; i++) {
+ if (p->sattrs[i] != NULL) {
+ free(p->sattrs[i]->attrvalue[0]);
+ free(p->sattrs[i]);
+ }
+ }
+ free(p->sattrs);
+ }
+}
+
/*
* 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.
+ * If the Shadow Update functionality is enabled, then we check to
+ * see if the caller has 0 as the euid or has all zone privs. If so,
+ * the caller would be able to modify shadow(4) data stored on the
+ * LDAP server. Otherwise, when LDAP Shadow Update is not enabled,
+ * 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,
@@ -121,11 +206,47 @@ ldap_user_to_authenticate(char *user, pwu_repository_t *rep,
uid = getuid();
+ /*
+ * need equivalent of write access to /etc/shadow
+ * the privilege escalation model is euid == 0 || all zone privs
+ */
+ if (__ns_ldap_is_shadow_update_enabled()) {
+ boolean_t priv;
+
+ priv = (geteuid() == 0);
+ if (!priv) {
+ priv_set_t *ps = priv_allocset(); /* caller */
+ priv_set_t *zs; /* zone */
+
+ (void) getppriv(PRIV_EFFECTIVE, ps);
+ zs = priv_str_to_set("zone", ",", NULL);
+ priv = priv_isequalset(ps, zs);
+ priv_freeset(ps);
+ priv_freeset(zs);
+ }
+ /*
+ * priv can change anyone's password,
+ * only root isn't prompted.
+ */
+ *privileged = 0; /* for proper prompting */
+ if (priv) {
+ if (uid == 0) {
+ *privileged = 1;
+ *auth_user = NULL;
+ return (res);
+ } else if (uid == pw->pw_uid) {
+ STRDUP_OR_ERR(*auth_user, user, res);
+ return (res);
+ }
+ }
+
+ return (PWU_DENIED);
+ }
+
if (uid == pw->pw_uid) {
- /* changing out own, not privileged */
+ /* changing our own, not privileged */
*privileged = 0;
- if ((*auth_user = strdup(user)) == NULL)
- res = PWU_NOMEM;
+ STRDUP_OR_RET(*auth_user, user);
} else {
char pwd_buf[1024];
struct passwd pwr;
@@ -141,17 +262,15 @@ ldap_user_to_authenticate(char *user, pwu_repository_t *rep,
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;
+ sizeof (pwd_buf)) != NULL) {
+ STRDUP_OR_ERR(*auth_user, pwr.pw_name, res);
} 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) {
+ if ((*auth_user = malloc(MAX_INT_LEN)) == NULL) {
res = PWU_NOMEM;
} else {
- (void) snprintf(*auth_user, MAX_UID_LEN, "%d",
+ (void) snprintf(*auth_user, MAX_INT_LEN, "%d",
(int)uid);
}
}
@@ -169,70 +288,45 @@ ldap_user_to_authenticate(char *user, pwu_repository_t *rep,
int
ldap_getattr(char *name, attrlist *items, pwu_repository_t *rep)
{
+ attrlist *w;
int res;
+ ldapbuf_t *ldapbuf;
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 */
+ res = ldap_getpwnam(name, items, rep, (void **)&ldapbuf);
+ if (res != PWU_SUCCESS)
+ return (res);
- /* 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;
- }
- }
+ pw = ldapbuf->pwd;
+ spw = ldapbuf->spwd;
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;
+ STRDUP_OR_ERR(w->data.val_s, pw->pw_name, res);
break;
case ATTR_COMMENT:
- if ((w->data.val_s = strdup(pw->pw_comment)) == NULL)
- res = PWU_NOMEM;
+ STRDUP_OR_ERR(w->data.val_s, pw->pw_comment, res);
break;
case ATTR_GECOS:
- if ((w->data.val_s = strdup(pw->pw_gecos)) == NULL)
- res = PWU_NOMEM;
+ STRDUP_OR_ERR(w->data.val_s, pw->pw_gecos, res);
break;
case ATTR_HOMEDIR:
- if ((w->data.val_s = strdup(pw->pw_dir)) == NULL)
- res = PWU_NOMEM;
+ STRDUP_OR_ERR(w->data.val_s, pw->pw_dir, res);
break;
case ATTR_SHELL:
- if ((w->data.val_s = strdup(pw->pw_shell)) == NULL)
- res = PWU_NOMEM;
+ STRDUP_OR_ERR(w->data.val_s, pw->pw_shell, res);
break;
case ATTR_PASSWD:
case ATTR_PASSWD_SERVER_POLICY:
- if ((w->data.val_s = strdup(spw->sp_pwdp)) == NULL)
- res = PWU_NOMEM;
+ STRDUP_OR_ERR(w->data.val_s, spw->sp_pwdp, res);
break;
case ATTR_AGE:
- if ((w->data.val_s = strdup(pw->pw_age)) == NULL)
- res = PWU_NOMEM;
+ STRDUP_OR_ERR(w->data.val_s, pw->pw_age, res);
break;
case ATTR_REP_NAME:
- if ((w->data.val_s = strdup("ldap")) == NULL)
- res = PWU_NOMEM;
+ STRDUP_OR_ERR(w->data.val_s, "ldap", res);
break;
/* integer values */
@@ -243,24 +337,47 @@ ldap_getattr(char *name, attrlist *items, pwu_repository_t *rep)
w->data.val_i = pw->pw_gid;
break;
case ATTR_LSTCHG:
- w->data.val_i = -1;
+ if (ldapbuf->shadow_update_enabled)
+ w->data.val_i = spw->sp_lstchg;
+ else
+ w->data.val_i = -1;
break;
case ATTR_MIN:
- w->data.val_i = -1;
+ if (ldapbuf->shadow_update_enabled)
+ w->data.val_i = spw->sp_min;
+ else
+ w->data.val_i = -1;
break;
case ATTR_MAX:
- w->data.val_i = -1;
+ if (ldapbuf->shadow_update_enabled)
+ w->data.val_i = spw->sp_max;
+ else
+ w->data.val_i = -1;
break;
case ATTR_WARN:
- w->data.val_i = -1;
+ if (ldapbuf->shadow_update_enabled)
+ w->data.val_i = spw->sp_warn;
+ else
+ w->data.val_i = -1;
break;
case ATTR_INACT:
- w->data.val_i = -1;
+ if (ldapbuf->shadow_update_enabled)
+ w->data.val_i = spw->sp_inact;
+ else
+ w->data.val_i = -1;
break;
case ATTR_EXPIRE:
- w->data.val_i = -1;
+ if (ldapbuf->shadow_update_enabled)
+ w->data.val_i = spw->sp_expire;
+ else
+ w->data.val_i = -1;
break;
case ATTR_FLAG:
+ if (ldapbuf->shadow_update_enabled)
+ w->data.val_i = spw->sp_flag;
+ break;
+ case ATTR_FAILED_LOGINS:
+ w->data.val_i = spw->sp_flag & FAILCOUNT_MASK;
break;
default:
break;
@@ -268,11 +385,8 @@ ldap_getattr(char *name, attrlist *items, pwu_repository_t *rep)
}
out:
- if (pw)
- free_pwd(pw);
- if (spw)
- free_spwd(spw);
-
+ free_ldapbuf(ldapbuf);
+ free(ldapbuf);
return (res);
}
@@ -292,45 +406,54 @@ 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;
- }
+ int res = PWU_NOMEM;
+ /*
+ * [sp]attrs is treated as NULL terminated
+ */
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);
+ ldapbuf->pattrs = calloc(_PWD_MAX_ATTR, sizeof (ns_ldap_attr_t *));
+ if (ldapbuf->pattrs == NULL)
+ goto out;
+ ldapbuf->npattrs = _PWD_MAX_ATTR;
- if (need_pwd) {
- struct spwd *spw;
+ ldapbuf->sattrs = calloc(_S_MAX_ATTR, sizeof (ns_ldap_attr_t *));
+ if (ldapbuf->sattrs == NULL)
+ goto out;
+ ldapbuf->nsattrs = _S_MAX_ATTR;
- res = dup_pw(&ldapbuf->pwd, getpwnam_from(name, rep, REP_LDAP));
- if (res != PWU_SUCCESS)
- return (res);
+ res = dup_pw(&ldapbuf->pwd, getpwnam_from(name, rep, REP_LDAP));
+ if (res != PWU_SUCCESS)
+ goto out;
- spw = getspnam_from(name, rep, REP_LDAP);
- if (spw) {
- ldapbuf->passwd = strdup(spw->sp_pwdp);
+ res = dup_spw(&ldapbuf->spwd, getspnam_from(name, rep, REP_LDAP));
+ if (res != PWU_SUCCESS)
+ goto out;
+ else {
+ char *spw = ldapbuf->spwd->sp_pwdp;
+ if (spw != NULL && *spw != '\0') {
+ ldapbuf->passwd = strdup(spw);
if (ldapbuf->passwd == NULL)
- return (PWU_NOMEM);
- }
+ goto out;
+ } else
+ ldapbuf->passwd = NULL;
}
- *buf = ldapbuf;
- return (0);
+ /* remember if shadow update is enabled */
+ ldapbuf->shadow_update_enabled = __ns_ldap_is_shadow_update_enabled();
+
+ *buf = (void *)ldapbuf;
+ return (PWU_SUCCESS);
+
+out:
+ free_ldapbuf(ldapbuf);
+ free(ldapbuf);
+ return (res);
}
/*
@@ -359,6 +482,63 @@ new_attr(char *name, char *value)
}
/*
+ * max_present(list)
+ *
+ * returns '1' if a ATTR_MAX with value != -1 is present. (in other words:
+ * if password aging is to be turned on).
+ */
+static int
+max_present(attrlist *list)
+{
+ while (list != NULL)
+ if (list->type == ATTR_MAX && list->data.val_i != -1)
+ return (1);
+ else
+ list = list->next;
+ return (0);
+}
+
+/*
+ * attr_addmod(attrs, idx, item, val)
+ *
+ * Adds or updates attribute 'item' in ldap_attrs list to value
+ * update idx if item is added
+ * return: -1 - PWU_NOMEM/error, 0 - success
+ */
+static int
+attr_addmod(ns_ldap_attr_t **attrs, int *idx, char *item, int value)
+{
+ char numbuf[MAX_INT_LEN], *strp;
+ int i;
+
+ /* stringize the value or abort */
+ if (snprintf(numbuf, MAX_INT_LEN, "%d", value) >= MAX_INT_LEN)
+ return (-1);
+
+ /* check for existence and modify existing */
+ for (i = 0; i < *idx; i++) {
+ if (attrs[i] != NULL &&
+ strcmp(item, attrs[i]->attrname) == 0) {
+ strp = strdup(numbuf);
+ if (strp == NULL)
+ return (-1);
+ free(attrs[i]->attrvalue[0]);
+ attrs[i]->attrvalue[0] = strp;
+ return (0);
+ }
+ }
+ /* else add */
+ strp = strdup(numbuf);
+ if (strp == NULL)
+ return (-1);
+ attrs[*idx] = new_attr(item, strp);
+ if (attrs[*idx] == NULL)
+ return (-1);
+ (*idx)++;
+ return (0);
+}
+
+/*
* ldap_update(items, rep, buf)
*
* create LDAP attributes in 'buf' for each attribute in 'items'.
@@ -368,91 +548,399 @@ 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;
+ struct spwd *spw;
+ ns_ldap_attr_t **pattrs = ldapbuf->pattrs;
+ int pidx = 0;
+ ns_ldap_attr_t **sattrs = ldapbuf->sattrs;
+ int sidx = 0;
char *pwd, *val;
char *salt;
size_t cryptlen;
+ int len;
+ int count;
+ int rc = PWU_SUCCESS;
+ int aging_needed = 0;
+ int aging_set = 0;
+ int disable_aging;
+
+ spw = ldapbuf->spwd;
+
+ /*
+ * if sp_max==0 and shadow update is enabled:
+ * disable passwd aging after updating the password
+ */
+ disable_aging = (spw != NULL && spw->sp_max == 0 &&
+ ldapbuf->shadow_update_enabled);
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)
+ /*
+ * There is a special case for ldap: if the
+ * password is to be deleted (-d to passwd),
+ * p->data.val_s will be NULL.
+ */
+ if (p->data.val_s == NULL) {
+ if (!ldapbuf->shadow_update_enabled)
+ return (PWU_CHANGE_NOT_ALLOWED);
+ cryptlen =
+ sizeof ("{crypt}" NS_LDAP_NO_UNIX_PASSWORD);
+ val = malloc(cryptlen);
+ if (val == NULL)
return (PWU_NOMEM);
- else {
+ (void) snprintf(val, cryptlen,
+ "{crypt}" NS_LDAP_NO_UNIX_PASSWORD);
+ } else { /* not deleting password */
+ salt = crypt_gensalt(ldapbuf->passwd,
+ ldapbuf->pwd);
+
+ if (salt == NULL) {
+ if (errno == ENOMEM)
+ return (PWU_NOMEM);
+
/* 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);
+ }
+
+ /*
+ * If not managing passwordAccount,
+ * insert the new password in the
+ * passwd attr array and break.
+ */
+ if (!ldapbuf->shadow_update_enabled) {
+ NEW_ATTR(pattrs, pidx,
+ _PWD_USERPASSWORD, val);
+ break;
}
- pwd = crypt(p->data.val_s, salt);
- free(salt);
- cryptlen = strlen(pwd) + sizeof ("{crypt}");
- val = malloc(cryptlen);
- if (val == NULL)
+ /*
+ * Managing passwordAccount, insert the
+ * new password, along with lastChange and
+ * shadowFlag, in the shadow attr array.
+ */
+ NEW_ATTR(sattrs, sidx, _PWD_USERPASSWORD, val);
+
+ if (attr_addmod(sattrs, &sidx, _S_LASTCHANGE,
+ DAY_NOW_32) < 0)
return (PWU_NOMEM);
- (void) snprintf(val, cryptlen, "{crypt}%s", pwd);
+ spw->sp_lstchg = DAY_NOW_32;
- attrs[idx] = new_attr(_PWD_USERPASSWORD, val);
+ if (attr_addmod(sattrs, &sidx, _S_FLAG,
+ spw->sp_flag & ~FAILCOUNT_MASK) < 0)
+ return (PWU_NOMEM);
+ spw->sp_flag &= ~FAILCOUNT_MASK; /* reset count */
+ aging_needed = 1;
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);
+ /*
+ * 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
+ */
+ STRDUP_OR_RET(val, p->data.val_s);
+
+ NEW_ATTR(pattrs, pidx, _PWD_USERPASSWORD, val);
break;
case ATTR_COMMENT:
/* XX correct? */
- attrs[idx] = new_attr(_PWD_DESCRIPTION, p->data.val_s);
+ NEW_ATTR(pattrs, pidx, _PWD_DESCRIPTION, p->data.val_s);
break;
case ATTR_GECOS:
- attrs[idx] = new_attr(_PWD_GECOS, p->data.val_s);
+ if (!ldapbuf->shadow_update_enabled) {
+ NEW_ATTR(pattrs, pidx, _PWD_GECOS,
+ p->data.val_s);
+ } else {
+ NEW_ATTR(sattrs, sidx, _PWD_GECOS,
+ p->data.val_s);
+ }
break;
case ATTR_HOMEDIR:
- attrs[idx] = new_attr(_PWD_HOMEDIRECTORY,
- p->data.val_s);
+ if (!ldapbuf->shadow_update_enabled) {
+ NEW_ATTR(pattrs, pidx, _PWD_HOMEDIRECTORY,
+ p->data.val_s);
+ } else {
+ NEW_ATTR(sattrs, sidx, _PWD_HOMEDIRECTORY,
+ p->data.val_s);
+ }
break;
case ATTR_SHELL:
- attrs[idx] = new_attr(_PWD_LOGINSHELL, p->data.val_s);
+ if (!ldapbuf->shadow_update_enabled) {
+ NEW_ATTR(pattrs, pidx, _PWD_LOGINSHELL,
+ p->data.val_s);
+ } else {
+ NEW_ATTR(sattrs, sidx, _PWD_LOGINSHELL,
+ p->data.val_s);
+ }
break;
- /* Unsupported items are below this line */
+ /* We don't update NAME, UID, GID */
case ATTR_NAME:
case ATTR_UID:
case ATTR_GID:
+ /* Unsupported item */
case ATTR_AGE:
+ break;
+ case ATTR_LOCK_ACCOUNT:
+ if (!ldapbuf->shadow_update_enabled)
+ break; /* not managing passwordAccount */
+ if (spw->sp_pwdp == NULL) {
+ spw->sp_pwdp = LOCKSTRING;
+ } else if (strncmp(spw->sp_pwdp, LOCKSTRING,
+ sizeof (LOCKSTRING)-1) != 0) {
+ len = sizeof (LOCKSTRING)-1 +
+ strlen(spw->sp_pwdp) + 1 +
+ sizeof ("{crypt}");
+ pwd = malloc(len);
+ if (pwd == NULL) {
+ return (PWU_NOMEM);
+ }
+ (void) strlcpy(pwd, "{crypt}", len);
+ (void) strlcat(pwd, LOCKSTRING, len);
+ (void) strlcat(pwd, spw->sp_pwdp, len);
+ free(spw->sp_pwdp);
+ spw->sp_pwdp = pwd;
+ NEW_ATTR(sattrs, sidx, _PWD_USERPASSWORD,
+ spw->sp_pwdp);
+ }
+ if (attr_addmod(sattrs, &sidx, _S_LASTCHANGE,
+ DAY_NOW_32) < 0)
+ return (PWU_NOMEM);
+ spw->sp_lstchg = DAY_NOW_32;
+ break;
+
+ case ATTR_UNLOCK_ACCOUNT:
+ if (!ldapbuf->shadow_update_enabled)
+ break; /* not managing passwordAccount */
+ if (spw->sp_pwdp &&
+ strncmp(spw->sp_pwdp, LOCKSTRING,
+ sizeof (LOCKSTRING)-1) == 0) {
+ len = (sizeof ("{crypt}") -
+ sizeof (LOCKSTRING)) +
+ strlen(spw->sp_pwdp) + 1;
+ pwd = malloc(len);
+ if (pwd == NULL) {
+ return (PWU_NOMEM);
+ }
+ (void) strlcpy(pwd, "{crypt}", len);
+ (void) strlcat(pwd, spw->sp_pwdp +
+ sizeof (LOCKSTRING)-1, len);
+ free(spw->sp_pwdp);
+ spw->sp_pwdp = pwd;
+
+ NEW_ATTR(sattrs, sidx, _PWD_USERPASSWORD,
+ spw->sp_pwdp);
+ if (attr_addmod(sattrs, &sidx, _S_LASTCHANGE,
+ DAY_NOW_32) < 0)
+ return (PWU_NOMEM);
+ spw->sp_lstchg = DAY_NOW_32;
+ }
+ break;
+
+ case ATTR_NOLOGIN_ACCOUNT:
+ if (!ldapbuf->shadow_update_enabled)
+ break; /* not managing passwordAccount */
+ free(spw->sp_pwdp);
+ STRDUP_OR_RET(spw->sp_pwdp, "{crypt}" NOLOGINSTRING);
+ NEW_ATTR(sattrs, sidx, _PWD_USERPASSWORD, spw->sp_pwdp);
+ if (attr_addmod(sattrs, &sidx, _S_LASTCHANGE,
+ DAY_NOW_32) < 0)
+ return (PWU_NOMEM);
+ spw->sp_lstchg = DAY_NOW_32;
+ break;
+
+ case ATTR_EXPIRE_PASSWORD:
+ if (!ldapbuf->shadow_update_enabled)
+ break; /* not managing passwordAccount */
+ NUM_TO_STR(val, 0);
+ NEW_ATTR(sattrs, sidx, _S_LASTCHANGE, val);
+ break;
+
case ATTR_LSTCHG:
+ if (!ldapbuf->shadow_update_enabled)
+ break; /* not managing passwordAccount */
+ NUM_TO_STR(val, p->data.val_i);
+ NEW_ATTR(sattrs, sidx, _S_LASTCHANGE, val);
+ break;
+
case ATTR_MIN:
+ if (!ldapbuf->shadow_update_enabled)
+ break; /* not managing passwordAccount */
+ if (spw->sp_max == -1 && p->data.val_i != -1 &&
+ max_present(p->next) == 0)
+ return (PWU_AGING_DISABLED);
+ NUM_TO_STR(val, p->data.val_i);
+ NEW_ATTR(sattrs, sidx, _S_MIN, val);
+ aging_set = 1;
+ break;
+
case ATTR_MAX:
+ if (!ldapbuf->shadow_update_enabled)
+ break; /* not managing passwordAccount */
+ if (p->data.val_i == -1) {
+ /* Turn off aging. Reset min and warn too */
+ spw->sp_max = spw->sp_min = spw->sp_warn = -1;
+ NUM_TO_STR(val, -1);
+ NEW_ATTR(sattrs, sidx, _S_MIN, val);
+ NUM_TO_STR(val, -1);
+ NEW_ATTR(sattrs, sidx, _S_WARNING, val);
+ } else {
+ /* Turn account aging on */
+ if (spw->sp_min == -1) {
+ /*
+ * minage was not set with command-
+ * line option: set to zero
+ */
+ spw->sp_min = 0;
+ NUM_TO_STR(val, 0);
+ NEW_ATTR(sattrs, sidx, _S_MIN,
+ val);
+ }
+ /*
+ * If aging was turned off, we update lstchg.
+ * We take care not to update lstchg if the
+ * user has no password, otherwise the user
+ * might not be required to provide a password
+ * the next time [s]he logs in.
+ *
+ * Also, if lstchg != -1 (i.e., not set)
+ * we keep the old value.
+ */
+ if (spw->sp_max == -1 &&
+ spw->sp_pwdp != NULL && *spw->sp_pwdp &&
+ spw->sp_lstchg == -1) {
+ if (attr_addmod(sattrs, &sidx,
+ _S_LASTCHANGE,
+ DAY_NOW_32) < 0)
+ return (PWU_NOMEM);
+ spw->sp_lstchg = DAY_NOW_32;
+ }
+ }
+ NUM_TO_STR(val, p->data.val_i);
+ NEW_ATTR(sattrs, sidx, _S_MAX, val);
+ aging_set = 1;
+ break;
+
case ATTR_WARN:
+ if (!ldapbuf->shadow_update_enabled)
+ break; /* not managing passwordAccount */
+ if (spw->sp_max == -1 &&
+ p->data.val_i != -1 && max_present(p->next) == 0)
+ return (PWU_AGING_DISABLED);
+ NUM_TO_STR(val, p->data.val_i);
+ NEW_ATTR(sattrs, sidx, _S_WARNING, val);
+ break;
+
case ATTR_INACT:
+ if (!ldapbuf->shadow_update_enabled)
+ break; /* not managing passwordAccount */
+ NUM_TO_STR(val, p->data.val_i);
+ NEW_ATTR(sattrs, sidx, _S_INACTIVE, val);
+ break;
+
case ATTR_EXPIRE:
+ if (!ldapbuf->shadow_update_enabled)
+ break; /* not managing passwordAccount */
+ NUM_TO_STR(val, p->data.val_i);
+ NEW_ATTR(sattrs, sidx, _S_EXPIRE, val);
+ break;
+
case ATTR_FLAG:
+ if (!ldapbuf->shadow_update_enabled)
+ break; /* not managing passwordAccount */
+ NUM_TO_STR(val, p->data.val_i);
+ NEW_ATTR(sattrs, sidx, _S_FLAG, val);
+ break;
+ case ATTR_INCR_FAILED_LOGINS:
+ if (!ldapbuf->shadow_update_enabled) {
+ rc = PWU_CHANGE_NOT_ALLOWED;
+ break; /* not managing passwordAccount */
+ }
+ count = (spw->sp_flag & FAILCOUNT_MASK) + 1;
+ spw->sp_flag &= ~FAILCOUNT_MASK;
+ spw->sp_flag |= min(FAILCOUNT_MASK, count);
+ p->data.val_i = count;
+ NUM_TO_STR(val, spw->sp_flag);
+ NEW_ATTR(sattrs, sidx, _S_FLAG, val);
+ break;
+ case ATTR_RST_FAILED_LOGINS:
+ if (!ldapbuf->shadow_update_enabled) {
+ rc = PWU_CHANGE_NOT_ALLOWED;
+ break; /* not managing passwordAccount */
+ }
+ p->data.val_i = spw->sp_flag & FAILCOUNT_MASK;
+ spw->sp_flag &= ~FAILCOUNT_MASK;
+ NUM_TO_STR(val, spw->sp_flag);
+ NEW_ATTR(sattrs, sidx, _S_FLAG, val);
break;
default:
break;
}
- if (attrs[idx] == NULL)
- return (PWU_NOMEM);
- idx++;
}
- attrs[idx] = NULL;
+ /*
+ * If the ldap client is configured with shadow update enabled,
+ * then what should the new aging values look like?
+ *
+ * There are a number of different conditions
+ *
+ * a) aging is already configured: don't touch it
+ *
+ * b) disable_aging is set: disable aging
+ *
+ * c) aging is not configured: turn on default aging;
+ *
+ * b) and c) of course only if aging_needed and !aging_set.
+ * (i.e., password changed, and aging values not changed)
+ */
- return (PWU_SUCCESS);
+ if (ldapbuf->shadow_update_enabled && spw != NULL && spw->sp_max <= 0) {
+ /* a) aging not yet configured */
+ if (aging_needed && !aging_set) {
+ if (disable_aging) {
+ /* b) turn off aging */
+ spw->sp_min = spw->sp_max = spw->sp_warn = -1;
+ if (attr_addmod(sattrs, &sidx, _S_MIN, -1) < 0)
+ return (PWU_NOMEM);
+ if (attr_addmod(sattrs, &sidx, _S_MAX, -1) < 0)
+ return (PWU_NOMEM);
+ if (attr_addmod(sattrs, &sidx, _S_WARNING,
+ -1) < 0)
+ return (PWU_NOMEM);
+ } else {
+ /* c) */
+ turn_on_default_aging(spw);
+
+ if (attr_addmod(sattrs, &sidx, _S_MIN,
+ spw->sp_min) < 0)
+ return (PWU_NOMEM);
+ if (attr_addmod(sattrs, &sidx, _S_MAX,
+ spw->sp_max) < 0)
+ return (PWU_NOMEM);
+ if (attr_addmod(sattrs, &sidx,
+ _S_WARNING, spw->sp_warn) < 0)
+ return (PWU_NOMEM);
+ }
+ }
+ }
+
+ pattrs[pidx] = NULL;
+ sattrs[sidx] = NULL;
+
+ return (rc);
}
/*
@@ -492,7 +980,7 @@ ldap_to_pwu_code(int error, int pwd_status)
int
ldap_replaceattr(const char *dn, ns_ldap_attr_t **attrs, const char *binddn,
- const char *pwd, int *pwd_status)
+ const char *pwd, int *pwd_status, int flags)
{
int result = NS_LDAP_OP_FAILED;
int ldaprc;
@@ -507,18 +995,20 @@ ldap_replaceattr(const char *dn, ns_ldap_attr_t **attrs, const char *binddn,
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;
+ return (NS_LDAP_MEMORY); /* map to PWU_NOMEM */
- credp->cred.unix_cred.userID = strdup(binddn);
- credp->cred.unix_cred.passwd = strdup(pwd);
+ /* for admin shadow update, dn and pwd will be set later in libsldap */
+ if ((flags & NS_LDAP_UPDATE_SHADOW) == 0) {
+ /* 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);
+ (void ***)&certpath, &errorp);
if (ldaprc != NS_LDAP_SUCCESS)
goto out;
@@ -527,7 +1017,7 @@ ldap_replaceattr(const char *dn, ns_ldap_attr_t **attrs, const char *binddn,
/* Load the service specific authentication method */
ldaprc = __ns_ldap_getServiceAuthMethods("passwd-cmd", &authpp,
- &errorp);
+ &errorp);
if (ldaprc != NS_LDAP_SUCCESS)
goto out;
@@ -538,7 +1028,7 @@ ldap_replaceattr(const char *dn, ns_ldap_attr_t **attrs, const char *binddn,
*/
if (authpp == NULL) {
ldaprc = __ns_ldap_getParam(NS_LDAP_AUTH_P, (void ***)&authpp,
- &errorp);
+ &errorp);
if (ldaprc != NS_LDAP_SUCCESS)
goto out;
}
@@ -570,21 +1060,32 @@ ldap_replaceattr(const char *dn, ns_ldap_attr_t **attrs, const char *binddn,
credp->auth.saslopt = authp->saslopt;
ldaprc = __ns_ldap_repAttr("shadow", dn,
- (const ns_ldap_attr_t * const *)attrs,
- credp, 0, &errorp);
+ (const ns_ldap_attr_t * const *)attrs,
+ credp, flags, &errorp);
if (ldaprc == NS_LDAP_SUCCESS) {
result = NS_LDAP_SUCCESS;
goto out;
}
/*
+ * if change not allowed due to configuration, indicate so
+ * to the caller
+ */
+ if (ldaprc == NS_LDAP_CONFIG &&
+ errorp->status == NS_CONFIG_NOTALLOW) {
+ result = NS_LDAP_CONFIG;
+ *pwd_status = NS_PASSWD_CHANGE_NOT_ALLOWED;
+ 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))) {
+ (errorp->pwd_mgmt.status == NS_PASSWD_GOOD) &&
+ ((errorp->status == LDAP_INAPPROPRIATE_AUTH) ||
+ (errorp->status == LDAP_INVALID_CREDENTIALS))) {
result = ldaprc;
goto out;
}
@@ -594,7 +1095,7 @@ ldap_replaceattr(const char *dn, ns_ldap_attr_t **attrs, const char *binddn,
* return it to caller
*/
if ((ldaprc == NS_LDAP_INTERNAL) &&
- errorp->pwd_mgmt.status != NS_PASSWD_GOOD) {
+ errorp->pwd_mgmt.status != NS_PASSWD_GOOD) {
*pwd_status = errorp->pwd_mgmt.status;
result = ldaprc;
goto out;
@@ -611,7 +1112,7 @@ ldap_replaceattr(const char *dn, ns_ldap_attr_t **attrs, const char *binddn,
result = NS_LDAP_CONFIG;
goto out;
}
- result = PWU_DENIED;
+ result = NS_LDAP_OP_FAILED; /* map to PWU_DENIED */
out:
if (credp)
@@ -627,7 +1128,6 @@ out:
}
-
/*
* ldap_putpwnam(name, oldpw, dummy, rep, buf)
*
@@ -645,7 +1145,8 @@ ldap_putpwnam(char *name, char *oldpw, char *dummy,
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;
+ ns_ldap_attr_t **pattrs = ldapbuf->pattrs;
+ ns_ldap_attr_t **sattrs = ldapbuf->sattrs;
struct passwd *pw;
int pwd_status;
uid_t uid;
@@ -654,6 +1155,26 @@ ldap_putpwnam(char *name, char *oldpw, char *dummy,
return (PWU_NOT_FOUND);
/*
+ * 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;
+
+ /* update shadow via ldap_cachemgr if it is enabled */
+ if (ldapbuf->shadow_update_enabled &&
+ sattrs != NULL && sattrs[0] != NULL) {
+ /*
+ * flag NS_LDAP_UPDATE_SHADOW indicates the shadow update
+ * should be done via ldap_cachemgr
+ */
+ res = ldap_replaceattr(dn, sattrs, NULL, NULL, &pwd_status,
+ NS_LDAP_UPDATE_SHADOW);
+ goto out;
+ }
+
+ /*
* 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
@@ -667,14 +1188,6 @@ ldap_putpwnam(char *name, char *oldpw, char *dummy,
*/
/*
- * 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();
@@ -696,21 +1209,14 @@ ldap_putpwnam(char *name, char *oldpw, char *dummy,
if (res != NS_LDAP_SUCCESS)
goto out;
- res = ldap_replaceattr(dn, attrs, binddn, oldpw,
- &pwd_status);
+ if (pattrs && pattrs[0] != NULL) {
+ res = ldap_replaceattr(dn, pattrs, binddn, oldpw,
+ &pwd_status, 0);
+ } else
+ res = NS_LDAP_OP_FAILED;
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_ldapbuf(ldapbuf);
free(dn);
return (ldap_to_pwu_code(res, pwd_status));