foo_httpcontrol: More strict Allowed paths checking; Fixed excessive error messages for Browse command; 0.97.17 released.

This commit is contained in:
oblikoamorale
2016-01-14 19:11:49 +03:00
parent 6b50e1ce47
commit d7c0b5cf19
9 changed files with 114 additions and 59 deletions

View File

@@ -95,7 +95,7 @@
<OptimizeReferences>true</OptimizeReferences>
<IgnoreSpecificDefaultLibraries>%(IgnoreSpecificDefaultLibraries)</IgnoreSpecificDefaultLibraries>
<AdditionalDependencies>$(SolutionDir)lib/foobar2000/shared/shared.lib;$(SolutionDir)lib/atlmfc/lib/atls.lib;shlwapi.lib;Ws2_32.lib;%(AdditionalDependencies)</AdditionalDependencies>
<Version>0.97.15</Version>
<Version>0.97.17</Version>
<ShowProgress>NotSet</ShowProgress>
<ModuleDefinitionFile>
</ModuleDefinitionFile>

View File

@@ -28,7 +28,7 @@
</style>
</head>
<body>
<h2>foo_httpcontrol 0.97.16 13 Jan 2016</h2>
<h2>foo_httpcontrol 0.97.17 14 Jan 2016</h2>
<p><a href="https://bitbucket.org/oblikoamorale/foo_httpcontrol/wiki/Home">https://bitbucket.org/oblikoamorale/foo_httpcontrol/wiki/Home</a></p>
<h2 id="warn">Warning/disclaimer: this software comes without any warranties at all. It is still in the very early development stage, and so on, and so forth, blah-blah-blah.
@@ -75,11 +75,12 @@ customizing any template you are using, or even writing your very own one.</p>
<li>Opens component page in default browser. Button is inactive if server/listener isn't started.</li>
<li>Limit access to component by setting the allowed IP address. Connections initiated from other addresses will be discarded. Setting this to 0.0.0.0 allows all connections.</li>
<li>Limit access to component by defining a login/password pair. Untick to disable passwords.</li>
<li>Built-in file browser can be limited to certain paths of your file system. It is useful to simplify the browsing process or hide your secret files from prying eyes. For example, setting Allowed paths to d:\music|c:\temp\music|e:\ permits browsing only in these three file system branches.</li>
<li>Built-in file browser can be limited to certain paths of your file system. It is useful to simplify the browsing process or hide your secret files from prying eyes. For example, setting Allowed paths to d:\music|c:\temp\music|e:\ permits browsing only in these three file system branches.
To additionally access Microsoft Network, add 'Network:' and '\\' (without quotes) to allowed paths.</li>
<li>You can specify additional file extensions to be treated as playable files by built-in file browser. It is required for opening archives containing playable files.</li>
<li>Ignore files with specific extensions in file browser.</li>
<li>Urls of specified protocols are allowed to be enqueued by Browse command. Enables enqueueing of urls handled by 3rd party components like foo_youtube (3dydfy protocol).<br>
Suggested value setting: http|https</li>
Suggested value: http|https</li>
<li>Removes all files which foobar2000 core considers unplayable from built-in file browser.</li>
<li>Specifies path where component will look for template files. Pressing Open opens specified or default directory in Explorer. If not sure, leave it blank. </li>
<li>Enables gzip compression of component output. Enabling it is usually harmless and quite beneficial as component generates a lot of text which is very compressible. Disable if you are using some funky browser and having unexpected problems.</li>
@@ -94,6 +95,12 @@ Don't forget to press Apply or your changes won't have any effect until fb2k is
<a href="http://www.hydrogenaudio.org/forums/index.php?showtopic=62218">http://www.hydrogenaudio.org/forums/index.php?showtopic=62218</a></p>
<h2>Changes history 2016</h2>
<p>v0.97.17 14 Jan</p>
<ul>
<li>fix: More strict Allowed paths checking. Now, to be able to access Microsoft Network when operating in Allowed paths mode, add 'Network:' and '\\' (without quotes) to Allowed paths value;<br>
<li>fix: Excessive error messages for Browse command;</li>
</ul>
<p>v0.97.16 13 Jan</p>
<ul>
<li>add: Allowed protocols GUI setting: urls of specified protocols are allowed to be enqueued by Browse command. Previously the only hardcoded protocol was http. New setting also enables enqueueing of urls handled by 3rd party components like foo_youtube (3dydfy protocol).<br>
@@ -101,7 +108,6 @@ Suggested value setting: http|https</li>
<li>add: Meaningful console error messages when Browse command refuses to add specified file / location;</li>
</ul>
<h2>Changes history 2014</h2>
<p>v0.97.15 02 Mar</p>
<ul>
@@ -449,6 +455,11 @@ example: ?cmd=PlayingCommand&amp;param1=Playback%20Statistics%2FRating%2F3</li>
cmd=SetFocus<br/>
param1=item starting from 0</li>
<li>Get directory file list<br/>
cmd=Browse<br/>
param1=urlencoded directory path with trailing backslash<br/>
</li>
<li>Enqueue single file or url<br/>
cmd=Browse<br/>
param1=urlencoded file path / url</li>

View File

@@ -825,35 +825,64 @@ namespace control
if (httpc::enqueueing)
return false;
pfc::string8 &param1 = cmd->get_param(0);
pfc::string8 &param2 = cmd->get_param(1);
pfc::string8 param1 = cmd->get_param(cmd->E_PARAM1);
pfc::string8 param2 = cmd->get_param(cmd->E_PARAM2);
foo_browsefiles browser;
list_t<const char *> files; // files/dirs to be enqueued
pfc::string8 filename = param1;
bool is_dir = false;
bool is_dir_allowed = false;
bool is_file = false;
bool is_url = false;
// if no path specified, use last browse directory
if (param1.get_length() == 0)
foo_error("param1 cannot be empty");
param1 = cfg.misc.last_browse_dir;
// check if param2 is a valid command
if (param2.get_length() > 0 &&
(strcmp(param2, "EnqueueDir") != 0 && strcmp(param2, "EnqueueDirSubdirs") != 0))
foo_error(pfc::string8() << "unsupported param2 value: \"" << param2 << "\"");
foo_error(pfc::string8() << "ignoring param2: \"" << param2 << "\": unknown mode");
if (( httpc::is_extension_registered(filename) != pfc::infinite_size || httpc::is_protocol_allowed(filename) ) && param2.get_length() == 0) // adding a single file
files.add_item(filename);
// assuming path is a directory if ends with separator
// " " is a root shortcut, also counts as a directory
if (param1.ends_with('\\') || strcmp(param1, " ") == 0)
is_dir = true;
if (httpc::is_protocol_allowed(param1))
is_url = true;
if (!is_dir && !is_url)
is_file = true;
if (is_dir || is_file)
{
is_dir_allowed = httpc::is_path_allowed(param1);
if (!is_dir_allowed)
foo_error(pfc::string8() << "ignoring param1: \"" << param1 << "\": doesn't match Allowed paths");
}
// try to enqueue param1 if is a file with allowed extension, or url with allowed protocol
if ((is_file && httpc::is_extension_registered(param1) != pfc::infinite_size || is_url)
&& param2.get_length() == 0) // add a file / location
{
files.add_item(param1);
}
else
if (param2.get_length() == 0)
foo_error(pfc::string8() << "skipping \"" << filename << "\": unrecognized extension or not allowed protocol");
is_file = false;
if (param1.get_length() != 0 && param2.get_length() != 0)
if (!is_dir && !is_file && !is_url)
foo_error(pfc::string8() << "ignoring param1: \"" << param1 << "\": not a directory, unrecognized extension or not allowed protocol");
if (is_dir && is_dir_allowed && param2.get_length() != 0)
{
if (strcmp(param2, "EnqueueDirSubdirs") == 0) // adding a nested directory
{
files.add_item(filename);
}
files.add_item(param1);
if (strcmp(param2, "EnqueueDir") == 0) // adding a directory without nesting;
{
browser.browse(const_cast<char *>(filename.operator const char *()));
foo_browsefiles browser;
browser.browse(const_cast<char *>(param1.operator const char *()));
t_size l = browser.entries.get_count();
for(unsigned int i = 0; i < l; ++i)

View File

@@ -51,7 +51,7 @@ void config_main::reset()
gzip_enable = false;
extra_formats = "zip|rar";
ignored_formats = "";
allowed_protocols = "http|https|3dydfy";
allowed_protocols = "http|https";
}
void config_main::get_data_raw(stream_writer * p_stream,abort_callback & p_abort)

View File

@@ -3,7 +3,7 @@
DECLARE_COMPONENT_VERSION(
"HTTP Control",
"0.97.16",
"0.97.17",
"control foobar2000 via http "__DATE__)
VALIDATE_COMPONENT_FILENAME("foo_httpcontrol.dll");

View File

@@ -558,49 +558,30 @@ void foo_httpserv::process_request()
else
if (strcmp(cmd, "Browse") == 0)
{
if (param1.get_length() == 0)
param1 = cfg.misc.last_browse_dir;
if (cfg.restrict_to_path_list.get_count()) // allowing to browse only specified dirs (if any)
{
bool isallowed = false;
if (param1.get_length() > 0)
{
pfc::string tmp2(param1.toString());
t_size l = cfg.restrict_to_path_list.get_count();
if ((tmp2.indexOf("..\\") == ~0) && (tmp2.indexOf("../") == ~0)) // check for stuff like d:\music\..\..\..\temp
for (size_t i = 0; i < l; ++i)
{
pfc::string8_fast_aggressive tmp(param1);
tmp.truncate(cfg.restrict_to_path_list[i].get_length());
if (pfc::stringCompareCaseInsensitive(tmp, cfg.restrict_to_path_list[i]) == 0)
{
isallowed = true;
break;
}
}
}
if (! isallowed)
param1 = " ";
}
// strip file name from path if the user clicked on file
bool is_file = false;
bool is_url = httpc::is_protocol_allowed(param1);
if (param1.length() > 3 && param1[param1.length()-1] != '\\')
// if no path specified or adding url, use last browse directory
if (param1.get_length() == 0 || is_url)
param1 = cfg.misc.last_browse_dir;
if (!is_url)
if (!httpc::is_path_allowed(param1)) // allowing to browse only specified dirs (if any)
param1 = cfg.misc.last_browse_dir;
if (param1.length() > 3 && !param1.ends_with('\\') && !is_url)
{
char *stripped_filename = foo_browsefiles::get_path_parent((char *)param1.operator const char *());
if (stripped_filename)
if (stripped_filename && strlen(stripped_filename) > 2)
{
param1 = stripped_filename;
delete[] stripped_filename;
}
else // invalid request, displaying root
param1 = " ";
else // invalid request, displaying last browse dir
param1 = cfg.misc.last_browse_dir;
is_file = true;
}
@@ -611,7 +592,7 @@ void foo_httpserv::process_request()
httpc::ui::parse_buffer_browser(param1, show, timer);
}
if (param1.get_length() != 0 && !is_file)
if (param1.get_length() != 0 && !is_file && !is_url)
cfg.misc.last_browse_dir = param1;
}
else

View File

@@ -63,11 +63,17 @@ public:
class foo_httpserver_command
{
public:
enum commands {
E_PARAM1=0,
E_PARAM2=1,
E_PARAM3=2
};
foo_httpserver_command() { };
foo_httpserver_command(pfc::string8 cmd, pfc::string8 param1, pfc::string8 param2, pfc::string8 param3) : m_cmd(cmd), m_param1(param1), m_param2(param2), m_param3(param3) { };
pfc::string8 &get_command() { return m_cmd; }
pfc::string8 &get_param(int index)
pfc::string8 get_command() { return m_cmd; }
pfc::string8 get_param(int index)
{
switch(index) {
case 0: return m_param1;

View File

@@ -580,6 +580,30 @@ namespace httpc {
return false;
}
bool is_path_allowed(const char *path)
{
if (cfg.restrict_to_path_list.get_count()) // allowing to browse only specified dirs (if any)
{
if (strlen(path) > 0)
{
t_size l = cfg.restrict_to_path_list.get_count();
if (strstr(path, "..\\") == NULL && strstr(path, "../") == NULL) // check for stuff like d:\music\..\..\..\temp
for (size_t i = 0; i < l; ++i)
{
pfc::string8 tmp(path);
tmp.truncate(cfg.restrict_to_path_list[i].get_length());
if (pfc::stringCompareCaseInsensitive(tmp, cfg.restrict_to_path_list[i]) == 0)
return true;
}
return false;
}
}
return true;
}
void get_registered_extensions()
{
extensions.remove_all();
@@ -706,6 +730,9 @@ namespace httpc {
{
cfg.restrict_to_path_list.remove_all();
get_list(cfg.main.restrict_to_path, cfg.restrict_to_path_list, '|', true);
if (cfg.restrict_to_path_list.get_count())
cfg.restrict_to_path_list.add_item(" ");
}
void control_credentials_auth_hash_update()

View File

@@ -126,6 +126,7 @@ namespace httpc {
extern void set_allowed_protocols();
extern size_t is_extension_registered(const char *path); // infininte if not registered, list index elsewere
extern bool is_protocol_allowed(const char *path);
extern bool is_path_allowed(const char *path);
extern void choose_srv_home_dir();
extern void build_restrict_to_path_list();