Project

General

Profile

Download (16.5 KB) Statistics
| Branch: | Tag: | Revision:
1
<?php
2
/*
3
 * services_acb.php
4
 *
5
 * part of pfSense (https://www.pfsense.org)
6
 * Copyright (c) 2008-2013 BSD Perimeter
7
 * Copyright (c) 2013-2016 Electric Sheep Fencing
8
 * Copyright (c) 2014-2019 Rubicon Communications, LLC (Netgate)
9
 * All rights reserved.
10
 *
11
 * Licensed under the Apache License, Version 2.0 (the "License");
12
 * you may not use this file except in compliance with the License.
13
 * You may obtain a copy of the License at
14
 *
15
 * http://www.apache.org/licenses/LICENSE-2.0
16
 *
17
 * Unless required by applicable law or agreed to in writing, software
18
 * distributed under the License is distributed on an "AS IS" BASIS,
19
 * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
20
 * See the License for the specific language governing permissions and
21
 * limitations under the License.
22
 */
23

    
24
##|+PRIV
25
##|*IDENT=page-services-acb
26
##|*NAME=Services: Auto Config Backup: Restore
27
##|*DESCR=Restore from auto config backup.
28
##|*MATCH=services_acb.php*
29
##|-PRIV
30

    
31
require("guiconfig.inc");
32
require("acb.inc");
33

    
34
// Separator used during client / server communications
35
$oper_sep = "\|\|";
36
$exp_sep = '||';
37

    
38
// Encryption password
39
$decrypt_password = $config['system']['acb']['encryption_password'];
40

    
41
// Defined username. Username must be sent lowercase. See Redmine #7127 and Netgate Redmine #163
42
$username = strtolower($config['system']['acb']['gold_username']);
43
$password = $config['system']['acb']['gold_password'];
44

    
45
// URL to restore.php
46
$get_url = "https://portal.pfsense.org/pfSconfigbackups/restore.php";
47

    
48
// URL to stats
49
$stats_url = "https://portal.pfsense.org/pfSconfigbackups/showstats.php";
50

    
51
// URL to delete.php
52
$del_url = "https://portal.pfsense.org/pfSconfigbackups/delete.php";
53

    
54
// Set hostname
55
if ($_REQUEST['hostname']) {
56
	$hostname = $_REQUEST['hostname'];
57
} else {
58
	$hostname = $config['system']['hostname'] . "." . $config['system']['domain'];
59
}
60

    
61
// Hostname of local machine
62
$myhostname = $config['system']['hostname'] . "." . $config['system']['domain'];
63

    
64
if (!$decrypt_password) {
65
	Header("Location: /services_acb_settings.php");
66
	exit;
67
}
68

    
69
if ($_REQUEST['savemsg']) {
70
	$savemsg = htmlentities($_REQUEST['savemsg']);
71
}
72

    
73
if ($_REQUEST['download']) {
74
	$pgtitle = array("Services", "Auto Configuration Backup", "Revision Information");
75
} else {
76
	$pgtitle = array("Services", "Auto Configuration Backup", "Restore");
77
}
78

    
79
/* Set up time zones for conversion. See #5250 */
80
$acbtz = new DateTimeZone('America/Chicago');
81
$mytz = new DateTimeZone(date_default_timezone_get());
82

    
83
include("head.inc");
84

    
85
function get_hostnames() {
86
	global $stats_url, $username, $password, $oper_sep, $config, $g, $exp_sep;
87
	// Populate available backups
88
	$curl_session = curl_init();
89
	curl_setopt($curl_session, CURLOPT_URL, $stats_url);
90
	curl_setopt($curl_session, CURLOPT_HTTPHEADER, array("Authorization: Basic " . base64_encode("{$username}:{$password}")));
91
	curl_setopt($curl_session, CURLOPT_SSL_VERIFYPEER, 1);
92
	curl_setopt($curl_session, CURLOPT_POST, 1);
93
	curl_setopt($curl_session, CURLOPT_RETURNTRANSFER, 1);
94
	curl_setopt($curl_session, CURLOPT_POSTFIELDS, "action=showstats");
95
	curl_setopt($curl_session, CURLOPT_USERAGENT, $g['product_name'] . '/' . rtrim(file_get_contents("/etc/version")));
96
	// Proxy
97
	curl_setopt_array($curl_session, configure_proxy());
98

    
99
	$data = curl_exec($curl_session);
100
	if (curl_errno($curl_session)) {
101
		$fd = fopen("/tmp/acb_statsdebug.txt", "w");
102
		fwrite($fd, $stats_url . "" . "action=showstats" . "\n\n");
103
		fwrite($fd, $data);
104
		fwrite($fd, curl_error($curl_session));
105
		fclose($fd);
106
	} else {
107
		curl_close($curl_session);
108
	}
109

    
110
	// Loop through and create new confvers
111
	$data_split = explode("\n", $data);
112
	$statvers = array();
113
	foreach ($data_split as $ds) {
114
		$ds_split = explode($exp_sep, $ds);
115
		if ($ds_split[0]) {
116
			$statvers[] = $ds_split[0];
117
		}
118
	}
119
	return $statvers;
120
}
121

    
122
if ($_REQUEST['rmver'] != "") {
123
	$curl_session = curl_init();
124
	curl_setopt($curl_session, CURLOPT_URL, "https://acb.netgate.com/rmbkp");
125
	curl_setopt($curl_session, CURLOPT_POSTFIELDS, "userkey=" . $userkey .
126
		"&revision=" . urlencode($_REQUEST['rmver']) .
127
		"&version=" . $g['product_version'] .
128
		"&uid=" . urlencode($uniqueID));
129
	curl_setopt($curl_session, CURLOPT_POST, 3);
130
	curl_setopt($curl_session, CURLOPT_SSL_VERIFYPEER, 1);
131
	curl_setopt($curl_session, CURLOPT_RETURNTRANSFER, 1);
132
	curl_setopt($curl_session, CURLOPT_USERAGENT, $g['product_name'] . '/' . rtrim(file_get_contents("/etc/version")));
133
	// Proxy
134
	curl_setopt_array($curl_session, configure_proxy());
135

    
136
	$data = curl_exec($curl_session);
137
	if (curl_errno($curl_session)) {
138
		$fd = fopen("/tmp/acb_deletedebug.txt", "w");
139
		fwrite($fd, $get_url . "" . "action=delete&hostname=" . urlencode($hostname) . "&revision=" . urlencode($_REQUEST['rmver']) . "\n\n");
140
		fwrite($fd, $data);
141
		fwrite($fd, curl_error($curl_session));
142
		fclose($fd);
143
		$savemsg = "An error occurred while trying to remove the item from portal.pfsense.org.";
144
	} else {
145
		curl_close($curl_session);
146
		$budate = new DateTime($_REQUEST['rmver'], $acbtz);
147
		$budate->setTimezone($mytz);
148
		$savemsg = "Backup revision " . htmlspecialchars($budate->format(DATE_RFC2822)) . " has been removed.";
149
	}
150
}
151

    
152
if ($_REQUEST['newver'] != "") {
153
	// Phone home and obtain backups
154
	$curl_session = curl_init();
155

    
156
	curl_setopt($curl_session, CURLOPT_URL, "https://acb.netgate.com/getbkp");
157
	curl_setopt($curl_session, CURLOPT_POSTFIELDS, "userkey=" . $userkey .
158
		"&revision=" . urlencode($_REQUEST['newver']) .
159
		"&version=" . $g['product_version'] .
160
		"&uid=" . urlencode($uniqueID));
161
	curl_setopt($curl_session, CURLOPT_POST, 3);
162
	curl_setopt($curl_session, CURLOPT_SSL_VERIFYPEER, 1);
163
	curl_setopt($curl_session, CURLOPT_RETURNTRANSFER, 1);
164
	curl_setopt($curl_session, CURLOPT_USERAGENT, $g['product_name'] . '/' . rtrim(file_get_contents("/etc/version")));
165
	// Proxy
166
	curl_setopt_array($curl_session, configure_proxy());
167
	$data = curl_exec($curl_session);
168
	$data_split = explode('++++', $data);
169
	$sha256 = trim($data_split[0]);
170
	$data = $data_split[1];
171

    
172
	if (!tagfile_deformat($data, $data, "config.xml")) {
173
		$input_errors[] = "The downloaded file does not appear to contain an encrypted pfSense configuration.";
174
	}
175

    
176
	$out = decrypt_data($data, $decrypt_password);
177

    
178
	$pos = stripos($out, "</pfsense>");
179
	$data = substr($out, 0, $pos);
180
	$data = $data . "</pfsense>\n";
181

    
182
	$fd = fopen("/tmp/config_restore.xml", "w");
183
	fwrite($fd, $data);
184
	fclose($fd);
185

    
186
	if (strlen($data) < 50) {
187
		$input_errors[] = "The decrypted config.xml is under 50 characters, something went wrong. Aborting.";
188
	}
189

    
190
	$ondisksha256 = trim(shell_exec("/sbin/sha256 /tmp/config_restore.xml | /usr/bin/awk '{ print $4 }'"));
191
	// We might not have a sha256 on file for older backups
192
	if ($sha256 != "0" && $sha256 != "") {
193
		if ($ondisksha256 != $sha256) {
194
			$input_errors[] = "SHA256 values do not match, cannot restore. $ondisksha256 != $sha256";
195
		}
196
	}
197
	if (curl_errno($curl_session)) {
198
		/* If an error occurred, log the error in /tmp/ */
199
		$fd = fopen("/tmp/acb_restoredebug.txt", "w");
200
		fwrite($fd, $get_url . "" . "action=restore&hostname={$hostname}&revision=" . urlencode($_REQUEST['newver']) . "\n\n");
201
		fwrite($fd, $data);
202
		fwrite($fd, curl_error($curl_session));
203
		fclose($fd);
204
	} else {
205
		curl_close($curl_session);
206
	}
207

    
208
	if (!$input_errors && $data) {
209
		if (config_restore("/tmp/config_restore.xml") == 0) {
210
			$savemsg = "Successfully reverted the pfSense configuration to revision " . urldecode($_REQUEST['newver']) . ".";
211
			$savemsg .= <<<EOF
212
			<br />
213
		<form action="diag_reboot.php" method="post">
214
			Reboot the firewall to full activate changes?
215
			<input name="override" type="hidden" value="yes" />
216
			<input name="Submit" type="submit" class="formbtn" value=" Yes " />
217
		</form>
218
EOF;
219
		} else {
220
			$savemsg = "Unable to revert to the selected configuration.";
221
		}
222
	} else {
223
		log_error("There was an error when restoring the AutoConfigBackup item");
224
	}
225
	unlink_if_exists("/tmp/config_restore.xml");
226
}
227

    
228
if ($_REQUEST['download']) {
229
	// Phone home and obtain backups
230
	$curl_session = curl_init();
231

    
232
	curl_setopt($curl_session, CURLOPT_URL, "https://acb.netgate.com/getbkp");
233
	curl_setopt($curl_session, CURLOPT_POSTFIELDS, "userkey=" . $userkey . "&revision=" . urlencode($_REQUEST['download']));
234
	curl_setopt($curl_session, CURLOPT_POST, 3);
235
	curl_setopt($curl_session, CURLOPT_SSL_VERIFYPEER, 1);
236
	curl_setopt($curl_session, CURLOPT_RETURNTRANSFER, 1);
237

    
238
	curl_setopt($curl_session, CURLOPT_USERAGENT, $g['product_name'] . '/' . rtrim(file_get_contents("/etc/version")));
239
	// Proxy
240
	curl_setopt_array($curl_session, configure_proxy());
241
	$data = curl_exec($curl_session);
242

    
243
	if (!tagfile_deformat($data, $data1, "config.xml")) {
244
		$input_errors[] = "The downloaded file does not appear to contain an encrypted pfSense configuration.";
245
	} else {
246
		$ds = explode('++++', $data);
247
		$revision = $_REQUEST['download'];
248
		$sha256sum = $ds[0];
249
		if ($sha256sum == "0") {
250
			$sha256sum = "None on file.";
251
		}
252
		$data = $ds[1];
253
		$configtype = "Encrypted";
254
		if (!tagfile_deformat($data, $data, "config.xml")) {
255
			$input_errors[] = "The downloaded file does not appear to contain an encrypted pfSense configuration.";
256
		}
257
		$data = decrypt_data($data, $decrypt_password);
258
		if (!strstr($data, "pfsense")) {
259
			$data = "Could not decrypt. Different encryption key?";
260
			$input_errors[] = "Could not decrypt config.xml";
261
		}
262
	}
263
}
264

    
265
// $confvers must be populated viewing info but there were errors
266
if ( !($_REQUEST['download']) || $input_errors) {
267
	// Populate available backups
268
	$curl_session = curl_init();
269

    
270
	curl_setopt($curl_session, CURLOPT_URL, "https://acb.netgate.com/list");
271
	curl_setopt($curl_session, CURLOPT_POSTFIELDS, "userkey=" . $userkey .
272
		"&uid=eb6a4e6f76c10734b636" .
273
		"&version=" . $g['product_version'] .
274
		"&uid=" . urlencode($uniqueID));
275
	curl_setopt($curl_session, CURLOPT_SSL_VERIFYPEER, 1);
276
	curl_setopt($curl_session, CURLOPT_POST, 1);
277
	curl_setopt($curl_session, CURLOPT_RETURNTRANSFER, 1);
278

    
279
	curl_setopt($curl_session, CURLOPT_USERAGENT, $g['product_name'] . '/' . rtrim(file_get_contents("/etc/version")));
280
	// Proxy
281
	curl_setopt_array($curl_session, configure_proxy());
282

    
283
	$data = curl_exec($curl_session);
284

    
285
	if (curl_errno($curl_session)) {
286
		$fd = fopen("/tmp/acb_backupdebug.txt", "w");
287
		fwrite($fd, $get_url . "" . "action=showbackups" . "\n\n");
288
		fwrite($fd, $data);
289
		fwrite($fd, curl_error($curl_session));
290
		fclose($fd);
291
	} else {
292
		curl_close($curl_session);
293
	}
294

    
295
	// Loop through and create new confvers
296
	$data_split = explode("\n", $data);
297

    
298
	$confvers = array();
299

    
300
	foreach ($data_split as $ds) {
301
		$ds_split = explode($exp_sep, $ds);
302
		$tmp_array = array();
303
		$tmp_array['username'] = $ds_split[0];
304
		$tmp_array['reason'] = $ds_split[1];
305
		$tmp_array['time'] = $ds_split[2];
306

    
307
		/* Convert the time from server time to local. See #5250 */
308
		$budate = new DateTime($tmp_array['time'], $acbtz);
309
		$budate->setTimezone($mytz);
310
		$tmp_array['localtime'] = $budate->format(DATE_RFC2822);
311

    
312
		if ($ds_split[2] && $ds_split[0]) {
313
			$confvers[] = $tmp_array;
314
		}
315
	}
316
}
317

    
318
if ($input_errors) {
319
	print_input_errors($input_errors);
320
}
321
if ($savemsg) {
322
	print_info_box($savemsg, 'success');
323
}
324

    
325
$tab_array = array();
326
$tab_array[0] = array("Settings", false, "/services_acb_settings.php");
327
if ($_REQUEST['download']) {
328
	$active = false;
329
} else {
330
	$active = true;
331
}
332

    
333
$tab_array[1] = array("Restore", $active, "/services_acb.php");
334

    
335
if ($_REQUEST['download']) {
336
	$tab_array[] = array("Revision", true, "/services_acb.php?download=" . htmlspecialchars($_REQUEST['download']));
337
}
338

    
339
$tab_array[] = array("Backup now", false, "/services_acb_backup.php");
340

    
341
display_top_tabs($tab_array);
342

    
343
$hostnames = get_hostnames();
344
?>
345

    
346
<div id="loading">
347
	<i class="fa fa-spinner fa-spin"></i> Loading, please wait...
348
</div>
349

    
350

    
351
<?php if ($_REQUEST['download'] && (!$input_errors)):
352

    
353
$form = new Form(false);
354

    
355
$section = new Form_Section('Backup Details');
356

    
357
$section->addInput(new Form_Input(
358
	'download',
359
	'Revision date/time',
360
	'text',
361
	$_REQUEST['download']
362
))->setWidth(7)->setReadOnly();
363

    
364
$section->addInput(new Form_Input(
365
	'reason',
366
	'Revision Reason',
367
	'text',
368
	$_REQUEST['reason']
369
))->setWidth(7)->setReadOnly();
370

    
371
$section->addInput(new Form_Input(
372
	'shasum',
373
	'SHA256 summary',
374
	'text',
375
	$sha256sum
376
))->setWidth(7)->setReadOnly();
377

    
378
$section->addInput(new Form_Textarea(
379
	'config_xml',
380
	'Encrypted config.xml',
381
	$ds[1]
382
))->setWidth(7)->setAttribute("rows", "40")->setAttribute("wrap", "off");
383

    
384
$section->addInput(new Form_Textarea(
385
	'dec_config_xml',
386
	'Decrypted config.xml',
387
	$data
388
))->setWidth(7)->setAttribute("rows", "40")->setAttribute("wrap", "off");
389

    
390
$form->add($section);
391

    
392
print($form);
393

    
394
?>
395
<a class="btn btn-primary" title="<?=gettext('Restore this revision')?>" href="services_acb.php?newver=<?= urlencode($_REQUEST['download']) ?>" onclick="return confirm('<?=gettext("Are you sure you want to restore {$cv['localtime']}?")?>')"><i class="fa fa-undo"></i> Install this revision</a>
396

    
397
<?php else:
398

    
399
$section2 = new Form_Section('Device key');
400
$group = new Form_Group("Device key");
401

    
402
$group->add(new Form_Input(
403
	'devkey',
404
	'Device key',
405
	'text',
406
	$userkey
407
))->setWidth(7)->setHelp("ID used to identify this firewall (derived from the SSH public key.) " .
408
	"See help below for more details. %sPlease make a safe copy of this ID value.%s If it is lost, your backups will" .
409
	" be lost too!", "<strong>", "</strong>");
410

    
411
$group->add(new Form_Button(
412
	'upduserkey',
413
	'Submit',
414
	null,
415
	'fa-save'
416
))->addClass('btn-success btn-xs');
417

    
418
$group->add(new Form_Button(
419
	'restore',
420
	'Reset',
421
	null,
422
	'fa-refresh'
423
))->addClass('btn-info btn-xs');
424

    
425
$section2->add($group);
426
print($section2);
427

    
428
print('<div class="infoblock">');
429
print_info_box(gettext("The Device key listed above is derived from the SSH public key of the firewall. When a configuration is saved, it is identified by this value." .
430
	" If you are restoring the configuration of another firewall, paste the Device key from that firewall into the Device ID field above and click \"Submit\"." .
431
	" This will temporarily override the ID for this session."), 'info', false);
432
print('</div>');
433

    
434
?>
435
<div class="panel panel-default">
436
	<div class="panel-heading"><h2 class="panel-title"><?=gettext("Automatic Configuration Backups")?></h2></div>
437
	<div class="panel-body">
438
		<div class="table-responsive">
439
		</div>
440
		<div class="table-responsive">
441
			<table class="table table-striped table-hover table-condensed" id="backups">
442
				<thead>
443
					<tr>
444
						<th width="30%"><?=gettext("Date")?></th>
445
						<th width="60%"><?=gettext("Configuration Change")?></th>
446
						<th width="10%"><?=gettext("Actions")?></th>
447
					</tr>
448
				</thead>
449
				<tbody>
450

    
451
			<?php
452
				$counter = 0;
453
				foreach ($confvers as $cv):
454
			?>
455
					<tr>
456
						<td><?= $cv['localtime']; ?></td>
457
						<td><?= $cv['reason']; ?></td>
458
						<td>
459
							<a class="fa fa-undo"		title="<?=gettext('Restore this revision')?>"	href="services_acb.php?hostname=<?=urlencode($hostname)?>&userkey=<?=urlencode($userkey)?>&newver=<?=urlencode($cv['time'])?>"	onclick="return confirm('<?=gettext("Are you sure you want to restore {$cv['localtime']}?")?>')"></a>
460
							<a class="fa fa-download"	title="<?=gettext('Show info')?>"	href="services_acb.php?download=<?=urlencode($cv['time'])?>&hostname=<?=urlencode($hostname)?>&userkey=<?=urlencode($userkey)?>&reason=<?=urlencode($cv['reason'])?>"></a>
461
<?php
462
		if ($userkey == $origkey) {
463
?>
464
							<a class="fa fa-trash"		title="<?=gettext('Delete config')?>"	href="services_acb.php?hostname=<?=urlencode($hostname)?>&rmver=<?=urlencode($cv['time'])?>"></a>
465
<?php 	} ?>
466
						</td>
467
					</tr>
468
				<?php	$counter++;
469
				endforeach;
470
				if ($counter == 0): ?>
471
					<tr>
472
						<td colspan="3" align="center" class="text-danger"><strong>
473
							<?=gettext("No backups could be located for this device.")?>
474
							</strong>
475
						</td>
476
					</tr>
477
				<?php else: ?>
478
					<tr>
479
						<td colspan="3" align="center">
480
							<br /><?=gettext("Current count of hosted backups")?> : <?= $counter ?>
481
						</td>
482
					</tr>
483
				<?php endif; ?>
484
				</tbody>
485
			</table>
486
		</div>
487
	</div>
488
</div>
489
<?php
490

    
491
endif; ?>
492

    
493
</form>
494

    
495
<script type="text/javascript">
496
//<![CDATA[
497
events.push(function(){
498
	$('#loading').hide();
499

    
500
	// On clicking Submit", reload the page but with a POST parameter "userkey" set
501
	$('#upduserkey').click(function() {
502
		var $form = $('<form>');
503
		var newuserkey = $('#devkey').val();
504

    
505
		$form
506
			.attr("method", "POST")
507
			.attr("action", '/services_acb.php')
508
			// The CSRF magic is required because we will be viewing the results of the POST
509
			.append(
510
				$("<input>")
511
					.attr("type", "hidden")
512
					.attr("name", "__csrf_magic")
513
					.val(csrfMagicToken)
514
			)
515
			.append(
516
			$("<input>")
517
				.attr("type", "hidden")
518
				.attr("name", "userkey")
519
				.val(newuserkey)
520
			)
521
			.appendTo('body')
522
			.submit();
523
	});
524

    
525
	$('#restore').click(function() {
526
		$('#devkey').val("<?=$origkey?>");
527
	});
528
});
529
//]]>
530
</script>
531

    
532
<?php include("foot.inc"); ?>
(101-101/227)