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