1 |
efrain |
1 |
<?php
|
|
|
2 |
/**
|
|
|
3 |
* An object to represent lots of information about an RPC-peer machine
|
|
|
4 |
*
|
|
|
5 |
* @author Donal McMullan donal@catalyst.net.nz
|
|
|
6 |
* @version 0.0.1
|
|
|
7 |
* @license http://www.gnu.org/copyleft/gpl.html GNU Public License
|
|
|
8 |
* @package mnet
|
|
|
9 |
*/
|
|
|
10 |
|
|
|
11 |
require_once($CFG->libdir . '/filelib.php'); // download_file_content() used here
|
|
|
12 |
|
|
|
13 |
class mnet_peer {
|
|
|
14 |
|
|
|
15 |
/** No SSL verification. */
|
|
|
16 |
const SSL_NONE = 0;
|
|
|
17 |
|
|
|
18 |
/** SSL verification for host. */
|
|
|
19 |
const SSL_HOST = 1;
|
|
|
20 |
|
|
|
21 |
/** SSL verification for host and peer. */
|
|
|
22 |
const SSL_HOST_AND_PEER = 2;
|
|
|
23 |
|
|
|
24 |
var $id = 0;
|
|
|
25 |
var $wwwroot = '';
|
|
|
26 |
var $ip_address = '';
|
|
|
27 |
var $name = '';
|
|
|
28 |
var $public_key = '';
|
|
|
29 |
var $public_key_expires = 0;
|
|
|
30 |
var $last_connect_time = 0;
|
|
|
31 |
var $last_log_id = 0;
|
|
|
32 |
var $force_theme = 0;
|
|
|
33 |
var $theme = '';
|
|
|
34 |
var $applicationid = 1; // Default of 1 == Moodle
|
|
|
35 |
var $keypair = array();
|
|
|
36 |
var $error = array();
|
|
|
37 |
var $bootstrapped = false; // set when the object is populated
|
|
|
38 |
|
|
|
39 |
/** @var int $sslverification The level of SSL verification to apply. */
|
|
|
40 |
public $sslverification = self::SSL_HOST_AND_PEER;
|
|
|
41 |
|
|
|
42 |
/** @var int deleted status. */
|
|
|
43 |
public $deleted;
|
|
|
44 |
|
|
|
45 |
/** @var stdClass data from mnet_application table in DB. */
|
|
|
46 |
public $application;
|
|
|
47 |
|
|
|
48 |
/**
|
|
|
49 |
* Current SSL public key
|
|
|
50 |
*
|
|
|
51 |
* MNet need to compare the remote machine's SSL Cert and the public key to warn users of any mismatch.
|
|
|
52 |
* The property is the remote machine's SSL Cert.
|
|
|
53 |
*
|
|
|
54 |
* @see admin/mnet/peers.php
|
|
|
55 |
* @var string
|
|
|
56 |
*/
|
|
|
57 |
public $currentkey;
|
|
|
58 |
|
|
|
59 |
/*
|
|
|
60 |
* Fetch information about a peer identified by wwwroot
|
|
|
61 |
* If information does not preexist in db, collect it together based on
|
|
|
62 |
* supplied information
|
|
|
63 |
*
|
|
|
64 |
* @param string $wwwroot - address of peer whose details we want
|
|
|
65 |
* @param string $pubkey - to use if we add a record to db for new peer
|
|
|
66 |
* @param int $application - table id - what kind of peer are we talking to
|
|
|
67 |
* @return bool - indication of success or failure
|
|
|
68 |
*/
|
|
|
69 |
function bootstrap($wwwroot, $pubkey, $application) {
|
|
|
70 |
global $DB;
|
|
|
71 |
|
|
|
72 |
if (substr($wwwroot, -1, 1) == '/') {
|
|
|
73 |
$wwwroot = substr($wwwroot, 0, -1);
|
|
|
74 |
}
|
|
|
75 |
|
|
|
76 |
// If a peer record already exists for this address,
|
|
|
77 |
// load that info and return
|
|
|
78 |
if ($this->set_wwwroot($wwwroot)) {
|
|
|
79 |
return true;
|
|
|
80 |
}
|
|
|
81 |
|
|
|
82 |
$hostname = mnet_get_hostname_from_uri($wwwroot);
|
|
|
83 |
// Get the IP address for that host - if this fails, it will return the hostname string
|
|
|
84 |
$ip_address = gethostbyname($hostname);
|
|
|
85 |
|
|
|
86 |
// Couldn't find the IP address?
|
|
|
87 |
if ($ip_address === $hostname && !preg_match('/^\d+\.\d+\.\d+.\d+$/',$hostname)) {
|
|
|
88 |
throw new moodle_exception('noaddressforhost', 'mnet', '', $hostname);
|
|
|
89 |
}
|
|
|
90 |
|
|
|
91 |
$this->name = $wwwroot;
|
|
|
92 |
|
|
|
93 |
// TODO: In reality, this will be prohibitively slow... need another
|
|
|
94 |
// default - maybe blank string
|
|
|
95 |
$homepage = download_file_content($wwwroot);
|
|
|
96 |
if (!empty($homepage)) {
|
|
|
97 |
$count = preg_match("@<title>(.*)</title>@siU", $homepage, $matches);
|
|
|
98 |
if ($count > 0) {
|
|
|
99 |
$this->name = $matches[1];
|
|
|
100 |
}
|
|
|
101 |
}
|
|
|
102 |
|
|
|
103 |
$this->wwwroot = $wwwroot;
|
|
|
104 |
$this->ip_address = $ip_address;
|
|
|
105 |
$this->deleted = 0;
|
|
|
106 |
|
|
|
107 |
$this->application = $DB->get_record('mnet_application', array('name'=>$application));
|
|
|
108 |
if (empty($this->application)) {
|
|
|
109 |
$this->application = $DB->get_record('mnet_application', array('name'=>'moodle'));
|
|
|
110 |
}
|
|
|
111 |
|
|
|
112 |
$this->applicationid = $this->application->id;
|
|
|
113 |
|
|
|
114 |
if(empty($pubkey)) {
|
|
|
115 |
$this->public_key = clean_param(mnet_get_public_key($this->wwwroot, $this->application), PARAM_PEM);
|
|
|
116 |
} else {
|
|
|
117 |
$this->public_key = clean_param($pubkey, PARAM_PEM);
|
|
|
118 |
}
|
|
|
119 |
$this->public_key_expires = $this->check_common_name($this->public_key);
|
|
|
120 |
$this->last_connect_time = 0;
|
|
|
121 |
$this->last_log_id = 0;
|
|
|
122 |
if ($this->public_key_expires == false) {
|
|
|
123 |
$this->public_key == '';
|
|
|
124 |
return false;
|
|
|
125 |
}
|
|
|
126 |
$this->bootstrapped = true;
|
|
|
127 |
return true;
|
|
|
128 |
}
|
|
|
129 |
|
|
|
130 |
/*
|
|
|
131 |
* Delete mnet peer
|
|
|
132 |
* the peer is marked as deleted in the database
|
|
|
133 |
* we delete current sessions.
|
|
|
134 |
* @return bool - success
|
|
|
135 |
*/
|
|
|
136 |
function delete() {
|
|
|
137 |
global $DB;
|
|
|
138 |
|
|
|
139 |
if ($this->deleted) {
|
|
|
140 |
return true;
|
|
|
141 |
}
|
|
|
142 |
|
|
|
143 |
$this->delete_all_sessions();
|
|
|
144 |
|
|
|
145 |
$this->deleted = 1;
|
|
|
146 |
return $this->commit();
|
|
|
147 |
}
|
|
|
148 |
|
|
|
149 |
function count_live_sessions() {
|
|
|
150 |
global $DB;
|
|
|
151 |
$obj = $this->delete_expired_sessions();
|
|
|
152 |
return $DB->count_records('mnet_session', array('mnethostid'=>$this->id));
|
|
|
153 |
}
|
|
|
154 |
|
|
|
155 |
function delete_expired_sessions() {
|
|
|
156 |
global $DB;
|
|
|
157 |
$now = time();
|
|
|
158 |
return $DB->delete_records_select('mnet_session', " mnethostid = ? AND expires < ? ", array($this->id, $now));
|
|
|
159 |
}
|
|
|
160 |
|
|
|
161 |
function delete_all_sessions() {
|
|
|
162 |
global $CFG, $DB;
|
|
|
163 |
// TODO: Expires each PHP session individually
|
|
|
164 |
$sessions = $DB->get_records('mnet_session', array('mnethostid'=>$this->id));
|
|
|
165 |
|
|
|
166 |
if (count($sessions) > 0 && file_exists($CFG->dirroot.'/auth/mnet/auth.php')) {
|
|
|
167 |
require_once($CFG->dirroot.'/auth/mnet/auth.php');
|
|
|
168 |
$auth = new auth_plugin_mnet();
|
|
|
169 |
$auth->end_local_sessions($sessions);
|
|
|
170 |
}
|
|
|
171 |
|
|
|
172 |
$deletereturn = $DB->delete_records('mnet_session', array('mnethostid'=>$this->id));
|
|
|
173 |
return true;
|
|
|
174 |
}
|
|
|
175 |
|
|
|
176 |
function check_common_name($key) {
|
|
|
177 |
$credentials = $this->check_credentials($key);
|
|
|
178 |
return $credentials['validTo_time_t'];
|
|
|
179 |
}
|
|
|
180 |
|
|
|
181 |
function check_credentials($key) {
|
|
|
182 |
$credentials = openssl_x509_parse($key);
|
|
|
183 |
if ($credentials == false) {
|
|
|
184 |
$this->error[] = array('code' => 3, 'text' => get_string("nonmatchingcert", 'mnet', array('subject' => '','host' => '')));
|
|
|
185 |
return false;
|
|
|
186 |
} elseif (array_key_exists('subjectAltName', $credentials['subject']) && $credentials['subject']['subjectAltName'] != $this->wwwroot) {
|
|
|
187 |
$a['subject'] = $credentials['subject']['subjectAltName'];
|
|
|
188 |
$a['host'] = $this->wwwroot;
|
|
|
189 |
$this->error[] = array('code' => 5, 'text' => get_string("nonmatchingcert", 'mnet', $a));
|
|
|
190 |
return false;
|
|
|
191 |
} else if ($credentials['subject']['CN'] !== substr($this->wwwroot, 0, 64)) {
|
|
|
192 |
$a['subject'] = $credentials['subject']['CN'];
|
|
|
193 |
$a['host'] = $this->wwwroot;
|
|
|
194 |
$this->error[] = array('code' => 4, 'text' => get_string("nonmatchingcert", 'mnet', $a));
|
|
|
195 |
return false;
|
|
|
196 |
} else {
|
|
|
197 |
if (array_key_exists('subjectAltName', $credentials['subject'])) {
|
|
|
198 |
$credentials['wwwroot'] = $credentials['subject']['subjectAltName'];
|
|
|
199 |
} else {
|
|
|
200 |
$credentials['wwwroot'] = $credentials['subject']['CN'];
|
|
|
201 |
}
|
|
|
202 |
return $credentials;
|
|
|
203 |
}
|
|
|
204 |
}
|
|
|
205 |
|
|
|
206 |
function commit() {
|
|
|
207 |
global $DB;
|
|
|
208 |
$obj = new stdClass();
|
|
|
209 |
|
|
|
210 |
$obj->wwwroot = $this->wwwroot;
|
|
|
211 |
$obj->ip_address = $this->ip_address;
|
|
|
212 |
$obj->name = $this->name;
|
|
|
213 |
$obj->public_key = $this->public_key;
|
|
|
214 |
$obj->public_key_expires = $this->public_key_expires;
|
|
|
215 |
$obj->deleted = $this->deleted;
|
|
|
216 |
$obj->last_connect_time = $this->last_connect_time;
|
|
|
217 |
$obj->last_log_id = $this->last_log_id;
|
|
|
218 |
$obj->force_theme = $this->force_theme;
|
|
|
219 |
$obj->theme = $this->theme;
|
|
|
220 |
$obj->applicationid = $this->applicationid;
|
|
|
221 |
$obj->sslverification = $this->sslverification;
|
|
|
222 |
|
|
|
223 |
if (isset($this->id) && $this->id > 0) {
|
|
|
224 |
$obj->id = $this->id;
|
|
|
225 |
return $DB->update_record('mnet_host', $obj);
|
|
|
226 |
} else {
|
|
|
227 |
$this->id = $DB->insert_record('mnet_host', $obj);
|
|
|
228 |
return $this->id > 0;
|
|
|
229 |
}
|
|
|
230 |
}
|
|
|
231 |
|
|
|
232 |
function touch() {
|
|
|
233 |
$this->last_connect_time = time();
|
|
|
234 |
$this->commit();
|
|
|
235 |
}
|
|
|
236 |
|
|
|
237 |
function set_name($newname) {
|
|
|
238 |
if (is_string($newname) && strlen($newname <= 80)) {
|
|
|
239 |
$this->name = $newname;
|
|
|
240 |
return true;
|
|
|
241 |
}
|
|
|
242 |
return false;
|
|
|
243 |
}
|
|
|
244 |
|
|
|
245 |
function set_applicationid($applicationid) {
|
|
|
246 |
if (is_numeric($applicationid) && $applicationid == intval($applicationid)) {
|
|
|
247 |
$this->applicationid = $applicationid;
|
|
|
248 |
return true;
|
|
|
249 |
}
|
|
|
250 |
return false;
|
|
|
251 |
}
|
|
|
252 |
|
|
|
253 |
/**
|
|
|
254 |
* Load information from db about an mnet peer into this object's properties
|
|
|
255 |
*
|
|
|
256 |
* @param string $wwwroot - address of peer whose details we want to load
|
|
|
257 |
* @return bool - indication of success or failure
|
|
|
258 |
*/
|
|
|
259 |
function set_wwwroot($wwwroot) {
|
|
|
260 |
global $CFG, $DB;
|
|
|
261 |
|
|
|
262 |
$hostinfo = $DB->get_record('mnet_host', array('wwwroot'=>$wwwroot));
|
|
|
263 |
|
|
|
264 |
if ($hostinfo != false) {
|
|
|
265 |
$this->populate($hostinfo);
|
|
|
266 |
return true;
|
|
|
267 |
}
|
|
|
268 |
return false;
|
|
|
269 |
}
|
|
|
270 |
|
|
|
271 |
function set_id($id) {
|
|
|
272 |
global $CFG, $DB;
|
|
|
273 |
|
|
|
274 |
if (clean_param($id, PARAM_INT) != $id) {
|
|
|
275 |
$this->error[] = ['code' => 1, 'text' => 'Your id ('.$id.') is not legal'];
|
|
|
276 |
return false;
|
|
|
277 |
}
|
|
|
278 |
|
|
|
279 |
$sql = "
|
|
|
280 |
SELECT
|
|
|
281 |
h.*
|
|
|
282 |
FROM
|
|
|
283 |
{mnet_host} h
|
|
|
284 |
WHERE
|
|
|
285 |
h.id = ?";
|
|
|
286 |
|
|
|
287 |
if ($hostinfo = $DB->get_record_sql($sql, array($id))) {
|
|
|
288 |
$this->populate($hostinfo);
|
|
|
289 |
return true;
|
|
|
290 |
}
|
|
|
291 |
return false;
|
|
|
292 |
}
|
|
|
293 |
|
|
|
294 |
/**
|
|
|
295 |
* Several methods can be used to get an 'mnet_host' record. They all then
|
|
|
296 |
* send it to this private method to populate this object's attributes.
|
|
|
297 |
*
|
|
|
298 |
* @param object $hostinfo A database record from the mnet_host table
|
|
|
299 |
* @return void
|
|
|
300 |
*/
|
|
|
301 |
function populate($hostinfo) {
|
|
|
302 |
global $DB;
|
|
|
303 |
$this->id = $hostinfo->id;
|
|
|
304 |
$this->wwwroot = $hostinfo->wwwroot;
|
|
|
305 |
$this->ip_address = $hostinfo->ip_address;
|
|
|
306 |
$this->name = $hostinfo->name;
|
|
|
307 |
$this->deleted = $hostinfo->deleted;
|
|
|
308 |
$this->public_key = $hostinfo->public_key;
|
|
|
309 |
$this->public_key_expires = $hostinfo->public_key_expires;
|
|
|
310 |
$this->last_connect_time = $hostinfo->last_connect_time;
|
|
|
311 |
$this->last_log_id = $hostinfo->last_log_id;
|
|
|
312 |
$this->force_theme = $hostinfo->force_theme;
|
|
|
313 |
$this->theme = $hostinfo->theme;
|
|
|
314 |
$this->applicationid = $hostinfo->applicationid;
|
|
|
315 |
$this->sslverification = $hostinfo->sslverification;
|
|
|
316 |
$this->application = $DB->get_record('mnet_application', array('id'=>$this->applicationid));
|
|
|
317 |
$this->bootstrapped = true;
|
|
|
318 |
}
|
|
|
319 |
|
|
|
320 |
/**
|
|
|
321 |
* Get public key.
|
|
|
322 |
*
|
|
|
323 |
* @deprecated since Moodle 4.3
|
|
|
324 |
* @todo MDL-78304 Final deprecation.
|
|
|
325 |
*/
|
|
|
326 |
function get_public_key() {
|
|
|
327 |
debugging('Function get_public_key() is deprecated.', DEBUG_DEVELOPER);
|
|
|
328 |
if (isset($this->public_key_ref)) return $this->public_key_ref;
|
|
|
329 |
$this->public_key_ref = openssl_pkey_get_public($this->public_key);
|
|
|
330 |
return $this->public_key_ref;
|
|
|
331 |
}
|
|
|
332 |
}
|