1#!/usr/local/bin/perl
2# rpc.cgi
3# Handles remote_foreign_require and remote_foreign_call requests from
4# other webmin servers. State is preserved by starting a process for each
5# session that listens for requests on a named pipe (and dies after a few
6# seconds of inactivity)
7# access{'rpc'}  0=not allowed 1=allowed 2=allowed if root or admin
8
9BEGIN { push(@INC, "."); };
10use WebminCore;
11use POSIX;
12
13&init_config();
14if ($ENV{'REQUEST_METHOD'} eq 'POST') {
15	local $got;
16	local $left = $ENV{'CONTENT_LENGTH'} - length($rawarg);
17	while($left > 0) {
18		read(STDIN, $got, $left) > 0 || last;
19		$rawarg .= $got;
20		$left = $ENV{'CONTENT_LENGTH'} - length($rawarg);
21		}
22	}
23else {
24	$rawarg = $ENV{'QUERY_STRING'};
25	}
26$| = 1;
27print "Content-type: text/plain\n\n";
28
29# Can this user make remote calls?
30%access = &get_module_acl();
31if ($access{'rpc'} == 0 || $access{'rpc'} == 2 &&
32    $base_remote_user ne 'admin' && $base_remote_user ne 'root' &&
33    $base_remote_user ne 'sysadm') {
34	print &serialise_variable( { 'status' => 0 } );
35	exit;
36	}
37$arg = &unserialise_variable($rawarg);
38
39if ($arg->{'newsession'}) {
40	# Need to fork a new session-handler process
41	$fifo1 = &tempname();
42	$fifo2 = &tempname();
43	mkfifo($fifo1, 0700);
44	mkfifo($fifo2, 0700);
45	if (!fork()) {
46		# This is the subprocess where execution really happens
47		$SIG{'ALRM'} = "fifo_timeout";
48		untie(*STDIN);
49		untie(*STDOUT);
50		close(STDIN);
51		close(STDOUT);
52		close(miniserv::SOCK);
53		local $stime = time();
54		local $rcount = 0;
55		while(1) {
56			local ($rawcmd, $cmd, @rv);
57			alarm($rcount ? 360 : 60);
58			open(FIFO, "<$fifo1") || last;
59			while(<FIFO>) {
60				$rawcmd .= $_;
61				}
62			close(FIFO);
63			alarm(0);
64			$cmd = &unserialise_variable($rawcmd);
65			if ($cmd->{'action'} eq 'quit') {
66				# time to end this session (after the reply)
67				@rv = ( { 'time' => time() - $stime } );
68				}
69			elsif ($cmd->{'action'} eq 'require') {
70				# require a library
71				&foreign_require($cmd->{'module'},
72						 $cmd->{'file'});
73				@rv = ( { 'session' => [ $fifo1, $fifo2 ] } );
74				}
75			elsif ($cmd->{'action'} eq 'call') {
76				# execute a function
77				@rv = &foreign_call($cmd->{'module'},
78						    $cmd->{'func'},
79						    @{$cmd->{'args'}});
80				}
81			elsif ($cmd->{'action'} eq 'eval') {
82				# eval some perl code
83				if ($cmd->{'module'}) {
84					@rv = eval <<EOF;
85package $cmd->{'module'};
86$cmd->{'code'}
87EOF
88					}
89				else {
90					@rv = eval $cmd->{'code'};
91					}
92				}
93			open(FIFO, ">$fifo2");
94			if (@rv == 1) {
95				print FIFO &serialise_variable(
96					{ 'status' => 1, 'rv' => $rv[0] } );
97				}
98			else {
99				print FIFO &serialise_variable(
100					{ 'status' => 1, 'arv' => \@rv } );
101				}
102			close(FIFO);
103			last if ($cmd->{'action'} eq 'quit');
104			$rcount++;
105			}
106		unlink($fifo1);
107		unlink($fifo2);
108		exit;
109		}
110	$session = [ $fifo1, $fifo2 ];
111	}
112else {
113	# Use the provided session id
114	$session = $arg->{'session'};
115	}
116
117if ($arg->{'action'} eq 'ping') {
118	# Just respond with an OK
119	print &serialise_variable( { 'status' => 1 } );
120	}
121elsif ($arg->{'action'} eq 'check') {
122	# Check if some module is supported
123	print &serialise_variable(
124		{ 'status' => 1,
125		  'rv' => &foreign_check($arg->{'module'}, undef, undef,
126					 $arg->{'api'}) } );
127	}
128elsif ($arg->{'action'} eq 'config') {
129	# Get the config for some module
130	local %config = &foreign_config($arg->{'module'});
131	print &serialise_variable(
132		{ 'status' => 1, 'rv' => \%config } );
133	}
134elsif ($arg->{'action'} eq 'write') {
135	# Transfer data to a local temp file
136	local $file = $arg->{'file'} ? $arg->{'file'} :
137		      $arg->{'name'} ? &tempname($arg->{'name'}) :
138				       &tempname();
139	open(FILE, ">$file");
140	print FILE $arg->{'data'};
141	close(FILE);
142	print &serialise_variable(
143		{ 'status' => 1, 'rv' => $file } );
144	}
145elsif ($arg->{'action'} eq 'read') {
146	# Transfer data from a file
147	local ($data, $got);
148	open(FILE, "<$arg->{'file'}");
149	while(read(FILE, $got, 1024) > 0) {
150		$data .= $got;
151		}
152	close(FILE);
153	print &serialise_variable(
154		{ 'status' => 1, 'rv' => $data } );
155	}
156else {
157	# Pass the request on to the subprocess
158	open(FIFO, ">$session->[0]");
159	print FIFO $rawarg;
160	close(FIFO);
161	open(FIFO, "<$session->[1]");
162	while(<FIFO>) {
163		print;
164		}
165	close(FIFO);
166	}
167
168sub fifo_timeout
169{
170unlink($fifo1);
171unlink($fifo2);
172exit;
173}
174
175