Skip to content

Commit

Permalink
Issue #23: Update mod_vroot to tweak various commands when dealing wi…
Browse files Browse the repository at this point in the history
…th mod_sftp's SFTP/SCP idiosyncrasies.
  • Loading branch information
Castaglia committed Jul 4, 2021
1 parent e842a90 commit 2788b55
Show file tree
Hide file tree
Showing 5 changed files with 672 additions and 52 deletions.
1 change: 1 addition & 0 deletions .gitignore
Original file line number Diff line number Diff line change
Expand Up @@ -9,5 +9,6 @@ mod_vroot.h
*.lo
*.log
*Tests*.log
*.a
*.o
*~
245 changes: 215 additions & 30 deletions mod_vroot.c
Original file line number Diff line number Diff line change
@@ -1,7 +1,7 @@
/*
* ProFTPD: mod_vroot -- a module implementing a virtual chroot capability
* via the FSIO API
* Copyright (c) 2002-2019 TJ Saunders
* Copyright (c) 2002-2021 TJ Saunders
*
* 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
Expand Down Expand Up @@ -41,6 +41,7 @@ unsigned int vroot_opts = 0;
module vroot_module;

static int vroot_engine = FALSE;
static const char *trace_channel = "vroot";

#if PROFTPD_VERSION_NUMBER >= 0x0001030407
static int vroot_use_mkdtemp = FALSE;
Expand Down Expand Up @@ -242,19 +243,20 @@ MODRET set_vrootserverroot(cmd_rec *cmd) {
/* Command handlers
*/

MODRET vroot_log_retr(cmd_rec *cmd) {
const char *key, *path;

if (vroot_engine == FALSE ||
session.chroot_path == NULL) {
return PR_DECLINED(cmd);
}

key = "mod_xfer.retr-path";
static const char *vroot_cmd_fixup_path(cmd_rec *cmd, const char *key,
int use_best_path) {
const char *path;
char *real_path = NULL;

path = pr_table_get(cmd->notes, key, NULL);
if (path != NULL) {
char *real_path;
if (use_best_path == TRUE) {
/* Only needed for mod_sftp sessions, to do what mod_xfer does for FTP
* commands, but in a way that does not require mod_sftp changes.
* Probably too clever.
*/
path = dir_best_path(cmd->pool, path);
}

if (*path == '/') {
const char *base_path;
Expand All @@ -267,43 +269,209 @@ MODRET vroot_log_retr(cmd_rec *cmd) {
real_path = vroot_realpath(cmd->pool, path, VROOT_REALPATH_FL_ABS_PATH);
}

pr_trace_msg(trace_channel, 17,
"fixed up '%s' path in command %s; was '%s', now '%s'", key,
(char *) cmd->argv[0], path, real_path);
pr_table_set(cmd->notes, key, real_path, 0);
}

return real_path;
}

MODRET vroot_pre_scp_retr(cmd_rec *cmd) {
const char *key, *proto, *real_path;

if (vroot_engine == FALSE ||
session.chroot_path == NULL) {
return PR_DECLINED(cmd);
}

/* As a PRE_CMD handler, we only run for SCP sessions. */
proto = pr_session_get_protocol(0);
if (strcmp(proto, "scp") != 0) {
return PR_DECLINED(cmd);
}

/* Unlike SFTP sessions, mod_sftp does NOT set these cmd->notes for SCP
* sessions before doing the PRE_CMD dispatching. So we do it ourselves,
* pre-emptively, before using our other machinery.
*/
key = "mod_xfer.retr-path";
(void) pr_table_add(cmd->notes, key, pstrdup(cmd->pool, cmd->arg), 0);

real_path = vroot_cmd_fixup_path(cmd, key, TRUE);
if (real_path != NULL) {
/* In addition, for SCP sessions, we modify cmd->arg as well, for
* mod_sftp's benefit.
*/
cmd->arg = (char *) real_path;
}

return PR_DECLINED(cmd);
}

MODRET vroot_log_stor(cmd_rec *cmd) {
const char *key, *path;
MODRET vroot_pre_sftp_retr(cmd_rec *cmd) {
const char *key, *proto, *real_path;

if (vroot_engine == FALSE ||
session.chroot_path == NULL) {
return PR_DECLINED(cmd);
}

key = "mod_xfer.store-path";
/* As a PRE_CMD handler, we only run for SFTP sessions. */
proto = pr_session_get_protocol(0);
if (strcmp(proto, "sftp") != 0) {
return PR_DECLINED(cmd);
}

key = "mod_xfer.retr-path";
real_path = vroot_cmd_fixup_path(cmd, key, TRUE);
if (real_path != NULL) {
/* In addition, for SFTP sessions, we modify cmd->arg as well, for
* mod_sftp's benefit.
*/
cmd->arg = (char *) real_path;
}

return PR_DECLINED(cmd);
}

MODRET vroot_post_sftp_retr(cmd_rec *cmd) {
const char *key, *path, *proto;

if (vroot_engine == FALSE ||
session.chroot_path == NULL) {
return PR_DECLINED(cmd);
}

/* As a POST_CMD handler, we only run for SFTP sessions. */
proto = pr_session_get_protocol(0);
if (strcmp(proto, "sftp") != 0) {
return PR_DECLINED(cmd);
}

key = "mod_xfer.retr-path";
path = pr_table_get(cmd->notes, key, NULL);
if (path != NULL) {
char *real_path;
/* In addition, for SFTP sessions, we modify session.xfer.path as well,
* for mod_xfer's benefit in TransferLog entries.
*/
session.xfer.path = pstrdup(session.xfer.p, path);
}

if (*path == '/') {
const char *base_path;
return PR_DECLINED(cmd);
}

base_path = vroot_path_get_base(cmd->tmp_pool, NULL);
real_path = pdircat(cmd->pool, base_path, path, NULL);
vroot_path_clean(real_path);
MODRET vroot_log_retr(cmd_rec *cmd) {
const char *key;

} else {
real_path = vroot_realpath(cmd->pool, path, VROOT_REALPATH_FL_ABS_PATH);
}
if (vroot_engine == FALSE ||
session.chroot_path == NULL) {
return PR_DECLINED(cmd);
}

pr_table_set(cmd->notes, key, real_path, 0);
key = "mod_xfer.retr-path";
(void) vroot_cmd_fixup_path(cmd, key, FALSE);
return PR_DECLINED(cmd);
}

MODRET vroot_pre_scp_stor(cmd_rec *cmd) {
const char *key, *proto, *real_path;

if (vroot_engine == FALSE ||
session.chroot_path == NULL) {
return PR_DECLINED(cmd);
}

/* As a PRE_CMD handler, we only run for SCP sessions. */
proto = pr_session_get_protocol(0);
if (strcmp(proto, "scp") != 0) {
return PR_DECLINED(cmd);
}

/* Unlike SFTP sessions, mod_sftp does NOT set these cmd->notes for SCP
* sessions before doing the PRE_CMD dispatching. So we do it ourselves,
* pre-emptively, before using our other machinery.
*/
key = "mod_xfer.store-path";
(void) pr_table_add(cmd->notes, key, pstrdup(cmd->pool, cmd->arg), 0);

real_path = vroot_cmd_fixup_path(cmd, key, TRUE);
if (real_path != NULL) {
/* In addition, for SCP sessions, we modify cmd->arg as well, for
* mod_sftp's benefit.
*/
cmd->arg = (char *) real_path;
}

return PR_DECLINED(cmd);
}

MODRET vroot_pre_sftp_stor(cmd_rec *cmd) {
const char *key, *proto, *real_path;

if (vroot_engine == FALSE ||
session.chroot_path == NULL) {
return PR_DECLINED(cmd);
}

/* As a PRE_CMD handler, we only run for SFTP sessions. */
proto = pr_session_get_protocol(0);
if (strcmp(proto, "sftp") != 0) {
return PR_DECLINED(cmd);
}

key = "mod_xfer.store-path";
real_path = vroot_cmd_fixup_path(cmd, key, TRUE);
if (real_path != NULL) {
/* In addition, for SFTP sessions, we modify cmd->arg as well, for
* mod_sftp's benefit.
*/
cmd->arg = (char *) real_path;
}

return PR_DECLINED(cmd);
}

MODRET vroot_post_sftp_stor(cmd_rec *cmd) {
const char *key, *path, *proto;

if (vroot_engine == FALSE ||
session.chroot_path == NULL) {
return PR_DECLINED(cmd);
}

/* As a POST_CMD handler, we only run for SFTP sessions. */
proto = pr_session_get_protocol(0);
if (strcmp(proto, "sftp") != 0) {
return PR_DECLINED(cmd);
}

key = "mod_xfer.store-path";
path = pr_table_get(cmd->notes, key, NULL);
if (path != NULL) {
/* In addition, for SFTP sessions, we modify session.xfer.path as well,
* for mod_xfer's benefit in TransferLog entries.
*/
session.xfer.path = pstrdup(session.xfer.p, path);
}

return PR_DECLINED(cmd);
}

MODRET vroot_log_stor(cmd_rec *cmd) {
const char *key;

if (vroot_engine == FALSE ||
session.chroot_path == NULL) {
return PR_DECLINED(cmd);
}

key = "mod_xfer.store-path";
(void) vroot_cmd_fixup_path(cmd, key, FALSE);
return PR_DECLINED(cmd);
}

MODRET vroot_pre_mkd(cmd_rec *cmd) {
if (vroot_engine == FALSE ||
session.chroot_path == NULL) {
Expand Down Expand Up @@ -527,12 +695,13 @@ static cmdtable vroot_cmdtab[] = {
/* These command handlers are for manipulating cmd->notes, to get
* paths properly logged.
*
* Ideally these would be LOG_CMD/LOG_CMD_ERR phase handlers. HOWEVER,
* we need to transform things before the cmd is dispatched to mod_log,
* and mod_log uses a C_ANY handler for logging. And when dispatching,
* C_ANY handlers are run before named handlers. This means that using
* LOG_CMD/LOG_CMD_ERR handlers would be run AFTER mod_log's handler,
* even though we appear BEFORE mod_log in the module load order.
* Ideally these POST_CMD handlers would be LOG_CMD/LOG_CMD_ERR phase
* handlers. HOWEVER, we need to transform things before the cmd is
* dispatched to mod_log, and mod_log uses a C_ANY handler for logging.
* And when dispatching, C_ANY handlers are run before named handlers.
* This means that using * LOG_CMD/LOG_CMD_ERR handlers would be run AFTER
* mod_log's handler, even though we appear BEFORE mod_log in the module
* load order.
*
* Thus to do the transformation, we actually use CMD/POST_CMD_ERR phase
* handlers here. The reason to use CMD, rather than POST_CMD, is the
Expand All @@ -549,6 +718,22 @@ static cmdtable vroot_cmdtab[] = {
{ CMD, C_STOR, G_NONE, vroot_log_stor, FALSE, FALSE, CL_WRITE },
{ POST_CMD_ERR, C_STOR, G_NONE, vroot_log_stor, FALSE, FALSE },

/* To make this more complicated, we DO actually want these handlers to
* run as PRE_CMD handlers, but only for mod_sftp sessions. Why? The
* mod_sftp module does not use the normal CMD handlers; it handles
* dispatching on its own. And we do still want mod_vroot to fix up
* the paths properly for SFTP/SCP sessions, too.
*/
{ PRE_CMD, C_APPE, G_NONE, vroot_pre_sftp_stor, FALSE, FALSE, CL_WRITE },
{ POST_CMD, C_APPE, G_NONE, vroot_post_sftp_stor, FALSE, FALSE },
{ PRE_CMD, C_RETR, G_NONE, vroot_pre_sftp_retr, FALSE, FALSE, CL_READ },
{ POST_CMD, C_RETR, G_NONE, vroot_post_sftp_retr, FALSE, FALSE },
{ PRE_CMD, C_STOR, G_NONE, vroot_pre_sftp_stor, FALSE, FALSE, CL_WRITE },
{ POST_CMD, C_STOR, G_NONE, vroot_post_sftp_stor, FALSE, FALSE },

{ PRE_CMD, C_RETR, G_NONE, vroot_pre_scp_retr, FALSE, FALSE, CL_READ },
{ PRE_CMD, C_STOR, G_NONE, vroot_pre_scp_stor, FALSE, FALSE, CL_WRITE },

{ 0, NULL }
};

Expand Down
2 changes: 1 addition & 1 deletion mod_vroot.h.in
Original file line number Diff line number Diff line change
Expand Up @@ -27,7 +27,7 @@

#include "conf.h"

#define MOD_VROOT_VERSION "mod_vroot/0.9.8"
#define MOD_VROOT_VERSION "mod_vroot/0.9.9"

/* Make sure the version of proftpd is as necessary. */
#if PROFTPD_VERSION_NUMBER < 0x0001030602
Expand Down
3 changes: 1 addition & 2 deletions t/lib/ProFTPD/Tests/Modules/mod_vroot.pm
Original file line number Diff line number Diff line change
Expand Up @@ -10313,15 +10313,14 @@ sub vroot_log_extlog_stor {
auth_group_write($auth_group_file, $group, $gid, $user);

my $test_file = File::Spec->rel2abs("$tmpdir/test.txt");

my $ext_log = File::Spec->rel2abs("$tmpdir/custom.log");

my $config = {
PidFile => $pid_file,
ScoreboardFile => $scoreboard_file,
SystemLog => $log_file,
TraceLog => $log_file,
Trace => 'fsio:10',
Trace => 'fsio:10 jot:20 vroot:20',

AuthUserFile => $auth_user_file,
AuthGroupFile => $auth_group_file,
Expand Down
Loading

0 comments on commit 2788b55

Please sign in to comment.