Project

General

Profile

Download (17.4 KB) Statistics
| Branch: | Tag: | Revision:
1
<?php
2
/*
3
	$Id$
4

    
5
        xmlrpc.php
6
        Copyright (C) 2009, 2010 Scott Ullrich
7
        Copyright (C) 2005 Colin Smith
8
        All rights reserved.
9

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

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

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

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

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

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

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

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

    
60
	return false;
61
}
62

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

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

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

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

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

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

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

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

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

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

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

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

    
145
	if (xmlrpc_loop_detect())
146
		log_error("Disallowing CARP sync loop");
147

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

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

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

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

    
171
	$old_config = $config;
172

    
173
	if (xmlrpc_loop_detect())
174
		log_error("Disallowing CARP sync loop");
175

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

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

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

    
209
        // For vip section, first keep items sent from the master
210
	$config = array_merge_recursive_unique($config, $params[0]);
211

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

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

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

    
252
			switch ($vip['mode']) {
253
			case "proxyarp":
254
				$anyproxyarp = true;
255
				break;
256
			case "ipalias":
257
				interface_ipalias_configure($vip);
258
				break;
259
			case "carp":
260
				if ($carp_setuped == false)
261
                                        $carp_setuped = true;
262
				interface_carp_configure($vip);
263
				break;
264
			}
265
		}
266
		/* Cleanup remaining old carps */
267
		foreach ($oldvips as $oldvipif => $oldvippar) {
268
			if (!is_ipaddr($oldvipif) && does_interface_exist($oldvipif))
269
					pfSense_interface_destroy($oldvipif);
270
		}
271
		if ($carp_setuped == true)
272
			interfaces_carp_setup();
273
		if ($anyproxyarp == true)
274
			interface_proxyarp_configure();
275
	}
276

    
277
	if (isset($old_config['ipsec']['enable']) !== isset($config['ipsec']['enable']))
278
		vpn_ipsec_configure();
279

    
280
	unset($old_config);
281

    
282
	return $xmlrpc_g['return']['true'];
283
}
284

    
285
/*****************************/
286
$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.");
287
$merge_config_section_sig = array(
288
	array(
289
		$XML_RPC_Boolean,
290
		$XML_RPC_String,
291
		$XML_RPC_Struct
292
	)
293
);
294

    
295
function merge_installedpackages_section_xmlrpc($raw_params) {
296
	global $config, $xmlrpc_g;
297

    
298
	if (xmlrpc_loop_detect())
299
		log_error("Disallowing CARP sync loop");
300

    
301
	$params = xmlrpc_params_to_php($raw_params);
302
	if(!xmlrpc_auth($params)) {
303
		xmlrpc_authfail();
304
		return $xmlrpc_g['return']['authfail'];
305
	}
306
	$config['installedpackages'] = array_merge($config['installedpackages'], $params[0]);
307
	$mergedkeys = implode(",", array_keys($params[0]));
308
	write_config(sprintf(gettext("Merged in config (%s sections) from XMLRPC client."),$mergedkeys));
309

    
310
	return $xmlrpc_g['return']['true'];
311
}
312

    
313
/*****************************/
314
$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.");
315
$merge_config_section_sig = array(
316
	array(
317
		$XML_RPC_Boolean,
318
		$XML_RPC_String,
319
		$XML_RPC_Struct
320
	)
321
);
322

    
323
function merge_config_section_xmlrpc($raw_params) {
324
	global $config, $xmlrpc_g;
325

    
326
	if (xmlrpc_loop_detect())
327
		log_error("Disallowing CARP sync loop");
328

    
329
	$params = xmlrpc_params_to_php($raw_params);
330
	if(!xmlrpc_auth($params)) {
331
		xmlrpc_authfail();
332
		return $xmlrpc_g['return']['authfail'];
333
	}
334
	$config_new = array_overlay($config, $params[0]);
335
	$config = $config_new;
336
	$mergedkeys = implode(",", array_keys($params[0]));
337
	write_config(sprintf(gettext("Merged in config (%s sections) from XMLRPC client."), $mergedkeys));
338
	return $xmlrpc_g['return']['true'];
339
}
340

    
341
/*****************************/
342
$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.");
343
$filter_configure_sig = array(
344
	array(
345
		$XML_RPC_Boolean,
346
		$XML_RPC_String
347
	)
348
);
349

    
350
function filter_configure_xmlrpc($raw_params) {
351
	global $xmlrpc_g;
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
	filter_configure();
359
	system_routing_configure();
360
	setup_gateways_monitor();
361
	relayd_configure();
362
	require_once("openvpn.inc");
363
	openvpn_resync_all();
364
	services_dhcpd_configure();
365
	services_dnsmasq_configure();
366
	local_sync_accounts();
367

    
368
	return $xmlrpc_g['return']['true'];
369
}
370

    
371
/*****************************/
372
$carp_configure_doc = gettext("Basic XMLRPC wrapper for configuring CARP interfaces.");
373
$carp_configure_sig = array(
374
	array(
375
		$XML_RPC_Boolean,
376
		$XML_RPC_String
377
	)
378
);
379

    
380
function interfaces_carp_configure_xmlrpc($raw_params) {
381
	global $xmlrpc_g;
382

    
383
	if (xmlrpc_loop_detect())
384
		log_error("Disallowing CARP sync loop");
385

    
386
	$params = xmlrpc_params_to_php($raw_params);
387
	if(!xmlrpc_auth($params)) {
388
		xmlrpc_authfail();
389
		return $xmlrpc_g['return']['authfail'];
390
	}
391
	interfaces_vips_configure();
392

    
393
	return $xmlrpc_g['return']['true'];
394
}
395

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

    
399
$check_firmware_version_sig = array(
400
	array(
401
		$XML_RPC_String,
402
		$XML_RPC_String
403
	)
404
);
405

    
406
function check_firmware_version_xmlrpc($raw_params) {
407
	global $xmlrpc_g, $XML_RPC_String;
408

    
409
	$params = xmlrpc_params_to_php($raw_params);
410
	if(!xmlrpc_auth($params)) {
411
		xmlrpc_authfail();
412
		return $xmlrpc_g['return']['authfail'];
413
	}
414
	return new XML_RPC_Response(new XML_RPC_Value(check_firmware_version(false), $XML_RPC_String));
415
}
416

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

    
420
$pfsense_firmware_version_sig = array (
421
        array (
422
                $XML_RPC_Struct,
423
                $XML_RPC_String
424
        )
425
);
426

    
427
function pfsense_firmware_version_xmlrpc($raw_params) {
428
        global $xmlrpc_g;
429

    
430
        $params = xmlrpc_params_to_php($raw_params);
431
        if(!xmlrpc_auth($params)) {
432
			xmlrpc_authfail();
433
			return $xmlrpc_g['return']['authfail'];
434
		}
435
        return new XML_RPC_Response(XML_RPC_encode(host_firmware_version()));
436
}
437

    
438
/*****************************/
439
$reboot_doc = gettext("Basic XMLRPC wrapper for rc.reboot.");
440
$reboot_sig = array(array($XML_RPC_Boolean, $XML_RPC_String));
441
function reboot_xmlrpc($raw_params) {
442
	global $xmlrpc_g;
443

    
444
	$params = xmlrpc_params_to_php($raw_params);
445
	if(!xmlrpc_auth($params)) {
446
		xmlrpc_authfail();
447
		return $xmlrpc_g['return']['authfail'];
448
	}
449
	mwexec_bg("/etc/rc.reboot");
450

    
451
	return $xmlrpc_g['return']['true'];
452
}
453

    
454
/*****************************/
455
$get_notices_sig = array(
456
	array(
457
		$XML_RPC_Array,
458
		$XML_RPC_String
459
	),
460
	array(
461
		$XML_RPC_Array
462
	)
463
);
464

    
465
function get_notices_xmlrpc($raw_params) {
466
	global $g, $xmlrpc_g;
467

    
468
	$params = xmlrpc_params_to_php($raw_params);
469
	if(!xmlrpc_auth($params)) {
470
		xmlrpc_authfail();
471
		return $xmlrpc_g['return']['authfail'];
472
	}
473
	require("notices.inc");
474
	if(!$params) {
475
		$toreturn = get_notices();
476
	} else {
477
		$toreturn = get_notices($params);
478
	}
479
	$response = new XML_RPC_Response(XML_RPC_encode($toreturn));
480

    
481
	return $response;
482
}
483

    
484
$xmlrpclockkey = lock('xmlrpc', LOCK_EX);
485

    
486
/*****************************/
487
$server = new XML_RPC_Server(
488
        array(
489
		'pfsense.exec_shell' => array('function' => 'exec_shell_xmlrpc',
490
			'signature' => $exec_shell_sig,
491
			'docstring' => $exec_shell_doc),
492
		'pfsense.exec_php' => array('function' => 'exec_php_xmlrpc',
493
			'signature' => $exec_php_sig,
494
			'docstring' => $exec_php_doc),	
495
		'pfsense.filter_configure' => array('function' => 'filter_configure_xmlrpc',
496
			'signature' => $filter_configure_sig,
497
			'docstring' => $filter_configure_doc),
498
		'pfsense.interfaces_carp_configure' => array('function' => 'interfaces_carp_configure_xmlrpc',
499
			'docstring' => $carp_configure_sig),
500
		'pfsense.backup_config_section' => array('function' => 'backup_config_section_xmlrpc',
501
			'signature' => $backup_config_section_sig,
502
			'docstring' => $backup_config_section_doc),
503
		'pfsense.restore_config_section' => array('function' => 'restore_config_section_xmlrpc',
504
			'signature' => $restore_config_section_sig,
505
			'docstring' => $restore_config_section_doc),
506
		'pfsense.merge_config_section' => array('function' => 'merge_config_section_xmlrpc',
507
			'signature' => $merge_config_section_sig,
508
			'docstring' => $merge_config_section_doc),
509
		'pfsense.merge_installedpackages_section_xmlrpc' => array('function' => 'merge_installedpackages_section_xmlrpc',
510
			'signature' => $merge_config_section_sig,
511
			'docstring' => $merge_config_section_doc),							
512
		'pfsense.check_firmware_version' => array('function' => 'check_firmware_version_xmlrpc',
513
			'signature' => $check_firmware_version_sig,
514
			'docstring' => $check_firmware_version_doc),
515
		'pfsense.host_firmware_version' => array('function' => 'pfsense_firmware_version_xmlrpc',
516
			'signature' => $pfsense_firmware_version_sig,
517
			'docstring' => $host_firmware_version_doc),
518
		'pfsense.reboot' => array('function' => 'reboot_xmlrpc',
519
			'signature' => $reboot_sig,
520
			'docstring' => $reboot_doc),
521
		'pfsense.get_notices' => array('function' => 'get_notices_xmlrpc',
522
			'signature' => $get_notices_sig)
523
        )
524
);
525

    
526
unlock($xmlrpclockkey);
527

    
528
    function array_overlay($a1,$a2)
529
    {
530
        foreach($a1 as $k => $v) {
531
            if(!array_key_exists($k,$a2)) continue;
532
            if(is_array($v) && is_array($a2[$k])){
533
                $a1[$k] = array_overlay($v,$a2[$k]);
534
            }else{
535
                $a1[$k] = $a2[$k];
536
            }
537
        }
538
        return $a1;
539
    }
540

    
541
?>
(246-246/246)