Project

General

Profile

Download (18.4 KB) Statistics
| Branch: | Tag: | Revision:
1
<?php
2
/*
3
	xmlrpc.php
4
	Copyright (C) 2013-2015 Electric Sheep Fencing, LP
5
	Copyright (C) 2009, 2010 Scott Ullrich
6
	Copyright (C) 2005 Colin Smith
7
	All rights reserved.
8

    
9
	Redistribution and use in source and binary forms, with or without
10
	modification, are permitted provided that the following conditions are met:
11

    
12
	1. Redistributions of source code must retain the above copyright notice,
13
	   this list of conditions and the following disclaimer.
14

    
15
	2. Redistributions in binary form must reproduce the above copyright
16
	   notice, this list of conditions and the following disclaimer in the
17
	   documentation and/or other materials provided with the distribution.
18

    
19
	THIS SOFTWARE IS PROVIDED ``AS IS'' AND ANY EXPRESS OR IMPLIED WARRANTIES,
20
	INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY
21
	AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE
22
	AUTHOR BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY,
23
	OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF
24
	SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS
25
	INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN
26
	CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE)
27
	ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE
28
	POSSIBILITY OF SUCH DAMAGE.
29
*/
30

    
31
##|+PRIV
32
##|*IDENT=page-xmlrpclibrary
33
##|*NAME=XMLRPC Library page
34
##|*DESCR=Allow access to the 'XMLRPC Library' page.
35
##|*MATCH=xmlrpc.php*
36
##|-PRIV
37

    
38
require("config.inc");
39
require("functions.inc");
40
require_once("filter.inc");
41
require("ipsec.inc");
42
require("vpn.inc");
43
require("shaper.inc");
44
require("xmlrpc_server.inc");
45
require("xmlrpc.inc");
46
require("array_intersect_key.inc");
47

    
48
function xmlrpc_loop_detect() {
49
	global $config;
50

    
51
	/* grab sync to ip if enabled */
52
	if ($config['hasync'])
53
		$synchronizetoip = $config['hasync']['synchronizetoip'];
54
	if($synchronizetoip) {
55
		if($synchronizetoip == $_SERVER['REMOTE_ADDR'])
56
			return true;
57
	}
58

    
59
	return false;
60
}
61

    
62
$xmlrpc_g = array(
63
	"return" => array(
64
		"true" => new XML_RPC_Response(new XML_RPC_Value(true, $XML_RPC_Boolean)),
65
		"false" => new XML_RPC_Response(new XML_RPC_Value(false, $XML_RPC_Boolean)),
66
		"authfail" => new XML_RPC_Response(new XML_RPC_Value(gettext("Authentication failed"), $XML_RPC_String))
67
	)
68
);
69

    
70
/*
71
 *   pfSense XMLRPC errors
72
 *   $XML_RPC_erruser + 1 = Auth failure
73
 */
74
$XML_RPC_erruser = 200;
75

    
76
/* EXPOSED FUNCTIONS */
77
$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.");
78
$exec_php_sig = array(
79
	array(
80
		$XML_RPC_Boolean, // First signature element is return value.
81
		$XML_RPC_String, // password
82
		$XML_RPC_String, // shell code to exec
83
	)
84
);
85

    
86
function xmlrpc_authfail() {
87
	log_auth("webConfigurator authentication error for 'admin' from {$_SERVER['REMOTE_ADDR']}");
88
}
89

    
90
function exec_php_xmlrpc($raw_params) {
91
	global $config, $xmlrpc_g;
92

    
93
	$params = xmlrpc_params_to_php($raw_params);
94
	if(!xmlrpc_auth($params)) {
95
		xmlrpc_authfail();
96
		return $xmlrpc_g['return']['authfail'];
97
	}
98
	$exec_php = $params[0];
99
	eval($exec_php);
100
	if($toreturn) {
101
		$response = XML_RPC_encode($toreturn);
102
		return new XML_RPC_Response($response);
103
	} else
104
		return $xmlrpc_g['return']['true'];
105
}
106

    
107
/*****************************/
108
$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.");
109
$exec_shell_sig = array(
110
	array(
111
		$XML_RPC_Boolean, // First signature element is return value.
112
		$XML_RPC_String, // password
113
		$XML_RPC_String, // shell code to exec
114
	)
115
);
116

    
117
function exec_shell_xmlrpc($raw_params) {
118
	global $config, $xmlrpc_g;
119

    
120
	$params = xmlrpc_params_to_php($raw_params);
121
	if(!xmlrpc_auth($params)) {
122
		xmlrpc_authfail();
123
		return $xmlrpc_g['return']['authfail'];
124
	}
125
	$shell_cmd = $params[0];
126
	mwexec($shell_cmd);
127

    
128
	return $xmlrpc_g['return']['true'];
129
}
130

    
131
/*****************************/
132
$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.");
133
$backup_config_section_sig = array(
134
	array(
135
		$XML_RPC_Struct, // First signature element is return value.
136
		$XML_RPC_String,
137
		$XML_RPC_Array
138
	)
139
);
140

    
141
function backup_config_section_xmlrpc($raw_params) {
142
	global $config, $xmlrpc_g;
143

    
144
	if (xmlrpc_loop_detect()) {
145
		log_error("Disallowing CARP sync loop");
146
		return;
147
	}
148

    
149
	$params = xmlrpc_params_to_php($raw_params);
150
	if(!xmlrpc_auth($params)) {
151
		xmlrpc_authfail();
152
		return $xmlrpc_g['return']['authfail'];
153
	}
154
	$val = array_intersect_key($config, array_flip($params[0]));
155

    
156
	return new XML_RPC_Response(XML_RPC_encode($val));
157
}
158

    
159
/*****************************/
160
$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.");
161
$restore_config_section_sig = array(
162
	array(
163
		$XML_RPC_Boolean,
164
		$XML_RPC_String,
165
		$XML_RPC_Struct
166
	)
167
);
168

    
169
function restore_config_section_xmlrpc($raw_params) {
170
	global $config, $xmlrpc_g;
171

    
172
	$old_config = $config;
173

    
174
	if (xmlrpc_loop_detect()) {
175
		log_error("Disallowing CARP sync loop");
176
		return;
177
	}
178

    
179
	$params = xmlrpc_params_to_php($raw_params);
180
	if(!xmlrpc_auth($params)) {
181
		xmlrpc_authfail();
182
		return $xmlrpc_g['return']['authfail'];
183
	}
184

    
185
	/*
186
	 * Make sure it doesn't end up with both dnsmasq and unbound enabled
187
	 * simultaneously in secondary
188
	 * */
189
	if (isset($params[0]['unbound']['enable']) && isset($config['dnsmasq']['enable'])) {
190
		unset($config['dnsmasq']['enable']);
191
		services_dnsmasq_configure();
192
	} else if (isset($params[0]['dnsmasq']['enable']) && isset($config['unbound']['enable'])) {
193
		unset($config['unbound']['enable']);
194
		services_unbound_configure();
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
	$sync_full = array('dnsmasq', 'unbound', 'ipsec', 'aliases', 'wol', 'load_balancer', 'openvpn', 'cert', 'ca', 'crl', 'schedules', 'filter', 'nat', 'dhcpd', 'dhcpv6');
200
	$sync_full_done = array();
201
	foreach ($sync_full as $syncfull) {
202
		if (isset($params[0][$syncfull])) {
203
			$config[$syncfull] = $params[0][$syncfull];
204
			unset($params[0][$syncfull]);
205
			$sync_full_done[] = $syncfull;
206
		}
207
	}
208

    
209
	$vipbackup = array();
210
	$oldvips = array();
211
	if (isset($params[0]['virtualip'])) {
212
		if (is_array($config['virtualip']['vip'])) {
213
			foreach ($config['virtualip']['vip'] as $vipindex => $vip) {
214
				if ($vip['mode'] == "carp")
215
					$oldvips["{$vip['interface']}_vip{$vip['vhid']}"] = "{$vip['password']}{$vip['advskew']}{$vip['subnet']}{$vip['subnet_bits']}{$vip['advbase']}";
216
				else if ($vip['mode'] == "ipalias" && (substr($vip['interface'], 0, 4) == '_vip') || strpos($vip['interface'], "lo0"))
217
					$oldvips[$vip['subnet']] = "{$vip['interface']}{$vip['subnet']}{$vip['subnet_bits']}";
218
				else if (($vip['mode'] == "ipalias" || $vip['mode'] == 'proxyarp') && !(substr($vip['interface'], 0, 4) == '_vip') || strpos($vip['interface'], "lo0"))
219
					$vipbackup[] = $vip;
220
			}
221
		}
222
	}
223

    
224
        // For vip section, first keep items sent from the master
225
	$config = array_merge_recursive_unique($config, $params[0]);
226

    
227
        /* Then add ipalias and proxyarp types already defined on the backup */
228
	if (is_array($vipbackup) && !empty($vipbackup)) {
229
		if (!is_array($config['virtualip']))
230
			$config['virtualip'] = array();
231
		if (!is_array($config['virtualip']['vip']))
232
			$config['virtualip']['vip'] = array();
233
		foreach ($vipbackup as $vip)
234
			array_unshift($config['virtualip']['vip'], $vip);
235
	}
236

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

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

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

    
297
	if (isset($old_config['ipsec']['enable']) !== isset($config['ipsec']['enable']))
298
		vpn_ipsec_configure();
299

    
300
	unset($old_config);
301

    
302
	return $xmlrpc_g['return']['true'];
303
}
304

    
305
/*****************************/
306
$merge_config_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.");
307
$merge_config_section_sig = array(
308
	array(
309
		$XML_RPC_Boolean,
310
		$XML_RPC_String,
311
		$XML_RPC_Struct
312
	)
313
);
314

    
315
function merge_installedpackages_section_xmlrpc($raw_params) {
316
	global $config, $xmlrpc_g;
317

    
318
	if (xmlrpc_loop_detect()) {
319
		log_error("Disallowing CARP sync loop");
320
		return;
321
	}
322

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

    
332
	return $xmlrpc_g['return']['true'];
333
}
334

    
335
/*****************************/
336
$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.");
337
$merge_config_section_sig = array(
338
	array(
339
		$XML_RPC_Boolean,
340
		$XML_RPC_String,
341
		$XML_RPC_Struct
342
	)
343
);
344

    
345
function merge_config_section_xmlrpc($raw_params) {
346
	global $config, $xmlrpc_g;
347

    
348
	if (xmlrpc_loop_detect()) {
349
		log_error("Disallowing CARP sync loop");
350
		return;
351
	}
352

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

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

    
374
function filter_configure_xmlrpc($raw_params) {
375
	global $xmlrpc_g, $config;
376

    
377
	$params = xmlrpc_params_to_php($raw_params);
378
	if(!xmlrpc_auth($params)) {
379
		xmlrpc_authfail();
380
		return $xmlrpc_g['return']['authfail'];
381
	}
382
	filter_configure();
383
	system_routing_configure();
384
	setup_gateways_monitor();
385
	relayd_configure();
386
	require_once("openvpn.inc");
387
	openvpn_resync_all();
388
	if (isset($config['dnsmasq']['enable']))
389
		services_dnsmasq_configure();
390
	elseif (isset($config['unbound']['enable']))
391
		services_unbound_configure();
392
	else
393
		# Both calls above run services_dhcpd_configure(), then we just
394
		# need to call it when them are not called to avoid restart dhcpd
395
		# twice, as described on ticket #3797
396
		services_dhcpd_configure();
397
	local_sync_accounts();
398

    
399
	return $xmlrpc_g['return']['true'];
400
}
401

    
402
/*****************************/
403
$carp_configure_doc = gettext("Basic XMLRPC wrapper for configuring CARP interfaces.");
404
$carp_configure_sig = array(
405
	array(
406
		$XML_RPC_Boolean,
407
		$XML_RPC_String
408
	)
409
);
410

    
411
function interfaces_carp_configure_xmlrpc($raw_params) {
412
	global $xmlrpc_g;
413

    
414
	if (xmlrpc_loop_detect()) {
415
		log_error("Disallowing CARP sync loop");
416
		return;
417
	}
418

    
419
	$params = xmlrpc_params_to_php($raw_params);
420
	if(!xmlrpc_auth($params)) {
421
		xmlrpc_authfail();
422
		return $xmlrpc_g['return']['authfail'];
423
	}
424
	interfaces_vips_configure();
425

    
426
	return $xmlrpc_g['return']['true'];
427
}
428

    
429
/*****************************/
430
$check_firmware_version_doc = gettext("Basic XMLRPC wrapper for check_firmware_version. This function will return the output of check_firmware_version upon completion.");
431

    
432
$check_firmware_version_sig = array(
433
	array(
434
		$XML_RPC_String,
435
		$XML_RPC_String
436
	)
437
);
438

    
439
function check_firmware_version_xmlrpc($raw_params) {
440
	global $xmlrpc_g, $XML_RPC_String;
441

    
442
	$params = xmlrpc_params_to_php($raw_params);
443
	if(!xmlrpc_auth($params)) {
444
		xmlrpc_authfail();
445
		return $xmlrpc_g['return']['authfail'];
446
	}
447
	return new XML_RPC_Response(new XML_RPC_Value(check_firmware_version(false), $XML_RPC_String));
448
}
449

    
450
/*****************************/
451
$pfsense_firmware_version_doc = gettext("Basic XMLRPC wrapper for check_firmware_version. This function will return the output of check_firmware_version upon completion.");
452

    
453
$pfsense_firmware_version_sig = array (
454
        array (
455
                $XML_RPC_Struct,
456
                $XML_RPC_String
457
        )
458
);
459

    
460
function pfsense_firmware_version_xmlrpc($raw_params) {
461
        global $xmlrpc_g;
462

    
463
        $params = xmlrpc_params_to_php($raw_params);
464
        if(!xmlrpc_auth($params)) {
465
			xmlrpc_authfail();
466
			return $xmlrpc_g['return']['authfail'];
467
		}
468
        return new XML_RPC_Response(XML_RPC_encode(host_firmware_version()));
469
}
470

    
471
/*****************************/
472
$reboot_doc = gettext("Basic XMLRPC wrapper for rc.reboot.");
473
$reboot_sig = array(array($XML_RPC_Boolean, $XML_RPC_String));
474
function reboot_xmlrpc($raw_params) {
475
	global $xmlrpc_g;
476

    
477
	$params = xmlrpc_params_to_php($raw_params);
478
	if(!xmlrpc_auth($params)) {
479
		xmlrpc_authfail();
480
		return $xmlrpc_g['return']['authfail'];
481
	}
482
	mwexec_bg("/etc/rc.reboot");
483

    
484
	return $xmlrpc_g['return']['true'];
485
}
486

    
487
/*****************************/
488
$get_notices_sig = array(
489
	array(
490
		$XML_RPC_Array,
491
		$XML_RPC_String
492
	),
493
	array(
494
		$XML_RPC_Array
495
	)
496
);
497

    
498
function get_notices_xmlrpc($raw_params) {
499
	global $g, $xmlrpc_g;
500

    
501
	$params = xmlrpc_params_to_php($raw_params);
502
	if(!xmlrpc_auth($params)) {
503
		xmlrpc_authfail();
504
		return $xmlrpc_g['return']['authfail'];
505
	}
506
	if(!function_exists("get_notices"))
507
		require("notices.inc");
508
	if(!$params) {
509
		$toreturn = get_notices();
510
	} else {
511
		$toreturn = get_notices($params);
512
	}
513
	$response = new XML_RPC_Response(XML_RPC_encode($toreturn));
514

    
515
	return $response;
516
}
517

    
518
$xmlrpclockkey = lock('xmlrpc', LOCK_EX);
519

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

    
560
unlock($xmlrpclockkey);
561

    
562
    function array_overlay($a1,$a2)
563
    {
564
        foreach($a1 as $k => $v) {
565
            if(!array_key_exists($k,$a2)) continue;
566
            if(is_array($v) && is_array($a2[$k])){
567
                $a1[$k] = array_overlay($v,$a2[$k]);
568
            }else{
569
                $a1[$k] = $a2[$k];
570
            }
571
        }
572
        return $a1;
573
    }
574

    
575
?>
(252-252/252)