diff options
author | Michen Chang <Michen.Chang@Sun.COM> | 2009-02-13 18:18:56 -0800 |
---|---|---|
committer | Michen Chang <Michen.Chang@Sun.COM> | 2009-02-13 18:18:56 -0800 |
commit | dd1104fbe0f0f41434502f335b9f0b34999f771c (patch) | |
tree | 57c95d64bc40cebf2b145329e1f49e811352d502 /usr/src/lib/passwdutil/ldap_attr.c | |
parent | d68ef20e3fe871e73146fc684d29d335521dcd99 (diff) | |
download | illumos-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.c | 860 |
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)); |