Skip to content

Commit e1454e2

Browse files
committed
Added basic command whitelist functionality
1 parent c317cbb commit e1454e2

File tree

8 files changed

+241
-2
lines changed

8 files changed

+241
-2
lines changed

inc/webdriver_access.h

Lines changed: 37 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,37 @@
1+
#ifndef WEBDRIVER_ACCESS_H
2+
#define WEBDRIVER_ACCESS_H
3+
4+
#include <string>
5+
#include <vector>
6+
#include "base/file_util.h"
7+
8+
namespace webdriver {
9+
10+
struct AccessCommandTable
11+
{
12+
std::string method;
13+
std::string url;
14+
};
15+
16+
struct AccessRule {
17+
long host_ip;
18+
bool allowed;
19+
std::vector<AccessCommandTable> commandList;
20+
};
21+
22+
class AccessValidator
23+
{
24+
public:
25+
AccessValidator();
26+
~AccessValidator();
27+
void setWhiteList(FilePath &xmlPath);
28+
bool isAllowed(const long &remote_ip, const std::string &url, const std::string &method);
29+
30+
private:
31+
bool convertIpString(const char *str_ip, long *int_ip);
32+
std::vector<AccessRule> accessList;
33+
};
34+
35+
} // namespace webdriver
36+
37+
#endif // WEBDRIVER_ACCESS_H

inc/webdriver_server.h

Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -61,6 +61,7 @@ For all options please refer:
6161
#include "base/command_line.h"
6262
#include "base/memory/singleton.h"
6363
#include "base/values.h"
64+
#include "webdriver_access.h"
6465

6566
struct mg_context;
6667
struct mg_connection;
@@ -127,6 +128,7 @@ class Server {
127128
std::string url_base_;
128129
struct mg_context* mg_ctx_;
129130
State state_;
131+
AccessValidator accessValidor;
130132

131133
void DispatchCommand(const std::string& matched_route,
132134
Command* command_ptr,

inc/webdriver_switches.h

Lines changed: 7 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -79,6 +79,13 @@ class Switches {
7979
/// if parameter specified, user input device enabled
8080
static const char kUserInputDevice[];
8181

82+
/// \page page_webdriver_switches WD Server switches
83+
/// - <b>config</b><br>
84+
/// The path to whitelist file (e.g. whitelist.xml) in
85+
/// XML format with specified list of IP with allowed/disallowed
86+
/// commands for each of them
87+
static const char kWhiteList[];
88+
8289
};
8390

8491
} // namespace webdriver

src/webdriver/webdriver_access.cc

Lines changed: 176 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,176 @@
1+
#include "webdriver_access.h"
2+
#include "webdriver_logging.h"
3+
#include <base/string_util.h>
4+
#include "third_party/pugixml/pugixml.hpp"
5+
6+
namespace webdriver {
7+
8+
AccessValidator::AccessValidator(){
9+
}
10+
11+
AccessValidator::~AccessValidator(){
12+
}
13+
14+
bool AccessValidator::isAllowed(const long &remote_ip, const std::string &url, const std::string &method)
15+
{
16+
if (accessList.empty())
17+
return true;
18+
bool result = false;
19+
for (std::vector<AccessRule>::iterator it = accessList.begin(); it != accessList.end(); ++it)
20+
{
21+
AccessRule host = *it;
22+
if (host.host_ip == remote_ip)
23+
{
24+
if (!host.allowed) {
25+
for (std::vector<AccessCommandTable>::iterator it = host.commandList.begin(); it != host.commandList.end(); ++it) {
26+
AccessCommandTable table = *it;
27+
if (MatchPattern(url, table.url) && MatchPattern(method, table.method))
28+
return false;
29+
}
30+
return true;
31+
} else {
32+
if (host.commandList.empty())
33+
return true;
34+
for (std::vector<AccessCommandTable>::iterator it = host.commandList.begin(); it != host.commandList.end(); ++it) {
35+
AccessCommandTable table = *it;
36+
if (MatchPattern(url, table.url) && MatchPattern(method, table.method))
37+
return true;
38+
}
39+
return false;
40+
}
41+
}
42+
}
43+
return result;
44+
}
45+
46+
void AccessValidator::setWhiteList(FilePath &xmlPath)
47+
{
48+
std::string white_list;
49+
50+
if (file_util::ReadFileToString(xmlPath, &white_list))
51+
{
52+
const void* content = white_list.c_str();
53+
int content_size = white_list.size();
54+
55+
pugi::xml_document doc;
56+
pugi::xml_parse_result result = doc.load_buffer(content, content_size);
57+
58+
if (result) {
59+
// Select host nodes
60+
pugi::xpath_query query_nodes("/hosts/host");
61+
pugi::xpath_node_set found_nodes = query_nodes.evaluate_node_set(doc);
62+
63+
if ( (NULL == query_nodes.result().error) &&
64+
(pugi::xpath_type_node_set == query_nodes.return_type()) ) {
65+
for (pugi::xpath_node_set::const_iterator it = found_nodes.begin(); it != found_nodes.end(); ++it) {
66+
pugi::xpath_node node = *it;
67+
pugi::xml_node xnode = node.node();
68+
pugi::xml_attribute atr = xnode.attribute("ip");
69+
70+
AccessRule rule;
71+
if (!convertIpString(atr.value(), &rule.host_ip)) {
72+
std::string error_descr = "WhiteList: "+ std::string(atr.value()) + " is not a valid ip address";
73+
GlobalLogger::Log(kWarningLogLevel, error_descr);
74+
continue;
75+
}
76+
77+
rule.allowed = true;
78+
pugi::xpath_query query_nodes("./deny");
79+
pugi::xpath_node_set deny_nodes = query_nodes.evaluate_node_set(xnode);
80+
if ( (NULL == query_nodes.result().error) &&
81+
(pugi::xpath_type_node_set == query_nodes.return_type()) ) {
82+
for (pugi::xpath_node_set::const_iterator it = deny_nodes.begin(); it != deny_nodes.end(); ++it) {
83+
pugi::xpath_node node = *it;
84+
pugi::xml_node xnode = node.node();
85+
pugi::xml_attribute atr = xnode.attribute("url");
86+
87+
AccessCommandTable rt;
88+
rt.url = atr.value();
89+
atr = xnode.attribute("method");
90+
rt.method = atr.value();
91+
rule.commandList.push_back(rt);
92+
rule.allowed = false;
93+
}
94+
}
95+
if (rule.allowed)
96+
{
97+
pugi::xpath_query query_nodes("./allow");
98+
pugi::xpath_node_set allow_nodes = query_nodes.evaluate_node_set(xnode);
99+
if ( (NULL == query_nodes.result().error) &&
100+
(pugi::xpath_type_node_set == query_nodes.return_type()) ) {
101+
for (pugi::xpath_node_set::const_iterator it = allow_nodes.begin(); it != allow_nodes.end(); ++it) {
102+
pugi::xpath_node node = *it;
103+
pugi::xml_node xnode = node.node();
104+
pugi::xml_attribute atr = xnode.attribute("url");
105+
AccessCommandTable rt;
106+
rt.url = atr.value();
107+
atr = xnode.attribute("method");
108+
rt.method = atr.value();
109+
rule.commandList.push_back(rt);
110+
}
111+
}
112+
}
113+
114+
accessList.push_back(rule);
115+
}
116+
} else {
117+
std::string error_descr = "WhiteList: Cant evaluate XPath to node set: ";
118+
error_descr += query_nodes.result().description();
119+
GlobalLogger::Log(kWarningLogLevel, error_descr);
120+
}
121+
}
122+
else
123+
{
124+
std::string error_descr = " Error description: ";
125+
error_descr += result.description();
126+
GlobalLogger::Log(kWarningLogLevel, "WhiteList: XML parsed with errors:");
127+
GlobalLogger::Log(kWarningLogLevel, error_descr);
128+
}
129+
130+
// destroy tree
131+
doc.reset();
132+
}
133+
else
134+
{
135+
GlobalLogger::Log(kWarningLogLevel, "WhiteList: Can't read file");
136+
}
137+
}
138+
139+
bool AccessValidator::convertIpString(const char *str_ip, long *int_ip)
140+
{
141+
bool result = false;
142+
char *p, c;
143+
long octet, n;
144+
145+
*int_ip = 0;
146+
octet = 0;
147+
n = 0;
148+
149+
char buff[sizeof "000.000.000.000"];
150+
if (strlen(str_ip) < sizeof buff) {
151+
strcpy(buff, str_ip);
152+
}
153+
154+
for (p = buff; *p != '\0' ; ++p) {
155+
c = *p;
156+
if (c >= '0' && c <= '9') {
157+
octet = octet * 10 + (c - '0');
158+
continue;
159+
}
160+
if (c == '.' && octet < 256) {
161+
*int_ip = (*int_ip << 8) + octet;
162+
octet = 0;
163+
n++;
164+
continue;
165+
}
166+
return result;
167+
}
168+
169+
if (n == 3 && octet < 256) {
170+
*int_ip = (*int_ip << 8) + octet;
171+
result = true;
172+
}
173+
return result;
174+
}
175+
176+
} // namespace webdriver

src/webdriver/webdriver_server.cc

Lines changed: 15 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -33,7 +33,6 @@
3333
#include "webdriver_view_enumerator.h"
3434
#include "webdriver_view_executor.h"
3535

36-
3736
namespace webdriver {
3837

3938
Server::Server()
@@ -80,6 +79,13 @@ int Server::Configure(const CommandLine &options) {
8079
"\nBuild Time: "+ VersionInfo::BuildDateTime() ;
8180
GlobalLogger::Log(kInfoLogLevel, driver_info);
8281

82+
if (options.HasSwitch(webdriver::Switches::kWhiteList))
83+
{
84+
FilePath xmlPath(options_->GetSwitchValueNative(webdriver::Switches::kWhiteList));
85+
accessValidor.setWhiteList(xmlPath);
86+
}
87+
88+
8389
// set default route table
8490
routeTable_.reset(new DefaultRouteTable());
8591

@@ -426,6 +432,14 @@ bool Server::ParseRequestInfo(const struct mg_request_info* const request_info,
426432

427433
base::SplitString(uri, '/', path_segments);
428434

435+
if (!accessValidor.isAllowed(request_info->remote_ip, uri, *method))
436+
{
437+
response->SetError(new Error(
438+
kUnknownCommand,
439+
"Command was restricted by whitelist"));
440+
return false;
441+
}
442+
429443
if (*method == "POST") {
430444
std::string json;
431445
ReadRequestBody(request_info, connection, &json);

src/webdriver/webdriver_switches.cc

Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -26,4 +26,6 @@ const char Switches::kVNCLogin[] = "vnc-login";
2626

2727
const char Switches::kUserInputDevice[] = "uinput";
2828

29+
const char Switches::kWhiteList[] = "white-list";
30+
2931
} // namespace webdriver

wd_core.gyp

Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -58,6 +58,7 @@
5858
'src/webdriver/frame_path.cc',
5959
'src/webdriver/http_response.cc',
6060
'src/webdriver/value_conversion_traits.cc',
61+
'src/webdriver/webdriver_access.cc',
6162
'src/webdriver/webdriver_basic_types.cc',
6263
'src/webdriver/webdriver_capabilities_parser.cc',
6364
'src/webdriver/webdriver_element_id.cc',
@@ -78,6 +79,7 @@
7879
'src/webdriver/url_command_wrapper.cc',
7980
'src/webdriver/versioninfo.cc',
8081
'src/webdriver/webdriver_version.cc',
82+
'src/third_party/pugixml/pugixml.cpp'
8183
],
8284

8385
'conditions': [

wd_ext_qt.gyp

Lines changed: 0 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -55,7 +55,6 @@
5555
'src/webdriver/extension_qt/vnc_event_dispatcher.cc',
5656
'src/webdriver/extension_qt/wd_event_dispatcher.cc',
5757
'src/webdriver/extension_qt/uinput_event_dispatcher.cc',
58-
'src/third_party/pugixml/pugixml.cpp'
5958
],
6059

6160
'conditions': [

0 commit comments

Comments
 (0)