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; len = pr_fsio_readlink(src_path, link_path, PR_TUNABLE_BUFFER_SIZE-1); if (len < 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; }
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_cache2(dir); res = pr_fsio_stat(dir, &st); if (res == -1 && errno != ENOENT) { int xerrno = errno; pr_log_pri(PR_LOG_WARNING, "error checking '%s': %s", dir, strerror(xerrno)); errno = xerrno; return -1; } /* The directory already exists. */ if (res == 0) { pr_trace_msg(trace_channel, 8, "'%s' already exists", dir); 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) { int xerrno = errno; umask(prev_mask); pr_log_pri(PR_LOG_WARNING, "error creating '%s': %s", dir, strerror(xerrno)); errno = xerrno; return -1; } umask(prev_mask); if (pr_fsio_chown(dir, uid, gid) < 0) { int xerrno = errno; pr_log_pri(PR_LOG_WARNING, "error setting ownership of '%s': %s", dir, strerror(xerrno)); errno = xerrno; return -1; } pr_trace_msg(trace_channel, 8, "directory '%s' created", dir); pr_log_debug(DEBUG6, "CreateHome: directory '%s' created", dir); 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, *dst_path; pr_signals_handle(); /* Skip "." and ".." */ if (strncmp(dent->d_name, ".", 2) == 0 || strncmp(dent->d_name, "..", 3) == 0) { continue; } src_path = pdircat(p, src_dir, dent->d_name, NULL); dst_path = pdircat(p, dst_dir, dent->d_name, NULL); 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; }