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