config.lib.inc 13.3 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 36
require_once("notices.inc");
require_once("legacy_bindings.inc");
37
require_once('upgrade_config.inc');
38
require_once("certs.inc");
39

Ad Schellevis's avatar
Ad Schellevis committed
40

41 42 43 44 45 46 47 48 49 50 51 52 53 54 55
/* make a global alias table (for faster lookups) */
function alias_make_table($config)
{
	global $aliastable;

	$aliastable = array();

	if (isset($config['aliases']['alias'])) {
		foreach ($config['aliases']['alias'] as $alias) {
			if ($alias['name'])
				$aliastable[$alias['name']] = $alias['address'];
		}
	}
}

Ad Schellevis's avatar
Ad Schellevis committed
56 57
/****f* config/parse_config
 * NAME
58
 *   parse_config - Read in config.xml if needed and return $config array
Ad Schellevis's avatar
Ad Schellevis committed
59 60 61
 * RESULT
 *   $config      - array containing all configuration variables
 ******/
62
function parse_config()
63
{
64
	$cnf = OPNsense\Core\Config::getInstance();
65 66 67 68 69
	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
70
			syslog(LOG_ERR,	sprintf('parse_config(): %s', gettext('No (valid) config.xml found, attempting last known config restore.')));
71
			file_notice("config.xml", gettext("No (valid) config.xml found, attempting last known config restore."), "OPNsenseConfigurator", "");
72
			$cnf->restoreBackup($backups[0]);
Ad Schellevis's avatar
Ad Schellevis committed
73
		} else {
74
			// we don't have backups, try to load the default
75
			syslog(LOG_ERR, sprintf('parse_config(): %s', gettext('No config.xml found, attempting to restore factory config.')));
76
			$cnf->restoreBackup('/usr/local/etc/config.xml');
Ad Schellevis's avatar
Ad Schellevis committed
77 78 79
		}
	}

80
	// return config data as array, use old "listags" construction to mark certain elements as array (even if they're not recurring)
81 82 83 84
	$config=$cnf->toArray(listtags());

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

86
	return $config;
87
}
Ad Schellevis's avatar
Ad Schellevis committed
88 89 90 91 92 93 94 95 96 97 98 99 100 101 102 103 104 105 106 107 108 109 110 111

/****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++) {
112
			if(stristr($config['cron']['item'][$x]['command'], 'rc.update_bogons')) {
Ad Schellevis's avatar
Ad Schellevis committed
113 114 115 116
				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"));
117
				}
Ad Schellevis's avatar
Ad Schellevis committed
118 119 120 121 122 123 124 125 126 127 128 129 130
			}
		}
	}
	if ($config['version'] == $g['latest_config'])
		return;		/* already at latest version */

	// Save off config version
	$prev_version = $config['version'];
	/* 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);
131
		if (function_exists($migration_function)) {
Ad Schellevis's avatar
Ad Schellevis committed
132
			$migration_function();
133
		}
Ad Schellevis's avatar
Ad Schellevis committed
134 135 136 137 138 139 140 141 142 143 144 145 146 147 148 149 150 151 152 153 154 155 156 157
		$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 */
158 159
function write_config($desc = 'Unknown', $backup = true)
{
Franco Fichtner's avatar
Franco Fichtner committed
160
	global $config;
Ad Schellevis's avatar
Ad Schellevis committed
161 162

	if (!empty($_SERVER['REMOTE_ADDR'])) {
163 164
		if (session_status() == PHP_SESSION_NONE) {
			session_start();
165 166
		}
		if (!empty($_SESSION['Username']) && ($_SESSION['Username'] != 'root')) {
Ad Schellevis's avatar
Ad Schellevis committed
167 168
			$user = getUserEntry($_SESSION['Username']);
			if (is_array($user) && userHasPrivilege($user, "user-config-readonly")) {
169
				session_write_close();
170 171
				// 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
172 173 174 175 176
				return false;
			}
		}
	}

177
	if (!isset($argc)) {
178
		session_write_close();
179
	}
Ad Schellevis's avatar
Ad Schellevis committed
180

Ad Schellevis's avatar
Ad Schellevis committed
181 182 183 184 185 186
	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
187 188 189 190 191 192 193
	$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
194
		log_error(gettext("WARNING: Config contents could not be saved. Could not open file!"));
Ad Schellevis's avatar
Ad Schellevis committed
195 196
		file_notice('config.xml', sprintf("%s\n", gettext('Unable to open /conf/config.xml for writing in write_config()')));
		return -1;
197
	}
Ad Schellevis's avatar
Ad Schellevis committed
198

Ad Schellevis's avatar
Ad Schellevis committed
199
	/* sync carp entries to other firewalls */
200 201 202
	if ( isset($config['hasync']['synchronizetoip']) && trim($config['hasync']['synchronizetoip']) != "") {
	    configd_run('filter sync load');
	}
Ad Schellevis's avatar
Ad Schellevis committed
203

Ad Schellevis's avatar
Ad Schellevis committed
204 205 206
	/* cleanup backups */
	cleanup_backups();

207 208
	// on succesfull save, serialize config back to global.
	$config = $cnf->toArray(listtags());
Ad Schellevis's avatar
Ad Schellevis committed
209
	return $config;
Ad Schellevis's avatar
Ad Schellevis committed
210 211 212 213 214 215 216

}

/****f* config/reset_factory_defaults
 * NAME
 *   reset_factory_defaults - Reset the system to its default configuration.
 ******/
217
function reset_factory_defaults($sync = true)
218
{
219
	mwexec('/bin/rm -r /conf/*');
Ad Schellevis's avatar
Ad Schellevis committed
220
	disable_security_checks();
221
	setup_serial_port(false);
222 223

	/* as we go through a special case directly reboot */
224 225 226 227 228 229
	$shutdown_cmd = '/sbin/shutdown -or now';
	if ($sync) {
		mwexec($shutdown_cmd);
	} else {
		mwexec_bg($shutdown_cmd);
	}
Ad Schellevis's avatar
Ad Schellevis committed
230 231
}

232 233
function config_restore($conffile)
{
Franco Fichtner's avatar
Franco Fichtner committed
234
	global $config;
Ad Schellevis's avatar
Ad Schellevis committed
235 236 237 238

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

Ad Schellevis's avatar
Ad Schellevis committed
239 240
	$cnf = OPNsense\Core\Config::getInstance();
	$cnf->backup();
241
	$cnf->restoreBackup($conffile);
Ad Schellevis's avatar
Ad Schellevis committed
242 243 244

	disable_security_checks();

245
	$config = parse_config();
Ad Schellevis's avatar
Ad Schellevis committed
246 247 248 249 250 251 252 253 254 255 256 257 258 259

	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.
 */
260 261 262
function disable_security_checks()
{
	touch('/tmp/disable_security_checks');
Ad Schellevis's avatar
Ad Schellevis committed
263 264 265
}

/* Restores security checks.  Should be called after all succeed. */
266 267 268
function restore_security_checks()
{
	@unlink('/tmp/disable_security_checks');
Ad Schellevis's avatar
Ad Schellevis committed
269 270 271
}

/* Returns status of security check temporary disable. */
272 273 274
function security_checks_disabled()
{
	return file_exists('/tmp/disable_security_checks');
Ad Schellevis's avatar
Ad Schellevis committed
275 276
}

Ad Schellevis's avatar
Ad Schellevis committed
277 278 279
/**
 * remove old backups
 */
Franco Fichtner's avatar
Franco Fichtner committed
280 281 282
function cleanup_backups()
{
	global $config;
Ad Schellevis's avatar
Ad Schellevis committed
283 284
	$i = false;

285 286 287 288 289 290
	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
291
	$cnf = OPNsense\Core\Config::getInstance();
292

Ad Schellevis's avatar
Ad Schellevis committed
293 294 295 296
	$cnt=1;
	foreach ($cnf->getBackups() as $filename) {
		if ($cnt > $revisions ) {
			unlink($filename);
Ad Schellevis's avatar
Ad Schellevis committed
297
		}
Ad Schellevis's avatar
Ad Schellevis committed
298
		++$cnt ;
Ad Schellevis's avatar
Ad Schellevis committed
299 300 301 302 303 304 305 306 307 308 309 310 311 312 313 314 315 316 317 318 319 320 321
	}
}


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) {
322 323 324 325 326 327 328 329 330 331 332 333 334
	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
335
		$username = $override_user;
336
	}
Ad Schellevis's avatar
Ad Schellevis committed
337 338 339 340 341 342 343 344 345 346 347 348 349 350 351

	$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;
}

352
/**
353
 * backup config to google drive and return current file list (/ info)
354 355
 *
 */
356 357 358 359 360 361 362 363 364 365 366 367 368 369 370
function backup_to_google_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") {
			try {
				$client = new Google\API\Drive();
				$client->login($config->system->remotebackup->GDriveEmail->__toString(),
					$config->system->remotebackup->GDriveP12key->__toString());
			} catch (Exception $e) {
				log_error("error connecting to Google Drive");
				return array();
			}

371
			// backup source data to local strings (plain/encrypted)
372 373
			$confdata = file_get_contents('/conf/config.xml');
			$confdata_enc = encrypt_data($confdata, $config->system->remotebackup->GDrivePassword->__toString());
374 375 376
			tagfile_reformat($confdata_enc, $confdata_enc, "config.xml");


377
			// read filelist (config-*.xml)
378 379 380 381 382 383 384 385
			try {
				$files = $client->listFiles($config->system->remotebackup->GDriveFolderID->__toString());
			} catch (Exception $e) {
				log_error("error while fetching filelist from Google Drive");
				return array();
			}

			$configfiles = array();
386
			foreach ($files as $file) {
387 388 389
				if (fnmatch("config-*.xml", $file['title'])) {
					$configfiles[$file['title']] = $file;
				}
390 391
			}
			krsort($configfiles);
392

393

394
			// backup new file if changed (or if first in backup)
395
			$target_filename = "config-" . time() . ".xml";
396
			if (count($configfiles) > 1) {
397
				// compare last backup with current, only save new
398
				$bck_data_enc_in = $client->download($configfiles[array_keys($configfiles)[0]]);
399 400
				$bck_data_enc = "";
				tagfile_deformat($bck_data_enc_in, $bck_data_enc, "config.xml");
401
				$bck_data = decrypt_data($bck_data_enc, $config->system->remotebackup->GDrivePassword->__toString());
402 403
				if ($bck_data == $confdata) {
					$target_filename = null;
404 405
				}
			}
406 407
			if (!is_null($target_filename)) {
				log_error("backup configuration as " . $target_filename);
408 409
				$configfiles[$target_filename] = $client->upload($config->system->remotebackup->GDriveFolderID->__toString(),
					$target_filename, $confdata_enc);
410 411 412 413 414 415 416 417
				krsort($configfiles);
			}

			// cleanup old files
			if (isset($config->system->remotebackup->GDriveBackupCount) && is_numeric($config->system->remotebackup->GDriveBackupCount->__toString())) {
				$fcount = 0;
				foreach ($configfiles as $filename => $file) {
					if ($fcount >= $config->system->remotebackup->GDriveBackupCount->__toString()) {
418
						log_error("remove " . $filename . " from Google Drive");
419 420 421 422 423
						try {
							$client->delete($file);
						} catch (Google_Service_Exception $e) {
							log_error("unable to remove " . $filename . " from Google Drive");
						}
424
					}
425
					$fcount++;
426
				}
427 428
			}

429
			// return filelist
430
			return $configfiles;
431 432
		}
	}
433

434
	// not configured / issue, return empty list
435
	return array();
436
}