Project

General

Profile

Download (30.1 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-2013 BSD Perimeter
7
 * Copyright (c) 2013-2016 Electric Sheep Fencing
8
 * Copyright (c) 2014-2019 Rubicon Communications, LLC (Netgate)
9
 * Copyright (c) 2009 Erik Kristensen
10
 * All rights reserved.
11
 *
12
 * originally part of m0n0wall (http://m0n0.ch/wall)
13
 * Copyright (c) 2003-2004 Manuel Kasper <mk@neon1.net>.
14
 * All rights reserved.
15
 *
16
 * Licensed under the Apache License, Version 2.0 (the "License");
17
 * you may not use this file except in compliance with the License.
18
 * You may obtain a copy of the License at
19
 *
20
 * http://www.apache.org/licenses/LICENSE-2.0
21
 *
22
 * Unless required by applicable law or agreed to in writing, software
23
 * distributed under the License is distributed on an "AS IS" BASIS,
24
 * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
25
 * See the License for the specific language governing permissions and
26
 * limitations under the License.
27
 */
28

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

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

    
44
	if (!platform_booting()) {
45
		return;
46
	}
47

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

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

    
86
	$lockkey = lock('config');
87
	$config_parsed = false;
88

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

    
101
	if (platform_booting(true)) {
102
		echo ".";
103
	}
104

    
105
	// Check for encrypted config.xml
106
	encrypted_configxml();
107

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

    
148
	if (platform_booting(true)) {
149
		echo ".";
150
	}
151

    
152
	$config_parsed = true;
153
	unlock($lockkey);
154

    
155
	alias_make_table($config);
156

    
157
	return $config;
158
}
159

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

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

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

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

    
198
	return basename($last_backup);
199
}
200

    
201
function restore_backup($file) {
202
	global $g;
203

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

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

    
224
	if (platform_booting()) {
225
		echo ".";
226
	}
227

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

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

    
265
	$config = parse_config(true);
266

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

    
270

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

    
279

    
280
EOD;
281
		}
282

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

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

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

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

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

    
343
	// Save off config version
344
	$prev_version = $config['version'];
345

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

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

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

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

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

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

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

    
396
	additional_config_upgrade();
397
}
398

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

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

    
439
	/* XXX Re-add pfSense_fsync() call here after it's fixed */
440
	// if (!pfSense_fsync($tmp_file) || !rename($tmp_file, $file)) {
441
	if (!rename($tmp_file, $file)) {
442
		// Unable to move temporary file to original
443
		@unlink($tmp_file);
444
		return false;
445
	}
446

    
447
	// Sync file before returning
448
	//return pfSense_fsync($file);
449
	return true;
450
}
451

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

    
469
	// Certain strings may be embedded in the $desc (reason) parameter to trigger certain behavior.
470
	// If detected, those strings are reomved and a variable set.
471
	$doacb = true;
472
	$manual_acb = false;
473
	$rcnt = 0;
474

    
475
	$desc = str_replace("-MaNuAlBaCkUp", "", $desc, $rcnt);
476
	if ($rcnt > 0) {
477
		$manual_acb = true; // Manual backups require special processing on the server
478
	}
479

    
480
	$rcnt = 0;
481
	$desc = str_replace("-NoReMoTeBaCkUp", "", $desc, $rcnt);
482
	if ($rcnt > 0) {
483
		$doacb = false; // No ACB will be performed if this string is detected
484
	}
485

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

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

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

    
520
	if ($backup) {
521
		backup_config();
522
	}
523

    
524
	$config['revision'] = make_config_revision_entry($desc);
525

    
526
	$lockkey = lock('config', LOCK_EX);
527

    
528
	/* generate configuration XML */
529
	$xmlconfig = dump_xml_config($config, $g['xml_rootobj']);
530

    
531
	/* write new configuration */
532
	if (!safe_write_file("{$g['cf_conf_path']}/config.xml", $xmlconfig)) {
533
		log_error(gettext("WARNING: Config contents could not be saved. Could not open file!"));
534
		unlock($lockkey);
535
		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"));
536
		return -1;
537
	}
538

    
539
	cleanup_backupcache(true);
540

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

    
562
	unlock($lockkey);
563

    
564
	if ($write_config_only) {
565
		return $config;
566
	}
567

    
568
	unlink_if_exists("/usr/local/pkg/pf/carp_sync_client.php");
569

    
570
	/* sync carp entries to other firewalls */
571
	carp_sync_client();
572

    
573
	if (is_dir("/usr/local/pkg/write_config")) {
574
		/* process packager manager custom rules */
575
		run_plugins("/usr/local/pkg/write_config/");
576
	}
577

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

    
586
		if (file_exists("/tmp/forceacb")) {
587
			unlink("/tmp/forceacb");
588
		}
589
	}
590

    
591
	return $config;
592
}
593

    
594
/****f* config/reset_factory_defaults
595
 * NAME
596
 *   reset_factory_defaults - Reset the system to its default configuration.
597
 * RESULT
598
 *   integer	- indicates completion
599
 ******/
600
function reset_factory_defaults($lock = false, $reboot_required = true) {
601
	global $config, $g;
602

    
603
	/* Remove all additional packages */
604
	mwexec("/bin/sh /usr/local/sbin/{$g['product_name']}-upgrade " .
605
	    "-r ALL_PACKAGES -f");
606

    
607
	if (!$lock) {
608
		$lockkey = lock('config', LOCK_EX);
609
	}
610

    
611
	/* create conf directory, if necessary */
612
	safe_mkdir($g['cf_conf_path']);
613

    
614
	/* clear out /conf */
615
	$dh = opendir($g['conf_path']);
616
	while ($filename = readdir($dh)) {
617
		if (($filename != ".") && ($filename != "..") &&
618
		    (!is_dir($g['conf_path'] . "/" . $filename))) {
619
			unlink_if_exists($g['conf_path'] . "/" . $filename);
620
		}
621
	}
622
	closedir($dh);
623
	unlink_if_exists($g['tmp_path'] . "/config.cache");
624

    
625
	/* copy default configuration */
626
	copy("{$g['conf_default_path']}/config.xml",
627
	    "{$g['cf_conf_path']}/config.xml");
628

    
629
	disable_security_checks();
630

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

    
637
	   In both cases, we want to ensure that write_config does not flush the
638
	   in-memory config back to disk.
639
	*/
640
	$config['reset_factory_defaults'] = true;
641

    
642
	/* call the wizard */
643
	if ($reboot_required) {
644
		// If we need a reboot first then touch a different trigger file.
645
		touch("/conf/trigger_initial_wizard_after_reboot");
646
	} else {
647
		touch("/conf/trigger_initial_wizard");
648
	}
649
	if (!$lock) {
650
		unlock($lockkey);
651
	}
652
	console_configure();
653
	return 0;
654
}
655

    
656
function config_restore($conffile) {
657
	global $config, $g;
658

    
659
	if (!file_exists($conffile)) {
660
		return 1;
661
	}
662

    
663
	backup_config();
664

    
665

    
666
	$lockkey = lock('config', LOCK_EX);
667

    
668
	unlink_if_exists("{$g['tmp_path']}/config.cache");
669
	copy($conffile, "{$g['cf_conf_path']}/config.xml");
670

    
671
	disable_security_checks();
672

    
673
	unlock($lockkey);
674

    
675
	$config = parse_config(true);
676

    
677

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

    
680
	return 0;
681
}
682

    
683
function config_install($conffile) {
684
	global $config, $g;
685

    
686
	if (!file_exists($conffile)) {
687
		return 1;
688
	}
689

    
690
	if (!config_validate("{$conffile}")) {
691
		return 1;
692
	}
693

    
694
	if (platform_booting()) {
695
		echo gettext("Installing configuration...") . "\n";
696
	} else {
697
		log_error(gettext("Installing configuration ...."));
698
	}
699

    
700
	$lockkey = lock('config', LOCK_EX);
701

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

    
704
	disable_security_checks();
705

    
706
	/* unlink cache file if it exists */
707
	if (file_exists("{$g['tmp_path']}/config.cache")) {
708
		unlink("{$g['tmp_path']}/config.cache");
709
	}
710

    
711
	unlock($lockkey);
712

    
713
	return 0;
714
}
715

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

    
729
/* Restores security checks.  Should be called after all succeed. */
730
function restore_security_checks() {
731
	global $g;
732
	unlink_if_exists("{$g['tmp_path']}/disable_security_checks");
733
}
734

    
735
/* Returns status of security check temporary disable. */
736
function security_checks_disabled() {
737
	global $g;
738
	return file_exists("{$g['tmp_path']}/disable_security_checks");
739
}
740

    
741
function config_validate($conffile) {
742

    
743
	global $g, $xmlerr;
744

    
745
	$xml_parser = xml_parser_create();
746

    
747
	if (!($fp = fopen($conffile, "r"))) {
748
		$xmlerr = gettext("XML error: unable to open file");
749
		return false;
750
	}
751

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

    
762
	fclose($fp);
763

    
764
	return true;
765
}
766

    
767
function cleanup_backupcache($lock = false) {
768
	global $config, $g;
769
	$i = false;
770

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

    
773
	if (!$lock) {
774
		$lockkey = lock('config');
775
	}
776

    
777

    
778
	$backups = get_backups();
779
	if ($backups) {
780
		$baktimes = $backups['versions'];
781
		unset($backups['versions']);
782
	} else {
783
		$backups = array();
784
		$baktimes = array();
785
	}
786
	$newbaks = array();
787
	$bakfiles = glob($g['cf_conf_path'] . "/backup/config-*");
788
	$tocache = array();
789

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

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

    
855
	if (!$lock) {
856
		unlock($lockkey);
857
	}
858
}
859

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

    
878
function backup_config() {
879
	global $config, $g;
880

    
881

    
882
	/* Create backup directory if needed */
883
	safe_mkdir("{$g['cf_conf_path']}/backup");
884
	if ($config['revision']['time'] == "") {
885
		$baktime = 0;
886
	} else {
887
		$baktime = $config['revision']['time'];
888
	}
889

    
890
	if ($config['revision']['description'] == "") {
891
		$bakdesc = "Unknown";
892
	} else {
893
		$bakdesc = $config['revision']['description'];
894
	}
895

    
896
	$bakver = ($config['version'] == "") ? "?" : $config['version'];
897
	$bakfilename = $g['cf_conf_path'] . '/backup/config-' . $baktime . '.xml';
898
	copy($g['cf_conf_path'] . '/config.xml', $bakfilename);
899

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

    
911

    
912
	return true;
913
}
914

    
915
function set_device_perms() {
916
	$devices = array(
917
		'pf' => array(
918
			'user' => 'root',
919
			'group' => 'proxy',
920
			'mode' => 0660),
921
		);
922

    
923
	foreach ($devices as $name => $attr) {
924
		$path = "/dev/$name";
925
		if (file_exists($path)) {
926
			chown($path, $attr['user']);
927
			chgrp($path, $attr['group']);
928
			chmod($path, $attr['mode']);
929
		}
930
	}
931
}
932

    
933
function get_config_user() {
934
	if (empty($_SESSION["Username"])) {
935
		$username = getenv("USER");
936
		if (empty($conuser) || $conuser == "root") {
937
			$username = "(system)";
938
		}
939
	} else {
940
		$username = $_SESSION["Username"];
941
	}
942

    
943
	if (!empty($_SERVER['REMOTE_ADDR'])) {
944
		$username .= '@' . get_user_remote_address() . get_user_remote_authsource();
945
	}
946

    
947
	return $username;
948
}
949

    
950
function make_config_revision_entry($desc = null, $override_user = null) {
951
	if (empty($override_user)) {
952
		$username = get_config_user();
953
	} else {
954
		$username = $override_user;
955
	}
956

    
957
	$revision = array();
958

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

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

    
974
function pfSense_clear_globals() {
975
	global $config, $FilterIfList, $GatewaysList, $filterdns, $aliases, $aliastable;
976

    
977
	$error = error_get_last();
978

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

    
995
	if (isset($FilterIfList)) {
996
		unset($FilterIfList);
997
	}
998

    
999
	if (isset($GatewaysList)) {
1000
		unset($GatewaysList);
1001
	}
1002

    
1003
	/* Used for the hostname dns resolver */
1004
	if (isset($filterdns)) {
1005
		unset($filterdns);
1006
	}
1007

    
1008
	/* Used for aliases and interface macros */
1009
	if (isset($aliases)) {
1010
		unset($aliases);
1011
	}
1012
	if (isset($aliastable)) {
1013
		unset($aliastable);
1014
	}
1015

    
1016
	unset($config);
1017
}
1018

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

    
1037
register_shutdown_function('pfSense_clear_globals');
1038

    
1039
?>
(11-11/59)