Project

General

Profile

Download (23.6 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;
49
		$username = $_SERVER['PHP_AUTH_USER'];
50
		$password = $_SERVER['PHP_AUTH_PW'];
51

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

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

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

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

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

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

    
93
		return;
94
	}
95

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

    
108
		return $a1;
109
	}
110

    
111
	public function __construct() {
112
		global $config;
113

    
114
		$this->remote_addr = $_SERVER['REMOTE_ADDR'];
115

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

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

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

    
143
		eval($code);
144
		if ($toreturn) {
145
			return $toreturn;
146
		}
147

    
148
		return true;
149
	}
150

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

    
161
		mwexec($code);
162
		return true;
163
	}
164

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

    
175
		global $config;
176

    
177
		return array_intersect_key($config, array_flip($section));
178
	}
179

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

    
190
		global $config, $cpzone, $cpzoneid;
191

    
192
		$old_config = $config;
193
		$old_ipsec_enabled = ipsec_enabled();
194

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

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

    
223
		$syncd_full_sections = array();
224

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

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

    
235
		/* Create a list of CP zones to be deleted locally */
236
		$cp_to_del = array();
237
		if (is_array($config['captiveportal'])) {
238
			if (is_array($sections['captiveportal'])) {
239
				$remote_cp = $sections['captiveportal'];
240
			} else {
241
				$remote_cp = array();
242
			}
243
			foreach ($config['captiveportal'] as $zone => $item) {
244
				if (!isset($remote_cp[$zone])) {
245
					$cp_to_del[] = $zone;
246
				}
247
			}
248
			unset($remote_cp);
249
		}
250

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

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

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

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

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

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

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

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

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

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

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

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

    
431
		/* Remove local CP zones removed remote */
432
		foreach ($cp_to_del as $zone) {
433
			$cpzone = $zone;
434
			$cpzoneid = $config['captiveportal'][$cpzone]['zoneid'];
435
			unset($config['captiveportal'][$cpzone]['enable']);
436
			captiveportal_configure_zone(
437
			    $config['captiveportal'][$cpzone]);
438
			unset($config['captiveportal'][$cpzone]);
439
			if (isset($config['voucher'][$cpzone])) {
440
				unset($config['voucher'][$cpzone]);
441
			}
442
		}
443

    
444
		/* Remove locally items removed remote */
445
		foreach ($voucher as $zone => $item) {
446
			/* No rolls on master, delete local ones */
447
			if (!is_array($item['roll'])) {
448
				unset($config['voucher'][$zone]['roll']);
449
			}
450
		}
451

    
452
		$l_rolls = array();
453
		if (is_array($config['voucher'])) {
454
			foreach ($config['voucher'] as $zone => $item) {
455
				if (!is_array($item['roll'])) {
456
					continue;
457
				}
458
				foreach ($item['roll'] as $idx => $roll) {
459
					/* Make it easy to find roll by # */
460
					$l_rolls[$zone][$roll['number']] = $idx;
461
				}
462
			}
463
		}
464

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

    
488
				if (isset($roll['lastsync']) &&
489
				    $roll['lastsync'] != $l_roll['lastsync']) {
490
					$l_vouchers['roll'][$l_roll_idx] =
491
					    $roll;
492
					unset($l_rolls[$zone][$roll['number']]);
493
				}
494
			}
495
		}
496

    
497
		/*
498
		 * At this point $l_rolls contains only items that are not
499
		 * present on primary node. They must be removed
500
		 */
501
		foreach ($l_rolls as $zone => $item) {
502
			foreach ($item as $number => $idx) {
503
				unset($config['voucher'][$zone][$idx]);
504
			}
505
		}
506

    
507
		/*
508
		 * Then add ipalias and proxyarp types already defined
509
		 * on the backup
510
		 */
511
		if (is_array($vipbackup) && !empty($vipbackup)) {
512
			if (!is_array($config['virtualip'])) {
513
				$config['virtualip'] = array();
514
			}
515
			if (!is_array($config['virtualip']['vip'])) {
516
				$config['virtualip']['vip'] = array();
517
			}
518
			foreach ($vipbackup as $vip) {
519
				array_unshift($config['virtualip']['vip'], $vip);
520
			}
521
		}
522

    
523
		/* Log what happened */
524
		$mergedkeys = implode(", ", array_merge(array_keys($sections),
525
		    $syncd_full_sections));
526
		write_config(sprintf(gettext(
527
		    "Merged in config (%s sections) from XMLRPC client."),
528
		    $mergedkeys));
529

    
530
		/*
531
		 * The real work on handling the vips specially
532
		 * This is a copy of intefaces_vips_configure with addition of
533
		 * not reloading existing/not changed carps
534
		 */
535
		if (isset($sections['virtualip']) &&
536
		    is_array($config['virtualip']) &&
537
		    is_array($config['virtualip']['vip'])) {
538
			$carp_setuped = false;
539
			$anyproxyarp = false;
540

    
541
			foreach ($config['virtualip']['vip'] as $vip) {
542
				$key = "{$vip['interface']}_vip{$vip['vhid']}";
543

    
544
				if ($vip['mode'] == "carp" &&
545
				    isset($oldvips[$key])) {
546
					if ($oldvips[$key]['content'] ==
547
					    $vip['password'] .
548
					    $vip['advskew'] .
549
					    $vip['subnet'] .
550
					    $vip['subnet_bits'] .
551
					    $vip['advbase'] &&
552
					    does_vip_exist($vip)) {
553
						unset($oldvips[$key]);
554
						/*
555
						 * Skip reconfiguring this vips
556
						 * since nothing has changed.
557
						 */
558
						continue;
559
					}
560

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

    
581
				switch ($vip['mode']) {
582
				case "proxyarp":
583
					$anyproxyarp = true;
584
					break;
585
				case "ipalias":
586
					interface_ipalias_configure($vip);
587
					break;
588
				case "carp":
589
					$carp_setuped = true;
590
					interface_carp_configure($vip);
591
					break;
592
				}
593
			}
594

    
595
			/* Cleanup remaining old carps */
596
			foreach ($oldvips as $oldvipar) {
597
				$oldvipif = get_real_interface(
598
				    $oldvipar['interface']);
599

    
600
				if (empty($oldvipif)) {
601
					continue;
602
				}
603

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

    
623
		if ($old_ipsec_enabled !== ipsec_enabled()) {
624
			ipsec_configure();
625
		}
626

    
627
		unset($old_config);
628

    
629
		local_sync_accounts($u2add, $u2del, $g2add, $g2del);
630
		$this->filter_configure(false);
631

    
632
		return true;
633
	}
634

    
635
	/**
636
	 * Merge items into installedpackages config section
637
	 *
638
	 * @param array $section
639
	 *
640
	 * @return bool
641
	 */
642
	public function merge_installedpackages_section($section) {
643
		$this->auth();
644

    
645
		global $config;
646

    
647
		if ($this->loop_detected) {
648
			log_error("Disallowing CARP sync loop");
649
			return true;
650
		}
651

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

    
659
		return true;
660
	}
661

    
662
	/**
663
	 * Merge items into config
664
	 *
665
	 * @param array $section
666
	 *
667
	 * @return bool
668
	 */
669
	public function merge_config_section($section) {
670
		$this->auth();
671

    
672
		global $config;
673

    
674
		if ($this->loop_detected) {
675
			log_error("Disallowing CARP sync loop");
676
			return true;
677
		}
678

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

    
686
		return true;
687
	}
688

    
689
	/**
690
	 * Wrapper for filter_configure()
691
	 *
692
	 * @return bool
693
	 */
694
	private function filter_configure($reset_accounts = true) {
695
		global $g, $config;
696

    
697
		filter_configure();
698
		system_routing_configure();
699
		setup_gateways_monitor();
700
		require_once("openvpn.inc");
701
		openvpn_resync_all();
702

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

    
729
		/*
730
		 * Call this separately since the above are manually set to
731
		 * skip the DHCP restart they normally perform.
732
		 * This avoids restarting dhcpd twice as described on
733
		 * ticket #3797
734
		 */
735
		services_dhcpd_configure();
736

    
737
		if ($reset_accounts) {
738
			local_reset_accounts();
739
		}
740

    
741
		captiveportal_configure();
742
		voucher_configure();
743

    
744
		return true;
745
	}
746

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

    
762
		if (empty($arguments['op']) || empty($arguments['zone']) || empty($config['captiveportal'][$arguments['zone']])) {
763
			return false;
764
		}
765
		$cpzone = $arguments['zone'];
766

    
767
		if ($arguments['op'] === 'get_databases') {
768
			$active_vouchers = array();
769
			$expired_vouchers = array();
770

    
771
			if (is_array($config['voucher'][$cpzone]['roll'])) {
772
				foreach($config['voucher'][$cpzone]['roll'] as $id => $roll) {
773
					$expired_vouchers[$roll['number']] = base64_encode(voucher_read_used_db($roll['number']));
774
					$active_vouchers[$roll['number']] = voucher_read_active_db($roll['number']);
775
				}
776
			}
777
			// base64 is here for safety reasons, as we don't fully control
778
			// the content of these arrays.
779
			$returndata = array('connected_users' => base64_encode(serialize(captiveportal_read_db())),
780
			'active_vouchers' => base64_encode(serialize($active_vouchers)),
781
			'expired_vouchers' => base64_encode(serialize($expired_vouchers)));
782

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

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

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

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

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

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

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

    
840
		if ($this->loop_detected) {
841
			log_error("Disallowing CARP sync loop");
842
			return true;
843
		}
844

    
845
		interfaces_vips_configure();
846

    
847
		return true;
848
	}
849

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

    
858
		mwexec_bg("/etc/rc.reboot");
859

    
860
		return true;
861
	}
862
}
863

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

    
868
$xmlrpclockkey = lock('xmlrpc', LOCK_EX);
869

    
870
XML_RPC2_Backend::setBackend('php');
871
$HTTP_RAW_POST_DATA = file_get_contents('php://input');
872

    
873
$options = array(
874
	'prefix' => 'pfsense.',
875
	'encoding' => 'utf-8',
876
	'autoDocument' => false,
877
);
878

    
879
$server = XML_RPC2_Server::create(new pfsense_xmlrpc_server(), $options);
880
$server->handleCall();
881

    
882
unlock($xmlrpclockkey);
883

    
884
?>
(228-228/228)