Project

General

Profile

Download (23.4 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
				}
249
			}
250
		}
251

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

    
265
				foreach ($sections['system']['group'] as $group) {
266
					$idx = array_search($group['name'],
267
					    array_column($local_groups, 'name'));
268

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

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

    
302
				foreach ($sections['system']['user'] as $user) {
303
					$idx = array_search($user['name'],
304
					    array_column($local_users, 'name'));
305

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

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

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

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

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

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

    
429
		/* For vip section, first keep items sent from the master */
430
		$config = array_merge_recursive_unique($config, $sections);
431

    
432

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

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

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

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

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

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

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

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

    
530
			foreach ($config['virtualip']['vip'] as $vip) {
531
				$key = "{$vip['interface']}_vip{$vip['vhid']}";
532

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

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

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

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

    
589
				if (empty($oldvipif)) {
590
					continue;
591
				}
592

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

    
612
		if ($old_ipsec_enabled !== ipsec_enabled()) {
613
			ipsec_configure();
614
		}
615

    
616
		unset($old_config);
617

    
618
		local_sync_accounts($u2add, $u2del, $g2add, $g2del);
619
		$this->filter_configure(false);
620

    
621
		return true;
622
	}
623

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

    
634
		global $config;
635

    
636
		if ($this->loop_detected) {
637
			log_error("Disallowing CARP sync loop");
638
			return true;
639
		}
640

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

    
648
		return true;
649
	}
650

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

    
661
		global $config;
662

    
663
		if ($this->loop_detected) {
664
			log_error("Disallowing CARP sync loop");
665
			return true;
666
		}
667

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

    
675
		return true;
676
	}
677

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

    
686
		filter_configure();
687
		system_routing_configure();
688
		setup_gateways_monitor();
689
		require_once("openvpn.inc");
690
		openvpn_resync_all();
691

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

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

    
726
		if ($reset_accounts) {
727
			local_reset_accounts();
728
		}
729

    
730
		captiveportal_configure();
731
		voucher_configure();
732

    
733
		return true;
734
	}
735

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

    
751
		if (empty($arguments['op']) || empty($arguments['zone']) || empty($config['captiveportal'][$arguments['zone']])) {
752
			return false;
753
		}
754
		$cpzone = $arguments['zone'];
755

    
756
		if ($arguments['op'] === 'get_databases') {
757
			$active_vouchers = array();
758
			$expired_vouchers = array();
759

    
760
			if (is_array($config['voucher'][$cpzone]['roll'])) {
761
				foreach($config['voucher'][$cpzone]['roll'] as $id => $roll) {
762
					$expired_vouchers[$roll['number']] = base64_encode(voucher_read_used_db($roll['number']));
763
					$active_vouchers[$roll['number']] = voucher_read_active_db($roll['number']);
764
				}
765
			}
766
			// base64 is here for safety reasons, as we don't fully control
767
			// the content of these arrays.
768
			$returndata = array('connected_users' => base64_encode(serialize(captiveportal_read_db())),
769
			'active_vouchers' => base64_encode(serialize($active_vouchers)),
770
			'expired_vouchers' => base64_encode(serialize($expired_vouchers)));
771

    
772
			return $returndata;
773
		} elseif ($arguments['op'] === 'connect_user') {
774
			$user = unserialize(base64_decode($arguments['user']));
775
			$user['attributes']['allow_time'] = $user['allow_time'];
776

    
777
			// pipeno might be different between primary and secondary
778
			$pipeno = captiveportal_get_next_dn_ruleno('auth');
779
			return portal_allow($user['clientip'], $user['clientmac'], $user['username'], $user['password'], null,
780
			    $user['attributes'], $pipeno, $user['authmethod'], $user['context'], $user['sessionid']);
781
		} elseif ($arguments['op'] === 'disconnect_user') {
782
			$session = unserialize(base64_decode($arguments['session']));
783
			/* read database again, as pipeno might be different between primary & secondary */
784
			$sessionid = SQLite3::escapeString($session['sessionid']);
785
			$local_dbentry = captiveportal_read_db("WHERE sessionid = '{$sessionid}'");
786

    
787
			if (!empty($local_dbentry) && count($local_dbentry) == 1) {
788
				return captiveportal_disconnect($local_dbentry[0], $session['term_cause'], $session['stop_time'], true);
789
			} else {
790
				return false;
791
			}
792
		} elseif ($arguments['op'] === 'remove_entries') {
793
			$entries = unserialize(base64_decode($arguments['entries']));
794

    
795
			return captiveportal_remove_entries($entries, true);
796
		} elseif ($arguments['op'] === 'disconnect_all') {
797
			$arguments = unserialize(base64_decode($arguments['arguments']));
798

    
799
			return captiveportal_disconnect_all($arguments['term_cause'], $arguments['logout_reason'], true);
800
		} elseif ($arguments['op'] === 'write_vouchers') {
801
			$arguments = unserialize(base64_decode($arguments['arguments']));
802

    
803
			if (is_array($arguments['active_and_used_vouchers_bitmasks'])) {
804
				foreach ($arguments['active_and_used_vouchers_bitmasks'] as $roll => $used) {
805
					if (is_array($used)) {
806
						foreach ($used as $u) {
807
							voucher_write_used_db($roll, base64_encode($u));
808
						}
809
					} else {
810
						voucher_write_used_db($roll, base64_encode($used));
811
					}
812
				}
813
			}
814
			foreach ($arguments['active_vouchers'] as $roll => $active_vouchers) {
815
				voucher_write_active_db($roll, $active_vouchers);
816
			}
817
			return true;
818
		}
819
	}
820

    
821
	/**
822
	 * Wrapper for configuring CARP interfaces
823
	 *
824
	 * @return bool
825
	 */
826
	public function interfaces_carp_configure() {
827
		$this->auth();
828

    
829
		if ($this->loop_detected) {
830
			log_error("Disallowing CARP sync loop");
831
			return true;
832
		}
833

    
834
		interfaces_vips_configure();
835

    
836
		return true;
837
	}
838

    
839
	/**
840
	 * Wrapper for rc.reboot
841
	 *
842
	 * @return bool
843
	 */
844
	public function reboot() {
845
		$this->auth();
846

    
847
		mwexec_bg("/etc/rc.reboot");
848

    
849
		return true;
850
	}
851
}
852

    
853
// run script until its done and can 'unlock' the xmlrpc.lock, this prevents hanging php-fpm / webgui
854
ignore_user_abort(true);
855
set_time_limit(0);
856

    
857
$xmlrpclockkey = lock('xmlrpc', LOCK_EX);
858

    
859
XML_RPC2_Backend::setBackend('php');
860
$HTTP_RAW_POST_DATA = file_get_contents('php://input');
861

    
862
$options = array(
863
	'prefix' => 'pfsense.',
864
	'encoding' => 'utf-8',
865
	'autoDocument' => false,
866
);
867

    
868
$server = XML_RPC2_Server::create(new pfsense_xmlrpc_server(), $options);
869
$server->handleCall();
870

    
871
unlock($xmlrpclockkey);
872

    
873
?>
(228-228/228)