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($already_run[$migration_function]);
476
			} else {
477
				$migration_function();
478
			}
479
		}
480
		$config['version'] = sprintf('%.1f', $next / 10);
481
		if (platform_booting()) {
482
			echo ".";
483
		}
484
	}
485

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

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

    
493
	additional_config_upgrade();
494
}
495

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

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

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

    
542
	// Sync file before returning
543
	return pfSense_fsync($file);
544
}
545

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

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

    
576
	if (!isset($argc)) {
577
		session_commit();
578
	}
579

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

    
590
	if ($backup) {
591
		backup_config();
592
	}
593

    
594
	$config['revision'] = make_config_revision_entry($desc);
595

    
596
	conf_mount_rw();
597
	$lockkey = lock('config', LOCK_EX);
598

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

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

    
610
	cleanup_backupcache(true);
611

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

    
633
	unlock($lockkey);
634

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

    
641
	unlink_if_exists("/usr/local/pkg/pf/carp_sync_client.php");
642

    
643
	/* tell kernel to sync fs data */
644
	conf_mount_ro();
645

    
646
	/* sync carp entries to other firewalls */
647
	carp_sync_client();
648

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

    
654
	return $config;
655
}
656

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

    
666
	conf_mount_rw();
667

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

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

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

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

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

    
694
	disable_security_checks();
695

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

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

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

    
722
function config_restore($conffile) {
723
	global $config, $g;
724

    
725
	if (!file_exists($conffile)) {
726
		return 1;
727
	}
728

    
729
	backup_config();
730

    
731
	conf_mount_rw();
732

    
733
	$lockkey = lock('config', LOCK_EX);
734

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

    
738
	disable_security_checks();
739

    
740
	unlock($lockkey);
741

    
742
	$config = parse_config(true);
743

    
744
	conf_mount_ro();
745

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

    
748
	return 0;
749
}
750

    
751
function config_install($conffile) {
752
	global $config, $g;
753

    
754
	if (!file_exists($conffile)) {
755
		return 1;
756
	}
757

    
758
	if (!config_validate("{$conffile}")) {
759
		return 1;
760
	}
761

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

    
768
	conf_mount_rw();
769
	$lockkey = lock('config', LOCK_EX);
770

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

    
773
	disable_security_checks();
774

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

    
780
	unlock($lockkey);
781
	conf_mount_ro();
782

    
783
	return 0;
784
}
785

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

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

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

    
811
function config_validate($conffile) {
812

    
813
	global $g, $xmlerr;
814

    
815
	$xml_parser = xml_parser_create();
816

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

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

    
832
	fclose($fp);
833

    
834
	return true;
835
}
836

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

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

    
843
	if (!$lock) {
844
		$lockkey = lock('config');
845
	}
846

    
847
	conf_mount_rw();
848

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

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

    
921
	if (!$lock) {
922
		unlock($lockkey);
923
	}
924
}
925

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

    
944
function backup_config() {
945
	global $config, $g;
946

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

    
951
	conf_mount_rw();
952

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

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

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

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

    
982
	conf_mount_ro();
983

    
984
	return true;
985
}
986

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

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

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

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

    
1019
	return $username;
1020
}
1021

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

    
1029
	$revision = array();
1030

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

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

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

    
1049
	$error = error_get_last();
1050

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

    
1067
	if (isset($FilterIfList)) {
1068
		unset($FilterIfList);
1069
	}
1070

    
1071
	if (isset($GatewaysList)) {
1072
		unset($GatewaysList);
1073
	}
1074

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

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

    
1088
	unset($config);
1089
}
1090

    
1091
register_shutdown_function('pfSense_clear_globals');
1092

    
1093
?>
(14-14/67)