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