config.lib.inc 12.5 KB
Newer Older
Ad Schellevis's avatar
Ad Schellevis committed
1 2
<?php

3
/*
4
	Copyright (C) 2015 Franco Fichtner <franco@opnsense.org>
Ad Schellevis's avatar
Ad Schellevis committed
5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31
	Ported from config.inc by Erik Kristensen
	Copyright (C) 2004-2010 Scott Ullrich
	Copyright (C) 2003-2004 Manuel Kasper <mk@neon1.net>.
	All rights reserved.

	Redistribution and use in source and binary forms, with or without
	modification, are permitted provided that the following conditions are met:

	1. Redistributions of source code must retain the above copyright notice,
	   this list of conditions and the following disclaimer.

	2. Redistributions in binary form must reproduce the above copyright
	   notice, this list of conditions and the following disclaimer in the
	   documentation and/or other materials provided with the distribution.

	THIS SOFTWARE IS PROVIDED ``AS IS'' AND ANY EXPRESS OR IMPLIED WARRANTIES,
	INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY
	AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE
	AUTHOR BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY,
	OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF
	SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS
	INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN
	CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE)
	ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE
	POSSIBILITY OF SUCH DAMAGE.
*/

32 33 34
require_once("globals.inc");
require_once("xmlparse.inc");
require_once("crypt.inc");
35
require_once("util.inc");
36
require_once("script/load_phalcon.php");
Ad Schellevis's avatar
Ad Schellevis committed
37 38 39

/****f* config/parse_config
 * NAME
40
 *   parse_config - Read in config.xml if needed and return $config array
Ad Schellevis's avatar
Ad Schellevis committed
41 42 43
 * RESULT
 *   $config      - array containing all configuration variables
 ******/
44
function parse_config()
45
{
46
	$cnf = OPNsense\Core\Config::getInstance();
47 48 49 50 51 52
	if (!$cnf->isValid()) {
		// there was an issue with loading the config, try to restore the last backup
		$backups = $cnf->getBackups();
		if (count($backups) > 0) {
			// load last backup
			log_error(gettext("No (valid) config.xml found, attempting last known config restore."));
53
			file_notice("config.xml", gettext("No (valid) config.xml found, attempting last known config restore."), "OPNsenseConfigurator", "");
54
			$cnf->restoreBackup($backups[0]);
Ad Schellevis's avatar
Ad Schellevis committed
55
		} else {
56
			// we don't have backups, try to load the default
57
			log_error(gettext('No config.xml found, attempting to restore factory config.'));
58
			$cnf->restoreBackup('/usr/local/etc/config.xml');
Ad Schellevis's avatar
Ad Schellevis committed
59 60 61
		}
	}

62
	// return config data as array, use old "listags" construction to mark certain elements as array (even if they're not recurring)
63 64 65 66
	$config=$cnf->toArray(listtags());

	/* make alias table (for faster lookups) */
	alias_make_table($config);
67

68
	return $config;
69
}
Ad Schellevis's avatar
Ad Schellevis committed
70 71 72 73 74 75 76 77 78 79 80 81 82 83 84 85 86 87 88 89 90 91 92 93

/****f* config/convert_config
 * NAME
 *   convert_config - Attempt to update config.xml.
 * DESCRIPTION
 *   convert_config() reads the current global configuration
 *   and attempts to convert it to conform to the latest
 *   config.xml version. This allows major formatting changes
 *   to be made with a minimum of breakage.
 * RESULT
 *   null
 ******/
/* convert configuration, if necessary */
function convert_config() {
	global $config, $g;
	$now = date("H:i:s");
	log_error(sprintf(gettext("Start Configuration upgrade at %s, set execution timeout to 15 minutes"), $now));
	//ini_set("max_execution_time", "900");

	/* special case upgrades */
	/* fix every minute crontab bogons entry */
	if (is_array($config['cron'])) {
		$cron_item_count = count($config['cron']['item']);
		for($x=0; $x<$cron_item_count; $x++) {
94
			if(stristr($config['cron']['item'][$x]['command'], 'rc.update_bogons')) {
Ad Schellevis's avatar
Ad Schellevis committed
95 96 97 98
				if($config['cron']['item'][$x]['hour'] == "*" ) {
				$config['cron']['item'][$x]['hour'] = "3";
					write_config(gettext("Updated bogon update frequency to 3am"));
					log_error(gettext("Updated bogon update frequency to 3am"));
99
				}
Ad Schellevis's avatar
Ad Schellevis committed
100 101 102 103 104 105 106 107
			}
		}
	}
	if ($config['version'] == $g['latest_config'])
		return;		/* already at latest version */

	// Save off config version
	$prev_version = $config['version'];
108

Ad Schellevis's avatar
Ad Schellevis committed
109 110
	include_once('auth.inc');
	include_once('upgrade_config.inc');
111

Ad Schellevis's avatar
Ad Schellevis committed
112 113 114 115 116
	/* Loop and run upgrade_VER_to_VER() until we're at current version */
	while ($config['version'] < $g['latest_config']) {
		$cur = $config['version'] * 10;
		$next = $cur + 1;
		$migration_function = sprintf('upgrade_%03d_to_%03d', $cur, $next);
117
		if (function_exists($migration_function)) {
Ad Schellevis's avatar
Ad Schellevis committed
118
			$migration_function();
119
		}
Ad Schellevis's avatar
Ad Schellevis committed
120 121 122 123 124 125 126 127 128 129 130 131 132 133 134 135 136 137 138 139 140 141 142 143
		$config['version'] = sprintf('%.1f', $next / 10);
	}

	$now = date("H:i:s");
	log_error(sprintf(gettext("Ended Configuration upgrade at %s"), $now));

	if ($prev_version != $config['version'])
		write_config(sprintf(gettext('Upgraded config version level from %1$s to %2$s'), $prev_version, $config['version']));
}


/****f* config/write_config
 * NAME
 *   write_config - Backup and write the firewall configuration.
 * DESCRIPTION
 *   write_config() handles backing up the current configuration,
 *   applying changes, and regenerating the configuration cache.
 * INPUTS
 *   $desc	- string containing the a description of configuration changes
 *   $backup	- boolean: do not back up current configuration if false.
 * RESULT
 *   null
 ******/
/* save the system configuration */
144 145
function write_config($desc = 'Unknown', $backup = true)
{
Ad Schellevis's avatar
Ad Schellevis committed
146 147 148
	global $config, $g;

	if (!empty($_SERVER['REMOTE_ADDR'])) {
149 150
		if (session_status() == PHP_SESSION_NONE) {
			session_start();
151 152
		}
		if (!empty($_SESSION['Username']) && ($_SESSION['Username'] != 'root')) {
Ad Schellevis's avatar
Ad Schellevis committed
153 154
			$user = getUserEntry($_SESSION['Username']);
			if (is_array($user) && userHasPrivilege($user, "user-config-readonly")) {
155
				session_write_close();
156 157
				// okay, it's not very nice to check permissions here, but let's make it explicit while we do...
				log_error(gettext("WARNING: User")." ".$_SESSION['Username']." ".gettext("may not write config (user-config-readonly set)"));
Ad Schellevis's avatar
Ad Schellevis committed
158 159 160 161 162
				return false;
			}
		}
	}

163
	if (!isset($argc)) {
164
		session_write_close();
165
	}
Ad Schellevis's avatar
Ad Schellevis committed
166

Ad Schellevis's avatar
Ad Schellevis committed
167 168 169 170 171 172
	if (!isset($config['version'])) {
		// Examine config.xml, if for some strange reason the content is unexpected : exit directly.
		log_error(gettext("WARNING: Corrupt config!"));
		return -1;
	}

Ad Schellevis's avatar
Ad Schellevis committed
173 174 175 176 177 178 179
	$cnf = OPNsense\Core\Config::getInstance();
	$cnf->fromArray($config);
	$revision_info = make_config_revision_entry($desc);
	try {
		$cnf->save($revision_info, $backup);
	} catch (OPNsense\Core\ConfigException $e) {
		// write failure
Isaac (.ike) Levy's avatar
Isaac (.ike) Levy committed
180
		log_error(gettext("WARNING: Config contents could not be saved. Could not open file!"));
Ad Schellevis's avatar
Ad Schellevis committed
181 182
		file_notice('config.xml', sprintf("%s\n", gettext('Unable to open /conf/config.xml for writing in write_config()')));
		return -1;
183
	}
Ad Schellevis's avatar
Ad Schellevis committed
184

Ad Schellevis's avatar
Ad Schellevis committed
185
	/* sync carp entries to other firewalls */
186 187 188
	if ( isset($config['hasync']['synchronizetoip']) && trim($config['hasync']['synchronizetoip']) != "") {
	    configd_run('filter sync load');
	}
Ad Schellevis's avatar
Ad Schellevis committed
189

Ad Schellevis's avatar
Ad Schellevis committed
190 191 192
	/* cleanup backups */
	cleanup_backups();

193 194
	// on succesfull save, serialize config back to global.
	$config = $cnf->toArray(listtags());
Ad Schellevis's avatar
Ad Schellevis committed
195
	return $config;
Ad Schellevis's avatar
Ad Schellevis committed
196 197 198 199 200 201 202 203 204

}

/****f* config/reset_factory_defaults
 * NAME
 *   reset_factory_defaults - Reset the system to its default configuration.
 * RESULT
 *   integer	- indicates completion
 ******/
205
function reset_factory_defaults($sync = true)
206
{
207
	mwexec('/bin/rm -r /conf/*');
Ad Schellevis's avatar
Ad Schellevis committed
208 209
	disable_security_checks();
	setup_serial_port();
210 211

	/* as we go through a special case directly reboot */
212 213 214 215 216 217
	$shutdown_cmd = '/sbin/shutdown -or now';
	if ($sync) {
		mwexec($shutdown_cmd);
	} else {
		mwexec_bg($shutdown_cmd);
	}
Ad Schellevis's avatar
Ad Schellevis committed
218 219
}

220 221
function config_restore($conffile)
{
Ad Schellevis's avatar
Ad Schellevis committed
222 223 224 225 226
	global $config, $g;

	if (!file_exists($conffile))
		return 1;

Ad Schellevis's avatar
Ad Schellevis committed
227 228
	$cnf = OPNsense\Core\Config::getInstance();
	$cnf->backup();
229
	$cnf->restoreBackup($conffile);
Ad Schellevis's avatar
Ad Schellevis committed
230 231 232

	disable_security_checks();

233
	$config = parse_config();
Ad Schellevis's avatar
Ad Schellevis committed
234 235 236 237 238 239 240 241 242 243 244 245 246 247

	write_config(gettext("Reverted to") . " " . array_pop(explode("/", $conffile)) . ".", false);

	return 0;
}

/*
 * Disable security checks for DNS rebind and HTTP referrer until next time
 * they pass (or reboot), to aid in preventing accidental lockout when
 * restoring settings like hostname, domain, IP addresses, and settings
 * related to the DNS rebind and HTTP referrer checks.
 * Intended for use when restoring a configuration or directly
 * modifying config.xml without an unconditional reboot.
 */
248 249 250
function disable_security_checks()
{
	touch('/tmp/disable_security_checks');
Ad Schellevis's avatar
Ad Schellevis committed
251 252 253
}

/* Restores security checks.  Should be called after all succeed. */
254 255 256
function restore_security_checks()
{
	@unlink('/tmp/disable_security_checks');
Ad Schellevis's avatar
Ad Schellevis committed
257 258 259
}

/* Returns status of security check temporary disable. */
260 261 262
function security_checks_disabled()
{
	return file_exists('/tmp/disable_security_checks');
Ad Schellevis's avatar
Ad Schellevis committed
263 264
}

Ad Schellevis's avatar
Ad Schellevis committed
265 266 267 268
/**
 * remove old backups
 */
function cleanup_backups() {
269
	global $g, $config;
Ad Schellevis's avatar
Ad Schellevis committed
270 271
	$i = false;

272 273 274 275 276 277
	if (isset($config['system']['backupcount']) && is_numeric($config['system']['backupcount']) && ($config['system']['backupcount'] >= 0)) {
		$revisions = intval($config['system']['backupcount']);
	} else {
		$revisions = 30;
	}

Ad Schellevis's avatar
Ad Schellevis committed
278
	$cnf = OPNsense\Core\Config::getInstance();
279

Ad Schellevis's avatar
Ad Schellevis committed
280 281 282 283
	$cnt=1;
	foreach ($cnf->getBackups() as $filename) {
		if ($cnt > $revisions ) {
			unlink($filename);
Ad Schellevis's avatar
Ad Schellevis committed
284
		}
Ad Schellevis's avatar
Ad Schellevis committed
285
		++$cnt ;
Ad Schellevis's avatar
Ad Schellevis committed
286 287 288 289 290 291 292 293 294 295 296 297 298 299 300 301 302 303 304 305 306 307 308
	}
}


function set_device_perms() {
	$devices = array(
		'pf'	=> array(	'user'	=> 'root',
					'group'	=> 'proxy',
					'mode'	=> 0660),
		);

	foreach ($devices as $name => $attr) {
		$path = "/dev/$name";
		if (file_exists($path)) {
			chown($path, $attr['user']);
			chgrp($path, $attr['group']);
			chmod($path, $attr['mode']);
		}
	}
}


function make_config_revision_entry($desc = null, $override_user = null) {
309 310 311 312 313 314 315 316 317 318 319 320 321
	if (empty($override_user)) {
		if (empty($_SESSION["Username"])) {
			$username = getenv("USER");
			if (empty($conuser) || $conuser == "root")
				$username = "(system)";
		} else {
			$username = $_SESSION["Username"];
		}
		if (!empty($_SERVER['REMOTE_ADDR'])) {
			$username .= '@' . $_SERVER['REMOTE_ADDR'];
		}
	}
	else {
Ad Schellevis's avatar
Ad Schellevis committed
322
		$username = $override_user;
323
	}
Ad Schellevis's avatar
Ad Schellevis committed
324 325 326 327 328 329 330 331 332 333 334 335 336 337 338

	$revision = array();

	if (time() > mktime(0, 0, 0, 9, 1, 2004))       /* make sure the clock settings are plausible */
		$revision['time'] = time();

	/* Log the running script so it's not entirely unlogged what changed */
	if ($desc == "Unknown")
		$desc = sprintf(gettext("%s made unknown change"), $_SERVER['SCRIPT_NAME']);
	if (!empty($desc))
		$revision['description'] = "{$username}: " . $desc;
	$revision['username'] = $username;
	return $revision;
}

339
/**
340
 * backup config to google drive and return current file list (/ info)
341 342 343 344 345 346 347 348
 *
 */
function backup_to_google_drive() {
	$client = new Google\API\Drive() ;
        $cnf = OPNsense\Core\Config::getInstance();
        if ($cnf->isValid()) {
		$config = $cnf->object() ;
		if (  isset($config->system->remotebackup) && isset($config->system->remotebackup->GDriveEnabled) && $config->system->remotebackup->GDriveEnabled == "on" ){
349
			$client->login($config->system->remotebackup->GDriveEmail->__toString(), $config->system->remotebackup->GDriveP12key->__toString() );
350
			// backup source data to local strings (plain/encrypted)
351
			$confdata = file_get_contents('/conf/config.xml') ;
352
			$confdata_enc = encrypt_data($confdata, $config->system->remotebackup->GDrivePassword->__toString()) ;
353 354 355
			tagfile_reformat($confdata_enc, $confdata_enc, "config.xml");


356
			// read filelist (config-*.xml) and cleanup old files
357
			$files = $client->listFiles($config->system->remotebackup->GDriveFolderID->__toString());
358 359 360 361 362 363 364
			$configfiles  = array();
			foreach ($files as $file) {
			    if (fnmatch("config-*.xml", $file['title'])) {
			        $configfiles[$file['title']] = $file;
			    }
			}
			krsort($configfiles);
365

366
			if (isset($config->system->remotebackup->GDriveBackupCount) && is_numeric($config->system->remotebackup->GDriveBackupCount->__toString())) {
367
				$fcount = 0;
368
				foreach ($configfiles as $filename => $file) {
369 370
					if ($fcount > $config->system->remotebackup->GDriveBackupCount->__toString()) {
						log_error("remove " . $filename . " from Google Drive" );
371 372 373 374
						$client->delete($file);
					}
					$fcount++ ;
				}
375
			}
376

377 378
			// backup new file if changed (or if first in backup)
			$target_filename = "config-".time().".xml";
379
			if (count($configfiles) > 1) {
380
				// compare last backup with current, only save new
381 382
				$bck_data_enc = $client->download($configfiles[array_keys($configfiles)[0]]);
				$bck_data = decrypt_data($bck_data_enc, $config->system->remotebackup->GDrivePassword->__toString());
383 384
				if ($bck_data == $confdata) {
					$target_filename = null;
385 386
				}
			}
387 388 389
			if (!is_null($target_filename)) {
				log_error("backup configuration as " . $target_filename);
				$configfiles[$target_filename] = $client->upload($config->system->remotebackup->GDriveFolderID->__toString(),$target_filename, $confdata_enc );
390 391
			}

392
			// return filelist
393
			return $configfiles;
394 395 396

		}
	}
397

398
	// not configured / issue, return empty list
399
	return array();
400
}