Project

General

Profile

Download (28 KB) Statistics
| Branch: | Tag: | Revision:
1 50d49018 Colin Smith
<?php
2
/*
3 c5d81585 Renato Botelho
 * xmlrpc.php
4 191cb31d Stephen Beaver
 *
5 c5d81585 Renato Botelho
 * part of pfSense (https://www.pfsense.org)
6 38809d47 Renato Botelho do Couto
 * Copyright (c) 2004-2013 BSD Perimeter
7
 * Copyright (c) 2013-2016 Electric Sheep Fencing
8 402c98a2 Reid Linnemann
 * Copyright (c) 2014-2023 Rubicon Communications, LLC (Netgate)
9 c5d81585 Renato Botelho
 * Copyright (c) 2005 Colin Smith
10
 * All rights reserved.
11 191cb31d Stephen Beaver
 *
12 b12ea3fb Renato Botelho
 * Licensed under the Apache License, Version 2.0 (the "License");
13
 * you may not use this file except in compliance with the License.
14
 * You may obtain a copy of the License at
15 191cb31d Stephen Beaver
 *
16 b12ea3fb Renato Botelho
 * http://www.apache.org/licenses/LICENSE-2.0
17 191cb31d Stephen Beaver
 *
18 b12ea3fb Renato Botelho
 * Unless required by applicable law or agreed to in writing, software
19
 * distributed under the License is distributed on an "AS IS" BASIS,
20
 * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
21
 * See the License for the specific language governing permissions and
22
 * limitations under the License.
23 191cb31d Stephen Beaver
 */
24 50d49018 Colin Smith
25 6b07c15a Matthew Grooms
##|+PRIV
26
##|*IDENT=page-xmlrpclibrary
27 5230f468 jim-p
##|*NAME=XMLRPC Library
28 6b07c15a Matthew Grooms
##|*DESCR=Allow access to the 'XMLRPC Library' page.
29
##|*MATCH=xmlrpc.php*
30
##|-PRIV
31
32 c81ef6e2 Phil Davis
require_once("config.inc");
33
require_once("functions.inc");
34 f81e7cc4 Renato Botelho
require_once("auth.inc");
35 f6339216 jim-p
require_once("filter.inc");
36 c81ef6e2 Phil Davis
require_once("ipsec.inc");
37
require_once("vpn.inc");
38 23fcdccc Viktor G
require_once("openvpn.inc");
39 7cab6335 Renato Botelho
require_once("captiveportal.inc");
40 c81ef6e2 Phil Davis
require_once("shaper.inc");
41 f81e7cc4 Renato Botelho
require_once("XML/RPC2/Server.php");
42 50d49018 Colin Smith
43 f81e7cc4 Renato Botelho
class pfsense_xmlrpc_server {
44 c87f4b70 Ermal
45 f81e7cc4 Renato Botelho
	private $loop_detected = false;
46
	private $remote_addr;
47 c87f4b70 Ermal
48 dc5f639f PiBa-NL
	private function auth() {
49 c472f9a1 jim-p
		global $config, $userindex;
50
		$userindex = index_users();
51
52 dc5f639f PiBa-NL
		$username = $_SERVER['PHP_AUTH_USER'];
53
		$password = $_SERVER['PHP_AUTH_PW'];
54 8da3de34 Colin Smith
55 fb1234ab Renato Botelho
		$login_ok = false;
56 f81e7cc4 Renato Botelho
		if (!empty($username) && !empty($password)) {
57
			$attributes = array();
58
			$authcfg = auth_get_authserver(
59
			    $config['system']['webgui']['authmode']);
60 c3638879 Scott Ullrich
61 f81e7cc4 Renato Botelho
			if (authenticate_user($username, $password,
62
			    $authcfg, $attributes) ||
63
			    authenticate_user($username, $password)) {
64 fb1234ab Renato Botelho
				$login_ok = true;
65 f81e7cc4 Renato Botelho
			}
66
		}
67 3dd2a278 Scott Ullrich
68 fb1234ab Renato Botelho
		if (!$login_ok) {
69 6e0d4751 jim-p
			log_auth(sprintf(gettext("webConfigurator authentication error for user '%1\$s' from: %2\$s"),
70
			    $username,
71
			    $this->remote_addr));
72 137f46d8 Ermal
73 fb1234ab Renato Botelho
			require_once("XML/RPC2/Exception.php");
74
			throw new XML_RPC2_FaultException(gettext(
75
			    'Authentication failed: Invalid username or password'),
76
			    -1);
77
		}
78
79
		$user_entry = getUserEntry($username);
80
		/*
81
		 * admin (uid = 0) is allowed
82
		 * or regular user with necessary privilege
83
		 */
84
		if (isset($user_entry['uid']) && $user_entry['uid'] != '0' &&
85
		    !userHasPrivilege($user_entry, 'system-xmlrpc-ha-sync')) {
86
			log_auth("webConfigurator authentication error for '" .
87
			    $username . "' from " . $this->remote_addr .
88
			    " not enough privileges");
89
90
			require_once("XML/RPC2/Exception.php");
91
			throw new XML_RPC2_FaultException(gettext(
92
			    'Authentication failed: not enough privileges'),
93
			    -2);
94
		}
95
96
		return;
97 3dd2a278 Scott Ullrich
	}
98 f81e7cc4 Renato Botelho
99
	private function array_overlay($a1, $a2) {
100
		foreach ($a1 as $k => $v) {
101
			if (!array_key_exists($k, $a2)) {
102
				continue;
103
			}
104
			if (is_array($v) && is_array($a2[$k])) {
105
				$a1[$k] = $this->array_overlay($v, $a2[$k]);
106
			} else {
107
				$a1[$k] = $a2[$k];
108
			}
109
		}
110
111
		return $a1;
112 962f215d Phil Davis
	}
113 c3638879 Scott Ullrich
114 f81e7cc4 Renato Botelho
	public function __construct() {
115
		global $config;
116 c3638879 Scott Ullrich
117 f82f991c Renato Botelho
		$this->remote_addr = $_SERVER['REMOTE_ADDR'];
118 137f46d8 Ermal
119 f81e7cc4 Renato Botelho
		/* grab sync to ip if enabled */
120
		if (isset($config['hasync']['synchronizetoip']) &&
121 8d44b2cb PiBa-NL
		    $config['hasync']['synchronizetoip'] == $this->remote_addr) {
122 f81e7cc4 Renato Botelho
			$this->loop_detected = true;
123
		}
124 3dd2a278 Scott Ullrich
	}
125 137f46d8 Ermal
126 f81e7cc4 Renato Botelho
	/**
127
	 * Get host version information
128
	 *
129
	 * @return array
130
	 */
131 de3f6463 Reid Linnemann
	public function host_firmware_version($dummy, $timeout) {
132 4f26f187 Viktor G
		ini_set('default_socket_timeout', $timeout);
133 dc5f639f PiBa-NL
		$this->auth();
134 f81e7cc4 Renato Botelho
		return host_firmware_version();
135
	}
136 21dc3a7d Colin Smith
137 f81e7cc4 Renato Botelho
	/**
138
	 * Executes a PHP block of code
139
	 *
140
	 * @param string $code
141
	 *
142
	 * @return bool
143
	 */
144 dc5f639f PiBa-NL
	public function exec_php($code) {
145
		$this->auth();
146 137f46d8 Ermal
147 f81e7cc4 Renato Botelho
		eval($code);
148
		if ($toreturn) {
149
			return $toreturn;
150
		}
151 c87f4b70 Ermal
152 f81e7cc4 Renato Botelho
		return true;
153 3dd2a278 Scott Ullrich
	}
154 137f46d8 Ermal
155 f81e7cc4 Renato Botelho
	/**
156
	 * Executes shell commands
157
	 *
158
	 * @param string $code
159
	 *
160
	 * @return bool
161
	 */
162 dc5f639f PiBa-NL
	public function exec_shell($code) {
163
		$this->auth();
164 50d49018 Colin Smith
165 f81e7cc4 Renato Botelho
		mwexec($code);
166
		return true;
167
	}
168 21dc3a7d Colin Smith
169 f81e7cc4 Renato Botelho
	/**
170
	 * Backup chosen config sections
171
	 *
172
	 * @param array $section
173
	 *
174
	 * @return array
175
	 */
176 dc5f639f PiBa-NL
	public function backup_config_section($section) {
177
		$this->auth();
178 137f46d8 Ermal
179 f81e7cc4 Renato Botelho
		global $config;
180 d026178f Renato Botelho
181 f81e7cc4 Renato Botelho
		return array_intersect_key($config, array_flip($section));
182 fb0eb20b Ermal
	}
183 c87f4b70 Ermal
184 f81e7cc4 Renato Botelho
	/**
185
	 * Restore defined config section into local config
186
	 *
187
	 * @param array $sections
188
	 *
189
	 * @return bool
190
	 */
191 4f26f187 Viktor G
	public function restore_config_section($sections, $timeout) {
192
		ini_set('default_socket_timeout', $timeout);
193 dc5f639f PiBa-NL
		$this->auth();
194 f81e7cc4 Renato Botelho
195 23fcdccc Viktor G
		global $config, $cpzone, $cpzoneid, $old_config;
196 1b99e1e5 jim-p
197 f81e7cc4 Renato Botelho
		$old_config = $config;
198
199
		if ($this->loop_detected) {
200
			log_error("Disallowing CARP sync loop");
201
			return true;
202
		}
203
204
		/*
205
		 * Some sections should just be copied and not merged or we end
206
		 * up unable to sync the deletion of the last item in a section
207
		 */
208
		$sync_full_sections = array(
209
			'aliases',
210
			'ca',
211
			'cert',
212
			'crl',
213
			'dhcpd',
214 30169caa Viktor G
			'dhcrelay',
215
			'dhcrelay6',
216 c9a96f16 Viktor Gurov
			'dnshaper',
217 f81e7cc4 Renato Botelho
			'dnsmasq',
218
			'filter',
219
			'ipsec',
220
			'nat',
221
			'openvpn',
222
			'schedules',
223 c9a96f16 Viktor Gurov
			'shaper',
224 f81e7cc4 Renato Botelho
			'unbound',
225
			'wol',
226
		);
227
228
		$syncd_full_sections = array();
229
230
		foreach ($sync_full_sections as $section) {
231 9bfd8974 jim-p
			/* Do not test for empty here or removing final entry
232
			 * from a section will not work */
233
			if (!array_key_exists($section, $sections)) {
234 f81e7cc4 Renato Botelho
				continue;
235
			}
236
237 9bfd8974 jim-p
			config_set_path($section, array_get_path($sections, $section));
238
			array_del_path($sections, $section);
239 f81e7cc4 Renato Botelho
			$syncd_full_sections[] = $section;
240 1b99e1e5 jim-p
		}
241
242 146b0a43 Augustin-FL
		/* If captive portal sync is enabled on primary node, remove local CP on the secondary */
243
		if (is_array($config['captiveportal']) && is_array($sections['captiveportal'])) {
244 7cab6335 Renato Botelho
			foreach ($config['captiveportal'] as $zone => $item) {
245 146b0a43 Augustin-FL
				if (!isset($sections['captiveportal'][$zone])) {
246
					$cpzone = $zone;
247 7e3ea4a8 Christian McDonald
					config_del_path("captiveportal/{$cpzone}/enable");
248 146b0a43 Augustin-FL
					captiveportal_configure_zone($config['captiveportal'][$cpzone]);
249 7e3ea4a8 Christian McDonald
					config_del_path("captiveportal/{$cpzone}");
250 146b0a43 Augustin-FL
					if (isset($config['voucher'][$cpzone])) {
251 7e3ea4a8 Christian McDonald
						config_del_path("voucher/{$cpzone}");
252 146b0a43 Augustin-FL
					}
253 c31f4e95 Viktor G
					unlink_if_exists("/var/db/captiveportal{$cpzone}.db");
254
					unlink_if_exists("/var/db/captiveportal_usedmacs_{$cpzone}.db");
255
					unlink_if_exists("/var/db/voucher_{$cpzone}_*.db");
256 7cab6335 Renato Botelho
				}
257
			}
258
		}
259
260 d3cc158c jim-p
		/* Only touch users if users are set to synchronize from the primary node
261
		 * See https://redmine.pfsense.org/issues/8450
262
		 */
263
		if ($sections['system']['user'] && $sections['system']['group']) {
264
			$g2add = array();
265
			$g2del = array();
266
			$g2del_idx = array();
267
			$g2keep = array();
268
			if (is_array($sections['system']['group'])) {
269
				$local_groups = isset($config['system']['group'])
270
				    ? $config['system']['group']
271
				    : array();
272
273
				foreach ($sections['system']['group'] as $group) {
274
					$idx = array_search($group['name'],
275
					    array_column($local_groups, 'name'));
276
277
					if ($idx === false) {
278
						$g2add[] = $group;
279
					} else if ($group['gid'] < 1999) {
280
						$g2keep[] = $idx;
281
					} else if ($group != $local_groups[$idx]) {
282
						$g2add[] = $group;
283
						$g2del[] = $group;
284
						$g2del_idx[] = $idx;
285
					} else {
286
						$g2keep[] = $idx;
287
					}
288 79f7bc7f Renato Botelho
				}
289
			}
290 d3cc158c jim-p
			if (is_array($config['system']['group'])) {
291
				foreach ($config['system']['group'] as $idx => $group) {
292
					if (array_search($idx, $g2keep) === false &&
293
					    array_search($idx, $g2del_idx) === false) {
294
						$g2del[] = $group;
295
						$g2del_idx[] = $idx;
296
					}
297 79f7bc7f Renato Botelho
				}
298
			}
299 d3cc158c jim-p
			unset($sections['system']['group'], $g2keep, $g2del_idx);
300
301
			$u2add = array();
302
			$u2del = array();
303
			$u2del_idx = array();
304
			$u2keep = array();
305
			if (is_array($sections['system']['user'])) {
306
				$local_users = isset($config['system']['user'])
307
				    ? $config['system']['user']
308
				    : array();
309
310
				foreach ($sections['system']['user'] as $user) {
311
					$idx = array_search($user['name'],
312
					    array_column($local_users, 'name'));
313
314
					if ($idx === false) {
315
						$u2add[] = $user;
316 f9ed5d57 James Webb
					} else if (($user['uid'] < 2000) && ($sections['hasync']['adminsync'] != 'on')) {
317 d3cc158c jim-p
						$u2keep[] = $idx;
318
					} else if ($user != $local_users[$idx]) {
319
						$u2add[] = $user;
320
						$u2del[] = $user;
321
						$u2del_idx[] = $idx;
322
					} else {
323
						$u2keep[] = $idx;
324
					}
325 79f7bc7f Renato Botelho
				}
326
			}
327 d3cc158c jim-p
			if (is_array($config['system']['user'])) {
328
				foreach ($config['system']['user'] as $idx => $user) {
329
					if (array_search($idx, $u2keep) === false &&
330
					    array_search($idx, $u2del_idx) === false) {
331
						$u2del[] = $user;
332
						$u2del_idx[] = $idx;
333
					}
334 79f7bc7f Renato Botelho
				}
335
			}
336 d3cc158c jim-p
			unset($sections['system']['user'], $u2keep, $u2del_idx);
337 79f7bc7f Renato Botelho
		}
338
339 b8963db6 Renato Botelho
		$voucher = array();
340
		if (is_array($sections['voucher'])) {
341
			/* Save voucher rolls to process after merge */
342
			$voucher = $sections['voucher'];
343
344
			foreach($sections['voucher'] as $zone => $item) {
345
				unset($sections['voucher'][$zone]['roll']);
346 06ef0830 Augustin-FL
				// Note : This code can be safely deleted once #97 fix has been applied and deployed to pfSense stable release.
347
				// Please do not delete this code before
348 b8963db6 Renato Botelho
				if (isset($config['voucher'][$zone]['vouchersyncdbip'])) {
349
					$sections['voucher'][$zone]['vouchersyncdbip'] =
350 829322b3 Christian McDonald
					    config_get_path("voucher/{$zone}/vouchersyncdbip");
351 b8963db6 Renato Botelho
				} else {
352
					unset($sections['voucher'][$zone]['vouchersyncdbip']);
353
				}
354
				if (isset($config['voucher'][$zone]['vouchersyncusername'])) {
355
					$sections['voucher'][$zone]['vouchersyncusername'] =
356 829322b3 Christian McDonald
					    config_get_path("voucher/{$zone}/vouchersyncusername");
357 b8963db6 Renato Botelho
				} else {
358
					unset($sections['voucher'][$zone]['vouchersyncusername']);
359
				}
360
				if (isset($config['voucher'][$zone]['vouchersyncpass'])) {
361
					$sections['voucher'][$zone]['vouchersyncpass'] =
362 829322b3 Christian McDonald
					    config_get_path("voucher/{$zone}/vouchersyncpass");
363 b8963db6 Renato Botelho
				} else {
364
					unset($sections['voucher'][$zone]['vouchersyncpass']);
365
				}
366 06ef0830 Augustin-FL
				// End note.
367 b8963db6 Renato Botelho
			}
368
		}
369
370 06ef0830 Augustin-FL
		if (is_array($sections['captiveportal'])) {
371
			// Captiveportal : Backward HA settings should remain local.
372
			foreach ($sections['captiveportal'] as $zone => $cp) {
373
				if (isset($config['captiveportal'][$zone]['enablebackwardsync'])) {
374 829322b3 Christian McDonald
					$sections['captiveportal'][$zone]['enablebackwardsync'] = config_get_path("captiveportal/{$zone}/enablebackwardsync");
375 06ef0830 Augustin-FL
				} else {
376
					unset($sections['captiveportal'][$zone]['enablebackwardsync']);
377
				}
378
				if (isset($config['captiveportal'][$zone]['backwardsyncip'])) {
379 829322b3 Christian McDonald
					$sections['captiveportal'][$zone]['backwardsyncip'] = config_get_path("captiveportal/{$zone}/backwardsyncip");
380 06ef0830 Augustin-FL
				} else {
381
					unset($sections['captiveportal'][$zone]['backwardsyncip']);
382
				}
383
				if (isset($config['captiveportal'][$zone]['backwardsyncuser'])) {
384 829322b3 Christian McDonald
					$sections['captiveportal'][$zone]['backwardsyncuser'] = config_get_path("captiveportal/{$zone}/backwardsyncuser");
385 06ef0830 Augustin-FL
				} else {
386
					unset($sections['captiveportal'][$zone]['backwardsyncuser']);
387
				}
388
				if (isset($config['captiveportal'][$zone]['backwardsyncpassword'])) {
389 829322b3 Christian McDonald
					$sections['captiveportal'][$zone]['backwardsyncpassword'] = config_get_path("captiveportal/{$zone}/backwardsyncpassword");
390 06ef0830 Augustin-FL
				} else {
391
					unset($sections['captiveportal'][$zone]['vouchersyncpass']);
392
				}
393 b8963db6 Renato Botelho
			}
394 06ef0830 Augustin-FL
			$config['captiveportal'] = $sections['captiveportal'];
395
			unset($sections['captiveportal']);
396 b8963db6 Renato Botelho
		}
397
398 f81e7cc4 Renato Botelho
		$vipbackup = array();
399
		$oldvips = array();
400 9bfd8974 jim-p
		if (array_key_exists('virtualip', $sections)) {
401
			foreach (config_get_path('virtualip/vip', []) as $vip) {
402
				if (empty($vip)) {
403
					continue;
404
				}
405 c14781e3 Renato Botelho
				if ($vip['mode'] == "carp") {
406 f81e7cc4 Renato Botelho
					$key = $vip['interface'] .
407
					    "_vip" . $vip['vhid'];
408
409
					$oldvips[$key]['content'] =
410
					    $vip['password'] .
411
					    $vip['advskew'] .
412
					    $vip['subnet'] .
413
					    $vip['subnet_bits'] .
414
					    $vip['advbase'];
415
					$oldvips[$key]['interface'] =
416
					    $vip['interface'];
417
					$oldvips[$key]['subnet'] =
418
					    $vip['subnet'];
419
				} else if ($vip['mode'] == "ipalias" &&
420
				    (substr($vip['interface'], 0, 4) == '_vip'
421
				    || strstr($vip['interface'], "lo0"))) {
422
					$oldvips[$vip['subnet']]['content'] =
423
					    $vip['interface'] .
424
					    $vip['subnet'] .
425
					    $vip['subnet_bits'];
426
					$oldvips[$vip['subnet']]['interface'] =
427
					    $vip['interface'];
428
					$oldvips[$vip['subnet']]['subnet'] =
429
					    $vip['subnet'];
430
				} else if (($vip['mode'] == "ipalias" ||
431
				    $vip['mode'] == 'proxyarp') &&
432
				    !(substr($vip['interface'], 0, 4) == '_vip')
433
				    || strstr($vip['interface'], "lo0")) {
434 51611440 Ermal
					$vipbackup[] = $vip;
435 c14781e3 Renato Botelho
				}
436 51611440 Ermal
			}
437 19b5c3e7 Ermal
		}
438 f51d4f98 Ermal
439 f81e7cc4 Renato Botelho
		/* For vip section, first keep items sent from the master */
440
		$config = array_merge_recursive_unique($config, $sections);
441 51611440 Ermal
442 b8963db6 Renato Botelho
		/* Remove locally items removed remote */
443
		foreach ($voucher as $zone => $item) {
444
			/* No rolls on master, delete local ones */
445
			if (!is_array($item['roll'])) {
446 7e3ea4a8 Christian McDonald
				config_del_path("voucher/{$zone}/roll");
447 b8963db6 Renato Botelho
			}
448
		}
449
450
		$l_rolls = array();
451
		if (is_array($config['voucher'])) {
452
			foreach ($config['voucher'] as $zone => $item) {
453
				if (!is_array($item['roll'])) {
454
					continue;
455
				}
456
				foreach ($item['roll'] as $idx => $roll) {
457
					/* Make it easy to find roll by # */
458
					$l_rolls[$zone][$roll['number']] = $idx;
459
				}
460
			}
461
		}
462
463
		/*
464
		 * Process vouchers sent by primary node and:
465
		 * - Add new items
466
		 * - Update existing items based on 'lastsync' field
467
		 */
468
		foreach ($voucher as $zone => $item) {
469
			if (!is_array($item['roll'])) {
470
				continue;
471
			}
472 d35a18fc Christian McDonald
			foreach ($item['roll'] as $roll) {
473 b8963db6 Renato Botelho
				if (!isset($l_rolls[$zone][$roll['number']])) {
474
					$config['voucher'][$zone]['roll'][] =
475
					    $roll;
476
					continue;
477
				}
478
				$l_roll_idx = $l_rolls[$zone][$roll['number']];
479 c6c398c6 jim-p
				init_config_arr(array('voucher', $zone));
480 b8963db6 Renato Botelho
				$l_vouchers = &$config['voucher'][$zone];
481
				$l_roll = $l_vouchers['roll'][$l_roll_idx];
482
				if (!isset($l_roll['lastsync'])) {
483
					$l_roll['lastsync'] = 0;
484
				}
485
486
				if (isset($roll['lastsync']) &&
487
				    $roll['lastsync'] != $l_roll['lastsync']) {
488
					$l_vouchers['roll'][$l_roll_idx] =
489
					    $roll;
490
					unset($l_rolls[$zone][$roll['number']]);
491
				}
492
			}
493
		}
494
495
		/*
496
		 * At this point $l_rolls contains only items that are not
497
		 * present on primary node. They must be removed
498
		 */
499
		foreach ($l_rolls as $zone => $item) {
500 d35a18fc Christian McDonald
			foreach ($item as $idx) {
501 7e3ea4a8 Christian McDonald
				config_del_path("voucher/{$zone}/{$idx}");
502 b8963db6 Renato Botelho
			}
503
		}
504
505 f81e7cc4 Renato Botelho
		/*
506
		 * Then add ipalias and proxyarp types already defined
507
		 * on the backup
508
		 */
509
		if (is_array($vipbackup) && !empty($vipbackup)) {
510 9bfd8974 jim-p
			$vips = config_get_path('virtualip/vip', []);
511 f81e7cc4 Renato Botelho
			foreach ($vipbackup as $vip) {
512 9bfd8974 jim-p
				array_unshift($vips, $vip);
513 f81e7cc4 Renato Botelho
			}
514 9bfd8974 jim-p
			config_set_path('virtualip/vip', $vips);
515 962f215d Phil Davis
		}
516 51611440 Ermal
517 f81e7cc4 Renato Botelho
		/* Log what happened */
518 8cb29dac doktornotor
		$mergedkeys = implode(", ", array_merge(array_keys($sections),
519 f81e7cc4 Renato Botelho
		    $syncd_full_sections));
520
		write_config(sprintf(gettext(
521
		    "Merged in config (%s sections) from XMLRPC client."),
522
		    $mergedkeys));
523
524
		/*
525
		 * The real work on handling the vips specially
526 51b0b50b jim-p
		 * This is a copy of interfaces_vips_configure with addition of
527 f81e7cc4 Renato Botelho
		 * not reloading existing/not changed carps
528
		 */
529 23fcdccc Viktor G
		$force_filterconfigure = false;
530 f81e7cc4 Renato Botelho
		if (isset($sections['virtualip']) &&
531
		    is_array($config['virtualip']) &&
532
		    is_array($config['virtualip']['vip'])) {
533
			$carp_setuped = false;
534
			$anyproxyarp = false;
535
536
			foreach ($config['virtualip']['vip'] as $vip) {
537
				$key = "{$vip['interface']}_vip{$vip['vhid']}";
538
539
				if ($vip['mode'] == "carp" &&
540
				    isset($oldvips[$key])) {
541
					if ($oldvips[$key]['content'] ==
542
					    $vip['password'] .
543
					    $vip['advskew'] .
544
					    $vip['subnet'] .
545
					    $vip['subnet_bits'] .
546
					    $vip['advbase'] &&
547
					    does_vip_exist($vip)) {
548
						unset($oldvips[$key]);
549
						/*
550
						 * Skip reconfiguring this vips
551
						 * since nothing has changed.
552
						 */
553
						continue;
554 19ed1624 Ermal
					}
555 5fda51cd jim-p
556 f81e7cc4 Renato Botelho
				} elseif ($vip['mode'] == "ipalias" &&
557 5fda51cd jim-p
				    (substr($vip['interface'], 0, 4) == '_vip'
558
				    || strstr($vip['interface'], "lo0")) &&
559 f81e7cc4 Renato Botelho
				    isset($oldvips[$vip['subnet']])) {
560
					$key = $vip['subnet'];
561
					if ($oldvips[$key]['content'] ==
562
					    $vip['interface'] .
563
					    $vip['subnet'] .
564
					    $vip['subnet_bits'] &&
565
					    does_vip_exist($vip)) {
566
						unset($oldvips[$key]);
567
						/*
568
						 * Skip reconfiguring this vips
569
						 * since nothing has changed.
570
						 */
571
						continue;
572 2708a5cf Ermal
					}
573 f81e7cc4 Renato Botelho
					unset($oldvips[$key]);
574 2708a5cf Ermal
				}
575 51611440 Ermal
576 f81e7cc4 Renato Botelho
				switch ($vip['mode']) {
577 962f215d Phil Davis
				case "proxyarp":
578
					$anyproxyarp = true;
579
					break;
580
				case "ipalias":
581
					interface_ipalias_configure($vip);
582
					break;
583
				case "carp":
584 f81e7cc4 Renato Botelho
					$carp_setuped = true;
585 3c15b353 Viktor G
					if (does_vip_exist($vip) && isset($oldvips[$key]['vhid']) &&
586
					    ($oldvips[$key]['vhid'] ^ $vip['vhid'])) {
587
						/* properly remove the old VHID
588
						 * see https://redmine.pfsense.org/issues/12202 */
589
						$realif = get_real_interface($vip['interface']);
590
						mwexec("/sbin/ifconfig {$realif} " .
591
							escapeshellarg($vip['subnet']) . " -alias");
592
						$ipalias_reload = true;
593
					} else {
594
						$ipalias_reload = false;
595
					}
596
					interface_carp_configure($vip, false, $ipalias_reload);
597 962f215d Phil Davis
					break;
598 f81e7cc4 Renato Botelho
				}
599 23fcdccc Viktor G
				$force_filterconfigure = true;
600 51611440 Ermal
			}
601 f81e7cc4 Renato Botelho
602
			/* Cleanup remaining old carps */
603
			foreach ($oldvips as $oldvipar) {
604
				$oldvipif = get_real_interface(
605
				    $oldvipar['interface']);
606
607
				if (empty($oldvipif)) {
608
					continue;
609
				}
610
611 e7cac368 Viktor G
				/* do not remove VIP if the IP address remains the same */
612
				foreach ($config['virtualip']['vip'] as $vip) {
613
					if ($vip['subnet'] == $oldvipar['subnet']) {
614
						continue 2;
615
					}
616
				}
617
618 962f215d Phil Davis
				if (is_ipaddrv6($oldvipar['subnet'])) {
619 f81e7cc4 Renato Botelho
					 mwexec("/sbin/ifconfig " .
620
					     escapeshellarg($oldvipif) .
621
					     " inet6 " .
622
					     escapeshellarg($oldvipar['subnet']) .
623
					     " delete");
624 962f215d Phil Davis
				} else {
625 f81e7cc4 Renato Botelho
					pfSense_interface_deladdress($oldvipif,
626
					    $oldvipar['subnet']);
627 962f215d Phil Davis
				}
628 e3cffd6c Ermal LUÇI
			}
629 f81e7cc4 Renato Botelho
			if ($carp_setuped == true) {
630
				interfaces_sync_setup();
631
			}
632
			if ($anyproxyarp == true) {
633
				interface_proxyarp_configure();
634
			}
635 51611440 Ermal
		}
636 f81e7cc4 Renato Botelho
637 79f7bc7f Renato Botelho
		local_sync_accounts($u2add, $u2del, $g2add, $g2del);
638 23fcdccc Viktor G
		$this->filter_configure(false, $force_filterconfigure);
639
		unset($old_config);
640 79f7bc7f Renato Botelho
641 f81e7cc4 Renato Botelho
		return true;
642 962f215d Phil Davis
	}
643 d026178f Renato Botelho
644 f81e7cc4 Renato Botelho
	/**
645
	 * Merge items into installedpackages config section
646
	 *
647
	 * @param array $section
648
	 *
649
	 * @return bool
650
	 */
651 4f26f187 Viktor G
	public function merge_installedpackages_section($section, $timeout) {
652
		ini_set('default_socket_timeout', $timeout);
653 dc5f639f PiBa-NL
		$this->auth();
654 d026178f Renato Botelho
655 f81e7cc4 Renato Botelho
		global $config;
656 50d49018 Colin Smith
657 f81e7cc4 Renato Botelho
		if ($this->loop_detected) {
658
			log_error("Disallowing CARP sync loop");
659
			return true;
660
		}
661 82ae5cfc Scott Ullrich
662 f81e7cc4 Renato Botelho
		$config['installedpackages'] = array_merge(
663
		    $config['installedpackages'], $section);
664 8cb29dac doktornotor
		$mergedkeys = implode(", ", array_keys($section));
665 f81e7cc4 Renato Botelho
		write_config(sprintf(gettext(
666
		    "Merged in config (%s sections) from XMLRPC client."),
667
		    $mergedkeys));
668 137f46d8 Ermal
669 f81e7cc4 Renato Botelho
		return true;
670 fb0eb20b Ermal
	}
671 c87f4b70 Ermal
672 f81e7cc4 Renato Botelho
	/**
673
	 * Merge items into config
674
	 *
675
	 * @param array $section
676
	 *
677
	 * @return bool
678
	 */
679 4f26f187 Viktor G
	public function merge_config_section($section, $timeout) {
680
		ini_set('default_socket_timeout', $timeout);
681 dc5f639f PiBa-NL
		$this->auth();
682 137f46d8 Ermal
683 f81e7cc4 Renato Botelho
		global $config;
684 82ae5cfc Scott Ullrich
685 f81e7cc4 Renato Botelho
		if ($this->loop_detected) {
686
			log_error("Disallowing CARP sync loop");
687
			return true;
688
		}
689 dc1cd85d Scott Ullrich
690 f81e7cc4 Renato Botelho
		$config_new = $this->array_overlay($config, $section);
691
		$config = $config_new;
692 8cb29dac doktornotor
		$mergedkeys = implode(", ", array_keys($section));
693 f81e7cc4 Renato Botelho
		write_config(sprintf(gettext(
694
		    "Merged in config (%s sections) from XMLRPC client."),
695
		    $mergedkeys));
696 c87f4b70 Ermal
697 f81e7cc4 Renato Botelho
		return true;
698 fb0eb20b Ermal
	}
699 c87f4b70 Ermal
700 f81e7cc4 Renato Botelho
	/**
701
	 * Wrapper for filter_configure()
702
	 *
703
	 * @return bool
704 57b5da70 jim-p
	 */
705 23fcdccc Viktor G
	private function filter_configure($reset_accounts = true, $force = false) {
706
		global $g, $config, $old_config;
707 f81e7cc4 Renato Botelho
708
		filter_configure();
709
		system_routing_configure();
710
		setup_gateways_monitor();
711 23fcdccc Viktor G
712
		/* do not restart unchanged services on XMLRPC sync,
713
		 * see https://redmine.pfsense.org/issues/11082 
714
		 */
715
		if (is_array($config['openvpn']) || is_array($old_config['openvpn'])) {
716
			foreach (array("server", "client") as $type) {
717
				$remove_id = array();
718
				if (is_array($old_config['openvpn']["openvpn-{$type}"])) {
719
					foreach ($old_config['openvpn']["openvpn-{$type}"] as & $old_settings) {
720
						$remove_id[] = $old_settings['vpnid'];
721
					}
722
				}
723
				if (!is_array($config['openvpn']["openvpn-{$type}"])) {
724
					continue;
725
				}
726
				foreach ($config['openvpn']["openvpn-{$type}"] as & $settings) {
727
					$new_instance = true;
728
					if (in_array($settings['vpnid'], $remove_id)) {
729
						$remove_id = array_diff($remove_id, array($settings['vpnid']));
730
					}
731
					if (is_array($old_config['openvpn']["openvpn-{$type}"])) {
732
						foreach ($old_config['openvpn']["openvpn-{$type}"] as & $old_settings) {
733
							if ($settings['vpnid'] == $old_settings['vpnid']) {
734
								$new_instance = false;
735
								if (($settings != $old_settings) || $force) {
736
									/* restart changed openvpn instance */
737
									openvpn_resync($type, $settings);
738
									break;
739
								}
740
							}
741
						}
742
					}
743
					if ($new_instance) {
744
						/* start new openvpn instance */
745
						openvpn_resync($type, $settings);
746
					}
747
				}
748
				if (!empty($remove_id)) {
749
					foreach ($remove_id as $id) {
750
						/* stop/delete removed openvpn instances */
751
						openvpn_delete($type, array('vpnid' => $id));
752
					}
753
				}
754
			}
755
			/* no service restart required */
756
			openvpn_resync_csc_all();
757
		}
758 f81e7cc4 Renato Botelho
759 6ae26227 Viktor G
		/* run ipsec_configure() on any IPsec change, see https://redmine.pfsense.org/issues/12075 */
760
		if (((is_array($config['ipsec']) || is_array($old_config['ipsec'])) &&
761
		    ($config['ipsec'] != $old_config['ipsec'])) ||
762
		    $force) {
763
			ipsec_configure();
764
		}
765
766 f81e7cc4 Renato Botelho
		/*
767
		 * The DNS Resolver and the DNS Forwarder may both be active so
768
		 * long as * they are running on different ports.
769
		 * See ticket #5882
770
		 */
771 23fcdccc Viktor G
		if (((is_array($config['dnsmasq']) || is_array($old_config['dnsmasq'])) &&
772
		    ($config['dnsmasq'] != $old_config['dnsmasq'])) ||
773
		    $force) {
774
			if (isset($config['dnsmasq']['enable'])) {
775
				/* Configure dnsmasq but tell it NOT to restart DHCP */
776
				services_dnsmasq_configure(false);
777
			} else {
778
				/* kill any running dnsmasq instance */
779
				if (isvalidpid("{$g['varrun_path']}/dnsmasq.pid")) {
780
					sigkillbypid("{$g['varrun_path']}/dnsmasq.pid",
781
					    "TERM");
782
				}
783 f81e7cc4 Renato Botelho
			}
784 57b5da70 jim-p
		}
785 23fcdccc Viktor G
		if (((is_array($config['unbound']) || is_array($old_config['unbound'])) &&
786
		    ($config['unbound'] != $old_config['unbound'])) ||
787
		    $force) {
788
			if (isset($config['unbound']['enable'])) {
789
				/* Configure unbound but tell it NOT to restart DHCP */
790
				services_unbound_configure(false);
791
			} else {
792
				/* kill any running Unbound instance */
793
				if (isvalidpid("{$g['varrun_path']}/unbound.pid")) {
794
					sigkillbypid("{$g['varrun_path']}/unbound.pid",
795
					    "TERM");
796
				}
797 f81e7cc4 Renato Botelho
			}
798 57b5da70 jim-p
		}
799 137f46d8 Ermal
800 f81e7cc4 Renato Botelho
		/*
801
		 * Call this separately since the above are manually set to
802
		 * skip the DHCP restart they normally perform.
803
		 * This avoids restarting dhcpd twice as described on
804
		 * ticket #3797
805
		 */
806 23fcdccc Viktor G
		if (((is_array($config['dhcpd']) || is_array($old_config['dhcpd'])) &&
807
		    ($config['dhcpd'] != $old_config['dhcpd'])) ||
808
		    $force) {
809
			services_dhcpd_configure();
810
		}
811 137f46d8 Ermal
812 30169caa Viktor G
		if (((is_array($config['dhcrelay']) || is_array($old_config['dhcrelay'])) &&
813
		    ($config['dhcrelay'] != $old_config['dhcrelay'])) ||
814
		    $force) {
815
			services_dhcrelay_configure();
816
		}
817
818
		if (((is_array($config['dhcrelay6']) || is_array($old_config['dhcrelay6'])) &&
819
		    ($config['dhcrelay6'] != $old_config['dhcrelay6'])) ||
820
		    $force) {
821
			services_dhcrelay6_configure();
822
		}
823
824 79f7bc7f Renato Botelho
		if ($reset_accounts) {
825
			local_reset_accounts();
826
		}
827 c87f4b70 Ermal
828 23fcdccc Viktor G
		if ((is_array($config['captiveportal']) || is_array($old_config['captiveportal']) &&
829
		    ($config['captiveportal'] != $old_config['captiveportal'])) ||
830
		    $force) {
831
			captiveportal_configure();
832
		}
833
		if ((is_array($config['voucher']) || is_array($old_config['voucher']) &&
834
		    ($config['voucher'] != $old_config['voucher'])) ||
835
		    $force) {
836
			voucher_configure();
837
		}
838 7cab6335 Renato Botelho
839 f81e7cc4 Renato Botelho
		return true;
840 3dd2a278 Scott Ullrich
	}
841 137f46d8 Ermal
842 24600471 Augustin-FL
	/**
843
	 * Wrapper for captiveportal connected users and
844
	 * active/expired vouchers synchronization
845
	 *
846
	 * @param array $arguments
847
	 *
848
	 * @return array
849
	 */
850 4f26f187 Viktor G
	public function captive_portal_sync($arguments, $timeout) {
851
		ini_set('default_socket_timeout', $timeout);
852 24600471 Augustin-FL
		$this->auth();
853
		// Note : no protection against CARP loop is done here, and this is in purpose.
854
		// This function is used for bi-directionnal sync, which is precisely what CARP loop protection is supposed to prevent.
855
		// CARP loop has to be managed within functions using captive_portal_sync()
856
		global $g, $config, $cpzone;
857
858
		if (empty($arguments['op']) || empty($arguments['zone']) || empty($config['captiveportal'][$arguments['zone']])) {
859
			return false;
860
		}
861
		$cpzone = $arguments['zone'];
862
863
		if ($arguments['op'] === 'get_databases') {
864 8de9ebba Christian McDonald
			$active_vouchers = [];
865
			$expired_vouchers = [];
866
			$usedmacs = [];
867 24600471 Augustin-FL
868
			if (is_array($config['voucher'][$cpzone]['roll'])) {
869 d35a18fc Christian McDonald
				foreach($config['voucher'][$cpzone]['roll'] as $roll) {
870 24600471 Augustin-FL
					$expired_vouchers[$roll['number']] = base64_encode(voucher_read_used_db($roll['number']));
871
					$active_vouchers[$roll['number']] = voucher_read_active_db($roll['number']);
872
				}
873
			}
874 a81a6edc Viktor G
			if (!empty($config['captiveportal'][$cpzone]['freelogins_count']) &&
875
			    !empty($config['captiveportal'][$cpzone]['freelogins_resettimeout'])) {
876
				$usedmacs = captiveportal_read_usedmacs_db();
877
			}
878 24600471 Augustin-FL
			// base64 is here for safety reasons, as we don't fully control
879
			// the content of these arrays.
880
			$returndata = array('connected_users' => base64_encode(serialize(captiveportal_read_db())),
881
			'active_vouchers' => base64_encode(serialize($active_vouchers)),
882 a81a6edc Viktor G
			'expired_vouchers' => base64_encode(serialize($expired_vouchers)),
883
			'usedmacs' => base64_encode(serialize($usedmacs)));
884 24600471 Augustin-FL
885
			return $returndata;
886
		} elseif ($arguments['op'] === 'connect_user') {
887
			$user = unserialize(base64_decode($arguments['user']));
888
			$user['attributes']['allow_time'] = $user['allow_time'];
889
890
			// pipeno might be different between primary and secondary
891
			$pipeno = captiveportal_get_next_dn_ruleno('auth');
892
			return portal_allow($user['clientip'], $user['clientmac'], $user['username'], $user['password'], null,
893
			    $user['attributes'], $pipeno, $user['authmethod'], $user['context'], $user['sessionid']);
894 4a778ba9 Augustin-FL
		} elseif ($arguments['op'] === 'disconnect_user') {
895
			$session = unserialize(base64_decode($arguments['session']));
896
			/* read database again, as pipeno might be different between primary & secondary */
897
			$sessionid = SQLite3::escapeString($session['sessionid']);
898
			$local_dbentry = captiveportal_read_db("WHERE sessionid = '{$sessionid}'");
899
900
			if (!empty($local_dbentry) && count($local_dbentry) == 1) {
901
				return captiveportal_disconnect($local_dbentry[0], $session['term_cause'], $session['stop_time'], true);
902
			} else {
903
				return false;
904
			}
905
		} elseif ($arguments['op'] === 'remove_entries') {
906
			$entries = unserialize(base64_decode($arguments['entries']));
907
908
			return captiveportal_remove_entries($entries, true);
909 78784180 Augustin-FL
		} elseif ($arguments['op'] === 'disconnect_all') {
910
			$arguments = unserialize(base64_decode($arguments['arguments']));
911
912
			return captiveportal_disconnect_all($arguments['term_cause'], $arguments['logout_reason'], true);
913 318e3f81 Augustin-FL
		} elseif ($arguments['op'] === 'write_vouchers') {
914
			$arguments = unserialize(base64_decode($arguments['arguments']));
915
916
			if (is_array($arguments['active_and_used_vouchers_bitmasks'])) {
917
				foreach ($arguments['active_and_used_vouchers_bitmasks'] as $roll => $used) {
918
					if (is_array($used)) {
919
						foreach ($used as $u) {
920
							voucher_write_used_db($roll, base64_encode($u));
921
						}
922
					} else {
923
						voucher_write_used_db($roll, base64_encode($used));
924
					}
925
				}
926
			}
927
			foreach ($arguments['active_vouchers'] as $roll => $active_vouchers) {
928
				voucher_write_active_db($roll, $active_vouchers);
929
			}
930
			return true;
931 a81a6edc Viktor G
		} elseif ($arguments['op'] === 'write_usedmacs') {
932
			$arguments = unserialize(base64_decode($arguments['arguments']));
933
934
			captiveportal_write_usedmacs_db($arguments['usedmacs']); 
935
			return true;
936 24600471 Augustin-FL
		}
937
	}
938
939 f81e7cc4 Renato Botelho
	/**
940
	 * Wrapper for configuring CARP interfaces
941
	 *
942
	 * @return bool
943
	 */
944 dc5f639f PiBa-NL
	public function interfaces_carp_configure() {
945
		$this->auth();
946 efe7562e Scott Ullrich
947 f81e7cc4 Renato Botelho
		if ($this->loop_detected) {
948
			log_error("Disallowing CARP sync loop");
949
			return true;
950
		}
951 0567899d Ermal
952 f81e7cc4 Renato Botelho
		interfaces_vips_configure();
953 e501de37 Ermal
954 f81e7cc4 Renato Botelho
		return true;
955
	}
956 e501de37 Ermal
957 f81e7cc4 Renato Botelho
	/**
958
	 * Wrapper for rc.reboot
959
	 *
960
	 * @return bool
961
	 */
962 dc5f639f PiBa-NL
	public function reboot() {
963
		$this->auth();
964 e501de37 Ermal
965 f81e7cc4 Renato Botelho
		mwexec_bg("/etc/rc.reboot");
966 137f46d8 Ermal
967 f81e7cc4 Renato Botelho
		return true;
968 3dd2a278 Scott Ullrich
	}
969 d9064267 Colin Smith
}
970
971 f3f98e97 Phil Davis
// run script until its done and can 'unlock' the xmlrpc.lock, this prevents hanging php-fpm / webgui
972 179377b0 robjarsen
ignore_user_abort(true);
973 8239af2d PiBa-NL
set_time_limit(0);
974
975 67d78c87 Ermal
$xmlrpclockkey = lock('xmlrpc', LOCK_EX);
976
977 f81e7cc4 Renato Botelho
XML_RPC2_Backend::setBackend('php');
978
$HTTP_RAW_POST_DATA = file_get_contents('php://input');
979
980
$options = array(
981
	'prefix' => 'pfsense.',
982
	'encoding' => 'utf-8',
983 4f78ae1d Renato Botelho
	'autoDocument' => false,
984 50d49018 Colin Smith
);
985 b298dd06 Scott Ullrich
986 f81e7cc4 Renato Botelho
$server = XML_RPC2_Server::create(new pfsense_xmlrpc_server(), $options);
987
$server->handleCall();
988 67d78c87 Ermal
989 f81e7cc4 Renato Botelho
unlock($xmlrpclockkey);
990 0b581a8a Scott Ullrich
991 de63649b Rafael Lucas
?>