Project

General

Profile

Download (12.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-2016 Rubicon Communications, LLC (Netgate)
7
 * Copyright (c) 2005 Colin Smith
8
 * All rights reserved.
9
 *
10
 * Licensed under the Apache License, Version 2.0 (the "License");
11
 * you may not use this file except in compliance with the License.
12
 * You may obtain a copy of the License at
13
 *
14
 * http://www.apache.org/licenses/LICENSE-2.0
15
 *
16
 * Unless required by applicable law or agreed to in writing, software
17
 * distributed under the License is distributed on an "AS IS" BASIS,
18
 * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
19
 * See the License for the specific language governing permissions and
20
 * limitations under the License.
21
 */
22

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

    
30
require_once("config.inc");
31
require_once("functions.inc");
32
require_once("auth.inc");
33
require_once("filter.inc");
34
require_once("ipsec.inc");
35
require_once("vpn.inc");
36
require_once("shaper.inc");
37
require_once("XML/RPC2/Server.php");
38

    
39
class pfsense_xmlrpc_server {
40

    
41
	private $loop_detected = false;
42
	private $remote_addr;
43

    
44
	private function auth() {
45
		global $config;
46
		$username = $_SERVER['PHP_AUTH_USER'];
47
		$password = $_SERVER['PHP_AUTH_PW'];
48

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

    
55
			if (authenticate_user($username, $password,
56
			    $authcfg, $attributes) ||
57
			    authenticate_user($username, $password)) {
58
				$login_ok = true;
59
			}
60
		}
61

    
62
		if (!$login_ok) {
63
			log_auth("webConfigurator authentication error for '" .
64
			    $username . "' from " . $this->remote_addr);
65

    
66
			require_once("XML/RPC2/Exception.php");
67
			throw new XML_RPC2_FaultException(gettext(
68
			    'Authentication failed: Invalid username or password'),
69
			    -1);
70
		}
71

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

    
83
			require_once("XML/RPC2/Exception.php");
84
			throw new XML_RPC2_FaultException(gettext(
85
			    'Authentication failed: not enough privileges'),
86
			    -2);
87
		}
88

    
89
		return;
90
	}
91

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

    
104
		return $a1;
105
	}
106

    
107
	public function __construct() {
108
		global $config;
109

    
110
		$this->remote_addr = $_SERVER['REMOTE_ADDR'];
111

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

    
119
	/**
120
	 * Get host version information
121
	 *
122
	 * @return array
123
	 */
124
	public function host_firmware_version($dummy = 1) {
125
		$this->auth();
126
		return host_firmware_version();
127
	}
128

    
129
	/**
130
	 * Executes a PHP block of code
131
	 *
132
	 * @param string $code
133
	 *
134
	 * @return bool
135
	 */
136
	public function exec_php($code) {
137
		$this->auth();
138

    
139
		eval($code);
140
		if ($toreturn) {
141
			return $toreturn;
142
		}
143

    
144
		return true;
145
	}
146

    
147
	/**
148
	 * Executes shell commands
149
	 *
150
	 * @param string $code
151
	 *
152
	 * @return bool
153
	 */
154
	public function exec_shell($code) {
155
		$this->auth();
156

    
157
		mwexec($code);
158
		return true;
159
	}
160

    
161
	/**
162
	 * Backup chosen config sections
163
	 *
164
	 * @param array $section
165
	 *
166
	 * @return array
167
	 */
168
	public function backup_config_section($section) {
169
		$this->auth();
170

    
171
		global $config;
172

    
173
		return array_intersect_key($config, array_flip($section));
174
	}
175

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

    
186
		global $config;
187

    
188
		$old_config = $config;
189
		$old_ipsec_enabled = ipsec_enabled();
190

    
191
		if ($this->loop_detected) {
192
			log_error("Disallowing CARP sync loop");
193
			return true;
194
		}
195

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

    
218
		$syncd_full_sections = array();
219

    
220
		foreach ($sync_full_sections as $section) {
221
			if (!isset($sections[$section])) {
222
				continue;
223
			}
224

    
225
			$config[$section] = $sections[$section];
226
			unset($sections[$section]);
227
			$syncd_full_sections[] = $section;
228
		}
229

    
230
		$vipbackup = array();
231
		$oldvips = array();
232
		if (isset($sections['virtualip']) &&
233
		    is_array($config['virtualip']['vip'])) {
234
			foreach ($config['virtualip']['vip'] as $vip) {
235
				if ($vip['mode'] == "carp") {
236
					$key = $vip['interface'] .
237
					    "_vip" . $vip['vhid'];
238

    
239
					$oldvips[$key]['content'] =
240
					    $vip['password'] .
241
					    $vip['advskew'] .
242
					    $vip['subnet'] .
243
					    $vip['subnet_bits'] .
244
					    $vip['advbase'];
245
					$oldvips[$key]['interface'] =
246
					    $vip['interface'];
247
					$oldvips[$key]['subnet'] =
248
					    $vip['subnet'];
249
				} else if ($vip['mode'] == "ipalias" &&
250
				    (substr($vip['interface'], 0, 4) == '_vip'
251
				    || strstr($vip['interface'], "lo0"))) {
252
					$oldvips[$vip['subnet']]['content'] =
253
					    $vip['interface'] .
254
					    $vip['subnet'] .
255
					    $vip['subnet_bits'];
256
					$oldvips[$vip['subnet']]['interface'] =
257
					    $vip['interface'];
258
					$oldvips[$vip['subnet']]['subnet'] =
259
					    $vip['subnet'];
260
				} else if (($vip['mode'] == "ipalias" ||
261
				    $vip['mode'] == 'proxyarp') &&
262
				    !(substr($vip['interface'], 0, 4) == '_vip')
263
				    || strstr($vip['interface'], "lo0")) {
264
					$vipbackup[] = $vip;
265
				}
266
			}
267
		}
268

    
269
		/* For vip section, first keep items sent from the master */
270
		$config = array_merge_recursive_unique($config, $sections);
271

    
272
		/*
273
		 * Then add ipalias and proxyarp types already defined
274
		 * on the backup
275
		 */
276
		if (is_array($vipbackup) && !empty($vipbackup)) {
277
			if (!is_array($config['virtualip'])) {
278
				$config['virtualip'] = array();
279
			}
280
			if (!is_array($config['virtualip']['vip'])) {
281
				$config['virtualip']['vip'] = array();
282
			}
283
			foreach ($vipbackup as $vip) {
284
				array_unshift($config['virtualip']['vip'], $vip);
285
			}
286
		}
287

    
288
		/* Log what happened */
289
		$mergedkeys = implode(",", array_merge(array_keys($sections),
290
		    $syncd_full_sections));
291
		write_config(sprintf(gettext(
292
		    "Merged in config (%s sections) from XMLRPC client."),
293
		    $mergedkeys));
294

    
295
		/*
296
		 * The real work on handling the vips specially
297
		 * This is a copy of intefaces_vips_configure with addition of
298
		 * not reloading existing/not changed carps
299
		 */
300
		if (isset($sections['virtualip']) &&
301
		    is_array($config['virtualip']) &&
302
		    is_array($config['virtualip']['vip'])) {
303
			$carp_setuped = false;
304
			$anyproxyarp = false;
305

    
306
			foreach ($config['virtualip']['vip'] as $vip) {
307
				$key = "{$vip['interface']}_vip{$vip['vhid']}";
308

    
309
				if ($vip['mode'] == "carp" &&
310
				    isset($oldvips[$key])) {
311
					if ($oldvips[$key]['content'] ==
312
					    $vip['password'] .
313
					    $vip['advskew'] .
314
					    $vip['subnet'] .
315
					    $vip['subnet_bits'] .
316
					    $vip['advbase'] &&
317
					    does_vip_exist($vip)) {
318
						unset($oldvips[$key]);
319
						/*
320
						 * Skip reconfiguring this vips
321
						 * since nothing has changed.
322
						 */
323
						continue;
324
					}
325
				} elseif ($vip['mode'] == "ipalias" &&
326
				    strstr($vip['interface'], "_vip") &&
327
				    isset($oldvips[$vip['subnet']])) {
328

    
329
					$key = $vip['subnet'];
330
					if ($oldvips[$key]['content'] ==
331
					    $vip['interface'] .
332
					    $vip['subnet'] .
333
					    $vip['subnet_bits'] &&
334
					    does_vip_exist($vip)) {
335
						unset($oldvips[$key]);
336
						/*
337
						 * Skip reconfiguring this vips
338
						 * since nothing has changed.
339
						 */
340
						continue;
341
					}
342
					unset($oldvips[$key]);
343
				}
344

    
345
				switch ($vip['mode']) {
346
				case "proxyarp":
347
					$anyproxyarp = true;
348
					break;
349
				case "ipalias":
350
					interface_ipalias_configure($vip);
351
					break;
352
				case "carp":
353
					$carp_setuped = true;
354
					interface_carp_configure($vip);
355
					break;
356
				}
357
			}
358

    
359
			/* Cleanup remaining old carps */
360
			foreach ($oldvips as $oldvipar) {
361
				$oldvipif = get_real_interface(
362
				    $oldvipar['interface']);
363

    
364
				if (empty($oldvipif)) {
365
					continue;
366
				}
367

    
368
				if (is_ipaddrv6($oldvipar['subnet'])) {
369
					 mwexec("/sbin/ifconfig " .
370
					     escapeshellarg($oldvipif) .
371
					     " inet6 " .
372
					     escapeshellarg($oldvipar['subnet']) .
373
					     " delete");
374
				} else {
375
					pfSense_interface_deladdress($oldvipif,
376
					    $oldvipar['subnet']);
377
				}
378
			}
379
			if ($carp_setuped == true) {
380
				interfaces_sync_setup();
381
			}
382
			if ($anyproxyarp == true) {
383
				interface_proxyarp_configure();
384
			}
385
		}
386

    
387
		if ($old_ipsec_enabled !== ipsec_enabled()) {
388
			vpn_ipsec_configure();
389
		}
390

    
391
		unset($old_config);
392

    
393
		return true;
394
	}
395

    
396
	/**
397
	 * Merge items into installedpackages config section
398
	 *
399
	 * @param array $section
400
	 *
401
	 * @return bool
402
	 */
403
	public function merge_installedpackages_section($section) {
404
		$this->auth();
405

    
406
		global $config;
407

    
408
		if ($this->loop_detected) {
409
			log_error("Disallowing CARP sync loop");
410
			return true;
411
		}
412

    
413
		$config['installedpackages'] = array_merge(
414
		    $config['installedpackages'], $section);
415
		$mergedkeys = implode(",", array_keys($section));
416
		write_config(sprintf(gettext(
417
		    "Merged in config (%s sections) from XMLRPC client."),
418
		    $mergedkeys));
419

    
420
		return true;
421
	}
422

    
423
	/**
424
	 * Merge items into config
425
	 *
426
	 * @param array $section
427
	 *
428
	 * @return bool
429
	 */
430
	public function merge_config_section($section) {
431
		$this->auth();
432

    
433
		global $config;
434

    
435
		if ($this->loop_detected) {
436
			log_error("Disallowing CARP sync loop");
437
			return true;
438
		}
439

    
440
		$config_new = $this->array_overlay($config, $section);
441
		$config = $config_new;
442
		$mergedkeys = implode(",", array_keys($section));
443
		write_config(sprintf(gettext(
444
		    "Merged in config (%s sections) from XMLRPC client."),
445
		    $mergedkeys));
446

    
447
		return true;
448
	}
449

    
450
	/**
451
	 * Wrapper for filter_configure()
452
	 *
453
	 * @return bool
454
	 */
455
	public function filter_configure() {
456
		$this->auth();
457

    
458
		global $g, $config;
459

    
460
		filter_configure();
461
		system_routing_configure();
462
		setup_gateways_monitor();
463
		relayd_configure();
464
		require_once("openvpn.inc");
465
		openvpn_resync_all();
466

    
467
		/*
468
		 * The DNS Resolver and the DNS Forwarder may both be active so
469
		 * long as * they are running on different ports.
470
		 * See ticket #5882
471
		 */
472
		if (isset($config['dnsmasq']['enable'])) {
473
			/* Configure dnsmasq but tell it NOT to restart DHCP */
474
			services_dnsmasq_configure(false);
475
		} else {
476
			/* kill any running dnsmasq instance */
477
			if (isvalidpid("{$g['varrun_path']}/dnsmasq.pid")) {
478
				sigkillbypid("{$g['varrun_path']}/dnsmasq.pid",
479
				    "TERM");
480
			}
481
		}
482
		if (isset($config['unbound']['enable'])) {
483
			/* Configure unbound but tell it NOT to restart DHCP */
484
			services_unbound_configure(false);
485
		} else {
486
			/* kill any running Unbound instance */
487
			if (isvalidpid("{$g['varrun_path']}/unbound.pid")) {
488
				sigkillbypid("{$g['varrun_path']}/unbound.pid",
489
				    "TERM");
490
			}
491
		}
492

    
493
		/*
494
		 * Call this separately since the above are manually set to
495
		 * skip the DHCP restart they normally perform.
496
		 * This avoids restarting dhcpd twice as described on
497
		 * ticket #3797
498
		 */
499
		services_dhcpd_configure();
500

    
501
		local_sync_accounts();
502

    
503
		return true;
504
	}
505

    
506
	/**
507
	 * Wrapper for configuring CARP interfaces
508
	 *
509
	 * @return bool
510
	 */
511
	public function interfaces_carp_configure() {
512
		$this->auth();
513

    
514
		if ($this->loop_detected) {
515
			log_error("Disallowing CARP sync loop");
516
			return true;
517
		}
518

    
519
		interfaces_vips_configure();
520

    
521
		return true;
522
	}
523

    
524
	/**
525
	 * Wrapper for rc.reboot
526
	 *
527
	 * @return bool
528
	 */
529
	public function reboot() {
530
		$this->auth();
531

    
532
		mwexec_bg("/etc/rc.reboot");
533

    
534
		return true;
535
	}
536

    
537
	/**
538
	 * Wrapper for get_notices()
539
	 *
540
	 * @param string $category
541
	 *
542
	 * @return bool
543
	 */
544
	public function get_notices($category = 'all') {
545
		$this->auth();
546

    
547
		global $g;
548

    
549
		if (!function_exists("get_notices")) {
550
			require_once("notices.inc");
551
		}
552
		if (!$params) {
553
			$toreturn = get_notices();
554
		} else {
555
			$toreturn = get_notices($params);
556
		}
557

    
558
		return $toreturn;
559
	}
560
}
561

    
562
$xmlrpclockkey = lock('xmlrpc', LOCK_EX);
563

    
564
XML_RPC2_Backend::setBackend('php');
565
$HTTP_RAW_POST_DATA = file_get_contents('php://input');
566

    
567
$options = array(
568
	'prefix' => 'pfsense.',
569
	'encoding' => 'utf-8',
570
	'autoDocument' => false,
571
);
572

    
573
$server = XML_RPC2_Server::create(new pfsense_xmlrpc_server(), $options);
574
$server->handleCall();
575

    
576
unlock($xmlrpclockkey);
577

    
578
?>
(225-225/225)