Project

General

Profile

Download (29.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-2019 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
	/* XXX Re-add pfSense_fsync() call here after it's fixed */
438
	// if (!pfSense_fsync($tmp_file) || !rename($tmp_file, $file)) {
439
	if (!rename($tmp_file, $file)) {
440
		// Unable to move temporary file to original
441
		@unlink($tmp_file);
442
		return false;
443
	}
444

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

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

    
467
	if (!empty($_SERVER['REMOTE_ADDR'])) {
468
		@phpsession_begin();
469
		if (!empty($_SESSION['Username']) && ($_SESSION['Username'] != "admin")) {
470
			$user = getUserEntry($_SESSION['Username']);
471
			if (is_array($user) && userHasPrivilege($user, "user-config-readonly")) {
472
				syslog(LOG_AUTHPRIV, sprintf(gettext("Save config permission denied by the 'User - Config: Deny Config Write' permission for user '%s'."), get_config_user()));
473
				phpsession_end(true);
474
				return false;
475
			}
476
		}
477
		if (!isset($argc)) {
478
			phpsession_end(true);
479
		}
480
	}
481

    
482
	if (isset($config['reset_factory_defaults'])) {
483
		/*
484
		   We have put a default config.xml on disk and are about to reboot
485
		   or reload it. Do not let any system or package code try to save
486
		   state to config because that would overwrite the default config
487
		   with the running config.
488
		*/
489
		return false;
490
	}
491

    
492
	if ($backup) {
493
		backup_config();
494
	}
495

    
496
	$config['revision'] = make_config_revision_entry($desc);
497

    
498
	$lockkey = lock('config', LOCK_EX);
499

    
500
	/* generate configuration XML */
501
	$xmlconfig = dump_xml_config($config, $g['xml_rootobj']);
502

    
503
	/* write new configuration */
504
	if (!safe_write_file("{$g['cf_conf_path']}/config.xml", $xmlconfig)) {
505
		log_error(gettext("WARNING: Config contents could not be saved. Could not open file!"));
506
		unlock($lockkey);
507
		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"));
508
		return -1;
509
	}
510

    
511
	cleanup_backupcache(true);
512

    
513
	/* re-read configuration */
514
	/* NOTE: We assume that the file can be parsed since we wrote it. */
515
	$config = parse_xml_config("{$g['conf_path']}/config.xml", $g['xml_rootobj']);
516
	if ($config == -1) {
517
		copy("{$g['conf_path']}/config.xml", "{$g['conf_path']}/config.xml.bad");
518
		$last_backup = discover_last_backup();
519
		if ($last_backup) {
520
			restore_backup("/cf/conf/backup/{$last_backup}");
521
			$config = parse_xml_config("{$g['conf_path']}/config.xml", $g['xml_rootobj']);
522
			if (platform_booting()) {
523
				echo "\n\n ************** WARNING **************";
524
				echo "\n\n Configuration could not be validated. A previous configuration was restored. \n";
525
				echo "\n The failed configuration file has been saved as {$g['conf_path']}/config.xml.bad \n\n";
526
			}
527
		} else {
528
			log_error(gettext("Could not restore config.xml."));
529
		}
530
	} else {
531
		generate_config_cache($config);
532
	}
533

    
534
	unlock($lockkey);
535

    
536
	if ($write_config_only) {
537
		return $config;
538
	}
539

    
540
	unlink_if_exists("/usr/local/pkg/pf/carp_sync_client.php");
541

    
542
	/* sync carp entries to other firewalls */
543
	carp_sync_client();
544

    
545
	if (is_dir("/usr/local/pkg/write_config")) {
546
		/* process packager manager custom rules */
547
		run_plugins("/usr/local/pkg/write_config/");
548
	}
549

    
550
	// Try the core AutoConfigBackup system
551
	if (is_array($config['system']['acb']) && $config['system']['acb']['enable'] == "yes" &&
552
	    (!isset($config['system']['acb']['frequency']) || $config['system']['acb']['frequency'] == "every") || file_exists("/tmp/forceacb")) {
553
		require_once("acb.inc");
554
		upload_config();
555
		if (file_exists("/tmp/forceacb")) {
556
			unlink("/tmp/forceacb");
557
		}
558
	}
559

    
560
	return $config;
561
}
562

    
563
/****f* config/reset_factory_defaults
564
 * NAME
565
 *   reset_factory_defaults - Reset the system to its default configuration.
566
 * RESULT
567
 *   integer	- indicates completion
568
 ******/
569
function reset_factory_defaults($lock = false, $reboot_required = true) {
570
	global $config, $g;
571

    
572
	/* Remove all additional packages */
573
	mwexec("/bin/sh /usr/local/sbin/{$g['product_name']}-upgrade " .
574
	    "-r ALL_PACKAGES -f");
575

    
576
	if (!$lock) {
577
		$lockkey = lock('config', LOCK_EX);
578
	}
579

    
580
	/* create conf directory, if necessary */
581
	safe_mkdir($g['cf_conf_path']);
582

    
583
	/* clear out /conf */
584
	$dh = opendir($g['conf_path']);
585
	while ($filename = readdir($dh)) {
586
		if (($filename != ".") && ($filename != "..") &&
587
		    (!is_dir($g['conf_path'] . "/" . $filename))) {
588
			unlink_if_exists($g['conf_path'] . "/" . $filename);
589
		}
590
	}
591
	closedir($dh);
592
	unlink_if_exists($g['tmp_path'] . "/config.cache");
593

    
594
	/* copy default configuration */
595
	copy("{$g['conf_default_path']}/config.xml",
596
	    "{$g['cf_conf_path']}/config.xml");
597

    
598
	disable_security_checks();
599

    
600
	/*
601
	   Let write_config know that we are awaiting reload of the current config
602
	   to factory defaults. Either the system is about to reboot, throwing away
603
	   the current in-memory config as it shuts down, or the in-memory config
604
	   is about to be reloaded on-the-fly by parse_config.
605

    
606
	   In both cases, we want to ensure that write_config does not flush the
607
	   in-memory config back to disk.
608
	*/
609
	$config['reset_factory_defaults'] = true;
610

    
611
	/* call the wizard */
612
	if ($reboot_required) {
613
		// If we need a reboot first then touch a different trigger file.
614
		touch("/conf/trigger_initial_wizard_after_reboot");
615
	} else {
616
		touch("/conf/trigger_initial_wizard");
617
	}
618
	if (!$lock) {
619
		unlock($lockkey);
620
	}
621
	console_configure();
622
	return 0;
623
}
624

    
625
function config_restore($conffile) {
626
	global $config, $g;
627

    
628
	if (!file_exists($conffile)) {
629
		return 1;
630
	}
631

    
632
	backup_config();
633

    
634

    
635
	$lockkey = lock('config', LOCK_EX);
636

    
637
	unlink_if_exists("{$g['tmp_path']}/config.cache");
638
	copy($conffile, "{$g['cf_conf_path']}/config.xml");
639

    
640
	disable_security_checks();
641

    
642
	unlock($lockkey);
643

    
644
	$config = parse_config(true);
645

    
646

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

    
649
	return 0;
650
}
651

    
652
function config_install($conffile) {
653
	global $config, $g;
654

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

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

    
663
	if (platform_booting()) {
664
		echo gettext("Installing configuration...") . "\n";
665
	} else {
666
		log_error(gettext("Installing configuration ...."));
667
	}
668

    
669
	$lockkey = lock('config', LOCK_EX);
670

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

    
673
	disable_security_checks();
674

    
675
	/* unlink cache file if it exists */
676
	if (file_exists("{$g['tmp_path']}/config.cache")) {
677
		unlink("{$g['tmp_path']}/config.cache");
678
	}
679

    
680
	unlock($lockkey);
681

    
682
	return 0;
683
}
684

    
685
/*
686
 * Disable security checks for DNS rebind and HTTP referrer until next time
687
 * they pass (or reboot), to aid in preventing accidental lockout when
688
 * restoring settings like hostname, domain, IP addresses, and settings
689
 * related to the DNS rebind and HTTP referrer checks.
690
 * Intended for use when restoring a configuration or directly
691
 * modifying config.xml without an unconditional reboot.
692
 */
693
function disable_security_checks() {
694
	global $g;
695
	touch("{$g['tmp_path']}/disable_security_checks");
696
}
697

    
698
/* Restores security checks.  Should be called after all succeed. */
699
function restore_security_checks() {
700
	global $g;
701
	unlink_if_exists("{$g['tmp_path']}/disable_security_checks");
702
}
703

    
704
/* Returns status of security check temporary disable. */
705
function security_checks_disabled() {
706
	global $g;
707
	return file_exists("{$g['tmp_path']}/disable_security_checks");
708
}
709

    
710
function config_validate($conffile) {
711

    
712
	global $g, $xmlerr;
713

    
714
	$xml_parser = xml_parser_create();
715

    
716
	if (!($fp = fopen($conffile, "r"))) {
717
		$xmlerr = gettext("XML error: unable to open file");
718
		return false;
719
	}
720

    
721
	while ($data = fread($fp, 4096)) {
722
		if (!xml_parse($xml_parser, $data, feof($fp))) {
723
			$xmlerr = sprintf(gettext('%1$s at line %2$d'),
724
						xml_error_string(xml_get_error_code($xml_parser)),
725
						xml_get_current_line_number($xml_parser));
726
			return false;
727
		}
728
	}
729
	xml_parser_free($xml_parser);
730

    
731
	fclose($fp);
732

    
733
	return true;
734
}
735

    
736
function cleanup_backupcache($lock = false) {
737
	global $config, $g;
738
	$i = false;
739

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

    
742
	if (!$lock) {
743
		$lockkey = lock('config');
744
	}
745

    
746

    
747
	$backups = get_backups();
748
	if ($backups) {
749
		$baktimes = $backups['versions'];
750
		unset($backups['versions']);
751
	} else {
752
		$backups = array();
753
		$baktimes = array();
754
	}
755
	$newbaks = array();
756
	$bakfiles = glob($g['cf_conf_path'] . "/backup/config-*");
757
	$tocache = array();
758

    
759
	foreach ($bakfiles as $backup) { // Check for backups in the directory not represented in the cache.
760
		$backupsize = filesize($backup);
761
		if ($backupsize == 0) {
762
			unlink($backup);
763
			continue;
764
		}
765
		$backupexp = explode('-', $backup);
766
		$backupexp = explode('.', array_pop($backupexp));
767
		$tocheck = array_shift($backupexp);
768
		unset($backupexp);
769
		if (!in_array($tocheck, $baktimes)) {
770
			$i = true;
771
			if (platform_booting()) {
772
				echo ".";
773
			}
774
			try {
775
				$newxml = parse_xml_config($backup, array($g['xml_rootobj'], 'pfsense'));
776
			} catch (Exception $exc) {
777
				log_error(sprintf(gettext("The backup cache file %s is corrupted. Parser error message: %s"), $backup, $exc->getMessage()));
778
				$newxml = "-1";
779
			}
780

    
781
			if ($newxml == "-1") {
782
				log_error(sprintf(gettext("The backup cache file %s is corrupted.  Unlinking."), $backup));
783
				unlink($backup);
784
				continue;
785
			}
786
			if ($newxml['revision']['description'] == "") {
787
				$newxml['revision']['description'] = "Unknown";
788
			}
789
			if ($newxml['version'] == "") {
790
				$newxml['version'] = "?";
791
			}
792
			$tocache[$tocheck] = array('description' => $newxml['revision']['description'], 'version' => $newxml['version'], 'filesize' => $backupsize);
793
		}
794
	}
795
	foreach ($backups as $checkbak) {
796
		if (count(preg_grep('/' . $checkbak['time'] . '/i', $bakfiles)) != 0) {
797
			$newbaks[] = $checkbak;
798
		} else {
799
			$i = true;
800
			if (platform_booting()) print " " . $tocheck . "r";
801
		}
802
	}
803
	foreach ($newbaks as $todo) {
804
		$tocache[$todo['time']] = array('description' => $todo['description'], 'version' => $todo['version'], 'filesize' => $todo['filesize']);
805
	}
806
	if (is_int($revisions) and (count($tocache) > $revisions)) {
807
		$toslice = array_slice(array_keys($tocache), 0, $revisions);
808
		$newcache = array();
809
		foreach ($toslice as $sliced) {
810
			$newcache[$sliced] = $tocache[$sliced];
811
		}
812
		foreach ($tocache as $version => $versioninfo) {
813
			if (!in_array($version, array_keys($newcache))) {
814
				unlink_if_exists($g['conf_path'] . '/backup/config-' . $version . '.xml');
815
			}
816
		}
817
		$tocache = $newcache;
818
	}
819
	$bakout = fopen($g['cf_conf_path'] . '/backup/backup.cache', "w");
820
	fwrite($bakout, serialize($tocache));
821
	fclose($bakout);
822
	//pfSense_fsync("{$g['cf_conf_path']}/backup/backup.cache");
823

    
824
	if (!$lock) {
825
		unlock($lockkey);
826
	}
827
}
828

    
829
function get_backups() {
830
	global $g;
831
	if (file_exists("{$g['cf_conf_path']}/backup/backup.cache")) {
832
		$confvers = unserialize(file_get_contents("{$g['cf_conf_path']}/backup/backup.cache"));
833
		$bakvers = array_keys($confvers);
834
		$toreturn = array();
835
		sort($bakvers);
836
		// 	$bakvers = array_reverse($bakvers);
837
		foreach (array_reverse($bakvers) as $bakver) {
838
			$toreturn[] = array('time' => $bakver, 'description' => $confvers[$bakver]['description'], 'version' => $confvers[$bakver]['version'], 'filesize' => $confvers[$bakver]['filesize']);
839
		}
840
	} else {
841
		return false;
842
	}
843
	$toreturn['versions'] = $bakvers;
844
	return $toreturn;
845
}
846

    
847
function backup_config() {
848
	global $config, $g;
849

    
850

    
851
	/* Create backup directory if needed */
852
	safe_mkdir("{$g['cf_conf_path']}/backup");
853
	if ($config['revision']['time'] == "") {
854
		$baktime = 0;
855
	} else {
856
		$baktime = $config['revision']['time'];
857
	}
858

    
859
	if ($config['revision']['description'] == "") {
860
		$bakdesc = "Unknown";
861
	} else {
862
		$bakdesc = $config['revision']['description'];
863
	}
864

    
865
	$bakver = ($config['version'] == "") ? "?" : $config['version'];
866
	$bakfilename = $g['cf_conf_path'] . '/backup/config-' . $baktime . '.xml';
867
	copy($g['cf_conf_path'] . '/config.xml', $bakfilename);
868

    
869
	if (file_exists($g['cf_conf_path'] . '/backup/backup.cache')) {
870
		$backupcache = unserialize(file_get_contents($g['cf_conf_path'] . '/backup/backup.cache'));
871
	} else {
872
		$backupcache = array();
873
	}
874
	$backupcache[$baktime] = array('description' => $bakdesc, 'version' => $bakver, 'filesize' => filesize($bakfilename));
875
	$bakout = fopen($g['cf_conf_path'] . '/backup/backup.cache', "w");
876
	fwrite($bakout, serialize($backupcache));
877
	fclose($bakout);
878
	//pfSense_fsync("{$g['cf_conf_path']}/backup/backup.cache");
879

    
880

    
881
	return true;
882
}
883

    
884
function set_device_perms() {
885
	$devices = array(
886
		'pf' => array(
887
			'user' => 'root',
888
			'group' => 'proxy',
889
			'mode' => 0660),
890
		);
891

    
892
	foreach ($devices as $name => $attr) {
893
		$path = "/dev/$name";
894
		if (file_exists($path)) {
895
			chown($path, $attr['user']);
896
			chgrp($path, $attr['group']);
897
			chmod($path, $attr['mode']);
898
		}
899
	}
900
}
901

    
902
function get_config_user() {
903
	if (empty($_SESSION["Username"])) {
904
		$username = getenv("USER");
905
		if (empty($conuser) || $conuser == "root") {
906
			$username = "(system)";
907
		}
908
	} else {
909
		$username = $_SESSION["Username"];
910
	}
911

    
912
	if (!empty($_SERVER['REMOTE_ADDR'])) {
913
		$username .= '@' . get_user_remote_address() . get_user_remote_authsource();
914
	}
915

    
916
	return $username;
917
}
918

    
919
function make_config_revision_entry($desc = null, $override_user = null) {
920
	if (empty($override_user)) {
921
		$username = get_config_user();
922
	} else {
923
		$username = $override_user;
924
	}
925

    
926
	$revision = array();
927

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

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

    
943
function pfSense_clear_globals() {
944
	global $config, $FilterIfList, $GatewaysList, $filterdns, $aliases, $aliastable;
945

    
946
	$error = error_get_last();
947

    
948
	// Errors generated by user code (diag_commands.php) are identified by path and not added to notices
949
	if ($error !== NULL && !preg_match('|^' . preg_quote($g['tmp_path_user_code']) . '/[^/]{1,16}$|', $error['file'])) {
950
		if (in_array($error['type'], array(E_ERROR, E_COMPILE_ERROR, E_CORE_ERROR, E_RECOVERABLE_ERROR))) {
951
			$errorstr = "PHP ERROR: Type: {$error['type']}, File: {$error['file']}, Line: {$error['line']}, Message: {$error['message']}";
952
			print($errorstr);
953
			log_error($errorstr);
954
			file_notice("phperror", $errorstr, 'PHP errors');
955
		} else if ($error['type'] != E_NOTICE) {
956
			$errorstr = "PHP WARNING: Type: {$error['type']}, File: {$error['file']}, Line: {$error['line']}, Message: {$error['message']}";
957
			// XXX: comment out for now, should re-enable post-2.2
958
			//print($errorstr);
959
			//log_error($errorstr);
960
			//file_notice("phpwarning", $errorstr, 'PHP warning');
961
		}
962
	}
963

    
964
	if (isset($FilterIfList)) {
965
		unset($FilterIfList);
966
	}
967

    
968
	if (isset($GatewaysList)) {
969
		unset($GatewaysList);
970
	}
971

    
972
	/* Used for the hostname dns resolver */
973
	if (isset($filterdns)) {
974
		unset($filterdns);
975
	}
976

    
977
	/* Used for aliases and interface macros */
978
	if (isset($aliases)) {
979
		unset($aliases);
980
	}
981
	if (isset($aliastable)) {
982
		unset($aliastable);
983
	}
984

    
985
	unset($config);
986
}
987

    
988
/* Initialize a config array multiple levels deep only if unset
989
 * Pass it an array of keys to test and create
990
 * init_config_arr(array('virtualip', 'vip'));
991
 */
992
function init_config_arr($keys) {
993
	global $config;
994
	$c = &$config;
995
	if (!is_array($keys)) {
996
		return null;
997
	}
998
	foreach ($keys as $k) {
999
		if (!is_array($c[$k])) {
1000
			$c[$k] = array();
1001
		}
1002
		$c = &$c[$k];
1003
	}
1004
}
1005

    
1006
register_shutdown_function('pfSense_clear_globals');
1007

    
1008
?>
(11-11/59)