Project

General

Profile

Download (24.1 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-2020 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("captiveportal.inc");
39
require_once("shaper.inc");
40
require_once("XML/RPC2/Server.php");
41

    
42
class pfsense_xmlrpc_server {
43

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

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

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

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

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

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

    
72
			require_once("XML/RPC2/Exception.php");
73
			throw new XML_RPC2_FaultException(gettext(
74
			    'Authentication failed: Invalid username or password'),
75
			    -1);
76
		}
77

    
78
		$user_entry = getUserEntry($username);
79
		/*
80
		 * admin (uid = 0) is allowed
81
		 * or regular user with necessary privilege
82
		 */
83
		if (isset($user_entry['uid']) && $user_entry['uid'] != '0' &&
84
		    !userHasPrivilege($user_entry, 'system-xmlrpc-ha-sync')) {
85
			log_auth("webConfigurator authentication error for '" .
86
			    $username . "' from " . $this->remote_addr .
87
			    " not enough privileges");
88

    
89
			require_once("XML/RPC2/Exception.php");
90
			throw new XML_RPC2_FaultException(gettext(
91
			    'Authentication failed: not enough privileges'),
92
			    -2);
93
		}
94

    
95
		return;
96
	}
97

    
98
	private function array_overlay($a1, $a2) {
99
		foreach ($a1 as $k => $v) {
100
			if (!array_key_exists($k, $a2)) {
101
				continue;
102
			}
103
			if (is_array($v) && is_array($a2[$k])) {
104
				$a1[$k] = $this->array_overlay($v, $a2[$k]);
105
			} else {
106
				$a1[$k] = $a2[$k];
107
			}
108
		}
109

    
110
		return $a1;
111
	}
112

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

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

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

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

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

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

    
150
		return true;
151
	}
152

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

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

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

    
177
		global $config;
178

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

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

    
192
		global $config, $cpzone, $cpzoneid;
193

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

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

    
202
		/*
203
		 * Some sections should just be copied and not merged or we end
204
		 * up unable to sync the deletion of the last item in a section
205
		 */
206
		$sync_full_sections = array(
207
			'aliases',
208
			'ca',
209
			'cert',
210
			'crl',
211
			'dhcpd',
212
			'dhcpv6',
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
		if (isset($sections['virtualip']) &&
528
		    is_array($config['virtualip']) &&
529
		    is_array($config['virtualip']['vip'])) {
530
			$carp_setuped = false;
531
			$anyproxyarp = false;
532

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

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

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

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

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

    
592
				if (empty($oldvipif)) {
593
					continue;
594
				}
595

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

    
615
		if ($old_ipsec_enabled !== ipsec_enabled()) {
616
			ipsec_configure();
617
		}
618

    
619
		unset($old_config);
620

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

    
624
		return true;
625
	}
626

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

    
637
		global $config;
638

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

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

    
651
		return true;
652
	}
653

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

    
664
		global $config;
665

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

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

    
678
		return true;
679
	}
680

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

    
689
		filter_configure();
690
		system_routing_configure();
691
		setup_gateways_monitor();
692
		require_once("openvpn.inc");
693
		openvpn_resync_all();
694

    
695
		/*
696
		 * The DNS Resolver and the DNS Forwarder may both be active so
697
		 * long as * they are running on different ports.
698
		 * See ticket #5882
699
		 */
700
		if (isset($config['dnsmasq']['enable'])) {
701
			/* Configure dnsmasq but tell it NOT to restart DHCP */
702
			services_dnsmasq_configure(false);
703
		} else {
704
			/* kill any running dnsmasq instance */
705
			if (isvalidpid("{$g['varrun_path']}/dnsmasq.pid")) {
706
				sigkillbypid("{$g['varrun_path']}/dnsmasq.pid",
707
				    "TERM");
708
			}
709
		}
710
		if (isset($config['unbound']['enable'])) {
711
			/* Configure unbound but tell it NOT to restart DHCP */
712
			services_unbound_configure(false);
713
		} else {
714
			/* kill any running Unbound instance */
715
			if (isvalidpid("{$g['varrun_path']}/unbound.pid")) {
716
				sigkillbypid("{$g['varrun_path']}/unbound.pid",
717
				    "TERM");
718
			}
719
		}
720

    
721
		/*
722
		 * Call this separately since the above are manually set to
723
		 * skip the DHCP restart they normally perform.
724
		 * This avoids restarting dhcpd twice as described on
725
		 * ticket #3797
726
		 */
727
		services_dhcpd_configure();
728

    
729
		if ($reset_accounts) {
730
			local_reset_accounts();
731
		}
732

    
733
		captiveportal_configure();
734
		voucher_configure();
735

    
736
		return true;
737
	}
738

    
739
	/**
740
	 * Wrapper for captiveportal connected users and
741
	 * active/expired vouchers synchronization
742
	 *
743
	 * @param array $arguments
744
	 *
745
	 * @return array
746
	 */
747
	public function captive_portal_sync($arguments) {
748
		$this->auth();
749
		// Note : no protection against CARP loop is done here, and this is in purpose.
750
		// This function is used for bi-directionnal sync, which is precisely what CARP loop protection is supposed to prevent.
751
		// CARP loop has to be managed within functions using captive_portal_sync()
752
		global $g, $config, $cpzone;
753

    
754
		if (empty($arguments['op']) || empty($arguments['zone']) || empty($config['captiveportal'][$arguments['zone']])) {
755
			return false;
756
		}
757
		$cpzone = $arguments['zone'];
758

    
759
		if ($arguments['op'] === 'get_databases') {
760
			$active_vouchers = array();
761
			$expired_vouchers = array();
762
			$usedmacs = '';
763

    
764
			if (is_array($config['voucher'][$cpzone]['roll'])) {
765
				foreach($config['voucher'][$cpzone]['roll'] as $id => $roll) {
766
					$expired_vouchers[$roll['number']] = base64_encode(voucher_read_used_db($roll['number']));
767
					$active_vouchers[$roll['number']] = voucher_read_active_db($roll['number']);
768
				}
769
			}
770
			if (!empty($config['captiveportal'][$cpzone]['freelogins_count']) &&
771
			    !empty($config['captiveportal'][$cpzone]['freelogins_resettimeout'])) {
772
				$usedmacs = captiveportal_read_usedmacs_db();
773
			}
774
			// base64 is here for safety reasons, as we don't fully control
775
			// the content of these arrays.
776
			$returndata = array('connected_users' => base64_encode(serialize(captiveportal_read_db())),
777
			'active_vouchers' => base64_encode(serialize($active_vouchers)),
778
			'expired_vouchers' => base64_encode(serialize($expired_vouchers)),
779
			'usedmacs' => base64_encode(serialize($usedmacs)));
780

    
781
			return $returndata;
782
		} elseif ($arguments['op'] === 'connect_user') {
783
			$user = unserialize(base64_decode($arguments['user']));
784
			$user['attributes']['allow_time'] = $user['allow_time'];
785

    
786
			// pipeno might be different between primary and secondary
787
			$pipeno = captiveportal_get_next_dn_ruleno('auth');
788
			return portal_allow($user['clientip'], $user['clientmac'], $user['username'], $user['password'], null,
789
			    $user['attributes'], $pipeno, $user['authmethod'], $user['context'], $user['sessionid']);
790
		} elseif ($arguments['op'] === 'disconnect_user') {
791
			$session = unserialize(base64_decode($arguments['session']));
792
			/* read database again, as pipeno might be different between primary & secondary */
793
			$sessionid = SQLite3::escapeString($session['sessionid']);
794
			$local_dbentry = captiveportal_read_db("WHERE sessionid = '{$sessionid}'");
795

    
796
			if (!empty($local_dbentry) && count($local_dbentry) == 1) {
797
				return captiveportal_disconnect($local_dbentry[0], $session['term_cause'], $session['stop_time'], true);
798
			} else {
799
				return false;
800
			}
801
		} elseif ($arguments['op'] === 'remove_entries') {
802
			$entries = unserialize(base64_decode($arguments['entries']));
803

    
804
			return captiveportal_remove_entries($entries, true);
805
		} elseif ($arguments['op'] === 'disconnect_all') {
806
			$arguments = unserialize(base64_decode($arguments['arguments']));
807

    
808
			return captiveportal_disconnect_all($arguments['term_cause'], $arguments['logout_reason'], true);
809
		} elseif ($arguments['op'] === 'write_vouchers') {
810
			$arguments = unserialize(base64_decode($arguments['arguments']));
811

    
812
			if (is_array($arguments['active_and_used_vouchers_bitmasks'])) {
813
				foreach ($arguments['active_and_used_vouchers_bitmasks'] as $roll => $used) {
814
					if (is_array($used)) {
815
						foreach ($used as $u) {
816
							voucher_write_used_db($roll, base64_encode($u));
817
						}
818
					} else {
819
						voucher_write_used_db($roll, base64_encode($used));
820
					}
821
				}
822
			}
823
			foreach ($arguments['active_vouchers'] as $roll => $active_vouchers) {
824
				voucher_write_active_db($roll, $active_vouchers);
825
			}
826
			return true;
827
		} elseif ($arguments['op'] === 'write_usedmacs') {
828
			$arguments = unserialize(base64_decode($arguments['arguments']));
829

    
830
			captiveportal_write_usedmacs_db($arguments['usedmacs']); 
831
			return true;
832
		}
833
	}
834

    
835
	/**
836
	 * Wrapper for configuring CARP interfaces
837
	 *
838
	 * @return bool
839
	 */
840
	public function interfaces_carp_configure() {
841
		$this->auth();
842

    
843
		if ($this->loop_detected) {
844
			log_error("Disallowing CARP sync loop");
845
			return true;
846
		}
847

    
848
		interfaces_vips_configure();
849

    
850
		return true;
851
	}
852

    
853
	/**
854
	 * Wrapper for rc.reboot
855
	 *
856
	 * @return bool
857
	 */
858
	public function reboot() {
859
		$this->auth();
860

    
861
		mwexec_bg("/etc/rc.reboot");
862

    
863
		return true;
864
	}
865
}
866

    
867
// run script until its done and can 'unlock' the xmlrpc.lock, this prevents hanging php-fpm / webgui
868
ignore_user_abort(true);
869
set_time_limit(0);
870

    
871
$xmlrpclockkey = lock('xmlrpc', LOCK_EX);
872

    
873
XML_RPC2_Backend::setBackend('php');
874
$HTTP_RAW_POST_DATA = file_get_contents('php://input');
875

    
876
$options = array(
877
	'prefix' => 'pfsense.',
878
	'encoding' => 'utf-8',
879
	'autoDocument' => false,
880
);
881

    
882
$server = XML_RPC2_Server::create(new pfsense_xmlrpc_server(), $options);
883
$server->handleCall();
884

    
885
unlock($xmlrpclockkey);
886

    
887
?>
(230-230/230)