Project

General

Profile

Download (31.9 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-2016 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
 * Redistribution and use in source and binary forms, with or without
15
 * modification, are permitted provided that the following conditions are met:
16
 *
17
 * 1. Redistributions of source code must retain the above copyright notice,
18
 *    this list of conditions and the following disclaimer.
19
 *
20
 * 2. Redistributions in binary form must reproduce the above copyright
21
 *    notice, this list of conditions and the following disclaimer in
22
 *    the documentation and/or other materials provided with the
23
 *    distribution.
24
 *
25
 * 3. All advertising materials mentioning features or use of this software
26
 *    must display the following acknowledgment:
27
 *    "This product includes software developed by the pfSense Project
28
 *    for use in the pfSense® software distribution. (http://www.pfsense.org/).
29
 *
30
 * 4. The names "pfSense" and "pfSense Project" must not be used to
31
 *    endorse or promote products derived from this software without
32
 *    prior written permission. For written permission, please contact
33
 *    coreteam@pfsense.org.
34
 *
35
 * 5. Products derived from this software may not be called "pfSense"
36
 *    nor may "pfSense" appear in their names without prior written
37
 *    permission of the Electric Sheep Fencing, LLC.
38
 *
39
 * 6. Redistributions of any form whatsoever must retain the following
40
 *    acknowledgment:
41
 *
42
 * "This product includes software developed by the pfSense Project
43
 * for use in the pfSense software distribution (http://www.pfsense.org/).
44
 *
45
 * THIS SOFTWARE IS PROVIDED BY THE pfSense PROJECT ``AS IS'' AND ANY
46
 * EXPRESSED OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
47
 * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR
48
 * PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE pfSense PROJECT OR
49
 * ITS CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL,
50
 * SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT
51
 * NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES;
52
 * LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
53
 * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT,
54
 * STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE)
55
 * ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED
56
 * OF THE POSSIBILITY OF SUCH DAMAGE.
57
 */
58

    
59
/****f* config/encrypted_configxml
60
 * NAME
61
 *   encrypted_configxml - Checks to see if config.xml is encrypted and if so, prompts to unlock.
62
 * INPUTS
63
 *   None
64
 * RESULT
65
 *   $config 	- rewrites config.xml without encryption
66
 ******/
67
function encrypted_configxml() {
68
	global $g, $config;
69

    
70
	if (!file_exists($g['conf_path'] . "/config.xml")) {
71
		return;
72
	}
73

    
74
	if (!platform_booting()) {
75
		return;
76
	}
77

    
78
	$configtxt = file_get_contents($g['conf_path'] . "/config.xml");
79
	if (tagfile_deformat($configtxt, $configtxt, "config.xml")) {
80
		$fp = fopen('php://stdin', 'r');
81
		$data = "";
82
		echo "\n\n*** Encrypted config.xml detected ***\n";
83
		while ($data == "") {
84
			echo "\nEnter the password to decrypt config.xml: ";
85
			$decrypt_password = chop(fgets($fp));
86
			$data = decrypt_data($configtxt, $decrypt_password);
87
			if (!strstr($data, "<pfsense>")) {
88
				$data = "";
89
			}
90
			if ($data) {
91
				$fd = fopen($g['conf_path'] . "/config.xml.tmp", "w");
92
				fwrite($fd, $data);
93
				fclose($fd);
94
				exec("/bin/mv {$g['conf_path']}/config.xml.tmp {$g['conf_path']}/config.xml");
95
				echo "\n" . gettext("Config.xml unlocked.") . "\n";
96
				fclose($fp);
97
				pfSense_fsync("{$g['conf_path']}/config.xml");
98
			} else {
99
				echo "\n" . gettext("Invalid password entered.  Please try again.") . "\n";
100
			}
101
		}
102
	}
103
}
104

    
105
/****f* config/parse_config
106
 * NAME
107
 *   parse_config - Read in config.cache or config.xml if needed and return $config array
108
 * INPUTS
109
 *   $parse       - boolean to force parse_config() to read config.xml and generate config.cache
110
 * RESULT
111
 *   $config      - array containing all configuration variables
112
 ******/
113
function parse_config($parse = false) {
114
	global $g, $config_parsed, $config_extra;
115

    
116
	$lockkey = lock('config');
117
	$config_parsed = false;
118

    
119
	if (!file_exists("{$g['conf_path']}/config.xml") || filesize("{$g['conf_path']}/config.xml") == 0) {
120
		$last_backup = discover_last_backup();
121
		if ($last_backup) {
122
			log_error(gettext("No config.xml found, attempting last known config restore."));
123
			file_notice("config.xml", gettext("No config.xml found, attempting last known config restore."), "pfSenseConfigurator", "");
124
			restore_backup("{$g['conf_path']}/backup/{$last_backup}");
125
		} else {
126
			unlock($lockkey);
127
			die(gettext("Config.xml is corrupted and is 0 bytes.  Could not restore a previous backup."));
128
		}
129
	}
130

    
131
	if (platform_booting(true)) {
132
		echo ".";
133
	}
134

    
135
	// Check for encrypted config.xml
136
	encrypted_configxml();
137

    
138
	if (!$parse) {
139
		if (file_exists($g['tmp_path'] . '/config.cache')) {
140
			$config = unserialize(file_get_contents($g['tmp_path'] . '/config.cache'));
141
			if (!is_array($config)) {
142
				$parse = true;
143
			}
144
		} else {
145
			$parse = true;
146
		}
147
	}
148
	if ($parse == true) {
149
		if (!file_exists($g['conf_path'] . "/config.xml")) {
150
			if (platform_booting(true)) {
151
				echo ".";
152
			}
153
			log_error("No config.xml found, attempting last known config restore.");
154
			file_notice("config.xml", "No config.xml found, attempting last known config restore.", "pfSenseConfigurator", "");
155
			$last_backup = discover_last_backup();
156
			if ($last_backup) {
157
				restore_backup("/cf/conf/backup/{$last_backup}");
158
			} else {
159
				log_error(gettext("Could not restore config.xml."));
160
				unlock($lockkey);
161
				die(gettext("Config.xml is corrupted and is 0 bytes.  Could not restore a previous backup."));
162
			}
163
		}
164
		$config = parse_xml_config($g['conf_path'] . '/config.xml', array($g['xml_rootobj'], 'pfsense'));
165
		if ($config == -1) {
166
			$last_backup = discover_last_backup();
167
			if ($last_backup) {
168
				restore_backup("/cf/conf/backup/{$last_backup}");
169
			} else {
170
				log_error(gettext("Could not restore config.xml."));
171
				unlock($lockkey);
172
				die("Config.xml is corrupted and is 0 bytes.  Could not restore a previous backup.");
173
			}
174
		}
175
		generate_config_cache($config);
176
	}
177

    
178
	if (platform_booting(true)) {
179
		echo ".";
180
	}
181

    
182
	$config_parsed = true;
183
	unlock($lockkey);
184

    
185
	alias_make_table($config);
186

    
187
	return $config;
188
}
189

    
190
/****f* config/generate_config_cache
191
 * NAME
192
 *   generate_config_cache - Write serialized configuration to cache.
193
 * INPUTS
194
 *   $config	- array containing current firewall configuration
195
 * RESULT
196
 *   boolean	- true on completion
197
 ******/
198
function generate_config_cache($config) {
199
	global $g, $config_extra;
200

    
201
	$configcache = fopen($g['tmp_path'] . '/config.cache', "w");
202
	fwrite($configcache, serialize($config));
203
	fclose($configcache);
204
	pfSense_fsync("{$g['tmp_path']}/config.cache");
205

    
206
	unset($configcache);
207
	/* Used for config.extra.xml */
208
	if (file_exists($g['tmp_path'] . '/config.extra.cache') && $config_extra) {
209
		$configcacheextra = fopen($g['tmp_path'] . '/config.extra.cache', "w");
210
		fwrite($configcacheextra, serialize($config_extra));
211
		fclose($configcacheextra);
212
		pfSense_fsync("{$g['tmp_path']}/config.extra.cache");
213
		unset($configcacheextra);
214
	}
215
}
216

    
217
function discover_last_backup() {
218
	$backups = glob('/cf/conf/backup/*.xml');
219
	$last_backup = "";
220
	$last_mtime = 0;
221
	foreach ($backups as $backup) {
222
		if (filemtime($backup) > $last_mtime) {
223
			$last_mtime = filemtime($backup);
224
			$last_backup = $backup;
225
		}
226
	}
227

    
228
	return basename($last_backup);
229
}
230

    
231
function restore_backup($file) {
232
	global $g;
233

    
234
	if (file_exists($file)) {
235
		conf_mount_rw();
236
		unlink_if_exists("{$g['tmp_path']}/config.cache");
237
		copy("$file", "/cf/conf/config.xml");
238
		pfSense_fsync("/cf/conf/config.xml");
239
		pfSense_fsync($g['conf_path']);
240
		disable_security_checks();
241
		log_error(sprintf(gettext('%1$s is restoring the configuration %2$s'), $g['product_name'], $file));
242
		file_notice("config.xml", sprintf(gettext('%1$s is restoring the configuration %2$s'), $g['product_name'], $file), "pfSenseConfigurator", "");
243
		conf_mount_ro();
244
	}
245
}
246

    
247
/****f* config/parse_config_bootup
248
 * NAME
249
 *   parse_config_bootup - Bootup-specific configuration checks.
250
 * RESULT
251
 *   null
252
 ******/
253
function parse_config_bootup() {
254
	global $config, $g;
255

    
256
	if (platform_booting()) {
257
		echo ".";
258
	}
259

    
260
	$lockkey = lock('config');
261
	if (!file_exists("{$g['conf_path']}/config.xml")) {
262
		if (platform_booting()) {
263
			if (strstr($g['platform'], "cdrom")) {
264
				/* try copying the default config. to the floppy */
265
				echo gettext("Resetting factory defaults...") . "\n";
266
				reset_factory_defaults(true, false);
267
				if (!file_exists("{$g['conf_path']}/config.xml")) {
268
					echo gettext("No XML configuration file found - using factory defaults.\n" .
269
								 "Make sure that the configuration floppy disk with the conf/config.xml\n" .
270
								 "file is inserted. If it isn't, the configuration changes will be lost\n" .
271
								 "on reboot.\n");
272
				}
273
			} else {
274
				$last_backup = discover_last_backup();
275
				if ($last_backup) {
276
					log_error("No config.xml found, attempting last known config restore.");
277
					file_notice("config.xml", gettext("No config.xml found, attempting last known config restore."), "pfSenseConfigurator", "");
278
					restore_backup("/cf/conf/backup/{$last_backup}");
279
				}
280
				if (!file_exists("{$g['conf_path']}/config.xml")) {
281
					echo sprintf(gettext("XML configuration file not found.  %s cannot continue booting."), $g['product_name']) . "\n";
282
					unlock($lockkey);
283
					mwexec("/sbin/halt");
284
					exit;
285
				}
286
				log_error("Last known config found and restored.  Please double check the configuration file for accuracy.");
287
				file_notice("config.xml", gettext("Last known config found and restored.  Please double check the configuration file for accuracy."), "pfSenseConfigurator", "");
288
			}
289
		} else {
290
			unlock($lockkey);
291
			log_error(gettext("Could not find a usable configuration file! Exiting...."));
292
			exit(0);
293
		}
294
	}
295

    
296
	if (filesize("{$g['conf_path']}/config.xml") == 0) {
297
		$last_backup = discover_last_backup();
298
		if ($last_backup) {
299
			log_error(gettext("No config.xml found, attempting last known config restore."));
300
			file_notice("config.xml", gettext("No config.xml found, attempting last known config restore."), "pfSenseConfigurator", "");
301
			restore_backup("{$g['conf_path']}/backup/{$last_backup}");
302
		} else {
303
			unlock($lockkey);
304
			die(gettext("Config.xml is corrupted and is 0 bytes.  Could not restore a previous backup."));
305
		}
306
	}
307
	unlock($lockkey);
308

    
309
	$config = parse_config(true);
310

    
311
	if ((float)$config['version'] > (float)$g['latest_config']) {
312
		echo <<<EOD
313

    
314

    
315
*******************************************************************************
316
* WARNING!                                                                    *
317
* The current configuration has been created with a newer version of {$g['product_name']}  *
318
* than this one! This can lead to serious misbehavior and even security       *
319
* holes! You are urged to either upgrade to a newer version of {$g['product_name']} or     *
320
* revert to the default configuration immediately!                            *
321
*******************************************************************************
322

    
323

    
324
EOD;
325
		}
326

    
327
	/* make alias table (for faster lookups) */
328
	alias_make_table($config);
329
}
330

    
331
/****f* config/conf_mount_rw
332
 * NAME
333
 *   conf_mount_rw - Mount filesystems read/write.
334
 * RESULT
335
 *   null
336
 ******/
337
/* mount flash card read/write */
338
function conf_mount_rw() {
339
	global $g, $config;
340

    
341
	/* do not mount on cdrom platform */
342
	if ($g['platform'] == "cdrom" or $g['platform'] == $g['product_name']) {
343
		return;
344
	}
345

    
346
	if ((refcount_reference(1000) > 1) && is_writable("/")) {
347
		return;
348
	}
349

    
350
	$status = mwexec("/sbin/mount -u -w -o sync,noatime {$g['cf_path']}");
351
	if ($status <> 0) {
352
		if (platform_booting()) {
353
			echo gettext("/cf Filesystem is dirty.") . "\n";
354
		}
355
		$status = mwexec("/sbin/mount -u -w -o sync,noatime {$g['cf_path']}");
356
	}
357

    
358
	/*    if the platform is soekris or wrap or $product, lets mount the
359
	 *    compact flash cards root.
360
	*/
361
	$status = mwexec("/sbin/mount -u -w -o sync,noatime /");
362
	/* we could not mount this correctly. */
363
	if ($status <> 0) {
364
		log_error(gettext("/ File system is dirty."));
365
		$status = mwexec("/sbin/mount -u -w -o sync,noatime /");
366
	}
367

    
368
	mark_subsystem_dirty('mount');
369
}
370

    
371
/****f* config/conf_mount_ro
372
 * NAME
373
 *   conf_mount_ro - Mount filesystems readonly.
374
 * RESULT
375
 *   null
376
 ******/
377
function conf_mount_ro() {
378
	global $g, $config;
379

    
380
	// do nothing here. redmine #6184
381
	return;
382

    
383
	/* Do not trust $g['platform'] since this can be clobbered during factory reset. */
384
	$platform = trim(file_get_contents("/etc/platform"));
385
	/* do not umount on cdrom or pfSense platforms */
386
	if ($platform == "cdrom" or $platform == $g['product_name']) {
387
		return;
388
	}
389

    
390
	if (refcount_unreference(1000) > 0) {
391
		return;
392
	}
393

    
394
	if (isset($config['system']['nanobsd_force_rw'])) {
395
		return;
396
	}
397

    
398
	if (platform_booting()) {
399
		return;
400
	}
401

    
402
	clear_subsystem_dirty('mount');
403
	/* sync data, then force a remount of /cf */
404
	pfSense_fsync($g['cf_path']);
405
	mwexec("/sbin/mount -u -r -f -o sync,noatime {$g['cf_path']}");
406
	mwexec("/sbin/mount -u -r -f -o sync,noatime /");
407
}
408

    
409
/****f* config/convert_config
410
 * NAME
411
 *   convert_config - Attempt to update config.xml.
412
 * DESCRIPTION
413
 *   convert_config() reads the current global configuration
414
 *   and attempts to convert it to conform to the latest
415
 *   config.xml version. This allows major formatting changes
416
 *   to be made with a minimum of breakage.
417
 * RESULT
418
 *   null
419
 ******/
420
/* convert configuration, if necessary */
421
function convert_config() {
422
	global $config, $g;
423
	$now = date("H:i:s");
424
	log_error(sprintf(gettext("Start Configuration upgrade at %s, set execution timeout to 15 minutes"), $now));
425
	//ini_set("max_execution_time", "900");
426

    
427
	/* special case upgrades */
428
	/* fix every minute crontab bogons entry */
429
	if (is_array($config['cron'])) {
430
		$cron_item_count = count($config['cron']['item']);
431
		for ($x = 0; $x < $cron_item_count; $x++) {
432
			if (stristr($config['cron']['item'][$x]['command'], "rc.update_bogons.sh")) {
433
				if ($config['cron']['item'][$x]['hour'] == "*") {
434
					$config['cron']['item'][$x]['hour'] = "3";
435
					write_config(gettext("Updated bogon update frequency to 3am"));
436
					log_error(gettext("Updated bogon update frequency to 3am"));
437
				}
438
			}
439
		}
440
	}
441

    
442
	// Save off config version
443
	$prev_version = $config['version'];
444

    
445
	include_once('auth.inc');
446
	include_once('upgrade_config.inc');
447
	if (file_exists("/etc/inc/upgrade_config_custom.inc")) {
448
		include_once("upgrade_config_custom.inc");
449
	}
450

    
451
	if ($config['version'] == $g['latest_config']) {
452
		additional_config_upgrade();
453
		return;		/* already at latest version */
454
	}
455

    
456
	if (!is_array($config['system']['already_run_config_upgrade'])) {
457
		$config['system']['already_run_config_upgrade'] = array();
458
	}
459
	$already_run = $config['system']['already_run_config_upgrade'];
460

    
461
	/* Loop and run upgrade_VER_to_VER() until we're at current version */
462
	while ($config['version'] < $g['latest_config']) {
463
		$cur = $config['version'] * 10;
464
		$next = $cur + 1;
465
		$migration_function = sprintf('upgrade_%03d_to_%03d', $cur,
466
		    $next);
467

    
468
		foreach (array("", "_custom") as $suffix) {
469
			$migration_function .= $suffix;
470
			if (!function_exists($migration_function)) {
471
				continue;
472
			}
473
			if (isset($already_run[$migration_function])) {
474
				/* Already executed, skip now */
475
				unset($config['system']
476
				    ['already_run_config_upgrade']
477
				    [$migration_function]);
478
			} else {
479
				$migration_function();
480
			}
481
		}
482
		$config['version'] = sprintf('%.1f', $next / 10);
483
		if (platform_booting()) {
484
			echo ".";
485
		}
486
	}
487

    
488
	if ($prev_version != $config['version']) {
489
		$now = date("H:i:s");
490
		log_error(sprintf(gettext("Ended Configuration upgrade at %s"), $now));
491

    
492
		write_config(sprintf(gettext('Upgraded config version level from %1$s to %2$s'), $prev_version, $config['version']));
493
	}
494

    
495
	additional_config_upgrade();
496
}
497

    
498
/****f* config/safe_write_file
499
 * NAME
500
 *   safe_write_file - Write a file out atomically
501
 * DESCRIPTION
502
 *   safe_write_file() Writes a file out atomically by first writing to a
503
 *   temporary file of the same name but ending with the pid of the current
504
 *   process, them renaming the temporary file over the original.
505
 * INPUTS
506
 *   $filename  - string containing the filename of the file to write
507
 *   $content   - string or array containing the file content to write to file
508
 *   $force_binary      - boolean denoting whether we should force binary
509
 *   mode writing.
510
 * RESULT
511
 *   boolean - true if successful, false if not
512
 ******/
513
function safe_write_file($file, $content, $force_binary = false) {
514
	$tmp_file = $file . "." . getmypid();
515
	$write_mode = $force_binary ? "wb" : "w";
516

    
517
	$fd = fopen($tmp_file, $write_mode);
518
	if (!$fd) {
519
		// Unable to open temporary file for writing
520
		return false;
521
	}
522
	if (is_array($content)) {
523
		foreach ($content as $line) {
524
			if (!fwrite($fd, $line . "\n")) {
525
				// Unable to write to temporary file
526
				fclose($fd);
527
				return false;
528
			}
529
		}
530
	} elseif (!fwrite($fd, $content)) {
531
		// Unable to write to temporary file
532
		fclose($fd);
533
		return false;
534
	}
535
	fflush($fd);
536
	fclose($fd);
537

    
538
	if (!pfSense_fsync($tmp_file) || !rename($tmp_file, $file)) {
539
		// Unable to move temporary file to original
540
		@unlink($tmp_file);
541
		return false;
542
	}
543

    
544
	// Sync file before returning
545
	return pfSense_fsync($file);
546
}
547

    
548
/****f* config/write_config
549
 * NAME
550
 *   write_config - Backup and write the firewall configuration.
551
 * DESCRIPTION
552
 *   write_config() handles backing up the current configuration,
553
 *   applying changes, and regenerating the configuration cache.
554
 * INPUTS
555
 *   $desc	- string containing the a description of configuration changes
556
 *   $backup	- boolean: do not back up current configuration if false.
557
 *   $write_config_only	- boolean: do not sync or reload anything; just save the configuration if true.
558
 * RESULT
559
 *   null
560
 ******/
561
/* save the system configuration */
562
function write_config($desc="Unknown", $backup = true, $write_config_only = false) {
563
	global $config, $g;
564

    
565
	if (!empty($_SERVER['REMOTE_ADDR'])) {
566
		if (!session_id()) {
567
			@session_start();
568
		}
569
		if (!empty($_SESSION['Username']) && ($_SESSION['Username'] != "admin")) {
570
			$user = getUserEntry($_SESSION['Username']);
571
			if (is_array($user) && userHasPrivilege($user, "user-config-readonly")) {
572
				session_commit();
573
				return false;
574
			}
575
		}
576
	}
577

    
578
	if (!isset($argc)) {
579
		session_commit();
580
	}
581

    
582
	if (isset($config['reset_factory_defaults'])) {
583
		/*
584
		   We have put a default config.xml on disk and are about to reboot
585
		   or reload it. Do not let any system or package code try to save
586
		   state to config because that would overwrite the default config
587
		   with the running config.
588
		*/
589
		return false;
590
	}
591

    
592
	if ($backup) {
593
		backup_config();
594
	}
595

    
596
	$config['revision'] = make_config_revision_entry($desc);
597

    
598
	conf_mount_rw();
599
	$lockkey = lock('config', LOCK_EX);
600

    
601
	/* generate configuration XML */
602
	$xmlconfig = dump_xml_config($config, $g['xml_rootobj']);
603

    
604
	/* write new configuration */
605
	if (!safe_write_file("{$g['cf_conf_path']}/config.xml", $xmlconfig)) {
606
		log_error(gettext("WARNING: Config contents could not be saved. Could not open file!"));
607
		unlock($lockkey);
608
		file_notice("config.xml", sprintf(gettext("Unable to open %s/config.xml for writing in write_config()%s"), $g['cf_conf_path'], "\n"));
609
		return -1;
610
	}
611

    
612
	cleanup_backupcache(true);
613

    
614
	/* re-read configuration */
615
	/* NOTE: We assume that the file can be parsed since we wrote it. */
616
	$config = parse_xml_config("{$g['conf_path']}/config.xml", $g['xml_rootobj']);
617
	if ($config == -1) {
618
		copy("{$g['conf_path']}/config.xml", "{$g['conf_path']}/config.xml.bad");
619
		$last_backup = discover_last_backup();
620
		if ($last_backup) {
621
			restore_backup("/cf/conf/backup/{$last_backup}");
622
			$config = parse_xml_config("{$g['conf_path']}/config.xml", $g['xml_rootobj']);
623
			if (platform_booting()) {
624
				echo "\n\n ************** WARNING **************";
625
				echo "\n\n Configuration could not be validated. A previous configuration was restored. \n";
626
				echo "\n The failed configuration file has been saved as {$g['conf_path']}/config.xml.bad \n\n";
627
			}
628
		} else {
629
			log_error(gettext("Could not restore config.xml."));
630
		}
631
	} else {
632
		generate_config_cache($config);
633
	}
634

    
635
	unlock($lockkey);
636

    
637
	if ($write_config_only) {
638
		/* tell kernel to sync fs data */
639
		conf_mount_ro();
640
		return $config;
641
	}
642

    
643
	unlink_if_exists("/usr/local/pkg/pf/carp_sync_client.php");
644

    
645
	/* tell kernel to sync fs data */
646
	conf_mount_ro();
647

    
648
	/* sync carp entries to other firewalls */
649
	carp_sync_client();
650

    
651
	if (is_dir("/usr/local/pkg/write_config")) {
652
		/* process packager manager custom rules */
653
		run_plugins("/usr/local/pkg/write_config/");
654
	}
655

    
656
	return $config;
657
}
658

    
659
/****f* config/reset_factory_defaults
660
 * NAME
661
 *   reset_factory_defaults - Reset the system to its default configuration.
662
 * RESULT
663
 *   integer	- indicates completion
664
 ******/
665
function reset_factory_defaults($lock = false, $reboot_required = true) {
666
	global $config, $g;
667

    
668
	conf_mount_rw();
669

    
670
	/* Remove all additional packages */
671
	mwexec("/bin/sh /usr/local/sbin/{$g['product_name']}-upgrade " .
672
	    "-r ALL_PACKAGES");
673

    
674
	if (!$lock) {
675
		$lockkey = lock('config', LOCK_EX);
676
	}
677

    
678
	/* create conf directory, if necessary */
679
	safe_mkdir($g['cf_conf_path']);
680

    
681
	/* clear out /conf */
682
	$dh = opendir($g['conf_path']);
683
	while ($filename = readdir($dh)) {
684
		if (($filename != ".") && ($filename != "..") &&
685
		    (!is_dir($g['conf_path'] . "/" . $filename))) {
686
			unlink_if_exists($g['conf_path'] . "/" . $filename);
687
		}
688
	}
689
	closedir($dh);
690
	unlink_if_exists($g['tmp_path'] . "/config.cache");
691

    
692
	/* copy default configuration */
693
	copy("{$g['conf_default_path']}/config.xml",
694
	    "{$g['cf_conf_path']}/config.xml");
695

    
696
	disable_security_checks();
697

    
698
	/*
699
	   Let write_config know that we are awaiting reload of the current config
700
	   to factory defaults. Either the system is about to reboot, throwing away
701
	   the current in-memory config as it shuts down, or the in-memory config
702
	   is about to be reloaded on-the-fly by parse_config.
703

    
704
	   In both cases, we want to ensure that write_config does not flush the
705
	   in-memory config back to disk.
706
	*/
707
	$config['reset_factory_defaults'] = true;
708

    
709
	/* call the wizard */
710
	if ($reboot_required) {
711
		// If we need a reboot first then touch a different trigger file.
712
		touch("/conf/trigger_initial_wizard_after_reboot");
713
	} else {
714
		touch("/conf/trigger_initial_wizard");
715
	}
716
	if (!$lock) {
717
		unlock($lockkey);
718
	}
719
	conf_mount_ro();
720
	setup_serial_port();
721
	return 0;
722
}
723

    
724
function config_restore($conffile) {
725
	global $config, $g;
726

    
727
	if (!file_exists($conffile)) {
728
		return 1;
729
	}
730

    
731
	backup_config();
732

    
733
	conf_mount_rw();
734

    
735
	$lockkey = lock('config', LOCK_EX);
736

    
737
	unlink_if_exists("{$g['tmp_path']}/config.cache");
738
	copy($conffile, "{$g['cf_conf_path']}/config.xml");
739

    
740
	disable_security_checks();
741

    
742
	unlock($lockkey);
743

    
744
	$config = parse_config(true);
745

    
746
	conf_mount_ro();
747

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

    
750
	return 0;
751
}
752

    
753
function config_install($conffile) {
754
	global $config, $g;
755

    
756
	if (!file_exists($conffile)) {
757
		return 1;
758
	}
759

    
760
	if (!config_validate("{$conffile}")) {
761
		return 1;
762
	}
763

    
764
	if (platform_booting()) {
765
		echo gettext("Installing configuration...") . "\n";
766
	} else {
767
		log_error(gettext("Installing configuration ...."));
768
	}
769

    
770
	conf_mount_rw();
771
	$lockkey = lock('config', LOCK_EX);
772

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

    
775
	disable_security_checks();
776

    
777
	/* unlink cache file if it exists */
778
	if (file_exists("{$g['tmp_path']}/config.cache")) {
779
		unlink("{$g['tmp_path']}/config.cache");
780
	}
781

    
782
	unlock($lockkey);
783
	conf_mount_ro();
784

    
785
	return 0;
786
}
787

    
788
/*
789
 * Disable security checks for DNS rebind and HTTP referrer until next time
790
 * they pass (or reboot), to aid in preventing accidental lockout when
791
 * restoring settings like hostname, domain, IP addresses, and settings
792
 * related to the DNS rebind and HTTP referrer checks.
793
 * Intended for use when restoring a configuration or directly
794
 * modifying config.xml without an unconditional reboot.
795
 */
796
function disable_security_checks() {
797
	global $g;
798
	touch("{$g['tmp_path']}/disable_security_checks");
799
}
800

    
801
/* Restores security checks.  Should be called after all succeed. */
802
function restore_security_checks() {
803
	global $g;
804
	unlink_if_exists("{$g['tmp_path']}/disable_security_checks");
805
}
806

    
807
/* Returns status of security check temporary disable. */
808
function security_checks_disabled() {
809
	global $g;
810
	return file_exists("{$g['tmp_path']}/disable_security_checks");
811
}
812

    
813
function config_validate($conffile) {
814

    
815
	global $g, $xmlerr;
816

    
817
	$xml_parser = xml_parser_create();
818

    
819
	if (!($fp = fopen($conffile, "r"))) {
820
		$xmlerr = gettext("XML error: unable to open file");
821
		return false;
822
	}
823

    
824
	while ($data = fread($fp, 4096)) {
825
		if (!xml_parse($xml_parser, $data, feof($fp))) {
826
			$xmlerr = sprintf(gettext('%1$s at line %2$d'),
827
						xml_error_string(xml_get_error_code($xml_parser)),
828
						xml_get_current_line_number($xml_parser));
829
			return false;
830
		}
831
	}
832
	xml_parser_free($xml_parser);
833

    
834
	fclose($fp);
835

    
836
	return true;
837
}
838

    
839
function cleanup_backupcache($lock = false) {
840
	global $config, $g;
841
	$i = false;
842

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

    
845
	if (!$lock) {
846
		$lockkey = lock('config');
847
	}
848

    
849
	conf_mount_rw();
850

    
851
	$backups = get_backups();
852
	if ($backups) {
853
		$baktimes = $backups['versions'];
854
		unset($backups['versions']);
855
	} else {
856
		$backups = array();
857
		$baktimes = array();
858
	}
859
	$newbaks = array();
860
	$bakfiles = glob($g['cf_conf_path'] . "/backup/config-*");
861
	$tocache = array();
862

    
863
	foreach ($bakfiles as $backup) { // Check for backups in the directory not represented in the cache.
864
		$backupsize = filesize($backup);
865
		if ($backupsize == 0) {
866
			unlink($backup);
867
			continue;
868
		}
869
		$backupexp = explode('-', $backup);
870
		$backupexp = explode('.', array_pop($backupexp));
871
		$tocheck = array_shift($backupexp);
872
		unset($backupexp);
873
		if (!in_array($tocheck, $baktimes)) {
874
			$i = true;
875
			if (platform_booting()) {
876
				echo ".";
877
			}
878
			$newxml = parse_xml_config($backup, array($g['xml_rootobj'], 'pfsense'));
879
			if ($newxml == "-1") {
880
				log_error(sprintf(gettext("The backup cache file %s is corrupted.  Unlinking."), $backup));
881
				unlink($backup);
882
				log_error(sprintf(gettext("The backup cache file %s is corrupted.  Unlinking."), $backup));
883
				continue;
884
			}
885
			if ($newxml['revision']['description'] == "") {
886
				$newxml['revision']['description'] = "Unknown";
887
			}
888
			if ($newxml['version'] == "") {
889
				$newxml['version'] = "?";
890
			}
891
			$tocache[$tocheck] = array('description' => $newxml['revision']['description'], 'version' => $newxml['version'], 'filesize' => $backupsize);
892
		}
893
	}
894
	foreach ($backups as $checkbak) {
895
		if (count(preg_grep('/' . $checkbak['time'] . '/i', $bakfiles)) != 0) {
896
			$newbaks[] = $checkbak;
897
		} else {
898
			$i = true;
899
			if (platform_booting()) print " " . $tocheck . "r";
900
		}
901
	}
902
	foreach ($newbaks as $todo) {
903
		$tocache[$todo['time']] = array('description' => $todo['description'], 'version' => $todo['version'], 'filesize' => $todo['filesize']);
904
	}
905
	if (is_int($revisions) and (count($tocache) > $revisions)) {
906
		$toslice = array_slice(array_keys($tocache), 0, $revisions);
907
		foreach ($toslice as $sliced) {
908
			$newcache[$sliced] = $tocache[$sliced];
909
		}
910
		foreach ($tocache as $version => $versioninfo) {
911
			if (!in_array($version, array_keys($newcache))) {
912
				unlink_if_exists($g['conf_path'] . '/backup/config-' . $version . '.xml');
913
			}
914
		}
915
		$tocache = $newcache;
916
	}
917
	$bakout = fopen($g['cf_conf_path'] . '/backup/backup.cache', "w");
918
	fwrite($bakout, serialize($tocache));
919
	fclose($bakout);
920
	pfSense_fsync("{$g['cf_conf_path']}/backup/backup.cache");
921
	conf_mount_ro();
922

    
923
	if (!$lock) {
924
		unlock($lockkey);
925
	}
926
}
927

    
928
function get_backups() {
929
	global $g;
930
	if (file_exists("{$g['cf_conf_path']}/backup/backup.cache")) {
931
		$confvers = unserialize(file_get_contents("{$g['cf_conf_path']}/backup/backup.cache"));
932
		$bakvers = array_keys($confvers);
933
		$toreturn = array();
934
		sort($bakvers);
935
		// 	$bakvers = array_reverse($bakvers);
936
		foreach (array_reverse($bakvers) as $bakver) {
937
			$toreturn[] = array('time' => $bakver, 'description' => $confvers[$bakver]['description'], 'version' => $confvers[$bakver]['version'], 'filesize' => $confvers[$bakver]['filesize']);
938
		}
939
	} else {
940
		return false;
941
	}
942
	$toreturn['versions'] = $bakvers;
943
	return $toreturn;
944
}
945

    
946
function backup_config() {
947
	global $config, $g;
948

    
949
	if ($g['platform'] == "cdrom") {
950
		return;
951
	}
952

    
953
	conf_mount_rw();
954

    
955
	/* Create backup directory if needed */
956
	safe_mkdir("{$g['cf_conf_path']}/backup");
957
	if ($config['revision']['time'] == "") {
958
		$baktime = 0;
959
	} else {
960
		$baktime = $config['revision']['time'];
961
	}
962

    
963
	if ($config['revision']['description'] == "") {
964
		$bakdesc = "Unknown";
965
	} else {
966
		$bakdesc = $config['revision']['description'];
967
	}
968

    
969
	$bakver = ($config['version'] == "") ? "?" : $config['version'];
970
	$bakfilename = $g['cf_conf_path'] . '/backup/config-' . $baktime . '.xml';
971
	copy($g['cf_conf_path'] . '/config.xml', $bakfilename);
972

    
973
	if (file_exists($g['cf_conf_path'] . '/backup/backup.cache')) {
974
		$backupcache = unserialize(file_get_contents($g['cf_conf_path'] . '/backup/backup.cache'));
975
	} else {
976
		$backupcache = array();
977
	}
978
	$backupcache[$baktime] = array('description' => $bakdesc, 'version' => $bakver, 'filesize' => filesize($bakfilename));
979
	$bakout = fopen($g['cf_conf_path'] . '/backup/backup.cache', "w");
980
	fwrite($bakout, serialize($backupcache));
981
	fclose($bakout);
982
	pfSense_fsync("{$g['cf_conf_path']}/backup/backup.cache");
983

    
984
	conf_mount_ro();
985

    
986
	return true;
987
}
988

    
989
function set_device_perms() {
990
	$devices = array(
991
		'pf' => array(
992
			'user' => 'root',
993
			'group' => 'proxy',
994
			'mode' => 0660),
995
		);
996

    
997
	foreach ($devices as $name => $attr) {
998
		$path = "/dev/$name";
999
		if (file_exists($path)) {
1000
			chown($path, $attr['user']);
1001
			chgrp($path, $attr['group']);
1002
			chmod($path, $attr['mode']);
1003
		}
1004
	}
1005
}
1006

    
1007
function get_config_user() {
1008
	if (empty($_SESSION["Username"])) {
1009
		$username = getenv("USER");
1010
		if (empty($conuser) || $conuser == "root") {
1011
			$username = "(system)";
1012
		}
1013
	} else {
1014
		$username = $_SESSION["Username"];
1015
	}
1016

    
1017
	if (!empty($_SERVER['REMOTE_ADDR'])) {
1018
		$username .= '@' . $_SERVER['REMOTE_ADDR'];
1019
	}
1020

    
1021
	return $username;
1022
}
1023

    
1024
function make_config_revision_entry($desc = null, $override_user = null) {
1025
	if (empty($override_user)) {
1026
		$username = get_config_user();
1027
	} else {
1028
		$username = $override_user;
1029
	}
1030

    
1031
	$revision = array();
1032

    
1033
	if (time() > mktime(0, 0, 0, 9, 1, 2004)) {     /* make sure the clock settings are plausible */
1034
		$revision['time'] = time();
1035
	}
1036

    
1037
	/* Log the running script so it's not entirely unlogged what changed */
1038
	if ($desc == "Unknown") {
1039
		$desc = sprintf(gettext("%s made unknown change"), $_SERVER['SCRIPT_NAME']);
1040
	}
1041
	if (!empty($desc)) {
1042
		$revision['description'] = "{$username}: " . $desc;
1043
	}
1044
	$revision['username'] = $username;
1045
	return $revision;
1046
}
1047

    
1048
function pfSense_clear_globals() {
1049
	global $config, $FilterIfList, $GatewaysList, $filterdns, $aliases, $aliastable;
1050

    
1051
	$error = error_get_last();
1052

    
1053
	// Errors generated by user code (diag_commands.php) are identified by path and not added to notices
1054
	if ($error !== NULL && !preg_match('|^' . preg_quote($g['tmp_path_user_code']) . '/[^/]{1,16}$|', $error['file'])) {
1055
		if (in_array($error['type'], array(E_ERROR, E_COMPILE_ERROR, E_CORE_ERROR, E_RECOVERABLE_ERROR))) {
1056
			$errorstr = "PHP ERROR: Type: {$error['type']}, File: {$error['file']}, Line: {$error['line']}, Message: {$error['message']}";
1057
			print($errorstr);
1058
			log_error($errorstr);
1059
			file_notice("phperror", $errorstr, 'PHP errors');
1060
		} else if ($error['type'] != E_NOTICE) {
1061
			$errorstr = "PHP WARNING: Type: {$error['type']}, File: {$error['file']}, Line: {$error['line']}, Message: {$error['message']}";
1062
			// XXX: comment out for now, should re-enable post-2.2
1063
			//print($errorstr);
1064
			//log_error($errorstr);
1065
			//file_notice("phpwarning", $errorstr, 'PHP warning');
1066
		}
1067
	}
1068

    
1069
	if (isset($FilterIfList)) {
1070
		unset($FilterIfList);
1071
	}
1072

    
1073
	if (isset($GatewaysList)) {
1074
		unset($GatewaysList);
1075
	}
1076

    
1077
	/* Used for the hostname dns resolver */
1078
	if (isset($filterdns)) {
1079
		unset($filterdns);
1080
	}
1081

    
1082
	/* Used for aliases and interface macros */
1083
	if (isset($aliases)) {
1084
		unset($aliases);
1085
	}
1086
	if (isset($aliastable)) {
1087
		unset($aliastable);
1088
	}
1089

    
1090
	unset($config);
1091
}
1092

    
1093
register_shutdown_function('pfSense_clear_globals');
1094

    
1095
?>
(14-14/67)