Project

General

Profile

Download (15 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-2020 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_name'] . '/' . rtrim(file_get_contents("/etc/version")));
88
	// Proxy
89
	curl_setopt_array($curl_session, configure_proxy());
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_name'] . '/' . rtrim(file_get_contents("/etc/version")));
120
	// Proxy
121
	curl_setopt_array($curl_session, configure_proxy());
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="override" type="hidden" value="yes" />
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_name'] . '/' . rtrim(file_get_contents("/etc/version")));
194
	// Proxy
195
	curl_setopt_array($curl_session, configure_proxy());
196
	$data = curl_exec($curl_session);
197

    
198
	if (!tagfile_deformat($data, $data1, "config.xml")) {
199
		$input_errors[] = "The downloaded file does not appear to contain an encrypted pfSense configuration.";
200
	} else {
201
		$ds = explode('++++', $data);
202
		$revision = $_REQUEST['download'];
203
		$sha256sum = $ds[0];
204
		if ($sha256sum == "0") {
205
			$sha256sum = "None on file.";
206
		}
207
		$data = $ds[1];
208
		$configtype = "Encrypted";
209
		if (!tagfile_deformat($data, $data, "config.xml")) {
210
			$input_errors[] = "The downloaded file does not appear to contain an encrypted pfSense configuration.";
211
		}
212
		$data = decrypt_data($data, $decrypt_password);
213
		if (!strstr($data, "pfsense")) {
214
			$data = "Could not decrypt. Different encryption key?";
215
			$input_errors[] = "Could not decrypt config.xml";
216
		}
217
	}
218
}
219

    
220
// $confvers must be populated viewing info but there were errors
221
if ( !($_REQUEST['download']) || $input_errors) {
222
	// Populate available backups
223
	$curl_session = curl_init();
224

    
225
	curl_setopt($curl_session, CURLOPT_URL, "https://acb.netgate.com/list");
226
	curl_setopt($curl_session, CURLOPT_POSTFIELDS, "userkey=" . $userkey .
227
		"&uid=eb6a4e6f76c10734b636" .
228
		"&version=" . $g['product_version'] .
229
		"&uid=" . urlencode($uniqueID));
230
	curl_setopt($curl_session, CURLOPT_SSL_VERIFYPEER, 1);
231
	curl_setopt($curl_session, CURLOPT_POST, 1);
232
	curl_setopt($curl_session, CURLOPT_RETURNTRANSFER, 1);
233

    
234
	curl_setopt($curl_session, CURLOPT_USERAGENT, $g['product_name'] . '/' . rtrim(file_get_contents("/etc/version")));
235
	// Proxy
236
	curl_setopt_array($curl_session, configure_proxy());
237

    
238
	$data = curl_exec($curl_session);
239

    
240
	if (curl_errno($curl_session)) {
241
		$fd = fopen("/tmp/acb_backupdebug.txt", "w");
242
		fwrite($fd, "https://acb.netgate.com/list" . "" . "action=showbackups" . "\n\n");
243
		fwrite($fd, $data);
244
		fwrite($fd, curl_error($curl_session));
245
		fclose($fd);
246
	} else {
247
		curl_close($curl_session);
248
	}
249

    
250
	// Loop through and create new confvers
251
	$data_split = explode("\n", $data);
252

    
253
	$confvers = array();
254

    
255
	foreach ($data_split as $ds) {
256
		$ds_split = explode($exp_sep, $ds);
257
		$tmp_array = array();
258
		$tmp_array['username'] = $ds_split[0];
259
		$tmp_array['reason'] = $ds_split[1];
260
		$tmp_array['time'] = $ds_split[2];
261

    
262
		/* Convert the time from server time to local. See #5250 */
263
		$budate = new DateTime($tmp_array['time'], $acbtz);
264
		$budate->setTimezone($mytz);
265
		$tmp_array['localtime'] = $budate->format(DATE_RFC2822);
266

    
267
		if ($ds_split[2] && $ds_split[0]) {
268
			$confvers[] = $tmp_array;
269
		}
270
	}
271
}
272

    
273
if ($input_errors) {
274
	print_input_errors($input_errors);
275
}
276
if ($savemsg) {
277
	print_info_box($savemsg, 'success');
278
}
279

    
280
$tab_array = array();
281
$tab_array[0] = array("Settings", false, "/services_acb_settings.php");
282
if ($_REQUEST['download']) {
283
	$active = false;
284
} else {
285
	$active = true;
286
}
287

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

    
290
if ($_REQUEST['download']) {
291
	$tab_array[] = array("Revision", true, "/services_acb.php?download=" . htmlspecialchars($_REQUEST['download']));
292
}
293

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

    
296
display_top_tabs($tab_array);
297
?>
298

    
299
<div id="loading">
300
	<i class="fa fa-spinner fa-spin"></i> Loading, please wait...
301
</div>
302

    
303

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

    
306
$form = new Form(false);
307

    
308
$section = new Form_Section('Backup Details');
309

    
310
$section->addInput(new Form_Input(
311
	'download',
312
	'Revision date/time',
313
	'text',
314
	$_REQUEST['download']
315
))->setWidth(7)->setReadOnly();
316

    
317
$section->addInput(new Form_Input(
318
	'reason',
319
	'Revision Reason',
320
	'text',
321
	$_REQUEST['reason']
322
))->setWidth(7)->setReadOnly();
323

    
324
$section->addInput(new Form_Input(
325
	'shasum',
326
	'SHA256 summary',
327
	'text',
328
	$sha256sum
329
))->setWidth(7)->setReadOnly();
330

    
331
$section->addInput(new Form_Textarea(
332
	'config_xml',
333
	'Encrypted config.xml',
334
	$ds[1]
335
))->setWidth(7)->setAttribute("rows", "40")->setAttribute("wrap", "off");
336

    
337
$section->addInput(new Form_Textarea(
338
	'dec_config_xml',
339
	'Decrypted config.xml',
340
	$data
341
))->setWidth(7)->setAttribute("rows", "40")->setAttribute("wrap", "off");
342

    
343
$form->add($section);
344

    
345
print($form);
346

    
347
?>
348
<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>
349

    
350
<?php else:
351

    
352
$section2 = new Form_Section('Device key');
353
$group = new Form_Group("Device key");
354

    
355
$group->add(new Form_Input(
356
	'devkey',
357
	'Device key',
358
	'text',
359
	$userkey
360
))->setWidth(7)->setHelp("ID used to identify this firewall (derived from the SSH public key.) " .
361
	"See help below for more details. %sPlease make a safe copy of this ID value.%s If it is lost, your backups will" .
362
	" be lost too!", "<strong>", "</strong>");
363

    
364
$group->add(new Form_Button(
365
	'upduserkey',
366
	'Submit',
367
	null,
368
	'fa-save'
369
))->addClass('btn-success btn-xs');
370

    
371
$group->add(new Form_Button(
372
	'restore',
373
	'Reset',
374
	null,
375
	'fa-refresh'
376
))->addClass('btn-info btn-xs');
377

    
378
$section2->add($group);
379
print($section2);
380

    
381
print('<div class="infoblock">');
382
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." .
383
	" 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\"." .
384
	" This will temporarily override the ID for this session."), 'info', false);
385
print('</div>');
386

    
387
?>
388
<div class="panel panel-default">
389
	<div class="panel-heading"><h2 class="panel-title"><?=gettext("Automatic Configuration Backups")?></h2></div>
390
	<div class="panel-body">
391
		<div class="table-responsive">
392
		</div>
393
		<div class="table-responsive">
394
			<table class="table table-striped table-hover table-condensed" id="backups">
395
				<thead>
396
					<tr>
397
						<th width="30%"><?=gettext("Date")?></th>
398
						<th width="60%"><?=gettext("Configuration Change")?></th>
399
						<th width="10%"><?=gettext("Actions")?></th>
400
					</tr>
401
				</thead>
402
				<tbody>
403

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

    
444
endif; ?>
445

    
446
</form>
447

    
448
<script type="text/javascript">
449
//<![CDATA[
450
events.push(function(){
451
	$('#loading').hide();
452

    
453
	// On clicking Submit", reload the page but with a POST parameter "userkey" set
454
	$('#upduserkey').click(function() {
455
		var $form = $('<form>');
456
		var newuserkey = $('#devkey').val();
457

    
458
		$form
459
			.attr("method", "POST")
460
			.attr("action", '/services_acb.php')
461
			// The CSRF magic is required because we will be viewing the results of the POST
462
			.append(
463
				$("<input>")
464
					.attr("type", "hidden")
465
					.attr("name", "__csrf_magic")
466
					.val(csrfMagicToken)
467
			)
468
			.append(
469
			$("<input>")
470
				.attr("type", "hidden")
471
				.attr("name", "userkey")
472
				.val(newuserkey)
473
			)
474
			.appendTo('body')
475
			.submit();
476
	});
477

    
478
	$('#restore').click(function() {
479
		$('#devkey').val("<?=$origkey?>");
480
	});
481
});
482
//]]>
483
</script>
484

    
485
<?php include("foot.inc"); ?>
(101-101/228)