<commit_before>/*
   passwd.c - password entry lookup routines
   Parts of this file were part of the nss_ldap library (as ldap-pwd.c)
   which has been forked into the nss-pam-ldapd library.

   Copyright (C) 1997-2005 <NAME>
   Copyright (C) 2006 West Consulting
   Copyright (C) 2006, 2007, 2008, 2009, 2010 <NAME>

   This library is free software; you can redistribute it and/or
   modify it under the terms of the GNU Lesser General Public
   License as published by the Free Software Foundation; either
   version 2.1 of the License, or (at your option) any later version.

   This library is distributed in the hope that it will be useful,
   but WITHOUT ANY WARRANTY; without even the implied warranty of
   MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
   Lesser General Public License for more details.

   You should have received a copy of the GNU Lesser General Public
   License along with this library; if not, write to the Free Software
   Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA
   02110-1301 USA
*/

#include "config.h"

#include <stdio.h>
#include <stdlib.h>
#include <unistd.h>
#include <sys/types.h>
#include <string.h>
#include <pthread.h>

#include "common.h"
#include "log.h"
#include "myldap.h"
#include "cfg.h"
#include "attmap.h"
#include "common/dict.h"

/* ( nisSchema.2.0 NAME 'posixAccount' SUP top AUXILIARY
 *   DESC 'Abstraction of an account with POSIX attributes'
 *   MUST ( cn $ uid $ uidNumber $ gidNumber $ homeDirectory )
 *   MAY ( userPassword $ loginShell $ gecos $ description ) )
 */

/* the search base for searches */
const char *passwd_bases[NSS_LDAP_CONFIG_MAX_BASES] = { NULL };

/* the search scope for searches */
int passwd_scope = LDAP_SCOPE_DEFAULT;

/* the basic search filter for searches */
const char *passwd_filter = "(objectClass=posixAccount)";

/* the attributes used in searches */
const char *attmap_passwd_uid           = "uid";
const char *attmap_passwd_userPassword  = "<PASSWORD>";
const char *attmap_passwd_uidNumber     = "uidNumber";
const char *attmap_passwd_gidNumber     = "gidNumber";
const char *attmap_passwd_gecos         = "\"${gecos:-$cn}\"";
const char *attmap_passwd_homeDirectory = "homeDirectory";
const char *attmap_passwd_loginShell    = "loginShell";

/* default values for attributes */
static const char *default_passwd_userPassword     = "*"; /* unmatchable */

/* Note that the resulting password value should be one of:
   <empty> - no password set, allow login without password
   *       - often used to prevent logins
   x       - "valid" encrypted password that does not match any valid password
             often used to indicate that the password is defined elsewhere
   other   - encrypted password, usually in crypt(3) format */

/* the attribute list to request with searches */
static const char **passwd_attrs=NULL;

/* create a search filter for searching a passwd entry
   by name, return -1 on errors */
static int mkfilter_passwd_byname(const char *name,
                                  char *buffer,size_t buflen)
{
  char safename[300];
  /* escape attribute */
  if(myldap_escape(name,safename,sizeof(safename)))
    return -1;
  /* build filter */
  return mysnprintf(buffer,buflen,
                    "(&%s(%s=%s))",
                    passwd_filter,
                    attmap_passwd_uid,safename);
}

/* create a search filter for searching a passwd entry
   by uid, return -1 on errors */
static int mkfilter_passwd_byuid(uid_t uid,
                                 char *buffer,size_t buflen)
{
  return mysnprintf(buffer,buflen,
                    "(&%s(%s=%d))",
                    passwd_filter,
                    attmap_passwd_uidNumber,(int)uid);
}

void passwd_init(void)
{
  int i;
  SET *set;
  /* set up search bases */
  if (passwd_bases[0]==NULL)
    for (i=0;i<NSS_LDAP_CONFIG_MAX_BASES;i++)
      passwd_bases[i]=nslcd_cfg->ldc_bases[i];
  /* set up scope */
  if (passwd_scope==LDAP_SCOPE_DEFAULT)
    passwd_scope=nslcd_cfg->ldc_scope;
  /* set up attribute list */
  set=set_new();
  attmap_add_attributes(set,"objectClass"); /* for testing shadowAccount */
  attmap_add_attributes(set,attmap_passwd_uid);
  attmap_add_attributes(set,attmap_passwd_userPassword);
  attmap_add_attributes(set,attmap_passwd_uidNumber);
  attmap_add_attributes(set,attmap_passwd_gidNumber);
  attmap_add_attributes(set,attmap_passwd_gecos);
  attmap_add_attributes(set,attmap_passwd_homeDirectory);
  attmap_add_attributes(set,attmap_passwd_loginShell);
  passwd_attrs=set_tolist(set);
  set_free(set);
}

/* the cache that is used in dn2uid() */
static pthread_mutex_t dn2uid_cache_mutex=PTHREAD_MUTEX_INITIALIZER;
static DICT *dn2uid_cache=NULL;
struct dn2uid_cache_entry
{
  time_t timestamp;
  char *uid;
};
#define DN2UID_CACHE_TIMEOUT (15*60)

/* Perform an LDAP lookup to translate the DN into a uid.
   This function either returns NULL or a strdup()ed string. */
char *lookup_dn2uid(MYLDAP_SESSION *session,const char *dn,int *rcp)
{
  MYLDAP_SEARCH *search;
  MYLDAP_ENTRY *entry;
  static const char *attrs[2];
  int rc=LDAP_SUCCESS;
  const char **values;
  char *uid;
  if (rcp==NULL)
    rcp=&rc;
  /* we have to look up the entry */
  attrs[0]=attmap_passwd_uid;
  attrs[1]=NULL;
  search=myldap_search(session,dn,LDAP_SCOPE_BASE,passwd_filter,attrs,rcp);
  if (search==NULL)
  {
    log_log(LOG_WARNING,"lookup of user %s failed: %s",dn,ldap_err2string(*rcp));
    return NULL;
  }
  entry=myldap_get_entry(search,rcp);
  if (entry==NULL)
  {
    if (*rcp!=LDAP_SUCCESS)
      log_log(LOG_WARNING,"lookup of user %s failed: %s",dn,ldap_err2string(*rcp));
    return NULL;
  }
  /* get uid (just use first one) */
  values=myldap_get_values(entry,attmap_passwd_uid);
  /* check the result for presence and validity */
  if ((values!=NULL)&&(values[0]!=NULL)&&isvalidname(values[0]))
    uid=strdup(values[0]);
  else
    uid=NULL;
  myldap_search_close(search);
  return uid;
}

/* Translate the DN into a user name. This function tries several aproaches
   at getting the user name, including looking in the DN for a uid attribute,
   looking in the cache and falling back to looking up a uid attribute in a
   LDAP query. */
char *dn2uid(MYLDAP_SESSION *session,const char *dn,char *buf,size_t buflen)
{
  struct dn2uid_cache_entry *cacheentry=NULL;
  char *uid;
  /* check for empty string */
  if ((dn==NULL)||(*dn=='\0'))
    return NULL;
  /* try to look up uid within DN string */
  if (myldap_cpy_rdn_value(dn,attmap_passwd_uid,buf,buflen)!=NULL)
  {
    /* check if it is valid */
    if (!isvalidname(buf))
      return NULL;
    return buf;
  }
  /* see if we have a cached entry */
  pthread_mutex_lock(&dn2uid_cache_mutex);
  if (dn2uid_cache==NULL)
    dn2uid_cache=dict_new();
  if ((dn2uid_cache!=NULL) && ((cacheentry=dict_get(dn2uid_cache,dn))!=NULL))
  {
    /* if the cached entry is still valid, return that */
    if (time(NULL) < (cacheentry->timestamp+DN2UID_CACHE_TIMEOUT))
    {
      if ((cacheentry->uid!=NULL)&&(strlen(cacheentry->uid)<buflen))
        strcpy(buf,cacheentry->uid);
      else
        buf=NULL;
      pthread_mutex_unlock(&dn2uid_cache_mutex);
      return buf;
    }
    /* leave the entry intact, just replace the uid below */
  }
  pthread_mutex_unlock(&dn2uid_cache_mutex);
  /* look up the uid using an LDAP query */
  uid=lookup_dn2uid(session,dn,NULL);
  /* store the result in the cache */
  pthread_mutex_lock(&dn2uid_cache_mutex);
  if (cacheentry==NULL)
  {
    /* allocate a new entry in the cache */
    cacheentry=(struct dn2uid_cache_entry *)malloc(sizeof(struct dn2uid_cache_entry));
    if (cacheentry!=NULL)
      dict_put(dn2uid_cache,dn,cacheentry);
  }
  else if (cacheentry->uid!=NULL)
    free(cacheentry->uid);
  /* update the cache entry */
  if (cacheentry!=NULL)
  {
    cacheentry->timestamp=time(NULL);
    cacheentry->uid=uid;
  }
  pthread_mutex_unlock(&dn2uid_cache_mutex);
  /* copy the result into the buffer */
  if ((uid!=NULL)&&(strlen(uid)<buflen))
    strcpy(buf,uid);
  else
    buf=NULL;
  return buf;
}

MYLDAP_ENTRY *uid2entry(MYLDAP_SESSION *session,const char *uid)
{
  MYLDAP_SEARCH *search=NULL;<commit_msg>fix race condition that could cause a memory leak<commit_after>/*
   passwd.c - password entry lookup routines
   Parts of this file were part of the nss_ldap library (as ldap-pwd.c)
   which has been forked into the nss-pam-ldapd library.

   Copyright (C) 1997-2005 <NAME>ard
   Copyright (C) 2006 West Consulting
   Copyright (C) 2006, 2007, 2008, 2009, 2010 <NAME>

   This library is free software; you can redistribute it and/or
   modify it under the terms of the GNU Lesser General Public
   License as published by the Free Software Foundation; either
   version 2.1 of the License, or (at your option) any later version.

   This library is distributed in the hope that it will be useful,
   but WITHOUT ANY WARRANTY; without even the implied warranty of
   MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
   Lesser General Public License for more details.

   You should have received a copy of the GNU Lesser General Public
   License along with this library; if not, write to the Free Software
   Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA
   02110-1301 USA
*/

#include "config.h"

#include <stdio.h>
#include <stdlib.h>
#include <unistd.h>
#include <sys/types.h>
#include <string.h>
#include <pthread.h>

#include "common.h"
#include "log.h"
#include "myldap.h"
#include "cfg.h"
#include "attmap.h"
#include "common/dict.h"

/* ( nisSchema.2.0 NAME 'posixAccount' SUP top AUXILIARY
 *   DESC 'Abstraction of an account with POSIX attributes'
 *   MUST ( cn $ uid $ uidNumber $ gidNumber $ homeDirectory )
 *   MAY ( userPassword $ loginShell $ gecos $ description ) )
 */

/* the search base for searches */
const char *passwd_bases[NSS_LDAP_CONFIG_MAX_BASES] = { NULL };

/* the search scope for searches */
int passwd_scope = LDAP_SCOPE_DEFAULT;

/* the basic search filter for searches */
const char *passwd_filter = "(objectClass=posixAccount)";

/* the attributes used in searches */
const char *attmap_passwd_uid           = "uid";
const char *attmap_passwd_userPassword  = "<PASSWORD>";
const char *attmap_passwd_uidNumber     = "uidNumber";
const char *attmap_passwd_gidNumber     = "gidNumber";
const char *attmap_passwd_gecos         = "\"${gecos:-$cn}\"";
const char *attmap_passwd_homeDirectory = "homeDirectory";
const char *attmap_passwd_loginShell    = "loginShell";

/* default values for attributes */
static const char *default_passwd_userPassword     = "*"; /* unmatchable */

/* Note that the resulting password value should be one of:
   <empty> - no password set, allow login without password
   *       - often used to prevent logins
   x       - "valid" encrypted password that does not match any valid password
             often used to indicate that the password is defined elsewhere
   other   - encrypted password, usually in crypt(3) format */

/* the attribute list to request with searches */
static const char **passwd_attrs=NULL;

/* create a search filter for searching a passwd entry
   by name, return -1 on errors */
static int mkfilter_passwd_byname(const char *name,
                                  char *buffer,size_t buflen)
{
  char safename[300];
  /* escape attribute */
  if(myldap_escape(name,safename,sizeof(safename)))
    return -1;
  /* build filter */
  return mysnprintf(buffer,buflen,
                    "(&%s(%s=%s))",
                    passwd_filter,
                    attmap_passwd_uid,safename);
}

/* create a search filter for searching a passwd entry
   by uid, return -1 on errors */
static int mkfilter_passwd_byuid(uid_t uid,
                                 char *buffer,size_t buflen)
{
  return mysnprintf(buffer,buflen,
                    "(&%s(%s=%d))",
                    passwd_filter,
                    attmap_passwd_uidNumber,(int)uid);
}

void passwd_init(void)
{
  int i;
  SET *set;
  /* set up search bases */
  if (passwd_bases[0]==NULL)
    for (i=0;i<NSS_LDAP_CONFIG_MAX_BASES;i++)
      passwd_bases[i]=nslcd_cfg->ldc_bases[i];
  /* set up scope */
  if (passwd_scope==LDAP_SCOPE_DEFAULT)
    passwd_scope=nslcd_cfg->ldc_scope;
  /* set up attribute list */
  set=set_new();
  attmap_add_attributes(set,"objectClass"); /* for testing shadowAccount */
  attmap_add_attributes(set,attmap_passwd_uid);
  attmap_add_attributes(set,attmap_passwd_userPassword);
  attmap_add_attributes(set,attmap_passwd_uidNumber);
  attmap_add_attributes(set,attmap_passwd_gidNumber);
  attmap_add_attributes(set,attmap_passwd_gecos);
  attmap_add_attributes(set,attmap_passwd_homeDirectory);
  attmap_add_attributes(set,attmap_passwd_loginShell);
  passwd_attrs=set_tolist(set);
  set_free(set);
}

/* the cache that is used in dn2uid() */
static pthread_mutex_t dn2uid_cache_mutex=PTHREAD_MUTEX_INITIALIZER;
static DICT *dn2uid_cache=NULL;
struct dn2uid_cache_entry
{
  time_t timestamp;
  char *uid;
};
#define DN2UID_CACHE_TIMEOUT (15*60)

/* Perform an LDAP lookup to translate the DN into a uid.
   This function either returns NULL or a strdup()ed string. */
char *lookup_dn2uid(MYLDAP_SESSION *session,const char *dn,int *rcp)
{
  MYLDAP_SEARCH *search;
  MYLDAP_ENTRY *entry;
  static const char *attrs[2];
  int rc=LDAP_SUCCESS;
  const char **values;
  char *uid;
  if (rcp==NULL)
    rcp=&rc;
  /* we have to look up the entry */
  attrs[0]=attmap_passwd_uid;
  attrs[1]=NULL;
  search=myldap_search(session,dn,LDAP_SCOPE_BASE,passwd_filter,attrs,rcp);
  if (search==NULL)
  {
    log_log(LOG_WARNING,"lookup of user %s failed: %s",dn,ldap_err2string(*rcp));
    return NULL;
  }
  entry=myldap_get_entry(search,rcp);
  if (entry==NULL)
  {
    if (*rcp!=LDAP_SUCCESS)
      log_log(LOG_WARNING,"lookup of user %s failed: %s",dn,ldap_err2string(*rcp));
    return NULL;
  }
  /* get uid (just use first one) */
  values=myldap_get_values(entry,attmap_passwd_uid);
  /* check the result for presence and validity */
  if ((values!=NULL)&&(values[0]!=NULL)&&isvalidname(values[0]))
    uid=strdup(values[0]);
  else
    uid=NULL;
  myldap_search_close(search);
  return uid;
}

/* Translate the DN into a user name. This function tries several aproaches
   at getting the user name, including looking in the DN for a uid attribute,
   looking in the cache and falling back to looking up a uid attribute in a
   LDAP query. */
char *dn2uid(MYLDAP_SESSION *session,const char *dn,char *buf,size_t buflen)
{
  struct dn2uid_cache_entry *cacheentry=NULL;
  char *uid;
  /* check for empty string */
  if ((dn==NULL)||(*dn=='\0'))
    return NULL;
  /* try to look up uid within DN string */
  if (myldap_cpy_rdn_value(dn,attmap_passwd_uid,buf,buflen)!=NULL)
  {
    /* check if it is valid */
    if (!isvalidname(buf))
      return NULL;
    return buf;
  }
  /* see if we have a cached entry */
  pthread_mutex_lock(&dn2uid_cache_mutex);
  if (dn2uid_cache==NULL)
    dn2uid_cache=dict_new();
  if ((dn2uid_cache!=NULL) && ((cacheentry=dict_get(dn2uid_cache,dn))!=NULL))
  {
    /* if the cached entry is still valid, return that */
    if (time(NULL) < (cacheentry->timestamp+DN2UID_CACHE_TIMEOUT))
    {
      if ((cacheentry->uid!=NULL)&&(strlen(cacheentry->uid)<buflen))
        strcpy(buf,cacheentry->uid);
      else
        buf=NULL;
      pthread_mutex_unlock(&dn2uid_cache_mutex);
      return buf;
    }
    /* leave the entry intact, just replace the uid below */
  }
  pthread_mutex_unlock(&dn2uid_cache_mutex);
  /* look up the uid using an LDAP query */
  uid=lookup_dn2uid(session,dn,NULL);
  /* store the result in the cache */
  pthread_mutex_lock(&dn2uid_cache_mutex);
  /* try to get the entry from the cache here again because it could have
     changed in the meantime */
  cacheentry=dict_get(dn2uid_cache,dn);
  if (cacheentry==NULL)
  {
    /* allocate a new entry in the cache */
    cacheentry=(struct dn2uid_cache_entry *)malloc(sizeof(struct dn2uid_cache_entry));
    if (cacheentry!=NULL)
      dict_put(dn2uid_cache,dn,cacheentry);
  }
  else if (cacheentry->uid!=NULL)
    free(cacheentry->uid);
  /* update the cache entry */
  if (cacheentry!=NULL)
  {
    cacheentry->timestamp=time(NULL);
    cacheentry->uid=uid;
  }
  pthread_mutex_unlock(&dn2uid_cache_mutex);
  /* copy the result into the buffer */
  if ((uid!=NULL)&&(strlen(uid)<buflen))
    strcpy(buf,uid);
  else
    buf=NULL;
  return buf;
}

MYLDAP_ENTRY *uid2entry(MYLDAP_SESSION *session,const char *uid)
{
  MYLDAP_SEARCH *search=NULL;
{'fraction_non_alphanumeric': 0.10897552130553037, 'fraction_numerical': 0.01118162586884255, 'mean_word_length': 3.734191702432046, 'pattern_counts': {'":': 0, '<': 31, '<?xml version=': 0, '>': 43, 'https://': 0, 'lorem ipsum': 0, 'www.': 0, 'xml': 0}, 'pii_count': 0, 'substrings_counts': 0, 'word_list_counts': {'cursed_substrings.json': 4, 'profanity_word_list.json': 0, 'sexual_word_list.json': 0, 'zh_pornsignals.json': 0}}
{'dir': 'git-commits-cleaned', 'id': '1623794', 'n_tokens_mistral': 5691, 'n_tokens_neox': 5535, 'n_words': 1542}