/*
* ProFTPD - FTP server daemon
* Copyright (c) 2003 The ProFTPD Project team
*
* This program is free software; you can redistribute it and/or modify
* it under the terms of the GNU General Public License as published by
* the Free Software Foundation; either version 2 of the License, or
* (at your option) any later version.
*
* This program 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 General Public License for more details.
*
* You should have received a copy of the GNU General Public License
* along with this program; if not, write to the Free Software
* Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307, USA.
*
* As a special exemption, The ProFTPD Project team and other respective
* copyright holders give permission to link this program with OpenSSL, and
* distribute the resulting executable, without including the source code for
* OpenSSL in the source distribution.
*/
/* Home-on-demand support
* $Id: mkhome.c,v 1.8 2005/06/20 05:41:31 castaglia Exp $
*/
#include "conf.h"
#include "privs.h"
static int create_dir(const char *dir, uid_t uid, gid_t gid,
mode_t mode) {
mode_t prev_mask;
struct stat st;
int res = -1;
pr_fs_clear_cache();
res = pr_fsio_stat(dir, &st);
if (res == -1 && errno != ENOENT) {
pr_log_pri(PR_LOG_WARNING, "error checking '%s': %s", dir,
strerror(errno));
return -1;
}
/* The directory already exists. */
if (res == 0) {
pr_log_debug(DEBUG3, "CreateHome: '%s' already exists", dir);
return 0;
}
/* The given mode is absolute, not subject to any Umask setting. */
prev_mask = umask(0);
if (pr_fsio_mkdir(dir, mode) < 0) {
umask(prev_mask);
pr_log_pri(PR_LOG_WARNING, "error creating '%s': %s", dir,
strerror(errno));
return -1;
}
umask(prev_mask);
if (pr_fsio_chown(dir, uid, gid) < 0) {
pr_log_pri(PR_LOG_WARNING, "error setting ownership of '%s': %s", dir,
strerror(errno));
return -1;
}
pr_log_debug(DEBUG6, "CreateHome: directory '%s' created", dir);
return 0;
}
/* Walk along a path, making sure that all directories in that path exist,
* creating them if necessary.
*/
static int create_path(pool *p, const char *path, const char *user, uid_t uid,
gid_t gid, mode_t dir_mode, mode_t dst_mode) {
char *currpath = NULL, *tmppath = NULL;
struct stat st;
pr_fs_clear_cache();
if (pr_fsio_stat(path, &st) == 0) {
/* Path already exists, nothing to be done. */
errno = EEXIST;
return -1;
}
pr_event_generate("core.create-home", user);
pr_log_debug(DEBUG3, "creating home directory '%s' for user '%s'", path,
user);
tmppath = pstrdup(p, path);
currpath = "/";
while (tmppath && *tmppath) {
char *currdir = strsep(&tmppath, "/");
currpath = pdircat(p, currpath, currdir, NULL);
/* If tmppath is NULL, we are creating the last part of the path, so we
* use the configured mode, and chown it to the given UID and GID.
*/
if ((tmppath == NULL) || (*tmppath == '\0'))
create_dir(currpath, uid, gid, dst_mode);
else
create_dir(currpath, 0, 0, dir_mode);
pr_signals_handle();
}
pr_log_debug(DEBUG3, "home directory '%s' created", path);
return 0;
}
static int copy_symlink(pool *p, const char *src_dir, const char *src_path,
const char *dst_dir, const char *dst_path, uid_t uid, gid_t gid) {
char *link_path = pcalloc(p, PR_TUNABLE_BUFFER_SIZE);
int len;
if ((len = pr_fsio_readlink(src_path, link_path, sizeof(link_path)-1)) < 0) {
pr_log_pri(PR_LOG_WARNING, "CreateHome: error reading link '%s': %s",
src_path, strerror(errno));
return -1;
}
link_path[len] = '\0';
/* If the target of the link lies within the src path, rename that portion
* of the link to be the corresponding part of the dst path.
*/
if (strncmp(link_path, src_dir, strlen(src_dir)) == 0)
link_path = pdircat(p, dst_dir, link_path + strlen(src_dir), NULL);
if (pr_fsio_symlink(link_path, dst_path) < 0) {
pr_log_pri(PR_LOG_WARNING, "CreateHome: error symlinking '%s' to '%s': %s",
link_path, dst_path, strerror(errno));
return -1;
}
/* Make sure the new symlink has the proper ownership. */
if (pr_fsio_chown(dst_path, uid, gid) < 0)
pr_log_pri(PR_LOG_WARNING, "CreateHome: error chown'ing '%s' to %u/%u: %s",
dst_path, (unsigned int) uid, (unsigned int) gid, strerror(errno));
return 0;
}
/* srcdir is to be considered a "skeleton" directory, in the manner of
* /etc/skel, and destdir is a user's newly created home directory that needs
* to be populated with the files in srcdir.
*/
static int copy_dir(pool *p, const char *src_dir, const char *dst_dir,
uid_t uid, gid_t gid) {
DIR *dh = NULL;
struct dirent *dent = NULL;
dh = opendir(src_dir);
if (dh == NULL) {
pr_log_pri(PR_LOG_WARNING, "CreateHome: error copying '%s' skel files: %s",
src_dir, strerror(errno));
return -1;
}
while ((dent = readdir(dh)) != NULL) {
struct stat st;
char *src_path = pdircat(p, src_dir, dent->d_name, NULL);
char *dst_path = pdircat(p, dst_dir, dent->d_name, NULL);
pr_signals_handle();
/* Skip "." and ".." */
if (strcmp(dent->d_name, ".") == 0 || strcmp(dent->d_name, "..") == 0)
continue;
if (pr_fsio_lstat(src_path, &st) < 0) {
pr_log_debug(DEBUG3, "CreateHome: unable to stat '%s' (%s), skipping",
src_path, strerror(errno));
continue;
}
/* Is this path to a directory? */
if (S_ISDIR(st.st_mode)) {
create_dir(dst_path, uid, gid, st.st_mode);
copy_dir(p, src_path, dst_path, uid, gid);
continue;
/* Is this path to a regular file? */
} else if (S_ISREG(st.st_mode)) {
mode_t dst_mode = st.st_mode;
/* Make sure to prevent S{U,G}ID permissions on target files. */
if (dst_mode & S_ISUID)
dst_mode &= ~S_ISUID;
if (dst_mode & S_ISGID)
dst_mode &= ~S_ISGID;
(void) pr_fs_copy_file(src_path, dst_path);
/* Make sure the destination file has the proper ownership and mode. */
if (pr_fsio_chown(dst_path, uid, gid) < 0)
pr_log_pri(PR_LOG_WARNING, "CreateHome: error chown'ing '%s' "
"to %u/%u: %s", dst_path, (unsigned int) uid, (unsigned int) gid,
strerror(errno));
if (pr_fsio_chmod(dst_path, dst_mode) < 0)
pr_log_pri(PR_LOG_WARNING, "CreateHome: error chmod'ing '%s' to "
"%04o: %s", dst_path, (unsigned int) dst_mode, strerror(errno));
continue;
/* Is this path a symlink? */
} else if (S_ISLNK(st.st_mode)) {
copy_symlink(p, src_dir, src_path, dst_dir, dst_path, uid, gid);
continue;
/* All other file types are skipped */
} else {
pr_log_debug(DEBUG3, "CreateHome: skipping skel file '%s'", src_path);
continue;
}
}
closedir(dh);
return 0;
}
/* Check for a CreateHome directive, and act on it if present. If not, do
* nothing.
*/
int create_home(pool *p, const char *home, const char *user, uid_t uid,
gid_t gid) {
int res;
config_rec *c = find_config(main_server->conf, CONF_PARAM, "CreateHome",
FALSE);
if (!c || (c && *((unsigned char *) c->argv[0]) == FALSE))
return 0;
PRIVS_ROOT
/* Create the configured path. */
res = create_path(p, home, user, uid, gid, *((mode_t *) c->argv[2]),
*((mode_t *) c->argv[1]));
if (res < 0 && errno != EEXIST) {
PRIVS_RELINQUISH
return -1;
}
if (res == 0 && c->argv[3]) {
char *skel_dir = c->argv[3];
/* Populate the home directory with files from the configured
* skeleton (a la /etc/skel) directory.
*/
pr_log_debug(DEBUG4, "CreateHome: copying skel files from '%s' into '%s'",
skel_dir, home);
if (copy_dir(p, skel_dir, home, uid, gid) < 0)
pr_log_debug(DEBUG4, "CreateHome: error copying skel files");
}
PRIVS_RELINQUISH
return 0;
}
Last Updated: Thu Feb 23 11:07:19 2006
HTML generated by tj's src2html script