| Línea 53... |
Línea 53... |
| 53 |
'http' => 80,
|
53 |
'http' => 80,
|
| 54 |
'https' => 443
|
54 |
'https' => 443
|
| 55 |
];
|
55 |
];
|
| Línea 56... |
Línea 56... |
| 56 |
|
56 |
|
| - |
|
57 |
/**
|
| - |
|
58 |
* @var string the host of the URL being checked by the helper.
|
| - |
|
59 |
*/
|
| - |
|
60 |
protected $host;
|
| - |
|
61 |
|
| - |
|
62 |
/**
|
| - |
|
63 |
* @var array IP address or addresses the URL is allowed to be requested from (passed the blocked hosts check).
|
| - |
|
64 |
*/
|
| - |
|
65 |
protected $allowedips = [];
|
| - |
|
66 |
|
| - |
|
67 |
/**
|
| - |
|
68 |
* @var ?int The port the URL is allowed to be requested from (passed the allowed port check).
|
| - |
|
69 |
*/
|
| - |
|
70 |
protected $allowedport;
|
| - |
|
71 |
|
| 57 |
/**
|
72 |
/**
|
| 58 |
* Checks whether the given URL is blocked by checking its address and port number against the allow/block lists.
|
73 |
* Checks whether the given URL is blocked by checking its address and port number against the allow/block lists.
|
| 59 |
* The behaviour of this function can be classified as strict, as it returns true for URLs which are invalid or
|
74 |
* The behaviour of this function can be classified as strict, as it returns true for URLs which are invalid or
|
| 60 |
* could not be parsed, as well as those valid URLs which were found in the blocklist.
|
75 |
* could not be parsed, as well as those valid URLs which were found in the blocklist.
|
| 61 |
*
|
76 |
*
|
| Línea 83... |
Línea 98... |
| 83 |
} catch (\moodle_exception $e) {
|
98 |
} catch (\moodle_exception $e) {
|
| 84 |
// Moodle exception is thrown if the $urlstring is invalid. Treat as blocked.
|
99 |
// Moodle exception is thrown if the $urlstring is invalid. Treat as blocked.
|
| 85 |
return true;
|
100 |
return true;
|
| 86 |
}
|
101 |
}
|
| Línea -... |
Línea 102... |
| - |
|
102 |
|
| - |
|
103 |
$this->host = $parsed['host'];
|
| 87 |
|
104 |
|
| 88 |
// The port will be empty unless explicitly set in the $url (uncommon), so try to infer it from the supported schemes.
|
105 |
// The port will be empty unless explicitly set in the $url (uncommon), so try to infer it from the supported schemes.
|
| 89 |
if (!$parsed['port'] && $parsed['scheme'] && isset($this->transportschemes[$parsed['scheme']])) {
|
106 |
if (!$parsed['port'] && $parsed['scheme'] && isset($this->transportschemes[$parsed['scheme']])) {
|
| 90 |
$parsed['port'] = $this->transportschemes[$parsed['scheme']];
|
107 |
$parsed['port'] = $this->transportschemes[$parsed['scheme']];
|
| Línea 160... |
Línea 177... |
| 160 |
// If we don't get a valid record, bail (so cURL is never called).
|
177 |
// If we don't get a valid record, bail (so cURL is never called).
|
| 161 |
if (!$hostips) {
|
178 |
if (!$hostips) {
|
| 162 |
return true;
|
179 |
return true;
|
| 163 |
}
|
180 |
}
|
| Línea 164... |
Línea 181... |
| 164 |
|
181 |
|
| - |
|
182 |
// If any of the returned IPs are in the blocklist, block the request. Otherwise, temporarily record the IPs.
|
| 165 |
// If any of the returned IPs are in the blocklist, block the request.
|
183 |
$allowedips = [];
|
| 166 |
foreach ($hostips as $hostip) {
|
184 |
foreach ($hostips as $hostip) {
|
| 167 |
if ($this->address_explicitly_blocked($hostip)) {
|
185 |
if ($this->address_explicitly_blocked($hostip)) {
|
| 168 |
return true;
|
186 |
return true;
|
| - |
|
187 |
}
|
| 169 |
}
|
188 |
$allowedips[] = $hostip;
|
| - |
|
189 |
}
|
| - |
|
190 |
|
| - |
|
191 |
// If none of the IPs are blocked, set them on the allow list so we can enforce them on subsequent requests.
|
| 170 |
}
|
192 |
$this->allowedips = $allowedips;
|
| 171 |
}
|
193 |
}
|
| 172 |
} else {
|
194 |
} else {
|
| 173 |
// Was not something we consider to be a valid IP or domain name, block it.
|
195 |
// Was not something we consider to be a valid IP or domain name, block it.
|
| 174 |
return true;
|
196 |
return true;
|
| Línea 199... |
Línea 221... |
| 199 |
// Intentionally block port 0 and below and check the int cast was valid.
|
221 |
// Intentionally block port 0 and below and check the int cast was valid.
|
| 200 |
if (empty($port) || (string)$portnum !== (string)$port || $port < 0) {
|
222 |
if (empty($port) || (string)$portnum !== (string)$port || $port < 0) {
|
| 201 |
return true;
|
223 |
return true;
|
| 202 |
}
|
224 |
}
|
| 203 |
$allowedports = $this->get_allowed_ports();
|
225 |
$allowedports = $this->get_allowed_ports();
|
| - |
|
226 |
|
| 204 |
return !empty($allowedports) && !in_array($portnum, $allowedports);
|
227 |
$isblocked = !empty($allowedports) && !in_array($portnum, $allowedports);
|
| - |
|
228 |
|
| - |
|
229 |
// If port is allowed, add it to our allow list so we can enforce it on subsequent requests.
|
| - |
|
230 |
if (!$isblocked) {
|
| - |
|
231 |
$this->allowedport = $portnum;
|
| - |
|
232 |
}
|
| - |
|
233 |
|
| - |
|
234 |
return $isblocked;
|
| 205 |
}
|
235 |
}
|
| Línea 206... |
Línea 236... |
| 206 |
|
236 |
|
| 207 |
/**
|
237 |
/**
|
| 208 |
* Convenience method to check whether we have any entries in the host blocklist or ports allowlist admin settings.
|
238 |
* Convenience method to check whether we have any entries in the host blocklist or ports allowlist admin settings.
|
| Línea 289... |
Línea 319... |
| 289 |
}
|
319 |
}
|
| 290 |
return array_filter(array_map('trim', explode("\n", $CFG->curlsecurityblockedhosts)), function($entry) {
|
320 |
return array_filter(array_map('trim', explode("\n", $CFG->curlsecurityblockedhosts)), function($entry) {
|
| 291 |
return !empty($entry);
|
321 |
return !empty($entry);
|
| 292 |
});
|
322 |
});
|
| 293 |
}
|
323 |
}
|
| - |
|
324 |
|
| - |
|
325 |
/**
|
| - |
|
326 |
* Helper that returns host, IP and port information for the URL that has passed the blocked hosts/allowed ports checks.
|
| - |
|
327 |
*
|
| - |
|
328 |
* This data is in a format compatible with CURLOPT_RESOLVE, so it can be passed directly into that option.
|
| - |
|
329 |
* Doing so will prevent cURL re-fetching the info from DNS, preventing subsequent requests to the remote host from
|
| - |
|
330 |
* modifying the IP/port to ones that haven't been validated.
|
| - |
|
331 |
*
|
| - |
|
332 |
* @return array of strings in the format hostname:port:ip_address.
|
| - |
|
333 |
* @throws \coding_exception
|
| - |
|
334 |
*/
|
| - |
|
335 |
public function get_resolve_info(): array {
|
| - |
|
336 |
if (empty($this->host || empty($this->allowedips) || empty($this->allowedport))) {
|
| - |
|
337 |
$exception = 'In the curl_security_helper class, url_is_blocked() must be called before get_resolve_info() is called.';
|
| - |
|
338 |
throw new \core\exception\coding_exception($exception);
|
| - |
|
339 |
}
|
| - |
|
340 |
|
| - |
|
341 |
return array_map(fn($ip) => "$this->host:$this->allowedport:$ip", $this->allowedips);
|
| - |
|
342 |
}
|
| 294 |
}
|
343 |
}
|