Project

General

Profile

Download (26.8 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, $timeout) {
132
		ini_set('default_socket_timeout', $timeout);
133
		$this->auth();
134
		return host_firmware_version();
135
	}
136

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

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

    
152
		return true;
153
	}
154

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

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

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

    
179
		global $config;
180

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

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

    
195
		global $config, $cpzone, $cpzoneid, $old_config;
196

    
197
		$old_config = $config;
198
		$old_ipsec_enabled = ipsec_enabled();
199

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

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

    
227
		$syncd_full_sections = array();
228

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

    
437

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

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

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

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

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

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

    
517
		/* Log what happened */
518
		$mergedkeys = implode(", ", array_merge(array_keys($sections),
519
		    $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
		 * This is a copy of interfaces_vips_configure with addition of
527
		 * not reloading existing/not changed carps
528
		 */
529
		$force_filterconfigure = false;
530
		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
					}
555

    
556
				} elseif ($vip['mode'] == "ipalias" &&
557
				    (substr($vip['interface'], 0, 4) == '_vip'
558
				    || strstr($vip['interface'], "lo0")) &&
559
				    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
					}
573
					unset($oldvips[$key]);
574
				}
575

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

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

    
596
				if (empty($oldvipif)) {
597
					continue;
598
				}
599

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

    
619
		if ($old_ipsec_enabled !== ipsec_enabled()) {
620
			ipsec_configure();
621
		}
622

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

    
627
		return true;
628
	}
629

    
630
	/**
631
	 * Merge items into installedpackages config section
632
	 *
633
	 * @param array $section
634
	 *
635
	 * @return bool
636
	 */
637
	public function merge_installedpackages_section($section, $timeout) {
638
		ini_set('default_socket_timeout', $timeout);
639
		$this->auth();
640

    
641
		global $config;
642

    
643
		if ($this->loop_detected) {
644
			log_error("Disallowing CARP sync loop");
645
			return true;
646
		}
647

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

    
655
		return true;
656
	}
657

    
658
	/**
659
	 * Merge items into config
660
	 *
661
	 * @param array $section
662
	 *
663
	 * @return bool
664
	 */
665
	public function merge_config_section($section, $timeout) {
666
		ini_set('default_socket_timeout', $timeout);
667
		$this->auth();
668

    
669
		global $config;
670

    
671
		if ($this->loop_detected) {
672
			log_error("Disallowing CARP sync loop");
673
			return true;
674
		}
675

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

    
683
		return true;
684
	}
685

    
686
	/**
687
	 * Wrapper for filter_configure()
688
	 *
689
	 * @return bool
690
	 */
691
	private function filter_configure($reset_accounts = true, $force = false) {
692
		global $g, $config, $old_config;
693

    
694
		filter_configure();
695
		system_routing_configure();
696
		setup_gateways_monitor();
697

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

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

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

    
791
		if ($reset_accounts) {
792
			local_reset_accounts();
793
		}
794

    
795
		if ((is_array($config['captiveportal']) || is_array($old_config['captiveportal']) &&
796
		    ($config['captiveportal'] != $old_config['captiveportal'])) ||
797
		    $force) {
798
			captiveportal_configure();
799
		}
800
		if ((is_array($config['voucher']) || is_array($old_config['voucher']) &&
801
		    ($config['voucher'] != $old_config['voucher'])) ||
802
		    $force) {
803
			voucher_configure();
804
		}
805

    
806
		return true;
807
	}
808

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

    
825
		if (empty($arguments['op']) || empty($arguments['zone']) || empty($config['captiveportal'][$arguments['zone']])) {
826
			return false;
827
		}
828
		$cpzone = $arguments['zone'];
829

    
830
		if ($arguments['op'] === 'get_databases') {
831
			$active_vouchers = array();
832
			$expired_vouchers = array();
833
			$usedmacs = '';
834

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

    
852
			return $returndata;
853
		} elseif ($arguments['op'] === 'connect_user') {
854
			$user = unserialize(base64_decode($arguments['user']));
855
			$user['attributes']['allow_time'] = $user['allow_time'];
856

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

    
867
			if (!empty($local_dbentry) && count($local_dbentry) == 1) {
868
				return captiveportal_disconnect($local_dbentry[0], $session['term_cause'], $session['stop_time'], true);
869
			} else {
870
				return false;
871
			}
872
		} elseif ($arguments['op'] === 'remove_entries') {
873
			$entries = unserialize(base64_decode($arguments['entries']));
874

    
875
			return captiveportal_remove_entries($entries, true);
876
		} elseif ($arguments['op'] === 'disconnect_all') {
877
			$arguments = unserialize(base64_decode($arguments['arguments']));
878

    
879
			return captiveportal_disconnect_all($arguments['term_cause'], $arguments['logout_reason'], true);
880
		} elseif ($arguments['op'] === 'write_vouchers') {
881
			$arguments = unserialize(base64_decode($arguments['arguments']));
882

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

    
901
			captiveportal_write_usedmacs_db($arguments['usedmacs']); 
902
			return true;
903
		}
904
	}
905

    
906
	/**
907
	 * Wrapper for configuring CARP interfaces
908
	 *
909
	 * @return bool
910
	 */
911
	public function interfaces_carp_configure() {
912
		$this->auth();
913

    
914
		if ($this->loop_detected) {
915
			log_error("Disallowing CARP sync loop");
916
			return true;
917
		}
918

    
919
		interfaces_vips_configure();
920

    
921
		return true;
922
	}
923

    
924
	/**
925
	 * Wrapper for rc.reboot
926
	 *
927
	 * @return bool
928
	 */
929
	public function reboot() {
930
		$this->auth();
931

    
932
		mwexec_bg("/etc/rc.reboot");
933

    
934
		return true;
935
	}
936
}
937

    
938
// run script until its done and can 'unlock' the xmlrpc.lock, this prevents hanging php-fpm / webgui
939
ignore_user_abort(true);
940
set_time_limit(0);
941

    
942
$xmlrpclockkey = lock('xmlrpc', LOCK_EX);
943

    
944
XML_RPC2_Backend::setBackend('php');
945
$HTTP_RAW_POST_DATA = file_get_contents('php://input');
946

    
947
$options = array(
948
	'prefix' => 'pfsense.',
949
	'encoding' => 'utf-8',
950
	'autoDocument' => false,
951
);
952

    
953
$server = XML_RPC2_Server::create(new pfsense_xmlrpc_server(), $options);
954
$server->handleCall();
955

    
956
unlock($xmlrpclockkey);
957

    
958
?>
(227-227/227)