Skip to content
Snippets Groups Projects
Commit 2305aa26 authored by Deucе's avatar Deucе :ok_hand_tone4:
Browse files

Add support for /vfiles in sftp

The /vfiles root uses the configured virtual paths as in the FTP
and web servers.  These paths should be shorter than the ones used
in /files, and also have the benefit of translating slashes so
users will not need to type a Big Solidus if they're trying to
type long lib or dir names.
parent 8047d7bd
No related branches found
No related tags found
No related merge requests found
Pipeline #6019 failed
...@@ -400,8 +400,15 @@ struct mouse_hotspot { // Mouse hot-spot ...@@ -400,8 +400,15 @@ struct mouse_hotspot { // Mouse hot-spot
bool hungry; bool hungry;
}; };
enum sftp_dir_tree {
SFTP_DTREE_FULL,
SFTP_DTREE_SHORT,
SFTP_DTREE_VIRTUAL
};
typedef struct sftp_dirdes { typedef struct sftp_dirdes {
bool is_static; bool is_static;
enum sftp_dir_tree tree;
union { union {
struct { struct {
int lib; int lib;
......
...@@ -9,6 +9,8 @@ constexpr uint32_t lib_mask {~lib_flag}; ...@@ -9,6 +9,8 @@ constexpr uint32_t lib_mask {~lib_flag};
constexpr uint32_t users_gid {UINT32_MAX}; constexpr uint32_t users_gid {UINT32_MAX};
#define SLASH_FILES "/files" #define SLASH_FILES "/files"
#define SLASH_VFILES "/vfiles"
#define SLASH_FLS "/fls"
#define SLASH_HOME "/home" #define SLASH_HOME "/home"
#define MAX_FILES_PER_READDIR 25 #define MAX_FILES_PER_READDIR 25
...@@ -56,11 +58,16 @@ static sftp_file_attr_t sshkeys_attrs(sbbs_t *sbbs, const char *path); ...@@ -56,11 +58,16 @@ static sftp_file_attr_t sshkeys_attrs(sbbs_t *sbbs, const char *path);
static char *sftp_parse_crealpath(sbbs_t *sbbs, const char *filename); static char *sftp_parse_crealpath(sbbs_t *sbbs, const char *filename);
static char *expand_slash(const char *orig); static char *expand_slash(const char *orig);
static bool is_in_filebase(const char *path); static bool is_in_filebase(const char *path);
static enum sftp_dir_tree in_tree(const char *path);
static char * get_longname(sbbs_t *sbbs, const char *path, const char *link, sftp_file_attr_t attr); static char * get_longname(sbbs_t *sbbs, const char *path, const char *link, sftp_file_attr_t attr);
static sftp_file_attr_t get_attrs(sbbs_t *sbbs, const char *path, char **link); static sftp_file_attr_t get_attrs(sbbs_t *sbbs, const char *path, char **link);
const char files_path[] = SLASH_FILES; const char files_path[] = SLASH_FILES;
constexpr size_t files_path_len = (sizeof(files_path) - 1); constexpr size_t files_path_len = (sizeof(files_path) - 1);
const char vfiles_path[] = SLASH_VFILES;
constexpr size_t vfiles_path_len = (sizeof(vfiles_path) - 1);
const char fls_path[] = SLASH_FLS;
constexpr size_t fls_path_len = (sizeof(fls_path) - 1);
static struct pathmap static_files[] = { static struct pathmap static_files[] = {
// TODO: ftpalias.cfg // TODO: ftpalias.cfg
...@@ -68,6 +75,7 @@ static struct pathmap static_files[] = { ...@@ -68,6 +75,7 @@ static struct pathmap static_files[] = {
// TODO: Upload to sysop // TODO: Upload to sysop
{"/", nullptr, nullptr, rootdir_attrs}, {"/", nullptr, nullptr, rootdir_attrs},
{SLASH_FILES "/", nullptr, nullptr, rootdir_attrs}, {SLASH_FILES "/", nullptr, nullptr, rootdir_attrs},
//{SLASH_FLS "/", nullptr, nullptr, rootdir_attrs},
{SLASH_HOME "/", nullptr, nullptr, homedir_attrs}, {SLASH_HOME "/", nullptr, nullptr, homedir_attrs},
{SLASH_HOME "/%s/", nullptr, nullptr, homedir_attrs}, {SLASH_HOME "/%s/", nullptr, nullptr, homedir_attrs},
// TODO: Some way for a sysop/mod authour to map things in here // TODO: Some way for a sysop/mod authour to map things in here
...@@ -77,11 +85,13 @@ static struct pathmap static_files[] = { ...@@ -77,11 +85,13 @@ static struct pathmap static_files[] = {
{SLASH_HOME "/%s/plan", "%suser/%04d.plan", nullptr, homefile_attrs}, {SLASH_HOME "/%s/plan", "%suser/%04d.plan", nullptr, homefile_attrs},
{SLASH_HOME "/%s/signature", "%suser/%04d.sig", nullptr, homefile_attrs}, {SLASH_HOME "/%s/signature", "%suser/%04d.sig", nullptr, homefile_attrs},
{SLASH_HOME "/%s/smtptags", "%suser/%04d.smtptags", nullptr, homefile_attrs}, {SLASH_HOME "/%s/smtptags", "%suser/%04d.smtptags", nullptr, homefile_attrs},
{SLASH_VFILES "/", nullptr, nullptr, rootdir_attrs},
}; };
constexpr size_t static_files_sz = (sizeof(static_files) / sizeof(static_files[0])); constexpr size_t static_files_sz = (sizeof(static_files) / sizeof(static_files[0]));
class path_map { class path_map {
bool is_static_ {true}; bool is_static_ {true};
enum sftp_dir_tree tree_ {SFTP_DTREE_FULL};
map_path_result_t result_ {MAP_FAILED}; map_path_result_t result_ {MAP_FAILED};
// Too lazy to write a std::expected thing here. // Too lazy to write a std::expected thing here.
...@@ -90,7 +100,18 @@ class path_map { ...@@ -90,7 +100,18 @@ class path_map {
for (int l = 0; l < sbbs->cfg.total_libs; l++) { for (int l = 0; l < sbbs->cfg.total_libs; l++) {
if (!can_user_access_lib(&sbbs->cfg, l, &sbbs->useron, &sbbs->client)) if (!can_user_access_lib(&sbbs->cfg, l, &sbbs->useron, &sbbs->client))
continue; continue;
char *exp = expand_slash(sbbs->cfg.lib[l]->lname); char *exp {};
switch (tree_) {
case SFTP_DTREE_FULL:
exp = expand_slash(sbbs->cfg.lib[l]->lname);
break;
case SFTP_DTREE_SHORT:
exp = expand_slash(sbbs->cfg.lib[l]->sname);
break;
case SFTP_DTREE_VIRTUAL:
exp = expand_slash(sbbs->cfg.lib[l]->vdir);
break;
}
if (exp == nullptr) if (exp == nullptr)
return -1; return -1;
if ((memcmp(libnam, exp, lnsz) == 0) if ((memcmp(libnam, exp, lnsz) == 0)
...@@ -110,7 +131,18 @@ class path_map { ...@@ -110,7 +131,18 @@ class path_map {
continue; continue;
if (!can_user_access_dir(&sbbs->cfg, d, &sbbs->useron, &sbbs->client)) if (!can_user_access_dir(&sbbs->cfg, d, &sbbs->useron, &sbbs->client))
continue; continue;
char *exp = expand_slash(sbbs->cfg.dir[d]->lname); char *exp {};
switch (tree_) {
case SFTP_DTREE_FULL:
exp = expand_slash(sbbs->cfg.dir[d]->lname);
break;
case SFTP_DTREE_SHORT:
exp = expand_slash(sbbs->cfg.dir[d]->code);
break;
case SFTP_DTREE_VIRTUAL:
exp = expand_slash(sbbs->cfg.dir[d]->vdir);
break;
}
if (exp == nullptr) if (exp == nullptr)
return -1; return -1;
if ((memcmp(dirnam, exp, dnsz) == 0) if ((memcmp(dirnam, exp, dnsz) == 0)
...@@ -123,7 +155,6 @@ class path_map { ...@@ -123,7 +155,6 @@ class path_map {
return -1; return -1;
} }
public: public:
const map_path_mode_t mode; const map_path_mode_t mode;
sbbs_t * const sbbs{}; sbbs_t * const sbbs{};
...@@ -163,6 +194,7 @@ public: ...@@ -163,6 +194,7 @@ public:
return; return;
} }
if (is_in_filebase(this->sftp_path)) { if (is_in_filebase(this->sftp_path)) {
tree_ = in_tree(this->sftp_path);
// This is in the file base. // This is in the file base.
if (mode == MAP_RDWR) { if (mode == MAP_RDWR) {
result_ = MAP_PERMISSION_DENIED; result_ = MAP_PERMISSION_DENIED;
...@@ -172,12 +204,25 @@ public: ...@@ -172,12 +204,25 @@ public:
this->info.filebase.dir = -1; this->info.filebase.dir = -1;
this->info.filebase.lib = -1; this->info.filebase.lib = -1;
this->info.filebase.idx = dot; this->info.filebase.idx = dot;
if (this->sftp_path[files_path_len] == 0 || this->sftp_path[files_path_len+1] == 0) { char *lib;
size_t sz;
switch (tree_) {
case SFTP_DTREE_FULL:
sz = files_path_len;
break;
case SFTP_DTREE_SHORT:
sz = fls_path_len;
break;
case SFTP_DTREE_VIRTUAL:
sz = vfiles_path_len;
break;
}
if (this->sftp_path[sz] == 0 || this->sftp_path[sz+1] == 0) {
// Root... // Root...
result_ = MAP_TO_DIR; result_ = MAP_TO_DIR;
return; return;
} }
const char *lib = &this->sftp_path[files_path_len + 1]; lib = &this->sftp_path[sz + 1];
c = strchr(lib, '/'); c = strchr(lib, '/');
size_t libsz; size_t libsz;
if (c == nullptr) { if (c == nullptr) {
...@@ -332,6 +377,10 @@ public: ...@@ -332,6 +377,10 @@ public:
return is_static_; return is_static_;
} }
const enum sftp_dir_tree &tree(void) const {
return tree_;
}
const bool success(void) { const bool success(void) {
return result_ >= MAP_SUCCESS; return result_ >= MAP_SUCCESS;
} }
...@@ -449,9 +498,35 @@ is_in_filebase(const char *path) ...@@ -449,9 +498,35 @@ is_in_filebase(const char *path)
if (path[files_path_len] == 0 || path[files_path_len] == '/') if (path[files_path_len] == 0 || path[files_path_len] == '/')
return true; return true;
} }
if (memcmp(fls_path, path, fls_path_len) == 0) {
if (path[fls_path_len] == 0 || path[fls_path_len] == '/')
return true;
}
if (memcmp(vfiles_path, path, vfiles_path_len) == 0) {
if (path[vfiles_path_len] == 0 || path[vfiles_path_len] == '/')
return true;
}
return false; return false;
} }
static enum sftp_dir_tree
in_tree(const char *path)
{
if (memcmp(files_path, path, files_path_len) == 0) {
if (path[files_path_len] == 0 || path[files_path_len] == '/')
return SFTP_DTREE_FULL;
}
if (memcmp(fls_path, path, fls_path_len) == 0) {
if (path[fls_path_len] == 0 || path[fls_path_len] == '/')
return SFTP_DTREE_SHORT;
}
if (memcmp(vfiles_path, path, vfiles_path_len) == 0) {
if (path[vfiles_path_len] == 0 || path[vfiles_path_len] == '/')
return SFTP_DTREE_VIRTUAL;
}
throw std::invalid_argument( "Invalid path" );
}
/* /*
* Replaces a Solidus (aka: slash) with a U+29F8 Big Solidus * Replaces a Solidus (aka: slash) with a U+29F8 Big Solidus
* The SFTP protocol requires the use of Solidus as a path separator, * The SFTP protocol requires the use of Solidus as a path separator,
...@@ -827,11 +902,11 @@ get_filebase_attrs(sbbs_t *sbbs, int32_t dir, smbfile_t *file) ...@@ -827,11 +902,11 @@ get_filebase_attrs(sbbs_t *sbbs, int32_t dir, smbfile_t *file)
} }
static int static int
find_lib(sbbs_t *sbbs, const char *path) find_lib(sbbs_t *sbbs, const char *path, enum sftp_dir_tree tree)
{ {
char *p = strdup(path); char *p = strdup(path);
char *c = strchr(p, '/'); char *c = strchr(p, '/');
char *exp; char *exp {};
int l; int l;
if (c) if (c)
...@@ -839,7 +914,17 @@ find_lib(sbbs_t *sbbs, const char *path) ...@@ -839,7 +914,17 @@ find_lib(sbbs_t *sbbs, const char *path)
for (l = 0; l < sbbs->cfg.total_libs; l++) { for (l = 0; l < sbbs->cfg.total_libs; l++) {
if (!can_user_access_lib(&sbbs->cfg, l, &sbbs->useron, &sbbs->client)) if (!can_user_access_lib(&sbbs->cfg, l, &sbbs->useron, &sbbs->client))
continue; continue;
exp = expand_slash(sbbs->cfg.lib[l]->lname); switch (tree) {
case SFTP_DTREE_FULL:
exp = expand_slash(sbbs->cfg.lib[l]->lname);
break;
case SFTP_DTREE_SHORT:
exp = expand_slash(sbbs->cfg.lib[l]->sname);
break;
case SFTP_DTREE_VIRTUAL:
exp = expand_slash(sbbs->cfg.lib[l]->vdir);
break;
}
if (exp == nullptr) { if (exp == nullptr) {
free(p); free(p);
return -1; return -1;
...@@ -858,13 +943,13 @@ find_lib(sbbs_t *sbbs, const char *path) ...@@ -858,13 +943,13 @@ find_lib(sbbs_t *sbbs, const char *path)
} }
static int static int
find_dir(sbbs_t *sbbs, const char *path, int lib) find_dir(sbbs_t *sbbs, const char *path, int lib, enum sftp_dir_tree tree)
{ {
char *p = strdup(path); char *p = strdup(path);
char *c; char *c;
char *e; char *e;
int d; int d;
char *exp; char *exp{};
if (p == nullptr) if (p == nullptr)
return -1; return -1;
...@@ -883,7 +968,17 @@ find_dir(sbbs_t *sbbs, const char *path, int lib) ...@@ -883,7 +968,17 @@ find_dir(sbbs_t *sbbs, const char *path, int lib)
continue; continue;
if (!can_user_access_dir(&sbbs->cfg, d, &sbbs->useron, &sbbs->client)) if (!can_user_access_dir(&sbbs->cfg, d, &sbbs->useron, &sbbs->client))
continue; continue;
exp = expand_slash(sbbs->cfg.dir[d]->lname); switch (tree) {
case SFTP_DTREE_FULL:
exp = expand_slash(sbbs->cfg.dir[d]->lname);
break;
case SFTP_DTREE_SHORT:
exp = expand_slash(sbbs->cfg.dir[d]->code);
break;
case SFTP_DTREE_VIRTUAL:
exp = expand_slash(sbbs->cfg.dir[d]->vdir);
break;
}
if (exp == nullptr) { if (exp == nullptr) {
free(p); free(p);
return -1; return -1;
...@@ -934,15 +1029,26 @@ get_attrs(sbbs_t *sbbs, const char *path, char **link) ...@@ -934,15 +1029,26 @@ get_attrs(sbbs_t *sbbs, const char *path, char **link)
if (!is_in_filebase(path)) if (!is_in_filebase(path))
return nullptr; return nullptr;
libp = path + files_path_len + 1; enum sftp_dir_tree tree = in_tree(path);
lib = find_lib(sbbs, libp); switch (tree) {
case SFTP_DTREE_FULL:
libp = path + files_path_len + 1;
break;
case SFTP_DTREE_SHORT:
libp = path + fls_path_len + 1;
break;
case SFTP_DTREE_VIRTUAL:
libp = path + vfiles_path_len + 1;
break;
}
lib = find_lib(sbbs, libp, tree);
if (lib == -1) { if (lib == -1) {
return nullptr; return nullptr;
} }
const char *c = strchr(libp, '/'); const char *c = strchr(libp, '/');
if (c == nullptr || c[1] == 0) if (c == nullptr || c[1] == 0)
return get_lib_attrs(sbbs, lib); return get_lib_attrs(sbbs, lib);
dir = find_dir(sbbs, libp, lib); dir = find_dir(sbbs, libp, lib, tree);
if (dir == -1) if (dir == -1)
return nullptr; return nullptr;
c = strchr(c + 1, '/'); c = strchr(c + 1, '/');
...@@ -1406,6 +1512,7 @@ sftp_opendir(sftp_str_t path, void *cb_data) ...@@ -1406,6 +1512,7 @@ sftp_opendir(sftp_str_t path, void *cb_data)
if (pmap.result() != MAP_TO_DIR) if (pmap.result() != MAP_TO_DIR)
return pmap.cleanup(); return pmap.cleanup();
sbbs->sftp_dirdes[ddidx] = static_cast<sftp_dirdescriptor_t>(malloc(sizeof(*sbbs->sftp_dirdes[ddidx]))); sbbs->sftp_dirdes[ddidx] = static_cast<sftp_dirdescriptor_t>(malloc(sizeof(*sbbs->sftp_dirdes[ddidx])));
sbbs->sftp_dirdes[ddidx]->tree = pmap.tree();
if (pmap.is_static()) { if (pmap.is_static()) {
sbbs->sftp_dirdes[ddidx]->is_static = true; sbbs->sftp_dirdes[ddidx]->is_static = true;
sbbs->sftp_dirdes[ddidx]->info.rootdir.mapping = pmap.info.rootdir.mapping; sbbs->sftp_dirdes[ddidx]->info.rootdir.mapping = pmap.info.rootdir.mapping;
...@@ -1527,7 +1634,17 @@ sftp_readdir(sftp_dirhandle_t handle, void *cb_data) ...@@ -1527,7 +1634,17 @@ sftp_readdir(sftp_dirhandle_t handle, void *cb_data)
} }
if (dd->info.filebase.idx == dot) { if (dd->info.filebase.idx == dot) {
const char *dir = "."; const char *dir = ".";
strcpy(tmppath, SLASH_FILES); switch (dd->tree) {
case SFTP_DTREE_FULL:
strcpy(tmppath, SLASH_FILES);
break;
case SFTP_DTREE_SHORT:
strcpy(tmppath, SLASH_FLS);
break;
case SFTP_DTREE_VIRTUAL:
strcpy(tmppath, SLASH_VFILES);
break;
}
if (!fn.generic_dot_entry(strdup(dir), tmppath, dd->info.filebase.idx)) if (!fn.generic_dot_entry(strdup(dir), tmppath, dd->info.filebase.idx))
return sftps_send_error(sbbs->sftp_state, SSH_FX_EOF, "Error adding topdoot"); return sftps_send_error(sbbs->sftp_state, SSH_FX_EOF, "Error adding topdoot");
} }
...@@ -1551,7 +1668,17 @@ sftp_readdir(sftp_dirhandle_t handle, void *cb_data) ...@@ -1551,7 +1668,17 @@ sftp_readdir(sftp_dirhandle_t handle, void *cb_data)
attr = get_lib_attrs(sbbs, dd->info.filebase.idx); attr = get_lib_attrs(sbbs, dd->info.filebase.idx);
if (attr == nullptr) if (attr == nullptr)
return sftps_send_error(sbbs->sftp_state, SSH_FX_FAILURE, "Attributes allocation failure"); return sftps_send_error(sbbs->sftp_state, SSH_FX_FAILURE, "Attributes allocation failure");
ename = expand_slash(sbbs->cfg.lib[dd->info.filebase.idx]->lname); switch (dd->tree) {
case SFTP_DTREE_FULL:
ename = expand_slash(sbbs->cfg.lib[dd->info.filebase.idx]->lname);
break;
case SFTP_DTREE_SHORT:
ename = expand_slash(sbbs->cfg.lib[dd->info.filebase.idx]->sname);
break;
case SFTP_DTREE_VIRTUAL:
ename = expand_slash(sbbs->cfg.lib[dd->info.filebase.idx]->vdir);
break;
}
if (ename == nullptr) { if (ename == nullptr) {
sftp_fattr_free(attr); sftp_fattr_free(attr);
return sftps_send_error(sbbs->sftp_state, SSH_FX_FAILURE, "Ename allocation failure"); return sftps_send_error(sbbs->sftp_state, SSH_FX_FAILURE, "Ename allocation failure");
...@@ -1582,7 +1709,17 @@ sftp_readdir(sftp_dirhandle_t handle, void *cb_data) ...@@ -1582,7 +1709,17 @@ sftp_readdir(sftp_dirhandle_t handle, void *cb_data)
} }
if (dd->info.filebase.idx == dotdot) { if (dd->info.filebase.idx == dotdot) {
const char *dir = ".."; const char *dir = "..";
strcpy(tmppath, SLASH_FILES); switch (dd->tree) {
case SFTP_DTREE_FULL:
strcpy(tmppath, SLASH_FILES);
break;
case SFTP_DTREE_SHORT:
strcpy(tmppath, SLASH_FLS);
break;
case SFTP_DTREE_VIRTUAL:
strcpy(tmppath, SLASH_VFILES);
break;
}
if (!fn.generic_dot_entry(strdup(dir), tmppath, dd->info.filebase.idx)) if (!fn.generic_dot_entry(strdup(dir), tmppath, dd->info.filebase.idx))
return sftps_send_error(sbbs->sftp_state, SSH_FX_EOF, "Adding libdootdoot"); return sftps_send_error(sbbs->sftp_state, SSH_FX_EOF, "Adding libdootdoot");
} }
...@@ -1604,7 +1741,17 @@ sftp_readdir(sftp_dirhandle_t handle, void *cb_data) ...@@ -1604,7 +1741,17 @@ sftp_readdir(sftp_dirhandle_t handle, void *cb_data)
attr = get_dir_attrs(sbbs, dd->info.filebase.idx); attr = get_dir_attrs(sbbs, dd->info.filebase.idx);
if (attr == nullptr) if (attr == nullptr)
return sftps_send_error(sbbs->sftp_state, SSH_FX_FAILURE, "Attributes allocation failure"); return sftps_send_error(sbbs->sftp_state, SSH_FX_FAILURE, "Attributes allocation failure");
ename = expand_slash(sbbs->cfg.dir[dd->info.filebase.idx]->lname); switch (dd->tree) {
case SFTP_DTREE_FULL:
ename = expand_slash(sbbs->cfg.dir[dd->info.filebase.idx]->lname);
break;
case SFTP_DTREE_SHORT:
ename = expand_slash(sbbs->cfg.dir[dd->info.filebase.idx]->code);
break;
case SFTP_DTREE_VIRTUAL:
ename = expand_slash(sbbs->cfg.dir[dd->info.filebase.idx]->vdir);
break;
}
if (ename == nullptr) { if (ename == nullptr) {
sftp_fattr_free(attr); sftp_fattr_free(attr);
return sftps_send_error(sbbs->sftp_state, SSH_FX_FAILURE, "EName allocation failure"); return sftps_send_error(sbbs->sftp_state, SSH_FX_FAILURE, "EName allocation failure");
......
0% Loading or .
You are about to add 0 people to the discussion. Proceed with caution.
Please register or to comment