Project

General

Profile

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

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

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

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

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

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

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

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

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

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

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

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

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

    
155
	alias_make_table();
156

    
157
	return $config;
158
}
159

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

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

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

    
187
function discover_last_backup() {
188
	global $g;
189

    
190
	$backups = glob('/cf/conf/backup/*.xml');
191
	foreach (array_reverse($backups) as $backup) {
192
		/* checking multiple backups when detecting invalid configuration
193
		 * https://redmine.pfsense.org/issues/11748 */
194
		if (filesize($backup) != 0) {
195
			$testconfig = parse_xml_config($backup, $g['xml_rootobj']);
196
			if ($testconfig != -1) {
197
				return basename($backup);
198
			}
199
		} 
200
	}
201

    
202
	return false;
203
}
204

    
205
function restore_backup($file) {
206
	global $g;
207

    
208
	if (file_exists($file)) {
209
		/* restore rrddata/xmldata and clear appropriate data,
210
		 * see https://redmine.pfsense.org/issues/11050 */
211
		$data = file_get_contents("$file");
212
		$conf = parse_xml_config("$file", $g['xml_rootobj']);
213
		if ($conf['rrddata']) {
214
			restore_rrddata($conf);
215
			$data = clear_tagdata("rrd", $data);
216
		}
217
		if ($conf['sshdata']) {
218
			restore_sshdata($conf);
219
			$data = clear_tagdata("ssh", $data);
220
		}
221
		foreach ($g['backuppath'] as $bk => $path) {
222
			if (!empty($conf[$bk][$bk.'data'])) {
223
				restore_xmldatafile($bk, $conf);
224
				$data = clear_tagdata($bk, $data);
225
			}
226
		}
227
		file_put_contents($file, $data);
228
		unlink_if_exists("{$g['tmp_path']}/config.cache");
229
		copy("$file", "/cf/conf/config.xml");
230
		//pfSense_fsync("/cf/conf/config.xml");
231
		//pfSense_fsync($g['conf_path']);
232
		disable_security_checks();
233
		log_error(sprintf(gettext('%1$s is restoring the configuration %2$s'), $g['product_label'], $file));
234
		file_notice("config.xml", sprintf(gettext('%1$s is restoring the configuration %2$s'), $g['product_label'], $file), "pfSenseConfigurator", "");
235
	}
236
}
237

    
238
/*
239
 *	Backup RRD/XML Data
240
 */
241

    
242
/* If the config on disk had rrddata/xmldata tags already, remove that section first.
243
 * See https://redmine.pfsense.org/issues/8994,
244
 *     https://redmine.pfsense.org/issues/10508, 
245
 *     https://redmine.pfsense.org/issues/11050 */
246
function clear_tagdata($tag = "rrd", $data) {
247
	$data = preg_replace("/[[:blank:]]*<{$tag}data>.*<\\/{$tag}data>[[:blank:]]*\n*/s", "", $data);
248
	$data = preg_replace("/[[:blank:]]*<{$tag}data\\/>[[:blank:]]*\n*/", "", $data);
249

    
250
	return $data;
251
}
252

    
253
function restore_xmldatafile($type='voucher', $conf = false) {
254
	global $config, $g;
255

    
256
	if (!$conf) {
257
		$conf = & $config;
258
	}
259

    
260
	foreach ($conf[$type]["{$type}data"]["xmldatafile"] as $file) {
261
		$basename = basename($file['filename']);
262
		$dirname = dirname($g['backuppath'][$type]);
263
		$xmldata_file = "{$dirname}/{$basename}";
264
		if (file_put_contents($xmldata_file, gzinflate(base64_decode($file['data']))) === false) {
265
			log_error(sprintf(gettext("Cannot write %s"), $xmldata_file));
266
			continue;
267
		}
268
	}
269
}
270

    
271
function restore_rrddata($conf = false) {
272
	global $config, $g, $rrdtool, $input_errors;
273

    
274
	if (!$conf) {
275
		$conf = & $config;
276
	}
277

    
278
	foreach ($conf['rrddata']['rrddatafile'] as $rrd) {
279
		if ($rrd['xmldata']) {
280
			$rrd_file = "{$g['vardb_path']}/rrd/{$rrd['filename']}";
281
			$xml_file = preg_replace('/\.rrd$/', ".xml", $rrd_file);
282
			if (file_put_contents($xml_file, gzinflate(base64_decode($rrd['xmldata']))) === false) {
283
				log_error(sprintf(gettext("Cannot write %s"), $xml_file));
284
				continue;
285
			}
286
			$output = array();
287
			$status = null;
288
			exec("$rrdtool restore -f '{$xml_file}' '{$rrd_file}'", $output, $status);
289
			if ($status) {
290
				log_error("rrdtool restore -f '{$xml_file}' '{$rrd_file}' failed returning {$status}.");
291
				continue;
292
			}
293
			unlink($xml_file);
294
		} else if ($rrd['data']) {
295
			$rrd_file = "{$g['vardb_path']}/rrd/{$rrd['filename']}";
296
			$rrd_fd = fopen($rrd_file, "w");
297
			if (!$rrd_fd) {
298
				log_error(sprintf(gettext("Cannot write %s"), $rrd_file));
299
				continue;
300
			}
301
			$data = base64_decode($rrd['data']);
302
			/* Try to decompress the data. */
303
			$dcomp = @gzinflate($data);
304
			if ($dcomp) {
305
				/* If the decompression worked, write the decompressed data */
306
				if (fwrite($rrd_fd, $dcomp) === false) {
307
					log_error(sprintf(gettext("fwrite %s failed"), $rrd_file));
308
					continue;
309
				}
310
			} else {
311
				/* If the decompression failed, it wasn't compressed, so write raw data */
312
				if (fwrite($rrd_fd, $data) === false) {
313
					log_error(sprintf(gettext("fwrite %s failed"), $rrd_file));
314
					continue;
315
				}
316
			}
317
			if (fclose($rrd_fd) === false) {
318
				log_error(sprintf(gettext("fclose %s failed"), $rrd_file));
319
				continue;
320
			}
321
		}
322
	}
323
}
324

    
325
function restore_sshdata($conf = false) {
326
	global $config, $sshConfigDir;
327

    
328
	if (!$conf) {
329
		$conf = & $config;
330
	}
331

    
332
	$oldmask = umask();
333
	foreach ($conf["sshdata"]["sshkeyfile"] as $sshkey) {
334
		$keypath = "{$sshConfigDir}/{$sshkey['filename']}";
335
		if (strstr($sshkey['filename'], 'pub')) {
336
			umask(0133);
337
		} else {
338
			umask(0177);
339
		}
340
		if (file_put_contents($keypath, gzinflate(base64_decode($sshkey['xmldata']))) === false) {
341
			log_error(sprintf(gettext("Cannot write %s"), $sshkey['filename']));
342
			continue;
343
		}
344
	}
345
	umask($oldmask);
346
}
347

    
348
/****f* config/parse_config_bootup
349
 * NAME
350
 *   parse_config_bootup - Bootup-specific configuration checks.
351
 * RESULT
352
 *   null
353
 ******/
354
function parse_config_bootup() {
355
	global $config, $g;
356

    
357
	if (platform_booting()) {
358
		echo ".";
359
	}
360

    
361
	$lockkey = lock('config');
362
	if (!file_exists("{$g['conf_path']}/config.xml")) {
363
		if (platform_booting()) {
364
			$last_backup = discover_last_backup();
365
			if ($last_backup) {
366
				log_error("No config.xml found, attempting last known config restore.");
367
				file_notice("config.xml", gettext("No config.xml found, attempting last known config restore."), "pfSenseConfigurator", "");
368
				restore_backup("/cf/conf/backup/{$last_backup}");
369
			}
370
			if (!file_exists("{$g['conf_path']}/config.xml")) {
371
				echo sprintf(gettext("XML configuration file not found.  %s cannot continue booting."), $g['product_label']) . "\n";
372
				unlock($lockkey);
373
				die(gettext("Could not find a usable configuration file or it's backup! Exiting...."));
374
			} else {
375
				log_error("Last known config found and restored.  Please double check the configuration file for accuracy.");
376
				file_notice("config.xml", gettext("Last known config found and restored.  Please double check the configuration file for accuracy."), "pfSenseConfigurator", "");
377
			}
378
		} else {
379
			unlock($lockkey);
380
			log_error(gettext("Could not find a usable configuration file! Exiting...."));
381
			exit(0);
382
		}
383
	}
384

    
385
	if (filesize("{$g['conf_path']}/config.xml") == 0) {
386
		$last_backup = discover_last_backup();
387
		if ($last_backup) {
388
			log_error(gettext("No config.xml found, attempting last known config restore."));
389
			file_notice("config.xml", gettext("No config.xml found, attempting last known config restore."), "pfSenseConfigurator", "");
390
			restore_backup("{$g['conf_path']}/backup/{$last_backup}");
391
		} else {
392
			unlock($lockkey);
393
			die(gettext("Config.xml is corrupted and is 0 bytes.  Could not restore a previous backup."));
394
		}
395
	}
396
	unlock($lockkey);
397

    
398
	$config = parse_config(true);
399

    
400
	if ((float)$config['version'] > (float)$g['latest_config']) {
401
		echo <<<EOD
402

    
403

    
404
*******************************************************************************
405
* WARNING!                                                                    *
406
* The current configuration has been created with a newer version of {$g['product_label']}  *
407
* than this one! This can lead to serious misbehavior and even security       *
408
* holes! You are urged to either upgrade to a newer version of {$g['product_label']} or     *
409
* revert to the default configuration immediately!                            *
410
*******************************************************************************
411

    
412

    
413
EOD;
414
		}
415

    
416
	/* make alias table (for faster lookups) */
417
	alias_make_table();
418
}
419

    
420
/****f* config/conf_mount_rw
421
 * NAME
422
 *   conf_mount_rw - Mount filesystems read/write.
423
 * RESULT
424
 *   null
425
 ******/
426
/* mount flash card read/write */
427
function conf_mount_rw() {
428
	/* Obsoleted. Keep it here until all calls are removed */
429
	return;
430
}
431

    
432
/****f* config/conf_mount_ro
433
 * NAME
434
 *   conf_mount_ro - Mount filesystems readonly.
435
 * RESULT
436
 *   null
437
 ******/
438
function conf_mount_ro() {
439
	/* Obsoleted. Keep it here until all calls are removed */
440
	return;
441
}
442

    
443
/****f* config/convert_config
444
 * NAME
445
 *   convert_config - Attempt to update config.xml.
446
 * DESCRIPTION
447
 *   convert_config() reads the current global configuration
448
 *   and attempts to convert it to conform to the latest
449
 *   config.xml version. This allows major formatting changes
450
 *   to be made with a minimum of breakage.
451
 * RESULT
452
 *   null
453
 ******/
454
/* convert configuration, if necessary */
455
function convert_config() {
456
	global $config, $g;
457
	$now = date("H:i:s");
458
	log_error(sprintf(gettext("Start Configuration upgrade at %s, set execution timeout to 15 minutes"), $now));
459
	//ini_set("max_execution_time", "900");
460

    
461
	/* special case upgrades */
462
	/* fix every minute crontab bogons entry */
463
	if (is_array($config['cron'])) {
464
		$cron_item_count = count($config['cron']['item']);
465
		for ($x = 0; $x < $cron_item_count; $x++) {
466
			if (stristr($config['cron']['item'][$x]['command'], "rc.update_bogons.sh")) {
467
				if ($config['cron']['item'][$x]['hour'] == "*") {
468
					$config['cron']['item'][$x]['hour'] = "3";
469
					write_config(gettext("Updated bogon update frequency to 3am"));
470
					log_error(gettext("Updated bogon update frequency to 3am"));
471
				}
472
			}
473
		}
474
	}
475

    
476
	// Save off config version
477
	$prev_version = $config['version'];
478

    
479
	include_once('auth.inc');
480
	include_once('upgrade_config.inc');
481
	if (file_exists("/etc/inc/upgrade_config_custom.inc")) {
482
		include_once("upgrade_config_custom.inc");
483
	}
484

    
485
	if ($config['version'] == $g['latest_config']) {
486
		additional_config_upgrade();
487
		return;		/* already at latest version */
488
	}
489

    
490
	if (!is_array($config['system']['already_run_config_upgrade'])) {
491
		$config['system']['already_run_config_upgrade'] = array();
492
	}
493
	$already_run = $config['system']['already_run_config_upgrade'];
494

    
495
	/* Loop and run upgrade_VER_to_VER() until we're at current version */
496
	while ($config['version'] < $g['latest_config']) {
497
		$cur = $config['version'] * 10;
498
		$next = $cur + 1;
499
		$migration_function = sprintf('upgrade_%03d_to_%03d', $cur,
500
		    $next);
501

    
502
		foreach (array("", "_custom") as $suffix) {
503
			$migration_function .= $suffix;
504
			if (!function_exists($migration_function)) {
505
				continue;
506
			}
507
			if (isset($already_run[$migration_function])) {
508
				/* Already executed, skip now */
509
				unset($config['system']
510
				    ['already_run_config_upgrade']
511
				    [$migration_function]);
512
			} else {
513
				$migration_function();
514
			}
515
		}
516
		$config['version'] = sprintf('%.1f', $next / 10);
517
		if (platform_booting()) {
518
			echo ".";
519
		}
520
	}
521

    
522
	if ($prev_version != $config['version']) {
523
		$now = date("H:i:s");
524
		log_error(sprintf(gettext("Ended Configuration upgrade at %s"), $now));
525

    
526
		write_config(sprintf(gettext('Upgraded config version level from %1$s to %2$s'), $prev_version, $config['version']));
527
	}
528

    
529
	additional_config_upgrade();
530
}
531

    
532
/****f* config/safe_write_file
533
 * NAME
534
 *   safe_write_file - Write a file out atomically
535
 * DESCRIPTION
536
 *   safe_write_file() Writes a file out atomically by first writing to a
537
 *   temporary file of the same name but ending with the pid of the current
538
 *   process, them renaming the temporary file over the original.
539
 * INPUTS
540
 *   $filename  - string containing the filename of the file to write
541
 *   $content   - string or array containing the file content to write to file
542
 *   $force_binary      - boolean denoting whether we should force binary
543
 *   mode writing.
544
 * RESULT
545
 *   boolean - true if successful, false if not
546
 ******/
547
function safe_write_file($file, $content, $force_binary = false) {
548
	$tmp_file = $file . "." . getmypid();
549
	$write_mode = $force_binary ? "wb" : "w";
550

    
551
	$fd = fopen($tmp_file, $write_mode);
552
	if (!$fd) {
553
		// Unable to open temporary file for writing
554
		return false;
555
	}
556
	if (is_array($content)) {
557
		foreach ($content as $line) {
558
			if (!fwrite($fd, $line . "\n")) {
559
				// Unable to write to temporary file
560
				fclose($fd);
561
				return false;
562
			}
563
		}
564
	} elseif (!fwrite($fd, $content)) {
565
		// Unable to write to temporary file
566
		fclose($fd);
567
		return false;
568
	}
569
	fflush($fd);
570
	fclose($fd);
571

    
572
	/* XXX Re-add pfSense_fsync() call here after it's fixed */
573
	// if (!pfSense_fsync($tmp_file) || !rename($tmp_file, $file)) {
574
	if (!rename($tmp_file, $file)) {
575
		// Unable to move temporary file to original
576
		@unlink($tmp_file);
577
		return false;
578
	}
579

    
580
	// Sync file before returning
581
	//return pfSense_fsync($file);
582
	return true;
583
}
584

    
585
/****f* config/write_config
586
 * NAME
587
 *   write_config - Backup and write the firewall configuration.
588
 * DESCRIPTION
589
 *   write_config() handles backing up the current configuration,
590
 *   applying changes, and regenerating the configuration cache.
591
 * INPUTS
592
 *   $desc	- string containing the a description of configuration changes
593
 *   $backup	- boolean: do not back up current configuration if false.
594
 *   $write_config_only	- boolean: do not sync or reload anything; just save the configuration if true.
595
 * RESULT
596
 *   null
597
 ******/
598
/* save the system configuration */
599
function write_config($desc="Unknown", $backup = true, $write_config_only = false) {
600
	global $config, $g;
601

    
602
	// Certain strings may be embedded in the $desc (reason) parameter to trigger certain behavior.
603
	// If detected, those strings are removed and a variable set.
604
	$doacb = true;
605
	$manual_acb = false;
606
	$rcnt = 0;
607

    
608
	$desc = str_replace("-MaNuAlBaCkUp", "", $desc, $rcnt);
609
	if ($rcnt > 0) {
610
		$manual_acb = true; // Manual backups require special processing on the server
611
	}
612

    
613
	$rcnt = 0;
614
	$desc = str_replace("-NoReMoTeBaCkUp", "", $desc, $rcnt);
615
	if ($rcnt > 0) {
616
		$doacb = false; // No ACB will be performed if this string is detected
617
	}
618

    
619
	/*
620
	* Syncing vouchers happens every minute and sometimes multiple times. We don't
621
	* want to fill up our db with a lot of the same config so just ignore that case.
622
	*/
623
	if((strpos($desc, 'Syncing vouchers') !== false ||
624
		strpos($desc, 'Captive Portal Voucher database synchronized') !== false) ) {
625
		$doacb = false;
626
	}
627

    
628
	if (!empty($_SERVER['REMOTE_ADDR'])) {
629
		@phpsession_begin();
630
		if (!empty($_SESSION['Username']) && ($_SESSION['Username'] != "admin")) {
631
			$user = getUserEntry($_SESSION['Username']);
632
			if (is_array($user) && userHasPrivilege($user, "user-config-readonly")) {
633
				syslog(LOG_AUTHPRIV, sprintf(gettext("Save config permission denied by the 'User - Config: Deny Config Write' permission for user '%s'."), get_config_user()));
634
				phpsession_end(true);
635
				return false;
636
			}
637
		}
638
		if (!isset($argc)) {
639
			phpsession_end(true);
640
		}
641
	}
642

    
643
	if (isset($config['reset_factory_defaults'])) {
644
		/*
645
		   We have put a default config.xml on disk and are about to reboot
646
		   or reload it. Do not let any system or package code try to save
647
		   state to config because that would overwrite the default config
648
		   with the running config.
649
		*/
650
		return false;
651
	}
652

    
653
	if ($backup) {
654
		backup_config();
655
	}
656

    
657
	if ($desc == "Unknown") {
658
		file_notice("config.xml", gettext(
659
		    'WARNING: write_config() was called without description'));
660
	}
661
	$config['revision'] = make_config_revision_entry($desc);
662

    
663
	$lockkey = lock('config', LOCK_EX);
664

    
665
	/* generate configuration XML */
666
	$xmlconfig = dump_xml_config($config, $g['xml_rootobj']);
667

    
668
	/* write new configuration */
669
	if (!safe_write_file("{$g['cf_conf_path']}/config.xml", $xmlconfig)) {
670
		log_error(gettext("WARNING: Config contents could not be saved. Could not open file!"));
671
		unlock($lockkey);
672
		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"));
673
		return -1;
674
	}
675

    
676
	init_config_arr('syslog');
677
	if ($config['syslog']['logconfigchanges'] != "disabled") {
678
		log_error(gettext("Configuration Change") . ": {$config['revision']['description']}");
679
	}
680

    
681
	cleanup_backupcache(true);
682

    
683
	/* re-read configuration */
684
	/* NOTE: We assume that the file can be parsed since we wrote it. */
685
	$config = parse_xml_config("{$g['conf_path']}/config.xml", $g['xml_rootobj']);
686
	if ($config == -1) {
687
		copy("{$g['conf_path']}/config.xml", "{$g['conf_path']}/config.xml.bad");
688
		$last_backup = discover_last_backup();
689
		if ($last_backup) {
690
			restore_backup("/cf/conf/backup/{$last_backup}");
691
			$config = parse_xml_config("{$g['conf_path']}/config.xml", $g['xml_rootobj']);
692
			if (platform_booting()) {
693
				echo "\n\n ************** WARNING **************";
694
				echo "\n\n Configuration could not be validated. A previous configuration was restored. \n";
695
				echo "\n The failed configuration file has been saved as {$g['conf_path']}/config.xml.bad \n\n";
696
			}
697
		} else {
698
			log_error(gettext("Could not restore config.xml."));
699
		}
700
	} else {
701
		generate_config_cache($config);
702
	}
703

    
704
	unlock($lockkey);
705

    
706
	if ($write_config_only) {
707
		return $config;
708
	}
709

    
710
	unlink_if_exists("/usr/local/pkg/pf/carp_sync_client.php");
711

    
712
	/* sync carp entries to other firewalls */
713
	carp_sync_client();
714

    
715
	if (is_dir("/usr/local/pkg/write_config")) {
716
		/* process packager manager custom rules */
717
		run_plugins("/usr/local/pkg/write_config/");
718
	}
719

    
720
	// Try the core AutoConfigBackup system
721
	if (is_array($config['system']['acb']) && $config['system']['acb']['enable'] == "yes" &&
722
	    (!isset($config['system']['acb']['frequency']) || $config['system']['acb']['frequency'] == "every") || file_exists("/tmp/forceacb")) {
723
	    if ($doacb) {
724
			require_once("acb.inc");
725
			upload_config($manual_acb);
726
		}
727

    
728
		if (file_exists("/tmp/forceacb")) {
729
			unlink("/tmp/forceacb");
730
		}
731
	}
732

    
733
	return $config;
734
}
735

    
736
/****f* config/reset_factory_defaults
737
 * NAME
738
 *   reset_factory_defaults - Reset the system to its default configuration.
739
 * RESULT
740
 *   integer	- indicates completion
741
 ******/
742
function reset_factory_defaults($lock = false, $reboot_required = true) {
743
	global $config, $g;
744

    
745
	/* Remove all additional packages */
746
	mwexec("/bin/sh /usr/local/sbin/{$g['product_name']}-upgrade " .
747
	    "-r ALL_PACKAGES -f");
748

    
749
	if (!$lock) {
750
		$lockkey = lock('config', LOCK_EX);
751
	}
752

    
753
	/* create conf directory, if necessary */
754
	safe_mkdir($g['cf_conf_path']);
755

    
756
	/* clear out /conf */
757
	$dh = opendir($g['conf_path']);
758
	while ($filename = readdir($dh)) {
759
		if (($filename != ".") && ($filename != "..") &&
760
		    (!is_dir($g['conf_path'] . "/" . $filename))) {
761
			if ($filename == "enableserial_force")
762
				continue;
763
			unlink_if_exists($g['conf_path'] . "/" . $filename);
764
		}
765
	}
766
	closedir($dh);
767
	unlink_if_exists($g['tmp_path'] . "/config.cache");
768

    
769
	/* copy default configuration */
770
	copy("{$g['conf_default_path']}/config.xml",
771
	    "{$g['cf_conf_path']}/config.xml");
772

    
773
	disable_security_checks();
774

    
775
	/*
776
	   Let write_config know that we are awaiting reload of the current config
777
	   to factory defaults. Either the system is about to reboot, throwing away
778
	   the current in-memory config as it shuts down, or the in-memory config
779
	   is about to be reloaded on-the-fly by parse_config.
780

    
781
	   In both cases, we want to ensure that write_config does not flush the
782
	   in-memory config back to disk.
783
	*/
784
	$config['reset_factory_defaults'] = true;
785

    
786
	/* call the wizard */
787
	if ($reboot_required) {
788
		// If we need a reboot first then touch a different trigger file.
789
		touch("/conf/trigger_initial_wizard_after_reboot");
790
	} else {
791
		touch("/conf/trigger_initial_wizard");
792
	}
793
	if (!$lock) {
794
		unlock($lockkey);
795
	}
796
	console_configure();
797
	return 0;
798
}
799

    
800
function config_restore($conffile) {
801
	global $config, $g;
802

    
803
	if (!file_exists($conffile)) {
804
		return 1;
805
	}
806

    
807
	backup_config();
808

    
809

    
810
	$lockkey = lock('config', LOCK_EX);
811

    
812
	unlink_if_exists("{$g['tmp_path']}/config.cache");
813
	copy($conffile, "{$g['cf_conf_path']}/config.xml");
814

    
815
	disable_security_checks();
816

    
817
	unlock($lockkey);
818

    
819
	$config = parse_config(true);
820

    
821

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

    
824
	return 0;
825
}
826

    
827
function config_install($conffile) {
828
	global $config, $g;
829

    
830
	if (!file_exists($conffile)) {
831
		return 1;
832
	}
833

    
834
	if (!config_validate("{$conffile}")) {
835
		return 1;
836
	}
837

    
838
	if (platform_booting()) {
839
		echo gettext("Installing configuration...") . "\n";
840
	} else {
841
		log_error(gettext("Installing configuration ...."));
842
	}
843

    
844
	$lockkey = lock('config', LOCK_EX);
845

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

    
848
	disable_security_checks();
849

    
850
	/* unlink cache file if it exists */
851
	if (file_exists("{$g['tmp_path']}/config.cache")) {
852
		unlink("{$g['tmp_path']}/config.cache");
853
	}
854

    
855
	unlock($lockkey);
856

    
857
	return 0;
858
}
859

    
860
/*
861
 * Disable security checks for DNS rebind and HTTP referrer until next time
862
 * they pass (or reboot), to aid in preventing accidental lockout when
863
 * restoring settings like hostname, domain, IP addresses, and settings
864
 * related to the DNS rebind and HTTP referrer checks.
865
 * Intended for use when restoring a configuration or directly
866
 * modifying config.xml without an unconditional reboot.
867
 */
868
function disable_security_checks() {
869
	global $g;
870
	touch("{$g['tmp_path']}/disable_security_checks");
871
}
872

    
873
/* Restores security checks.  Should be called after all succeed. */
874
function restore_security_checks() {
875
	global $g;
876
	unlink_if_exists("{$g['tmp_path']}/disable_security_checks");
877
}
878

    
879
/* Returns status of security check temporary disable. */
880
function security_checks_disabled() {
881
	global $g;
882
	return file_exists("{$g['tmp_path']}/disable_security_checks");
883
}
884

    
885
function config_validate($conffile) {
886

    
887
	global $g, $xmlerr;
888

    
889
	$xml_parser = xml_parser_create();
890

    
891
	if (!($fp = fopen($conffile, "r"))) {
892
		$xmlerr = gettext("XML error: unable to open file");
893
		return false;
894
	}
895

    
896
	while ($data = fread($fp, 4096)) {
897
		if (!xml_parse($xml_parser, $data, feof($fp))) {
898
			$xmlerr = sprintf(gettext('%1$s at line %2$d'),
899
						xml_error_string(xml_get_error_code($xml_parser)),
900
						xml_get_current_line_number($xml_parser));
901
			return false;
902
		}
903
	}
904
	xml_parser_free($xml_parser);
905

    
906
	fclose($fp);
907

    
908
	return true;
909
}
910

    
911
function cleanup_backupcache($lock = false) {
912
	global $config, $g;
913
	$i = false;
914

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

    
917
	if (!$lock) {
918
		$lockkey = lock('config');
919
	}
920

    
921

    
922
	$backups = get_backups();
923
	if ($backups) {
924
		$baktimes = $backups['versions'];
925
		unset($backups['versions']);
926
	} else {
927
		$backups = array();
928
		$baktimes = array();
929
	}
930
	$newbaks = array();
931
	$bakfiles = glob($g['cf_conf_path'] . "/backup/config-*");
932
	$tocache = array();
933

    
934
	foreach ($bakfiles as $backup) { // Check for backups in the directory not represented in the cache.
935
		$backupsize = filesize($backup);
936
		if ($backupsize == 0) {
937
			unlink($backup);
938
			continue;
939
		}
940
		$backupexp = explode('-', $backup);
941
		$backupexp = explode('.', array_pop($backupexp));
942
		$tocheck = array_shift($backupexp);
943
		unset($backupexp);
944
		if (!in_array($tocheck, $baktimes)) {
945
			$i = true;
946
			if (platform_booting()) {
947
				echo ".";
948
			}
949
			try {
950
				$newxml = parse_xml_config($backup, array($g['xml_rootobj'], 'pfsense'));
951
			} catch (Exception $exc) {
952
				log_error(sprintf(gettext("The backup cache file %s is corrupted. Parser error message: %s"), $backup, $exc->getMessage()));
953
				$newxml = "-1";
954
			}
955

    
956
			if ($newxml == "-1") {
957
				log_error(sprintf(gettext("The backup cache file %s is corrupted.  Unlinking."), $backup));
958
				unlink($backup);
959
				continue;
960
			}
961
			if ($newxml['revision']['description'] == "") {
962
				$newxml['revision']['description'] = "Unknown";
963
			}
964
			if ($newxml['version'] == "") {
965
				$newxml['version'] = "?";
966
			}
967
			$tocache[$tocheck] = array('description' => $newxml['revision']['description'], 'version' => $newxml['version'], 'filesize' => $backupsize);
968
		}
969
	}
970
	foreach ($backups as $checkbak) {
971
		if (count(preg_grep('/' . $checkbak['time'] . '/i', $bakfiles)) != 0) {
972
			$newbaks[] = $checkbak;
973
		} else {
974
			$i = true;
975
			if (platform_booting()) print " " . $tocheck . "r";
976
		}
977
	}
978
	foreach ($newbaks as $todo) {
979
		$tocache[$todo['time']] = array('description' => $todo['description'], 'version' => $todo['version'], 'filesize' => $todo['filesize']);
980
	}
981
	if (is_int($revisions) and (count($tocache) > $revisions)) {
982
		$toslice = array_slice(array_keys($tocache), 0, $revisions);
983
		$newcache = array();
984
		foreach ($toslice as $sliced) {
985
			$newcache[$sliced] = $tocache[$sliced];
986
		}
987
		foreach ($tocache as $version => $versioninfo) {
988
			if (!in_array($version, array_keys($newcache))) {
989
				unlink_if_exists($g['conf_path'] . '/backup/config-' . $version . '.xml');
990
			}
991
		}
992
		$tocache = $newcache;
993
	}
994
	$bakout = fopen($g['cf_conf_path'] . '/backup/backup.cache', "w");
995
	fwrite($bakout, serialize($tocache));
996
	fclose($bakout);
997
	//pfSense_fsync("{$g['cf_conf_path']}/backup/backup.cache");
998

    
999
	if (!$lock) {
1000
		unlock($lockkey);
1001
	}
1002
}
1003

    
1004
function get_backups() {
1005
	global $g;
1006
	if (file_exists("{$g['cf_conf_path']}/backup/backup.cache")) {
1007
		$confvers = unserialize(file_get_contents("{$g['cf_conf_path']}/backup/backup.cache"));
1008
		$bakvers = array_keys($confvers);
1009
		$toreturn = array();
1010
		sort($bakvers);
1011
		// 	$bakvers = array_reverse($bakvers);
1012
		foreach (array_reverse($bakvers) as $bakver) {
1013
			$toreturn[] = array('time' => $bakver, 'description' => $confvers[$bakver]['description'], 'version' => $confvers[$bakver]['version'], 'filesize' => $confvers[$bakver]['filesize']);
1014
		}
1015
	} else {
1016
		return false;
1017
	}
1018
	$toreturn['versions'] = $bakvers;
1019
	return $toreturn;
1020
}
1021

    
1022
function backup_config() {
1023
	global $config, $g;
1024

    
1025

    
1026
	/* Create backup directory if needed */
1027
	safe_mkdir("{$g['cf_conf_path']}/backup");
1028
	if ($config['revision']['time'] == "") {
1029
		$baktime = 0;
1030
	} else {
1031
		$baktime = $config['revision']['time'];
1032
	}
1033

    
1034
	if ($config['revision']['description'] == "") {
1035
		$bakdesc = "Unknown";
1036
	} else {
1037
		$bakdesc = $config['revision']['description'];
1038
	}
1039

    
1040
	$bakver = ($config['version'] == "") ? "?" : $config['version'];
1041
	$bakfilename = $g['cf_conf_path'] . '/backup/config-' . $baktime . '.xml';
1042
	copy($g['cf_conf_path'] . '/config.xml', $bakfilename);
1043

    
1044
	if (file_exists($g['cf_conf_path'] . '/backup/backup.cache')) {
1045
		$backupcache = unserialize(file_get_contents($g['cf_conf_path'] . '/backup/backup.cache'));
1046
	} else {
1047
		$backupcache = array();
1048
	}
1049
	$backupcache[$baktime] = array('description' => $bakdesc, 'version' => $bakver, 'filesize' => filesize($bakfilename));
1050
	$bakout = fopen($g['cf_conf_path'] . '/backup/backup.cache', "w");
1051
	fwrite($bakout, serialize($backupcache));
1052
	fclose($bakout);
1053
	//pfSense_fsync("{$g['cf_conf_path']}/backup/backup.cache");
1054

    
1055

    
1056
	return true;
1057
}
1058

    
1059
function backup_info($backup_info, $number) {
1060
	if ($backup_info['time'] != 0) {
1061
		$date = date(gettext("n/j/y H:i:s"), $backup_info['time']);
1062
	} else {
1063
		$date = gettext("Unknown");
1064
	}
1065

    
1066
	list($page, $reason) = explode(": ", $backup_info['description'], 2);
1067
	if (empty($reason)) {
1068
		$reason = $page;
1069
		$page = gettext("Unknown Page");
1070
	}
1071

    
1072
	$backup_info = sprintf("%02d", $number) . ". {$date}\tv{$backup_info['version']}\t{$page}\n";
1073
	if ($reason) {
1074
		$backup_info .= "    {$reason}\n";
1075
	}
1076
	return $backup_info;
1077
}
1078

    
1079
function set_device_perms() {
1080
	$devices = array(
1081
		'pf' => array(
1082
			'user' => 'root',
1083
			'group' => 'proxy',
1084
			'mode' => 0660),
1085
		);
1086

    
1087
	foreach ($devices as $name => $attr) {
1088
		$path = "/dev/$name";
1089
		if (file_exists($path)) {
1090
			chown($path, $attr['user']);
1091
			chgrp($path, $attr['group']);
1092
			chmod($path, $attr['mode']);
1093
		}
1094
	}
1095
}
1096

    
1097
function get_config_user() {
1098
	if (empty($_SESSION["Username"])) {
1099
		$username = getenv("USER");
1100
		if (empty($conuser) || $conuser == "root") {
1101
			$username = "(system)";
1102
		}
1103
	} else {
1104
		$username = $_SESSION["Username"];
1105
	}
1106

    
1107
	if (!empty($_SERVER['REMOTE_ADDR'])) {
1108
		$username .= '@' . get_user_remote_address() . get_user_remote_authsource();
1109
	}
1110

    
1111
	return $username;
1112
}
1113

    
1114
function make_config_revision_entry($desc = null, $override_user = null) {
1115
	if (empty($override_user)) {
1116
		$username = get_config_user();
1117
	} else {
1118
		$username = $override_user;
1119
	}
1120

    
1121
	$revision = array();
1122

    
1123
	if (time() > mktime(0, 0, 0, 9, 1, 2004)) {     /* make sure the clock settings are plausible */
1124
		$revision['time'] = time();
1125
	}
1126

    
1127
	/* Log the running script so it's not entirely unlogged what changed */
1128
	if ($desc == "Unknown") {
1129
		$desc = sprintf(gettext("%s made unknown change"), $_SERVER['SCRIPT_NAME']);
1130
	}
1131
	if (!empty($desc)) {
1132
		$revision['description'] = "{$username}: " . $desc;
1133
	}
1134
	$revision['username'] = $username;
1135
	return $revision;
1136
}
1137

    
1138
function pfSense_clear_globals() {
1139
	global $config, $g, $FilterIfList, $GatewaysList, $filterdns, $aliases, $aliastable;
1140

    
1141
	$error = error_get_last();
1142

    
1143
	// Errors generated by user code (diag_commands.php) are identified by path and not added to notices
1144
	if ($error !== NULL && !preg_match('|^' . preg_quote($g['tmp_path_user_code']) . '/[^/]{1,16}$|', $error['file'])) {
1145
		if (in_array($error['type'], array(E_ERROR, E_COMPILE_ERROR, E_CORE_ERROR, E_RECOVERABLE_ERROR))) {
1146
			$errorstr = "PHP ERROR: Type: {$error['type']}, File: {$error['file']}, Line: {$error['line']}, Message: {$error['message']}";
1147
			print($errorstr);
1148
			log_error($errorstr);
1149
			file_notice("phperror", $errorstr, 'PHP errors');
1150
		} else if ($error['type'] != E_NOTICE) {
1151
			$errorstr = "PHP WARNING: Type: {$error['type']}, File: {$error['file']}, Line: {$error['line']}, Message: {$error['message']}";
1152
			// XXX: comment out for now, should re-enable post-2.2
1153
			//print($errorstr);
1154
			//log_error($errorstr);
1155
			//file_notice("phpwarning", $errorstr, 'PHP warning');
1156
		}
1157
	}
1158

    
1159
	if (isset($FilterIfList)) {
1160
		unset($FilterIfList);
1161
	}
1162

    
1163
	if (isset($GatewaysList)) {
1164
		unset($GatewaysList);
1165
	}
1166

    
1167
	/* Used for the hostname dns resolver */
1168
	if (isset($filterdns)) {
1169
		unset($filterdns);
1170
	}
1171

    
1172
	/* Used for aliases and interface macros */
1173
	if (isset($aliases)) {
1174
		unset($aliases);
1175
	}
1176
	if (isset($aliastable)) {
1177
		unset($aliastable);
1178
	}
1179

    
1180
	unset($config);
1181
}
1182

    
1183
/* Initialize a config array multiple levels deep only if unset
1184
 * Pass it an array of keys to test and create
1185
 * init_config_arr(array('virtualip', 'vip'));
1186
 */
1187
function init_config_arr($keys) {
1188
	global $config;
1189
	$c = &$config;
1190
	if (!is_array($keys)) {
1191
		return null;
1192
	}
1193
	foreach ($keys as $k) {
1194
		if (!is_array($c[$k])) {
1195
			$c[$k] = array();
1196
		}
1197
		$c = &$c[$k];
1198
	}
1199
}
1200

    
1201
register_shutdown_function('pfSense_clear_globals');
1202

    
1203
?>
(11-11/61)