Blame nslcd/usermod.c

Packit 6bd9ab
/*
Packit 6bd9ab
   usermod.c - routines for changing user information such as full name,
Packit 6bd9ab
               login shell, etc
Packit 6bd9ab
Packit 6bd9ab
   Copyright (C) 2013-2017 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
#ifdef HAVE_STDINT_H
Packit 6bd9ab
#include <stdint.h>
Packit 6bd9ab
#endif /* HAVE_STDINT_H */
Packit 6bd9ab
#include <unistd.h>
Packit 6bd9ab
#include <sys/stat.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 "compat/shell.h"
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
    return NULL;
Packit 6bd9ab
  }
Packit 6bd9ab
  return entry;
Packit 6bd9ab
}
Packit 6bd9ab
Packit 6bd9ab
static int is_valid_homedir(const char *homedir)
Packit 6bd9ab
{
Packit 6bd9ab
  struct stat sb;
Packit 6bd9ab
  /* should be absolute path */
Packit 6bd9ab
  if (homedir[0] != '/')
Packit 6bd9ab
    return 0;
Packit 6bd9ab
  /* get directory status */
Packit 6bd9ab
  if (stat(homedir, &sb))
Packit 6bd9ab
  {
Packit 6bd9ab
    log_log(LOG_DEBUG, "cannot stat() %s: %s", homedir, strerror(errno));
Packit 6bd9ab
    return 0;
Packit 6bd9ab
  }
Packit 6bd9ab
  /* check if a directory */
Packit 6bd9ab
  if (!S_ISDIR(sb.st_mode))
Packit 6bd9ab
  {
Packit 6bd9ab
    log_log(LOG_DEBUG, "%s: not a directory", homedir);
Packit 6bd9ab
    return 0;
Packit 6bd9ab
  }
Packit 6bd9ab
  /* FIXME: check ownership */
Packit 6bd9ab
  return 1;
Packit 6bd9ab
}
Packit 6bd9ab
Packit 6bd9ab
static int is_valid_shell(const char *shell)
Packit 6bd9ab
{
Packit 6bd9ab
  int valid = 0;
Packit 6bd9ab
  char *l;
Packit 6bd9ab
  setusershell();
Packit 6bd9ab
  while ((l = getusershell()) != NULL)
Packit 6bd9ab
  {
Packit 6bd9ab
    if (strcmp(l, shell) == 0)
Packit 6bd9ab
    {
Packit 6bd9ab
      valid = 1;
Packit 6bd9ab
      break;
Packit 6bd9ab
    }
Packit 6bd9ab
  }
Packit 6bd9ab
  endusershell();
Packit 6bd9ab
  return valid;
Packit 6bd9ab
}
Packit 6bd9ab
Packit 6bd9ab
static MYLDAP_SESSION *get_session(const char *binddn, const char *password,
Packit 6bd9ab
                                   int *rcp)
Packit 6bd9ab
{
Packit 6bd9ab
  MYLDAP_SESSION *session;
Packit 6bd9ab
  /* set up a new connection */
Packit 6bd9ab
  session = myldap_create_session();
Packit 6bd9ab
  if (session == NULL)
Packit 6bd9ab
  {
Packit 6bd9ab
    *rcp = LDAP_UNAVAILABLE;
Packit 6bd9ab
    return NULL;
Packit 6bd9ab
  }
Packit 6bd9ab
  /* check that we can bind */
Packit 6bd9ab
  *rcp = myldap_bind(session, binddn, password, NULL, NULL);
Packit 6bd9ab
  if (*rcp != LDAP_SUCCESS)
Packit 6bd9ab
  {
Packit 6bd9ab
    myldap_session_close(session);
Packit 6bd9ab
    return NULL;
Packit 6bd9ab
  }
Packit 6bd9ab
  return session;
Packit 6bd9ab
}
Packit 6bd9ab
Packit 6bd9ab
#define ADD_MOD(attribute, value)                                           \
Packit 6bd9ab
  if ((value != NULL) && (attribute[0] != '"'))                             \
Packit 6bd9ab
  {                                                                         \
Packit 6bd9ab
    strvals[i * 2] = (char *)value;                                         \
Packit 6bd9ab
    strvals[i * 2 + 1] = NULL;                                              \
Packit 6bd9ab
    mods[i].mod_op = LDAP_MOD_REPLACE;                                      \
Packit 6bd9ab
    mods[i].mod_type = (char *)attribute;                                   \
Packit 6bd9ab
    mods[i].mod_values = strvals + (i * 2);                                 \
Packit 6bd9ab
    modsp[i] = mods + i;                                                    \
Packit 6bd9ab
    i++;                                                                    \
Packit 6bd9ab
  }
Packit 6bd9ab
Packit 6bd9ab
static int change(MYLDAP_SESSION *session, const char *userdn,
Packit 6bd9ab
                  const char *homedir, const char *shell)
Packit 6bd9ab
{
Packit 6bd9ab
  #define NUMARGS 2
Packit 6bd9ab
  char *strvals[(NUMARGS + 1) * 2];
Packit 6bd9ab
  LDAPMod mods[(NUMARGS + 1)], *modsp[(NUMARGS + 1)];
Packit 6bd9ab
  int i = 0;
Packit 6bd9ab
  /* build the list of modifications */
Packit 6bd9ab
  ADD_MOD(attmap_passwd_homeDirectory, homedir);
Packit 6bd9ab
  ADD_MOD(attmap_passwd_loginShell, shell);
Packit 6bd9ab
  /* terminate the list of modifications */
Packit 6bd9ab
  modsp[i] = NULL;
Packit 6bd9ab
  /* execute the update */
Packit 6bd9ab
  return myldap_modify(session, userdn, modsp);
Packit 6bd9ab
}
Packit 6bd9ab
Packit 6bd9ab
int nslcd_usermod(TFILE *fp, MYLDAP_SESSION *session, uid_t calleruid)
Packit 6bd9ab
{
Packit 6bd9ab
  int32_t tmpint32;
Packit 6bd9ab
  int rc = LDAP_SUCCESS;
Packit 6bd9ab
  char username[BUFLEN_NAME];
Packit 6bd9ab
  int asroot, isroot;
Packit 6bd9ab
  char password[BUFLEN_PASSWORD];
Packit 6bd9ab
  int32_t param;
Packit 6bd9ab
  char buffer[4096];
Packit 6bd9ab
  size_t buflen = sizeof(buffer);
Packit 6bd9ab
  size_t bufptr = 0;
Packit 6bd9ab
  const char *value = NULL;
Packit 6bd9ab
  const char *fullname = NULL, *roomnumber = NULL, *workphone = NULL;
Packit 6bd9ab
  const char *homephone = NULL, *other = NULL, *homedir = NULL;
Packit 6bd9ab
  const char *shell = NULL;
Packit 6bd9ab
  const char *binddn = NULL; /* the user performing the modification */
Packit 6bd9ab
  MYLDAP_ENTRY *entry;
Packit 6bd9ab
  MYLDAP_SESSION *newsession;
Packit 6bd9ab
  char errmsg[BUFLEN_MESSAGE];
Packit 6bd9ab
  /* read request parameters */
Packit 6bd9ab
  READ_STRING(fp, username);
Packit 6bd9ab
  READ_INT32(fp, asroot);
Packit 6bd9ab
  READ_STRING(fp, password);
Packit 6bd9ab
  /* read the usermod parameters */
Packit 6bd9ab
  while (1)
Packit 6bd9ab
  {
Packit 6bd9ab
    READ_INT32(fp, param);
Packit 6bd9ab
    if (param == NSLCD_USERMOD_END)
Packit 6bd9ab
      break;
Packit 6bd9ab
    READ_BUF_STRING(fp, value);
Packit 6bd9ab
    switch (param)
Packit 6bd9ab
    {
Packit 6bd9ab
      case NSLCD_USERMOD_FULLNAME:   fullname = value; break;
Packit 6bd9ab
      case NSLCD_USERMOD_ROOMNUMBER: roomnumber = value; break;
Packit 6bd9ab
      case NSLCD_USERMOD_WORKPHONE:  workphone = value; break;
Packit 6bd9ab
      case NSLCD_USERMOD_HOMEPHONE:  homephone = value; break;
Packit 6bd9ab
      case NSLCD_USERMOD_OTHER:      other = value; break;
Packit 6bd9ab
      case NSLCD_USERMOD_HOMEDIR:    homedir = value; break;
Packit 6bd9ab
      case NSLCD_USERMOD_SHELL:      shell = value; break;
Packit 6bd9ab
      default: /* other parameters are silently ignored */ break;
Packit 6bd9ab
    }
Packit 6bd9ab
  }
Packit 6bd9ab
  /* log call */
Packit 6bd9ab
  log_setrequest("usermod=\"%s\"", username);
Packit 6bd9ab
  log_log(LOG_DEBUG, "nslcd_usermod(\"%s\",%s,\"%s\")",
Packit 6bd9ab
          username, asroot ? "asroot" : "asuser", *password ? "***" : "");
Packit 6bd9ab
  if (fullname != NULL)
Packit 6bd9ab
    log_log(LOG_DEBUG, "nslcd_usermod(fullname=\"%s\")", fullname);
Packit 6bd9ab
  if (roomnumber != NULL)
Packit 6bd9ab
    log_log(LOG_DEBUG, "nslcd_usermod(roomnumber=\"%s\")", roomnumber);
Packit 6bd9ab
  if (workphone != NULL)
Packit 6bd9ab
    log_log(LOG_DEBUG, "nslcd_usermod(workphone=\"%s\")", workphone);
Packit 6bd9ab
  if (homephone != NULL)
Packit 6bd9ab
    log_log(LOG_DEBUG, "nslcd_usermod(homephone=\"%s\")", homephone);
Packit 6bd9ab
  if (other != NULL)
Packit 6bd9ab
    log_log(LOG_DEBUG, "nslcd_usermod(other=\"%s\")", other);
Packit 6bd9ab
  if (homedir != NULL)
Packit 6bd9ab
    log_log(LOG_DEBUG, "nslcd_usermod(homedir=\"%s\")", homedir);
Packit 6bd9ab
  if (shell != NULL)
Packit 6bd9ab
    log_log(LOG_DEBUG, "nslcd_usermod(shell=\"%s\")", shell);
Packit 6bd9ab
  /* write the response header */
Packit 6bd9ab
  WRITE_INT32(fp, NSLCD_VERSION);
Packit 6bd9ab
  WRITE_INT32(fp, NSLCD_ACTION_USERMOD);
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, otherwise break the protocol */
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 if it is a modification as root */
Packit 6bd9ab
  isroot = (calleruid == 0) && asroot;
Packit 6bd9ab
  if (asroot)
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
      return -1;
Packit 6bd9ab
    }
Packit 6bd9ab
    binddn = nslcd_cfg->rootpwmoddn;
Packit 6bd9ab
    /* check if rootpwmodpw should be used */
Packit 6bd9ab
    if ((*password == '\0') && isroot && (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_pwmod(): rootpwmodpw will not fit in password");
Packit 6bd9ab
        return -1;
Packit 6bd9ab
      }
Packit 6bd9ab
      strcpy(password, nslcd_cfg->rootpwmodpw);
Packit 6bd9ab
    }
Packit 6bd9ab
  }
Packit 6bd9ab
  else
Packit 6bd9ab
    binddn = myldap_get_dn(entry);
Packit 6bd9ab
  WRITE_INT32(fp, NSLCD_RESULT_BEGIN);
Packit 6bd9ab
  /* home directory change requires either root or valid directory */
Packit 6bd9ab
  if ((homedir != NULL) && (!isroot) && !is_valid_homedir(homedir))
Packit 6bd9ab
  {
Packit 6bd9ab
    log_log(LOG_NOTICE, "invalid directory: %s", homedir);
Packit 6bd9ab
    WRITE_INT32(fp, NSLCD_USERMOD_HOMEDIR);
Packit 6bd9ab
    WRITE_STRING(fp, "invalid directory");
Packit 6bd9ab
    homedir = NULL;
Packit 6bd9ab
  }
Packit 6bd9ab
  /* shell change requires either root or a valid shell */
Packit 6bd9ab
  if ((shell != NULL) && (!isroot) && !is_valid_shell(shell))
Packit 6bd9ab
  {
Packit 6bd9ab
    log_log(LOG_NOTICE, "invalid shell: %s", shell);
Packit 6bd9ab
    WRITE_INT32(fp, NSLCD_USERMOD_SHELL);
Packit 6bd9ab
    WRITE_STRING(fp, "invalid shell");
Packit 6bd9ab
    shell = NULL;
Packit 6bd9ab
  }
Packit 6bd9ab
  /* perform requested changes */
Packit 6bd9ab
  newsession = get_session(binddn, password, &rc);
Packit 6bd9ab
  if (newsession != NULL)
Packit 6bd9ab
  {
Packit 6bd9ab
    rc = change(newsession, myldap_get_dn(entry), homedir, shell);
Packit 6bd9ab
    myldap_session_close(newsession);
Packit 6bd9ab
  }
Packit 6bd9ab
  /* return response to caller */
Packit 6bd9ab
  if (rc != LDAP_SUCCESS)
Packit 6bd9ab
  {
Packit 6bd9ab
    log_log(LOG_WARNING, "%s: modification failed: %s",
Packit 6bd9ab
            myldap_get_dn(entry), ldap_err2string(rc));
Packit 6bd9ab
    mysnprintf(errmsg, sizeof(errmsg) - 1, "change failed: %s", ldap_err2string(rc));
Packit 6bd9ab
    WRITE_INT32(fp, NSLCD_USERMOD_RESULT);
Packit 6bd9ab
    WRITE_STRING(fp, errmsg);
Packit 6bd9ab
    WRITE_INT32(fp, NSLCD_USERMOD_END);
Packit 6bd9ab
    WRITE_INT32(fp, NSLCD_RESULT_END);
Packit 6bd9ab
    return 0;
Packit 6bd9ab
  }
Packit 6bd9ab
  log_log(LOG_NOTICE, "changed information for %s", myldap_get_dn(entry));
Packit 6bd9ab
  WRITE_INT32(fp, NSLCD_USERMOD_END);
Packit 6bd9ab
  WRITE_INT32(fp, NSLCD_RESULT_END);
Packit 6bd9ab
  return 0;
Packit 6bd9ab
}