Blame nslcd/shadow.c

Packit 6bd9ab
/*
Packit 6bd9ab
   shadow.c - shadow entry lookup routines
Packit 6bd9ab
   Parts of this file were part of the nss_ldap library (as ldap-spwd.c)
Packit 6bd9ab
   which has been forked into the nss-pam-ldapd library.
Packit 6bd9ab
Packit 6bd9ab
   Copyright (C) 1997-2005 Luke Howard
Packit 6bd9ab
   Copyright (C) 2006 West Consulting
Packit 6bd9ab
   Copyright (C) 2006-2014 Arthur de Jong
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
#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
Packit 6bd9ab
/* ( nisSchema.2.1 NAME 'shadowAccount' SUP top AUXILIARY
Packit 6bd9ab
 *   DESC 'Additional attributes for shadow passwords'
Packit 6bd9ab
 *   MUST uid
Packit 6bd9ab
 *   MAY ( userPassword $ shadowLastChange $ shadowMin
Packit 6bd9ab
 *         shadowMax $ shadowWarning $ shadowInactive $
Packit 6bd9ab
 *         shadowExpire $ shadowFlag $ description ) )
Packit 6bd9ab
 */
Packit 6bd9ab
Packit 6bd9ab
/* the search base for searches */
Packit 6bd9ab
const char *shadow_bases[NSS_LDAP_CONFIG_MAX_BASES] = { NULL };
Packit 6bd9ab
Packit 6bd9ab
/* the search scope for searches */
Packit 6bd9ab
int shadow_scope = LDAP_SCOPE_DEFAULT;
Packit 6bd9ab
Packit 6bd9ab
/* the basic search filter for searches */
Packit 6bd9ab
const char *shadow_filter = "(objectClass=shadowAccount)";
Packit 6bd9ab
Packit 6bd9ab
/* the attributes to request with searches */
Packit 6bd9ab
const char *attmap_shadow_uid              = "uid";
Packit 6bd9ab
const char *attmap_shadow_userPassword     = "\"*\"";
Packit 6bd9ab
const char *attmap_shadow_shadowLastChange = "\"${shadowLastChange:--1}\"";
Packit 6bd9ab
const char *attmap_shadow_shadowMin        = "\"${shadowMin:--1}\"";
Packit 6bd9ab
const char *attmap_shadow_shadowMax        = "\"${shadowMax:--1}\"";
Packit 6bd9ab
const char *attmap_shadow_shadowWarning    = "\"${shadowWarning:--1}\"";
Packit 6bd9ab
const char *attmap_shadow_shadowInactive   = "\"${shadowInactive:--1}\"";
Packit 6bd9ab
const char *attmap_shadow_shadowExpire     = "\"${shadowExpire:--1}\"";
Packit 6bd9ab
const char *attmap_shadow_shadowFlag       = "\"${shadowFlag:-0}\"";
Packit 6bd9ab
Packit 6bd9ab
/* default values for attributes */
Packit 6bd9ab
static const char *default_shadow_userPassword = "*"; /* unmatchable */
Packit 6bd9ab
Packit 6bd9ab
/* the attribute list to request with searches */
Packit 6bd9ab
static const char **shadow_attrs = NULL;
Packit 6bd9ab
Packit 6bd9ab
static int mkfilter_shadow_byname(const char *name, char *buffer, size_t buflen)
Packit 6bd9ab
{
Packit 6bd9ab
  char safename[BUFLEN_SAFENAME];
Packit 6bd9ab
  /* escape attribute */
Packit 6bd9ab
  if (myldap_escape(name, safename, sizeof(safename)))
Packit 6bd9ab
  {
Packit 6bd9ab
    log_log(LOG_ERR, "mkfilter_shadow_byname(): safename buffer too small");
Packit 6bd9ab
    return -1;
Packit 6bd9ab
  }
Packit 6bd9ab
  /* build filter */
Packit 6bd9ab
  return mysnprintf(buffer, buflen, "(&%s(%s=%s))",
Packit 6bd9ab
                    shadow_filter, attmap_shadow_uid, safename);
Packit 6bd9ab
}
Packit 6bd9ab
Packit 6bd9ab
void shadow_init(void)
Packit 6bd9ab
{
Packit 6bd9ab
  int i;
Packit 6bd9ab
  SET *set;
Packit 6bd9ab
  /* set up search bases */
Packit 6bd9ab
  if (shadow_bases[0] == NULL)
Packit 6bd9ab
    for (i = 0; i < NSS_LDAP_CONFIG_MAX_BASES; i++)
Packit 6bd9ab
      shadow_bases[i] = nslcd_cfg->bases[i];
Packit 6bd9ab
  /* set up scope */
Packit 6bd9ab
  if (shadow_scope == LDAP_SCOPE_DEFAULT)
Packit 6bd9ab
    shadow_scope = nslcd_cfg->scope;
Packit 6bd9ab
  /* set up attribute list */
Packit 6bd9ab
  set = set_new();
Packit 6bd9ab
  attmap_add_attributes(set, attmap_shadow_uid);
Packit 6bd9ab
  attmap_add_attributes(set, attmap_shadow_userPassword);
Packit 6bd9ab
  attmap_add_attributes(set, attmap_shadow_shadowLastChange);
Packit 6bd9ab
  attmap_add_attributes(set, attmap_shadow_shadowMax);
Packit 6bd9ab
  attmap_add_attributes(set, attmap_shadow_shadowMin);
Packit 6bd9ab
  attmap_add_attributes(set, attmap_shadow_shadowWarning);
Packit 6bd9ab
  attmap_add_attributes(set, attmap_shadow_shadowInactive);
Packit 6bd9ab
  attmap_add_attributes(set, attmap_shadow_shadowExpire);
Packit 6bd9ab
  attmap_add_attributes(set, attmap_shadow_shadowFlag);
Packit 6bd9ab
  shadow_attrs = set_tolist(set);
Packit 6bd9ab
  if (shadow_attrs == NULL)
Packit 6bd9ab
  {
Packit 6bd9ab
    log_log(LOG_CRIT, "malloc() failed to allocate memory");
Packit 6bd9ab
    exit(EXIT_FAILURE);
Packit 6bd9ab
  }
Packit 6bd9ab
  set_free(set);
Packit 6bd9ab
}
Packit 6bd9ab
Packit 6bd9ab
static long to_date(const char *dn, const char *date, const char *attr)
Packit 6bd9ab
{
Packit 6bd9ab
  char buffer[32];
Packit 6bd9ab
  long value;
Packit 6bd9ab
  char *tmp;
Packit 6bd9ab
  size_t l;
Packit 6bd9ab
  /* do some special handling for date values on AD */
Packit 6bd9ab
  if (strcasecmp(attr, "pwdLastSet") == 0)
Packit 6bd9ab
  {
Packit 6bd9ab
    /* we expect an AD 64-bit datetime value;
Packit 6bd9ab
       we should do date=date/864000000000-134774
Packit 6bd9ab
       but that causes problems on 32-bit platforms,
Packit 6bd9ab
       first we divide by 1000000000 by stripping the
Packit 6bd9ab
       last 9 digits from the string and going from there */
Packit 6bd9ab
    l = strlen(date) - 9;
Packit 6bd9ab
    if (l > (sizeof(buffer) - 1))
Packit 6bd9ab
      return -1; /* error */
Packit 6bd9ab
    strncpy(buffer, date, l);
Packit 6bd9ab
    buffer[l] = '\0';
Packit 6bd9ab
    errno = 0;
Packit 6bd9ab
    value = strtol(buffer, &tmp, 10);
Packit 6bd9ab
    if ((*date == '\0') || (*tmp != '\0'))
Packit 6bd9ab
    {
Packit 6bd9ab
      log_log(LOG_WARNING, "%s: %s: non-numeric", dn, attr);
Packit 6bd9ab
      return -1;
Packit 6bd9ab
    }
Packit 6bd9ab
    else if (errno != 0)
Packit 6bd9ab
    {
Packit 6bd9ab
      log_log(LOG_WARNING, "%s: %s: out of range", dn, attr);
Packit 6bd9ab
      return -1;
Packit 6bd9ab
    }
Packit 6bd9ab
    return value / 864 - 134774;
Packit 6bd9ab
    /* note that AD does not have expiry dates but a lastchangeddate
Packit 6bd9ab
       and some value that needs to be added */
Packit 6bd9ab
  }
Packit 6bd9ab
  errno = 0;
Packit 6bd9ab
  value = strtol(date, &tmp, 10);
Packit 6bd9ab
  if ((*date == '\0') || (*tmp != '\0'))
Packit 6bd9ab
  {
Packit 6bd9ab
    log_log(LOG_WARNING, "%s: %s: non-numeric", dn, attr);
Packit 6bd9ab
    return -1;
Packit 6bd9ab
  }
Packit 6bd9ab
  else if (errno != 0)
Packit 6bd9ab
  {
Packit 6bd9ab
    log_log(LOG_WARNING, "%s: %s: out of range", dn, attr);
Packit 6bd9ab
    return -1;
Packit 6bd9ab
  }
Packit 6bd9ab
  return value;
Packit 6bd9ab
}
Packit 6bd9ab
Packit 6bd9ab
#ifndef UF_DONT_EXPIRE_PASSWD
Packit 6bd9ab
#define UF_DONT_EXPIRE_PASSWD 0x10000
Packit 6bd9ab
#endif
Packit 6bd9ab
Packit 6bd9ab
#define GET_OPTIONAL_LONG(var, att, fallback)                               \
Packit 6bd9ab
  tmpvalue = attmap_get_value(entry, attmap_shadow_##att,                   \
Packit 6bd9ab
                              buffer, sizeof(buffer));                      \
Packit 6bd9ab
  if (tmpvalue == NULL)                                                     \
Packit 6bd9ab
    tmpvalue = "";                                                          \
Packit 6bd9ab
  errno = 0;                                                                \
Packit 6bd9ab
  var = strtol(tmpvalue, &tmp, 10);                                         \
Packit 6bd9ab
  if ((*(tmpvalue) == '\0') || (*tmp != '\0'))                              \
Packit 6bd9ab
  {                                                                         \
Packit 6bd9ab
    log_log(LOG_WARNING, "%s: %s: non-numeric",                             \
Packit 6bd9ab
            myldap_get_dn(entry), attmap_shadow_##att);                     \
Packit 6bd9ab
    var = fallback;                                                         \
Packit 6bd9ab
  }                                                                         \
Packit 6bd9ab
  else if (errno != 0)                                                      \
Packit 6bd9ab
  {                                                                         \
Packit 6bd9ab
    log_log(LOG_WARNING, "%s: %s: out of range",                            \
Packit 6bd9ab
            myldap_get_dn(entry), attmap_shadow_##att);                     \
Packit 6bd9ab
    var = fallback;                                                         \
Packit 6bd9ab
  }
Packit 6bd9ab
Packit 6bd9ab
void get_shadow_properties(MYLDAP_ENTRY *entry, long *lastchangedate,
Packit 6bd9ab
                           long *mindays, long *maxdays, long *warndays,
Packit 6bd9ab
                           long *inactdays, long *expiredate,
Packit 6bd9ab
                           unsigned long *flag)
Packit 6bd9ab
{
Packit 6bd9ab
  char buffer[64];
Packit 6bd9ab
  const char *tmpvalue;
Packit 6bd9ab
  char *tmp;
Packit 6bd9ab
  /* get lastchange date */
Packit 6bd9ab
  tmpvalue = attmap_get_value(entry, attmap_shadow_shadowLastChange,
Packit 6bd9ab
                              buffer, sizeof(buffer));
Packit 6bd9ab
  if (tmpvalue == NULL)
Packit 6bd9ab
    tmpvalue = "";
Packit 6bd9ab
  *lastchangedate = to_date(myldap_get_dn(entry), tmpvalue, attmap_shadow_shadowLastChange);
Packit 6bd9ab
  /* get other shadow properties */
Packit 6bd9ab
  GET_OPTIONAL_LONG(*mindays, shadowMin, -1);
Packit 6bd9ab
  GET_OPTIONAL_LONG(*maxdays, shadowMax, -1);
Packit 6bd9ab
  GET_OPTIONAL_LONG(*warndays, shadowWarning, -1);
Packit 6bd9ab
  GET_OPTIONAL_LONG(*inactdays, shadowInactive, -1);
Packit 6bd9ab
  GET_OPTIONAL_LONG(*expiredate, shadowExpire, -1);
Packit 6bd9ab
  GET_OPTIONAL_LONG(*flag, shadowFlag, 0);
Packit 6bd9ab
  /* if we're using AD handle the flag specially */
Packit 6bd9ab
  if (strcasecmp(attmap_shadow_shadowLastChange, "pwdLastSet") == 0)
Packit 6bd9ab
  {
Packit 6bd9ab
    if (*flag & UF_DONT_EXPIRE_PASSWD)
Packit 6bd9ab
      *maxdays = -1;
Packit 6bd9ab
    *flag = 0;
Packit 6bd9ab
  }
Packit 6bd9ab
}
Packit 6bd9ab
Packit 6bd9ab
static int write_shadow(TFILE *fp, MYLDAP_ENTRY *entry, const char *requser,
Packit 6bd9ab
                        uid_t calleruid)
Packit 6bd9ab
{
Packit 6bd9ab
  int32_t tmpint32;
Packit 6bd9ab
  const char **usernames;
Packit 6bd9ab
  const char *passwd;
Packit 6bd9ab
  long lastchangedate;
Packit 6bd9ab
  long mindays;
Packit 6bd9ab
  long maxdays;
Packit 6bd9ab
  long warndays;
Packit 6bd9ab
  long inactdays;
Packit 6bd9ab
  long expiredate;
Packit 6bd9ab
  unsigned long flag;
Packit 6bd9ab
  int i;
Packit 6bd9ab
  char passbuffer[BUFLEN_PASSWORDHASH];
Packit 6bd9ab
  /* get username */
Packit 6bd9ab
  usernames = myldap_get_values(entry, attmap_shadow_uid);
Packit 6bd9ab
  if ((usernames == NULL) || (usernames[0] == NULL))
Packit 6bd9ab
  {
Packit 6bd9ab
    log_log(LOG_WARNING, "%s: %s: missing",
Packit 6bd9ab
            myldap_get_dn(entry), attmap_shadow_uid);
Packit 6bd9ab
    return 0;
Packit 6bd9ab
  }
Packit 6bd9ab
  /* get password */
Packit 6bd9ab
  passwd = get_userpassword(entry, attmap_shadow_userPassword,
Packit 6bd9ab
                            passbuffer, sizeof(passbuffer));
Packit 6bd9ab
  if ((passwd == NULL) || (calleruid != 0))
Packit 6bd9ab
    passwd = default_shadow_userPassword;
Packit 6bd9ab
  /* get expiry properties */
Packit 6bd9ab
  get_shadow_properties(entry, &lastchangedate, &mindays, &maxdays, &warndays,
Packit 6bd9ab
                        &inactdays, &expiredate, &flag;;
Packit 6bd9ab
  /* write the entries */
Packit 6bd9ab
  for (i = 0; usernames[i] != NULL; i++)
Packit 6bd9ab
    if ((requser == NULL) || (STR_CMP(requser, usernames[i]) == 0))
Packit 6bd9ab
    {
Packit 6bd9ab
      if (!isvalidname(usernames[i]))
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
      }
Packit 6bd9ab
      else
Packit 6bd9ab
      {
Packit 6bd9ab
        WRITE_INT32(fp, NSLCD_RESULT_BEGIN);
Packit 6bd9ab
        WRITE_STRING(fp, usernames[i]);
Packit 6bd9ab
        WRITE_STRING(fp, passwd);
Packit 6bd9ab
        WRITE_INT32(fp, lastchangedate);
Packit 6bd9ab
        WRITE_INT32(fp, mindays);
Packit 6bd9ab
        WRITE_INT32(fp, maxdays);
Packit 6bd9ab
        WRITE_INT32(fp, warndays);
Packit 6bd9ab
        WRITE_INT32(fp, inactdays);
Packit 6bd9ab
        WRITE_INT32(fp, expiredate);
Packit 6bd9ab
        WRITE_INT32(fp, flag);
Packit 6bd9ab
      }
Packit 6bd9ab
    }
Packit 6bd9ab
  return 0;
Packit 6bd9ab
}
Packit 6bd9ab
Packit 6bd9ab
MYLDAP_ENTRY *shadow_uid2entry(MYLDAP_SESSION *session, const char *username,
Packit 6bd9ab
                               int *rcp)
Packit 6bd9ab
{
Packit 6bd9ab
  MYLDAP_SEARCH *search = NULL;
Packit 6bd9ab
  MYLDAP_ENTRY *entry = NULL;
Packit 6bd9ab
  const char *base;
Packit 6bd9ab
  char filter[BUFLEN_FILTER];
Packit 6bd9ab
  int i;
Packit 6bd9ab
  /* if it isn't a valid username, just bail out now */
Packit 6bd9ab
  if (!isvalidname(username))
Packit 6bd9ab
  {
Packit 6bd9ab
    if (rcp != NULL)
Packit 6bd9ab
      *rcp = LDAP_INVALID_SYNTAX;
Packit 6bd9ab
    return NULL;
Packit 6bd9ab
  }
Packit 6bd9ab
  /* we have to look up the entry */
Packit 6bd9ab
  mkfilter_shadow_byname(username, filter, sizeof(filter));
Packit 6bd9ab
  for (i = 0; (i < NSS_LDAP_CONFIG_MAX_BASES) && ((base = shadow_bases[i]) != NULL); i++)
Packit 6bd9ab
  {
Packit 6bd9ab
    search = myldap_search(session, base, shadow_scope, filter, shadow_attrs, rcp);
Packit 6bd9ab
    if (search == NULL)
Packit 6bd9ab
    {
Packit 6bd9ab
      if ((rcp != NULL) && (*rcp == LDAP_SUCCESS))
Packit 6bd9ab
        *rcp = LDAP_NO_SUCH_OBJECT;
Packit 6bd9ab
      return NULL;
Packit 6bd9ab
    }
Packit 6bd9ab
    entry = myldap_get_entry(search, rcp);
Packit 6bd9ab
    if (entry != NULL)
Packit 6bd9ab
      return entry;
Packit 6bd9ab
  }
Packit 6bd9ab
  if ((rcp != NULL) && (*rcp == LDAP_SUCCESS))
Packit 6bd9ab
    *rcp = LDAP_NO_SUCH_OBJECT;
Packit 6bd9ab
  return NULL;
Packit 6bd9ab
}
Packit 6bd9ab
Packit 6bd9ab
NSLCD_HANDLE_UID(
Packit 6bd9ab
  shadow, byname, NSLCD_ACTION_SHADOW_BYNAME,
Packit 6bd9ab
  char name[BUFLEN_NAME];
Packit 6bd9ab
  char filter[BUFLEN_FILTER];
Packit 6bd9ab
  READ_STRING(fp, name);
Packit 6bd9ab
  log_setrequest("shadow=\"%s\"", name);
Packit 6bd9ab
  if (!isvalidname(name))
Packit 6bd9ab
  {
Packit 6bd9ab
    log_log(LOG_WARNING, "request denied by validnames option");
Packit 6bd9ab
    return -1;
Packit 6bd9ab
  },
Packit 6bd9ab
  mkfilter_shadow_byname(name, filter, sizeof(filter)),
Packit 6bd9ab
  write_shadow(fp, entry, name, calleruid)
Packit 6bd9ab
)
Packit 6bd9ab
Packit 6bd9ab
NSLCD_HANDLE_UID(
Packit 6bd9ab
  shadow, all, NSLCD_ACTION_SHADOW_ALL,
Packit 6bd9ab
  const char *filter;
Packit 6bd9ab
  log_setrequest("shadow(all)");,
Packit 6bd9ab
  (filter = shadow_filter, 0),
Packit 6bd9ab
  write_shadow(fp, entry, NULL, calleruid)
Packit 6bd9ab
)