static bool copy_entry(const char *source, const char *destination, uid_t uid, gid_t gid) { struct stat sb; if (lstat(source, &sb) != 0) return false; struct timeval tv[2]; tv[0].tv_sec = sb.st_atime; tv[0].tv_usec = 0; tv[1].tv_sec = sb.st_mtime; tv[1].tv_usec = 0; if (S_ISDIR(sb.st_mode)) { if (!copy_dir(source, destination, uid, gid, &sb)) return false; } else if (S_ISLNK(sb.st_mode)) { if (!copy_symlink(source, destination, uid, gid, &sb)) return false; } else if (S_ISREG(sb.st_mode)) { if (!copy_file(source, destination, uid, gid, &sb)) return false; } else { if (!copy_special(source, destination, uid, gid, &sb)) return false; } if (lutimes(destination, tv) != 0) return false; return true; }
static int copy_paths(pool *p, const char *from, const char *to) { struct stat st; int res; xaset_t *set; set = get_dir_ctxt(p, (char *) to); res = pr_filter_allow_path(set, to); switch (res) { case 0: break; case PR_FILTER_ERR_FAILS_ALLOW_FILTER: pr_log_debug(DEBUG7, MOD_COPY_VERSION ": path '%s' denied by PathAllowFilter", to); errno = EPERM; return -1; case PR_FILTER_ERR_FAILS_DENY_FILTER: pr_log_debug(DEBUG7, MOD_COPY_VERSION ": path '%s' denied by PathDenyFilter", to); errno = EPERM; return -1; } /* Check whether from is a file, a directory, a symlink, or something * unsupported. */ res = pr_fsio_lstat(from, &st); if (res < 0) { int xerrno = errno; pr_log_debug(DEBUG7, MOD_COPY_VERSION ": error checking '%s': %s", from, strerror(xerrno)); errno = xerrno; return -1; } if (S_ISREG(st.st_mode)) { char *abs_path; pr_fs_clear_cache2(to); res = pr_fsio_stat(to, &st); if (res == 0) { unsigned char *allow_overwrite; allow_overwrite = get_param_ptr(CURRENT_CONF, "AllowOverwrite", FALSE); if (allow_overwrite == NULL || *allow_overwrite == FALSE) { pr_log_debug(DEBUG6, MOD_COPY_VERSION ": AllowOverwrite permission denied for '%s'", to); errno = EACCES; return -1; } } res = pr_fs_copy_file(from, to); if (res < 0) { int xerrno = errno; pr_log_debug(DEBUG7, MOD_COPY_VERSION ": error copying file '%s' to '%s': %s", from, to, strerror(xerrno)); errno = xerrno; return -1; } pr_fs_clear_cache2(to); if (pr_fsio_stat(to, &st) < 0) { pr_trace_msg(trace_channel, 3, "error stat'ing '%s': %s", to, strerror(errno)); } /* Write a TransferLog entry as well. */ abs_path = dir_abs_path(p, to, TRUE); if (session.sf_flags & SF_ANON) { xferlog_write(0, session.c->remote_name, st.st_size, abs_path, (session.sf_flags & SF_ASCII ? 'a' : 'b'), 'd', 'a', session.anon_user, 'c', "_"); } else { xferlog_write(0, session.c->remote_name, st.st_size, abs_path, (session.sf_flags & SF_ASCII ? 'a' : 'b'), 'd', 'r', session.user, 'c', "_"); } } else if (S_ISDIR(st.st_mode)) { res = create_path(p, to); if (res < 0) { int xerrno = errno; pr_log_debug(DEBUG7, MOD_COPY_VERSION ": error creating path '%s': %s", to, strerror(xerrno)); errno = xerrno; return -1; } res = copy_dir(p, from, to); if (res < 0) { int xerrno = errno; pr_log_debug(DEBUG7, MOD_COPY_VERSION ": error copying directory '%s' to '%s': %s", from, to, strerror(xerrno)); errno = xerrno; return -1; } } else if (S_ISLNK(st.st_mode)) { pr_fs_clear_cache2(to); res = pr_fsio_stat(to, &st); if (res == 0) { unsigned char *allow_overwrite; allow_overwrite = get_param_ptr(CURRENT_CONF, "AllowOverwrite", FALSE); if (allow_overwrite == NULL || *allow_overwrite == FALSE) { pr_log_debug(DEBUG6, MOD_COPY_VERSION ": AllowOverwrite permission denied for '%s'", to); errno = EACCES; return -1; } } res = copy_symlink(p, from, to); if (res < 0) { int xerrno = errno; pr_log_debug(DEBUG7, MOD_COPY_VERSION ": error copying symlink '%s' to '%s': %s", from, to, strerror(xerrno)); errno = xerrno; return -1; } } else { pr_log_debug(DEBUG7, MOD_COPY_VERSION ": unsupported file type for '%s'", from); errno = EINVAL; return -1; } return 0; }
static int copy_dir(pool *p, const char *src_dir, const char *dst_dir) { DIR *dh = NULL; struct dirent *dent = NULL; int res = 0; pool *iter_pool = NULL; dh = opendir(src_dir); if (dh == NULL) { pr_log_pri(PR_LOG_WARNING, MOD_COPY_VERSION ": error reading directory '%s': %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; } if (iter_pool != NULL) { destroy_pool(iter_pool); } iter_pool = pr_pool_create_sz(p, 128); src_path = pdircat(iter_pool, src_dir, dent->d_name, NULL); dst_path = pdircat(iter_pool, dst_dir, dent->d_name, NULL); if (pr_fsio_lstat(src_path, &st) < 0) { pr_log_debug(DEBUG3, MOD_COPY_VERSION ": unable to stat '%s' (%s), skipping", src_path, strerror(errno)); continue; } /* Is this path to a directory? */ if (S_ISDIR(st.st_mode)) { if (create_path(iter_pool, dst_path) < 0) { res = -1; break; } if (copy_dir(iter_pool, src_path, dst_path) < 0) { res = -1; break; } continue; /* Is this path to a regular file? */ } else if (S_ISREG(st.st_mode)) { cmd_rec *cmd; /* Dispatch fake COPY command, e.g. for mod_quotatab */ cmd = pr_cmd_alloc(iter_pool, 4, pstrdup(iter_pool, "SITE"), pstrdup(iter_pool, "COPY"), pstrdup(iter_pool, src_path), pstrdup(iter_pool, dst_path)); cmd->arg = pstrcat(iter_pool, "COPY ", src_path, " ", dst_path, NULL); cmd->cmd_class = CL_WRITE; pr_response_clear(&resp_list); pr_response_clear(&resp_err_list); if (pr_cmd_dispatch_phase(cmd, PRE_CMD, 0) < 0) { int xerrno = errno; pr_log_debug(DEBUG3, MOD_COPY_VERSION ": COPY of '%s' to '%s' blocked by COPY handler: %s", src_path, dst_path, strerror(xerrno)); pr_cmd_dispatch_phase(cmd, POST_CMD_ERR, 0); pr_cmd_dispatch_phase(cmd, LOG_CMD_ERR, 0); pr_response_clear(&resp_err_list); errno = xerrno; res = -1; break; } else { if (pr_fs_copy_file(src_path, dst_path) < 0) { pr_cmd_dispatch_phase(cmd, POST_CMD_ERR, 0); pr_cmd_dispatch_phase(cmd, LOG_CMD_ERR, 0); pr_response_clear(&resp_err_list); res = -1; break; } else { char *abs_path; pr_cmd_dispatch_phase(cmd, POST_CMD, 0); pr_cmd_dispatch_phase(cmd, LOG_CMD, 0); pr_response_clear(&resp_list); /* Write a TransferLog entry as well. */ pr_fs_clear_cache2(dst_path); pr_fsio_stat(dst_path, &st); abs_path = dir_abs_path(p, dst_path, TRUE); if (session.sf_flags & SF_ANON) { xferlog_write(0, session.c->remote_name, st.st_size, abs_path, (session.sf_flags & SF_ASCII ? 'a' : 'b'), 'd', 'a', session.anon_user, 'c', "_"); } else { xferlog_write(0, session.c->remote_name, st.st_size, abs_path, (session.sf_flags & SF_ASCII ? 'a' : 'b'), 'd', 'r', session.user, 'c', "_"); } } } continue; /* Is this path a symlink? */ } else if (S_ISLNK(st.st_mode)) { if (copy_symlink(iter_pool, src_path, dst_path) < 0) { res = -1; break; } continue; /* All other file types are skipped */ } else { pr_log_debug(DEBUG3, MOD_COPY_VERSION ": skipping supported file '%s'", src_path); continue; } } if (iter_pool != NULL) { destroy_pool(iter_pool); } closedir(dh); return res; }
/* * copy_entry - copy the entry of a directory * * Copy the entry src to dst. * Depending on the type of entry, this function will forward the * request to copy_dir(), copy_symlink(), copy_hardlink(), * copy_special(), or copy_file(). * * The access and modification time will not be modified. * * The permissions will be set to new_uid/new_gid. * * If new_uid (resp. new_gid) is equal to -1, the user (resp. group) will * not be modified. * * Only the files owned (resp. group-owned) by old_uid (resp. * old_gid) will be modified, unless old_uid (resp. old_gid) is set * to -1. */ static int copy_entry (const char *src, const char *dst, bool reset_selinux, uid_t old_uid, uid_t new_uid, gid_t old_gid, gid_t new_gid) { int err = 0; struct stat sb; struct link_name *lp; struct timeval mt[2]; if (LSTAT (src, &sb) == -1) { /* If we cannot stat the file, do not care. */ } else { #ifdef HAVE_STRUCT_STAT_ST_ATIM mt[0].tv_sec = sb.st_atim.tv_sec; mt[0].tv_usec = sb.st_atim.tv_nsec / 1000; #else /* !HAVE_STRUCT_STAT_ST_ATIM */ mt[0].tv_sec = sb.st_atime; # ifdef HAVE_STRUCT_STAT_ST_ATIMENSEC mt[0].tv_usec = sb.st_atimensec / 1000; # else /* !HAVE_STRUCT_STAT_ST_ATIMENSEC */ mt[0].tv_usec = 0; # endif /* !HAVE_STRUCT_STAT_ST_ATIMENSEC */ #endif /* !HAVE_STRUCT_STAT_ST_ATIM */ #ifdef HAVE_STRUCT_STAT_ST_MTIM mt[1].tv_sec = sb.st_mtim.tv_sec; mt[1].tv_usec = sb.st_mtim.tv_nsec / 1000; #else /* !HAVE_STRUCT_STAT_ST_MTIM */ mt[1].tv_sec = sb.st_mtime; # ifdef HAVE_STRUCT_STAT_ST_MTIMENSEC mt[1].tv_usec = sb.st_mtimensec / 1000; # else /* !HAVE_STRUCT_STAT_ST_MTIMENSEC */ mt[1].tv_usec = 0; # endif /* !HAVE_STRUCT_STAT_ST_MTIMENSEC */ #endif /* !HAVE_STRUCT_STAT_ST_MTIM */ if (S_ISDIR (sb.st_mode)) { err = copy_dir (src, dst, reset_selinux, &sb, mt, old_uid, new_uid, old_gid, new_gid); } #ifdef S_IFLNK /* * Copy any symbolic links */ else if (S_ISLNK (sb.st_mode)) { err = copy_symlink (src, dst, reset_selinux, &sb, mt, old_uid, new_uid, old_gid, new_gid); } #endif /* S_IFLNK */ /* * See if this is a previously copied link */ else if ((lp = check_link (src, &sb)) != NULL) { err = copy_hardlink (dst, reset_selinux, lp); } /* * Deal with FIFOs and special files. The user really * shouldn't have any of these, but it seems like it * would be nice to copy everything ... */ else if (!S_ISREG (sb.st_mode)) { err = copy_special (src, dst, reset_selinux, &sb, mt, old_uid, new_uid, old_gid, new_gid); } /* * Create the new file and copy the contents. The new * file will be owned by the provided UID and GID values. */ else { err = copy_file (src, dst, reset_selinux, &sb, mt, old_uid, new_uid, old_gid, new_gid); } } return err; }
/* 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; }
int transfer_begin(struct job *j) { int res; char *pread = NULL, *pwrite = NULL; size_t p_len; pthread_mutex_lock(&m_transfer); if (t_state.active) { DEBUG("called transfer_begin while a transfer is active!"); pthread_mutex_unlock(&m_transfer); return TRANSFER_FAIL; } pthread_mutex_unlock(&m_transfer); p_len = strlen(j->path); if (j->op == JOB_PUSH) { pread = cache_path2(j->path, p_len); pwrite = remote_path2(j->path, p_len); } else if (j->op == JOB_PULL) { pread = remote_path2(j->path, p_len); pwrite = cache_path2(j->path, p_len); } if (!pread || !pwrite) { free(pread); free(pwrite); errno = ENOMEM; return -1; } if (is_reg(pread)) { if (!is_reg(pwrite) && !is_nonexist(pwrite)) { DEBUG("write target is non-regular file: %s", pwrite); free(pread); free(pwrite); return TRANSFER_FAIL; } pthread_mutex_lock(&m_transfer); t_state.active = true; t_state.job = j; t_state.offset = 0; pthread_mutex_unlock(&m_transfer); res = transfer(pread, pwrite); free(pread); free(pwrite); return res; } /* if symlink, copy instantly */ else if (is_lnk(pread)) { DEBUG("push/pull on symlink"); copy_symlink(pread, pwrite); copy_attrs(pread, pwrite); free(pread); free(pwrite); return TRANSFER_FINISH; } else if (is_dir(pread)) { DEBUG("push/pull on DIR"); clone_dir(pread, pwrite); copy_attrs(pread, pwrite); free(pread); free(pwrite); return TRANSFER_FINISH; } else { ERROR("cannot read file %s", pread); free(pread); free(pwrite); return TRANSFER_FAIL; } DEBUG("wtf"); return TRANSFER_FAIL; }