|
Packit |
6bd9ab |
/*
|
|
Packit |
6bd9ab |
pam.c - pam processing routines
|
|
Packit |
6bd9ab |
|
|
Packit |
6bd9ab |
Copyright (C) 2009 Howard Chu
|
|
Packit |
6bd9ab |
Copyright (C) 2009-2017 Arthur de Jong
|
|
Packit |
6bd9ab |
Copyright (C) 2015 Nokia Solutions and Networks
|
|
Packit |
6bd9ab |
|
|
Packit |
6bd9ab |
This library is free software; you can redistribute it and/or
|
|
Packit |
6bd9ab |
modify it under the terms of the GNU Lesser General Public
|
|
Packit |
6bd9ab |
License as published by the Free Software Foundation; either
|
|
Packit |
6bd9ab |
version 2.1 of the License, or (at your option) any later version.
|
|
Packit |
6bd9ab |
|
|
Packit |
6bd9ab |
This library is distributed in the hope that it will be useful,
|
|
Packit |
6bd9ab |
but WITHOUT ANY WARRANTY; without even the implied warranty of
|
|
Packit |
6bd9ab |
MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
|
|
Packit |
6bd9ab |
Lesser General Public License for more details.
|
|
Packit |
6bd9ab |
|
|
Packit |
6bd9ab |
You should have received a copy of the GNU Lesser General Public
|
|
Packit |
6bd9ab |
License along with this library; if not, write to the Free Software
|
|
Packit |
6bd9ab |
Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA
|
|
Packit |
6bd9ab |
02110-1301 USA
|
|
Packit |
6bd9ab |
*/
|
|
Packit |
6bd9ab |
|
|
Packit |
6bd9ab |
#include "config.h"
|
|
Packit |
6bd9ab |
|
|
Packit |
6bd9ab |
#include <stdio.h>
|
|
Packit |
6bd9ab |
#include <stdlib.h>
|
|
Packit |
6bd9ab |
#include <string.h>
|
|
Packit |
6bd9ab |
#ifdef HAVE_STDINT_H
|
|
Packit |
6bd9ab |
#include <stdint.h>
|
|
Packit |
6bd9ab |
#endif /* HAVE_STDINT_H */
|
|
Packit |
6bd9ab |
#include <unistd.h>
|
|
Packit |
6bd9ab |
#include <time.h>
|
|
Packit |
6bd9ab |
|
|
Packit |
6bd9ab |
#include "common.h"
|
|
Packit |
6bd9ab |
#include "log.h"
|
|
Packit |
6bd9ab |
#include "myldap.h"
|
|
Packit |
6bd9ab |
#include "cfg.h"
|
|
Packit |
6bd9ab |
#include "attmap.h"
|
|
Packit |
6bd9ab |
#include "common/dict.h"
|
|
Packit |
6bd9ab |
#include "common/expr.h"
|
|
Packit |
6bd9ab |
|
|
Packit |
6bd9ab |
static void search_var_add(DICT *dict, const char *name, const char *value)
|
|
Packit |
6bd9ab |
{
|
|
Packit |
6bd9ab |
size_t sz;
|
|
Packit |
6bd9ab |
char *escaped_value;
|
|
Packit |
6bd9ab |
/* allocate memory for escaped string */
|
|
Packit |
6bd9ab |
sz = ((strlen(value) + 8) * 120) / 100;
|
|
Packit |
6bd9ab |
escaped_value = (char *)malloc(sz);
|
|
Packit |
6bd9ab |
if (escaped_value == NULL)
|
|
Packit |
6bd9ab |
{
|
|
Packit |
6bd9ab |
log_log(LOG_CRIT, "search_var_add(): malloc() failed to allocate memory");
|
|
Packit |
6bd9ab |
return;
|
|
Packit |
6bd9ab |
}
|
|
Packit |
6bd9ab |
/* perform escaping of the value */
|
|
Packit |
6bd9ab |
if (myldap_escape(value, escaped_value, sz))
|
|
Packit |
6bd9ab |
{
|
|
Packit |
6bd9ab |
log_log(LOG_ERR, "search_var_add(): escaped_value buffer too small");
|
|
Packit |
6bd9ab |
free(escaped_value);
|
|
Packit |
6bd9ab |
return;
|
|
Packit |
6bd9ab |
}
|
|
Packit |
6bd9ab |
/* add to dict */
|
|
Packit |
6bd9ab |
dict_put(dict, name, escaped_value);
|
|
Packit |
6bd9ab |
}
|
|
Packit |
6bd9ab |
|
|
Packit |
6bd9ab |
/* build a dictionary with variables that can be used in searches */
|
|
Packit |
6bd9ab |
static DICT *search_vars_new(const char *dn, const char *username,
|
|
Packit |
6bd9ab |
const char *service, const char *ruser,
|
|
Packit |
6bd9ab |
const char *rhost, const char *tty)
|
|
Packit |
6bd9ab |
{
|
|
Packit |
6bd9ab |
char hostname[BUFLEN_HOSTNAME];
|
|
Packit |
6bd9ab |
/* allocating this on the stack is OK because search_var_add()
|
|
Packit |
6bd9ab |
will allocate new memory for the value */
|
|
Packit |
6bd9ab |
const char *fqdn;
|
|
Packit |
6bd9ab |
DICT *dict;
|
|
Packit |
6bd9ab |
dict = dict_new();
|
|
Packit |
6bd9ab |
if (dict == NULL)
|
|
Packit |
6bd9ab |
{
|
|
Packit |
6bd9ab |
log_log(LOG_CRIT, "search_vars_new(): dict_new() failed to allocate memory");
|
|
Packit |
6bd9ab |
return NULL;
|
|
Packit |
6bd9ab |
}
|
|
Packit |
6bd9ab |
/* NOTE: any variables added here also need to be added to
|
|
Packit |
6bd9ab |
cfg.c:check_search_variables() */
|
|
Packit |
6bd9ab |
search_var_add(dict, "username", username);
|
|
Packit |
6bd9ab |
search_var_add(dict, "service", service);
|
|
Packit |
6bd9ab |
search_var_add(dict, "ruser", ruser);
|
|
Packit |
6bd9ab |
search_var_add(dict, "rhost", rhost);
|
|
Packit |
6bd9ab |
search_var_add(dict, "tty", tty);
|
|
Packit |
6bd9ab |
if (gethostname(hostname, sizeof(hostname)) == 0)
|
|
Packit |
6bd9ab |
search_var_add(dict, "hostname", hostname);
|
|
Packit |
6bd9ab |
if ((fqdn = getfqdn()) != NULL)
|
|
Packit |
6bd9ab |
search_var_add(dict, "fqdn", fqdn);
|
|
Packit |
6bd9ab |
search_var_add(dict, "dn", dn);
|
|
Packit |
6bd9ab |
search_var_add(dict, "uid", username);
|
|
Packit |
6bd9ab |
return dict;
|
|
Packit |
6bd9ab |
}
|
|
Packit |
6bd9ab |
|
|
Packit |
6bd9ab |
static void search_vars_free(DICT *dict)
|
|
Packit |
6bd9ab |
{
|
|
Packit |
6bd9ab |
int i;
|
|
Packit |
6bd9ab |
const char **keys;
|
|
Packit |
6bd9ab |
void *value;
|
|
Packit |
6bd9ab |
/* go over all keys and free all the values
|
|
Packit |
6bd9ab |
(they were allocated in search_var_add) */
|
|
Packit |
6bd9ab |
/* loop over dictionary contents */
|
|
Packit |
6bd9ab |
keys = dict_keys(dict);
|
|
Packit |
6bd9ab |
for (i = 0; keys[i] != NULL; i++)
|
|
Packit |
6bd9ab |
{
|
|
Packit |
6bd9ab |
value = dict_get(dict, keys[i]);
|
|
Packit |
6bd9ab |
if (value)
|
|
Packit |
6bd9ab |
free(value);
|
|
Packit |
6bd9ab |
}
|
|
Packit |
6bd9ab |
free(keys);
|
|
Packit |
6bd9ab |
/* after this values from the dict should obviously no longer be used */
|
|
Packit |
6bd9ab |
dict_free(dict);
|
|
Packit |
6bd9ab |
}
|
|
Packit |
6bd9ab |
|
|
Packit |
6bd9ab |
static const char *search_var_get(const char *name, void *expander_attr)
|
|
Packit |
6bd9ab |
{
|
|
Packit |
6bd9ab |
DICT *dict = (DICT *)expander_attr;
|
|
Packit |
6bd9ab |
return (const char *)dict_get(dict, name);
|
|
Packit |
6bd9ab |
/* TODO: if not set use entry to get attribute name (entry can be an
|
|
Packit |
6bd9ab |
element in the dict) */
|
|
Packit |
6bd9ab |
}
|
|
Packit |
6bd9ab |
|
|
Packit |
6bd9ab |
/* search all search bases using the provided filter */
|
|
Packit |
6bd9ab |
static int do_searches(MYLDAP_SESSION *session, const char *option,
|
|
Packit |
6bd9ab |
const char *filter)
|
|
Packit |
6bd9ab |
{
|
|
Packit |
6bd9ab |
int i;
|
|
Packit |
6bd9ab |
int rc;
|
|
Packit |
6bd9ab |
const char *base;
|
|
Packit |
6bd9ab |
static const char *attrs[2];
|
|
Packit |
6bd9ab |
MYLDAP_SEARCH *search;
|
|
Packit |
6bd9ab |
MYLDAP_ENTRY *entry;
|
|
Packit |
6bd9ab |
/* prepare the search */
|
|
Packit |
6bd9ab |
attrs[0] = "dn";
|
|
Packit |
6bd9ab |
attrs[1] = NULL;
|
|
Packit |
6bd9ab |
/* perform a search for each search base */
|
|
Packit |
6bd9ab |
log_log(LOG_DEBUG, "trying %s \"%s\"", option, filter);
|
|
Packit |
6bd9ab |
for (i = 0; (base = nslcd_cfg->bases[i]) != NULL; i++)
|
|
Packit |
6bd9ab |
{
|
|
Packit |
6bd9ab |
/* do the LDAP search */
|
|
Packit |
6bd9ab |
search = myldap_search(session, base, LDAP_SCOPE_SUBTREE, filter, attrs, &rc);
|
|
Packit |
6bd9ab |
if (search == NULL)
|
|
Packit |
6bd9ab |
{
|
|
Packit |
6bd9ab |
log_log(LOG_ERR, "%s \"%s\" failed: %s",
|
|
Packit |
6bd9ab |
option, filter, ldap_err2string(rc));
|
|
Packit |
6bd9ab |
return rc;
|
|
Packit |
6bd9ab |
}
|
|
Packit |
6bd9ab |
/* try to get an entry */
|
|
Packit |
6bd9ab |
entry = myldap_get_entry(search, &rc);
|
|
Packit |
6bd9ab |
if (entry != NULL)
|
|
Packit |
6bd9ab |
{
|
|
Packit |
6bd9ab |
log_log(LOG_DEBUG, "%s found \"%s\"", option, myldap_get_dn(entry));
|
|
Packit |
6bd9ab |
return LDAP_SUCCESS;
|
|
Packit |
6bd9ab |
}
|
|
Packit |
6bd9ab |
}
|
|
Packit |
6bd9ab |
log_log(LOG_ERR, "%s \"%s\" found no matches", option, filter);
|
|
Packit |
6bd9ab |
if (rc == LDAP_SUCCESS)
|
|
Packit |
6bd9ab |
rc = LDAP_NO_SUCH_OBJECT;
|
|
Packit |
6bd9ab |
return rc;
|
|
Packit |
6bd9ab |
}
|
|
Packit |
6bd9ab |
|
|
Packit |
6bd9ab |
/* set up a connection and try to bind with the specified DN and password,
|
|
Packit |
6bd9ab |
returns an LDAP result code */
|
|
Packit |
6bd9ab |
static int try_bind(const char *userdn, const char *password,
|
|
Packit |
6bd9ab |
const char *username, const char *service,
|
|
Packit |
6bd9ab |
const char *ruser, const char *rhost, const char *tty,
|
|
Packit |
6bd9ab |
int *authzrc, char *authzmsg, size_t authzmsgsz)
|
|
Packit |
6bd9ab |
{
|
|
Packit |
6bd9ab |
MYLDAP_SESSION *session;
|
|
Packit |
6bd9ab |
MYLDAP_SEARCH *search;
|
|
Packit |
6bd9ab |
MYLDAP_ENTRY *entry;
|
|
Packit |
6bd9ab |
static const char *attrs[2];
|
|
Packit |
6bd9ab |
int rc;
|
|
Packit |
6bd9ab |
const char *msg;
|
|
Packit |
6bd9ab |
DICT *dict;
|
|
Packit |
6bd9ab |
char filter[BUFLEN_FILTER];
|
|
Packit |
6bd9ab |
const char *res;
|
|
Packit |
6bd9ab |
/* set up a new connection */
|
|
Packit |
6bd9ab |
session = myldap_create_session();
|
|
Packit |
6bd9ab |
if (session == NULL)
|
|
Packit |
6bd9ab |
return LDAP_UNAVAILABLE;
|
|
Packit |
6bd9ab |
/* perform a BIND operation with user credentials */
|
|
Packit |
6bd9ab |
rc = myldap_bind(session, userdn, password, authzrc, &msg;;
|
|
Packit |
6bd9ab |
if (rc == LDAP_SUCCESS)
|
|
Packit |
6bd9ab |
{
|
|
Packit |
6bd9ab |
/* perform a search to trigger the BIND operation */
|
|
Packit |
6bd9ab |
attrs[0] = "dn";
|
|
Packit |
6bd9ab |
attrs[1] = NULL;
|
|
Packit |
6bd9ab |
if (strcasecmp(nslcd_cfg->pam_authc_search, "BASE") == 0)
|
|
Packit |
6bd9ab |
{
|
|
Packit |
6bd9ab |
/* do a simple search to check userdn existence */
|
|
Packit |
6bd9ab |
search = myldap_search(session, userdn, LDAP_SCOPE_BASE,
|
|
Packit |
6bd9ab |
"(objectClass=*)", attrs, &rc);
|
|
Packit |
6bd9ab |
if ((search == NULL) && (rc == LDAP_SUCCESS))
|
|
Packit |
6bd9ab |
rc = LDAP_LOCAL_ERROR;
|
|
Packit |
6bd9ab |
if (rc == LDAP_SUCCESS)
|
|
Packit |
6bd9ab |
{
|
|
Packit |
6bd9ab |
entry = myldap_get_entry(search, &rc);
|
|
Packit |
6bd9ab |
if ((entry == NULL) && (rc == LDAP_SUCCESS))
|
|
Packit |
6bd9ab |
rc = LDAP_NO_RESULTS_RETURNED;
|
|
Packit |
6bd9ab |
}
|
|
Packit |
6bd9ab |
}
|
|
Packit |
6bd9ab |
else if (strcasecmp(nslcd_cfg->pam_authc_search, "NONE") != 0)
|
|
Packit |
6bd9ab |
{
|
|
Packit |
6bd9ab |
/* build the search filter */
|
|
Packit |
6bd9ab |
dict = search_vars_new(userdn, username, service, ruser, rhost, tty);
|
|
Packit |
6bd9ab |
if (dict == NULL)
|
|
Packit |
6bd9ab |
{
|
|
Packit |
6bd9ab |
myldap_session_close(session);
|
|
Packit |
6bd9ab |
return LDAP_LOCAL_ERROR;
|
|
Packit |
6bd9ab |
}
|
|
Packit |
6bd9ab |
res = expr_parse(nslcd_cfg->pam_authc_search, filter, sizeof(filter),
|
|
Packit |
6bd9ab |
search_var_get, (void *)dict);
|
|
Packit |
6bd9ab |
if (res == NULL)
|
|
Packit |
6bd9ab |
{
|
|
Packit |
6bd9ab |
search_vars_free(dict);
|
|
Packit |
6bd9ab |
myldap_session_close(session);
|
|
Packit |
6bd9ab |
log_log(LOG_ERR, "invalid pam_authc_search \"%s\"",
|
|
Packit |
6bd9ab |
nslcd_cfg->pam_authc_search);
|
|
Packit |
6bd9ab |
return LDAP_LOCAL_ERROR;
|
|
Packit |
6bd9ab |
}
|
|
Packit |
6bd9ab |
/* perform a search for each search base */
|
|
Packit |
6bd9ab |
rc = do_searches(session, "pam_authc_search", filter);
|
|
Packit |
6bd9ab |
/* free search variables */
|
|
Packit |
6bd9ab |
search_vars_free(dict);
|
|
Packit |
6bd9ab |
}
|
|
Packit |
6bd9ab |
}
|
|
Packit |
6bd9ab |
/* log any authentication, search or authorsiation messages */
|
|
Packit |
6bd9ab |
if (rc != LDAP_SUCCESS)
|
|
Packit |
6bd9ab |
log_log(LOG_WARNING, "%s: %s", userdn, ldap_err2string(rc));
|
|
Packit |
6bd9ab |
if ((msg != NULL) && (msg[0] != '\0'))
|
|
Packit |
6bd9ab |
{
|
|
Packit |
6bd9ab |
mysnprintf(authzmsg, authzmsgsz - 1, "%s", msg);
|
|
Packit |
6bd9ab |
log_log(LOG_WARNING, "%s: %s", userdn, authzmsg);
|
|
Packit |
6bd9ab |
}
|
|
Packit |
6bd9ab |
/* close the session */
|
|
Packit |
6bd9ab |
myldap_session_close(session);
|
|
Packit |
6bd9ab |
/* return results */
|
|
Packit |
6bd9ab |
return rc;
|
|
Packit |
6bd9ab |
}
|
|
Packit |
6bd9ab |
|
|
Packit |
6bd9ab |
/* ensure that both userdn and username are filled in from the entry,
|
|
Packit |
6bd9ab |
returns an LDAP result code */
|
|
Packit |
6bd9ab |
static MYLDAP_ENTRY *validate_user(MYLDAP_SESSION *session,
|
|
Packit |
6bd9ab |
char *username, int *rcp)
|
|
Packit |
6bd9ab |
{
|
|
Packit |
6bd9ab |
int rc;
|
|
Packit |
6bd9ab |
MYLDAP_ENTRY *entry = NULL;
|
|
Packit |
6bd9ab |
/* check username for validity */
|
|
Packit |
6bd9ab |
if (!isvalidname(username))
|
|
Packit |
6bd9ab |
{
|
|
Packit |
6bd9ab |
log_log(LOG_WARNING, "request denied by validnames option");
|
|
Packit |
6bd9ab |
*rcp = LDAP_NO_SUCH_OBJECT;
|
|
Packit |
6bd9ab |
return NULL;
|
|
Packit |
6bd9ab |
}
|
|
Packit |
6bd9ab |
/* get the user entry based on the username */
|
|
Packit |
6bd9ab |
entry = uid2entry(session, username, &rc);
|
|
Packit |
6bd9ab |
if (entry == NULL)
|
|
Packit |
6bd9ab |
{
|
|
Packit |
6bd9ab |
if (rc == LDAP_SUCCESS)
|
|
Packit |
6bd9ab |
rc = LDAP_NO_SUCH_OBJECT;
|
|
Packit |
6bd9ab |
log_log(LOG_DEBUG, "\"%s\": user not found: %s", username, ldap_err2string(rc));
|
|
Packit |
6bd9ab |
*rcp = rc;
|
|
Packit |
6bd9ab |
}
|
|
Packit |
6bd9ab |
return entry;
|
|
Packit |
6bd9ab |
}
|
|
Packit |
6bd9ab |
|
|
Packit |
6bd9ab |
/* update the username value from the entry if needed */
|
|
Packit |
6bd9ab |
static void update_username(MYLDAP_ENTRY *entry, char *username,
|
|
Packit |
6bd9ab |
size_t username_len)
|
|
Packit |
6bd9ab |
{
|
|
Packit |
6bd9ab |
const char **values;
|
|
Packit |
6bd9ab |
const char *value;
|
|
Packit |
6bd9ab |
/* get the "real" username */
|
|
Packit |
6bd9ab |
value = myldap_get_rdn_value(entry, attmap_passwd_uid);
|
|
Packit |
6bd9ab |
if (value == NULL)
|
|
Packit |
6bd9ab |
{
|
|
Packit |
6bd9ab |
/* get the username from the uid attribute */
|
|
Packit |
6bd9ab |
values = myldap_get_values(entry, attmap_passwd_uid);
|
|
Packit |
6bd9ab |
if ((values == NULL) || (values[0] == NULL))
|
|
Packit |
6bd9ab |
{
|
|
Packit |
6bd9ab |
log_log(LOG_WARNING, "%s: %s: missing",
|
|
Packit |
6bd9ab |
myldap_get_dn(entry), attmap_passwd_uid);
|
|
Packit |
6bd9ab |
return;
|
|
Packit |
6bd9ab |
}
|
|
Packit |
6bd9ab |
value = values[0];
|
|
Packit |
6bd9ab |
}
|
|
Packit |
6bd9ab |
/* check the username */
|
|
Packit |
6bd9ab |
if ((value == NULL) || !isvalidname(value) || strlen(value) >= username_len)
|
|
Packit |
6bd9ab |
{
|
|
Packit |
6bd9ab |
log_log(LOG_WARNING, "%s: %s: denied by validnames option",
|
|
Packit |
6bd9ab |
myldap_get_dn(entry), attmap_passwd_uid);
|
|
Packit |
6bd9ab |
return;
|
|
Packit |
6bd9ab |
}
|
|
Packit |
6bd9ab |
/* check if the username is different and update it if needed */
|
|
Packit |
6bd9ab |
if (STR_CMP(username, value) != 0)
|
|
Packit |
6bd9ab |
{
|
|
Packit |
6bd9ab |
log_log(LOG_INFO, "username changed from \"%s\" to \"%s\"",
|
|
Packit |
6bd9ab |
username, value);
|
|
Packit |
6bd9ab |
strcpy(username, value);
|
|
Packit |
6bd9ab |
}
|
|
Packit |
6bd9ab |
}
|
|
Packit |
6bd9ab |
|
|
Packit |
6bd9ab |
static int check_shadow(MYLDAP_SESSION *session, const char *username,
|
|
Packit |
6bd9ab |
char *authzmsg, size_t authzmsgsz,
|
|
Packit |
6bd9ab |
int check_maxdays, int check_mindays)
|
|
Packit |
6bd9ab |
{
|
|
Packit |
6bd9ab |
MYLDAP_ENTRY *entry = NULL;
|
|
Packit |
6bd9ab |
long today, lastchangedate, mindays, maxdays, warndays, inactdays, expiredate;
|
|
Packit |
6bd9ab |
unsigned long flag;
|
|
Packit |
6bd9ab |
long daysleft, inactleft;
|
|
Packit |
6bd9ab |
/* get the shadow entry */
|
|
Packit |
6bd9ab |
entry = shadow_uid2entry(session, username, NULL);
|
|
Packit |
6bd9ab |
if (entry == NULL)
|
|
Packit |
6bd9ab |
return NSLCD_PAM_SUCCESS; /* no shadow entry found, nothing to check */
|
|
Packit |
6bd9ab |
/* get today's date */
|
|
Packit |
6bd9ab |
today = (long)(time(NULL) / (60 * 60 * 24));
|
|
Packit |
6bd9ab |
/* get shadow information */
|
|
Packit |
6bd9ab |
get_shadow_properties(entry, &lastchangedate, &mindays, &maxdays, &warndays,
|
|
Packit |
6bd9ab |
&inactdays, &expiredate, &flag;;
|
|
Packit |
6bd9ab |
/* check account expiry date */
|
|
Packit |
6bd9ab |
if ((expiredate != -1) && (today >= expiredate))
|
|
Packit |
6bd9ab |
{
|
|
Packit |
6bd9ab |
daysleft = today - expiredate;
|
|
Packit |
6bd9ab |
mysnprintf(authzmsg, authzmsgsz - 1, "account expired %ld days ago",
|
|
Packit |
6bd9ab |
daysleft);
|
|
Packit |
6bd9ab |
log_log(LOG_WARNING, "%s: %s: %s",
|
|
Packit |
6bd9ab |
myldap_get_dn(entry), attmap_shadow_shadowExpire, authzmsg);
|
|
Packit |
6bd9ab |
return NSLCD_PAM_ACCT_EXPIRED;
|
|
Packit |
6bd9ab |
}
|
|
Packit |
6bd9ab |
/* password expiration isn't interesting at this point because the user
|
|
Packit |
6bd9ab |
may not have authenticated with a password and if he did that would be
|
|
Packit |
6bd9ab |
checked in the authc phase */
|
|
Packit |
6bd9ab |
if (check_maxdays)
|
|
Packit |
6bd9ab |
{
|
|
Packit |
6bd9ab |
/* check lastchanged */
|
|
Packit |
6bd9ab |
if (lastchangedate == 0)
|
|
Packit |
6bd9ab |
{
|
|
Packit |
6bd9ab |
mysnprintf(authzmsg, authzmsgsz - 1, "need a new password");
|
|
Packit |
6bd9ab |
log_log(LOG_WARNING, "%s: %s: %s",
|
|
Packit |
6bd9ab |
myldap_get_dn(entry), attmap_shadow_shadowLastChange, authzmsg);
|
|
Packit |
6bd9ab |
return NSLCD_PAM_NEW_AUTHTOK_REQD;
|
|
Packit |
6bd9ab |
}
|
|
Packit |
6bd9ab |
else if (today < lastchangedate)
|
|
Packit |
6bd9ab |
log_log(LOG_WARNING, "%s: %s: password changed in the future",
|
|
Packit |
6bd9ab |
myldap_get_dn(entry), attmap_shadow_shadowLastChange);
|
|
Packit |
6bd9ab |
else if (maxdays != -1)
|
|
Packit |
6bd9ab |
{
|
|
Packit |
6bd9ab |
/* check maxdays */
|
|
Packit |
6bd9ab |
daysleft = lastchangedate + maxdays - today;
|
|
Packit |
6bd9ab |
if (daysleft == 0)
|
|
Packit |
6bd9ab |
mysnprintf(authzmsg, authzmsgsz - 1, "password will expire today");
|
|
Packit |
6bd9ab |
else if (daysleft < 0)
|
|
Packit |
6bd9ab |
mysnprintf(authzmsg, authzmsgsz - 1, "password expired %ld days ago",
|
|
Packit |
6bd9ab |
-daysleft);
|
|
Packit |
6bd9ab |
/* check inactdays */
|
|
Packit |
6bd9ab |
if ((daysleft <= 0) && (inactdays != -1))
|
|
Packit |
6bd9ab |
{
|
|
Packit |
6bd9ab |
inactleft = lastchangedate + maxdays + inactdays - today;
|
|
Packit |
6bd9ab |
if (inactleft == 0)
|
|
Packit |
6bd9ab |
mysnprintf(authzmsg + strlen(authzmsg), authzmsgsz - strlen(authzmsg) - 1,
|
|
Packit |
6bd9ab |
", account will be locked today");
|
|
Packit |
6bd9ab |
else if (inactleft > 0)
|
|
Packit |
6bd9ab |
mysnprintf(authzmsg + strlen(authzmsg), authzmsgsz - strlen(authzmsg) - 1,
|
|
Packit |
6bd9ab |
", account will be locked in %ld days", inactleft);
|
|
Packit |
6bd9ab |
else
|
|
Packit |
6bd9ab |
{
|
|
Packit |
6bd9ab |
mysnprintf(authzmsg + strlen(authzmsg), authzmsgsz - strlen(authzmsg) - 1,
|
|
Packit |
6bd9ab |
", account locked %ld days ago", -inactleft);
|
|
Packit |
6bd9ab |
log_log(LOG_WARNING, "%s: %s: %s", myldap_get_dn(entry),
|
|
Packit |
6bd9ab |
attmap_shadow_shadowInactive, authzmsg);
|
|
Packit |
6bd9ab |
return NSLCD_PAM_AUTHTOK_EXPIRED;
|
|
Packit |
6bd9ab |
}
|
|
Packit |
6bd9ab |
}
|
|
Packit |
6bd9ab |
if (daysleft <= 0)
|
|
Packit |
6bd9ab |
{
|
|
Packit |
6bd9ab |
/* log previously built message */
|
|
Packit |
6bd9ab |
log_log(LOG_WARNING, "%s: %s: %s",
|
|
Packit |
6bd9ab |
myldap_get_dn(entry), attmap_shadow_shadowMax, authzmsg);
|
|
Packit |
6bd9ab |
return NSLCD_PAM_NEW_AUTHTOK_REQD;
|
|
Packit |
6bd9ab |
}
|
|
Packit |
6bd9ab |
/* check warndays */
|
|
Packit |
6bd9ab |
if ((warndays > 0) && (daysleft <= warndays))
|
|
Packit |
6bd9ab |
{
|
|
Packit |
6bd9ab |
mysnprintf(authzmsg, authzmsgsz - 1,
|
|
Packit |
6bd9ab |
"password will expire in %ld days", daysleft);
|
|
Packit |
6bd9ab |
log_log(LOG_WARNING, "%s: %s: %s",
|
|
Packit |
6bd9ab |
myldap_get_dn(entry), attmap_shadow_shadowWarning, authzmsg);
|
|
Packit |
6bd9ab |
}
|
|
Packit |
6bd9ab |
}
|
|
Packit |
6bd9ab |
}
|
|
Packit |
6bd9ab |
if (check_mindays)
|
|
Packit |
6bd9ab |
{
|
|
Packit |
6bd9ab |
daysleft = lastchangedate + mindays - today;
|
|
Packit |
6bd9ab |
if ((mindays != -1) && (daysleft > 0))
|
|
Packit |
6bd9ab |
{
|
|
Packit |
6bd9ab |
mysnprintf(authzmsg, authzmsgsz - 1,
|
|
Packit |
6bd9ab |
"password cannot be changed for another %ld days", daysleft);
|
|
Packit |
6bd9ab |
log_log(LOG_WARNING, "%s: %s: %s",
|
|
Packit |
6bd9ab |
myldap_get_dn(entry), attmap_shadow_shadowMin, authzmsg);
|
|
Packit |
6bd9ab |
return NSLCD_PAM_AUTHTOK_ERR;
|
|
Packit |
6bd9ab |
}
|
|
Packit |
6bd9ab |
}
|
|
Packit |
6bd9ab |
return NSLCD_PAM_SUCCESS;
|
|
Packit |
6bd9ab |
}
|
|
Packit |
6bd9ab |
|
|
Packit |
6bd9ab |
/* check authentication credentials of the user */
|
|
Packit |
6bd9ab |
int nslcd_pam_authc(TFILE *fp, MYLDAP_SESSION *session, uid_t calleruid)
|
|
Packit |
6bd9ab |
{
|
|
Packit |
6bd9ab |
int32_t tmpint32;
|
|
Packit |
6bd9ab |
int rc;
|
|
Packit |
6bd9ab |
char username[BUFLEN_NAME], service[BUFLEN_NAME], ruser[BUFLEN_NAME], rhost[BUFLEN_HOSTNAME], tty[64];
|
|
Packit |
6bd9ab |
char password[BUFLEN_PASSWORD];
|
|
Packit |
6bd9ab |
const char *userdn;
|
|
Packit |
6bd9ab |
MYLDAP_ENTRY *entry;
|
|
Packit |
6bd9ab |
int authzrc = NSLCD_PAM_SUCCESS;
|
|
Packit |
6bd9ab |
char authzmsg[BUFLEN_MESSAGE];
|
|
Packit |
6bd9ab |
authzmsg[0] = '\0';
|
|
Packit |
6bd9ab |
/* read request parameters */
|
|
Packit |
6bd9ab |
READ_STRING(fp, username);
|
|
Packit |
6bd9ab |
READ_STRING(fp, service);
|
|
Packit |
6bd9ab |
READ_STRING(fp, ruser);
|
|
Packit |
6bd9ab |
READ_STRING(fp, rhost);
|
|
Packit |
6bd9ab |
READ_STRING(fp, tty);
|
|
Packit |
6bd9ab |
READ_STRING(fp, password);
|
|
Packit |
6bd9ab |
/* log call */
|
|
Packit |
6bd9ab |
log_setrequest("authc=\"%s\"", username);
|
|
Packit |
6bd9ab |
log_log(LOG_DEBUG, "nslcd_pam_authc(\"%s\",\"%s\",\"%s\")",
|
|
Packit |
6bd9ab |
username, service, *password ? "***" : "");
|
|
Packit |
6bd9ab |
/* write the response header */
|
|
Packit |
6bd9ab |
WRITE_INT32(fp, NSLCD_VERSION);
|
|
Packit |
6bd9ab |
WRITE_INT32(fp, NSLCD_ACTION_PAM_AUTHC);
|
|
Packit |
6bd9ab |
/* if the username is blank and rootpwmoddn is configured, try to
|
|
Packit |
6bd9ab |
authenticate as administrator, otherwise validate request as usual */
|
|
Packit |
6bd9ab |
if (*username == '\0')
|
|
Packit |
6bd9ab |
{
|
|
Packit |
6bd9ab |
if (nslcd_cfg->rootpwmoddn == NULL)
|
|
Packit |
6bd9ab |
{
|
|
Packit |
6bd9ab |
log_log(LOG_NOTICE, "rootpwmoddn not configured");
|
|
Packit |
6bd9ab |
/* we break the protocol */
|
|
Packit |
6bd9ab |
memset(password, 0, sizeof(password));
|
|
Packit |
6bd9ab |
return -1;
|
|
Packit |
6bd9ab |
}
|
|
Packit |
6bd9ab |
userdn = nslcd_cfg->rootpwmoddn;
|
|
Packit |
6bd9ab |
/* if the caller is root we will allow the use of the rootpwmodpw option */
|
|
Packit |
6bd9ab |
if ((*password == '\0') && (calleruid == 0) && (nslcd_cfg->rootpwmodpw != NULL))
|
|
Packit |
6bd9ab |
{
|
|
Packit |
6bd9ab |
if (strlen(nslcd_cfg->rootpwmodpw) >= sizeof(password))
|
|
Packit |
6bd9ab |
{
|
|
Packit |
6bd9ab |
log_log(LOG_ERR, "nslcd_pam_authc(): rootpwmodpw will not fit in password");
|
|
Packit |
6bd9ab |
memset(password, 0, sizeof(password));
|
|
Packit |
6bd9ab |
return -1;
|
|
Packit |
6bd9ab |
}
|
|
Packit |
6bd9ab |
strcpy(password, nslcd_cfg->rootpwmodpw);
|
|
Packit |
6bd9ab |
}
|
|
Packit |
6bd9ab |
}
|
|
Packit |
6bd9ab |
else
|
|
Packit |
6bd9ab |
{
|
|
Packit |
6bd9ab |
/* try normal authentication, lookup the user entry */
|
|
Packit |
6bd9ab |
entry = validate_user(session, username, &rc);
|
|
Packit |
6bd9ab |
if (entry == NULL)
|
|
Packit |
6bd9ab |
{
|
|
Packit |
6bd9ab |
/* for user not found we just say no result */
|
|
Packit |
6bd9ab |
if (rc == LDAP_NO_SUCH_OBJECT)
|
|
Packit |
6bd9ab |
{
|
|
Packit |
6bd9ab |
WRITE_INT32(fp, NSLCD_RESULT_END);
|
|
Packit |
6bd9ab |
}
|
|
Packit |
6bd9ab |
memset(password, 0, sizeof(password));
|
|
Packit |
6bd9ab |
return -1;
|
|
Packit |
6bd9ab |
}
|
|
Packit |
6bd9ab |
userdn = myldap_get_dn(entry);
|
|
Packit |
6bd9ab |
update_username(entry, username, sizeof(username));
|
|
Packit |
6bd9ab |
}
|
|
Packit |
6bd9ab |
/* try authentication */
|
|
Packit |
6bd9ab |
rc = try_bind(userdn, password, username, service, ruser, rhost, tty,
|
|
Packit |
6bd9ab |
&authzrc, authzmsg, sizeof(authzmsg));
|
|
Packit |
6bd9ab |
if (rc == LDAP_SUCCESS)
|
|
Packit |
6bd9ab |
log_log(LOG_DEBUG, "bind successful");
|
|
Packit |
6bd9ab |
/* map result code */
|
|
Packit |
6bd9ab |
switch (rc)
|
|
Packit |
6bd9ab |
{
|
|
Packit |
6bd9ab |
case LDAP_SUCCESS: rc = NSLCD_PAM_SUCCESS; break;
|
|
Packit |
6bd9ab |
case LDAP_INVALID_CREDENTIALS: rc = NSLCD_PAM_AUTH_ERR; break;
|
|
Packit |
6bd9ab |
default: rc = NSLCD_PAM_AUTH_ERR;
|
|
Packit |
6bd9ab |
}
|
|
Packit |
6bd9ab |
/* perform shadow attribute checks */
|
|
Packit |
6bd9ab |
if ((*username != '\0') && (authzrc == NSLCD_PAM_SUCCESS))
|
|
Packit |
6bd9ab |
authzrc = check_shadow(session, username, authzmsg, sizeof(authzmsg), 1, 0);
|
|
Packit |
6bd9ab |
/* write response */
|
|
Packit |
6bd9ab |
WRITE_INT32(fp, NSLCD_RESULT_BEGIN);
|
|
Packit |
6bd9ab |
WRITE_INT32(fp, rc);
|
|
Packit |
6bd9ab |
WRITE_STRING(fp, username);
|
|
Packit |
6bd9ab |
WRITE_INT32(fp, authzrc);
|
|
Packit |
6bd9ab |
WRITE_STRING(fp, authzmsg);
|
|
Packit |
6bd9ab |
WRITE_INT32(fp, NSLCD_RESULT_END);
|
|
Packit |
6bd9ab |
memset(password, 0, sizeof(password));
|
|
Packit |
6bd9ab |
return 0;
|
|
Packit |
6bd9ab |
}
|
|
Packit |
6bd9ab |
|
|
Packit |
6bd9ab |
/* perform an authorisation search, returns an LDAP status code */
|
|
Packit |
6bd9ab |
static int try_authz_search(MYLDAP_SESSION *session, const char *dn,
|
|
Packit |
6bd9ab |
const char *username, const char *service,
|
|
Packit |
6bd9ab |
const char *ruser, const char *rhost,
|
|
Packit |
6bd9ab |
const char *tty)
|
|
Packit |
6bd9ab |
{
|
|
Packit |
6bd9ab |
DICT *dict = NULL;
|
|
Packit |
6bd9ab |
char filter[BUFLEN_FILTER];
|
|
Packit |
6bd9ab |
int rc = LDAP_SUCCESS;
|
|
Packit |
6bd9ab |
const char *res;
|
|
Packit |
6bd9ab |
int i;
|
|
Packit |
6bd9ab |
/* go over all pam_authz_search options */
|
|
Packit |
6bd9ab |
for (i = 0; (i < NSS_LDAP_CONFIG_MAX_AUTHZ_SEARCHES) && (nslcd_cfg->pam_authz_searches[i] != NULL); i++)
|
|
Packit |
6bd9ab |
{
|
|
Packit |
6bd9ab |
if (dict == NULL)
|
|
Packit |
6bd9ab |
{
|
|
Packit |
6bd9ab |
dict = search_vars_new(dn, username, service, ruser, rhost, tty);
|
|
Packit |
6bd9ab |
if (dict == NULL)
|
|
Packit |
6bd9ab |
return LDAP_LOCAL_ERROR;
|
|
Packit |
6bd9ab |
}
|
|
Packit |
6bd9ab |
/* build the search filter */
|
|
Packit |
6bd9ab |
res = expr_parse(nslcd_cfg->pam_authz_searches[i],
|
|
Packit |
6bd9ab |
filter, sizeof(filter),
|
|
Packit |
6bd9ab |
search_var_get, (void *)dict);
|
|
Packit |
6bd9ab |
if (res == NULL)
|
|
Packit |
6bd9ab |
{
|
|
Packit |
6bd9ab |
search_vars_free(dict);
|
|
Packit |
6bd9ab |
log_log(LOG_ERR, "invalid pam_authz_search \"%s\"",
|
|
Packit |
6bd9ab |
nslcd_cfg->pam_authz_searches[i]);
|
|
Packit |
6bd9ab |
return LDAP_LOCAL_ERROR;
|
|
Packit |
6bd9ab |
}
|
|
Packit |
6bd9ab |
/* perform the actual searches on all bases */
|
|
Packit |
6bd9ab |
rc = do_searches(session, "pam_authz_search", filter);
|
|
Packit |
6bd9ab |
if (rc != LDAP_SUCCESS)
|
|
Packit |
6bd9ab |
break;
|
|
Packit |
6bd9ab |
}
|
|
Packit |
6bd9ab |
/* we went over all pam_authz_search entries */
|
|
Packit |
6bd9ab |
if (dict != NULL)
|
|
Packit |
6bd9ab |
search_vars_free(dict);
|
|
Packit |
6bd9ab |
return rc;
|
|
Packit |
6bd9ab |
}
|
|
Packit |
6bd9ab |
|
|
Packit |
6bd9ab |
/* check authorisation of the user */
|
|
Packit |
6bd9ab |
int nslcd_pam_authz(TFILE *fp, MYLDAP_SESSION *session)
|
|
Packit |
6bd9ab |
{
|
|
Packit |
6bd9ab |
int32_t tmpint32;
|
|
Packit |
6bd9ab |
int rc;
|
|
Packit |
6bd9ab |
char username[BUFLEN_NAME], service[BUFLEN_NAME], ruser[BUFLEN_NAME], rhost[BUFLEN_HOSTNAME], tty[64];
|
|
Packit |
6bd9ab |
MYLDAP_ENTRY *entry;
|
|
Packit |
6bd9ab |
char authzmsg[BUFLEN_MESSAGE];
|
|
Packit |
6bd9ab |
authzmsg[0] = '\0';
|
|
Packit |
6bd9ab |
/* read request parameters */
|
|
Packit |
6bd9ab |
READ_STRING(fp, username);
|
|
Packit |
6bd9ab |
READ_STRING(fp, service);
|
|
Packit |
6bd9ab |
READ_STRING(fp, ruser);
|
|
Packit |
6bd9ab |
READ_STRING(fp, rhost);
|
|
Packit |
6bd9ab |
READ_STRING(fp, tty);
|
|
Packit |
6bd9ab |
/* log call */
|
|
Packit |
6bd9ab |
log_setrequest("authz=\"%s\"", username);
|
|
Packit |
6bd9ab |
log_log(LOG_DEBUG, "nslcd_pam_authz(\"%s\",\"%s\",\"%s\",\"%s\",\"%s\")",
|
|
Packit |
6bd9ab |
username, service, ruser, rhost, tty);
|
|
Packit |
6bd9ab |
/* write the response header */
|
|
Packit |
6bd9ab |
WRITE_INT32(fp, NSLCD_VERSION);
|
|
Packit |
6bd9ab |
WRITE_INT32(fp, NSLCD_ACTION_PAM_AUTHZ);
|
|
Packit |
6bd9ab |
/* validate request */
|
|
Packit |
6bd9ab |
entry = validate_user(session, username, &rc);
|
|
Packit |
6bd9ab |
if (entry == NULL)
|
|
Packit |
6bd9ab |
{
|
|
Packit |
6bd9ab |
/* for user not found we just say no result */
|
|
Packit |
6bd9ab |
if (rc == LDAP_NO_SUCH_OBJECT)
|
|
Packit |
6bd9ab |
{
|
|
Packit |
6bd9ab |
WRITE_INT32(fp, NSLCD_RESULT_END);
|
|
Packit |
6bd9ab |
}
|
|
Packit |
6bd9ab |
return -1;
|
|
Packit |
6bd9ab |
}
|
|
Packit |
6bd9ab |
/* check authorisation search */
|
|
Packit |
6bd9ab |
rc = try_authz_search(session, myldap_get_dn(entry), username, service, ruser,
|
|
Packit |
6bd9ab |
rhost, tty);
|
|
Packit |
6bd9ab |
if (rc != LDAP_SUCCESS)
|
|
Packit |
6bd9ab |
{
|
|
Packit |
6bd9ab |
WRITE_INT32(fp, NSLCD_RESULT_BEGIN);
|
|
Packit |
6bd9ab |
WRITE_INT32(fp, NSLCD_PAM_PERM_DENIED);
|
|
Packit |
6bd9ab |
WRITE_STRING(fp, "LDAP authorisation check failed");
|
|
Packit |
6bd9ab |
WRITE_INT32(fp, NSLCD_RESULT_END);
|
|
Packit |
6bd9ab |
return 0;
|
|
Packit |
6bd9ab |
}
|
|
Packit |
6bd9ab |
/* perform shadow attribute checks */
|
|
Packit |
6bd9ab |
rc = check_shadow(session, username, authzmsg, sizeof(authzmsg), 0, 0);
|
|
Packit |
6bd9ab |
/* write response */
|
|
Packit |
6bd9ab |
WRITE_INT32(fp, NSLCD_RESULT_BEGIN);
|
|
Packit |
6bd9ab |
WRITE_INT32(fp, rc);
|
|
Packit |
6bd9ab |
WRITE_STRING(fp, authzmsg);
|
|
Packit |
6bd9ab |
WRITE_INT32(fp, NSLCD_RESULT_END);
|
|
Packit |
6bd9ab |
return 0;
|
|
Packit |
6bd9ab |
}
|
|
Packit |
6bd9ab |
|
|
Packit |
6bd9ab |
int nslcd_pam_sess_o(TFILE *fp, MYLDAP_SESSION UNUSED(*session))
|
|
Packit |
6bd9ab |
{
|
|
Packit |
6bd9ab |
int32_t tmpint32;
|
|
Packit |
6bd9ab |
char username[BUFLEN_NAME], service[BUFLEN_NAME], ruser[BUFLEN_NAME], rhost[BUFLEN_HOSTNAME], tty[64];
|
|
Packit |
6bd9ab |
char sessionid[25];
|
|
Packit |
6bd9ab |
static const char alphabet[] = "ABCDEFGHIJKLMNOPQRSTUVWXYZ"
|
|
Packit |
6bd9ab |
"abcdefghijklmnopqrstuvwxyz"
|
|
Packit |
6bd9ab |
"01234567890";
|
|
Packit |
6bd9ab |
unsigned int i;
|
|
Packit |
6bd9ab |
/* read request parameters */
|
|
Packit |
6bd9ab |
READ_STRING(fp, username);
|
|
Packit |
6bd9ab |
READ_STRING(fp, service);
|
|
Packit |
6bd9ab |
READ_STRING(fp, ruser);
|
|
Packit |
6bd9ab |
READ_STRING(fp, rhost);
|
|
Packit |
6bd9ab |
READ_STRING(fp, tty);
|
|
Packit |
6bd9ab |
/* generate pseudo-random session id */
|
|
Packit |
6bd9ab |
for (i = 0; i < (sizeof(sessionid) - 1); i++)
|
|
Packit |
6bd9ab |
sessionid[i] = alphabet[rand() % (sizeof(alphabet) - 1)];
|
|
Packit |
6bd9ab |
sessionid[i] = '\0';
|
|
Packit |
6bd9ab |
/* log call */
|
|
Packit |
6bd9ab |
log_setrequest("sess_o=\"%s\"", username);
|
|
Packit |
6bd9ab |
log_log(LOG_DEBUG, "nslcd_pam_sess_o(\"%s\",\"%s\",\"%s\",\"%s\",\"%s\"): %s",
|
|
Packit |
6bd9ab |
username, service, tty, rhost, ruser, sessionid);
|
|
Packit |
6bd9ab |
/* write the response header */
|
|
Packit |
6bd9ab |
WRITE_INT32(fp, NSLCD_VERSION);
|
|
Packit |
6bd9ab |
WRITE_INT32(fp, NSLCD_ACTION_PAM_SESS_O);
|
|
Packit |
6bd9ab |
/* write response */
|
|
Packit |
6bd9ab |
WRITE_INT32(fp, NSLCD_RESULT_BEGIN);
|
|
Packit |
6bd9ab |
WRITE_STRING(fp, sessionid);
|
|
Packit |
6bd9ab |
WRITE_INT32(fp, NSLCD_RESULT_END);
|
|
Packit |
6bd9ab |
return 0;
|
|
Packit |
6bd9ab |
}
|
|
Packit |
6bd9ab |
|
|
Packit |
6bd9ab |
int nslcd_pam_sess_c(TFILE *fp, MYLDAP_SESSION UNUSED(*session))
|
|
Packit |
6bd9ab |
{
|
|
Packit |
6bd9ab |
int32_t tmpint32;
|
|
Packit |
6bd9ab |
char username[BUFLEN_NAME], service[BUFLEN_NAME], ruser[BUFLEN_NAME], rhost[BUFLEN_HOSTNAME], tty[64];
|
|
Packit |
6bd9ab |
char sessionid[64];
|
|
Packit |
6bd9ab |
/* read request parameters */
|
|
Packit |
6bd9ab |
READ_STRING(fp, username);
|
|
Packit |
6bd9ab |
READ_STRING(fp, service);
|
|
Packit |
6bd9ab |
READ_STRING(fp, ruser);
|
|
Packit |
6bd9ab |
READ_STRING(fp, rhost);
|
|
Packit |
6bd9ab |
READ_STRING(fp, tty);
|
|
Packit |
6bd9ab |
READ_STRING(fp, sessionid);
|
|
Packit |
6bd9ab |
/* log call */
|
|
Packit |
6bd9ab |
log_setrequest("sess_c=\"%s\"", username);
|
|
Packit |
6bd9ab |
log_log(LOG_DEBUG, "nslcd_pam_sess_c(\"%s\",\"%s\",%s)",
|
|
Packit |
6bd9ab |
username, service, sessionid);
|
|
Packit |
6bd9ab |
/* write the response header */
|
|
Packit |
6bd9ab |
WRITE_INT32(fp, NSLCD_VERSION);
|
|
Packit |
6bd9ab |
WRITE_INT32(fp, NSLCD_ACTION_PAM_SESS_C);
|
|
Packit |
6bd9ab |
/* write response */
|
|
Packit |
6bd9ab |
WRITE_INT32(fp, NSLCD_RESULT_BEGIN);
|
|
Packit |
6bd9ab |
WRITE_INT32(fp, NSLCD_RESULT_END);
|
|
Packit |
6bd9ab |
return 0;
|
|
Packit |
6bd9ab |
}
|
|
Packit |
6bd9ab |
|
|
Packit |
6bd9ab |
extern const char *shadow_filter;
|
|
Packit |
6bd9ab |
|
|
Packit |
6bd9ab |
/* try to update the shadowLastChange attribute of the entry if possible */
|
|
Packit |
6bd9ab |
static int update_lastchange(MYLDAP_SESSION *session, const char *userdn)
|
|
Packit |
6bd9ab |
{
|
|
Packit |
6bd9ab |
MYLDAP_SEARCH *search;
|
|
Packit |
6bd9ab |
MYLDAP_ENTRY *entry;
|
|
Packit |
6bd9ab |
static const char *attrs[3];
|
|
Packit |
6bd9ab |
const char *attr;
|
|
Packit |
6bd9ab |
int rc;
|
|
Packit |
6bd9ab |
const char **values;
|
|
Packit |
6bd9ab |
LDAPMod mod, *mods[2];
|
|
Packit |
6bd9ab |
char buffer[64], *strvals[2];
|
|
Packit |
6bd9ab |
/* find the name of the attribute to use */
|
|
Packit |
6bd9ab |
if ((attmap_shadow_shadowLastChange == NULL) || (attmap_shadow_shadowLastChange[0] == '\0'))
|
|
Packit |
6bd9ab |
return LDAP_LOCAL_ERROR; /* attribute not mapped at all */
|
|
Packit |
6bd9ab |
else if (strcmp(attmap_shadow_shadowLastChange, "\"${shadowLastChange:--1}\"") == 0)
|
|
Packit |
6bd9ab |
attr = "shadowLastChange";
|
|
Packit |
6bd9ab |
else if (attmap_shadow_shadowLastChange[0] == '\"')
|
|
Packit |
6bd9ab |
return LDAP_LOCAL_ERROR; /* other expressions not supported for now */
|
|
Packit |
6bd9ab |
else
|
|
Packit |
6bd9ab |
attr = attmap_shadow_shadowLastChange;
|
|
Packit |
6bd9ab |
/* set up the attributes we need */
|
|
Packit |
6bd9ab |
attrs[0] = attmap_shadow_uid;
|
|
Packit |
6bd9ab |
attrs[1] = attr;
|
|
Packit |
6bd9ab |
attrs[2] = NULL;
|
|
Packit |
6bd9ab |
/* find the entry to see if the attribute is present */
|
|
Packit |
6bd9ab |
search = myldap_search(session, userdn, LDAP_SCOPE_BASE, shadow_filter, attrs, &rc);
|
|
Packit |
6bd9ab |
if (search == NULL)
|
|
Packit |
6bd9ab |
return rc;
|
|
Packit |
6bd9ab |
entry = myldap_get_entry(search, &rc);
|
|
Packit |
6bd9ab |
if (entry == NULL)
|
|
Packit |
6bd9ab |
return rc;
|
|
Packit |
6bd9ab |
values = myldap_get_values(entry, attr);
|
|
Packit |
6bd9ab |
if ((values == NULL) || (values[0] == NULL) || (values[0][0] == '\0'))
|
|
Packit |
6bd9ab |
return LDAP_NO_SUCH_ATTRIBUTE;
|
|
Packit |
6bd9ab |
/* build the value for the new attribute */
|
|
Packit |
6bd9ab |
if (strcasecmp(attr, "pwdLastSet") == 0)
|
|
Packit |
6bd9ab |
{
|
|
Packit |
6bd9ab |
/* for AD we use another timestamp */
|
|
Packit |
6bd9ab |
if (mysnprintf(buffer, sizeof(buffer), "%ld000000000",
|
|
Packit |
6bd9ab |
((long int)time(NULL) / 100L + (134774L * 864L))))
|
|
Packit |
6bd9ab |
return LDAP_LOCAL_ERROR;
|
|
Packit |
6bd9ab |
}
|
|
Packit |
6bd9ab |
else
|
|
Packit |
6bd9ab |
{
|
|
Packit |
6bd9ab |
/* time in days since Jan 1, 1970 */
|
|
Packit |
6bd9ab |
if (mysnprintf(buffer, sizeof(buffer), "%ld",
|
|
Packit |
6bd9ab |
((long int)(time(NULL) / (long int)(60 * 60 * 24)))))
|
|
Packit |
6bd9ab |
return LDAP_LOCAL_ERROR;
|
|
Packit |
6bd9ab |
}
|
|
Packit |
6bd9ab |
/* update the shadowLastChange attribute */
|
|
Packit |
6bd9ab |
strvals[0] = buffer;
|
|
Packit |
6bd9ab |
strvals[1] = NULL;
|
|
Packit |
6bd9ab |
mod.mod_op = LDAP_MOD_REPLACE;
|
|
Packit |
6bd9ab |
mod.mod_type = (char *)attr;
|
|
Packit |
6bd9ab |
mod.mod_values = strvals;
|
|
Packit |
6bd9ab |
mods[0] = &mod;
|
|
Packit |
6bd9ab |
mods[1] = NULL;
|
|
Packit |
6bd9ab |
rc = myldap_modify(session, userdn, mods);
|
|
Packit |
6bd9ab |
if (rc != LDAP_SUCCESS)
|
|
Packit |
6bd9ab |
log_log(LOG_WARNING, "%s: %s: modification failed: %s",
|
|
Packit |
6bd9ab |
userdn, attr, ldap_err2string(rc));
|
|
Packit |
6bd9ab |
else
|
|
Packit |
6bd9ab |
log_log(LOG_DEBUG, "%s: %s: modification succeeded", userdn, attr);
|
|
Packit |
6bd9ab |
return rc;
|
|
Packit |
6bd9ab |
}
|
|
Packit |
6bd9ab |
|
|
Packit |
6bd9ab |
/* perform an LDAP password modification, returns an LDAP status code */
|
|
Packit |
6bd9ab |
static int try_pwmod(MYLDAP_SESSION *oldsession,
|
|
Packit |
6bd9ab |
const char *binddn, const char *userdn,
|
|
Packit |
6bd9ab |
const char *oldpassword, const char *newpassword,
|
|
Packit |
6bd9ab |
char *authzmsg, size_t authzmsg_len)
|
|
Packit |
6bd9ab |
{
|
|
Packit |
6bd9ab |
MYLDAP_SESSION *session;
|
|
Packit |
6bd9ab |
char buffer[BUFLEN_MESSAGE];
|
|
Packit |
6bd9ab |
int rc;
|
|
Packit |
6bd9ab |
/* set up a new connection */
|
|
Packit |
6bd9ab |
session = myldap_create_session();
|
|
Packit |
6bd9ab |
if (session == NULL)
|
|
Packit |
6bd9ab |
return LDAP_UNAVAILABLE;
|
|
Packit |
6bd9ab |
/* perform a BIND operation */
|
|
Packit |
6bd9ab |
rc = myldap_bind(session, binddn, oldpassword, NULL, NULL);
|
|
Packit |
6bd9ab |
if (rc == LDAP_SUCCESS)
|
|
Packit |
6bd9ab |
{
|
|
Packit |
6bd9ab |
/* if doing password modification as admin, don't pass old password along */
|
|
Packit |
6bd9ab |
if ((nslcd_cfg->rootpwmoddn != NULL) &&
|
|
Packit |
6bd9ab |
(strcmp(binddn, nslcd_cfg->rootpwmoddn) == 0))
|
|
Packit |
6bd9ab |
oldpassword = NULL;
|
|
Packit |
6bd9ab |
/* perform password modification */
|
|
Packit |
6bd9ab |
rc = myldap_passwd(session, userdn, oldpassword, newpassword);
|
|
Packit |
6bd9ab |
if (rc == LDAP_SUCCESS)
|
|
Packit |
6bd9ab |
{
|
|
Packit |
6bd9ab |
/* try to update the shadowLastChange attribute */
|
|
Packit |
6bd9ab |
if (update_lastchange(session, userdn) != LDAP_SUCCESS)
|
|
Packit |
6bd9ab |
/* retry with the normal session */
|
|
Packit |
6bd9ab |
(void)update_lastchange(oldsession, userdn);
|
|
Packit |
6bd9ab |
}
|
|
Packit |
6bd9ab |
else
|
|
Packit |
6bd9ab |
{
|
|
Packit |
6bd9ab |
/* get a diagnostic or error message */
|
|
Packit |
6bd9ab |
if ((myldap_error_message(session, rc, buffer, sizeof(buffer)) == LDAP_SUCCESS) &&
|
|
Packit |
6bd9ab |
(buffer[0] != '\0'))
|
|
Packit |
6bd9ab |
mysnprintf(authzmsg, authzmsg_len - 1, "password change failed: %s",
|
|
Packit |
6bd9ab |
buffer);
|
|
Packit |
6bd9ab |
}
|
|
Packit |
6bd9ab |
}
|
|
Packit |
6bd9ab |
/* close the session */
|
|
Packit |
6bd9ab |
myldap_session_close(session);
|
|
Packit |
6bd9ab |
/* return */
|
|
Packit |
6bd9ab |
return rc;
|
|
Packit |
6bd9ab |
}
|
|
Packit |
6bd9ab |
|
|
Packit |
6bd9ab |
int nslcd_pam_pwmod(TFILE *fp, MYLDAP_SESSION *session, uid_t calleruid)
|
|
Packit |
6bd9ab |
{
|
|
Packit |
6bd9ab |
int32_t tmpint32;
|
|
Packit |
6bd9ab |
int rc;
|
|
Packit |
6bd9ab |
char username[BUFLEN_NAME], service[BUFLEN_NAME], ruser[BUFLEN_NAME], rhost[BUFLEN_HOSTNAME], tty[64];
|
|
Packit |
6bd9ab |
int asroot;
|
|
Packit |
6bd9ab |
char oldpassword[BUFLEN_PASSWORD];
|
|
Packit |
6bd9ab |
char newpassword[BUFLEN_PASSWORD];
|
|
Packit |
6bd9ab |
const char *binddn = NULL; /* the user performing the modification */
|
|
Packit |
6bd9ab |
MYLDAP_ENTRY *entry;
|
|
Packit |
6bd9ab |
char authzmsg[BUFLEN_MESSAGE];
|
|
Packit |
6bd9ab |
authzmsg[0] = '\0';
|
|
Packit |
6bd9ab |
/* read request parameters */
|
|
Packit |
6bd9ab |
READ_STRING(fp, username);
|
|
Packit |
6bd9ab |
READ_STRING(fp, service);
|
|
Packit |
6bd9ab |
READ_STRING(fp, ruser);
|
|
Packit |
6bd9ab |
READ_STRING(fp, rhost);
|
|
Packit |
6bd9ab |
READ_STRING(fp, tty);
|
|
Packit |
6bd9ab |
READ_INT32(fp, asroot);
|
|
Packit |
6bd9ab |
READ_STRING(fp, oldpassword);
|
|
Packit |
6bd9ab |
READ_STRING(fp, newpassword);
|
|
Packit |
6bd9ab |
/* log call */
|
|
Packit |
6bd9ab |
log_setrequest("pwmod=\"%s\"", username);
|
|
Packit |
6bd9ab |
log_log(LOG_DEBUG, "nslcd_pam_pwmod(\"%s\",%s,\"%s\",\"%s\",\"%s\")",
|
|
Packit |
6bd9ab |
username, asroot ? "asroot" : "asuser", service,
|
|
Packit |
6bd9ab |
*oldpassword ? "***" : "", *newpassword ? "***" : "");
|
|
Packit |
6bd9ab |
/* write the response header */
|
|
Packit |
6bd9ab |
WRITE_INT32(fp, NSLCD_VERSION);
|
|
Packit |
6bd9ab |
WRITE_INT32(fp, NSLCD_ACTION_PAM_PWMOD);
|
|
Packit |
6bd9ab |
/* validate request */
|
|
Packit |
6bd9ab |
entry = validate_user(session, username, &rc);
|
|
Packit |
6bd9ab |
if (entry == NULL)
|
|
Packit |
6bd9ab |
{
|
|
Packit |
6bd9ab |
/* for user not found we just say no result */
|
|
Packit |
6bd9ab |
if (rc == LDAP_NO_SUCH_OBJECT)
|
|
Packit |
6bd9ab |
{
|
|
Packit |
6bd9ab |
WRITE_INT32(fp, NSLCD_RESULT_END);
|
|
Packit |
6bd9ab |
}
|
|
Packit |
6bd9ab |
memset(oldpassword, 0, sizeof(oldpassword));
|
|
Packit |
6bd9ab |
memset(newpassword, 0, sizeof(newpassword));
|
|
Packit |
6bd9ab |
return -1;
|
|
Packit |
6bd9ab |
}
|
|
Packit |
6bd9ab |
/* check if pam_password_prohibit_message is set */
|
|
Packit |
6bd9ab |
if (nslcd_cfg->pam_password_prohibit_message != NULL)
|
|
Packit |
6bd9ab |
{
|
|
Packit |
6bd9ab |
log_log(LOG_NOTICE, "password change prohibited");
|
|
Packit |
6bd9ab |
WRITE_INT32(fp, NSLCD_RESULT_BEGIN);
|
|
Packit |
6bd9ab |
WRITE_INT32(fp, NSLCD_PAM_PERM_DENIED);
|
|
Packit |
6bd9ab |
WRITE_STRING(fp, nslcd_cfg->pam_password_prohibit_message);
|
|
Packit |
6bd9ab |
WRITE_INT32(fp, NSLCD_RESULT_END);
|
|
Packit |
6bd9ab |
memset(oldpassword, 0, sizeof(oldpassword));
|
|
Packit |
6bd9ab |
memset(newpassword, 0, sizeof(newpassword));
|
|
Packit |
6bd9ab |
return 0;
|
|
Packit |
6bd9ab |
}
|
|
Packit |
6bd9ab |
/* check if the the user passed the rootpwmoddn */
|
|
Packit |
6bd9ab |
if (asroot)
|
|
Packit |
6bd9ab |
{
|
|
Packit |
6bd9ab |
binddn = nslcd_cfg->rootpwmoddn;
|
|
Packit |
6bd9ab |
/* check if rootpwmodpw should be used */
|
|
Packit |
6bd9ab |
if ((*oldpassword == '\0') && (calleruid == 0) &&
|
|
Packit |
6bd9ab |
(nslcd_cfg->rootpwmodpw != NULL))
|
|
Packit |
6bd9ab |
{
|
|
Packit |
6bd9ab |
if (strlen(nslcd_cfg->rootpwmodpw) >= sizeof(oldpassword))
|
|
Packit |
6bd9ab |
{
|
|
Packit |
6bd9ab |
log_log(LOG_ERR, "nslcd_pam_pwmod(): rootpwmodpw will not fit in oldpassword");
|
|
Packit |
6bd9ab |
memset(oldpassword, 0, sizeof(oldpassword));
|
|
Packit |
6bd9ab |
memset(newpassword, 0, sizeof(newpassword));
|
|
Packit |
6bd9ab |
return -1;
|
|
Packit |
6bd9ab |
}
|
|
Packit |
6bd9ab |
strcpy(oldpassword, nslcd_cfg->rootpwmodpw);
|
|
Packit |
6bd9ab |
}
|
|
Packit |
6bd9ab |
}
|
|
Packit |
6bd9ab |
else
|
|
Packit |
6bd9ab |
{
|
|
Packit |
6bd9ab |
binddn = myldap_get_dn(entry);
|
|
Packit |
6bd9ab |
/* check whether shadow properties allow password change */
|
|
Packit |
6bd9ab |
rc = check_shadow(session, username, authzmsg, sizeof(authzmsg), 0, 1);
|
|
Packit |
6bd9ab |
if (rc != NSLCD_PAM_SUCCESS)
|
|
Packit |
6bd9ab |
{
|
|
Packit |
6bd9ab |
WRITE_INT32(fp, NSLCD_RESULT_BEGIN);
|
|
Packit |
6bd9ab |
WRITE_INT32(fp, rc);
|
|
Packit |
6bd9ab |
WRITE_STRING(fp, authzmsg);
|
|
Packit |
6bd9ab |
WRITE_INT32(fp, NSLCD_RESULT_END);
|
|
Packit |
6bd9ab |
memset(oldpassword, 0, sizeof(oldpassword));
|
|
Packit |
6bd9ab |
memset(newpassword, 0, sizeof(newpassword));
|
|
Packit |
6bd9ab |
return 0;
|
|
Packit |
6bd9ab |
}
|
|
Packit |
6bd9ab |
}
|
|
Packit |
6bd9ab |
/* perform password modification */
|
|
Packit |
6bd9ab |
rc = try_pwmod(session, binddn, myldap_get_dn(entry), oldpassword, newpassword,
|
|
Packit |
6bd9ab |
authzmsg, sizeof(authzmsg));
|
|
Packit |
6bd9ab |
if (rc != LDAP_SUCCESS)
|
|
Packit |
6bd9ab |
{
|
|
Packit |
6bd9ab |
if (authzmsg[0] == '\0')
|
|
Packit |
6bd9ab |
mysnprintf(authzmsg, sizeof(authzmsg) - 1, "password change failed: %s",
|
|
Packit |
6bd9ab |
ldap_err2string(rc));
|
|
Packit |
6bd9ab |
WRITE_INT32(fp, NSLCD_RESULT_BEGIN);
|
|
Packit |
6bd9ab |
WRITE_INT32(fp, NSLCD_PAM_PERM_DENIED);
|
|
Packit |
6bd9ab |
WRITE_STRING(fp, authzmsg);
|
|
Packit |
6bd9ab |
WRITE_INT32(fp, NSLCD_RESULT_END);
|
|
Packit |
6bd9ab |
memset(oldpassword, 0, sizeof(oldpassword));
|
|
Packit |
6bd9ab |
memset(newpassword, 0, sizeof(newpassword));
|
|
Packit |
6bd9ab |
return 0;
|
|
Packit |
6bd9ab |
}
|
|
Packit |
6bd9ab |
/* write response */
|
|
Packit |
6bd9ab |
log_log(LOG_NOTICE, "password changed for %s", myldap_get_dn(entry));
|
|
Packit |
6bd9ab |
WRITE_INT32(fp, NSLCD_RESULT_BEGIN);
|
|
Packit |
6bd9ab |
WRITE_INT32(fp, NSLCD_PAM_SUCCESS);
|
|
Packit |
6bd9ab |
WRITE_STRING(fp, "");
|
|
Packit |
6bd9ab |
WRITE_INT32(fp, NSLCD_RESULT_END);
|
|
Packit |
6bd9ab |
memset(oldpassword, 0, sizeof(oldpassword));
|
|
Packit |
6bd9ab |
memset(newpassword, 0, sizeof(newpassword));
|
|
Packit |
6bd9ab |
return 0;
|
|
Packit |
6bd9ab |
}
|