1<?php
2/**
3 * Info about the local environment, wrt RPC
4 *
5 * This should really be a singleton. A PHP5 Todo I guess.
6 */
7
8class mnet_environment {
9
10    var $id                 = 0;
11    var $wwwroot            = '';
12    var $ip_address         = '';
13    var $public_key         = '';
14    var $public_key_expires = 0;
15    var $last_connect_time  = 0;
16    var $last_log_id        = 0;
17    var $keypair            = array();
18    var $deleted            = 0;
19
20    function init() {
21        global $CFG, $DB;
22
23        // Bootstrap the object data on first load.
24        if (!$hostobject = $DB->get_record('mnet_host', array('id'=>$CFG->mnet_localhost_id))) {
25            return false;
26        }
27        $temparr = get_object_vars($hostobject);
28        foreach($temparr as $key => $value) {
29            $this->$key = $value;
30        }
31        unset($hostobject, $temparr);
32
33        // Unless this is an install/upgrade, generate the SSL keys.
34        if (empty($this->public_key)) {
35            $this->get_keypair();
36        }
37
38        // We need to set up a record that represents 'all hosts'. Any rights
39        // granted to this host will be conferred on all hosts.
40        if (empty($CFG->mnet_all_hosts_id) ) {
41            $hostobject                     = new stdClass();
42            $hostobject->wwwroot            = '';
43            $hostobject->ip_address         = '';
44            $hostobject->public_key         = '';
45            $hostobject->public_key_expires = 0;
46            $hostobject->last_connect_time  = 0;
47            $hostobject->last_log_id        = 0;
48            $hostobject->deleted            = 0;
49            $hostobject->name               = 'All Hosts';
50
51            $hostobject->id = $DB->insert_record('mnet_host',$hostobject);
52            set_config('mnet_all_hosts_id', $hostobject->id);
53            $CFG->mnet_all_hosts_id = $hostobject->id;
54            unset($hostobject);
55        }
56    }
57
58    function get_keypair() {
59        global $DB, $CFG;
60
61        // We don't generate keys on install/upgrade because we want the USER
62        // record to have an email address, city and country already.
63        if (during_initial_install()) return true;
64        if ($CFG->mnet_dispatcher_mode == 'off') return true;
65        if (!extension_loaded("openssl")) return true;
66        if (!empty($this->keypair)) return true;
67
68        $this->keypair = array();
69        $keypair = get_config('mnet', 'openssl');
70
71        if (!empty($keypair)) {
72            // Explode/Implode is faster than Unserialize/Serialize
73            list($this->keypair['certificate'], $this->keypair['keypair_PEM']) = explode('@@@@@@@@', $keypair);
74        }
75
76        if ($this->public_key_expires > time()) {
77            $this->keypair['privatekey'] = openssl_pkey_get_private($this->keypair['keypair_PEM']);
78            $this->keypair['publickey']  = openssl_pkey_get_public($this->keypair['certificate']);
79        } else {
80            // Key generation/rotation
81
82            // 1. Archive the current key (if there is one).
83            $result = get_config('mnet', 'openssl_history');
84            if(empty($result)) {
85                set_config('openssl_history', serialize(array()), 'mnet');
86                $openssl_history = array();
87            } else {
88                $openssl_history = unserialize($result);
89            }
90
91            if(count($this->keypair)) {
92                $this->keypair['expires'] = $this->public_key_expires;
93                array_unshift($openssl_history, $this->keypair);
94            }
95
96            // 2. How many old keys do we want to keep? Use array_slice to get
97            // rid of any we don't want
98            $openssl_generations = get_config('mnet', 'openssl_generations');
99            if(empty($openssl_generations)) {
100                set_config('openssl_generations', 3, 'mnet');
101                $openssl_generations = 3;
102            }
103
104            if(count($openssl_history) > $openssl_generations) {
105                $openssl_history = array_slice($openssl_history, 0, $openssl_generations);
106            }
107
108            set_config('openssl_history', serialize($openssl_history), 'mnet');
109
110            // 3. Generate fresh keys
111            $this->replace_keys();
112        }
113        return true;
114    }
115
116    function replace_keys() {
117        global $DB, $CFG;
118
119        $keypair = mnet_generate_keypair();
120        if (empty($keypair)) {
121            error_log('Can not generate keypair, sorry');
122            return;
123        }
124
125        $this->keypair = array();
126        $this->keypair            = $keypair;
127        $this->public_key         = $this->keypair['certificate'];
128        $details                  = openssl_x509_parse($this->public_key);
129        $this->public_key_expires = $details['validTo_time_t'];
130
131        $this->wwwroot            = $CFG->wwwroot;
132        if (empty($_SERVER['SERVER_ADDR'])) {
133            // SERVER_ADDR is only returned by Apache-like webservers
134            $my_hostname = mnet_get_hostname_from_uri($CFG->wwwroot);
135            $my_ip       = gethostbyname($my_hostname);  // Returns unmodified hostname on failure. DOH!
136            if ($my_ip == $my_hostname) {
137                $this->ip_address = 'UNKNOWN';
138            } else {
139                $this->ip_address = $my_ip;
140            }
141        } else {
142            $this->ip_address = $_SERVER['SERVER_ADDR'];
143        }
144
145        set_config('openssl', implode('@@@@@@@@', $this->keypair), 'mnet');
146
147        $DB->update_record('mnet_host', $this);
148        error_log('New public key has been generated. It expires ' . date('Y/m/d h:i:s', $this->public_key_expires));
149    }
150
151    function get_private_key() {
152        if (empty($this->keypair)) $this->get_keypair();
153        if (isset($this->keypair['privatekey'])) return $this->keypair['privatekey'];
154        $this->keypair['privatekey'] = openssl_pkey_get_private($this->keypair['keypair_PEM']);
155        return $this->keypair['privatekey'];
156    }
157
158    function get_public_key() {
159        if (!isset($this->keypair)) $this->get_keypair();
160        if (isset($this->keypair['publickey'])) return $this->keypair['publickey'];
161        $this->keypair['publickey'] = openssl_pkey_get_public($this->keypair['certificate']);
162        return $this->keypair['publickey'];
163    }
164}
165