1 // This file is part of BOINC.
2 // http://boinc.berkeley.edu
3 // Copyright (C) 2008 University of California
4 //
5 // BOINC is free software; you can redistribute it and/or modify it
6 // under the terms of the GNU Lesser General Public License
7 // as published by the Free Software Foundation,
8 // either version 3 of the License, or (at your option) any later version.
9 //
10 // BOINC is distributed in the hope that it will be useful,
11 // but WITHOUT ANY WARRANTY; without even the implied warranty of
12 // MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.
13 // See the GNU Lesser General Public License for more details.
14 //
15 // You should have received a copy of the GNU Lesser General Public License
16 // along with BOINC.  If not, see <http://www.gnu.org/licenses/>.
17 
18 // The plumbing of GUI RPC, server side
19 // (but not the actual RPCs)
20 
21 #include "cpp.h"
22 
23 #ifdef _WIN32
24 #include "boinc_win.h"
25 #else
26 #include "config.h"
27 #include <cstdio>
28 #include <cstddef>
29 #if HAVE_UNISTD_H
30 #include <unistd.h>
31 #endif
32 #include <cerrno>
33 #if HAVE_SYS_SOCKET_H
34 #include <sys/socket.h>
35 #endif
36 #if HAVE_SYS_STAT_H
37 #include <sys/stat.h>
38 #endif
39 #include <sys/un.h>
40 #include <vector>
41 #include <cstring>
42 #if HAVE_NETINET_IN_H
43 #include <netinet/in.h>
44 #endif
45 #if HAVE_NETINET_TCP_H
46 #include <netinet/tcp.h>
47 #endif
48 #if HAVE_ARPA_INET_H
49 #include <arpa/inet.h>
50 #endif
51 #if HAVE_FCNTL_H
52 #include <fcntl.h>
53 #endif
54 #endif
55 
56 #include "error_numbers.h"
57 #include "filesys.h"
58 #include "md5_file.h"
59 #include "network.h"
60 #include "str_replace.h"
61 #include "str_util.h"
62 #include "thread.h"
63 #include "util.h"
64 
65 #include "file_names.h"
66 #include "client_msgs.h"
67 #include "client_state.h"
68 #include "sandbox.h"
69 
70 using std::string;
71 using std::vector;
72 
GUI_RPC_CONN(int s)73 GUI_RPC_CONN::GUI_RPC_CONN(int s) :
74     xp(&mfin),
75     get_project_config_op(&gui_http),
76     lookup_account_op(&gui_http),
77     create_account_op(&gui_http)
78 {
79     sock = s;
80     mfout.init_mfile(&mout);
81     safe_strcpy(request_msg,"");
82     request_nbytes = 0;
83     safe_strcpy(nonce,"");
84     auth_needed = false;
85     got_auth1 = false;
86     got_auth2 = false;
87     sent_unauthorized = false;
88     is_local = false;
89     quit_flag = false;
90     au_ss_state = AU_SS_INIT;
91     au_mgr_state = AU_MGR_INIT;
92 
93     notice_refresh = false;
94 }
95 
~GUI_RPC_CONN()96 GUI_RPC_CONN::~GUI_RPC_CONN() {
97     boinc_close_socket(sock);
98 }
99 
GUI_RPC_CONN_SET()100 GUI_RPC_CONN_SET::GUI_RPC_CONN_SET() {
101     remote_hosts_file_exists = false;
102     lsock = -1;
103     time_of_last_rpc_needing_network = 0;
104     safe_strcpy(password,"");
105 }
106 
poll()107 bool GUI_RPC_CONN_SET::poll() {
108     unsigned int i;
109     bool action = false;
110     for (i=0; i<gui_rpcs.size(); i++) {
111         action |= gui_rpcs[i]->gui_http.poll();
112     }
113     return action;
114 }
115 
recent_rpc_needs_network(double interval)116 bool GUI_RPC_CONN_SET::recent_rpc_needs_network(double interval) {
117     if (!time_of_last_rpc_needing_network) return false;
118     if (gstate.now < time_of_last_rpc_needing_network + interval) return true;
119     return false;
120 }
121 
122 // read the GUI RPC password from gui_rpc_auth.cfg;
123 // create one if missing.
124 //
get_password()125 void GUI_RPC_CONN_SET::get_password() {
126     int retval;
127 
128     safe_strcpy(password, "");
129     FILE* f = fopen(GUI_RPC_PASSWD_FILE, "r");
130     if (f) {
131         if (fgets(password, 256, f)) {
132             strip_whitespace(password);
133         }
134         fclose(f);
135         if (!strlen(password)) {
136             msg_printf(NULL, MSG_INFO,
137                 "gui_rpc_auth.cfg is empty - no GUI RPC password protection"
138             );
139         }
140         return;
141     }
142 
143     // if no password file, make a random password
144     //
145     retval = make_random_string(password);
146     if (retval) {
147         if (cc_config.os_random_only) {
148             msg_printf(
149                 NULL, MSG_INTERNAL_ERROR,
150                 "OS random string generation failed, exiting"
151             );
152             exit(1);
153         }
154         gstate.host_info.make_random_string("guirpc", password);
155     }
156 
157     // try to write it to the file.
158     // if fail, just return
159     //
160     f = fopen(GUI_RPC_PASSWD_FILE, "w");
161     if (!f) {
162         msg_printf(NULL, MSG_USER_ALERT,
163             "Can't open gui_rpc_auth.cfg - fix permissions"
164         );
165     } else {
166         retval = fputs(password, f);
167         fclose(f);
168         if (retval == EOF) {
169             msg_printf(NULL, MSG_USER_ALERT,
170                 "Can't write gui_rpc_auth.cfg - fix permissions"
171             );
172         }
173     }
174 #ifndef _WIN32
175     // if someone can read the password,
176     // they can cause code to execute as this user.
177     // So better protect it.
178     //
179     if (g_use_sandbox) {
180         // Allow group access so authorized administrator can modify it
181         chmod(GUI_RPC_PASSWD_FILE, S_IRUSR|S_IWUSR | S_IRGRP | S_IWGRP);
182     } else {
183         chmod(GUI_RPC_PASSWD_FILE, S_IRUSR|S_IWUSR);
184     }
185 #endif
186 }
187 
get_allowed_hosts()188 int GUI_RPC_CONN_SET::get_allowed_hosts() {
189     int retval;
190     sockaddr_storage ip_addr;
191     char buf[256];
192 
193     allowed_remote_ip_addresses.clear();
194     remote_hosts_file_exists = false;
195 
196     // scan remote_hosts.cfg, convert names to IP addresses
197     //
198     FILE* f = fopen(REMOTEHOST_FILE_NAME, "r");
199     if (f) {
200         remote_hosts_file_exists = true;
201         if (log_flags.gui_rpc_debug) {
202             msg_printf(0, MSG_INFO,
203                 "[gui_rpc] found allowed hosts list"
204             );
205         }
206 
207         // read in each line, if it is not a comment
208         // then resolve the address and add to our allowed list
209         //
210         while (fgets(buf, 256, f)) {
211             strip_whitespace(buf);
212             if (!(buf[0] =='#' || buf[0] == ';') && strlen(buf) > 0 ) {
213                 retval = resolve_hostname_or_ip_addr(buf, ip_addr);
214                 if (retval) {
215                     msg_printf(NULL, MSG_INFO,
216                         "Can't resolve hostname in remote_hosts.cfg: %s",
217                         buf
218                     );
219                 } else {
220                     allowed_remote_ip_addresses.push_back(ip_addr);
221                 }
222             }
223         }
224         fclose(f);
225     }
226     return 0;
227 }
228 
insert(GUI_RPC_CONN * p)229 int GUI_RPC_CONN_SET::insert(GUI_RPC_CONN* p) {
230     gui_rpcs.push_back(p);
231     return 0;
232 }
233 
init_unix_domain()234 int GUI_RPC_CONN_SET::init_unix_domain() {
235 #if !defined(_WIN32)
236     struct sockaddr_un addr;
237     get_password();
238     int retval = boinc_socket(lsock, AF_UNIX);
239     if (retval) {
240         msg_printf(NULL, MSG_INTERNAL_ERROR,
241             "Failed to create Unix domain socket: %s", boincerror(retval)
242         );
243         return retval;
244     }
245     addr.sun_family = AF_UNIX;
246 #ifdef ANDROID
247     // bind socket in abstract address space, i.e. start with 0 byte
248     addr.sun_path[0] = '\0';
249     // using app specific socket name instead of GUI_RPC_FILE defintion
250     // to avoid interference with other BOINC based Android apps.
251     strcpy(&addr.sun_path[1], "edu_berkeley_boinc_client_socket");
252     socklen_t len = offsetof(struct sockaddr_un, sun_path) + 1 + strlen(&addr.sun_path[1]);
253 #else
254     // NOTE: if we ever add Mac OS X support, need to change this
255     //
256 #ifdef __APPLE__
257     addr.sun_len = sizeof(addr);
258 #endif
259     strcpy(addr.sun_path, GUI_RPC_FILE);
260     socklen_t len = offsetof(sockaddr_un, sun_path) + strlen(GUI_RPC_FILE);
261 #endif
262     unlink(GUI_RPC_FILE);
263     if (bind(lsock, (struct sockaddr*)&addr, len) < 0) {
264         msg_printf(NULL, MSG_INTERNAL_ERROR,
265             "Failed to bind Unix domain socket"
266         );
267         boinc_close_socket(lsock);
268         return ERR_BIND;
269     }
270     retval = listen(lsock, 999);
271     if (retval < 0) {
272         msg_printf(NULL, MSG_INTERNAL_ERROR,
273             "Failed to listen on Unix domain socket"
274         );
275         boinc_close_socket(lsock);
276         return ERR_LISTEN;
277     }
278 #endif
279     return 0;
280 }
281 
282 // If the client runs at boot time,
283 // it may be a while (~10 sec) before the DNS system is working.
284 // If this returns an error,
285 // it will get called once a second for up to 30 seconds.
286 // On the last call, "last_time" is set; print error messages then.
287 //
init_tcp(bool last_time)288 int GUI_RPC_CONN_SET::init_tcp(bool last_time) {
289     sockaddr_in addr;
290     int retval;
291     bool first = true;
292 
293     if (first) {
294         get_password();
295         get_allowed_hosts();
296         first = false;
297     }
298 
299     retval = boinc_socket(lsock, AF_INET);
300     if (retval) {
301         if (last_time) {
302             msg_printf(NULL, MSG_INTERNAL_ERROR,
303                 "Failed to create TCP socket: %s", boincerror(retval)
304             );
305         }
306         return retval;
307     }
308 
309     memset(&addr, 0, sizeof(addr));
310     addr.sin_family = AF_INET;
311 
312     if (gstate.cmdline_gui_rpc_port) {
313         addr.sin_port = htons(gstate.cmdline_gui_rpc_port);
314     } else {
315         addr.sin_port = htons(GUI_RPC_PORT);
316     }
317 
318 #ifdef __APPLE__
319     addr.sin_addr.s_addr = htonl(INADDR_ANY);
320 #else
321     if (cc_config.allow_remote_gui_rpc || remote_hosts_file_exists) {
322         addr.sin_addr.s_addr = htonl(INADDR_ANY);
323         if (log_flags.gui_rpc_debug) {
324             msg_printf(NULL, MSG_INFO, "[gui_rpc] Remote control allowed");
325         }
326     } else {
327         addr.sin_addr.s_addr = inet_addr("127.0.0.1");
328         if (log_flags.gui_rpc_debug) {
329             msg_printf(NULL, MSG_INFO, "[gui_rpc] Local control only allowed");
330         }
331     }
332 #endif
333 
334     int one = 1;
335     setsockopt(lsock, SOL_SOCKET, SO_REUSEADDR, (char*)&one, 4);
336 
337     retval = bind(lsock, (const sockaddr*)(&addr), (BOINC_SOCKLEN_T)sizeof(addr));
338     if (retval) {
339 #ifndef _WIN32
340         retval = errno;     // Display the real error code
341 #endif
342         if (last_time) {
343             msg_printf(NULL, MSG_INTERNAL_ERROR,
344                 "GUI RPC bind to port %d failed: %d", htons(addr.sin_port), retval
345             );
346         }
347         boinc_close_socket(lsock);
348         lsock = -1;
349         return ERR_BIND;
350     }
351     if (log_flags.gui_rpc_debug) {
352         msg_printf(NULL, MSG_INFO, "[gui_rpc] Listening on port %d", htons(addr.sin_port));
353     }
354 
355     retval = listen(lsock, 999);
356     if (retval) {
357         if (last_time) {
358             msg_printf(NULL, MSG_INTERNAL_ERROR,
359                 "GUI RPC listen failed: %d", retval
360             );
361         }
362         boinc_close_socket(lsock);
363         lsock = -1;
364         return ERR_LISTEN;
365     }
366     return 0;
367 }
368 
show_connect_error(sockaddr_storage & s)369 static void show_connect_error(sockaddr_storage& s) {
370     static double last_time=0;
371     static int count=0;
372 
373     if (last_time == 0) {
374         last_time = gstate.now;
375         count = 1;
376     } else {
377         if (!gstate.clock_change && gstate.now - last_time < CONNECT_ERROR_PERIOD) {
378             count++;
379             return;
380         }
381         last_time = gstate.now;
382     }
383     char buf[256];
384 #ifdef _WIN32
385     sockaddr_in* sin = (sockaddr_in*)&s;
386     safe_strcpy(buf, inet_ntoa(sin->sin_addr));
387 #else
388     inet_ntop(s.ss_family, &s, buf, 256);
389 #endif
390     msg_printf(NULL, MSG_INFO,
391         "GUI RPC request from non-allowed address %s",
392         buf
393     );
394     if (count > 1) {
395         msg_printf(
396             NULL, MSG_INFO,
397             "%d connections rejected in last 10 minutes",
398             count
399         );
400     }
401     count = 0;
402 }
403 
get_fdset(FDSET_GROUP & fg,FDSET_GROUP & all)404 void GUI_RPC_CONN_SET::get_fdset(FDSET_GROUP& fg, FDSET_GROUP& all) {
405     unsigned int i;
406     GUI_RPC_CONN* gr;
407 
408     if (lsock < 0) return;
409     for (i=0; i<gui_rpcs.size(); i++) {
410         gr = gui_rpcs[i];
411         int s = gr->sock;
412         FD_SET(s, &fg.read_fds);
413         FD_SET(s, &fg.exc_fds);
414         if (s > fg.max_fd) fg.max_fd = s;
415 
416         FD_SET(s, &all.read_fds);
417         FD_SET(s, &all.exc_fds);
418         if (s > all.max_fd) all.max_fd = s;
419     }
420     FD_SET(lsock, &fg.read_fds);
421     if (lsock > fg.max_fd) fg.max_fd = lsock;
422     FD_SET(lsock, &all.read_fds);
423     if (lsock > all.max_fd) all.max_fd = lsock;
424 }
425 
check_allowed_list(sockaddr_storage & peer_ip)426 bool GUI_RPC_CONN_SET::check_allowed_list(sockaddr_storage& peer_ip) {
427     vector<sockaddr_storage>::iterator remote_iter = allowed_remote_ip_addresses.begin();
428     while (remote_iter != allowed_remote_ip_addresses.end() ) {
429         if (same_ip_addr(peer_ip, *remote_iter)) {
430             return true;
431         }
432         ++remote_iter;
433     }
434     return false;
435 }
436 
got_select(FDSET_GROUP & fg)437 void GUI_RPC_CONN_SET::got_select(FDSET_GROUP& fg) {
438     int sock, retval;
439     vector<GUI_RPC_CONN*>::iterator iter;
440     GUI_RPC_CONN* gr;
441 
442     if (lsock < 0) return;
443 
444     if (FD_ISSET(lsock, &fg.read_fds)) {
445         struct sockaddr_storage addr;
446 
447         // For unknown reasons, the FD_ISSET() above succeeds
448         // after a SIGTERM, SIGHUP, SIGINT or SIGQUIT is received,
449         // even if there is no data available on the socket.
450         // This causes the accept() call to block, preventing the main
451         // loop from processing the exit request.
452         // This is a workaround for that problem.
453         //
454         if (gstate.requested_exit) {
455             return;
456         }
457 
458         BOINC_SOCKLEN_T addr_len = sizeof(addr);
459         sock = accept(lsock, (struct sockaddr*)&addr, (BOINC_SOCKLEN_T*)&addr_len);
460         if (sock == -1) {
461             return;
462         }
463 
464         // apps shouldn't inherit the socket!
465         //
466 #ifndef _WIN32
467         fcntl(sock, F_SETFD, FD_CLOEXEC);
468 #endif
469 
470         bool host_allowed;
471 
472         // accept the connection if:
473         // 1) allow_remote_gui_rpc is set or
474         // 2) client host is included in "remote_hosts" file or
475         // 3) client is on localhost
476         //
477         if (gstate.gui_rpc_unix_domain) {
478             host_allowed = true;
479         } else if (cc_config.allow_remote_gui_rpc) {
480             host_allowed = true;
481         } else if (is_localhost(addr)) {
482             host_allowed = true;
483         } else {
484             // reread host file because IP addresses might have changed
485             //
486             get_allowed_hosts();
487             host_allowed = check_allowed_list(addr);
488         }
489 
490         if (!host_allowed) {
491             show_connect_error(addr);
492             boinc_close_socket(sock);
493         } else {
494             gr = new GUI_RPC_CONN(sock);
495             if (strlen(password)) {
496                 gr->auth_needed = true;
497             }
498             if (gstate.gui_rpc_unix_domain) {
499                 gr->is_local = true;
500             } else {
501                 gr->is_local = is_localhost(addr);
502             }
503             if (log_flags.gui_rpc_debug) {
504                 msg_printf(0, MSG_INFO,
505                     "[gui_rpc] got new GUI RPC connection"
506                 );
507             }
508             insert(gr);
509         }
510     }
511 
512     // delete connections with failed sockets
513     //
514     iter = gui_rpcs.begin();
515     while (iter != gui_rpcs.end()) {
516         gr = *iter;
517         if (FD_ISSET(gr->sock, &fg.exc_fds)) {
518             delete gr;
519             iter = gui_rpcs.erase(iter);
520             continue;
521         }
522         ++iter;
523     }
524 
525     // handle RPCs on connections with pending requests
526     //
527     iter = gui_rpcs.begin();
528     while (iter != gui_rpcs.end()) {
529         gr = *iter;
530         if (FD_ISSET(gr->sock, &fg.read_fds)) {
531             retval = gr->handle_rpc();
532             if (retval) {
533                 if (log_flags.gui_rpc_debug) {
534                     msg_printf(NULL, MSG_INFO,
535                         "[gui_rpc] handler returned %d, closing socket\n",
536                         retval
537                     );
538                 }
539                 delete gr;
540                 iter = gui_rpcs.erase(iter);
541                 continue;
542             }
543         }
544         ++iter;
545     }
546 }
547 
548 // called when client is shutting down
549 //
close()550 void GUI_RPC_CONN_SET::close() {
551     if (log_flags.gui_rpc_debug) {
552         msg_printf(NULL, MSG_INFO,
553             "[gui_rpc] closing GUI RPC listening socket %d\n", lsock
554         );
555     }
556     if (lsock >= 0) {
557         boinc_close_socket(lsock);
558         lsock = -1;
559     }
560     for (unsigned int i=0; i<gui_rpcs.size(); i++) {
561         delete gui_rpcs[i];
562     }
563     gui_rpcs.clear();
564 }
565 
566 // this is called when we're ready to auto-update;
567 // set flags to send quit messages to screensaver and local manager
568 //
send_quits()569 void GUI_RPC_CONN_SET::send_quits() {
570     for (unsigned int i=0; i<gui_rpcs.size(); i++) {
571         GUI_RPC_CONN* gr = gui_rpcs[i];
572         if (gr->au_ss_state == AU_SS_GOT) {
573             gr->au_ss_state = AU_SS_QUIT_REQ;
574         }
575         if (gr->au_mgr_state == AU_MGR_GOT && gr->is_local) {
576             gr->au_mgr_state = AU_MGR_QUIT_REQ;
577         }
578     }
579 }
580 
581 // check whether the quit messages have actually been sent
582 //
quits_sent()583 bool GUI_RPC_CONN_SET::quits_sent() {
584     for (unsigned int i=0; i<gui_rpcs.size(); i++) {
585         GUI_RPC_CONN* gr = gui_rpcs[i];
586         if (gr->au_ss_state == AU_SS_QUIT_REQ) return false;
587         if (gr->au_mgr_state == AU_MGR_QUIT_REQ) return false;
588     }
589     return true;
590 }
591 
gui_rpc_handler(void * p)592 void* gui_rpc_handler(void* p) {
593     THREAD& thread = *((THREAD*)p);
594     GUI_RPC_CONN& grc = *((GUI_RPC_CONN*)thread.arg);
595     while (1) {
596         if(grc.handle_rpc()){
597             break;
598         }
599         if (grc.quit_flag) {
600             break;
601         }
602     }
603     return NULL;
604 }
605