Project

General

Profile

Download (29.8 KB) Statistics
| Branch: | Tag: | Revision:
1
<?php
2
/*
3
 * config.lib.inc
4
 *
5
 * part of pfSense (https://www.pfsense.org)
6
 * Copyright (c) 2004-2020 Rubicon Communications, LLC (Netgate)
7
 * Copyright (c) 2009 Erik Kristensen
8
 * All rights reserved.
9
 *
10
 * originally part of m0n0wall (http://m0n0.ch/wall)
11
 * Copyright (c) 2003-2004 Manuel Kasper <mk@neon1.net>.
12
 * All rights reserved.
13
 *
14
 * Licensed under the Apache License, Version 2.0 (the "License");
15
 * you may not use this file except in compliance with the License.
16
 * You may obtain a copy of the License at
17
 *
18
 * http://www.apache.org/licenses/LICENSE-2.0
19
 *
20
 * Unless required by applicable law or agreed to in writing, software
21
 * distributed under the License is distributed on an "AS IS" BASIS,
22
 * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
23
 * See the License for the specific language governing permissions and
24
 * limitations under the License.
25
 */
26

    
27
/****f* config/encrypted_configxml
28
 * NAME
29
 *   encrypted_configxml - Checks to see if config.xml is encrypted and if so, prompts to unlock.
30
 * INPUTS
31
 *   None
32
 * RESULT
33
 *   $config 	- rewrites config.xml without encryption
34
 ******/
35
function encrypted_configxml() {
36
	global $g, $config;
37

    
38
	if (!file_exists($g['conf_path'] . "/config.xml")) {
39
		return;
40
	}
41

    
42
	if (!platform_booting()) {
43
		return;
44
	}
45

    
46
	$configtxt = file_get_contents($g['conf_path'] . "/config.xml");
47
	if (tagfile_deformat($configtxt, $configtxt, "config.xml")) {
48
		$fp = fopen('php://stdin', 'r');
49
		$data = "";
50
		echo "\n\n*** Encrypted config.xml detected ***\n";
51
		while ($data == "") {
52
			echo "\nEnter the password to decrypt config.xml: ";
53
			$decrypt_password = chop(fgets($fp));
54
			$data = decrypt_data($configtxt, $decrypt_password);
55
			if (!strstr($data, "<pfsense>")) {
56
				$data = "";
57
			}
58
			if ($data) {
59
				$fd = fopen($g['conf_path'] . "/config.xml.tmp", "w");
60
				fwrite($fd, $data);
61
				fclose($fd);
62
				exec("/bin/mv {$g['conf_path']}/config.xml.tmp {$g['conf_path']}/config.xml");
63
				echo "\n" . gettext("Config.xml unlocked.") . "\n";
64
				fclose($fp);
65
				pfSense_fsync("{$g['conf_path']}/config.xml");
66
			} else {
67
				echo "\n" . gettext("Invalid password entered.  Please try again.") . "\n";
68
			}
69
		}
70
	}
71
}
72

    
73
/****f* config/parse_config
74
 * NAME
75
 *   parse_config - Read in config.cache or config.xml if needed and return $config array
76
 * INPUTS
77
 *   $parse       - boolean to force parse_config() to read config.xml and generate config.cache
78
 * RESULT
79
 *   $config      - array containing all configuration variables
80
 ******/
81
function parse_config($parse = false) {
82
	global $g, $config_parsed, $config_extra;
83

    
84
	$lockkey = lock('config');
85
	$config_parsed = false;
86

    
87
	if (!file_exists("{$g['conf_path']}/config.xml") || filesize("{$g['conf_path']}/config.xml") == 0) {
88
		$last_backup = discover_last_backup();
89
		if ($last_backup) {
90
			log_error(gettext("No config.xml found, attempting last known config restore."));
91
			file_notice("config.xml", gettext("No config.xml found, attempting last known config restore."), "pfSenseConfigurator", "");
92
			restore_backup("{$g['conf_path']}/backup/{$last_backup}");
93
		} else {
94
			unlock($lockkey);
95
			die(gettext("Config.xml is corrupted and is 0 bytes.  Could not restore a previous backup."));
96
		}
97
	}
98

    
99
	if (platform_booting(true)) {
100
		echo ".";
101
	}
102

    
103
	// Check for encrypted config.xml
104
	encrypted_configxml();
105

    
106
	if (!$parse) {
107
		if (file_exists($g['tmp_path'] . '/config.cache')) {
108
			$config = unserialize(file_get_contents($g['tmp_path'] . '/config.cache'));
109
			if (!is_array($config)) {
110
				$parse = true;
111
			}
112
		} else {
113
			$parse = true;
114
		}
115
	}
116
	if ($parse == true) {
117
		if (!file_exists($g['conf_path'] . "/config.xml")) {
118
			if (platform_booting(true)) {
119
				echo ".";
120
			}
121
			log_error("No config.xml found, attempting last known config restore.");
122
			file_notice("config.xml", "No config.xml found, attempting last known config restore.", "pfSenseConfigurator", "");
123
			$last_backup = discover_last_backup();
124
			if ($last_backup) {
125
				restore_backup("/cf/conf/backup/{$last_backup}");
126
			} else {
127
				log_error(gettext("Could not restore config.xml."));
128
				unlock($lockkey);
129
				die(gettext("Config.xml is corrupted and is 0 bytes.  Could not restore a previous backup."));
130
			}
131
		}
132
		$config = parse_xml_config($g['conf_path'] . '/config.xml', array($g['xml_rootobj'], 'pfsense'));
133
		if ($config == -1) {
134
			$last_backup = discover_last_backup();
135
			if ($last_backup) {
136
				restore_backup("/cf/conf/backup/{$last_backup}");
137
			} else {
138
				log_error(gettext("Could not restore config.xml."));
139
				unlock($lockkey);
140
				die("Config.xml is corrupted and is 0 bytes.  Could not restore a previous backup.");
141
			}
142
		}
143
		generate_config_cache($config);
144
	}
145

    
146
	if (platform_booting(true)) {
147
		echo ".";
148
	}
149

    
150
	$config_parsed = true;
151
	unlock($lockkey);
152

    
153
	alias_make_table($config);
154

    
155
	return $config;
156
}
157

    
158
/****f* config/generate_config_cache
159
 * NAME
160
 *   generate_config_cache - Write serialized configuration to cache.
161
 * INPUTS
162
 *   $config	- array containing current firewall configuration
163
 * RESULT
164
 *   boolean	- true on completion
165
 ******/
166
function generate_config_cache($config) {
167
	global $g, $config_extra;
168

    
169
	$configcache = fopen($g['tmp_path'] . '/config.cache', "w");
170
	fwrite($configcache, serialize($config));
171
	fclose($configcache);
172
	pfSense_fsync("{$g['tmp_path']}/config.cache");
173

    
174
	unset($configcache);
175
	/* Used for config.extra.xml */
176
	if (file_exists($g['tmp_path'] . '/config.extra.cache') && $config_extra) {
177
		$configcacheextra = fopen($g['tmp_path'] . '/config.extra.cache', "w");
178
		fwrite($configcacheextra, serialize($config_extra));
179
		fclose($configcacheextra);
180
		pfSense_fsync("{$g['tmp_path']}/config.extra.cache");
181
		unset($configcacheextra);
182
	}
183
}
184

    
185
function discover_last_backup() {
186
	$backups = glob('/cf/conf/backup/*.xml');
187
	$last_backup = "";
188
	$last_mtime = 0;
189
	foreach ($backups as $backup) {
190
		if (filemtime($backup) > $last_mtime) {
191
			$last_mtime = filemtime($backup);
192
			$last_backup = $backup;
193
		}
194
	}
195

    
196
	return basename($last_backup);
197
}
198

    
199
function restore_backup($file) {
200
	global $g;
201

    
202
	if (file_exists($file)) {
203
		unlink_if_exists("{$g['tmp_path']}/config.cache");
204
		copy("$file", "/cf/conf/config.xml");
205
		pfSense_fsync("/cf/conf/config.xml");
206
		pfSense_fsync($g['conf_path']);
207
		disable_security_checks();
208
		log_error(sprintf(gettext('%1$s is restoring the configuration %2$s'), $g['product_name'], $file));
209
		file_notice("config.xml", sprintf(gettext('%1$s is restoring the configuration %2$s'), $g['product_name'], $file), "pfSenseConfigurator", "");
210
	}
211
}
212

    
213
/****f* config/parse_config_bootup
214
 * NAME
215
 *   parse_config_bootup - Bootup-specific configuration checks.
216
 * RESULT
217
 *   null
218
 ******/
219
function parse_config_bootup() {
220
	global $config, $g;
221

    
222
	if (platform_booting()) {
223
		echo ".";
224
	}
225

    
226
	$lockkey = lock('config');
227
	if (!file_exists("{$g['conf_path']}/config.xml")) {
228
		if (platform_booting()) {
229
			$last_backup = discover_last_backup();
230
			if ($last_backup) {
231
				log_error("No config.xml found, attempting last known config restore.");
232
				file_notice("config.xml", gettext("No config.xml found, attempting last known config restore."), "pfSenseConfigurator", "");
233
				restore_backup("/cf/conf/backup/{$last_backup}");
234
			}
235
			if (!file_exists("{$g['conf_path']}/config.xml")) {
236
				echo sprintf(gettext("XML configuration file not found.  %s cannot continue booting."), $g['product_name']) . "\n";
237
				unlock($lockkey);
238
				mwexec("/sbin/halt");
239
				exit;
240
			}
241
			log_error("Last known config found and restored.  Please double check the configuration file for accuracy.");
242
			file_notice("config.xml", gettext("Last known config found and restored.  Please double check the configuration file for accuracy."), "pfSenseConfigurator", "");
243
		} else {
244
			unlock($lockkey);
245
			log_error(gettext("Could not find a usable configuration file! Exiting...."));
246
			exit(0);
247
		}
248
	}
249

    
250
	if (filesize("{$g['conf_path']}/config.xml") == 0) {
251
		$last_backup = discover_last_backup();
252
		if ($last_backup) {
253
			log_error(gettext("No config.xml found, attempting last known config restore."));
254
			file_notice("config.xml", gettext("No config.xml found, attempting last known config restore."), "pfSenseConfigurator", "");
255
			restore_backup("{$g['conf_path']}/backup/{$last_backup}");
256
		} else {
257
			unlock($lockkey);
258
			die(gettext("Config.xml is corrupted and is 0 bytes.  Could not restore a previous backup."));
259
		}
260
	}
261
	unlock($lockkey);
262

    
263
	$config = parse_config(true);
264

    
265
	if ((float)$config['version'] > (float)$g['latest_config']) {
266
		echo <<<EOD
267

    
268

    
269
*******************************************************************************
270
* WARNING!                                                                    *
271
* The current configuration has been created with a newer version of {$g['product_name']}  *
272
* than this one! This can lead to serious misbehavior and even security       *
273
* holes! You are urged to either upgrade to a newer version of {$g['product_name']} or     *
274
* revert to the default configuration immediately!                            *
275
*******************************************************************************
276

    
277

    
278
EOD;
279
		}
280

    
281
	/* make alias table (for faster lookups) */
282
	alias_make_table($config);
283
}
284

    
285
/****f* config/conf_mount_rw
286
 * NAME
287
 *   conf_mount_rw - Mount filesystems read/write.
288
 * RESULT
289
 *   null
290
 ******/
291
/* mount flash card read/write */
292
function conf_mount_rw() {
293
	/* Obsoleted. Keep it here untill all calls are removed */
294
	return;
295
}
296

    
297
/****f* config/conf_mount_ro
298
 * NAME
299
 *   conf_mount_ro - Mount filesystems readonly.
300
 * RESULT
301
 *   null
302
 ******/
303
function conf_mount_ro() {
304
	/* Obsoleted. Keep it here untill all calls are removed */
305
	return;
306
}
307

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

    
326
	/* special case upgrades */
327
	/* fix every minute crontab bogons entry */
328
	if (is_array($config['cron'])) {
329
		$cron_item_count = count($config['cron']['item']);
330
		for ($x = 0; $x < $cron_item_count; $x++) {
331
			if (stristr($config['cron']['item'][$x]['command'], "rc.update_bogons.sh")) {
332
				if ($config['cron']['item'][$x]['hour'] == "*") {
333
					$config['cron']['item'][$x]['hour'] = "3";
334
					write_config(gettext("Updated bogon update frequency to 3am"));
335
					log_error(gettext("Updated bogon update frequency to 3am"));
336
				}
337
			}
338
		}
339
	}
340

    
341
	// Save off config version
342
	$prev_version = $config['version'];
343

    
344
	include_once('auth.inc');
345
	include_once('upgrade_config.inc');
346
	if (file_exists("/etc/inc/upgrade_config_custom.inc")) {
347
		include_once("upgrade_config_custom.inc");
348
	}
349

    
350
	if ($config['version'] == $g['latest_config']) {
351
		additional_config_upgrade();
352
		return;		/* already at latest version */
353
	}
354

    
355
	if (!is_array($config['system']['already_run_config_upgrade'])) {
356
		$config['system']['already_run_config_upgrade'] = array();
357
	}
358
	$already_run = $config['system']['already_run_config_upgrade'];
359

    
360
	/* Loop and run upgrade_VER_to_VER() until we're at current version */
361
	while ($config['version'] < $g['latest_config']) {
362
		$cur = $config['version'] * 10;
363
		$next = $cur + 1;
364
		$migration_function = sprintf('upgrade_%03d_to_%03d', $cur,
365
		    $next);
366

    
367
		foreach (array("", "_custom") as $suffix) {
368
			$migration_function .= $suffix;
369
			if (!function_exists($migration_function)) {
370
				continue;
371
			}
372
			if (isset($already_run[$migration_function])) {
373
				/* Already executed, skip now */
374
				unset($config['system']
375
				    ['already_run_config_upgrade']
376
				    [$migration_function]);
377
			} else {
378
				$migration_function();
379
			}
380
		}
381
		$config['version'] = sprintf('%.1f', $next / 10);
382
		if (platform_booting()) {
383
			echo ".";
384
		}
385
	}
386

    
387
	if ($prev_version != $config['version']) {
388
		$now = date("H:i:s");
389
		log_error(sprintf(gettext("Ended Configuration upgrade at %s"), $now));
390

    
391
		write_config(sprintf(gettext('Upgraded config version level from %1$s to %2$s'), $prev_version, $config['version']));
392
	}
393

    
394
	additional_config_upgrade();
395
}
396

    
397
/****f* config/safe_write_file
398
 * NAME
399
 *   safe_write_file - Write a file out atomically
400
 * DESCRIPTION
401
 *   safe_write_file() Writes a file out atomically by first writing to a
402
 *   temporary file of the same name but ending with the pid of the current
403
 *   process, them renaming the temporary file over the original.
404
 * INPUTS
405
 *   $filename  - string containing the filename of the file to write
406
 *   $content   - string or array containing the file content to write to file
407
 *   $force_binary      - boolean denoting whether we should force binary
408
 *   mode writing.
409
 * RESULT
410
 *   boolean - true if successful, false if not
411
 ******/
412
function safe_write_file($file, $content, $force_binary = false) {
413
	$tmp_file = $file . "." . getmypid();
414
	$write_mode = $force_binary ? "wb" : "w";
415

    
416
	$fd = fopen($tmp_file, $write_mode);
417
	if (!$fd) {
418
		// Unable to open temporary file for writing
419
		return false;
420
	}
421
	if (is_array($content)) {
422
		foreach ($content as $line) {
423
			if (!fwrite($fd, $line . "\n")) {
424
				// Unable to write to temporary file
425
				fclose($fd);
426
				return false;
427
			}
428
		}
429
	} elseif (!fwrite($fd, $content)) {
430
		// Unable to write to temporary file
431
		fclose($fd);
432
		return false;
433
	}
434
	fflush($fd);
435
	fclose($fd);
436

    
437
	if (!pfSense_fsync($tmp_file) || !rename($tmp_file, $file)) {
438
		// Unable to move temporary file to original
439
		@unlink($tmp_file);
440
		return false;
441
	}
442

    
443
	// Sync file before returning
444
	return pfSense_fsync($file);
445
}
446

    
447
/****f* config/write_config
448
 * NAME
449
 *   write_config - Backup and write the firewall configuration.
450
 * DESCRIPTION
451
 *   write_config() handles backing up the current configuration,
452
 *   applying changes, and regenerating the configuration cache.
453
 * INPUTS
454
 *   $desc	- string containing the a description of configuration changes
455
 *   $backup	- boolean: do not back up current configuration if false.
456
 *   $write_config_only	- boolean: do not sync or reload anything; just save the configuration if true.
457
 * RESULT
458
 *   null
459
 ******/
460
/* save the system configuration */
461
function write_config($desc="Unknown", $backup = true, $write_config_only = false) {
462
	global $config, $g;
463

    
464
	// Certain strings may be embedded in the $desc (reason) parameter to trigger certain behavior.
465
	// If detected, those strings are reomved and a variable set.
466
	$doacb = true;
467
	$manual_acb = false;
468
	$rcnt = 0;
469

    
470
	$desc = str_replace("-MaNuAlBaCkUp", "", $desc, $rcnt);
471
	if ($rcnt > 0) {
472
		$manual_acb = true; // Manual backups require special processing on the server
473
	}
474

    
475
	$rcnt = 0;
476
	$desc = str_replace("-NoReMoTeBaCkUp", "", $desc, $rcnt);
477
	if ($rcnt > 0) {
478
		$doacb = false; // No ACB will be performed if this string is detected
479
	}
480

    
481
	/*
482
	* Syncing vouchers happens every minute and sometimes multiple times. We don't
483
	* want to fill up our db with a lot of the same config so just ignore that case.
484
	*/
485
	if((strpos($desc, 'Syncing vouchers') !== false ||
486
		strpos($desc, 'Captive Portal Voucher database synchronized') !== false) ) {
487
		$doacb = false;
488
	}
489

    
490
	if (!empty($_SERVER['REMOTE_ADDR'])) {
491
		@phpsession_begin();
492
		if (!empty($_SESSION['Username']) && ($_SESSION['Username'] != "admin")) {
493
			$user = getUserEntry($_SESSION['Username']);
494
			if (is_array($user) && userHasPrivilege($user, "user-config-readonly")) {
495
				syslog(LOG_AUTHPRIV, sprintf(gettext("Save config permission denied by the 'User - Config: Deny Config Write' permission for user '%s'."), get_config_user()));
496
				phpsession_end(true);
497
				return false;
498
			}
499
		}
500
		if (!isset($argc)) {
501
			phpsession_end(true);
502
		}
503
	}
504

    
505
	if (isset($config['reset_factory_defaults'])) {
506
		/*
507
		   We have put a default config.xml on disk and are about to reboot
508
		   or reload it. Do not let any system or package code try to save
509
		   state to config because that would overwrite the default config
510
		   with the running config.
511
		*/
512
		return false;
513
	}
514

    
515
	if ($backup) {
516
		backup_config();
517
	}
518

    
519
	$config['revision'] = make_config_revision_entry($desc);
520

    
521
	$lockkey = lock('config', LOCK_EX);
522

    
523
	/* generate configuration XML */
524
	$xmlconfig = dump_xml_config($config, $g['xml_rootobj']);
525

    
526
	/* write new configuration */
527
	if (!safe_write_file("{$g['cf_conf_path']}/config.xml", $xmlconfig)) {
528
		log_error(gettext("WARNING: Config contents could not be saved. Could not open file!"));
529
		unlock($lockkey);
530
		file_notice("config.xml", sprintf(gettext('Unable to open %1$s/config.xml for writing in write_config()%2$s'), $g['cf_conf_path'], "\n"));
531
		return -1;
532
	}
533

    
534
	cleanup_backupcache(true);
535

    
536
	/* re-read configuration */
537
	/* NOTE: We assume that the file can be parsed since we wrote it. */
538
	$config = parse_xml_config("{$g['conf_path']}/config.xml", $g['xml_rootobj']);
539
	if ($config == -1) {
540
		copy("{$g['conf_path']}/config.xml", "{$g['conf_path']}/config.xml.bad");
541
		$last_backup = discover_last_backup();
542
		if ($last_backup) {
543
			restore_backup("/cf/conf/backup/{$last_backup}");
544
			$config = parse_xml_config("{$g['conf_path']}/config.xml", $g['xml_rootobj']);
545
			if (platform_booting()) {
546
				echo "\n\n ************** WARNING **************";
547
				echo "\n\n Configuration could not be validated. A previous configuration was restored. \n";
548
				echo "\n The failed configuration file has been saved as {$g['conf_path']}/config.xml.bad \n\n";
549
			}
550
		} else {
551
			log_error(gettext("Could not restore config.xml."));
552
		}
553
	} else {
554
		generate_config_cache($config);
555
	}
556

    
557
	unlock($lockkey);
558

    
559
	if ($write_config_only) {
560
		return $config;
561
	}
562

    
563
	unlink_if_exists("/usr/local/pkg/pf/carp_sync_client.php");
564

    
565
	/* sync carp entries to other firewalls */
566
	carp_sync_client();
567

    
568
	if (is_dir("/usr/local/pkg/write_config")) {
569
		/* process packager manager custom rules */
570
		run_plugins("/usr/local/pkg/write_config/");
571
	}
572

    
573
	// Try the core AutoConfigBackup system
574
	if (is_array($config['system']['acb']) && $config['system']['acb']['enable'] == "yes" &&
575
	    (!isset($config['system']['acb']['frequency']) || $config['system']['acb']['frequency'] == "every") || file_exists("/tmp/forceacb")) {
576
	    if ($doacb) {
577
			require_once("acb.inc");
578
			upload_config($manual_acb);
579
		}
580

    
581
		if (file_exists("/tmp/forceacb")) {
582
			unlink("/tmp/forceacb");
583
		}
584
	}
585

    
586
	return $config;
587
}
588

    
589
/****f* config/reset_factory_defaults
590
 * NAME
591
 *   reset_factory_defaults - Reset the system to its default configuration.
592
 * RESULT
593
 *   integer	- indicates completion
594
 ******/
595
function reset_factory_defaults($lock = false, $reboot_required = true) {
596
	global $config, $g;
597

    
598
	/* Remove all additional packages */
599
	mwexec("/bin/sh /usr/local/sbin/{$g['product_name']}-upgrade " .
600
	    "-r ALL_PACKAGES -f");
601

    
602
	if (!$lock) {
603
		$lockkey = lock('config', LOCK_EX);
604
	}
605

    
606
	/* create conf directory, if necessary */
607
	safe_mkdir($g['cf_conf_path']);
608

    
609
	/* clear out /conf */
610
	$dh = opendir($g['conf_path']);
611
	while ($filename = readdir($dh)) {
612
		if (($filename != ".") && ($filename != "..") &&
613
		    (!is_dir($g['conf_path'] . "/" . $filename))) {
614
			unlink_if_exists($g['conf_path'] . "/" . $filename);
615
		}
616
	}
617
	closedir($dh);
618
	unlink_if_exists($g['tmp_path'] . "/config.cache");
619

    
620
	/* copy default configuration */
621
	copy("{$g['conf_default_path']}/config.xml",
622
	    "{$g['cf_conf_path']}/config.xml");
623

    
624
	disable_security_checks();
625

    
626
	/*
627
	   Let write_config know that we are awaiting reload of the current config
628
	   to factory defaults. Either the system is about to reboot, throwing away
629
	   the current in-memory config as it shuts down, or the in-memory config
630
	   is about to be reloaded on-the-fly by parse_config.
631

    
632
	   In both cases, we want to ensure that write_config does not flush the
633
	   in-memory config back to disk.
634
	*/
635
	$config['reset_factory_defaults'] = true;
636

    
637
	/* call the wizard */
638
	if ($reboot_required) {
639
		// If we need a reboot first then touch a different trigger file.
640
		touch("/conf/trigger_initial_wizard_after_reboot");
641
	} else {
642
		touch("/conf/trigger_initial_wizard");
643
	}
644
	if (!$lock) {
645
		unlock($lockkey);
646
	}
647
	setup_serial_port();
648
	return 0;
649
}
650

    
651
function config_restore($conffile) {
652
	global $config, $g;
653

    
654
	if (!file_exists($conffile)) {
655
		return 1;
656
	}
657

    
658
	backup_config();
659

    
660

    
661
	$lockkey = lock('config', LOCK_EX);
662

    
663
	unlink_if_exists("{$g['tmp_path']}/config.cache");
664
	copy($conffile, "{$g['cf_conf_path']}/config.xml");
665

    
666
	disable_security_checks();
667

    
668
	unlock($lockkey);
669

    
670
	$config = parse_config(true);
671

    
672

    
673
	write_config(sprintf(gettext("Reverted to %s."), array_pop(explode("/", $conffile))), false);
674

    
675
	return 0;
676
}
677

    
678
function config_install($conffile) {
679
	global $config, $g;
680

    
681
	if (!file_exists($conffile)) {
682
		return 1;
683
	}
684

    
685
	if (!config_validate("{$conffile}")) {
686
		return 1;
687
	}
688

    
689
	if (platform_booting()) {
690
		echo gettext("Installing configuration...") . "\n";
691
	} else {
692
		log_error(gettext("Installing configuration ...."));
693
	}
694

    
695
	$lockkey = lock('config', LOCK_EX);
696

    
697
	copy($conffile, "{$g['conf_path']}/config.xml");
698

    
699
	disable_security_checks();
700

    
701
	/* unlink cache file if it exists */
702
	if (file_exists("{$g['tmp_path']}/config.cache")) {
703
		unlink("{$g['tmp_path']}/config.cache");
704
	}
705

    
706
	unlock($lockkey);
707

    
708
	return 0;
709
}
710

    
711
/*
712
 * Disable security checks for DNS rebind and HTTP referrer until next time
713
 * they pass (or reboot), to aid in preventing accidental lockout when
714
 * restoring settings like hostname, domain, IP addresses, and settings
715
 * related to the DNS rebind and HTTP referrer checks.
716
 * Intended for use when restoring a configuration or directly
717
 * modifying config.xml without an unconditional reboot.
718
 */
719
function disable_security_checks() {
720
	global $g;
721
	touch("{$g['tmp_path']}/disable_security_checks");
722
}
723

    
724
/* Restores security checks.  Should be called after all succeed. */
725
function restore_security_checks() {
726
	global $g;
727
	unlink_if_exists("{$g['tmp_path']}/disable_security_checks");
728
}
729

    
730
/* Returns status of security check temporary disable. */
731
function security_checks_disabled() {
732
	global $g;
733
	return file_exists("{$g['tmp_path']}/disable_security_checks");
734
}
735

    
736
function config_validate($conffile) {
737

    
738
	global $g, $xmlerr;
739

    
740
	$xml_parser = xml_parser_create();
741

    
742
	if (!($fp = fopen($conffile, "r"))) {
743
		$xmlerr = gettext("XML error: unable to open file");
744
		return false;
745
	}
746

    
747
	while ($data = fread($fp, 4096)) {
748
		if (!xml_parse($xml_parser, $data, feof($fp))) {
749
			$xmlerr = sprintf(gettext('%1$s at line %2$d'),
750
						xml_error_string(xml_get_error_code($xml_parser)),
751
						xml_get_current_line_number($xml_parser));
752
			return false;
753
		}
754
	}
755
	xml_parser_free($xml_parser);
756

    
757
	fclose($fp);
758

    
759
	return true;
760
}
761

    
762
function cleanup_backupcache($lock = false) {
763
	global $config, $g;
764
	$i = false;
765

    
766
	$revisions = intval(is_numericint($config['system']['backupcount']) ? $config['system']['backupcount'] : $g['default_config_backup_count']);
767

    
768
	if (!$lock) {
769
		$lockkey = lock('config');
770
	}
771

    
772

    
773
	$backups = get_backups();
774
	if ($backups) {
775
		$baktimes = $backups['versions'];
776
		unset($backups['versions']);
777
	} else {
778
		$backups = array();
779
		$baktimes = array();
780
	}
781
	$newbaks = array();
782
	$bakfiles = glob($g['cf_conf_path'] . "/backup/config-*");
783
	$tocache = array();
784

    
785
	foreach ($bakfiles as $backup) { // Check for backups in the directory not represented in the cache.
786
		$backupsize = filesize($backup);
787
		if ($backupsize == 0) {
788
			unlink($backup);
789
			continue;
790
		}
791
		$backupexp = explode('-', $backup);
792
		$backupexp = explode('.', array_pop($backupexp));
793
		$tocheck = array_shift($backupexp);
794
		unset($backupexp);
795
		if (!in_array($tocheck, $baktimes)) {
796
			$i = true;
797
			if (platform_booting()) {
798
				echo ".";
799
			}
800
			try {
801
				$newxml = parse_xml_config($backup, array($g['xml_rootobj'], 'pfsense'));
802
			} catch (Exception $exc) {
803
				log_error(sprintf(gettext("The backup cache file %s is corrupted. Parser error message: %s"), $backup, $exc->getMessage()));
804
				$newxml = "-1";
805
			}
806

    
807
			if ($newxml == "-1") {
808
				log_error(sprintf(gettext("The backup cache file %s is corrupted.  Unlinking."), $backup));
809
				unlink($backup);
810
				continue;
811
			}
812
			if ($newxml['revision']['description'] == "") {
813
				$newxml['revision']['description'] = "Unknown";
814
			}
815
			if ($newxml['version'] == "") {
816
				$newxml['version'] = "?";
817
			}
818
			$tocache[$tocheck] = array('description' => $newxml['revision']['description'], 'version' => $newxml['version'], 'filesize' => $backupsize);
819
		}
820
	}
821
	foreach ($backups as $checkbak) {
822
		if (count(preg_grep('/' . $checkbak['time'] . '/i', $bakfiles)) != 0) {
823
			$newbaks[] = $checkbak;
824
		} else {
825
			$i = true;
826
			if (platform_booting()) print " " . $tocheck . "r";
827
		}
828
	}
829
	foreach ($newbaks as $todo) {
830
		$tocache[$todo['time']] = array('description' => $todo['description'], 'version' => $todo['version'], 'filesize' => $todo['filesize']);
831
	}
832
	if (is_int($revisions) and (count($tocache) > $revisions)) {
833
		$toslice = array_slice(array_keys($tocache), 0, $revisions);
834
		$newcache = array();
835
		foreach ($toslice as $sliced) {
836
			$newcache[$sliced] = $tocache[$sliced];
837
		}
838
		foreach ($tocache as $version => $versioninfo) {
839
			if (!in_array($version, array_keys($newcache))) {
840
				unlink_if_exists($g['conf_path'] . '/backup/config-' . $version . '.xml');
841
			}
842
		}
843
		$tocache = $newcache;
844
	}
845
	$bakout = fopen($g['cf_conf_path'] . '/backup/backup.cache', "w");
846
	fwrite($bakout, serialize($tocache));
847
	fclose($bakout);
848
	pfSense_fsync("{$g['cf_conf_path']}/backup/backup.cache");
849

    
850
	if (!$lock) {
851
		unlock($lockkey);
852
	}
853
}
854

    
855
function get_backups() {
856
	global $g;
857
	if (file_exists("{$g['cf_conf_path']}/backup/backup.cache")) {
858
		$confvers = unserialize(file_get_contents("{$g['cf_conf_path']}/backup/backup.cache"));
859
		$bakvers = array_keys($confvers);
860
		$toreturn = array();
861
		sort($bakvers);
862
		// 	$bakvers = array_reverse($bakvers);
863
		foreach (array_reverse($bakvers) as $bakver) {
864
			$toreturn[] = array('time' => $bakver, 'description' => $confvers[$bakver]['description'], 'version' => $confvers[$bakver]['version'], 'filesize' => $confvers[$bakver]['filesize']);
865
		}
866
	} else {
867
		return false;
868
	}
869
	$toreturn['versions'] = $bakvers;
870
	return $toreturn;
871
}
872

    
873
function backup_config() {
874
	global $config, $g;
875

    
876

    
877
	/* Create backup directory if needed */
878
	safe_mkdir("{$g['cf_conf_path']}/backup");
879
	if ($config['revision']['time'] == "") {
880
		$baktime = 0;
881
	} else {
882
		$baktime = $config['revision']['time'];
883
	}
884

    
885
	if ($config['revision']['description'] == "") {
886
		$bakdesc = "Unknown";
887
	} else {
888
		$bakdesc = $config['revision']['description'];
889
	}
890

    
891
	$bakver = ($config['version'] == "") ? "?" : $config['version'];
892
	$bakfilename = $g['cf_conf_path'] . '/backup/config-' . $baktime . '.xml';
893
	copy($g['cf_conf_path'] . '/config.xml', $bakfilename);
894

    
895
	if (file_exists($g['cf_conf_path'] . '/backup/backup.cache')) {
896
		$backupcache = unserialize(file_get_contents($g['cf_conf_path'] . '/backup/backup.cache'));
897
	} else {
898
		$backupcache = array();
899
	}
900
	$backupcache[$baktime] = array('description' => $bakdesc, 'version' => $bakver, 'filesize' => filesize($bakfilename));
901
	$bakout = fopen($g['cf_conf_path'] . '/backup/backup.cache', "w");
902
	fwrite($bakout, serialize($backupcache));
903
	fclose($bakout);
904
	pfSense_fsync("{$g['cf_conf_path']}/backup/backup.cache");
905

    
906

    
907
	return true;
908
}
909

    
910
function set_device_perms() {
911
	$devices = array(
912
		'pf' => array(
913
			'user' => 'root',
914
			'group' => 'proxy',
915
			'mode' => 0660),
916
		);
917

    
918
	foreach ($devices as $name => $attr) {
919
		$path = "/dev/$name";
920
		if (file_exists($path)) {
921
			chown($path, $attr['user']);
922
			chgrp($path, $attr['group']);
923
			chmod($path, $attr['mode']);
924
		}
925
	}
926
}
927

    
928
function get_config_user() {
929
	if (empty($_SESSION["Username"])) {
930
		$username = getenv("USER");
931
		if (empty($conuser) || $conuser == "root") {
932
			$username = "(system)";
933
		}
934
	} else {
935
		$username = $_SESSION["Username"];
936
	}
937

    
938
	if (!empty($_SERVER['REMOTE_ADDR'])) {
939
		$username .= '@' . get_user_remote_address() . get_user_remote_authsource();
940
	}
941

    
942
	return $username;
943
}
944

    
945
function make_config_revision_entry($desc = null, $override_user = null) {
946
	if (empty($override_user)) {
947
		$username = get_config_user();
948
	} else {
949
		$username = $override_user;
950
	}
951

    
952
	$revision = array();
953

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

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

    
969
function pfSense_clear_globals() {
970
	global $config, $FilterIfList, $GatewaysList, $filterdns, $aliases, $aliastable;
971

    
972
	$error = error_get_last();
973

    
974
	// Errors generated by user code (diag_commands.php) are identified by path and not added to notices
975
	if ($error !== NULL && !preg_match('|^' . preg_quote($g['tmp_path_user_code']) . '/[^/]{1,16}$|', $error['file'])) {
976
		if (in_array($error['type'], array(E_ERROR, E_COMPILE_ERROR, E_CORE_ERROR, E_RECOVERABLE_ERROR))) {
977
			$errorstr = "PHP ERROR: Type: {$error['type']}, File: {$error['file']}, Line: {$error['line']}, Message: {$error['message']}";
978
			print($errorstr);
979
			log_error($errorstr);
980
			file_notice("phperror", $errorstr, 'PHP errors');
981
		} else if ($error['type'] != E_NOTICE) {
982
			$errorstr = "PHP WARNING: Type: {$error['type']}, File: {$error['file']}, Line: {$error['line']}, Message: {$error['message']}";
983
			// XXX: comment out for now, should re-enable post-2.2
984
			//print($errorstr);
985
			//log_error($errorstr);
986
			//file_notice("phpwarning", $errorstr, 'PHP warning');
987
		}
988
	}
989

    
990
	if (isset($FilterIfList)) {
991
		unset($FilterIfList);
992
	}
993

    
994
	if (isset($GatewaysList)) {
995
		unset($GatewaysList);
996
	}
997

    
998
	/* Used for the hostname dns resolver */
999
	if (isset($filterdns)) {
1000
		unset($filterdns);
1001
	}
1002

    
1003
	/* Used for aliases and interface macros */
1004
	if (isset($aliases)) {
1005
		unset($aliases);
1006
	}
1007
	if (isset($aliastable)) {
1008
		unset($aliastable);
1009
	}
1010

    
1011
	unset($config);
1012
}
1013

    
1014
/* Initialize a config array multiple levels deep only if unset
1015
 * Pass it an array of keys to test and create
1016
 * init_config_arr(array('virtualip', 'vip'));
1017
 */
1018
function init_config_arr($keys) {
1019
	global $config;
1020
	$c = &$config;
1021
	if (!is_array($keys)) {
1022
		return null;
1023
	}
1024
	foreach ($keys as $k) {
1025
		if (!is_array($c[$k])) {
1026
			$c[$k] = array();
1027
		}
1028
		$c = &$c[$k];
1029
	}
1030
}
1031

    
1032
register_shutdown_function('pfSense_clear_globals');
1033

    
1034
?>
(11-11/60)