Project

General

Profile

Download (13.7 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($username, $password) {
45
		global $config;
46

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

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

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

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

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

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

    
87
		return;
88
	}
89

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

    
102
		return $a1;
103
	}
104

    
105
	public function __construct() {
106
		global $config;
107

    
108
		$this->remote_addr = $_SERVER['REMOTE_ADDR'];
109

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

    
117
	/**
118
	 * Get host version information
119
	 *
120
	 * @param string $username
121
	 * @param string $password
122
	 *
123
	 * @return array
124
	 */
125
	public function host_firmware_version($username, $password) {
126
		$this->auth($username, $password);
127

    
128
		return host_firmware_version();
129
	}
130

    
131
	/**
132
	 * Executes a PHP block of code
133
	 *
134
	 * @param string $username
135
	 * @param string $password
136
	 * @param string $code
137
	 *
138
	 * @return bool
139
	 */
140
	public function exec_php($username, $password, $code) {
141
		$this->auth($username, $password);
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 $username
155
	 * @param string $password
156
	 * @param string $code
157
	 *
158
	 * @return bool
159
	 */
160
	public function exec_shell($username, $password, $code) {
161
		$this->auth($username, $password);
162

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

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

    
179
		global $config;
180

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

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

    
196
		global $config;
197

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

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

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

    
228
		$syncd_full_sections = array();
229

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

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

    
240
		$vipbackup = array();
241
		$oldvips = array();
242
		if (isset($sections['virtualip']) &&
243
		    is_array($config['virtualip']['vip'])) {
244
			foreach ($config['virtualip']['vip'] as $vip) {
245
				if ($vip['mode'] == "carp") {
246
					$key = $vip['interface'] .
247
					    "_vip" . $vip['vhid'];
248

    
249
					$oldvips[$key]['content'] =
250
					    $vip['password'] .
251
					    $vip['advskew'] .
252
					    $vip['subnet'] .
253
					    $vip['subnet_bits'] .
254
					    $vip['advbase'];
255
					$oldvips[$key]['interface'] =
256
					    $vip['interface'];
257
					$oldvips[$key]['subnet'] =
258
					    $vip['subnet'];
259
				} else if ($vip['mode'] == "ipalias" &&
260
				    (substr($vip['interface'], 0, 4) == '_vip'
261
				    || strstr($vip['interface'], "lo0"))) {
262
					$oldvips[$vip['subnet']]['content'] =
263
					    $vip['interface'] .
264
					    $vip['subnet'] .
265
					    $vip['subnet_bits'];
266
					$oldvips[$vip['subnet']]['interface'] =
267
					    $vip['interface'];
268
					$oldvips[$vip['subnet']]['subnet'] =
269
					    $vip['subnet'];
270
				} else if (($vip['mode'] == "ipalias" ||
271
				    $vip['mode'] == 'proxyarp') &&
272
				    !(substr($vip['interface'], 0, 4) == '_vip')
273
				    || strstr($vip['interface'], "lo0")) {
274
					$vipbackup[] = $vip;
275
				}
276
			}
277
		}
278

    
279
		/* For vip section, first keep items sent from the master */
280
		$config = array_merge_recursive_unique($config, $sections);
281

    
282
		/*
283
		 * Then add ipalias and proxyarp types already defined
284
		 * on the backup
285
		 */
286
		if (is_array($vipbackup) && !empty($vipbackup)) {
287
			if (!is_array($config['virtualip'])) {
288
				$config['virtualip'] = array();
289
			}
290
			if (!is_array($config['virtualip']['vip'])) {
291
				$config['virtualip']['vip'] = array();
292
			}
293
			foreach ($vipbackup as $vip) {
294
				array_unshift($config['virtualip']['vip'], $vip);
295
			}
296
		}
297

    
298
		/* Log what happened */
299
		$mergedkeys = implode(",", array_merge(array_keys($sections),
300
		    $syncd_full_sections));
301
		write_config(sprintf(gettext(
302
		    "Merged in config (%s sections) from XMLRPC client."),
303
		    $mergedkeys));
304

    
305
		/*
306
		 * The real work on handling the vips specially
307
		 * This is a copy of intefaces_vips_configure with addition of
308
		 * not reloading existing/not changed carps
309
		 */
310
		if (isset($sections['virtualip']) &&
311
		    is_array($config['virtualip']) &&
312
		    is_array($config['virtualip']['vip'])) {
313
			$carp_setuped = false;
314
			$anyproxyarp = false;
315

    
316
			foreach ($config['virtualip']['vip'] as $vip) {
317
				$key = "{$vip['interface']}_vip{$vip['vhid']}";
318

    
319
				if ($vip['mode'] == "carp" &&
320
				    isset($oldvips[$key])) {
321
					if ($oldvips[$key]['content'] ==
322
					    $vip['password'] .
323
					    $vip['advskew'] .
324
					    $vip['subnet'] .
325
					    $vip['subnet_bits'] .
326
					    $vip['advbase'] &&
327
					    does_vip_exist($vip)) {
328
						unset($oldvips[$key]);
329
						/*
330
						 * Skip reconfiguring this vips
331
						 * since nothing has changed.
332
						 */
333
						continue;
334
					}
335
				} elseif ($vip['mode'] == "ipalias" &&
336
				    strstr($vip['interface'], "_vip") &&
337
				    isset($oldvips[$vip['subnet']])) {
338

    
339
					$key = $vip['subnet'];
340
					if ($oldvips[$key]['content'] ==
341
					    $vip['interface'] .
342
					    $vip['subnet'] .
343
					    $vip['subnet_bits'] &&
344
					    does_vip_exist($vip)) {
345
						unset($oldvips[$key]);
346
						/*
347
						 * Skip reconfiguring this vips
348
						 * since nothing has changed.
349
						 */
350
						continue;
351
					}
352
					unset($oldvips[$key]);
353
				}
354

    
355
				switch ($vip['mode']) {
356
				case "proxyarp":
357
					$anyproxyarp = true;
358
					break;
359
				case "ipalias":
360
					interface_ipalias_configure($vip);
361
					break;
362
				case "carp":
363
					$carp_setuped = true;
364
					interface_carp_configure($vip);
365
					break;
366
				}
367
			}
368

    
369
			/* Cleanup remaining old carps */
370
			foreach ($oldvips as $oldvipar) {
371
				$oldvipif = get_real_interface(
372
				    $oldvipar['interface']);
373

    
374
				if (empty($oldvipif)) {
375
					continue;
376
				}
377

    
378
				if (is_ipaddrv6($oldvipar['subnet'])) {
379
					 mwexec("/sbin/ifconfig " .
380
					     escapeshellarg($oldvipif) .
381
					     " inet6 " .
382
					     escapeshellarg($oldvipar['subnet']) .
383
					     " delete");
384
				} else {
385
					pfSense_interface_deladdress($oldvipif,
386
					    $oldvipar['subnet']);
387
				}
388
			}
389
			if ($carp_setuped == true) {
390
				interfaces_sync_setup();
391
			}
392
			if ($anyproxyarp == true) {
393
				interface_proxyarp_configure();
394
			}
395
		}
396

    
397
		if ($old_ipsec_enabled !== ipsec_enabled()) {
398
			vpn_ipsec_configure();
399
		}
400

    
401
		unset($old_config);
402

    
403
		return true;
404
	}
405

    
406
	/**
407
	 * Merge items into installedpackages config section
408
	 *
409
	 * @param string $username
410
	 * @param string $password
411
	 * @param array $section
412
	 *
413
	 * @return bool
414
	 */
415
	public function merge_installedpackages_section($username, $password, $section) {
416
		$this->auth($username, $password);
417

    
418
		global $config;
419

    
420
		if ($this->loop_detected) {
421
			log_error("Disallowing CARP sync loop");
422
			return true;
423
		}
424

    
425
		$config['installedpackages'] = array_merge(
426
		    $config['installedpackages'], $section);
427
		$mergedkeys = implode(",", array_keys($section));
428
		write_config(sprintf(gettext(
429
		    "Merged in config (%s sections) from XMLRPC client."),
430
		    $mergedkeys));
431

    
432
		return true;
433
	}
434

    
435
	/**
436
	 * Merge items into config
437
	 *
438
	 * @param string $username
439
	 * @param string $password
440
	 * @param array $section
441
	 *
442
	 * @return bool
443
	 */
444
	public function merge_config_section($username, $password, $section) {
445
		$this->auth($username, $password);
446

    
447
		global $config;
448

    
449
		if ($this->loop_detected) {
450
			log_error("Disallowing CARP sync loop");
451
			return true;
452
		}
453

    
454
		$config_new = $this->array_overlay($config, $section);
455
		$config = $config_new;
456
		$mergedkeys = implode(",", array_keys($section));
457
		write_config(sprintf(gettext(
458
		    "Merged in config (%s sections) from XMLRPC client."),
459
		    $mergedkeys));
460

    
461
		return true;
462
	}
463

    
464
	/**
465
	 * Wrapper for filter_configure()
466
	 *
467
	 * @param string $username
468
	 * @param string $password
469
	 *
470
	 * @return bool
471
	 */
472
	public function filter_configure($username, $password) {
473
		$this->auth($username, $password);
474

    
475
		global $g, $config;
476

    
477
		filter_configure();
478
		system_routing_configure();
479
		setup_gateways_monitor();
480
		relayd_configure();
481
		require_once("openvpn.inc");
482
		openvpn_resync_all();
483

    
484
		/*
485
		 * The DNS Resolver and the DNS Forwarder may both be active so
486
		 * long as * they are running on different ports.
487
		 * See ticket #5882
488
		 */
489
		if (isset($config['dnsmasq']['enable'])) {
490
			/* Configure dnsmasq but tell it NOT to restart DHCP */
491
			services_dnsmasq_configure(false);
492
		} else {
493
			/* kill any running dnsmasq instance */
494
			if (isvalidpid("{$g['varrun_path']}/dnsmasq.pid")) {
495
				sigkillbypid("{$g['varrun_path']}/dnsmasq.pid",
496
				    "TERM");
497
			}
498
		}
499
		if (isset($config['unbound']['enable'])) {
500
			/* Configure unbound but tell it NOT to restart DHCP */
501
			services_unbound_configure(false);
502
		} else {
503
			/* kill any running Unbound instance */
504
			if (isvalidpid("{$g['varrun_path']}/unbound.pid")) {
505
				sigkillbypid("{$g['varrun_path']}/unbound.pid",
506
				    "TERM");
507
			}
508
		}
509

    
510
		/*
511
		 * Call this separately since the above are manually set to
512
		 * skip the DHCP restart they normally perform.
513
		 * This avoids restarting dhcpd twice as described on
514
		 * ticket #3797
515
		 */
516
		services_dhcpd_configure();
517

    
518
		local_sync_accounts();
519

    
520
		return true;
521
	}
522

    
523
	/**
524
	 * Wrapper for configuring CARP interfaces
525
	 *
526
	 * @param string $username
527
	 * @param string $password
528
	 *
529
	 * @return bool
530
	 */
531
	public function interfaces_carp_configure($username, $password) {
532
		$this->auth($username, $password);
533

    
534
		if ($this->loop_detected) {
535
			log_error("Disallowing CARP sync loop");
536
			return true;
537
		}
538

    
539
		interfaces_vips_configure();
540

    
541
		return true;
542
	}
543

    
544
	/**
545
	 * Wrapper for rc.reboot
546
	 *
547
	 * @param string $username
548
	 * @param string $password
549
	 *
550
	 * @return bool
551
	 */
552
	public function reboot($username, $password) {
553
		$this->auth($username, $password);
554

    
555
		mwexec_bg("/etc/rc.reboot");
556

    
557
		return true;
558
	}
559

    
560
	/**
561
	 * Wrapper for get_notices()
562
	 *
563
	 * @param string $username
564
	 * @param string $password
565
	 * @param string $category
566
	 *
567
	 * @return bool
568
	 */
569
	public function get_notices($username, $password, $category = 'all') {
570
		$this->auth($username, $password);
571

    
572
		global $g;
573

    
574
		if (!function_exists("get_notices")) {
575
			require_once("notices.inc");
576
		}
577
		if (!$params) {
578
			$toreturn = get_notices();
579
		} else {
580
			$toreturn = get_notices($params);
581
		}
582

    
583
		return $toreturn;
584
	}
585
}
586

    
587
$xmlrpclockkey = lock('xmlrpc', LOCK_EX);
588

    
589
XML_RPC2_Backend::setBackend('php');
590
$HTTP_RAW_POST_DATA = file_get_contents('php://input');
591

    
592
$options = array(
593
	'prefix' => 'pfsense.',
594
	'encoding' => 'utf-8',
595
	'autoDocument' => false,
596
);
597

    
598
$server = XML_RPC2_Server::create(new pfsense_xmlrpc_server(), $options);
599
$server->handleCall();
600

    
601
unlock($xmlrpclockkey);
602

    
603
?>
(227-227/227)