Blame nslcd/common.c

Packit 6bd9ab
/*
Packit 6bd9ab
   common.c - common server code routines
Packit 6bd9ab
   This file is part of the nss-pam-ldapd library.
Packit 6bd9ab
Packit 6bd9ab
   Copyright (C) 2006 West Consulting
Packit 6bd9ab
   Copyright (C) 2006, 2007, 2008, 2009, 2010, 2011, 2012, 2013 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 <stdarg.h>
Packit 6bd9ab
#include <sys/types.h>
Packit 6bd9ab
#include <sys/socket.h>
Packit 6bd9ab
#include <netinet/in.h>
Packit 6bd9ab
#include <arpa/inet.h>
Packit 6bd9ab
#include <strings.h>
Packit 6bd9ab
#include <limits.h>
Packit 6bd9ab
#include <netdb.h>
Packit 6bd9ab
#include <string.h>
Packit 6bd9ab
#include <regex.h>
Packit 6bd9ab
#include <stdlib.h>
Packit 6bd9ab
#include <signal.h>
Packit 6bd9ab
Packit 6bd9ab
#include "nslcd.h"
Packit 6bd9ab
#include "common.h"
Packit 6bd9ab
#include "log.h"
Packit 6bd9ab
#include "attmap.h"
Packit 6bd9ab
#include "cfg.h"
Packit 6bd9ab
Packit 6bd9ab
/* simple wrapper around snptintf() to return non-zero in case
Packit 6bd9ab
   of any failure (but always keep string 0-terminated) */
Packit 6bd9ab
int mysnprintf(char *buffer, size_t buflen, const char *format, ...)
Packit 6bd9ab
{
Packit 6bd9ab
  int res;
Packit 6bd9ab
  va_list ap;
Packit 6bd9ab
  /* do snprintf */
Packit 6bd9ab
  va_start(ap, format);
Packit 6bd9ab
  res = vsnprintf(buffer, buflen, format, ap);
Packit 6bd9ab
  va_end(ap);
Packit 6bd9ab
  /* NULL-terminate the string just to be on the safe side */
Packit 6bd9ab
  buffer[buflen - 1] = '\0';
Packit 6bd9ab
  /* check if the string was completely written */
Packit 6bd9ab
  return ((res < 0) || (((size_t)res) >= buflen));
Packit 6bd9ab
}
Packit 6bd9ab
Packit 6bd9ab
/* get a name of a signal with a given signal number */
Packit 6bd9ab
const char *signame(int signum)
Packit 6bd9ab
{
Packit 6bd9ab
  switch (signum)
Packit 6bd9ab
  {
Packit 6bd9ab
    case SIGHUP:  return "SIGHUP";  /* Hangup detected */
Packit 6bd9ab
    case SIGINT:  return "SIGINT";  /* Interrupt from keyboard */
Packit 6bd9ab
    case SIGQUIT: return "SIGQUIT"; /* Quit from keyboard */
Packit 6bd9ab
    case SIGILL:  return "SIGILL";  /* Illegal Instruction */
Packit 6bd9ab
    case SIGABRT: return "SIGABRT"; /* Abort signal from abort(3) */
Packit 6bd9ab
    case SIGFPE:  return "SIGFPE";  /* Floating point exception */
Packit 6bd9ab
    case SIGKILL: return "SIGKILL"; /* Kill signal */
Packit 6bd9ab
    case SIGSEGV: return "SIGSEGV"; /* Invalid memory reference */
Packit 6bd9ab
    case SIGPIPE: return "SIGPIPE"; /* Broken pipe */
Packit 6bd9ab
    case SIGALRM: return "SIGALRM"; /* Timer signal from alarm(2) */
Packit 6bd9ab
    case SIGTERM: return "SIGTERM"; /* Termination signal */
Packit 6bd9ab
    case SIGUSR1: return "SIGUSR1"; /* User-defined signal 1 */
Packit 6bd9ab
    case SIGUSR2: return "SIGUSR2"; /* User-defined signal 2 */
Packit 6bd9ab
    case SIGCHLD: return "SIGCHLD"; /* Child stopped or terminated */
Packit 6bd9ab
    case SIGCONT: return "SIGCONT"; /* Continue if stopped */
Packit 6bd9ab
    case SIGSTOP: return "SIGSTOP"; /* Stop process */
Packit 6bd9ab
    case SIGTSTP: return "SIGTSTP"; /* Stop typed at tty */
Packit 6bd9ab
    case SIGTTIN: return "SIGTTIN"; /* tty input for background process */
Packit 6bd9ab
    case SIGTTOU: return "SIGTTOU"; /* tty output for background process */
Packit 6bd9ab
#ifdef SIGBUS
Packit 6bd9ab
    case SIGBUS:  return "SIGBUS";  /* Bus error */
Packit 6bd9ab
#endif
Packit 6bd9ab
#ifdef SIGPOLL
Packit 6bd9ab
    case SIGPOLL: return "SIGPOLL"; /* Pollable event */
Packit 6bd9ab
#endif
Packit 6bd9ab
#ifdef SIGPROF
Packit 6bd9ab
    case SIGPROF: return "SIGPROF"; /* Profiling timer expired */
Packit 6bd9ab
#endif
Packit 6bd9ab
#ifdef SIGSYS
Packit 6bd9ab
    case SIGSYS:  return "SIGSYS";  /* Bad argument to routine */
Packit 6bd9ab
#endif
Packit 6bd9ab
#ifdef SIGTRAP
Packit 6bd9ab
    case SIGTRAP: return "SIGTRAP"; /* Trace/breakpoint trap */
Packit 6bd9ab
#endif
Packit 6bd9ab
#ifdef SIGURG
Packit 6bd9ab
    case SIGURG:  return "SIGURG";  /* Urgent condition on socket */
Packit 6bd9ab
#endif
Packit 6bd9ab
#ifdef SIGVTALRM
Packit 6bd9ab
    case SIGVTALRM: return "SIGVTALRM"; /* Virtual alarm clock */
Packit 6bd9ab
#endif
Packit 6bd9ab
#ifdef SIGXCPU
Packit 6bd9ab
    case SIGXCPU: return "SIGXCPU"; /* CPU time limit exceeded */
Packit 6bd9ab
#endif
Packit 6bd9ab
#ifdef SIGXFSZ
Packit 6bd9ab
    case SIGXFSZ: return "SIGXFSZ"; /* File size limit exceeded */
Packit 6bd9ab
#endif
Packit 6bd9ab
    default:      return "UNKNOWN";
Packit 6bd9ab
  }
Packit 6bd9ab
}
Packit 6bd9ab
Packit 6bd9ab
/* return the fully qualified domain name of the current host */
Packit 6bd9ab
const char *getfqdn(void)
Packit 6bd9ab
{
Packit 6bd9ab
  static char *fqdn = NULL;
Packit 6bd9ab
  char hostname[BUFLEN_HOSTNAME];
Packit 6bd9ab
  int hostnamelen;
Packit 6bd9ab
  int i;
Packit 6bd9ab
  struct hostent *host = NULL;
Packit 6bd9ab
  /* if we already have a fqdn return that */
Packit 6bd9ab
  if (fqdn != NULL)
Packit 6bd9ab
    return fqdn;
Packit 6bd9ab
  /* get system hostname */
Packit 6bd9ab
  if (gethostname(hostname, sizeof(hostname)) < 0)
Packit 6bd9ab
  {
Packit 6bd9ab
    log_log(LOG_ERR, "gethostname() failed: %s", strerror(errno));
Packit 6bd9ab
    return NULL;
Packit 6bd9ab
  }
Packit 6bd9ab
  hostnamelen = strlen(hostname);
Packit 6bd9ab
  /* lookup hostent */
Packit 6bd9ab
  host = gethostbyname(hostname);
Packit 6bd9ab
  if (host == NULL)
Packit 6bd9ab
  {
Packit 6bd9ab
    log_log(LOG_ERR, "gethostbyname(%s): %s", hostname, hstrerror(h_errno));
Packit 6bd9ab
    /* fall back to hostname */
Packit 6bd9ab
    fqdn = strdup(hostname);
Packit 6bd9ab
    return fqdn;
Packit 6bd9ab
  }
Packit 6bd9ab
  /* check h_name for fqdn starting with our hostname */
Packit 6bd9ab
  if ((strncasecmp(hostname, host->h_name, hostnamelen) == 0) &&
Packit 6bd9ab
      (host->h_name[hostnamelen] == '.') &&
Packit 6bd9ab
      (host->h_name[hostnamelen + 1] != '\0'))
Packit 6bd9ab
  {
Packit 6bd9ab
    fqdn = strdup(host->h_name);
Packit 6bd9ab
    return fqdn;
Packit 6bd9ab
  }
Packit 6bd9ab
  /* also check h_aliases */
Packit 6bd9ab
  for (i = 0; host->h_aliases[i] != NULL; i++)
Packit 6bd9ab
  {
Packit 6bd9ab
    if ((strncasecmp(hostname, host->h_aliases[i], hostnamelen) == 0) &&
Packit 6bd9ab
        (host->h_aliases[i][hostnamelen] == '.') &&
Packit 6bd9ab
        (host->h_aliases[i][hostnamelen + 1] != '\0'))
Packit 6bd9ab
    {
Packit 6bd9ab
      fqdn = strdup(host->h_aliases[i]);
Packit 6bd9ab
      return fqdn;
Packit 6bd9ab
    }
Packit 6bd9ab
  }
Packit 6bd9ab
  /* fall back to h_name if it has a dot in it */
Packit 6bd9ab
  if (strchr(host->h_name, '.') != NULL)
Packit 6bd9ab
  {
Packit 6bd9ab
    fqdn = strdup(host->h_name);
Packit 6bd9ab
    return fqdn;
Packit 6bd9ab
  }
Packit 6bd9ab
  /* also check h_aliases */
Packit 6bd9ab
  for (i = 0; host->h_aliases[i] != NULL; i++)
Packit 6bd9ab
  {
Packit 6bd9ab
    if (strchr(host->h_aliases[i], '.') != NULL)
Packit 6bd9ab
    {
Packit 6bd9ab
      fqdn = strdup(host->h_aliases[i]);
Packit 6bd9ab
      return fqdn;
Packit 6bd9ab
    }
Packit 6bd9ab
  }
Packit 6bd9ab
  /* nothing found, fall back to hostname */
Packit 6bd9ab
  fqdn = strdup(hostname);
Packit 6bd9ab
  return fqdn;
Packit 6bd9ab
}
Packit 6bd9ab
Packit 6bd9ab
const char *get_userpassword(MYLDAP_ENTRY *entry, const char *attr,
Packit 6bd9ab
                             char *buffer, size_t buflen)
Packit 6bd9ab
{
Packit 6bd9ab
  const char *tmpvalue;
Packit 6bd9ab
  /* get the value */
Packit 6bd9ab
  tmpvalue = attmap_get_value(entry, attr, buffer, buflen);
Packit 6bd9ab
  if (tmpvalue == NULL)
Packit 6bd9ab
    return NULL;
Packit 6bd9ab
  /* go over the entries and return the remainder of the value if it
Packit 6bd9ab
     starts with {crypt} or crypt$ */
Packit 6bd9ab
  if (strncasecmp(tmpvalue, "{crypt}", 7) == 0)
Packit 6bd9ab
    return tmpvalue + 7;
Packit 6bd9ab
  if (strncasecmp(tmpvalue, "crypt$", 6) == 0)
Packit 6bd9ab
    return tmpvalue + 6;
Packit 6bd9ab
  /* just return the first value completely */
Packit 6bd9ab
  return tmpvalue;
Packit 6bd9ab
  /* TODO: support more password formats e.g. SMD5
Packit 6bd9ab
     (which is $1$ but in a different format)
Packit 6bd9ab
     (any code for this is more than welcome) */
Packit 6bd9ab
}
Packit 6bd9ab
Packit 6bd9ab
/* Checks if the specified name seems to be a valid user or group name. */
Packit 6bd9ab
int isvalidname(const char *name)
Packit 6bd9ab
{
Packit 6bd9ab
  return regexec(&nslcd_cfg->validnames, name, 0, NULL, 0) == 0;
Packit 6bd9ab
}
Packit 6bd9ab
Packit 6bd9ab
/* this writes a single address to the stream */
Packit 6bd9ab
int write_address(TFILE *fp, MYLDAP_ENTRY *entry, const char *attr,
Packit 6bd9ab
                  const char *addr)
Packit 6bd9ab
{
Packit 6bd9ab
  int32_t tmpint32;
Packit 6bd9ab
  struct in_addr ipv4addr;
Packit 6bd9ab
  struct in6_addr ipv6addr;
Packit 6bd9ab
  /* try to parse the address as IPv4 first, fall back to IPv6 */
Packit 6bd9ab
  if (inet_pton(AF_INET, addr, &ipv4addr) > 0)
Packit 6bd9ab
  {
Packit 6bd9ab
    /* write address type */
Packit 6bd9ab
    WRITE_INT32(fp, AF_INET);
Packit 6bd9ab
    /* write the address length */
Packit 6bd9ab
    WRITE_INT32(fp, sizeof(struct in_addr));
Packit 6bd9ab
    /* write the address itself (in network byte order) */
Packit 6bd9ab
    WRITE(fp, &ipv4addr, sizeof(struct in_addr));
Packit 6bd9ab
  }
Packit 6bd9ab
  else if (inet_pton(AF_INET6, addr, &ipv6addr) > 0)
Packit 6bd9ab
  {
Packit 6bd9ab
    /* write address type */
Packit 6bd9ab
    WRITE_INT32(fp, AF_INET6);
Packit 6bd9ab
    /* write the address length */
Packit 6bd9ab
    WRITE_INT32(fp, sizeof(struct in6_addr));
Packit 6bd9ab
    /* write the address itself (in network byte order) */
Packit 6bd9ab
    WRITE(fp, &ipv6addr, sizeof(struct in6_addr));
Packit 6bd9ab
  }
Packit 6bd9ab
  else
Packit 6bd9ab
  {
Packit 6bd9ab
    /* failure, log but write simple invalid address
Packit 6bd9ab
       (otherwise the address list is messed up) */
Packit 6bd9ab
    /* TODO: have error message in correct format */
Packit 6bd9ab
    log_log(LOG_WARNING, "%s: %s: \"%s\" unparsable",
Packit 6bd9ab
            myldap_get_dn(entry), attr, addr);
Packit 6bd9ab
    /* write an illegal address type */
Packit 6bd9ab
    WRITE_INT32(fp, -1);
Packit 6bd9ab
    /* write an emtpy address */
Packit 6bd9ab
    WRITE_INT32(fp, 0);
Packit 6bd9ab
  }
Packit 6bd9ab
  /* we're done */
Packit 6bd9ab
  return 0;
Packit 6bd9ab
}
Packit 6bd9ab
Packit 6bd9ab
int read_address(TFILE *fp, char *addr, int *addrlen, int *af)
Packit 6bd9ab
{
Packit 6bd9ab
  int32_t tmpint32;
Packit 6bd9ab
  int len;
Packit 6bd9ab
  /* read address family */
Packit 6bd9ab
  READ_INT32(fp, *af);
Packit 6bd9ab
  if ((*af != AF_INET) && (*af != AF_INET6))
Packit 6bd9ab
  {
Packit 6bd9ab
    log_log(LOG_WARNING, "incorrect address family specified: %d", *af);
Packit 6bd9ab
    return -1;
Packit 6bd9ab
  }
Packit 6bd9ab
  /* read address length */
Packit 6bd9ab
  READ_INT32(fp, len);
Packit 6bd9ab
  if ((len > *addrlen) || (len <= 0))
Packit 6bd9ab
  {
Packit 6bd9ab
    log_log(LOG_WARNING, "address length incorrect: %d", len);
Packit 6bd9ab
    return -1;
Packit 6bd9ab
  }
Packit 6bd9ab
  *addrlen = len;
Packit 6bd9ab
  /* read address */
Packit 6bd9ab
  READ(fp, addr, len);
Packit 6bd9ab
  /* we're done */
Packit 6bd9ab
  return 0;
Packit 6bd9ab
}
Packit 6bd9ab
Packit 6bd9ab
/* convert the provided string representation of a sid
Packit 6bd9ab
   (e.g. S-1-5-21-1936905831-823966427-12391542-23578)
Packit 6bd9ab
   to a format that can be used to search the objectSid property with */
Packit 6bd9ab
char *sid2search(const char *sid)
Packit 6bd9ab
{
Packit 6bd9ab
  const char *tmpsid = sid;
Packit 6bd9ab
  char *res, *tmp;
Packit 6bd9ab
  int i = 0;
Packit 6bd9ab
  unsigned long int l;
Packit 6bd9ab
  /* check the beginning of the string */
Packit 6bd9ab
  if (strncasecmp(sid, "S-", 2) != 0)
Packit 6bd9ab
  {
Packit 6bd9ab
    log_log(LOG_ERR, "error in SID %s", sid);
Packit 6bd9ab
    exit(EXIT_FAILURE);
Packit 6bd9ab
  }
Packit 6bd9ab
  /* count the number of dashes in the sid */
Packit 6bd9ab
  while (tmpsid != NULL)
Packit 6bd9ab
  {
Packit 6bd9ab
    i++;
Packit 6bd9ab
    tmpsid = strchr(tmpsid + 1, '-');
Packit 6bd9ab
  }
Packit 6bd9ab
  i -= 2; /* number of security ids plus one because we add the uid later */
Packit 6bd9ab
  /* allocate memory */
Packit 6bd9ab
  res = malloc(3 + 3 + 6 * 3 + i * 4 * 3 + 1);
Packit 6bd9ab
  if (res == NULL)
Packit 6bd9ab
  {
Packit 6bd9ab
    log_log(LOG_CRIT, "malloc() failed to allocate memory");
Packit 6bd9ab
    exit(1);
Packit 6bd9ab
  }
Packit 6bd9ab
  /* build the first part */
Packit 6bd9ab
  l = strtoul(sid + 2, &tmp, 10);
Packit 6bd9ab
  sprintf(res, "\\%02x\\%02x", (unsigned int)l & 0xff, (unsigned int)i);
Packit 6bd9ab
  /* build authority part (we only handle 32 of the 48 bits) */
Packit 6bd9ab
  l = strtoul(tmp + 1, &tmp, 10);
Packit 6bd9ab
  sprintf(res + strlen(res), "\\00\\00\\%02x\\%02x\\%02x\\%02x",
Packit 6bd9ab
          (unsigned int)((l >> 24) & 0xff),
Packit 6bd9ab
          (unsigned int)((l >> 16) & 0xff),
Packit 6bd9ab
          (unsigned int)((l >> 8) & 0xff),
Packit 6bd9ab
          (unsigned int)(l & 0xff));
Packit 6bd9ab
  /* go over the rest of the bits */
Packit 6bd9ab
  while (*tmp != '\0')
Packit 6bd9ab
  {
Packit 6bd9ab
    l = strtoul(tmp + 1, &tmp, 10);
Packit 6bd9ab
    sprintf(res + strlen(res), "\\%02x\\%02x\\%02x\\%02x",
Packit 6bd9ab
            (unsigned int)(l & 0xff),
Packit 6bd9ab
            (unsigned int)((l >> 8) & 0xff),
Packit 6bd9ab
            (unsigned int)((l >> 16) & 0xff),
Packit 6bd9ab
            (unsigned int)((l >> 24) & 0xff));
Packit 6bd9ab
  }
Packit 6bd9ab
  return res;
Packit 6bd9ab
}
Packit 6bd9ab
Packit 6bd9ab
/* return the last security identifier of the binary sid */
Packit 6bd9ab
unsigned long int binsid2id(const char *binsid)
Packit 6bd9ab
{
Packit 6bd9ab
  int i;
Packit 6bd9ab
  /* find the position of the last security id */
Packit 6bd9ab
  i = 2 + 6 + ((((unsigned int)binsid[1]) & 0xff) - 1) * 4;
Packit 6bd9ab
  return (((unsigned long int)binsid[i]) & 0xff) |
Packit 6bd9ab
         ((((unsigned long int)binsid[i + 1]) & 0xff) << 8) |
Packit 6bd9ab
         ((((unsigned long int)binsid[i + 2]) & 0xff) << 16) |
Packit 6bd9ab
         ((((unsigned long int)binsid[i + 3]) & 0xff) << 24);
Packit 6bd9ab
}
Packit 6bd9ab
Packit 8e28f4
/* provide a strtoid() implementation, similar to strtoul() but returning
Packit 8e28f4
   an range-checked uint32_t instead */
Packit 8e28f4
unsigned int strtoid(const char *nptr,char **endptr,int base)
Packit 6bd9ab
{
Packit 8e28f4
  long long val;
Packit 8e28f4
  /* use the fact that long long is 64-bit, even on 32-bit systems */
Packit 8e28f4
  val=strtoll(nptr,endptr,base);
Packit 8e28f4
  if (val>UINT32_MAX)
Packit 6bd9ab
  {
Packit 8e28f4
    errno=ERANGE;
Packit 8e28f4
    return UINT32_MAX;
Packit 6bd9ab
  }
Packit 8e28f4
  else if (val < 0)
Packit 8e28f4
  {
Packit 8e28f4
    errno=EINVAL;
Packit 8e28f4
    return UINT32_MAX;
Packit 8e28f4
  }
Packit 8e28f4
  /* If errno was set, we'll pass it back as-is */
Packit 8e28f4
  return (uint32_t)val;
Packit 6bd9ab
}