Project

General

Profile

Download (11.5 KB) Statistics
| Branch: | Tag: | Revision:
1
<?php
2
/*
3
 * autoconfigbackup.inc
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-2021 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
require_once("filter.inc");
25
require_once("notices.inc");
26

    
27
// If there is no ssh key in the system to identify this firewall, generate a pair now
28
function get_device_key() {
29
	if (!file_exists("/etc/ssh/ssh_host_ed25519_key.pub")) {
30
		// Remove any possible matching key so we don't get an overwrite prompt
31
		if (file_exists('/etc/ssh/ssh_host_ed25519_key')) {
32
			unlink('/etc/ssh/ssh_host_ed25519_key');
33
		}
34

    
35
		// Generate a new key pair
36
		exec("/usr/bin/nice -n20 /usr/bin/ssh-keygen -t ed25519 -b 4096 -N '' -f /etc/ssh/ssh_host_ed25519_key");
37
		sleep(2);
38

    
39
		$cnt = 0;
40
		while(!file_exists("/etc/ssh/ssh_host_ed25519_key.pub")) {
41
			sleep(2);
42
			if (++$cnt > 10) {
43
				return "";
44
			}
45
		}
46
	}
47

    
48
	$pkey =  file_get_contents("/etc/ssh/ssh_host_ed25519_key.pub");
49

    
50
	// Check that the key looks reasonable
51
	if (substr($pkey, 0, 3) != "ssh") {
52
		return "";
53
	}
54

    
55
	return hash("sha256", $pkey);
56
}
57

    
58
$origkey = get_device_key();
59

    
60
if (isset($_REQUEST['userkey']) && !empty($_REQUEST['userkey'])) {
61
	$userkey = htmlentities($_REQUEST['userkey']);
62
} else {
63
	$userkey = get_device_key();
64
}
65

    
66
$uniqueID = system_get_uniqueid();
67

    
68
/* Check whether ACB is enabled */
69
function acb_enabled() {
70
	global $config;
71
	$acb_enabled = false;
72

    
73
	if (is_array($config['system']['acb'])) {
74
		if ($config['system']['acb']['enable'] == "yes") {
75
			$acb_enabled = true;
76
		}
77
	}
78

    
79
	return $acb_enabled;
80
}
81

    
82
// Ensures patches match
83
function acb_custom_php_validation_command($post, &$input_errors) {
84
	global $_POST, $savemsg, $config;
85

    
86
	// Do nothing when ACB is disabled in configuration
87
	// This also makes it possible to delete the credentials from config.xml
88
	if (!acb_enabled()) {
89
		// We do not need to store this value.
90
		unset($_POST['testconnection']);
91
		return;
92
	}
93

    
94
	if (!$post['crypto_password'] or !$post['crypto_password2']) {
95
		$input_errors[] = "The encryption password is required.";
96
	}
97

    
98
	if ($post['crypto_password'] <> $post['crypto_password2']) {
99
		$input_errors[] = "Sorry, the entered encryption passwords do not match.";
100
	}
101

    
102
	if ($post['testconnection']) {
103
		$status = test_connection($post);
104
		if ($status) {
105
			$savemsg = "Connection to the ACB server was tested with no errors.";
106
		}
107
	}
108

    
109
	// We do not need to store this value.
110
	unset($_POST['testconnection']);
111
}
112

    
113
function acb_custom_php_resync_config_command() {
114
	// Do nothing when ACB is disabled in configuration
115
	if (!acb_enabled()) {
116
		return;
117
	}
118

    
119
	unlink_if_exists("/cf/conf/lastpfSbackup.txt");
120

    
121
	if (!function_exists("filter_configure")) {
122
		require_once("filter.inc");
123
	}
124

    
125
	filter_configure();
126

    
127
	if ($savemsg) {
128
		$savemsg .= "<br/>";
129
	}
130

    
131
	$savemsg .= "A configuration backup has been queued.";
132
}
133

    
134
function configure_proxy() {
135
	global $config;
136
	$ret = array();
137
	if (!empty($config['system']['proxyurl'])) {
138
		$ret[CURLOPT_PROXY] = $config['system']['proxyurl'];
139
		if (!empty($config['system']['proxyport'])) {
140
			$ret[CURLOPT_PROXYPORT] = $config['system']['proxyport'];
141
		}
142
		if (!empty($config['system']['proxyuser']) && !empty($config['system']['proxypass'])) {
143
			$ret[CURLOPT_PROXYAUTH] = CURLAUTH_ANY | CURLAUTH_ANYSAFE;
144
			$ret[CURLOPT_PROXYUSERPWD] = "{$config['system']['proxyuser']}:{$config['system']['proxypass']}";
145
		}
146
	}
147
	return $ret;
148
}
149

    
150
function test_connection($post) {
151
	global $savemsg, $config, $g;
152

    
153
	// Do nothing when booting or when not enabled
154
	if (platform_booting() || !acb_enabled()) {
155
		return;
156
	}
157

    
158
	// Separator used during client / server communications
159
	$oper_sep = "\|\|";
160

    
161
	// Encryption password
162
	$decrypt_password = $post['crypto_password'];
163

    
164
	// Defined username. Username must be sent lowercase. See Redmine #7127 and Netgate Redmine #163
165
	$username = strtolower($post['username']);
166

    
167
	// Defined password
168
	$password = $post['password'];
169

    
170
	// Set hostname
171
	$hostname = $config['system']['hostname'] . "." . $config['system']['domain'];
172

    
173
	// URL to restore.php
174
	$get_url = "https://acb.netgate.com/getbkp";
175

    
176
	// Populate available backups
177
	$curl_session = curl_init();
178
	curl_setopt($curl_session, CURLOPT_URL, $get_url);
179
	curl_setopt($curl_session, CURLOPT_SSL_VERIFYPEER, 1);
180
	curl_setopt($curl_session, CURLOPT_POST, 1);
181
	curl_setopt($curl_session, CURLOPT_RETURNTRANSFER, 1);
182
	curl_setopt($curl_session, CURLOPT_CONNECTTIMEOUT, 55);
183
	curl_setopt($curl_session, CURLOPT_TIMEOUT, 30);
184
	curl_setopt($curl_session, CURLOPT_USERAGENT, $g['product_label'] . '/' . rtrim(file_get_contents("/etc/version")));
185
	// Proxy
186
	curl_setopt_array($curl_session, configure_proxy());
187

    
188
	curl_setopt($curl_session, CURLOPT_POSTFIELDS, "action=showbackups&hostname={$hostname}");
189
	$data = curl_exec($curl_session);
190

    
191
	if (curl_errno($curl_session)) {
192
		return("An error occurred " . curl_error($curl_session));
193
	} else {
194
		curl_close($curl_session);
195
	}
196

    
197
	return;
198
}
199

    
200
function upload_config($manual = false) {
201
	global $config, $g, $input_errors, $userkey, $uniqueID;
202

    
203
	if (empty($userkey)) {
204
		$userkey = get_device_key();
205
	}
206

    
207
	if (empty($uniqueID)) {
208
		$uniqueID = system_get_uniqueid();
209
	}
210

    
211
	// Do nothing when booting or when not enabled
212
	if (platform_booting() || !acb_enabled()) {
213
		return;
214
	}
215

    
216
	/*
217
	 * pfSense upload config to acb.netgate.com script
218
	 * This file plugs into config.inc (/usr/local/pkg/parse_config)
219
	 * and runs every time the running firewall filter changes.
220
	 *
221
	 */
222

    
223
	if (file_exists("/tmp/acb_nooverwrite")) {
224
		unlink("/tmp/acb_nooverwrite");
225
		$nooverwrite = "true";
226
	} else {
227
		$nooverwrite = "false";
228
	}
229

    
230
	// Define some needed variables
231
	if (file_exists("/cf/conf/lastpfSbackup.txt")) {
232
		$last_backup_date = str_replace("\n", "", file_get_contents("/cf/conf/lastpfSbackup.txt"));
233
	} else {
234
		$last_backup_date = "";
235
	}
236

    
237
	$last_config_change = $config['revision']['time'];
238
	$hostname = $config['system']['hostname'] . "." . $config['system']['domain'];
239
	$reason	= $config['revision']['description'];
240

    
241
	if ($manual && array_key_exists('numman', $config['system']['acb'])) {
242
		$manmax = $config['system']['acb']['numman'];
243
	} else {
244
		$manmax = "0";
245
	}
246

    
247
	$encryptpw = $config['system']['acb']['encryption_password'];
248

    
249
	// Define upload_url, must be present after other variable definitions due to username, password
250
	$upload_url = "https://acb.netgate.com/save";
251

    
252
	if (!$encryptpw) {
253
		if (!file_exists("/cf/conf/autoconfigback.notice")) {
254
			$notice_text = "The encryption password is not set for Automatic Configuration Backup.";
255
			$notice_text .= " Please correct this in Services -> AutoConfigBackup -> Settings.";
256
			log_error($notice_text);
257
			file_notice("AutoConfigBackup", $notice_text, $notice_text, "");
258
			touch("/cf/conf/autoconfigback.notice");
259
		}
260
	} else {
261
		/* If configuration has changed, upload to pfS */
262
		if ($last_backup_date <> $last_config_change) {
263

    
264
			$notice_text = "Beginning configuration backup to " . $upload_url;
265
			log_error($notice_text);
266
			update_filter_reload_status($notice_text);
267

    
268
			// Encrypt config.xml
269
			$data = file_get_contents("/cf/conf/config.xml");
270
			$raw_config_sha256_hash = trim(shell_exec("/sbin/sha256 /cf/conf/config.xml | /usr/bin/awk '{ print $4 }'"));
271
			$data = encrypt_data($data, $encryptpw);
272
			$tmpname = "/tmp/" . $raw_config_sha256_hash . ".tmp";
273
			tagfile_reformat($data, $data, "config.xml");
274
			file_put_contents($tmpname, $data);
275

    
276
			$post_fields = array(
277
				'reason' => htmlspecialchars($reason),
278
				'uid' => $uniqueID,
279
				'file' => curl_file_create($tmpname, 'image/jpg', 'config.jpg'),
280
				'userkey' => htmlspecialchars($userkey),
281
				'sha256_hash' => $raw_config_sha256_hash,
282
				'version' => $g['product_version'],
283
				'hint' => $config['system']['acb']['hint'],
284
				'manmax' => $manmax
285
			);
286

    
287
			// Check configuration into the ESF repo
288
			$curl_session = curl_init();
289

    
290
			curl_setopt($curl_session, CURLOPT_URL, "https://acb.netgate.com/save");
291
			curl_setopt($curl_session, CURLOPT_POST, count($post_fields));
292
			curl_setopt($curl_session, CURLOPT_POSTFIELDS, $post_fields);
293
			curl_setopt($curl_session, CURLOPT_RETURNTRANSFER, 1);
294
			curl_setopt($curl_session, CURLOPT_SSL_VERIFYPEER, 1);
295
			curl_setopt($curl_session, CURLOPT_CONNECTTIMEOUT, 55);
296
			curl_setopt($curl_session, CURLOPT_TIMEOUT, 30);
297
			curl_setopt($curl_session, CURLOPT_USERAGENT, $g['product_label'] . '/' . rtrim(file_get_contents("/etc/version")));
298
			// Proxy
299
			curl_setopt_array($curl_session, configure_proxy());
300

    
301
			$data = curl_exec($curl_session);
302

    
303
			unlink_if_exists($tmpname);
304

    
305
			if (curl_errno($curl_session)) {
306
				$fd = fopen("/tmp/backupdebug.txt", "w");
307
				fwrite($fd, $upload_url . "" . $fields_string . "\n\n");
308
				fwrite($fd, $data);
309
				fwrite($fd, curl_error($curl_session));
310
				fclose($fd);
311
			} else {
312
				curl_close($curl_session);
313
			}
314

    
315
			if (strpos($data, "500") != false) {
316
				$notice_text = sprintf(gettext(
317
				    "An error occurred while uploading your %s configuration to "), $g['product_label']) .
318
				    $upload_url . " (" . htmlspecialchars($data) . ")";
319
				log_error($notice_text . " - " . $data);
320
				file_notice("AutoConfigBackup", $notice_text);
321
				update_filter_reload_status($notice_text);
322
				$input_errors["acb_upload"] = $notice_text;
323
			} else {
324
				// Update last pfS backup time
325
				$fd = fopen("/cf/conf/lastpfSbackup.txt", "w");
326
				fwrite($fd, $config['revision']['time']);
327
				fclose($fd);
328
				$notice_text = "End of configuration backup to " . $upload_url . " (success).";
329
				log_error($notice_text);
330
				update_filter_reload_status($notice_text);
331
			}
332

    
333
		} else {
334
			// Debugging
335
			//log_error("No https://acb.netgate.com backup required.");
336
		}
337
	}
338
}
339

    
340
// Save the updated ACB configuration
341
// Create a crontab entry for scheduled backups
342
// if frequency == "cron", a new crontab entry is created, otherwise any existing
343
// ACB entry is removed
344
function setup_ACB($enable, $hint, $frequency, $minute, $hours, $month, $day, $dow, $numman, $pwd) {
345
	global $config;
346

    
347
	$config['system']['acb']['enable'] = $enable;
348
	$config['system']['acb']['hint'] = $hint;
349
	$config['system']['acb']['frequency'] = $frequency;
350
	$config['system']['acb']['minute'] = $minute;
351
	$config['system']['acb']['hour'] = $hours;
352
	$config['system']['acb']['month'] = $month;
353
	$config['system']['acb']['day'] = $day;
354
	$config['system']['acb']['dow'] = $dow;
355
	$config['system']['acb']['numman'] = $numman;
356
	if (strlen($pwd) >= 8) {
357
		$config['system']['acb']['encryption_password'] = $pwd;
358
	}
359

    
360
	init_config_arr(array('cron'));
361
	$a_cron = &$config['cron']['item'];
362
	$croncmd = "/usr/bin/nice -n20 /usr/local/bin/php /usr/local/sbin/execacb.php";
363

    
364
	$i = 0;
365

    
366
	// Remove the existing cron job
367
	if (count($a_cron) > 0) {
368
		foreach ($a_cron as $ent) {
369
			if ($ent['command'] === $croncmd) {
370
				unset($a_cron[$i]);
371
				break;
372
			}
373

    
374
		$i++;
375
		}
376
	}
377

    
378
	if ($frequency == "cron") {
379
		$crondef = array();
380
		$crondef['minute'] = $minute;
381
		$crondef['hour'] = $hours;
382
		$crondef['mday'] = $day;
383
		$crondef['month'] = $month;
384
		$crondef['wday'] = $dow;
385
		$crondef['who'] = 'root';
386
		$crondef['command'] = $croncmd;
387

    
388
		$a_cron[$i] = $crondef;
389
	}
390

    
391
	write_config("AutoConfigBackup settings updated");
392
	configure_cron();
393
}
(1-1/61)