config.lib.inc 12.8 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

40 41 42 43 44 45 46 47 48
/* 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) {
49 50 51
			if ($alias['name']) {
				$aliastable[$alias['name']] = isset($alias['address']) ? $alias['address'] : null;
			}
52 53 54 55
		}
	}
}

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,	gettext('No valid config.xml found, attempting last known config restore.'));
71
			$cnf->restoreBackup($backups[0]);
Ad Schellevis's avatar
Ad Schellevis committed
72
		} else {
73
			// we don't have backups, try to load the default
74
			syslog(LOG_ERR, gettext('No valid config.xml found, attempting to restore factory config.'));
75
			$cnf->restoreBackup('/usr/local/etc/config.xml');
Ad Schellevis's avatar
Ad Schellevis committed
76 77 78
		}
	}

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

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

85
	return $config;
86
}
Ad Schellevis's avatar
Ad Schellevis committed
87 88 89 90 91 92 93 94 95 96 97 98 99

/****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 */
100 101
function convert_config()
{
Ad Schellevis's avatar
Ad Schellevis committed
102
	global $config, $g;
103 104 105 106 107 108 109 110 111 112

	if (!isset($config['revision'])) {
		/* force a revision tag for proper handling in config history */
		write_config('Factory configuration', false);
	}

	if ($config['version'] == $g['latest_config']) {
		/* already at latest version */
		return;
	}
Ad Schellevis's avatar
Ad Schellevis committed
113 114 115 116 117 118

	/* 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++) {
119
			if(stristr($config['cron']['item'][$x]['command'], 'rc.update_bogons')) {
Ad Schellevis's avatar
Ad Schellevis committed
120 121 122 123
				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"));
124
				}
Ad Schellevis's avatar
Ad Schellevis committed
125 126 127 128 129 130 131 132 133 134 135
			}
		}
	}

	// 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);
136
		if (function_exists($migration_function)) {
Ad Schellevis's avatar
Ad Schellevis committed
137
			$migration_function();
138
		}
Ad Schellevis's avatar
Ad Schellevis committed
139 140 141
		$config['version'] = sprintf('%.1f', $next / 10);
	}

142
	if ($prev_version != $config['version']) {
143
		write_config(sprintf(gettext('Upgraded config version level from %s to %s'), $prev_version, $config['version']));
144
	}
Ad Schellevis's avatar
Ad Schellevis committed
145 146 147 148 149 150 151 152 153 154 155 156 157 158 159 160
}


/****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 */
161 162
function write_config($desc = 'Unknown', $backup = true)
{
Franco Fichtner's avatar
Franco Fichtner committed
163
	global $config;
Ad Schellevis's avatar
Ad Schellevis committed
164 165

	if (!empty($_SERVER['REMOTE_ADDR'])) {
166 167
		if (session_status() == PHP_SESSION_NONE) {
			session_start();
168 169
		}
		if (!empty($_SESSION['Username']) && ($_SESSION['Username'] != 'root')) {
Ad Schellevis's avatar
Ad Schellevis committed
170 171
			$user = getUserEntry($_SESSION['Username']);
			if (is_array($user) && userHasPrivilege($user, "user-config-readonly")) {
172
				session_write_close();
173 174
				// 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
175 176 177
				return false;
			}
		}
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
194
		syslog(LOG_ERR, gettext('WARNING: Config contents could not be saved. Could not open file!'));
Ad Schellevis's avatar
Ad Schellevis committed
195
		return -1;
196
	}
Ad Schellevis's avatar
Ad Schellevis committed
197

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

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

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

}

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

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

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

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

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

	disable_security_checks();

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

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

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

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

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

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

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


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']);
		}
	}
}


320 321 322 323
function make_config_revision_entry($desc = null, $override_user = null)
{
	global $config;

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'];
		}
335
	} else {
Ad Schellevis's avatar
Ad Schellevis committed
336
		$username = $override_user;
337
	}
Ad Schellevis's avatar
Ad Schellevis committed
338 339 340

	$revision = array();
	$revision['username'] = $username;
341
	$revision['time'] = microtime(true);
342 343 344 345 346 347
	if ($desc == null || $desc == 'Unknown') {
		$revision['description'] = sprintf(gettext("%s made unknown change"), $_SERVER['SCRIPT_NAME']);
	} else {
		$revision['description'] = $desc;
	}

Ad Schellevis's avatar
Ad Schellevis committed
348 349 350
	return $revision;
}

351
/**
352
 * backup config to google drive and return current file list (/ info)
353 354
 *
 */
355 356 357 358 359 360 361 362 363 364 365 366 367 368 369
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();
			}

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


376
			// read filelist (config-*.xml)
377 378 379 380 381 382 383 384
			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();
385
			foreach ($files as $file) {
386 387 388
				if (fnmatch("config-*.xml", $file['title'])) {
					$configfiles[$file['title']] = $file;
				}
389 390
			}
			krsort($configfiles);
391

392

393
			// backup new file if changed (or if first in backup)
394
			$target_filename = "config-" . time() . ".xml";
395
			if (count($configfiles) > 1) {
396
				// compare last backup with current, only save new
397
				$bck_data_enc_in = $client->download($configfiles[array_keys($configfiles)[0]]);
398 399
				$bck_data_enc = "";
				tagfile_deformat($bck_data_enc_in, $bck_data_enc, "config.xml");
400
				$bck_data = decrypt_data($bck_data_enc, $config->system->remotebackup->GDrivePassword->__toString());
401 402
				if ($bck_data == $confdata) {
					$target_filename = null;
403 404
				}
			}
405 406
			if (!is_null($target_filename)) {
				log_error("backup configuration as " . $target_filename);
407 408
				$configfiles[$target_filename] = $client->upload($config->system->remotebackup->GDriveFolderID->__toString(),
					$target_filename, $confdata_enc);
409 410 411 412 413 414 415 416
				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()) {
417
						log_error("remove " . $filename . " from Google Drive");
418 419 420 421 422
						try {
							$client->delete($file);
						} catch (Google_Service_Exception $e) {
							log_error("unable to remove " . $filename . " from Google Drive");
						}
423
					}
424
					$fcount++;
425
				}
426 427
			}

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

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