Project

General

Profile

Download (15.4 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-2022 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
// Set hostname
46
if ($_REQUEST['hostname']) {
47
	$hostname = $_REQUEST['hostname'];
48
} else {
49
	$hostname = $config['system']['hostname'] . "." . $config['system']['domain'];
50
}
51

    
52
// Hostname of local machine
53
$myhostname = $config['system']['hostname'] . "." . $config['system']['domain'];
54

    
55
if (!$decrypt_password) {
56
	Header("Location: /services_acb_settings.php");
57
	exit;
58
}
59

    
60
if ($_REQUEST['savemsg']) {
61
	$savemsg = htmlentities($_REQUEST['savemsg']);
62
}
63

    
64
if ($_REQUEST['download']) {
65
	$pgtitle = array("Services", "Auto Configuration Backup", "Revision Information");
66
} else {
67
	$pgtitle = array("Services", "Auto Configuration Backup", "Restore");
68
}
69

    
70
/* Set up time zones for conversion. See #5250 */
71
$acbtz = new DateTimeZone('America/Chicago');
72
$mytz = new DateTimeZone(date_default_timezone_get());
73

    
74
include("head.inc");
75

    
76

    
77
if ($_REQUEST['rmver'] != "") {
78
	$curl_session = curl_init();
79
	curl_setopt($curl_session, CURLOPT_URL, "https://acb.netgate.com/rmbkp");
80
	curl_setopt($curl_session, CURLOPT_POSTFIELDS, "userkey=" . $userkey .
81
		"&revision=" . urlencode($_REQUEST['rmver']) .
82
		"&version=" . $g['product_version'] .
83
		"&uid=" . urlencode($uniqueID));
84
	curl_setopt($curl_session, CURLOPT_POST, 3);
85
	curl_setopt($curl_session, CURLOPT_SSL_VERIFYPEER, 1);
86
	curl_setopt($curl_session, CURLOPT_RETURNTRANSFER, 1);
87
	curl_setopt($curl_session, CURLOPT_USERAGENT, $g['product_label'] . '/' . rtrim(file_get_contents("/etc/version")));
88
	// Proxy
89
	set_curlproxy($curl_session);
90

    
91
	$data = curl_exec($curl_session);
92
	if (curl_errno($curl_session)) {
93
		$fd = fopen("/tmp/acb_deletedebug.txt", "w");
94
		fwrite($fd, "https://acb.netgate.com/rmbkp" . "" . "action=delete&hostname=" . urlencode($hostname) . "&revision=" . urlencode($_REQUEST['rmver']) . "\n\n");
95
		fwrite($fd, $data);
96
		fwrite($fd, curl_error($curl_session));
97
		fclose($fd);
98
		$savemsg = "An error occurred while trying to remove the item from acb.netgate.com.";
99
	} else {
100
		curl_close($curl_session);
101
		$budate = new DateTime($_REQUEST['rmver'], $acbtz);
102
		$budate->setTimezone($mytz);
103
		$savemsg = "Backup revision " . htmlspecialchars($budate->format(DATE_RFC2822)) . " has been removed.";
104
	}
105
}
106

    
107
if ($_REQUEST['newver'] != "") {
108
	// Phone home and obtain backups
109
	$curl_session = curl_init();
110

    
111
	curl_setopt($curl_session, CURLOPT_URL, "https://acb.netgate.com/getbkp");
112
	curl_setopt($curl_session, CURLOPT_POSTFIELDS, "userkey=" . $userkey .
113
		"&revision=" . urlencode($_REQUEST['newver']) .
114
		"&version=" . $g['product_version'] .
115
		"&uid=" . urlencode($uniqueID));
116
	curl_setopt($curl_session, CURLOPT_POST, 3);
117
	curl_setopt($curl_session, CURLOPT_SSL_VERIFYPEER, 1);
118
	curl_setopt($curl_session, CURLOPT_RETURNTRANSFER, 1);
119
	curl_setopt($curl_session, CURLOPT_USERAGENT, $g['product_label'] . '/' . rtrim(file_get_contents("/etc/version")));
120
	// Proxy
121
	set_curlproxy($curl_session);
122
	$data = curl_exec($curl_session);
123
	$data_split = explode('++++', $data);
124
	$sha256 = trim($data_split[0]);
125
	$data = $data_split[1];
126

    
127
	if (!tagfile_deformat($data, $data, "config.xml")) {
128
		$input_errors[] = "The downloaded file does not appear to contain an encrypted pfSense configuration.";
129
	}
130

    
131
	$out = decrypt_data($data, $decrypt_password);
132

    
133
	$pos = stripos($out, "</pfsense>");
134
	$data = substr($out, 0, $pos);
135
	$data = $data . "</pfsense>\n";
136

    
137
	$fd = fopen("/tmp/config_restore.xml", "w");
138
	fwrite($fd, $data);
139
	fclose($fd);
140

    
141
	if (strlen($data) < 50) {
142
		$input_errors[] = "The decrypted config.xml is under 50 characters, something went wrong. Aborting.";
143
	}
144

    
145
	$ondisksha256 = trim(shell_exec("/sbin/sha256 /tmp/config_restore.xml | /usr/bin/awk '{ print $4 }'"));
146
	// We might not have a sha256 on file for older backups
147
	if ($sha256 != "0" && $sha256 != "") {
148
		if ($ondisksha256 != $sha256) {
149
			$input_errors[] = "SHA256 values do not match, cannot restore. $ondisksha256 != $sha256";
150
		}
151
	}
152
	if (curl_errno($curl_session)) {
153
		/* If an error occurred, log the error in /tmp/ */
154
		$fd = fopen("/tmp/acb_restoredebug.txt", "w");
155
		fwrite($fd, "https://acb.netgate.com/getbkp" . "" . "action=restore&hostname={$hostname}&revision=" . urlencode($_REQUEST['newver']) . "\n\n");
156
		fwrite($fd, $data);
157
		fwrite($fd, curl_error($curl_session));
158
		fclose($fd);
159
	} else {
160
		curl_close($curl_session);
161
	}
162

    
163
	if (!$input_errors && $data) {
164
		if (config_restore("/tmp/config_restore.xml") == 0) {
165
			$savemsg = "Successfully reverted the pfSense configuration to revision " . urldecode($_REQUEST['newver']) . ".";
166
			$savemsg .= <<<EOF
167
			<br />
168
		<form action="diag_reboot.php" method="post">
169
			Reboot the firewall to full activate changes?
170
			<input name="rebootmode" type="hidden" value="Reboot" />
171
			<input name="Submit" type="submit" class="formbtn" value=" Yes " />
172
		</form>
173
EOF;
174
		} else {
175
			$savemsg = "Unable to revert to the selected configuration.";
176
		}
177
	} else {
178
		log_error("There was an error when restoring the AutoConfigBackup item");
179
	}
180
	unlink_if_exists("/tmp/config_restore.xml");
181
}
182

    
183
if ($_REQUEST['download']) {
184
	// Phone home and obtain backups
185
	$curl_session = curl_init();
186

    
187
	curl_setopt($curl_session, CURLOPT_URL, "https://acb.netgate.com/getbkp");
188
	curl_setopt($curl_session, CURLOPT_POSTFIELDS, "userkey=" . $userkey . "&revision=" . urlencode($_REQUEST['download']));
189
	curl_setopt($curl_session, CURLOPT_POST, 3);
190
	curl_setopt($curl_session, CURLOPT_SSL_VERIFYPEER, 1);
191
	curl_setopt($curl_session, CURLOPT_RETURNTRANSFER, 1);
192

    
193
	curl_setopt($curl_session, CURLOPT_USERAGENT, $g['product_label'] . '/' . rtrim(file_get_contents("/etc/version")));
194
	// Proxy
195
	set_curlproxy($curl_session);
196
	$data = curl_exec($curl_session);
197

    
198
	if (curl_errno($curl_session)) {
199
		$fd = fopen("/tmp/acb_backupdebug.txt", "w");
200
		fwrite($fd, "https://acb.netgate.com/getbkp" . "" . "action=sgetbackup" . "\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 (!tagfile_deformat($data, $data1, "config.xml")) {
209
		$input_errors[] = "The downloaded file does not appear to contain an encrypted pfSense configuration.";
210
	} else {
211
		$ds = explode('++++', $data);
212
		$revision = $_REQUEST['download'];
213
		$sha256sum = $ds[0];
214
		if ($sha256sum == "0") {
215
			$sha256sum = "None on file.";
216
		}
217
		$data = $ds[1];
218
		$configtype = "Encrypted";
219
		if (!tagfile_deformat($data, $data, "config.xml")) {
220
			$input_errors[] = "The downloaded file does not appear to contain an encrypted pfSense configuration.";
221
		}
222
		$data = decrypt_data($data, $decrypt_password);
223
		if (!strstr($data, "pfsense")) {
224
			$data = "Could not decrypt. Different encryption key?";
225
			$input_errors[] = "Could not decrypt config.xml";
226
		}
227
	}
228
}
229

    
230
// $confvers must be populated viewing info but there were errors
231
$confvers = array();
232
if ((!($_REQUEST['download']) || $input_errors) && check_dnsavailable()) {
233
	// Populate available backups
234
	$curl_session = curl_init();
235

    
236
	curl_setopt($curl_session, CURLOPT_URL, "https://acb.netgate.com/list");
237
	curl_setopt($curl_session, CURLOPT_POSTFIELDS, "userkey=" . $userkey .
238
		"&uid=eb6a4e6f76c10734b636" .
239
		"&version=" . $g['product_version'] .
240
		"&uid=" . urlencode($uniqueID));
241
	curl_setopt($curl_session, CURLOPT_SSL_VERIFYPEER, 1);
242
	curl_setopt($curl_session, CURLOPT_POST, 1);
243
	curl_setopt($curl_session, CURLOPT_RETURNTRANSFER, 1);
244

    
245
	curl_setopt($curl_session, CURLOPT_USERAGENT, $g['product_label'] . '/' . rtrim(file_get_contents("/etc/version")));
246
	// Proxy
247
	set_curlproxy($curl_session);
248

    
249
	$data = curl_exec($curl_session);
250

    
251
	if (curl_errno($curl_session)) {
252
		$fd = fopen("/tmp/acb_backupdebug.txt", "w");
253
		fwrite($fd, "https://acb.netgate.com/list" . "" . "action=showbackups" . "\n\n");
254
		fwrite($fd, $data);
255
		fwrite($fd, curl_error($curl_session));
256
		fclose($fd);
257
	} else {
258
		curl_close($curl_session);
259
	}
260

    
261
	// Loop through and create new confvers
262
	$data_split = explode("\n", $data);
263

    
264
	foreach ($data_split as $ds) {
265
		$ds_split = explode($exp_sep, $ds);
266
		$tmp_array = array();
267
		$tmp_array['username'] = $ds_split[0];
268
		$tmp_array['reason'] = $ds_split[1];
269
		$tmp_array['time'] = $ds_split[2];
270

    
271
		/* Convert the time from server time to local. See #5250 */
272
		$budate = new DateTime($tmp_array['time'], $acbtz);
273
		$budate->setTimezone($mytz);
274
		$tmp_array['localtime'] = $budate->format(DATE_RFC2822);
275

    
276
		if ($ds_split[2] && $ds_split[0]) {
277
			$confvers[] = $tmp_array;
278
		}
279
	}
280
}
281

    
282
if ($input_errors) {
283
	print_input_errors($input_errors);
284
}
285
if ($savemsg) {
286
	print_info_box($savemsg, 'success');
287
}
288

    
289
$tab_array = array();
290
$tab_array[0] = array("Settings", false, "/services_acb_settings.php");
291
if ($_REQUEST['download']) {
292
	$active = false;
293
} else {
294
	$active = true;
295
}
296

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

    
299
if ($_REQUEST['download']) {
300
	$tab_array[] = array("Revision", true, "/services_acb.php?download=" . htmlspecialchars($_REQUEST['download']));
301
}
302

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

    
305
display_top_tabs($tab_array);
306
?>
307

    
308
<div id="loading">
309
	<i class="fa fa-spinner fa-spin"></i> Loading, please wait...
310
</div>
311

    
312

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

    
315
$form = new Form(false);
316

    
317
$section = new Form_Section('Backup Details');
318

    
319
$section->addInput(new Form_Input(
320
	'download',
321
	'Revision date/time',
322
	'text',
323
	$_REQUEST['download']
324
))->setWidth(7)->setReadOnly();
325

    
326
$section->addInput(new Form_Input(
327
	'reason',
328
	'Revision Reason',
329
	'text',
330
	$_REQUEST['reason']
331
))->setWidth(7)->setReadOnly();
332

    
333
$section->addInput(new Form_Input(
334
	'shasum',
335
	'SHA256 summary',
336
	'text',
337
	$sha256sum
338
))->setWidth(7)->setReadOnly();
339

    
340
$section->addInput(new Form_Textarea(
341
	'config_xml',
342
	'Encrypted config.xml',
343
	$ds[1]
344
))->setWidth(7)->setAttribute("rows", "40")->setAttribute("wrap", "off");
345

    
346
$section->addInput(new Form_Textarea(
347
	'dec_config_xml',
348
	'Decrypted config.xml',
349
	$data
350
))->setWidth(7)->setAttribute("rows", "40")->setAttribute("wrap", "off");
351

    
352
$form->add($section);
353

    
354
print($form);
355

    
356
?>
357
<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>
358

    
359
<?php else:
360

    
361
$section2 = new Form_Section('Device key');
362
$group = new Form_Group("Device key");
363

    
364
$group->add(new Form_Input(
365
	'devkey',
366
	'Device key',
367
	'text',
368
	$userkey
369
))->setWidth(7)->setHelp("ID used to identify this firewall (derived from the SSH public key.) " .
370
	"See help below for more details. %sPlease make a safe copy of this ID value.%s If it is lost, your backups will" .
371
	" be lost too!", "<strong>", "</strong>");
372

    
373
$group->add(new Form_Button(
374
	'upduserkey',
375
	'Submit',
376
	null,
377
	'fa-save'
378
))->addClass('btn-success btn-xs');
379

    
380
$group->add(new Form_Button(
381
	'restore',
382
	'Reset',
383
	null,
384
	'fa-refresh'
385
))->addClass('btn-info btn-xs');
386

    
387
$section2->add($group);
388
print($section2);
389

    
390
print('<div class="infoblock">');
391
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." .
392
	" 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\"." .
393
	" This will temporarily override the ID for this session."), 'info', false);
394
print('</div>');
395

    
396
?>
397
<div class="panel panel-default">
398
	<div class="panel-heading"><h2 class="panel-title"><?=gettext("Automatic Configuration Backups")?></h2></div>
399
	<div class="panel-body">
400
		<div class="table-responsive">
401
		</div>
402
		<div class="table-responsive">
403
			<table class="table table-striped table-hover table-condensed sortable-theme-bootstrap" id="backups" data-sortable>
404
				<thead>
405
					<tr>
406
						<th data-sortable-type="date" width="30%"><?=gettext("Date")?></th>
407
						<th width="60%"><?=gettext("Configuration Change")?></th>
408
						<th width="10%"><?=gettext("Actions")?></th>
409
					</tr>
410
				</thead>
411
				<tbody>
412

    
413
			<?php
414
				$counter = 0;
415
				foreach ($confvers as $cv):
416
			?>
417
					<tr>
418
						<td><?= $cv['localtime']; ?></td>
419
						<td><?= $cv['reason']; ?></td>
420
						<td>
421
							<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>
422
							<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>
423
<?php
424
		if ($userkey == $origkey) {
425
?>
426
							<a class="fa fa-trash"		title="<?=gettext('Delete config')?>"	href="services_acb.php?hostname=<?=urlencode($hostname)?>&rmver=<?=urlencode($cv['time'])?>"></a>
427
<?php 	} ?>
428
						</td>
429
					</tr>
430
				<?php	$counter++;
431
				endforeach;
432
				?>
433
				</tbody>
434
			</table>
435
			<table class="table table-striped table-hover table-condensed" id="backups" data-sortable>
436
			<?php if ($counter == 0): ?>
437
				<tr>
438
					<td colspan="3" align="center" class="text-danger"><strong>
439
						<?=gettext("No backups could be located for this device.")?>
440
						</strong>
441
					</td>
442
				</tr>
443
			<?php else: ?>
444
				<tr>
445
					<td colspan="3" align="center">
446
						<br /><?=gettext("Current count of hosted backups")?> : <?= $counter ?>
447
					</td>
448
				</tr>
449
			<?php endif; ?>
450
			</table>
451
		</div>
452
	</div>
453
</div>
454
<?php
455

    
456
endif; ?>
457

    
458
</form>
459

    
460
<script type="text/javascript">
461
//<![CDATA[
462
events.push(function(){
463
	$('#loading').hide();
464

    
465
	// On clicking Submit", reload the page but with a POST parameter "userkey" set
466
	$('#upduserkey').click(function() {
467
		var $form = $('<form>');
468
		var newuserkey = $('#devkey').val();
469

    
470
		$form
471
			.attr("method", "POST")
472
			.attr("action", '/services_acb.php')
473
			// The CSRF magic is required because we will be viewing the results of the POST
474
			.append(
475
				$("<input>")
476
					.attr("type", "hidden")
477
					.attr("name", "__csrf_magic")
478
					.val(csrfMagicToken)
479
			)
480
			.append(
481
			$("<input>")
482
				.attr("type", "hidden")
483
				.attr("name", "userkey")
484
				.val(newuserkey)
485
			)
486
			.appendTo('body')
487
			.submit();
488
	});
489

    
490
	$('#restore').click(function() {
491
		$('#devkey').val("<?=$origkey?>");
492
	});
493
});
494
//]]>
495
</script>
496

    
497
<?php include("foot.inc"); ?>
(100-100/228)