rippled
Loading...
Searching...
No Matches
Role.cpp
1#include <xrpld/rpc/Role.h>
2
3#include <boost/beast/http/field.hpp>
4#include <boost/utility/string_view.hpp>
5
6#include <algorithm>
7
8namespace ripple {
9
10bool
12{
13 XRPL_ASSERT(
14 !(port.admin_nets_v4.empty() && port.admin_nets_v6.empty()),
15 "ripple::passwordUnrequiredOrSentCorrect : non-empty admin nets");
16 bool const passwordRequired =
17 (!port.admin_user.empty() || !port.admin_password.empty());
18
19 return !passwordRequired ||
20 ((params["admin_password"].isString() &&
21 params["admin_password"].asString() == port.admin_password) &&
22 (params["admin_user"].isString() &&
23 params["admin_user"].asString() == port.admin_user));
24}
25
26bool
28 beast::IP::Address const& remoteIp,
31{
32 // To test whether the remoteIP is part of one of the configured
33 // subnets, first convert it to a subnet definition. For ipv4,
34 // this means appending /32. For ipv6, /128. Then based on protocol
35 // check for whether the resulting network is either a subnet of or
36 // equal to each configured subnet, based on boost::asio's reasoning.
37 // For example, 10.1.2.3 is a subnet of 10.1.2.0/24, but 10.1.2.0 is
38 // not. However, 10.1.2.0 is equal to the network portion of 10.1.2.0/24.
39
40 std::string addrString = remoteIp.to_string();
41 if (remoteIp.is_v4())
42 {
43 addrString += "/32";
44 auto ipNet = boost::asio::ip::make_network_v4(addrString);
45 for (auto const& net : nets4)
46 {
47 if (ipNet.is_subnet_of(net) || ipNet == net)
48 return true;
49 }
50 }
51 else
52 {
53 addrString += "/128";
54 auto ipNet = boost::asio::ip::make_network_v6(addrString);
55 for (auto const& net : nets6)
56 {
57 if (ipNet.is_subnet_of(net) || ipNet == net)
58 return true;
59 }
60 }
61
62 return false;
63}
64
65bool
67 Port const& port,
68 Json::Value const& params,
69 beast::IP::Address const& remoteIp)
70{
71 return ipAllowed(remoteIp, port.admin_nets_v4, port.admin_nets_v6) &&
73}
74
75Role
77 Role const& required,
78 Port const& port,
79 Json::Value const& params,
80 beast::IP::Endpoint const& remoteIp,
82{
83 if (isAdmin(port, params, remoteIp.address()))
84 return Role::ADMIN;
85
86 if (required == Role::ADMIN)
87 return Role::FORBID;
88
89 if (ipAllowed(
90 remoteIp.address(),
93 {
94 if (user.size())
95 return Role::IDENTIFIED;
96 return Role::PROXY;
97 }
98
99 return Role::GUEST;
100}
101
105bool
106isUnlimited(Role const& role)
107{
108 return role == Role::ADMIN || role == Role::IDENTIFIED;
109}
110
111bool
113 Role const& required,
114 Port const& port,
115 Json::Value const& params,
116 beast::IP::Endpoint const& remoteIp,
117 std::string const& user)
118{
119 return isUnlimited(requestRole(required, port, params, remoteIp, user));
120}
121
122Resource::Consumer
124 Resource::Manager& manager,
125 beast::IP::Endpoint const& remoteAddress,
126 Role const& role,
127 std::string_view user,
129{
130 if (isUnlimited(role))
131 return manager.newUnlimitedEndpoint(remoteAddress);
132
133 return manager.newInboundEndpoint(
134 remoteAddress, role == Role::PROXY, forwardedFor);
135}
136
137static std::string_view
139{
140 // Lambda to trim leading and trailing spaces on the field.
141 auto trim = [](std::string_view str) -> std::string_view {
142 std::string_view ret = str;
143
144 // Only do the work if there's at least one leading space.
145 if (!ret.empty() && ret.front() == ' ')
146 {
147 std::size_t const firstNonSpace = ret.find_first_not_of(' ');
148 if (firstNonSpace == std::string_view::npos)
149 // We know there's at least one leading space. So if we got
150 // npos, then it must be all spaces. Return empty string_view.
151 return {};
152
153 ret = ret.substr(firstNonSpace);
154 }
155 // Trim trailing spaces.
156 if (!ret.empty())
157 {
158 // Only do the work if there's at least one trailing space.
159 if (unsigned char const c = ret.back();
160 c == ' ' || c == '\r' || c == '\n')
161 {
162 std::size_t const lastNonSpace = ret.find_last_not_of(" \r\n");
163 if (lastNonSpace == std::string_view::npos)
164 // We know there's at least one leading space. So if we
165 // got npos, then it must be all spaces.
166 return {};
167
168 ret = ret.substr(0, lastNonSpace + 1);
169 }
170 }
171 return ret;
172 };
173
174 std::string_view ret = trim(field);
175 if (ret.empty())
176 return {};
177
178 // If there are surrounding quotes, strip them.
179 if (ret.front() == '"')
180 {
181 ret.remove_prefix(1);
182 if (ret.empty() || ret.back() != '"')
183 return {}; // Unbalanced double quotes.
184
185 ret.remove_suffix(1);
186
187 // Strip leading and trailing spaces that were inside the quotes.
188 ret = trim(ret);
189 }
190 if (ret.empty())
191 return {};
192
193 // If we have an IPv6 or IPv6 (dual) address wrapped in square brackets,
194 // then we need to remove the square brackets.
195 if (ret.front() == '[')
196 {
197 // Remove leading '['.
198 ret.remove_prefix(1);
199
200 // We may have an IPv6 address in square brackets. Scan up to the
201 // closing square bracket.
202 auto const closeBracket =
203 std::find_if_not(ret.begin(), ret.end(), [](unsigned char c) {
204 return std::isxdigit(c) || c == ':' || c == '.' || c == ' ';
205 });
206
207 // If the string does not close with a ']', then it's not valid IPv6
208 // or IPv6 (dual).
209 if (closeBracket == ret.end() || (*closeBracket) != ']')
210 return {};
211
212 // Remove trailing ']'
213 ret = ret.substr(0, closeBracket - ret.begin());
214 ret = trim(ret);
215 }
216 if (ret.empty())
217 return {};
218
219 // If this is an IPv6 address (after unwrapping from square brackets),
220 // then there cannot be an appended port. In that case we're done.
221 {
222 // Skip any leading hex digits.
223 auto const colon =
224 std::find_if_not(ret.begin(), ret.end(), [](unsigned char c) {
225 return std::isxdigit(c) || c == ' ';
226 });
227
228 // If the string starts with optional hex digits followed by a colon
229 // it's an IVv6 address. We're done.
230 if (colon == ret.end() || (*colon) == ':')
231 return ret;
232 }
233
234 // If there's a port appended to the IP address, strip that by
235 // terminating at the colon.
236 if (std::size_t colon = ret.find(':'); colon != std::string_view::npos)
237 ret = ret.substr(0, colon);
238
239 return ret;
240}
241
244{
245 // Look for the Forwarded field in the request.
246 if (auto it = request.find(boost::beast::http::field::forwarded);
247 it != request.end())
248 {
249 auto ascii_tolower = [](char c) -> char {
250 return ((static_cast<unsigned>(c) - 65U) < 26) ? c + 'a' - 'A' : c;
251 };
252
253 // Look for the first (case insensitive) "for="
254 static std::string const forStr{"for="};
255 char const* found = std::search(
256 it->value().begin(),
257 it->value().end(),
258 forStr.begin(),
259 forStr.end(),
260 [&ascii_tolower](char c1, char c2) {
261 return ascii_tolower(c1) == ascii_tolower(c2);
262 });
263
264 if (found == it->value().end())
265 return {};
266
267 found += forStr.size();
268
269 // We found a "for=". Scan for the end of the IP address.
270 std::size_t const pos = [&found, &it]() {
271 std::size_t pos = std::string_view(found, it->value().end() - found)
272 .find_first_of(",;");
273 if (pos != std::string_view::npos)
274 return pos;
275
276 return it->value().size() - forStr.size();
277 }();
278
279 return extractIpAddrFromField({found, pos});
280 }
281
282 // Look for the X-Forwarded-For field in the request.
283 if (auto it = request.find("X-Forwarded-For"); it != request.end())
284 {
285 // The first X-Forwarded-For entry may be terminated by a comma.
286 std::size_t found = it->value().find(',');
287 if (found == boost::string_view::npos)
288 found = it->value().length();
289 return extractIpAddrFromField(it->value().substr(0, found));
290 }
291
292 return {};
293}
294
295} // namespace ripple
T back(T... args)
T begin(T... args)
Represents a JSON value.
Definition json_value.h:131
bool isString() const
std::string asString() const
Returns the unquoted string value.
A version-independent IP address and port combination.
Definition IPEndpoint.h:19
Address const & address() const
Returns the address portion of this endpoint.
Definition IPEndpoint.h:56
Tracks load and resource consumption.
virtual Consumer newInboundEndpoint(beast::IP::Endpoint const &address)=0
Create a new endpoint keyed by inbound IP address or the forwarded IP if proxied.
virtual Consumer newUnlimitedEndpoint(beast::IP::Endpoint const &address)=0
Create a new unlimited endpoint keyed by forwarded IP.
T empty(T... args)
T end(T... args)
T find_first_not_of(T... args)
T find_first_of(T... args)
T find_if_not(T... args)
T find_last_not_of(T... args)
T front(T... args)
boost::asio::ip::address Address
Definition IPAddress.h:20
Use hash_* containers for keys that do not need a cryptographically secure hashing algorithm.
Definition algorithm.h:6
Resource::Consumer requestInboundEndpoint(Resource::Manager &manager, beast::IP::Endpoint const &remoteAddress, Role const &role, std::string_view user, std::string_view forwardedFor)
Definition Role.cpp:123
static std::string_view extractIpAddrFromField(std::string_view field)
Definition Role.cpp:138
bool isAdmin(Port const &port, Json::Value const &params, beast::IP::Address const &remoteIp)
Definition Role.cpp:66
bool isUnlimited(Role const &role)
ADMIN and IDENTIFIED roles shall have unlimited resources.
Definition Role.cpp:106
std::string_view forwardedFor(http_request_type const &request)
Definition Role.cpp:243
boost::beast::http::request< boost::beast::http::dynamic_body > http_request_type
Definition Handoff.h:14
bool passwordUnrequiredOrSentCorrect(Port const &port, Json::Value const &params)
Definition Role.cpp:11
Role
Indicates the level of administrative permission to grant.
Definition Role.h:25
Role requestRole(Role const &required, Port const &port, Json::Value const &params, beast::IP::Endpoint const &remoteIp, std::string_view user)
Return the allowed privilege role.
Definition Role.cpp:76
bool ipAllowed(beast::IP::Address const &remoteIp, std::vector< boost::asio::ip::network_v4 > const &nets4, std::vector< boost::asio::ip::network_v6 > const &nets6)
True if remoteIp is in any of adminIp.
Definition Role.cpp:27
T remove_prefix(T... args)
T remove_suffix(T... args)
T search(T... args)
T size(T... args)
Configuration information for a Server listening port.
Definition Port.h:31
std::vector< boost::asio::ip::network_v6 > admin_nets_v6
Definition Port.h:39
std::vector< boost::asio::ip::network_v6 > secure_gateway_nets_v6
Definition Port.h:41
std::string admin_user
Definition Port.h:44
std::vector< boost::asio::ip::network_v4 > secure_gateway_nets_v4
Definition Port.h:40
std::string admin_password
Definition Port.h:45
std::vector< boost::asio::ip::network_v4 > admin_nets_v4
Definition Port.h:38
T substr(T... args)