Project

General

Profile

Download (26.5 KB) Statistics
| Branch: | Tag: | Revision:
1
<?php
2
/*
3
 * xmlrpc.php
4
 *
5
 * part of pfSense (https://www.pfsense.org)
6
 * Copyright (c) 2004-2013 BSD Perimeter
7
 * Copyright (c) 2013-2016 Electric Sheep Fencing
8
 * Copyright (c) 2014-2021 Rubicon Communications, LLC (Netgate)
9
 * Copyright (c) 2005 Colin Smith
10
 * All rights reserved.
11
 *
12
 * 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
 *
16
 * http://www.apache.org/licenses/LICENSE-2.0
17
 *
18
 * 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
 */
24

    
25
##|+PRIV
26
##|*IDENT=page-xmlrpclibrary
27
##|*NAME=XMLRPC Library
28
##|*DESCR=Allow access to the 'XMLRPC Library' page.
29
##|*MATCH=xmlrpc.php*
30
##|-PRIV
31

    
32
require_once("config.inc");
33
require_once("functions.inc");
34
require_once("auth.inc");
35
require_once("filter.inc");
36
require_once("ipsec.inc");
37
require_once("vpn.inc");
38
require_once("openvpn.inc");
39
require_once("captiveportal.inc");
40
require_once("shaper.inc");
41
require_once("XML/RPC2/Server.php");
42

    
43
class pfsense_xmlrpc_server {
44

    
45
	private $loop_detected = false;
46
	private $remote_addr;
47

    
48
	private function auth() {
49
		global $config, $userindex;
50
		$userindex = index_users();
51

    
52
		$username = $_SERVER['PHP_AUTH_USER'];
53
		$password = $_SERVER['PHP_AUTH_PW'];
54

    
55
		$login_ok = false;
56
		if (!empty($username) && !empty($password)) {
57
			$attributes = array();
58
			$authcfg = auth_get_authserver(
59
			    $config['system']['webgui']['authmode']);
60

    
61
			if (authenticate_user($username, $password,
62
			    $authcfg, $attributes) ||
63
			    authenticate_user($username, $password)) {
64
				$login_ok = true;
65
			}
66
		}
67

    
68
		if (!$login_ok) {
69
			log_auth(sprintf(gettext("webConfigurator authentication error for user '%1\$s' from: %2\$s"),
70
			    $username,
71
			    $this->remote_addr));
72

    
73
			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
	}
98

    
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
	}
113

    
114
	public function __construct() {
115
		global $config;
116

    
117
		$this->remote_addr = $_SERVER['REMOTE_ADDR'];
118

    
119
		/* grab sync to ip if enabled */
120
		if (isset($config['hasync']['synchronizetoip']) &&
121
		    $config['hasync']['synchronizetoip'] == $this->remote_addr) {
122
			$this->loop_detected = true;
123
		}
124
	}
125

    
126
	/**
127
	 * Get host version information
128
	 *
129
	 * @return array
130
	 */
131
	public function host_firmware_version($dummy = 1) {
132
		$this->auth();
133
		return host_firmware_version();
134
	}
135

    
136
	/**
137
	 * Executes a PHP block of code
138
	 *
139
	 * @param string $code
140
	 *
141
	 * @return bool
142
	 */
143
	public function exec_php($code) {
144
		$this->auth();
145

    
146
		eval($code);
147
		if ($toreturn) {
148
			return $toreturn;
149
		}
150

    
151
		return true;
152
	}
153

    
154
	/**
155
	 * Executes shell commands
156
	 *
157
	 * @param string $code
158
	 *
159
	 * @return bool
160
	 */
161
	public function exec_shell($code) {
162
		$this->auth();
163

    
164
		mwexec($code);
165
		return true;
166
	}
167

    
168
	/**
169
	 * Backup chosen config sections
170
	 *
171
	 * @param array $section
172
	 *
173
	 * @return array
174
	 */
175
	public function backup_config_section($section) {
176
		$this->auth();
177

    
178
		global $config;
179

    
180
		return array_intersect_key($config, array_flip($section));
181
	}
182

    
183
	/**
184
	 * Restore defined config section into local config
185
	 *
186
	 * @param array $sections
187
	 *
188
	 * @return bool
189
	 */
190
	public function restore_config_section($sections) {
191
		$this->auth();
192

    
193
		global $config, $cpzone, $cpzoneid, $old_config;
194

    
195
		$old_config = $config;
196
		$old_ipsec_enabled = ipsec_enabled();
197

    
198
		if ($this->loop_detected) {
199
			log_error("Disallowing CARP sync loop");
200
			return true;
201
		}
202

    
203
		/*
204
		 * Some sections should just be copied and not merged or we end
205
		 * up unable to sync the deletion of the last item in a section
206
		 */
207
		$sync_full_sections = array(
208
			'aliases',
209
			'ca',
210
			'cert',
211
			'crl',
212
			'dhcpd',
213
			'dnshaper',
214
			'dnsmasq',
215
			'filter',
216
			'ipsec',
217
			'nat',
218
			'openvpn',
219
			'schedules',
220
			'shaper',
221
			'unbound',
222
			'wol',
223
		);
224

    
225
		$syncd_full_sections = array();
226

    
227
		foreach ($sync_full_sections as $section) {
228
			if (!isset($sections[$section])) {
229
				continue;
230
			}
231

    
232
			$config[$section] = $sections[$section];
233
			unset($sections[$section]);
234
			$syncd_full_sections[] = $section;
235
		}
236

    
237
		/* If captive portal sync is enabled on primary node, remove local CP on the secondary */
238
		if (is_array($config['captiveportal']) && is_array($sections['captiveportal'])) {
239
			foreach ($config['captiveportal'] as $zone => $item) {
240
				if (!isset($sections['captiveportal'][$zone])) {
241
					$cpzone = $zone;
242
					unset($config['captiveportal'][$cpzone]['enable']);
243
					captiveportal_configure_zone($config['captiveportal'][$cpzone]);
244
					unset($config['captiveportal'][$cpzone]);
245
					if (isset($config['voucher'][$cpzone])) {
246
						unset($config['voucher'][$cpzone]);
247
					}
248
					unlink_if_exists("/var/db/captiveportal{$cpzone}.db");
249
					unlink_if_exists("/var/db/captiveportal_usedmacs_{$cpzone}.db");
250
					unlink_if_exists("/var/db/voucher_{$cpzone}_*.db");
251
				}
252
			}
253
		}
254

    
255
		/* Only touch users if users are set to synchronize from the primary node
256
		 * See https://redmine.pfsense.org/issues/8450
257
		 */
258
		if ($sections['system']['user'] && $sections['system']['group']) {
259
			$g2add = array();
260
			$g2del = array();
261
			$g2del_idx = array();
262
			$g2keep = array();
263
			if (is_array($sections['system']['group'])) {
264
				$local_groups = isset($config['system']['group'])
265
				    ? $config['system']['group']
266
				    : array();
267

    
268
				foreach ($sections['system']['group'] as $group) {
269
					$idx = array_search($group['name'],
270
					    array_column($local_groups, 'name'));
271

    
272
					if ($idx === false) {
273
						$g2add[] = $group;
274
					} else if ($group['gid'] < 1999) {
275
						$g2keep[] = $idx;
276
					} else if ($group != $local_groups[$idx]) {
277
						$g2add[] = $group;
278
						$g2del[] = $group;
279
						$g2del_idx[] = $idx;
280
					} else {
281
						$g2keep[] = $idx;
282
					}
283
				}
284
			}
285
			if (is_array($config['system']['group'])) {
286
				foreach ($config['system']['group'] as $idx => $group) {
287
					if (array_search($idx, $g2keep) === false &&
288
					    array_search($idx, $g2del_idx) === false) {
289
						$g2del[] = $group;
290
						$g2del_idx[] = $idx;
291
					}
292
				}
293
			}
294
			unset($sections['system']['group'], $g2keep, $g2del_idx);
295

    
296
			$u2add = array();
297
			$u2del = array();
298
			$u2del_idx = array();
299
			$u2keep = array();
300
			if (is_array($sections['system']['user'])) {
301
				$local_users = isset($config['system']['user'])
302
				    ? $config['system']['user']
303
				    : array();
304

    
305
				foreach ($sections['system']['user'] as $user) {
306
					$idx = array_search($user['name'],
307
					    array_column($local_users, 'name'));
308

    
309
					if ($idx === false) {
310
						$u2add[] = $user;
311
					} else if (($user['uid'] < 2000) && ($sections['hasync']['adminsync'] != 'on')) {
312
						$u2keep[] = $idx;
313
					} else if ($user != $local_users[$idx]) {
314
						$u2add[] = $user;
315
						$u2del[] = $user;
316
						$u2del_idx[] = $idx;
317
					} else {
318
						$u2keep[] = $idx;
319
					}
320
				}
321
			}
322
			if (is_array($config['system']['user'])) {
323
				foreach ($config['system']['user'] as $idx => $user) {
324
					if (array_search($idx, $u2keep) === false &&
325
					    array_search($idx, $u2del_idx) === false) {
326
						$u2del[] = $user;
327
						$u2del_idx[] = $idx;
328
					}
329
				}
330
			}
331
			unset($sections['system']['user'], $u2keep, $u2del_idx);
332
		}
333

    
334
		$voucher = array();
335
		if (is_array($sections['voucher'])) {
336
			/* Save voucher rolls to process after merge */
337
			$voucher = $sections['voucher'];
338

    
339
			foreach($sections['voucher'] as $zone => $item) {
340
				unset($sections['voucher'][$zone]['roll']);
341
				// Note : This code can be safely deleted once #97 fix has been applied and deployed to pfSense stable release.
342
				// Please do not delete this code before
343
				if (isset($config['voucher'][$zone]['vouchersyncdbip'])) {
344
					$sections['voucher'][$zone]['vouchersyncdbip'] =
345
					    $config['voucher'][$zone]['vouchersyncdbip'];
346
				} else {
347
					unset($sections['voucher'][$zone]['vouchersyncdbip']);
348
				}
349
				if (isset($config['voucher'][$zone]['vouchersyncusername'])) {
350
					$sections['voucher'][$zone]['vouchersyncusername'] =
351
					    $config['voucher'][$zone]['vouchersyncusername'];
352
				} else {
353
					unset($sections['voucher'][$zone]['vouchersyncusername']);
354
				}
355
				if (isset($config['voucher'][$zone]['vouchersyncpass'])) {
356
					$sections['voucher'][$zone]['vouchersyncpass'] =
357
					    $config['voucher'][$zone]['vouchersyncpass'];
358
				} else {
359
					unset($sections['voucher'][$zone]['vouchersyncpass']);
360
				}
361
				// End note.
362
			}
363
		}
364

    
365
		if (is_array($sections['captiveportal'])) {
366
			// Captiveportal : Backward HA settings should remain local.
367
			foreach ($sections['captiveportal'] as $zone => $cp) {
368
				if (isset($config['captiveportal'][$zone]['enablebackwardsync'])) {
369
					$sections['captiveportal'][$zone]['enablebackwardsync'] = $config['captiveportal'][$zone]['enablebackwardsync'];
370
				} else {
371
					unset($sections['captiveportal'][$zone]['enablebackwardsync']);
372
				}
373
				if (isset($config['captiveportal'][$zone]['backwardsyncip'])) {
374
					$sections['captiveportal'][$zone]['backwardsyncip'] = $config['captiveportal'][$zone]['backwardsyncip'];
375
				} else {
376
					unset($sections['captiveportal'][$zone]['backwardsyncip']);
377
				}
378
				if (isset($config['captiveportal'][$zone]['backwardsyncuser'])) {
379
					$sections['captiveportal'][$zone]['backwardsyncuser'] = $config['captiveportal'][$zone]['backwardsyncuser'];
380
				} else {
381
					unset($sections['captiveportal'][$zone]['backwardsyncuser']);
382
				}
383
				if (isset($config['captiveportal'][$zone]['backwardsyncpassword'])) {
384
					$sections['captiveportal'][$zone]['backwardsyncpassword'] = $config['captiveportal'][$zone]['backwardsyncpassword'];
385
				} else {
386
					unset($sections['captiveportal'][$zone]['vouchersyncpass']);
387
				}
388
			}
389
			$config['captiveportal'] = $sections['captiveportal'];
390
			unset($sections['captiveportal']);
391
		}
392

    
393
		$vipbackup = array();
394
		$oldvips = array();
395
		if (isset($sections['virtualip']) &&
396
		    is_array($config['virtualip']['vip'])) {
397
			foreach ($config['virtualip']['vip'] as $vip) {
398
				if ($vip['mode'] == "carp") {
399
					$key = $vip['interface'] .
400
					    "_vip" . $vip['vhid'];
401

    
402
					$oldvips[$key]['content'] =
403
					    $vip['password'] .
404
					    $vip['advskew'] .
405
					    $vip['subnet'] .
406
					    $vip['subnet_bits'] .
407
					    $vip['advbase'];
408
					$oldvips[$key]['interface'] =
409
					    $vip['interface'];
410
					$oldvips[$key]['subnet'] =
411
					    $vip['subnet'];
412
				} else if ($vip['mode'] == "ipalias" &&
413
				    (substr($vip['interface'], 0, 4) == '_vip'
414
				    || strstr($vip['interface'], "lo0"))) {
415
					$oldvips[$vip['subnet']]['content'] =
416
					    $vip['interface'] .
417
					    $vip['subnet'] .
418
					    $vip['subnet_bits'];
419
					$oldvips[$vip['subnet']]['interface'] =
420
					    $vip['interface'];
421
					$oldvips[$vip['subnet']]['subnet'] =
422
					    $vip['subnet'];
423
				} else if (($vip['mode'] == "ipalias" ||
424
				    $vip['mode'] == 'proxyarp') &&
425
				    !(substr($vip['interface'], 0, 4) == '_vip')
426
				    || strstr($vip['interface'], "lo0")) {
427
					$vipbackup[] = $vip;
428
				}
429
			}
430
		}
431

    
432
		/* For vip section, first keep items sent from the master */
433
		$config = array_merge_recursive_unique($config, $sections);
434

    
435

    
436
		/* Remove locally items removed remote */
437
		foreach ($voucher as $zone => $item) {
438
			/* No rolls on master, delete local ones */
439
			if (!is_array($item['roll'])) {
440
				unset($config['voucher'][$zone]['roll']);
441
			}
442
		}
443

    
444
		$l_rolls = array();
445
		if (is_array($config['voucher'])) {
446
			foreach ($config['voucher'] as $zone => $item) {
447
				if (!is_array($item['roll'])) {
448
					continue;
449
				}
450
				foreach ($item['roll'] as $idx => $roll) {
451
					/* Make it easy to find roll by # */
452
					$l_rolls[$zone][$roll['number']] = $idx;
453
				}
454
			}
455
		}
456

    
457
		/*
458
		 * Process vouchers sent by primary node and:
459
		 * - Add new items
460
		 * - Update existing items based on 'lastsync' field
461
		 */
462
		foreach ($voucher as $zone => $item) {
463
			if (!is_array($item['roll'])) {
464
				continue;
465
			}
466
			foreach ($item['roll'] as $idx => $roll) {
467
				if (!isset($l_rolls[$zone][$roll['number']])) {
468
					$config['voucher'][$zone]['roll'][] =
469
					    $roll;
470
					continue;
471
				}
472
				$l_roll_idx = $l_rolls[$zone][$roll['number']];
473
				init_config_arr(array('voucher', $zone));
474
				$l_vouchers = &$config['voucher'][$zone];
475
				$l_roll = $l_vouchers['roll'][$l_roll_idx];
476
				if (!isset($l_roll['lastsync'])) {
477
					$l_roll['lastsync'] = 0;
478
				}
479

    
480
				if (isset($roll['lastsync']) &&
481
				    $roll['lastsync'] != $l_roll['lastsync']) {
482
					$l_vouchers['roll'][$l_roll_idx] =
483
					    $roll;
484
					unset($l_rolls[$zone][$roll['number']]);
485
				}
486
			}
487
		}
488

    
489
		/*
490
		 * At this point $l_rolls contains only items that are not
491
		 * present on primary node. They must be removed
492
		 */
493
		foreach ($l_rolls as $zone => $item) {
494
			foreach ($item as $number => $idx) {
495
				unset($config['voucher'][$zone][$idx]);
496
			}
497
		}
498

    
499
		/*
500
		 * Then add ipalias and proxyarp types already defined
501
		 * on the backup
502
		 */
503
		if (is_array($vipbackup) && !empty($vipbackup)) {
504
			if (!is_array($config['virtualip'])) {
505
				$config['virtualip'] = array();
506
			}
507
			if (!is_array($config['virtualip']['vip'])) {
508
				$config['virtualip']['vip'] = array();
509
			}
510
			foreach ($vipbackup as $vip) {
511
				array_unshift($config['virtualip']['vip'], $vip);
512
			}
513
		}
514

    
515
		/* Log what happened */
516
		$mergedkeys = implode(", ", array_merge(array_keys($sections),
517
		    $syncd_full_sections));
518
		write_config(sprintf(gettext(
519
		    "Merged in config (%s sections) from XMLRPC client."),
520
		    $mergedkeys));
521

    
522
		/*
523
		 * The real work on handling the vips specially
524
		 * This is a copy of interfaces_vips_configure with addition of
525
		 * not reloading existing/not changed carps
526
		 */
527
		$force_filterconfigure = false;
528
		if (isset($sections['virtualip']) &&
529
		    is_array($config['virtualip']) &&
530
		    is_array($config['virtualip']['vip'])) {
531
			$carp_setuped = false;
532
			$anyproxyarp = false;
533

    
534
			foreach ($config['virtualip']['vip'] as $vip) {
535
				$key = "{$vip['interface']}_vip{$vip['vhid']}";
536

    
537
				if ($vip['mode'] == "carp" &&
538
				    isset($oldvips[$key])) {
539
					if ($oldvips[$key]['content'] ==
540
					    $vip['password'] .
541
					    $vip['advskew'] .
542
					    $vip['subnet'] .
543
					    $vip['subnet_bits'] .
544
					    $vip['advbase'] &&
545
					    does_vip_exist($vip)) {
546
						unset($oldvips[$key]);
547
						/*
548
						 * Skip reconfiguring this vips
549
						 * since nothing has changed.
550
						 */
551
						continue;
552
					}
553

    
554
				} elseif ($vip['mode'] == "ipalias" &&
555
				    (substr($vip['interface'], 0, 4) == '_vip'
556
				    || strstr($vip['interface'], "lo0")) &&
557
				    isset($oldvips[$vip['subnet']])) {
558
					$key = $vip['subnet'];
559
					if ($oldvips[$key]['content'] ==
560
					    $vip['interface'] .
561
					    $vip['subnet'] .
562
					    $vip['subnet_bits'] &&
563
					    does_vip_exist($vip)) {
564
						unset($oldvips[$key]);
565
						/*
566
						 * Skip reconfiguring this vips
567
						 * since nothing has changed.
568
						 */
569
						continue;
570
					}
571
					unset($oldvips[$key]);
572
				}
573

    
574
				switch ($vip['mode']) {
575
				case "proxyarp":
576
					$anyproxyarp = true;
577
					break;
578
				case "ipalias":
579
					interface_ipalias_configure($vip);
580
					break;
581
				case "carp":
582
					$carp_setuped = true;
583
					interface_carp_configure($vip);
584
					break;
585
				}
586
				$force_filterconfigure = true;
587
			}
588

    
589
			/* Cleanup remaining old carps */
590
			foreach ($oldvips as $oldvipar) {
591
				$oldvipif = get_real_interface(
592
				    $oldvipar['interface']);
593

    
594
				if (empty($oldvipif)) {
595
					continue;
596
				}
597

    
598
				if (is_ipaddrv6($oldvipar['subnet'])) {
599
					 mwexec("/sbin/ifconfig " .
600
					     escapeshellarg($oldvipif) .
601
					     " inet6 " .
602
					     escapeshellarg($oldvipar['subnet']) .
603
					     " delete");
604
				} else {
605
					pfSense_interface_deladdress($oldvipif,
606
					    $oldvipar['subnet']);
607
				}
608
			}
609
			if ($carp_setuped == true) {
610
				interfaces_sync_setup();
611
			}
612
			if ($anyproxyarp == true) {
613
				interface_proxyarp_configure();
614
			}
615
		}
616

    
617
		if ($old_ipsec_enabled !== ipsec_enabled()) {
618
			ipsec_configure();
619
		}
620

    
621
		local_sync_accounts($u2add, $u2del, $g2add, $g2del);
622
		$this->filter_configure(false, $force_filterconfigure);
623
		unset($old_config);
624

    
625
		return true;
626
	}
627

    
628
	/**
629
	 * Merge items into installedpackages config section
630
	 *
631
	 * @param array $section
632
	 *
633
	 * @return bool
634
	 */
635
	public function merge_installedpackages_section($section) {
636
		$this->auth();
637

    
638
		global $config;
639

    
640
		if ($this->loop_detected) {
641
			log_error("Disallowing CARP sync loop");
642
			return true;
643
		}
644

    
645
		$config['installedpackages'] = array_merge(
646
		    $config['installedpackages'], $section);
647
		$mergedkeys = implode(", ", array_keys($section));
648
		write_config(sprintf(gettext(
649
		    "Merged in config (%s sections) from XMLRPC client."),
650
		    $mergedkeys));
651

    
652
		return true;
653
	}
654

    
655
	/**
656
	 * Merge items into config
657
	 *
658
	 * @param array $section
659
	 *
660
	 * @return bool
661
	 */
662
	public function merge_config_section($section) {
663
		$this->auth();
664

    
665
		global $config;
666

    
667
		if ($this->loop_detected) {
668
			log_error("Disallowing CARP sync loop");
669
			return true;
670
		}
671

    
672
		$config_new = $this->array_overlay($config, $section);
673
		$config = $config_new;
674
		$mergedkeys = implode(", ", array_keys($section));
675
		write_config(sprintf(gettext(
676
		    "Merged in config (%s sections) from XMLRPC client."),
677
		    $mergedkeys));
678

    
679
		return true;
680
	}
681

    
682
	/**
683
	 * Wrapper for filter_configure()
684
	 *
685
	 * @return bool
686
	 */
687
	private function filter_configure($reset_accounts = true, $force = false) {
688
		global $g, $config, $old_config;
689

    
690
		filter_configure();
691
		system_routing_configure();
692
		setup_gateways_monitor();
693

    
694
		/* do not restart unchanged services on XMLRPC sync,
695
		 * see https://redmine.pfsense.org/issues/11082 
696
		 */
697
		if (is_array($config['openvpn']) || is_array($old_config['openvpn'])) {
698
			foreach (array("server", "client") as $type) {
699
				$remove_id = array();
700
				if (is_array($old_config['openvpn']["openvpn-{$type}"])) {
701
					foreach ($old_config['openvpn']["openvpn-{$type}"] as & $old_settings) {
702
						$remove_id[] = $old_settings['vpnid'];
703
					}
704
				}
705
				if (!is_array($config['openvpn']["openvpn-{$type}"])) {
706
					continue;
707
				}
708
				foreach ($config['openvpn']["openvpn-{$type}"] as & $settings) {
709
					$new_instance = true;
710
					if (in_array($settings['vpnid'], $remove_id)) {
711
						$remove_id = array_diff($remove_id, array($settings['vpnid']));
712
					}
713
					if (is_array($old_config['openvpn']["openvpn-{$type}"])) {
714
						foreach ($old_config['openvpn']["openvpn-{$type}"] as & $old_settings) {
715
							if ($settings['vpnid'] == $old_settings['vpnid']) {
716
								$new_instance = false;
717
								if (($settings != $old_settings) || $force) {
718
									/* restart changed openvpn instance */
719
									openvpn_resync($type, $settings);
720
									break;
721
								}
722
							}
723
						}
724
					}
725
					if ($new_instance) {
726
						/* start new openvpn instance */
727
						openvpn_resync($type, $settings);
728
					}
729
				}
730
				if (!empty($remove_id)) {
731
					foreach ($remove_id as $id) {
732
						/* stop/delete removed openvpn instances */
733
						openvpn_delete($type, array('vpnid' => $id));
734
					}
735
				}
736
			}
737
			/* no service restart required */
738
			openvpn_resync_csc_all();
739
		}
740

    
741
		/*
742
		 * The DNS Resolver and the DNS Forwarder may both be active so
743
		 * long as * they are running on different ports.
744
		 * See ticket #5882
745
		 */
746
		if (((is_array($config['dnsmasq']) || is_array($old_config['dnsmasq'])) &&
747
		    ($config['dnsmasq'] != $old_config['dnsmasq'])) ||
748
		    $force) {
749
			if (isset($config['dnsmasq']['enable'])) {
750
				/* Configure dnsmasq but tell it NOT to restart DHCP */
751
				services_dnsmasq_configure(false);
752
			} else {
753
				/* kill any running dnsmasq instance */
754
				if (isvalidpid("{$g['varrun_path']}/dnsmasq.pid")) {
755
					sigkillbypid("{$g['varrun_path']}/dnsmasq.pid",
756
					    "TERM");
757
				}
758
			}
759
		}
760
		if (((is_array($config['unbound']) || is_array($old_config['unbound'])) &&
761
		    ($config['unbound'] != $old_config['unbound'])) ||
762
		    $force) {
763
			if (isset($config['unbound']['enable'])) {
764
				/* Configure unbound but tell it NOT to restart DHCP */
765
				services_unbound_configure(false);
766
			} else {
767
				/* kill any running Unbound instance */
768
				if (isvalidpid("{$g['varrun_path']}/unbound.pid")) {
769
					sigkillbypid("{$g['varrun_path']}/unbound.pid",
770
					    "TERM");
771
				}
772
			}
773
		}
774

    
775
		/*
776
		 * Call this separately since the above are manually set to
777
		 * skip the DHCP restart they normally perform.
778
		 * This avoids restarting dhcpd twice as described on
779
		 * ticket #3797
780
		 */
781
		if (((is_array($config['dhcpd']) || is_array($old_config['dhcpd'])) &&
782
		    ($config['dhcpd'] != $old_config['dhcpd'])) ||
783
		    $force) {
784
			services_dhcpd_configure();
785
		}
786

    
787
		if ($reset_accounts) {
788
			local_reset_accounts();
789
		}
790

    
791
		if ((is_array($config['captiveportal']) || is_array($old_config['captiveportal']) &&
792
		    ($config['captiveportal'] != $old_config['captiveportal'])) ||
793
		    $force) {
794
			captiveportal_configure();
795
		}
796
		if ((is_array($config['voucher']) || is_array($old_config['voucher']) &&
797
		    ($config['voucher'] != $old_config['voucher'])) ||
798
		    $force) {
799
			voucher_configure();
800
		}
801

    
802
		return true;
803
	}
804

    
805
	/**
806
	 * Wrapper for captiveportal connected users and
807
	 * active/expired vouchers synchronization
808
	 *
809
	 * @param array $arguments
810
	 *
811
	 * @return array
812
	 */
813
	public function captive_portal_sync($arguments) {
814
		$this->auth();
815
		// Note : no protection against CARP loop is done here, and this is in purpose.
816
		// This function is used for bi-directionnal sync, which is precisely what CARP loop protection is supposed to prevent.
817
		// CARP loop has to be managed within functions using captive_portal_sync()
818
		global $g, $config, $cpzone;
819

    
820
		if (empty($arguments['op']) || empty($arguments['zone']) || empty($config['captiveportal'][$arguments['zone']])) {
821
			return false;
822
		}
823
		$cpzone = $arguments['zone'];
824

    
825
		if ($arguments['op'] === 'get_databases') {
826
			$active_vouchers = array();
827
			$expired_vouchers = array();
828
			$usedmacs = '';
829

    
830
			if (is_array($config['voucher'][$cpzone]['roll'])) {
831
				foreach($config['voucher'][$cpzone]['roll'] as $id => $roll) {
832
					$expired_vouchers[$roll['number']] = base64_encode(voucher_read_used_db($roll['number']));
833
					$active_vouchers[$roll['number']] = voucher_read_active_db($roll['number']);
834
				}
835
			}
836
			if (!empty($config['captiveportal'][$cpzone]['freelogins_count']) &&
837
			    !empty($config['captiveportal'][$cpzone]['freelogins_resettimeout'])) {
838
				$usedmacs = captiveportal_read_usedmacs_db();
839
			}
840
			// base64 is here for safety reasons, as we don't fully control
841
			// the content of these arrays.
842
			$returndata = array('connected_users' => base64_encode(serialize(captiveportal_read_db())),
843
			'active_vouchers' => base64_encode(serialize($active_vouchers)),
844
			'expired_vouchers' => base64_encode(serialize($expired_vouchers)),
845
			'usedmacs' => base64_encode(serialize($usedmacs)));
846

    
847
			return $returndata;
848
		} elseif ($arguments['op'] === 'connect_user') {
849
			$user = unserialize(base64_decode($arguments['user']));
850
			$user['attributes']['allow_time'] = $user['allow_time'];
851

    
852
			// pipeno might be different between primary and secondary
853
			$pipeno = captiveportal_get_next_dn_ruleno('auth');
854
			return portal_allow($user['clientip'], $user['clientmac'], $user['username'], $user['password'], null,
855
			    $user['attributes'], $pipeno, $user['authmethod'], $user['context'], $user['sessionid']);
856
		} elseif ($arguments['op'] === 'disconnect_user') {
857
			$session = unserialize(base64_decode($arguments['session']));
858
			/* read database again, as pipeno might be different between primary & secondary */
859
			$sessionid = SQLite3::escapeString($session['sessionid']);
860
			$local_dbentry = captiveportal_read_db("WHERE sessionid = '{$sessionid}'");
861

    
862
			if (!empty($local_dbentry) && count($local_dbentry) == 1) {
863
				return captiveportal_disconnect($local_dbentry[0], $session['term_cause'], $session['stop_time'], true);
864
			} else {
865
				return false;
866
			}
867
		} elseif ($arguments['op'] === 'remove_entries') {
868
			$entries = unserialize(base64_decode($arguments['entries']));
869

    
870
			return captiveportal_remove_entries($entries, true);
871
		} elseif ($arguments['op'] === 'disconnect_all') {
872
			$arguments = unserialize(base64_decode($arguments['arguments']));
873

    
874
			return captiveportal_disconnect_all($arguments['term_cause'], $arguments['logout_reason'], true);
875
		} elseif ($arguments['op'] === 'write_vouchers') {
876
			$arguments = unserialize(base64_decode($arguments['arguments']));
877

    
878
			if (is_array($arguments['active_and_used_vouchers_bitmasks'])) {
879
				foreach ($arguments['active_and_used_vouchers_bitmasks'] as $roll => $used) {
880
					if (is_array($used)) {
881
						foreach ($used as $u) {
882
							voucher_write_used_db($roll, base64_encode($u));
883
						}
884
					} else {
885
						voucher_write_used_db($roll, base64_encode($used));
886
					}
887
				}
888
			}
889
			foreach ($arguments['active_vouchers'] as $roll => $active_vouchers) {
890
				voucher_write_active_db($roll, $active_vouchers);
891
			}
892
			return true;
893
		} elseif ($arguments['op'] === 'write_usedmacs') {
894
			$arguments = unserialize(base64_decode($arguments['arguments']));
895

    
896
			captiveportal_write_usedmacs_db($arguments['usedmacs']); 
897
			return true;
898
		}
899
	}
900

    
901
	/**
902
	 * Wrapper for configuring CARP interfaces
903
	 *
904
	 * @return bool
905
	 */
906
	public function interfaces_carp_configure() {
907
		$this->auth();
908

    
909
		if ($this->loop_detected) {
910
			log_error("Disallowing CARP sync loop");
911
			return true;
912
		}
913

    
914
		interfaces_vips_configure();
915

    
916
		return true;
917
	}
918

    
919
	/**
920
	 * Wrapper for rc.reboot
921
	 *
922
	 * @return bool
923
	 */
924
	public function reboot() {
925
		$this->auth();
926

    
927
		mwexec_bg("/etc/rc.reboot");
928

    
929
		return true;
930
	}
931
}
932

    
933
// run script until its done and can 'unlock' the xmlrpc.lock, this prevents hanging php-fpm / webgui
934
ignore_user_abort(true);
935
set_time_limit(0);
936

    
937
$xmlrpclockkey = lock('xmlrpc', LOCK_EX);
938

    
939
XML_RPC2_Backend::setBackend('php');
940
$HTTP_RAW_POST_DATA = file_get_contents('php://input');
941

    
942
$options = array(
943
	'prefix' => 'pfsense.',
944
	'encoding' => 'utf-8',
945
	'autoDocument' => false,
946
);
947

    
948
$server = XML_RPC2_Server::create(new pfsense_xmlrpc_server(), $options);
949
$server->handleCall();
950

    
951
unlock($xmlrpclockkey);
952

    
953
?>
(227-227/227)