Differences Between: [Versions 310 and 402] [Versions 311 and 402] [Versions 39 and 402] [Versions 400 and 402]
1 <?php 2 3 // This file is part of Moodle - http://moodle.org/ 4 // 5 // Moodle is free software: you can redistribute it and/or modify 6 // it under the terms of the GNU General Public License as published by 7 // the Free Software Foundation, either version 3 of the License, or 8 // (at your option) any later version. 9 // 10 // Moodle is distributed in the hope that it will be useful, 11 // but WITHOUT ANY WARRANTY; without even the implied warranty of 12 // MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the 13 // GNU General Public License for more details. 14 // 15 // You should have received a copy of the GNU General Public License 16 // along with Moodle. If not, see <http://www.gnu.org/licenses/>. 17 18 /** 19 * Defines various element classes used in specific areas 20 * 21 * @package core_backup 22 * @subpackage moodle2 23 * @category backup 24 * @copyright 2010 onwards Eloy Lafuente (stronk7) {@link http://stronk7.com} 25 * @license http://www.gnu.org/copyleft/gpl.html GNU GPL v3 or later 26 */ 27 28 defined('MOODLE_INTERNAL') || die(); 29 30 /** 31 * Implementation of backup_final_element that provides one interceptor for anonymization of data 32 * 33 * This class overwrites the standard set_value() method, in order to get (by name) 34 * functions from backup_anonymizer_helper executed, producing anonymization of information 35 * to happen in a clean way 36 * 37 * TODO: Finish phpdocs 38 */ 39 class anonymizer_final_element extends backup_final_element { 40 41 public function set_value($value) { 42 // Get parent name 43 $pname = $this->get_parent()->get_name(); 44 // Get my name 45 $myname = $this->get_name(); 46 // Define class and function name 47 $classname = 'backup_anonymizer_helper'; 48 $methodname= 'process_' . $pname . '_' . $myname; 49 // Invoke the interception method 50 $result = call_user_func(array($classname, $methodname), $value); 51 // Finally set it 52 parent::set_value($result); 53 } 54 } 55 56 /** 57 * Implementation of backup_final_element that provides special handling of mnethosturl 58 * 59 * This class overwrites the standard set_value() method, in order to decide, 60 * based on various config options, what to do with the field. 61 * 62 * TODO: Finish phpdocs 63 */ 64 class mnethosturl_final_element extends backup_final_element { 65 66 public function set_value($value) { 67 global $CFG; 68 69 $localhostwwwroot = backup_plan_dbops::get_mnet_localhost_wwwroot(); 70 71 // If user wwwroot matches mnet local host one or if 72 // there isn't associated wwwroot, skip sending it to file 73 if ($localhostwwwroot == $value || empty($value)) { 74 // Do nothing 75 } else { 76 parent::set_value($value); 77 } 78 } 79 } 80 81 /** 82 * Implementation of {@link backup_final_element} that provides base64 encoding. 83 * 84 * This final element transparently encodes with base64_encode() contents that 85 * normally are not safe for being stored in utf-8 xml files (binaries, serialized 86 * data...). 87 */ 88 class base64_encode_final_element extends backup_final_element { 89 90 /** 91 * Set the value for the final element, encoding it as utf-8/xml safe base64. 92 * 93 * @param string $value Original value coming from backup step source, usually db. 94 */ 95 public function set_value($value) { 96 // Avoid null being passed to base64_encode. 97 $value = $value ?? ''; 98 parent::set_value(base64_encode($value)); 99 } 100 } 101 102 /** 103 * Implementation of {@link backup_final_element} that provides symmetric-key AES-256 encryption of contents. 104 * 105 * This final element transparently encrypts, for secure storage and transport, any content 106 * that shouldn't be shown normally in plain text. Usually, passwords or keys that cannot use 107 * hashing algorithms, although potentially can encrypt any content. All information is encoded 108 * using base64. 109 * 110 * Features: 111 * - requires openssl extension to work. Without it contents are completely omitted. 112 * - automatically creates an appropriate default key for the site and stores it into backup_encryptkey config (bas64 encoded). 113 * - uses a different appropriate init vector for every operation, which is transmited with the encrypted contents. 114 * - all generated data is base64 encoded for safe transmission. 115 * - automatically adds "encrypted" attribute for easier detection. 116 * - implements HMAC for providing integrity. 117 * 118 * @copyright 2017 onwards Eloy Lafuente (stronk7) {@link http://stronk7.com} 119 * @license http://www.gnu.org/copyleft/gpl.html GNU GPL v3 or later 120 */ 121 class encrypted_final_element extends backup_final_element { 122 123 /** @var string cypher appropiate raw key for backups in the site. Defaults to backup_encryptkey config. */ 124 protected $key = null; 125 126 /** 127 * Constructor - instantiates a encrypted_final_element, specifying its basic info. 128 * 129 * Overridden to automatically add the 'encrypted' attribute if missing. 130 * 131 * @param string $name name of the element 132 * @param array $attributes attributes this element will handle (optional, defaults to null) 133 */ 134 public function __construct($name, $attributes = null) { 135 parent::__construct($name, $attributes); 136 if (! $this->get_attribute('encrypted')) { 137 $this->add_attributes('encrypted'); 138 } 139 } 140 141 /** 142 * Set the encryption key manually, overriding default backup_encryptkey config. 143 * 144 * @param string $key key to be used for encrypting. Required to be 256-bit key. 145 * Use a safe generation technique. See self::generate_encryption_random_key() below. 146 */ 147 protected function set_key($key) { 148 $bytes = strlen($key); // Get key length in bytes. 149 150 // Only accept keys with the expected (backup::CIPHERKEYLEN) key length. There are a number of hashing, 151 // random generators to achieve this esasily, like the one shown below to create the default 152 // site encryption key and ivs. 153 if ($bytes !== backup::CIPHERKEYLEN) { 154 $info = (object)array('expected' => backup::CIPHERKEYLEN, 'found' => $bytes); 155 throw new base_element_struct_exception('encrypted_final_element incorrect key length', $info); 156 } 157 // Everything went ok, store the key. 158 $this->key = $key; 159 } 160 161 /** 162 * Set the value of the field. 163 * 164 * This method sets the value of the element, encrypted using the specified key for it, 165 * defaulting to (and generating) backup_encryptkey config. HMAC is used for integrity. 166 * 167 * @param string $value plain-text content the will be stored encrypted and encoded. 168 */ 169 public function set_value($value) { 170 171 // No openssl available, skip this field completely. 172 if (!function_exists('openssl_encrypt')) { 173 return; 174 } 175 176 // No hmac available, skip this field completely. 177 if (!function_exists('hash_hmac')) { 178 return; 179 } 180 181 // Cypher not available, skip this field completely. 182 if (!in_array(backup::CIPHER, openssl_get_cipher_methods())) { 183 return; 184 } 185 186 // Ensure we have a good key, manual or default. 187 if (empty($this->key)) { 188 // The key has not been set manually, look for it at config (base64 encoded there). 189 $enckey = get_config('backup', 'backup_encryptkey'); 190 if ($enckey === false) { 191 // Has not been set, calculate and save an appropiate random key automatically. 192 $enckey = base64_encode(self::generate_encryption_random_key(backup::CIPHERKEYLEN)); 193 set_config('backup_encryptkey', $enckey, 'backup'); 194 } 195 $this->set_key(base64_decode($enckey)); 196 } 197 198 // Now we need an iv for this operation. 199 $iv = self::generate_encryption_random_key(openssl_cipher_iv_length(backup::CIPHER)); 200 201 // Everything is ready, let's encrypt and prepend the 1-shot iv. 202 $value = $iv . openssl_encrypt($value ?? '', backup::CIPHER, $this->key, OPENSSL_RAW_DATA, $iv); 203 204 // Calculate the hmac of the value (iv + encrypted) and prepend it. 205 $hmac = hash_hmac('sha256', $value, $this->key, true); 206 $value = $hmac . $value; 207 208 // Ready, set the encoded value. 209 parent::set_value(base64_encode($value)); 210 211 // Finally, if the field has an "encrypted" attribute, set it to true. 212 if ($att = $this->get_attribute('encrypted')) { 213 $att->set_value('true'); 214 } 215 } 216 217 /** 218 * Generate an appropiate random key to be used for encrypting backup information. 219 * 220 * Normally used as site default encryption key (backup_encryptkey config) and also 221 * for calculating the init vectors. 222 * 223 * Note that until PHP 5.6.12 openssl_random_pseudo_bytes() did NOT 224 * use a "cryptographically strong algorithm" {@link https://bugs.php.net/bug.php?id=70014} 225 * But it's beyond my crypto-knowledge when it's worth finding a *real* better alternative. 226 * 227 * @param int $bytes Number of bytes to determine the key length expected. 228 */ 229 protected static function generate_encryption_random_key($bytes) { 230 return openssl_random_pseudo_bytes($bytes); 231 } 232 } 233 234 /** 235 * Implementation of backup_nested_element that provides special handling of files 236 * 237 * This class overwrites the standard fill_values() method, so it gets intercepted 238 * for each file record being set to xml, in order to copy, at the same file, the 239 * physical file from moodle file storage to backup file storage 240 * 241 * TODO: Finish phpdocs 242 */ 243 class file_nested_element extends backup_nested_element { 244 245 protected $backupid; 246 247 public function process($processor) { 248 // Get current backupid from processor, we'll need later 249 if (is_null($this->backupid)) { 250 $this->backupid = $processor->get_var(backup::VAR_BACKUPID); 251 } 252 return parent::process($processor); 253 } 254 255 public function fill_values($values) { 256 // Fill values 257 parent::fill_values($values); 258 // Do our own tasks (copy file from moodle to backup) 259 try { 260 backup_file_manager::copy_file_moodle2backup($this->backupid, $values); 261 } catch (file_exception $e) { 262 $this->add_result(array('missing_files_in_pool' => true)); 263 264 // Build helpful log message with all information necessary to identify 265 // file location. 266 $context = context::instance_by_id($values->contextid, IGNORE_MISSING); 267 $contextname = ''; 268 if ($context) { 269 $contextname = ' \'' . $context->get_context_name() . '\''; 270 } 271 $message = 'Missing file in pool: ' . $values->filepath . $values->filename . 272 ' (context ' . $values->contextid . $contextname . ', component ' . 273 $values->component . ', filearea ' . $values->filearea . ', itemid ' . 274 $values->itemid . ') [' . $e->debuginfo . ']'; 275 $this->add_log($message, backup::LOG_WARNING); 276 } 277 } 278 } 279 280 /** 281 * Implementation of backup_optigroup_element to be used by plugins stuff. 282 * Split just for better separation and future specialisation 283 */ 284 class backup_plugin_element extends backup_optigroup_element { } 285 286 /** 287 * Implementation of backup_optigroup_element to be used by subplugins stuff. 288 * Split just for better separation and future specialisation 289 */ 290 class backup_subplugin_element extends backup_optigroup_element { }
title
Description
Body
title
Description
Body
title
Description
Body
title
Body