|
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 |
}
|