Project

General

Profile

Download (17.5 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 Electric Sheep Fencing, LLC
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("filter.inc");
33
require_once("ipsec.inc");
34
require_once("vpn.inc");
35
require_once("shaper.inc");
36
require_once("xmlrpc_server.inc");
37
require_once("xmlrpc.inc");
38

    
39
function xmlrpc_loop_detect() {
40
	global $config;
41

    
42
	/* grab sync to ip if enabled */
43
	if ($config['hasync']) {
44
		$synchronizetoip = $config['hasync']['synchronizetoip'];
45
	}
46
	if ($synchronizetoip) {
47
		if ($synchronizetoip == $_SERVER['REMOTE_ADDR']) {
48
			return true;
49
		}
50
	}
51

    
52
	return false;
53
}
54

    
55
$xmlrpc_g = array(
56
	"return" => array(
57
		"true" => new XML_RPC_Response(new XML_RPC_Value(true, $XML_RPC_Boolean)),
58
		"false" => new XML_RPC_Response(new XML_RPC_Value(false, $XML_RPC_Boolean)),
59
		"authfail" => new XML_RPC_Response(new XML_RPC_Value(gettext("Authentication failed"), $XML_RPC_String))
60
	)
61
);
62

    
63
/*
64
 *   pfSense XMLRPC errors
65
 *   $XML_RPC_erruser + 1 = Auth failure
66
 */
67
$XML_RPC_erruser = 200;
68

    
69
/* EXPOSED FUNCTIONS */
70
$exec_php_doc = gettext("XMLRPC wrapper for eval(). This method must be called with two parameters: a string containing the local system\'s password followed by the PHP code to evaluate.");
71
$exec_php_sig = array(
72
	array(
73
		$XML_RPC_Boolean, // First signature element is return value.
74
		$XML_RPC_String, // password
75
		$XML_RPC_String, // shell code to exec
76
	)
77
);
78

    
79
function xmlrpc_authfail() {
80
	log_auth("webConfigurator authentication error for 'admin' from {$_SERVER['REMOTE_ADDR']}");
81
}
82

    
83
function exec_php_xmlrpc($raw_params) {
84
	global $config, $xmlrpc_g;
85

    
86
	$params = xmlrpc_params_to_php($raw_params);
87
	if (!xmlrpc_auth($params)) {
88
		xmlrpc_authfail();
89
		return $xmlrpc_g['return']['authfail'];
90
	}
91
	$exec_php = $params[0];
92
	eval($exec_php);
93
	if ($toreturn) {
94
		$response = XML_RPC_encode($toreturn);
95
		return new XML_RPC_Response($response);
96
	} else {
97
		return $xmlrpc_g['return']['true'];
98
	}
99
}
100

    
101
/*****************************/
102
$exec_shell_doc = gettext("XMLRPC wrapper for mwexec(). This method must be called with two parameters: a string containing the local system\'s password followed by an shell command to execute.");
103
$exec_shell_sig = array(
104
	array(
105
		$XML_RPC_Boolean, // First signature element is return value.
106
		$XML_RPC_String, // password
107
		$XML_RPC_String, // shell code to exec
108
	)
109
);
110

    
111
function exec_shell_xmlrpc($raw_params) {
112
	global $config, $xmlrpc_g;
113

    
114
	$params = xmlrpc_params_to_php($raw_params);
115
	if (!xmlrpc_auth($params)) {
116
		xmlrpc_authfail();
117
		return $xmlrpc_g['return']['authfail'];
118
	}
119
	$shell_cmd = $params[0];
120
	mwexec($shell_cmd);
121

    
122
	return $xmlrpc_g['return']['true'];
123
}
124

    
125
/*****************************/
126
$backup_config_section_doc = gettext("XMLRPC wrapper for backup_config_section. This method must be called with two parameters: a string containing the local system\'s password followed by an array containing the keys to be backed up.");
127
$backup_config_section_sig = array(
128
	array(
129
		$XML_RPC_Struct, // First signature element is return value.
130
		$XML_RPC_String,
131
		$XML_RPC_Array
132
	)
133
);
134

    
135
function backup_config_section_xmlrpc($raw_params) {
136
	global $config, $xmlrpc_g;
137

    
138
	if (xmlrpc_loop_detect()) {
139
		log_error("Disallowing CARP sync loop");
140
		return;
141
	}
142

    
143
	$params = xmlrpc_params_to_php($raw_params);
144
	if (!xmlrpc_auth($params)) {
145
		xmlrpc_authfail();
146
		return $xmlrpc_g['return']['authfail'];
147
	}
148
	$val = array_intersect_key($config, array_flip($params[0]));
149

    
150
	return new XML_RPC_Response(XML_RPC_encode($val));
151
}
152

    
153
/*****************************/
154
$restore_config_section_doc = gettext("XMLRPC wrapper for restore_config_section. This method must be called with two parameters: a string containing the local system\'s password and an array to merge into the system\'s config. This function returns true upon completion.");
155
$restore_config_section_sig = array(
156
	array(
157
		$XML_RPC_Boolean,
158
		$XML_RPC_String,
159
		$XML_RPC_Struct
160
	)
161
);
162

    
163
function restore_config_section_xmlrpc($raw_params) {
164
	global $config, $xmlrpc_g;
165

    
166
	$old_config = $config;
167
	$old_ipsec_enabled = ipsec_enabled();
168

    
169
	if (xmlrpc_loop_detect()) {
170
		log_error("Disallowing CARP sync loop");
171
		return;
172
	}
173

    
174
	$params = xmlrpc_params_to_php($raw_params);
175
	if (!xmlrpc_auth($params)) {
176
		xmlrpc_authfail();
177
		return $xmlrpc_g['return']['authfail'];
178
	}
179

    
180
	// Some sections should just be copied and not merged or we end
181
	//   up unable to sync the deletion of the last item in a section
182
	$sync_full = array('dnsmasq', 'unbound', 'ipsec', 'aliases', 'wol', 'load_balancer', 'openvpn', 'cert', 'ca', 'crl', 'schedules', 'filter', 'nat', 'dhcpd', 'dhcpv6');
183
	$sync_full_done = array();
184
	foreach ($sync_full as $syncfull) {
185
		if (isset($params[0][$syncfull])) {
186
			$config[$syncfull] = $params[0][$syncfull];
187
			unset($params[0][$syncfull]);
188
			$sync_full_done[] = $syncfull;
189
		}
190
	}
191

    
192
	$vipbackup = array();
193
	$oldvips = array();
194
	if (isset($params[0]['virtualip'])) {
195
		if (is_array($config['virtualip']['vip'])) {
196
			foreach ($config['virtualip']['vip'] as $vipindex => $vip) {
197
				if ($vip['mode'] == "carp") {
198
					$oldvips["{$vip['interface']}_vip{$vip['vhid']}"]['content'] = "{$vip['password']}{$vip['advskew']}{$vip['subnet']}{$vip['subnet_bits']}{$vip['advbase']}";
199
					$oldvips["{$vip['interface']}_vip{$vip['vhid']}"]['interface'] = $vip['interface'];
200
					$oldvips["{$vip['interface']}_vip{$vip['vhid']}"]['subnet'] = $vip['subnet'];
201
				} else if ($vip['mode'] == "ipalias" && (substr($vip['interface'], 0, 4) == '_vip' || strstr($vip['interface'], "lo0"))) {
202
					$oldvips[$vip['subnet']]['content'] = "{$vip['interface']}{$vip['subnet']}{$vip['subnet_bits']}";
203
					$oldvips[$vip['subnet']]['interface'] = $vip['interface'];
204
					$oldvips[$vip['subnet']]['subnet'] = $vip['subnet'];
205
				} else if (($vip['mode'] == "ipalias" || $vip['mode'] == 'proxyarp') && !(substr($vip['interface'], 0, 4) == '_vip') || strstr($vip['interface'], "lo0")) {
206
					$vipbackup[] = $vip;
207
				}
208
			}
209
		}
210
	}
211

    
212
	// For vip section, first keep items sent from the master
213
	$config = array_merge_recursive_unique($config, $params[0]);
214

    
215
	/* Then add ipalias and proxyarp types already defined on the backup */
216
	if (is_array($vipbackup) && !empty($vipbackup)) {
217
		if (!is_array($config['virtualip'])) {
218
			$config['virtualip'] = array();
219
		}
220
		if (!is_array($config['virtualip']['vip'])) {
221
			$config['virtualip']['vip'] = array();
222
		}
223
		foreach ($vipbackup as $vip) {
224
			array_unshift($config['virtualip']['vip'], $vip);
225
		}
226
	}
227

    
228
	/* Log what happened */
229
	$mergedkeys = implode(",", array_merge(array_keys($params[0]), $sync_full_done));
230
	write_config(sprintf(gettext("Merged in config (%s sections) from XMLRPC client."), $mergedkeys));
231

    
232
	/*
233
	 * The real work on handling the vips specially
234
	 * This is a copy of intefaces_vips_configure with addition of not reloading existing/not changed carps
235
	 */
236
	if (isset($params[0]['virtualip']) && is_array($config['virtualip']) && is_array($config['virtualip']['vip'])) {
237
		$carp_setuped = false;
238
		$anyproxyarp = false;
239
		foreach ($config['virtualip']['vip'] as $vip) {
240
			if ($vip['mode'] == "carp" && isset($oldvips["{$vip['interface']}_vip{$vip['vhid']}"])) {
241
				if ($oldvips["{$vip['interface']}_vip{$vip['vhid']}"]['content'] == "{$vip['password']}{$vip['advskew']}{$vip['subnet']}{$vip['subnet_bits']}{$vip['advbase']}") {
242
					if (does_vip_exist($vip)) {
243
						unset($oldvips["{$vip['interface']}_vip{$vip['vhid']}"]);
244
						continue; // Skip reconfiguring this vips since nothing has changed.
245
					}
246
				}
247
			} else if ($vip['mode'] == "ipalias" && strstr($vip['interface'], "_vip") && isset($oldvips[$vip['subnet']])) {
248
				if ($oldvips[$vip['subnet']]['content'] == "{$vip['interface']}{$vip['subnet']}{$vip['subnet_bits']}") {
249
					if (does_vip_exist($vip)) {
250
						unset($oldvips[$vip['subnet']]);
251
						continue; // Skip reconfiguring this vips since nothing has changed.
252
					}
253
				}
254
				unset($oldvips[$vip['subnet']]);
255
			}
256

    
257
			switch ($vip['mode']) {
258
				case "proxyarp":
259
					$anyproxyarp = true;
260
					break;
261
				case "ipalias":
262
					interface_ipalias_configure($vip);
263
					break;
264
				case "carp":
265
					if ($carp_setuped == false) {
266
						$carp_setuped = true;
267
					}
268
					interface_carp_configure($vip);
269
					break;
270
			}
271
		}
272
		/* Cleanup remaining old carps */
273
		foreach ($oldvips as $oldvipar) {
274
			$oldvipif = get_real_interface($oldvipar['interface']);
275
			if (!empty($oldvipif)) {
276
				if (is_ipaddrv6($oldvipar['subnet'])) {
277
					 mwexec("/sbin/ifconfig " . escapeshellarg($oldvipif) . " inet6 " . escapeshellarg($oldvipar['subnet']) . " delete");
278
				} else {
279
					pfSense_interface_deladdress($oldvipif, $oldvipar['subnet']);
280
				}
281
			}
282
		}
283
		if ($carp_setuped == true) {
284
			interfaces_sync_setup();
285
		}
286
		if ($anyproxyarp == true) {
287
			interface_proxyarp_configure();
288
		}
289
	}
290

    
291
	if ($old_ipsec_enabled !== ipsec_enabled()) {
292
		vpn_ipsec_configure();
293
	}
294

    
295
	unset($old_config);
296

    
297
	return $xmlrpc_g['return']['true'];
298
}
299

    
300
/*****************************/
301
$merge_installedpackages_section_doc = gettext("XMLRPC wrapper for merging package sections. This method must be called with two parameters: a string containing the local system\'s password and an array to merge into the system\'s config. This function returns true upon completion.");
302
$merge_installedpackages_section_sig = array(
303
	array(
304
		$XML_RPC_Boolean,
305
		$XML_RPC_String,
306
		$XML_RPC_Struct
307
	)
308
);
309

    
310
function merge_installedpackages_section_xmlrpc($raw_params) {
311
	global $config, $xmlrpc_g;
312

    
313
	if (xmlrpc_loop_detect()) {
314
		log_error("Disallowing CARP sync loop");
315
		return;
316
	}
317

    
318
	$params = xmlrpc_params_to_php($raw_params);
319
	if (!xmlrpc_auth($params)) {
320
		xmlrpc_authfail();
321
		return $xmlrpc_g['return']['authfail'];
322
	}
323
	$config['installedpackages'] = array_merge($config['installedpackages'], $params[0]);
324
	$mergedkeys = implode(",", array_keys($params[0]));
325
	write_config(sprintf(gettext("Merged in config (%s sections) from XMLRPC client."), $mergedkeys));
326

    
327
	return $xmlrpc_g['return']['true'];
328
}
329

    
330
/*****************************/
331
$merge_config_section_doc = gettext("XMLRPC wrapper for merge_config_section. This method must be called with two parameters: a string containing the local system\'s password and an array to merge into the system\'s config. This function returns true upon completion.");
332
$merge_config_section_sig = array(
333
	array(
334
		$XML_RPC_Boolean,
335
		$XML_RPC_String,
336
		$XML_RPC_Struct
337
	)
338
);
339

    
340
function merge_config_section_xmlrpc($raw_params) {
341
	global $config, $xmlrpc_g;
342

    
343
	if (xmlrpc_loop_detect()) {
344
		log_error("Disallowing CARP sync loop");
345
		return;
346
	}
347

    
348
	$params = xmlrpc_params_to_php($raw_params);
349
	if (!xmlrpc_auth($params)) {
350
		xmlrpc_authfail();
351
		return $xmlrpc_g['return']['authfail'];
352
	}
353
	$config_new = array_overlay($config, $params[0]);
354
	$config = $config_new;
355
	$mergedkeys = implode(",", array_keys($params[0]));
356
	write_config(sprintf(gettext("Merged in config (%s sections) from XMLRPC client."), $mergedkeys));
357
	return $xmlrpc_g['return']['true'];
358
}
359

    
360
/*****************************/
361
$filter_configure_doc = gettext("Basic XMLRPC wrapper for filter_configure. This method must be called with one parameter: a string containing the local system\'s password. This function returns true upon completion.");
362
$filter_configure_sig = array(
363
	array(
364
		$XML_RPC_Boolean,
365
		$XML_RPC_String
366
	)
367
);
368

    
369
function filter_configure_xmlrpc($raw_params) {
370
	global $xmlrpc_g, $g, $config;
371

    
372
	$params = xmlrpc_params_to_php($raw_params);
373
	if (!xmlrpc_auth($params)) {
374
		xmlrpc_authfail();
375
		return $xmlrpc_g['return']['authfail'];
376
	}
377
	filter_configure();
378
	system_routing_configure();
379
	setup_gateways_monitor();
380
	relayd_configure();
381
	require_once("openvpn.inc");
382
	openvpn_resync_all();
383

    
384
	/* The DNS Resolver and the DNS Forwarder may both be active so long as
385
	 * they are running on different ports. See ticket #5882
386
	 */
387
	$need_dhcp_start = true;
388
	if (isset($config['dnsmasq']['enable'])) {
389
		/* Configure dnsmasq but tell it NOT to restart DHCP */
390
		services_dnsmasq_configure(false);
391
	} else {
392
		/* kill any running dnsmasq since it is not enabled. */
393
		if (file_exists("{$g['varrun_path']}/dnsmasq.pid")) {
394
			sigkillbypid("{$g['varrun_path']}/dnsmasq.pid", "TERM");
395
		}
396
	}
397
	if (isset($config['unbound']['enable'])) {
398
		/* Configure unbound but tell it NOT to restart DHCP */
399
		services_unbound_configure(false);
400
	} else {
401
		/* kill any running Unbound instance since it is not enabled. */
402
		if (file_exists("{$g['varrun_path']}/unbound.pid")) {
403
			sigkillbypid("{$g['varrun_path']}/unbound.pid", "TERM");
404
		}
405
	}
406

    
407
	/* Call this separately since the above are manually set to skip the DHCP restart they normally perform.
408
	 * This avoids restarting dhcpd twice as described on ticket #3797
409
	 */
410
	services_dhcpd_configure();
411

    
412
	local_sync_accounts();
413

    
414
	return $xmlrpc_g['return']['true'];
415
}
416

    
417
/*****************************/
418
$carp_configure_doc = gettext("Basic XMLRPC wrapper for configuring CARP interfaces.");
419
$carp_configure_sig = array(
420
	array(
421
		$XML_RPC_Boolean,
422
		$XML_RPC_String
423
	)
424
);
425

    
426
function interfaces_carp_configure_xmlrpc($raw_params) {
427
	global $xmlrpc_g;
428

    
429
	if (xmlrpc_loop_detect()) {
430
		log_error("Disallowing CARP sync loop");
431
		return;
432
	}
433

    
434
	$params = xmlrpc_params_to_php($raw_params);
435
	if (!xmlrpc_auth($params)) {
436
		xmlrpc_authfail();
437
		return $xmlrpc_g['return']['authfail'];
438
	}
439
	interfaces_vips_configure();
440

    
441
	return $xmlrpc_g['return']['true'];
442
}
443

    
444
/*****************************/
445
$pfsense_firmware_version_doc = gettext("Basic XMLRPC wrapper for host_firmware_version. This function will return the output of host_firmware_version upon completion.");
446

    
447
$pfsense_firmware_version_sig = array (
448
	array (
449
		$XML_RPC_Struct,
450
		$XML_RPC_String
451
	)
452
);
453

    
454
function pfsense_firmware_version_xmlrpc($raw_params) {
455
		global $xmlrpc_g;
456

    
457
		$params = xmlrpc_params_to_php($raw_params);
458
		if (!xmlrpc_auth($params)) {
459
			xmlrpc_authfail();
460
			return $xmlrpc_g['return']['authfail'];
461
		}
462
		return new XML_RPC_Response(XML_RPC_encode(host_firmware_version()));
463
}
464

    
465
/*****************************/
466
$reboot_doc = gettext("Basic XMLRPC wrapper for rc.reboot.");
467
$reboot_sig = array(array($XML_RPC_Boolean, $XML_RPC_String));
468
function reboot_xmlrpc($raw_params) {
469
	global $xmlrpc_g;
470

    
471
	$params = xmlrpc_params_to_php($raw_params);
472
	if (!xmlrpc_auth($params)) {
473
		xmlrpc_authfail();
474
		return $xmlrpc_g['return']['authfail'];
475
	}
476
	mwexec_bg("/etc/rc.reboot");
477

    
478
	return $xmlrpc_g['return']['true'];
479
}
480

    
481
/*****************************/
482
$get_notices_sig = array(
483
	array(
484
		$XML_RPC_Array,
485
		$XML_RPC_String
486
	),
487
	array(
488
		$XML_RPC_Array
489
	)
490
);
491

    
492
function get_notices_xmlrpc($raw_params) {
493
	global $g, $xmlrpc_g;
494

    
495
	$params = xmlrpc_params_to_php($raw_params);
496
	if (!xmlrpc_auth($params)) {
497
		xmlrpc_authfail();
498
		return $xmlrpc_g['return']['authfail'];
499
	}
500
	if (!function_exists("get_notices")) {
501
		require_once("notices.inc");
502
	}
503
	if (!$params) {
504
		$toreturn = get_notices();
505
	} else {
506
		$toreturn = get_notices($params);
507
	}
508
	$response = new XML_RPC_Response(XML_RPC_encode($toreturn));
509

    
510
	return $response;
511
}
512

    
513
$xmlrpclockkey = lock('xmlrpc', LOCK_EX);
514

    
515
/*****************************/
516
$server = new XML_RPC_Server(
517
	array(
518
		'pfsense.exec_shell' => array('function' => 'exec_shell_xmlrpc',
519
			'signature' => $exec_shell_sig,
520
			'docstring' => $exec_shell_doc),
521
		'pfsense.exec_php' => array('function' => 'exec_php_xmlrpc',
522
			'signature' => $exec_php_sig,
523
			'docstring' => $exec_php_doc),
524
		'pfsense.filter_configure' => array('function' => 'filter_configure_xmlrpc',
525
			'signature' => $filter_configure_sig,
526
			'docstring' => $filter_configure_doc),
527
		'pfsense.interfaces_carp_configure' => array('function' => 'interfaces_carp_configure_xmlrpc',
528
			'signature' => $carp_configure_sig,
529
			'docstring' => $carp_configure_doc),
530
		'pfsense.backup_config_section' => array('function' => 'backup_config_section_xmlrpc',
531
			'signature' => $backup_config_section_sig,
532
			'docstring' => $backup_config_section_doc),
533
		'pfsense.restore_config_section' => array('function' => 'restore_config_section_xmlrpc',
534
			'signature' => $restore_config_section_sig,
535
			'docstring' => $restore_config_section_doc),
536
		'pfsense.merge_config_section' => array('function' => 'merge_config_section_xmlrpc',
537
			'signature' => $merge_config_section_sig,
538
			'docstring' => $merge_config_section_doc),
539
		'pfsense.merge_installedpackages_section_xmlrpc' => array('function' => 'merge_installedpackages_section_xmlrpc',
540
			'signature' => $merge_installedpackages_section_sig,
541
			'docstring' => $merge_installedpackages_section_doc),
542
		'pfsense.host_firmware_version' => array('function' => 'pfsense_firmware_version_xmlrpc',
543
			'signature' => $pfsense_firmware_version_sig,
544
			'docstring' => $pfsense_firmware_version_doc),
545
		'pfsense.reboot' => array('function' => 'reboot_xmlrpc',
546
			'signature' => $reboot_sig,
547
			'docstring' => $reboot_doc),
548
		'pfsense.get_notices' => array('function' => 'get_notices_xmlrpc',
549
			'signature' => $get_notices_sig)
550
	)
551
);
552

    
553
unlock($xmlrpclockkey);
554

    
555
function array_overlay($a1, $a2) {
556
	foreach ($a1 as $k => $v) {
557
		if (!array_key_exists($k, $a2)) {
558
			continue;
559
		}
560
		if (is_array($v) && is_array($a2[$k])) {
561
			$a1[$k] = array_overlay($v, $a2[$k]);
562
		} else {
563
			$a1[$k] = $a2[$k];
564
		}
565
	}
566
	return $a1;
567
}
568

    
569
?>
(227-227/227)