Branch data Line data Source code
1 : : /*
2 : : SSL server program listens on a port, accepts client connection, reads
3 : : the data into a temporary file, calls the systemtap translator and
4 : : then transmits the resulting file back to the client.
5 : :
6 : : Copyright (C) 2011-2012 Red Hat Inc.
7 : :
8 : : This file is part of systemtap, and is free software. You can
9 : : redistribute it and/or modify it under the terms of the GNU General Public
10 : : License as published by the Free Software Foundation; either version 2 of the
11 : : License, or (at your option) any later version.
12 : :
13 : : This program is distributed in the hope that it will be useful,
14 : : but WITHOUT ANY WARRANTY; without even the implied warranty of
15 : : MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
16 : : GNU General Public License for more details.
17 : :
18 : : You should have received a copy of the GNU General Public License
19 : : along with this program. If not, see <http://www.gnu.org/licenses/>.
20 : : */
21 : : #include "config.h"
22 : :
23 : : #include <fstream>
24 : : #include <string>
25 : : #include <cerrno>
26 : : #include <cassert>
27 : : #include <climits>
28 : : #include <iostream>
29 : : #include <map>
30 : :
31 : : extern "C" {
32 : : #include <unistd.h>
33 : : #include <getopt.h>
34 : : #include <wordexp.h>
35 : : #include <glob.h>
36 : : #include <fcntl.h>
37 : : #include <sys/stat.h>
38 : : #include <sys/utsname.h>
39 : : #include <sys/types.h>
40 : : #include <pwd.h>
41 : : #include <semaphore.h>
42 : :
43 : : #include <nspr.h>
44 : : #include <ssl.h>
45 : : #include <nss.h>
46 : : #include <keyhi.h>
47 : : #include <regex.h>
48 : :
49 : : #if HAVE_AVAHI
50 : : #include <avahi-client/publish.h>
51 : : #include <avahi-common/alternative.h>
52 : : #include <avahi-common/thread-watch.h>
53 : : #include <avahi-common/malloc.h>
54 : : #include <avahi-common/error.h>
55 : : #endif
56 : : }
57 : :
58 : : #include "util.h"
59 : : #include "nsscommon.h"
60 : : #include "cscommon.h"
61 : : #include "cmdline.h"
62 : :
63 : : using namespace std;
64 : :
65 : : static void cleanup ();
66 : : static PRStatus spawn_and_wait (const vector<string> &argv,
67 : : const char* fd0, const char* fd1, const char* fd2,
68 : : const char *pwd, const vector<string>& envVec = vector<string> ());
69 : :
70 : : /* getopt variables */
71 : : extern int optind;
72 : :
73 : : /* File scope statics. Set during argument parsing and initialization. */
74 : : static bool use_db_password;
75 : : static unsigned short port;
76 : : static long max_threads;
77 : 5 : static string cert_db_path;
78 : 5 : static string stap_options;
79 : 5 : static string uname_r;
80 : 5 : static string arch;
81 : 5 : static string cert_serial_number;
82 : 5 : static string B_options;
83 : 5 : static string I_options;
84 : 5 : static string R_option;
85 : 5 : static string D_options;
86 : : static bool keep_temp;
87 : :
88 : : sem_t sem_client;
89 : : static int pending_interrupts;
90 : : #define CONCURRENCY_TIMEOUT_S 3
91 : :
92 : : // Message handling.
93 : : // Server_error messages are printed to stderr and logged, if requested.
94 : : static void
95 : 5 : server_error (const string &msg, int logit = true)
96 : : {
97 : 5 : cerr << msg << endl << flush;
98 : : // Log it, but avoid repeated messages to the terminal.
99 [ + - ][ + - ]: 5 : if (logit && log_ok ())
[ + - ]
100 : 5 : log (msg);
101 : 5 : }
102 : :
103 : : // client_error messages are treated as server errors and also printed to the client's stderr.
104 : : static void
105 : 2 : client_error (const string &msg, string stapstderr)
106 : : {
107 : 2 : server_error (msg);
108 [ + - ]: 2 : if (! stapstderr.empty ())
109 : : {
110 [ + - ]: 2 : ofstream errfile;
111 [ + - ][ + - ]: 2 : errfile.open (stapstderr.c_str (), ios_base::app);
112 [ + - ][ - + ]: 2 : if (! errfile.good ())
113 : 0 : server_error (_F("Could not open client stderr file %s: %s", stapstderr.c_str (),
114 [ # # # # ]: 0 : strerror (errno)));
[ # # ][ # # ]
115 : : else
116 [ + - ][ + - ]: 2 : errfile << "Server: " << msg << endl;
[ + - ][ + - ]
117 : : // NB: No need to close errfile
118 : : }
119 : 2 : }
120 : :
121 : : // Messages from the nss common code are treated as server errors.
122 : : extern "C"
123 : : void
124 : 0 : nsscommon_error (const char *msg, int logit)
125 : : {
126 [ # # ][ # # ]: 0 : server_error (msg, logit);
[ # # ]
127 : 0 : }
128 : :
129 : : // Fatal errors are treated as server errors but also result in termination
130 : : // of the server.
131 : : static void
132 : 0 : fatal (const string &msg)
133 : : {
134 : 0 : server_error (msg);
135 : 0 : cleanup ();
136 : 0 : exit (1);
137 : : }
138 : :
139 : : // Argument handling
140 : : static void
141 : 0 : process_a (const string &arg)
142 : : {
143 : 0 : arch = arg;
144 [ # # ]: 0 : stap_options += " -a " + arg;
145 : 0 : }
146 : :
147 : : static void
148 : 0 : process_r (const string &arg)
149 : : {
150 [ # # ]: 0 : if (arg[0] == '/') // fully specified path
151 [ # # ]: 0 : uname_r = kernel_release_from_build_tree (arg);
152 : : else
153 : 0 : uname_r = arg;
154 [ # # ]: 0 : stap_options += " -r " + arg; // Pass the argument to stap directly.
155 : 0 : }
156 : :
157 : : static void
158 : 5 : process_log (const char *arg)
159 : : {
160 : 5 : start_log (arg);
161 : 5 : }
162 : :
163 : : static void
164 : 5 : parse_options (int argc, char **argv)
165 : : {
166 : : // Examine the command line. This is the command line for us (stap-serverd) not the command
167 : : // line for spawned stap instances.
168 : 5 : optind = 1;
169 : 6 : while (true)
170 : : {
171 : : char *num_endptr;
172 : : long port_tmp;
173 : : // NB: The values of these enumerators must not conflict with the values of ordinary
174 : : // characters, since those are returned by getopt_long for short options.
175 : : enum {
176 : : LONG_OPT_PORT = 256,
177 : : LONG_OPT_SSL,
178 : : LONG_OPT_LOG,
179 : : LONG_OPT_MAXTHREADS
180 : : };
181 : : static struct option long_options[] = {
182 : : { "port", 1, NULL, LONG_OPT_PORT },
183 : : { "ssl", 1, NULL, LONG_OPT_SSL },
184 : : { "log", 1, NULL, LONG_OPT_LOG },
185 : : { "max-threads", 1, NULL, LONG_OPT_MAXTHREADS },
186 : : { NULL, 0, NULL, 0 }
187 : : };
188 : 11 : int grc = getopt_long (argc, argv, "a:B:D:I:kPr:R:", long_options, NULL);
189 [ + + ]: 11 : if (grc < 0)
190 : : break;
191 [ - - - - : 6 : switch (grc)
- - - - -
- + + -
- ]
192 : : {
193 : : case 'a':
194 [ # # ][ # # ]: 0 : process_a (optarg);
[ # # ]
195 : 0 : break;
196 : : case 'B':
197 [ # # ][ # # ]: 0 : B_options += string (" -") + (char)grc + optarg;
[ # # ][ # # ]
[ # # ][ # # ]
[ # # ]
198 [ # # ][ # # ]: 0 : stap_options += string (" -") + (char)grc + optarg;
[ # # ][ # # ]
[ # # ][ # # ]
[ # # ]
199 : 0 : break;
200 : : case 'D':
201 [ # # ][ # # ]: 0 : D_options += string (" -") + (char)grc + optarg;
[ # # ][ # # ]
[ # # ][ # # ]
[ # # ]
202 [ # # ][ # # ]: 0 : stap_options += string (" -") + (char)grc + optarg;
[ # # ][ # # ]
[ # # ][ # # ]
[ # # ]
203 : 0 : break;
204 : : case 'I':
205 [ # # ][ # # ]: 0 : I_options += string (" -") + (char)grc + optarg;
[ # # ][ # # ]
[ # # ][ # # ]
[ # # ]
206 [ # # ][ # # ]: 0 : stap_options += string (" -") + (char)grc + optarg;
[ # # ][ # # ]
[ # # ][ # # ]
[ # # ]
207 : 0 : break;
208 : : case 'k':
209 : 0 : keep_temp = true;
210 : 0 : break;
211 : : case 'P':
212 : 0 : use_db_password = true;
213 : 0 : break;
214 : : case 'r':
215 [ # # ][ # # ]: 0 : process_r (optarg);
[ # # ]
216 : 0 : break;
217 : : case 'R':
218 [ # # ][ # # ]: 0 : R_option = string (" -") + (char)grc + optarg;
[ # # ][ # # ]
[ # # ][ # # ]
[ # # ]
219 [ # # ][ # # ]: 0 : stap_options += string (" -") + (char)grc + optarg;
[ # # ][ # # ]
[ # # ][ # # ]
[ # # ]
220 : 0 : break;
221 : : case LONG_OPT_PORT:
222 : 0 : port_tmp = strtol (optarg, &num_endptr, 10);
223 [ # # ]: 0 : if (*num_endptr != '\0')
224 [ # # ][ # # ]: 0 : fatal (_F("%s: cannot parse number '--port=%s'", argv[0], optarg));
[ # # ]
225 [ # # ][ # # ]: 0 : else if (port_tmp < 0 || port_tmp > 65535)
226 : 0 : fatal (_F("%s: invalid entry: port must be between 0 and 65535 '--port=%s'", argv[0],
227 [ # # ][ # # ]: 0 : optarg));
[ # # ]
228 : : else
229 : 0 : port = (unsigned short) port_tmp;
230 : 0 : break;
231 : : case LONG_OPT_SSL:
232 [ # # ]: 0 : cert_db_path = optarg;
233 : 0 : break;
234 : : case LONG_OPT_LOG:
235 [ + - ]: 5 : process_log (optarg);
236 : 5 : break;
237 : : case LONG_OPT_MAXTHREADS:
238 : 1 : max_threads = strtol (optarg, &num_endptr, 0);
239 [ - + ]: 1 : if (*num_endptr != '\0')
240 [ # # ][ # # ]: 0 : fatal (_F("%s: cannot parse number '--max-threads=%s'", argv[0], optarg));
[ # # ]
241 [ - + ]: 1 : else if (max_threads < 0)
242 : 0 : fatal (_F("%s: invalid entry: max threads must not be negative '--max-threads=%s'",
243 [ # # ][ # # ]: 0 : argv[0], optarg));
[ # # ]
244 : 1 : break;
245 : : case '?':
246 : : // Invalid/unrecognized option given. Message has already been issued.
247 : 0 : break;
248 : : default:
249 : : // Reached when one added a getopt option but not a corresponding switch/case:
250 [ # # ]: 0 : if (optarg)
251 [ # # ][ # # ]: 0 : server_error (_F("%s: unhandled option '%c %s'", argv[0], (char)grc, optarg));
[ # # ]
252 : : else
253 [ # # ][ # # ]: 0 : server_error (_F("%s: unhandled option '%c'", argv[0], (char)grc));
[ # # ]
254 : 0 : break;
255 : : }
256 : : }
257 : :
258 [ - + ]: 5 : for (int i = optind; i < argc; i++)
259 [ # # ]: 0 : server_error (_F("%s: unrecognized argument '%s'", argv[0], argv[i]));
260 : 5 : }
261 : :
262 : : static string
263 : 5 : server_cert_file ()
264 : : {
265 [ + - ]: 5 : return server_cert_db_path () + "/stap.cert";
266 : : }
267 : :
268 : : // Signal handling. When an interrupt is received, kill any spawned processes
269 : : // and exit.
270 : : extern "C"
271 : : void
272 : 5 : handle_interrupt (int sig)
273 : : {
274 : 5 : pending_interrupts++;
275 [ - + ]: 5 : if(pending_interrupts >= 2)
276 : : {
277 [ # # ]: 0 : log (_F("Received another signal %d, exiting (forced)", sig));
278 : 0 : _exit(0);
279 : : }
280 [ + - ]: 5 : log (_F("Received signal %d, exiting", sig));
281 : 5 : }
282 : :
283 : : static void
284 : 5 : setup_signals (sighandler_t handler)
285 : : {
286 : : struct sigaction sa;
287 : :
288 : 5 : memset(&sa, 0, sizeof(sa));
289 : 5 : sa.sa_handler = handler;
290 : 5 : sigemptyset (&sa.sa_mask);
291 [ + - ]: 5 : if (handler != SIG_IGN)
292 : : {
293 : 5 : sigaddset (&sa.sa_mask, SIGHUP);
294 : 5 : sigaddset (&sa.sa_mask, SIGPIPE);
295 : 5 : sigaddset (&sa.sa_mask, SIGINT);
296 : 5 : sigaddset (&sa.sa_mask, SIGTERM);
297 : 5 : sigaddset (&sa.sa_mask, SIGTTIN);
298 : 5 : sigaddset (&sa.sa_mask, SIGTTOU);
299 : 5 : sigaddset (&sa.sa_mask, SIGXFSZ);
300 : 5 : sigaddset (&sa.sa_mask, SIGXCPU);
301 : : }
302 : 5 : sa.sa_flags = SA_RESTART;
303 : :
304 : 5 : sigaction (SIGHUP, &sa, NULL);
305 : 5 : sigaction (SIGPIPE, &sa, NULL);
306 : 5 : sigaction (SIGINT, &sa, NULL);
307 : 5 : sigaction (SIGTERM, &sa, NULL);
308 : 5 : sigaction (SIGTTIN, &sa, NULL);
309 : 5 : sigaction (SIGTTOU, &sa, NULL);
310 : 5 : sigaction (SIGXFSZ, &sa, NULL);
311 : 5 : sigaction (SIGXCPU, &sa, NULL);
312 : 5 : }
313 : :
314 : : #if HAVE_AVAHI
315 : : static AvahiEntryGroup *avahi_group = NULL;
316 : : static AvahiThreadedPoll *avahi_threaded_poll = NULL;
317 : : static char *avahi_service_name = NULL;
318 : : static AvahiClient *avahi_client = 0;
319 : :
320 : : static void create_services (AvahiClient *c);
321 : :
322 : : static void
323 : 15 : entry_group_callback (
324 : : AvahiEntryGroup *g,
325 : : AvahiEntryGroupState state,
326 : : AVAHI_GCC_UNUSED void *userdata
327 : : ) {
328 [ + + ][ - + ]: 15 : assert(g == avahi_group || avahi_group == NULL);
329 : 15 : avahi_group = g;
330 : :
331 : : // Called whenever the entry group state changes.
332 [ + - - + : 15 : switch (state)
- ]
333 : : {
334 : : case AVAHI_ENTRY_GROUP_ESTABLISHED:
335 : : // The entry group has been established successfully.
336 [ + - ]: 5 : log (_F("Service '%s' successfully established.", avahi_service_name));
337 : 5 : break;
338 : :
339 : : case AVAHI_ENTRY_GROUP_COLLISION: {
340 : : char *n;
341 : : // A service name collision with a remote service.
342 : : // happened. Let's pick a new name.
343 : 0 : n = avahi_alternative_service_name (avahi_service_name);
344 : 0 : avahi_free (avahi_service_name);
345 : 0 : avahi_service_name = n;
346 [ # # ]: 0 : server_error (_F("Avahi service name collision, renaming service to '%s'", avahi_service_name));
347 : :
348 : : // And recreate the services.
349 : 0 : create_services (avahi_entry_group_get_client (g));
350 : 0 : break;
351 : : }
352 : :
353 : : case AVAHI_ENTRY_GROUP_FAILURE:
354 : 0 : server_error (_F("Avahi entry group failure: %s",
355 [ # # ]: 0 : avahi_strerror (avahi_client_errno (avahi_entry_group_get_client (g)))));
356 : : // Some kind of failure happened while we were registering our services.
357 : 0 : avahi_threaded_poll_stop (avahi_threaded_poll);
358 : 0 : break;
359 : :
360 : : case AVAHI_ENTRY_GROUP_UNCOMMITED:
361 : : case AVAHI_ENTRY_GROUP_REGISTERING:
362 : 10 : break;
363 : : }
364 : 15 : }
365 : :
366 : : static void
367 : 5 : create_services (AvahiClient *c) {
368 [ - + ]: 5 : assert (c);
369 : :
370 : : // If this is the first time we're called, let's create a new
371 : : // entry group if necessary.
372 [ + - ]: 5 : if (! avahi_group)
373 [ - + ]: 5 : if (! (avahi_group = avahi_entry_group_new (c, entry_group_callback, NULL)))
374 : : {
375 : 0 : server_error (_F("avahi_entry_group_new () failed: %s",
376 [ # # ]: 0 : avahi_strerror (avahi_client_errno (c))));
377 : 0 : goto fail;
378 : : }
379 : :
380 : : // If the group is empty (either because it was just created, or
381 : : // because it was reset previously, add our entries.
382 [ + - ]: 5 : if (avahi_entry_group_is_empty (avahi_group))
383 : : {
384 [ + - ][ + - ]: 5 : log (_F("Adding Avahi service '%s'", avahi_service_name));
[ + - ]
385 : :
386 : : // Create the txt tags that will be registered with our service.
387 [ + - ][ + - ]: 5 : string sysinfo = "sysinfo=" + uname_r + " " + arch;
[ + - ][ + - ]
[ + - ]
388 [ + - ]: 5 : string certinfo = "certinfo=" + cert_serial_number;
389 [ + - ][ + - ]: 5 : string version = string ("version=") + CURRENT_CS_PROTOCOL_VERSION;;
[ + - ]
390 [ + - ]: 5 : string optinfo = "optinfo=";
391 [ + - ]: 5 : string separator;
392 : : // These option strings already have a leading space.
393 [ + - ][ - + ]: 5 : if (! R_option.empty ())
394 : : {
395 [ # # ][ # # ]: 0 : optinfo += R_option.substr(1);
[ # # ]
396 [ # # ]: 0 : separator = " ";
397 : : }
398 [ + - ][ - + ]: 5 : if (! B_options.empty ())
399 : : {
400 [ # # ][ # # ]: 0 : optinfo += separator + B_options.substr(1);
[ # # ][ # # ]
[ # # ]
401 [ # # ]: 0 : separator = " ";
402 : : }
403 [ + - ][ - + ]: 5 : if (! D_options.empty ())
404 : : {
405 [ # # ][ # # ]: 0 : optinfo += separator + D_options.substr(1);
[ # # ][ # # ]
[ # # ]
406 [ # # ]: 0 : separator = " ";
407 : : }
408 [ + - ][ - + ]: 5 : if (! I_options.empty ())
409 [ # # ][ # # ]: 0 : optinfo += separator + I_options.substr(1);
[ # # ][ # # ]
[ # # ]
410 : :
411 : : // We will now add our service to the entry group. Only services with the
412 : : // same name should be put in the same entry group.
413 : : int ret;
414 [ + - ][ - + ]: 5 : if ((ret = avahi_entry_group_add_service (avahi_group, AVAHI_IF_UNSPEC, AVAHI_PROTO_UNSPEC,
415 : : (AvahiPublishFlags)0,
416 : : avahi_service_name, "_stap._tcp", NULL, NULL, port,
417 : : sysinfo.c_str (), optinfo.c_str (),
418 [ + - ][ + - ]: 5 : version.c_str (), certinfo.c_str (), NULL)) < 0)
[ + - ][ + - ]
419 : : {
420 [ # # ]: 0 : if (ret == AVAHI_ERR_COLLISION)
421 : : goto collision;
422 : :
423 [ # # ][ # # ]: 0 : server_error (_F("Failed to add _stap._tcp service: %s", avahi_strerror (ret)));
[ # # ][ # # ]
424 : : goto fail;
425 : : }
426 : :
427 : : // Tell the server to register the service.
428 [ + - ][ - + ]: 5 : if ((ret = avahi_entry_group_commit (avahi_group)) < 0)
429 : : {
430 [ # # ][ # # ]: 5 : server_error (_F("Failed to commit avahi entry group: %s", avahi_strerror (ret)));
[ # # ][ # # ]
431 : : goto fail;
432 [ + - ]: 5 : }
[ - - + ]
[ + - ]
[ - - + ]
[ + - ]
[ - - + ]
[ + - ]
[ - - + ]
[ + - ]
[ + - - ]
433 : : }
434 : 5 : return;
435 : :
436 : : collision:
437 : : // A service name collision with a local service happened. Let's
438 : : // pick a new name.
439 : : char *n;
440 : 0 : n = avahi_alternative_service_name (avahi_service_name);
441 : 0 : avahi_free(avahi_service_name);
442 : 0 : avahi_service_name = n;
443 [ # # ]: 0 : server_error (_F("Avahi service name collision, renaming service to '%s'", avahi_service_name));
444 : 0 : avahi_entry_group_reset (avahi_group);
445 : 0 : create_services (c);
446 : 0 : return;
447 : :
448 : : fail:
449 : 0 : avahi_entry_group_reset (avahi_group);
450 : 5 : avahi_threaded_poll_stop (avahi_threaded_poll);
451 : : }
452 : :
453 : : static void
454 : 5 : client_callback (AvahiClient *c, AvahiClientState state, AVAHI_GCC_UNUSED void * userdata)
455 : : {
456 [ - + ]: 5 : assert(c);
457 : :
458 : : // Called whenever the client or server state changes.
459 [ + - - - : 5 : switch (state)
- ]
460 : : {
461 : : case AVAHI_CLIENT_S_RUNNING:
462 : : // The server has startup successfully and registered its host
463 : : // name on the network, so it's time to create our services.
464 : 5 : create_services (c);
465 : 5 : break;
466 : :
467 : : case AVAHI_CLIENT_FAILURE:
468 [ # # ]: 0 : server_error (_F("Avahi client failure: %s", avahi_strerror (avahi_client_errno (c))));
469 : 0 : avahi_threaded_poll_stop (avahi_threaded_poll);
470 : 0 : break;
471 : :
472 : : case AVAHI_CLIENT_S_COLLISION:
473 : : // Let's drop our registered services. When the server is back
474 : : // in AVAHI_SERVER_RUNNING state we will register them
475 : : // again with the new host name.
476 : : // Fall through ...
477 : : case AVAHI_CLIENT_S_REGISTERING:
478 : : // The server records are now being established. This
479 : : // might be caused by a host name change. We need to wait
480 : : // for our own records to register until the host name is
481 : : // properly esatblished.
482 [ # # ]: 0 : if (avahi_group)
483 : 0 : avahi_entry_group_reset (avahi_group);
484 : 0 : break;
485 : :
486 : : case AVAHI_CLIENT_CONNECTING:
487 : 0 : break;
488 : : }
489 : 5 : }
490 : :
491 : : static void
492 : 10 : avahi_cleanup () {
493 [ + + ]: 10 : if (avahi_service_name)
494 [ + - ]: 5 : log (_F("Removing Avahi service '%s'", avahi_service_name));
495 : :
496 : : // Stop the avahi client, if it's running
497 [ + + ]: 10 : if (avahi_threaded_poll)
498 : 5 : avahi_threaded_poll_stop (avahi_threaded_poll);
499 : :
500 : : // Clean up the avahi objects. The order of freeing these is significant.
501 [ + + ]: 10 : if (avahi_group) {
502 : 5 : avahi_entry_group_reset (avahi_group);
503 : 5 : avahi_entry_group_free (avahi_group);
504 : 5 : avahi_group = 0;
505 : : }
506 [ + + ]: 10 : if (avahi_client) {
507 : 5 : avahi_client_free (avahi_client);
508 : 5 : avahi_client = 0;
509 : : }
510 [ + + ]: 10 : if (avahi_threaded_poll) {
511 : 5 : avahi_threaded_poll_free (avahi_threaded_poll);
512 : 5 : avahi_threaded_poll = 0;
513 : : }
514 [ + + ]: 10 : if (avahi_service_name) {
515 : 5 : avahi_free (avahi_service_name);
516 : 5 : avahi_service_name = 0;
517 : : }
518 : 10 : }
519 : :
520 : : // The entry point for the avahi client thread.
521 : : static void
522 : 5 : avahi_publish_service (CERTCertificate *cert)
523 : : {
524 [ + - ][ + - ]: 5 : cert_serial_number = get_cert_serial_number (cert);
[ + - ]
525 [ + - ]: 5 : string buf;
526 : : try
527 : : {
528 [ + - ][ + - ]: 5 : buf = "Systemtap Compile Server, pid=" + lex_cast (getpid ());
[ + - ][ + - ]
[ + - ]
529 : : }
530 [ # # ]: : catch (const runtime_error &e)
531 : : {
532 [ # # # # : : server_error(_F("Failed to cast pid '%d' to a string: %s", getpid(), e.what()));
# # ]
533 : : return;
534 : : }
535 [ + - ][ + - ]: 5 : avahi_service_name = avahi_strdup (buf.c_str ());
536 : :
537 : : // Allocate main loop object.
538 [ + - ][ - + ]: 5 : if (! (avahi_threaded_poll = avahi_threaded_poll_new ()))
539 : : {
540 [ # # ][ # # ]: 0 : server_error (_("Failed to create avahi threaded poll object."));
[ # # ]
541 : : return;
542 : : }
543 : :
544 : : // Always allocate a new client.
545 : : int error;
546 : : avahi_client = avahi_client_new (avahi_threaded_poll_get (avahi_threaded_poll),
547 : : (AvahiClientFlags)0,
548 [ + - ][ + - ]: 5 : client_callback, NULL, & error);
549 : : // Check whether creating the client object succeeded.
550 [ - + ]: 5 : if (! avahi_client)
551 : : {
552 [ # # ][ # # ]: 0 : server_error (_F("Failed to create avahi client: %s", avahi_strerror(error)));
[ # # ][ # # ]
553 : : return;
554 : : }
555 : :
556 : : // Run the main loop.
557 [ + - ]: 5 : avahi_threaded_poll_start (avahi_threaded_poll);
558 : :
559 [ + - ]: 5 : return;
560 : : }
561 : : #endif // HAVE_AVAHI
562 : :
563 : : static void
564 : 5 : advertise_presence (CERTCertificate *cert __attribute ((unused)))
565 : : {
566 : : #if HAVE_AVAHI
567 : 5 : avahi_publish_service (cert);
568 : : #else
569 : : server_error (_("Unable to advertise presence on the network. Avahi is not available"));
570 : : #endif
571 : 5 : }
572 : :
573 : : static void
574 : 10 : unadvertise_presence ()
575 : : {
576 : : #if HAVE_AVAHI
577 : 10 : avahi_cleanup ();
578 : : #endif
579 : 10 : }
580 : :
581 : : static void
582 : 5 : initialize (int argc, char **argv) {
583 : 5 : pending_interrupts = 0;
584 : 5 : setup_signals (& handle_interrupt);
585 : :
586 : : // Seed the random number generator. Used to generate noise used during key generation.
587 : 5 : srand (time (NULL));
588 : :
589 : : // Initial values.
590 : 5 : use_db_password = false;
591 : 5 : port = 0;
592 : 5 : max_threads = sysconf( _SC_NPROCESSORS_ONLN ); // Default to number of processors
593 : 5 : keep_temp = false;
594 : : struct utsname utsname;
595 : 5 : uname (& utsname);
596 [ + - ]: 5 : uname_r = utsname.release;
597 [ + - ][ + - ]: 5 : arch = normalize_machine (utsname.machine);
[ + - ][ + - ]
[ + - ]
598 : :
599 : : // Parse the arguments. This also starts the server log, if any, and should be done before
600 : : // any messages are issued.
601 [ + - ]: 5 : parse_options (argc, argv);
602 : :
603 : : // PR11197: security prophylactics.
604 : : // Reject use as root, except via a special environment variable.
605 [ - + ]: 5 : if (! getenv ("STAP_PR11197_OVERRIDE")) {
606 [ # # ]: 0 : if (geteuid () == 0)
607 [ # # ][ # # ]: 0 : fatal ("For security reasons, invocation of stap-serverd as root is not supported.");
[ # # ]
608 : : }
609 : :
610 [ + - ]: 5 : struct passwd *pw = getpwuid (geteuid ());
611 [ - + ]: 5 : if (! pw)
612 [ # # ][ # # ]: 0 : fatal (_F("Unable to determine effective user name: %s", strerror (errno)));
[ # # ]
613 [ + - ]: 5 : string username = pw->pw_name;
614 : 5 : pid_t pid = getpid ();
615 [ + - ][ + - ]: 5 : log (_F("===== compile server pid %d starting as %s =====", pid, username.c_str ()));
[ + - ][ + - ]
616 : :
617 : : // Where is the ssl certificate/key database?
618 [ + - ][ + - ]: 5 : if (cert_db_path.empty ())
619 [ + - ][ + - ]: 5 : cert_db_path = server_cert_db_path ();
[ + - ]
620 : :
621 : : // Make sure NSPR is initialized. Must be done before NSS is initialized
622 [ + - ]: 5 : PR_Init (PR_SYSTEM_THREAD, PR_PRIORITY_NORMAL, 1);
623 : : /* Set the cert database password callback. */
624 [ + - ][ + - ]: 5 : PK11_SetPasswordFunc (nssPasswordCallback);
625 : 5 : }
626 : :
627 : : static void
628 : 5 : cleanup ()
629 : : {
630 : 5 : unadvertise_presence ();
631 : 5 : end_log ();
632 : 5 : }
633 : :
634 : : /* Function: readDataFromSocket()
635 : : *
636 : : * Purpose: Read data from the socket into a temporary file.
637 : : *
638 : : */
639 : : static PRInt32
640 : 36 : readDataFromSocket(PRFileDesc *sslSocket, const char *requestFileName)
641 : : {
642 : 36 : PRFileDesc *local_file_fd = 0;
643 : : PRInt32 numBytesExpected;
644 : : PRInt32 numBytesRead;
645 : : PRInt32 numBytesWritten;
646 : 36 : PRInt32 totalBytes = 0;
647 : : #define READ_BUFFER_SIZE 4096
648 : : char buffer[READ_BUFFER_SIZE];
649 : :
650 : : /* Read the number of bytes to be received. */
651 : : /* XXX: impose a limit to prevent disk space consumption DoS */
652 [ + - ]: 36 : numBytesRead = PR_Read (sslSocket, & numBytesExpected, sizeof (numBytesExpected));
653 [ - + ]: 36 : if (numBytesRead == 0) /* EOF */
654 : : {
655 [ # # ][ # # ]: 0 : server_error (_("Error reading size of request file"));
[ # # ]
656 : 0 : goto done;
657 : : }
658 [ - + ]: 36 : if (numBytesRead < 0)
659 : : {
660 [ # # ][ # # ]: 0 : server_error (_("Error in PR_Read"));
[ # # ]
661 [ # # ]: 0 : nssError ();
662 : 0 : goto done;
663 : : }
664 : :
665 : : /* Convert numBytesExpected from network byte order to host byte order. */
666 : 36 : numBytesExpected = ntohl (numBytesExpected);
667 : :
668 : : /* If 0 bytes are expected, then we were contacted only to obtain our certificate.
669 : : There is no client request. */
670 [ + + ]: 36 : if (numBytesExpected == 0)
671 : 1 : return 0;
672 : :
673 : : /* Open the output file. */
674 : : local_file_fd = PR_Open(requestFileName, PR_WRONLY | PR_CREATE_FILE | PR_TRUNCATE,
675 [ + - ]: 35 : PR_IRUSR | PR_IWUSR);
676 [ - + ]: 35 : if (local_file_fd == NULL)
677 : : {
678 [ # # ][ # # ]: 0 : server_error (_F("Could not open output file %s", requestFileName));
[ # # ]
679 [ # # ]: 0 : nssError ();
680 : 0 : return -1;
681 : : }
682 : :
683 : : /* Read until EOF or until the expected number of bytes has been read. */
684 [ + + ]: 70 : for (totalBytes = 0; totalBytes < numBytesExpected; totalBytes += numBytesRead)
685 : : {
686 [ + - ]: 35 : numBytesRead = PR_Read(sslSocket, buffer, READ_BUFFER_SIZE);
687 [ - + ]: 35 : if (numBytesRead == 0)
688 : 0 : break; /* EOF */
689 [ - + ]: 35 : if (numBytesRead < 0)
690 : : {
691 [ # # ][ # # ]: 0 : server_error (_("Error in PR_Read"));
[ # # ]
692 [ # # ]: 0 : nssError ();
693 : 0 : goto done;
694 : : }
695 : :
696 : : /* Write to the request file. */
697 [ + - ]: 35 : numBytesWritten = PR_Write(local_file_fd, buffer, numBytesRead);
698 [ + ][ - + ]: 34 : if (numBytesWritten < 0 || (numBytesWritten != numBytesRead))
699 : : {
700 [ - - ][ # # ]: 0 : server_error (_F("Could not write to output file %s", requestFileName));
[ # # ]
701 [ # # ]: 0 : nssError ();
702 : 0 : goto done;
703 : : }
704 : : }
705 : :
706 [ - + ]: 35 : if (totalBytes != numBytesExpected)
707 : : {
708 : 0 : server_error (_F("Expected %d bytes, got %d while reading client request from socket",
709 [ # # ][ # # ]: 0 : numBytesExpected, totalBytes));
[ # # ]
710 : 0 : goto done;
711 : : }
712 : :
713 : : done:
714 [ + - ]: 35 : if (local_file_fd)
715 [ + - ]: 35 : PR_Close (local_file_fd);
716 : 36 : return totalBytes;
717 : : }
718 : :
719 : : /* Function: setupSSLSocket()
720 : : *
721 : : * Purpose: Configure a socket for SSL.
722 : : *
723 : : *
724 : : */
725 : : static PRFileDesc *
726 : 36 : setupSSLSocket (PRFileDesc *tcpSocket, CERTCertificate *cert, SECKEYPrivateKey *privKey)
727 : : {
728 : : PRFileDesc *sslSocket;
729 : : SSLKEAType certKEA;
730 : : SECStatus secStatus;
731 : :
732 : : /* Inport the socket into SSL. */
733 : 36 : sslSocket = SSL_ImportFD (NULL, tcpSocket);
734 [ - + ]: 36 : if (sslSocket == NULL)
735 : : {
736 [ # # ][ # # ]: 0 : server_error (_("Could not import socket into SSL"));
[ # # ]
737 : 0 : nssError ();
738 : 0 : return NULL;
739 : : }
740 : :
741 : : /* Set the appropriate flags. */
742 : 36 : secStatus = SSL_OptionSet (sslSocket, SSL_SECURITY, PR_TRUE);
743 [ - + ]: 36 : if (secStatus != SECSuccess)
744 : : {
745 [ # # ][ # # ]: 0 : server_error (_("Error setting SSL security for socket"));
[ # # ]
746 : 0 : nssError ();
747 : 0 : return NULL;
748 : : }
749 : :
750 : 36 : secStatus = SSL_OptionSet(sslSocket, SSL_HANDSHAKE_AS_SERVER, PR_TRUE);
751 [ - + ]: 36 : if (secStatus != SECSuccess)
752 : : {
753 [ # # ][ # # ]: 0 : server_error (_("Error setting handshake as server for socket"));
[ # # ]
754 : 0 : nssError ();
755 : 0 : return NULL;
756 : : }
757 : :
758 : 36 : secStatus = SSL_OptionSet(sslSocket, SSL_REQUEST_CERTIFICATE, PR_FALSE);
759 [ - + ]: 36 : if (secStatus != SECSuccess)
760 : : {
761 [ # # ][ # # ]: 0 : server_error (_("Error setting SSL client authentication mode for socket"));
[ # # ]
762 : 0 : nssError ();
763 : 0 : return NULL;
764 : : }
765 : :
766 : 36 : secStatus = SSL_OptionSet(sslSocket, SSL_REQUIRE_CERTIFICATE, PR_FALSE);
767 [ - + ]: 36 : if (secStatus != SECSuccess)
768 : : {
769 [ # # ][ # # ]: 0 : server_error (_("Error setting SSL client authentication mode for socket"));
[ # # ]
770 : 0 : nssError ();
771 : 0 : return NULL;
772 : : }
773 : :
774 : : /* Set the appropriate callback routines. */
775 : : #if 0 /* use the default */
776 : : secStatus = SSL_AuthCertificateHook (sslSocket, myAuthCertificate, CERT_GetDefaultCertDB());
777 : : if (secStatus != SECSuccess)
778 : : {
779 : : nssError ();
780 : : server_error (_("Error in SSL_AuthCertificateHook"));
781 : : return NULL;
782 : : }
783 : : #endif
784 : : #if 0 /* Use the default */
785 : : secStatus = SSL_BadCertHook(sslSocket, (SSLBadCertHandler)myBadCertHandler, &certErr);
786 : : if (secStatus != SECSuccess)
787 : : {
788 : : nssError ();
789 : : server_error (_("Error in SSL_BadCertHook"));
790 : : return NULL;
791 : : }
792 : : #endif
793 : : #if 0 /* no handshake callback */
794 : : secStatus = SSL_HandshakeCallback(sslSocket, myHandshakeCallback, NULL);
795 : : if (secStatus != SECSuccess)
796 : : {
797 : : server_error (_("Error in SSL_HandshakeCallback"));
798 : : nssError ();
799 : : return NULL;
800 : : }
801 : : #endif
802 : :
803 : 36 : certKEA = NSS_FindCertKEAType (cert);
804 : :
805 : 36 : secStatus = SSL_ConfigSecureServer (sslSocket, cert, privKey, certKEA);
806 [ - + ]: 36 : if (secStatus != SECSuccess)
807 : : {
808 [ # # ][ # # ]: 0 : server_error (_("Error configuring SSL server"));
[ # # ]
809 : 0 : nssError ();
810 : 0 : return NULL;
811 : : }
812 : :
813 : 36 : return sslSocket;
814 : : }
815 : :
816 : : #if 0 /* No client authentication (for now) and not authenticating after each transaction. */
817 : : /* Function: authenticateSocket()
818 : : *
819 : : * Purpose: Perform client authentication on the socket.
820 : : *
821 : : */
822 : : static SECStatus
823 : : authenticateSocket (PRFileDesc *sslSocket, PRBool requireCert)
824 : : {
825 : : CERTCertificate *cert;
826 : : SECStatus secStatus;
827 : :
828 : : /* Returns NULL if client authentication is not enabled or if the
829 : : * client had no certificate. */
830 : : cert = SSL_PeerCertificate(sslSocket);
831 : : if (cert)
832 : : {
833 : : /* Client had a certificate, so authentication is through. */
834 : : CERT_DestroyCertificate(cert);
835 : : return SECSuccess;
836 : : }
837 : :
838 : : /* Request client to authenticate itself. */
839 : : secStatus = SSL_OptionSet(sslSocket, SSL_REQUEST_CERTIFICATE, PR_TRUE);
840 : : if (secStatus != SECSuccess)
841 : : {
842 : : server_error (_("Error in SSL_OptionSet:SSL_REQUEST_CERTIFICATE"));
843 : : nssError ();
844 : : return SECFailure;
845 : : }
846 : :
847 : : /* If desired, require client to authenticate itself. Note
848 : : * SSL_REQUEST_CERTIFICATE must also be on, as above. */
849 : : secStatus = SSL_OptionSet(sslSocket, SSL_REQUIRE_CERTIFICATE, requireCert);
850 : : if (secStatus != SECSuccess)
851 : : {
852 : : server_error (_("Error in SSL_OptionSet:SSL_REQUIRE_CERTIFICATE"));
853 : : nssError ();
854 : : return SECFailure;
855 : : }
856 : :
857 : : /* Having changed socket configuration parameters, redo handshake. */
858 : : secStatus = SSL_ReHandshake(sslSocket, PR_TRUE);
859 : : if (secStatus != SECSuccess)
860 : : {
861 : : server_error (_("Error in SSL_ReHandshake"));
862 : : nssError ();
863 : : return SECFailure;
864 : : }
865 : :
866 : : /* Force the handshake to complete before moving on. */
867 : : secStatus = SSL_ForceHandshake(sslSocket);
868 : : if (secStatus != SECSuccess)
869 : : {
870 : : server_error (_("Error in SSL_ForceHandshake"));
871 : : nssError ();
872 : : return SECFailure;
873 : : }
874 : :
875 : : return SECSuccess;
876 : : }
877 : : #endif /* No client authentication and not authenticating after each transaction. */
878 : :
879 : : /* Function: writeDataToSocket
880 : : *
881 : : * Purpose: Write the server's response back to the socket.
882 : : *
883 : : */
884 : : static SECStatus
885 : 35 : writeDataToSocket(PRFileDesc *sslSocket, const char *responseFileName)
886 : : {
887 : 35 : PRFileDesc *local_file_fd = PR_Open (responseFileName, PR_RDONLY, 0);
888 [ - + ]: 35 : if (local_file_fd == NULL)
889 : : {
890 [ # # ]: 0 : server_error (_F("Could not open input file %s", responseFileName));
891 : 0 : nssError ();
892 : 0 : return SECFailure;
893 : : }
894 : :
895 : : /* Transmit the local file across the socket.
896 : : */
897 : : int numBytes = PR_TransmitFile (sslSocket, local_file_fd,
898 : : NULL, 0,
899 : : PR_TRANSMITFILE_KEEP_OPEN,
900 : 35 : PR_INTERVAL_NO_TIMEOUT);
901 : :
902 : : /* Error in transmission. */
903 : 35 : SECStatus secStatus = SECSuccess;
904 [ - + ]: 35 : if (numBytes < 0)
905 : : {
906 [ # # ][ # # ]: 0 : server_error (_("Error writing response to socket"));
[ # # ]
907 : 0 : nssError ();
908 : 0 : secStatus = SECFailure;
909 : : }
910 : :
911 : 35 : PR_Close (local_file_fd);
912 : 35 : return secStatus;
913 : : }
914 : :
915 : : static void
916 : 35 : get_stap_locale (const string &staplang, vector<string> &envVec, string stapstderr, cs_protocol_version *client_version)
917 : : {
918 : : // If the client version is < 1.6, then no file containing environment
919 : : // variables defining the locale has been passed.
920 [ + - ][ + - ]: 35 : if (*client_version < "1.6")
[ + - ][ + - ]
921 : : return;
922 : :
923 : : /* Go through each line of the file, verify it, then add it to the vector */
924 [ + - ]: 35 : ifstream langfile;
925 [ + - ][ + - ]: 35 : langfile.open(staplang.c_str());
926 [ + - ][ - + ]: 35 : if (!langfile.is_open())
927 : : {
928 : : // Not fatal. Proceed with the environment we have.
929 : 0 : server_error(_F("Unable to open file %s for reading: %s", staplang.c_str(),
930 [ # # # # ]: 0 : strerror (errno)));
[ # # ][ # # ]
931 : : return;
932 : : }
933 : :
934 : : /* Unpackage internationalization variables and verify their contents */
935 [ + - ]: 35 : map<string, string> envMap; /* To temporarily store the entire array of strings */
936 [ + - ]: 35 : string line;
937 [ + - ]: 35 : const set<string> &locVars = localization_variables();
938 : :
939 : : /* Copy the global environ variable into the map */
940 [ + - ]: 35 : if(environ != NULL)
941 : : {
942 [ + + ]: 1610 : for (unsigned i=0; environ[i]; i++)
943 : : {
944 [ + - ]: 1575 : string line = (string)environ[i];
945 : :
946 : : /* Find the first '=' sign */
947 [ + - ]: 1575 : size_t pos = line.find("=");
948 : :
949 : : /* Make sure it found an '=' sign */
950 [ + - ]: 1575 : if(pos != string::npos)
951 : : /* Everything before the '=' sign is the key, and everything after is the value. */
952 [ + - ][ + - ]: 1575 : envMap[line.substr(0, pos)] = line.substr(pos+1);
[ + - ][ + - ]
[ + - ][ + - ]
953 [ + - ]: 1575 : }
954 : : }
955 : :
956 : : /* Create regular expression objects to verify lines read from file. Should not allow
957 : : spaces, ctrl characters, etc */
958 : : regex_t checkre;
959 [ + - ][ - + ]: 35 : if ((regcomp(&checkre, "^[a-zA-Z0-9@_.=-]*$", REG_EXTENDED | REG_NOSUB) != 0))
960 : : {
961 : : // Not fatal. Proceed with the environment we have.
962 [ # # ][ # # ]: 0 : server_error(_F("Error in regcomp: %s", strerror (errno)));
[ # # ]
963 : : return;
964 : : }
965 : :
966 : 44 : while (1)
967 : : {
968 [ + - ]: 79 : getline(langfile, line);
969 [ + - ][ + + ]: 79 : if (!langfile.good())
970 : : break;
971 : :
972 : : /* Extract key and value from the line. Note: value may contain "=". */
973 [ + - ]: 44 : string key;
974 [ + - ]: 44 : string value;
975 : : size_t pos;
976 [ + - ]: 44 : pos = line.find("=");
977 [ + + ]: 44 : if (pos == string::npos)
978 : : {
979 [ + - ][ + - ]: 1 : client_error(_F("Localization key=value line '%s' cannot be parsed", line.c_str()), stapstderr);
[ + - ][ + - ]
[ + - ][ + - ]
980 : 1 : continue;
981 : : }
982 [ + - ][ + - ]: 43 : key = line.substr(0, pos);
[ + - ]
983 : 43 : pos++;
984 [ + - ][ + - ]: 43 : value = line.substr(pos);
[ + - ]
985 : :
986 : : /* Make sure the key is found in the localization variables global set */
987 [ + - ][ + - ]: 43 : if (locVars.find(key) == locVars.end())
[ - + ]
988 : : {
989 : : // Not fatal. Just ignore it.
990 [ # # ][ # # ]: 0 : client_error(_F("Localization key '%s' not found in global list", key.c_str()), stapstderr);
[ # # ][ # # ]
[ # # ][ # # ]
991 : 0 : continue;
992 : : }
993 : :
994 : : /* Make sure the value does not contain illegal characters */
995 [ + - ][ + - ]: 43 : if ((regexec(&checkre, value.c_str(), (size_t) 0, NULL, 0) != 0))
[ + + ]
996 : : {
997 : : // Not fatal. Just ignore it.
998 [ + - ][ + - ]: 1 : client_error(_F("Localization value '%s' contains illegal characters", value.c_str()), stapstderr);
[ + - ][ + - ]
[ + - ][ + - ]
999 : 1 : continue;
1000 : : }
1001 : :
1002 : : /* All is good, copy line into envMap, replacing if already there */
1003 [ + - ][ + - ]: 42 : envMap[key] = value;
1004 [ + - ][ + + ]: 44 : }
[ + - ][ + + ]
1005 : :
1006 [ + - ][ - + ]: 37 : if (!langfile.eof())
1007 : : {
1008 : : // Not fatal. Proceed with what we have.
1009 [ # # ][ # # ]: 0 : server_error(_F("Error reading file %s: %s", staplang.c_str(), strerror (errno)));
[ # # ][ # # ]
1010 : : }
1011 : :
1012 [ + - ]: 35 : regfree(&checkre);
1013 : :
1014 : : /* Copy map into vector */
1015 [ + - ][ + - ]: 1618 : for (map<string, string>::iterator it = envMap.begin(); it != envMap.end(); it++)
[ + + ]
1016 [ + - ][ + - ]: 1618 : envVec.push_back(it->first + "=" + it->second);
[ + - ][ + - ]
[ + - ][ + - ]
[ + - ][ + - ]
[ - + ][ + - ]
[ - + ][ + - ]
[ + - ]
1017 : : }
1018 : :
1019 : : // Filter paths prefixed with the server's home directory from the given file.
1020 : : //
1021 : : static void
1022 : 70 : filter_response_file (const string &file_name, const string &responseDirName)
1023 : : {
1024 [ + - ]: 70 : vector<string> cmd;
1025 : :
1026 : : // Filter the server's home directory name
1027 [ + - ]: 70 : cmd.clear();
1028 [ + - ][ + - ]: 70 : cmd.push_back ("sed");
[ + - ]
1029 [ + - ][ + - ]: 70 : cmd.push_back ("-i");
[ + - ]
1030 [ + - ][ + - ]: 70 : cmd.push_back (string ("s,") + get_home_directory () + ",<server>,g");
[ + - ][ + - ]
[ + - ][ + - ]
[ + - ][ + - ]
1031 [ + - ]: 70 : cmd.push_back (file_name);
1032 [ + - ]: 70 : stap_system (0, cmd);
1033 : :
1034 : : // Filter the server's response directory name
1035 [ + - ]: 70 : cmd.clear();
1036 [ + - ][ + - ]: 70 : cmd.push_back ("sed");
[ + - ]
1037 [ + - ][ + - ]: 70 : cmd.push_back ("-i");
[ + - ]
1038 [ + - ][ + - ]: 70 : cmd.push_back (string ("s,") + responseDirName + ",<server>,g");
[ + - ][ + - ]
[ + - ][ + - ]
[ + - ]
1039 [ + - ]: 70 : cmd.push_back (file_name);
1040 [ + - ][ + - ]: 70 : stap_system (0, cmd);
1041 : 70 : }
1042 : :
1043 : : static privilege_t
1044 : 35 : getRequestedPrivilege (const vector<string> &stapargv)
1045 : : {
1046 : : // The purpose of this function is to find the --privilege or --unprivileged option specified
1047 : : // by the user on the client side. We need to parse the command line completely, but we can
1048 : : // exit when we find the first --privilege or --unprivileged option, since stap does not allow
1049 : : // multiple privilege levels to specified on the same command line.
1050 : : //
1051 : : // Note that we need not do any options consistency checking since our spawned stap instance
1052 : : // will do that.
1053 : : //
1054 : : // Create an argv/argc for use by getopt_long.
1055 : 35 : int argc = stapargv.size();
1056 [ + - ]: 35 : char ** argv = new char *[argc + 1];
1057 [ + + ]: 242 : for (unsigned i = 0; i < stapargv.size(); ++i)
1058 : 207 : argv[i] = (char *)stapargv[i].c_str();
1059 : 35 : argv[argc] = NULL;
1060 : :
1061 : 35 : privilege_t privilege = pr_highest; // Until specified otherwise.
1062 : 35 : optind = 1;
1063 : 151 : while (true)
1064 : : {
1065 : : // We need only allow getopt to parse the options until we find a
1066 : : // --privilege or --unprivileged option.
1067 : 186 : int grc = getopt_long (argc, argv, STAP_SHORT_OPTIONS, stap_long_options, NULL);
1068 [ + + ]: 186 : if (grc < 0)
1069 : 32 : break;
1070 [ + + + ]: 154 : switch (grc)
1071 : : {
1072 : : default:
1073 : : // We can ignore all options other than --privilege and --unprivileged.
1074 : 151 : break;
1075 : : case LONG_OPT_PRIVILEGE:
1076 [ - + ]: 2 : if (strcmp (optarg, "stapdev") == 0)
1077 : 0 : privilege = pr_stapdev;
1078 [ + + ]: 2 : else if (strcmp (optarg, "stapsys") == 0)
1079 : 1 : privilege = pr_stapsys;
1080 [ + - ]: 1 : else if (strcmp (optarg, "stapusr") == 0)
1081 : 1 : privilege = pr_stapusr;
1082 : : else
1083 : : {
1084 [ # # ]: 0 : server_error (_F("Invalid argument '%s' for --privilege", optarg));
1085 : 0 : privilege = pr_highest;
1086 : : }
1087 : : // We have discovered the client side --privilege option. We can exit now since
1088 : : // stap only tolerates one privilege setting option.
1089 : 2 : goto done; // break 2 switches and a loop
1090 : : case LONG_OPT_UNPRIVILEGED:
1091 : 1 : privilege = pr_unprivileged;
1092 : : // We have discovered the client side --unprivileged option. We can exit now since
1093 : : // stap only tolerates one privilege setting option.
1094 : 1 : goto done; // break 2 switches and a loop
1095 : : }
1096 : : }
1097 : : done:
1098 [ + - ]: 35 : delete[] argv;
1099 : 35 : return privilege;
1100 : : }
1101 : :
1102 : : /* Run the translator on the data in the request directory, and produce output
1103 : : in the given output directory. */
1104 : : static void
1105 : 35 : handleRequest (const string &requestDirName, const string &responseDirName, string stapstderr)
1106 : : {
1107 [ + - ]: 35 : vector<string> stapargv;
1108 [ + - ]: 35 : cs_protocol_version client_version = "1.0"; // Assumed until discovered otherwise
1109 : : int rc;
1110 : : wordexp_t words;
1111 : : unsigned u;
1112 : : unsigned i;
1113 : : FILE* f;
1114 : :
1115 : : // Save the server version. Do this early, so the client knows what version of the server
1116 : : // it is dealing with, even if the request is not fully completed.
1117 [ + - ]: 35 : string stapversion = responseDirName + "/version";
1118 [ + - ][ + - ]: 35 : f = fopen (stapversion.c_str (), "w");
1119 [ + - ]: 35 : if (f)
1120 : : {
1121 [ + - ]: 35 : fputs (CURRENT_CS_PROTOCOL_VERSION, f);
1122 [ + - ]: 35 : fclose(f);
1123 : : }
1124 : : else
1125 [ # # ][ # # ]: 0 : server_error (_F("Unable to open client version file %s", stapversion.c_str ()));
[ # # ][ # # ]
1126 : :
1127 : : // Get the client version. The default version is already set. Use it if we fail here.
1128 [ + - ]: 35 : string filename = requestDirName + "/version";
1129 [ + - ][ + - ]: 35 : if (file_exists (filename))
1130 [ + - ]: 35 : read_from_file (filename, client_version);
1131 [ + - ][ + - ]: 35 : log (_F("Client version is %s", client_version.v));
[ + - ]
1132 : :
1133 : : // The name of the translator executable.
1134 [ + - ][ + - ]: 35 : stapargv.push_back ((char *)(getenv ("SYSTEMTAP_STAP") ?: STAP_PREFIX "/bin/stap"));
[ + - ][ + - ]
1135 : :
1136 : : /* Transcribe stap_options. We use plain wordexp(3), since these
1137 : : options are coming from the local trusted user, so malicious
1138 : : content is not a concern. */
1139 : : // TODO: Use tokenize here.
1140 [ + - ][ + - ]: 35 : rc = wordexp (stap_options.c_str (), & words, WRDE_NOCMD|WRDE_UNDEF);
1141 [ - + ]: 35 : if (rc)
1142 : : {
1143 [ # # ][ # # ]: 0 : server_error (_("Cannot parse stap options"));
[ # # ]
1144 : : return;
1145 : : }
1146 : :
1147 [ - + ]: 35 : for (u=0; u<words.we_wordc; u++)
1148 [ # # ][ # # ]: 0 : stapargv.push_back (words.we_wordv[u]);
[ # # ]
1149 : :
1150 : : /* Process the saved command line arguments. Avoid quoting/unquoting errors by
1151 : : transcribing literally. */
1152 [ + - ]: 35 : string new_staptmpdir = responseDirName + "/stap000000";
1153 [ + - ]: 35 : rc = mkdir(new_staptmpdir.c_str(), 0700);
1154 [ - + ]: 35 : if (rc)
1155 [ # # ][ # # ]: 0 : server_error(_F("Could not create temporary directory %s", new_staptmpdir.c_str()));
[ # # ][ # # ]
1156 : :
1157 [ + - ][ + - ]: 35 : stapargv.push_back("--tmpdir=" + new_staptmpdir);
[ + - ]
1158 : :
1159 [ + - ][ + - ]: 35 : stapargv.push_back ("--client-options");
[ + - ]
1160 : 137 : for (i=1 ; ; i++)
1161 : : {
1162 : : char stapargfile[PATH_MAX];
1163 : : FILE* argfile;
1164 : : struct stat st;
1165 : : char *arg;
1166 : :
1167 [ + - ]: 137 : snprintf (stapargfile, PATH_MAX, "%s/argv%d", requestDirName.c_str (), i);
1168 : :
1169 : 137 : rc = stat(stapargfile, & st);
1170 [ + + ]: 137 : if (rc) break;
1171 : :
1172 : 102 : arg = (char *)malloc (st.st_size+1);
1173 [ - + ]: 102 : if (!arg)
1174 : : {
1175 [ # # ][ # # ]: 0 : server_error (_("Out of memory"));
[ # # ]
1176 : : return;
1177 : : }
1178 : :
1179 [ + - ]: 102 : argfile = fopen(stapargfile, "r");
1180 [ - + ]: 102 : if (! argfile)
1181 : : {
1182 : 0 : free(arg);
1183 [ # # ][ # # ]: 0 : server_error (_F("Error opening %s: %s", stapargfile, strerror (errno)));
[ # # ]
1184 : : return;
1185 : : }
1186 : :
1187 [ + - ]: 102 : rc = fread(arg, 1, st.st_size, argfile);
1188 [ - + ]: 102 : if (rc != st.st_size)
1189 : : {
1190 : 0 : free(arg);
1191 [ # # ]: 0 : fclose(argfile);
1192 [ # # ][ # # ]: 0 : server_error (_F("Error reading %s: %s", stapargfile, strerror (errno)));
[ # # ]
1193 : : return;
1194 : : }
1195 : :
1196 : 102 : arg[st.st_size] = '\0';
1197 [ + - ][ + - ]: 102 : stapargv.push_back (arg);
[ + - ]
1198 : 102 : free (arg);
1199 [ + - ]: 102 : fclose (argfile);
1200 : : }
1201 : :
1202 [ + - ]: 35 : string stapstdout = responseDirName + "/stdout";
1203 : :
1204 : : // NB: Before, when we did not fully parse the client's command line using getopt_long,
1205 : : // we used to insert a --privilege=XXX option here in case some other argument was mistaken
1206 : : // for a --privilege or --unprivileged option by our spawned stap. Since we now parse
1207 : : // the client's command line using getopt_long and share the getopt_long options
1208 : : // string and table with stap, this is no longer necessary. stap will parse the
1209 : : // command line identically to the way we have parsed it and will discover the same
1210 : : // privilege-setting option.
1211 : :
1212 : : // Environment variables (possibly empty) to be passed to spawn_and_wait().
1213 [ + - ]: 35 : string staplang = requestDirName + "/locale";
1214 [ + - ]: 35 : vector<string> envVec;
1215 [ + - ][ + - ]: 35 : get_stap_locale (staplang, envVec, stapstderr, &client_version);
[ + - ]
1216 : :
1217 : : /* All ready, let's run the translator! */
1218 : : rc = spawn_and_wait(stapargv, "/dev/null", stapstdout.c_str (), stapstderr.c_str (),
1219 [ + - ][ + - ]: 35 : requestDirName.c_str (), envVec);
[ + - ][ + - ]
1220 : :
1221 : : /* Save the RC */
1222 [ + - ]: 35 : string staprc = responseDirName + "/rc";
1223 [ + - ][ + - ]: 35 : f = fopen(staprc.c_str (), "w");
1224 [ + - ]: 35 : if (f)
1225 : : {
1226 : : /* best effort basis */
1227 [ + - ]: 35 : fprintf(f, "%d", rc);
1228 [ + - ]: 35 : fclose(f);
1229 : : }
1230 : :
1231 : : // In unprivileged modes, if we have a module built, we need to sign the sucker.
1232 [ + - ]: 35 : privilege_t privilege = getRequestedPrivilege (stapargv);
1233 [ + - ][ + + ]: 35 : if (pr_contains (privilege, pr_stapusr) || pr_contains (privilege, pr_stapsys))
[ + - ][ + + ]
[ + + ]
1234 : : {
1235 : : glob_t globber;
1236 : : char pattern[PATH_MAX];
1237 [ + - ]: 3 : snprintf (pattern, PATH_MAX, "%s/*.ko", new_staptmpdir.c_str());
1238 : 3 : rc = glob (pattern, GLOB_ERR, NULL, &globber);
1239 [ + - ]: 3 : if (rc)
1240 [ + - ][ + - ]: 3 : server_error (_F("Unable to find a module in %s", new_staptmpdir.c_str()));
[ + - ][ + - ]
1241 [ # # ]: 0 : else if (globber.gl_pathc != 1)
1242 [ # # ][ # # ]: 0 : server_error (_F("Too many modules (%zu) in %s", globber.gl_pathc, new_staptmpdir.c_str()));
[ # # ][ # # ]
1243 : : else
1244 : : {
1245 : : sign_file (cert_db_path, server_cert_nickname(),
1246 [ # # ][ # # ]: 3 : globber.gl_pathv[0], string(globber.gl_pathv[0]) + ".sgn");
[ # # ][ # # ]
[ # # ][ # # ]
[ # # ][ # # ]
[ # # ][ # # ]
1247 : : }
1248 : : }
1249 : :
1250 : : /* If uprobes.ko is required, it will have been built or cache-copied into
1251 : : * the temp directory. We need to pack it into the response where the client
1252 : : * can find it, and sign, if necessary, for unprivileged users.
1253 : : */
1254 [ + - ]: 35 : string uprobes_ko = new_staptmpdir + "/uprobes/uprobes.ko";
1255 [ + - ][ - + ]: 35 : if (get_file_size(uprobes_ko) > 0)
1256 : : {
1257 : : /* uprobes.ko is required.
1258 : : *
1259 : : * It's already underneath the stap tmpdir, but older stap clients
1260 : : * don't know to look for it there, so, for these clients, we end up packing uprobes twice
1261 : : * into the zip. We could move instead of symlink.
1262 : : */
1263 [ # # ]: 0 : string uprobes_response;
1264 [ # # ][ # # ]: 0 : if (client_version < "1.6")
[ # # ][ # # ]
1265 : : {
1266 [ # # ][ # # ]: 0 : uprobes_response = (string)responseDirName + "/uprobes.ko";
[ # # ][ # # ]
[ # # ]
1267 [ # # ][ # # ]: 0 : rc = symlink(uprobes_ko.c_str(), uprobes_response.c_str());
1268 [ # # ]: 0 : if (rc != 0)
1269 : 0 : server_error (_F("Could not link to %s from %s",
1270 [ # # ][ # # ]: 0 : uprobes_ko.c_str(), uprobes_response.c_str()));
[ # # ][ # # ]
[ # # ]
1271 : : }
1272 : : else
1273 [ # # ]: 0 : uprobes_response = uprobes_ko;
1274 : :
1275 : : /* In unprivileged mode, we need a signature on uprobes as well. */
1276 [ # # ][ # # ]: 0 : if (! pr_contains (privilege, pr_stapdev))
1277 : : {
1278 : : sign_file (cert_db_path, server_cert_nickname(),
1279 [ # # ][ # # ]: 0 : uprobes_response, uprobes_response + ".sgn");
[ # # ][ # # ]
[ # # ][ # # ]
1280 [ # # ]: 0 : }
1281 : :
1282 : : }
1283 : :
1284 : : /* Free up all the arg string copies. Note that the first few were alloc'd
1285 : : by wordexp(), which wordfree() frees; others were hand-set to literal strings. */
1286 : 35 : wordfree (& words);
1287 : :
1288 : : // Filter paths prefixed with the server's home directory from the stdout and stderr
1289 : : // files in the response.
1290 [ + - ]: 35 : filter_response_file (stapstdout, responseDirName);
1291 [ + - ][ + - ]: 35 : filter_response_file (stapstderr, responseDirName);
[ + - ][ + - ]
[ + - ][ + - ]
[ + - ][ - + ]
[ + - ][ - + ]
[ + - ][ - + ]
[ + - ][ - + ]
[ + - ][ + - ]
1292 : :
1293 : : /* Sorry about the inconvenience. C string/file processing is such a pleasure. */
1294 : : }
1295 : :
1296 : :
1297 : : /* A front end for stap_spawn that handles stdin, stdout, stderr, switches to a working
1298 : : directory and returns overall success or failure. */
1299 : : static PRStatus
1300 : 70 : spawn_and_wait (const vector<string> &argv,
1301 : : const char* fd0, const char* fd1, const char* fd2,
1302 : : const char *pwd, const vector<string>& envVec)
1303 : : {
1304 : : pid_t pid;
1305 : : int rc;
1306 : : posix_spawn_file_actions_t actions;
1307 : 70 : int dotfd = -1;
1308 : :
1309 : : #define CHECKRC(msg) do { if (rc) { server_error (_(msg)); return PR_FAILURE; } } while (0)
1310 : :
1311 : 70 : rc = posix_spawn_file_actions_init (& actions);
1312 [ - + ][ # # ]: 70 : CHECKRC ("Error in spawn file actions ctor");
[ # # ][ # # ]
1313 [ + + ]: 70 : if (fd0) {
1314 : 35 : rc = posix_spawn_file_actions_addopen(& actions, 0, fd0, O_RDONLY, 0600);
1315 [ - + ][ # # ]: 35 : CHECKRC ("Error in spawn file actions fd0");
[ # # ][ # # ]
1316 : : }
1317 [ + + ]: 70 : if (fd1) {
1318 : 35 : rc = posix_spawn_file_actions_addopen(& actions, 1, fd1, O_WRONLY|O_CREAT, 0600);
1319 [ - + ][ # # ]: 35 : CHECKRC ("Error in spawn file actions fd1");
[ # # ][ # # ]
1320 : : }
1321 [ + + ]: 70 : if (fd2) {
1322 : : // Use append mode for stderr because it gets written to in other places in the server.
1323 : 35 : rc = posix_spawn_file_actions_addopen(& actions, 2, fd2, O_WRONLY|O_APPEND|O_CREAT, 0600);
1324 [ - + ][ # # ]: 35 : CHECKRC ("Error in spawn file actions fd2");
[ # # ][ # # ]
1325 : : }
1326 : :
1327 : : /* change temporarily to a directory if requested */
1328 [ + - ]: 70 : if (pwd)
1329 : : {
1330 [ + - ]: 70 : dotfd = open (".", O_RDONLY);
1331 [ - + ]: 70 : if (dotfd < 0)
1332 : : {
1333 [ # # ][ # # ]: 0 : server_error (_("Error in spawn getcwd"));
[ # # ]
1334 : 0 : return PR_FAILURE;
1335 : : }
1336 : :
1337 : 70 : rc = chdir (pwd);
1338 [ - + ]: 70 : if (rc)
1339 : : {
1340 [ # # ]: 0 : close(dotfd);
1341 [ # # ][ # # ]: 0 : server_error(_("Error in spawn chdir"));
[ # # ]
1342 : 0 : return PR_FAILURE;
1343 : : }
1344 : : }
1345 : :
1346 [ + - ]: 70 : pid = stap_spawn (0, argv, & actions, envVec);
1347 : : /* NB: don't react to pid==-1 right away; need to chdir back first. */
1348 : :
1349 [ + - ][ + - ]: 70 : if (pwd && dotfd >= 0)
1350 : : {
1351 : : int subrc;
1352 : 70 : subrc = fchdir (dotfd);
1353 [ + - ]: 70 : subrc |= close (dotfd);
1354 [ - + ]: 70 : if (subrc)
1355 [ # # ][ # # ]: 0 : server_error (_("Error in spawn unchdir"));
[ # # ]
1356 : : }
1357 : :
1358 [ - + ]: 70 : if (pid == -1)
1359 : : {
1360 [ # # ][ # # ]: 0 : server_error (_F("Error in spawn: %s", strerror (errno)));
[ # # ]
1361 : 0 : return PR_FAILURE;
1362 : : }
1363 : :
1364 [ + - ]: 70 : rc = stap_waitpid (0, pid);
1365 [ - + ]: 70 : if (rc == -1)
1366 : : {
1367 [ # # ][ # # ]: 0 : server_error (_("Error in waitpid"));
[ # # ]
1368 : 0 : return PR_FAILURE;
1369 : : }
1370 : :
1371 : 70 : rc = posix_spawn_file_actions_destroy (&actions);
1372 [ - + ][ # # ]: 70 : CHECKRC ("Error in spawn file actions dtor");
[ # # ][ # # ]
1373 : :
1374 : 70 : return PR_SUCCESS;
1375 : : #undef CHECKRC
1376 : : }
1377 : :
1378 : : /* Function: void *handle_connection()
1379 : : *
1380 : : * Purpose: Handle a connection to a socket. Copy in request zip
1381 : : * file, process it, copy out response. Temporary directories are
1382 : : * created & destroyed here.
1383 : : */
1384 : :
1385 : : void *
1386 : 36 : handle_connection (void *arg)
1387 : : {
1388 : 36 : PRFileDesc * sslSocket = NULL;
1389 : 36 : SECStatus secStatus = SECFailure;
1390 : : PRStatus prStatus;
1391 : : int rc;
1392 : : char *rc1;
1393 : : char tmpdir[PATH_MAX];
1394 : : char requestFileName[PATH_MAX];
1395 : : char requestDirName[PATH_MAX];
1396 : : char responseDirName[PATH_MAX];
1397 : : char responseFileName[PATH_MAX];
1398 [ + - ]: 36 : string stapstderr; /* Cannot be global since we need a unique
1399 : : copy for each connection.*/
1400 [ + - ]: 36 : vector<string> argv;
1401 : : PRInt32 bytesRead;
1402 : :
1403 : : /* Detatch to avoid a memory leak */
1404 [ + - ]: 36 : if(max_threads > 0)
1405 : 36 : pthread_detach(pthread_self());
1406 : :
1407 : : /* Unpack the arg */
1408 : 36 : thread_arg *t_arg = (thread_arg *) arg;
1409 : 36 : PRFileDesc *tcpSocket = t_arg->tcpSocket;
1410 : 36 : CERTCertificate *cert = t_arg->cert;
1411 : 36 : SECKEYPrivateKey *privKey = t_arg->privKey;
1412 : 36 : PRNetAddr addr = t_arg->addr;
1413 : :
1414 : 36 : tmpdir[0]='\0'; /* prevent cleanup-time /bin/rm of uninitialized directory */
1415 : :
1416 : : #if 0 // already done on the listenSocket
1417 : : /* Make sure the socket is blocking. */
1418 : : PRSocketOptionData socketOption;
1419 : : socketOption.option = PR_SockOpt_Nonblocking;
1420 : : socketOption.value.non_blocking = PR_FALSE;
1421 : : PR_SetSocketOption (tcpSocket, &socketOption);
1422 : : #endif
1423 : 36 : secStatus = SECFailure;
1424 [ + - ]: 36 : sslSocket = setupSSLSocket (tcpSocket, cert, privKey);
1425 [ - + ]: 36 : if (sslSocket == NULL)
1426 : : {
1427 : : // Message already issued.
1428 : 0 : goto cleanup;
1429 : : }
1430 : :
1431 [ + - ]: 36 : secStatus = SSL_ResetHandshake(sslSocket, /* asServer */ PR_TRUE);
1432 [ - + ]: 35 : if (secStatus != SECSuccess)
1433 : : {
1434 [ # # ][ # # ]: 0 : server_error (_("Error resetting SSL handshake"));
[ # # ]
1435 [ # # ]: 0 : nssError ();
1436 : 0 : goto cleanup;
1437 : : }
1438 : :
1439 : : #if 0 // The client authenticates the server, so the client initiates the handshake
1440 : : /* Force the handshake to complete before moving on. */
1441 : : secStatus = SSL_ForceHandshake(sslSocket);
1442 : : if (secStatus != SECSuccess)
1443 : : {
1444 : : server_error (_("Error forcing SSL handshake"));
1445 : : nssError ();
1446 : : goto cleanup;
1447 : : }
1448 : : #endif
1449 : :
1450 : 35 : secStatus = SECFailure;
1451 [ - + ]: 35 : snprintf(tmpdir, PATH_MAX, "%s/stap-server.XXXXXX", getenv("TMPDIR") ?: "/tmp");
1452 : 36 : rc1 = mkdtemp(tmpdir);
1453 [ - + ]: 36 : if (! rc1)
1454 : : {
1455 [ # # ][ # # ]: 0 : server_error (_F("Could not create temporary directory %s: %s", tmpdir, strerror(errno)));
[ # # ]
1456 : 0 : tmpdir[0]=0; /* prevent /bin/rm */
1457 : 0 : goto cleanup;
1458 : : }
1459 : :
1460 : : /* Create a temporary files names and directories. */
1461 : 36 : snprintf (requestFileName, PATH_MAX, "%s/request.zip", tmpdir);
1462 : :
1463 : 36 : snprintf (requestDirName, PATH_MAX, "%s/request", tmpdir);
1464 : 36 : rc = mkdir(requestDirName, 0700);
1465 [ - + ]: 35 : if (rc)
1466 : : {
1467 [ # # ][ # # ]: 0 : server_error (_F("Could not create temporary directory %s: %s", requestDirName, strerror (errno)));
[ # # ]
1468 : 0 : goto cleanup;
1469 : : }
1470 : :
1471 : 35 : snprintf (responseDirName, PATH_MAX, "%s/response", tmpdir);
1472 : 35 : rc = mkdir(responseDirName, 0700);
1473 [ - + ]: 36 : if (rc)
1474 : : {
1475 [ # # ][ # # ]: 0 : server_error (_F("Could not create temporary directory %s: %s", responseDirName, strerror (errno)));
[ # # ]
1476 : 0 : goto cleanup;
1477 : : }
1478 : : // Set this early, since it gets used for errors to be returned to the client.
1479 [ + - ][ + - ]: 36 : stapstderr = string(responseDirName) + "/stderr";
[ + - ][ + - ]
[ + - ]
1480 : :
1481 : 36 : snprintf (responseFileName, PATH_MAX, "%s/response.zip", tmpdir);
1482 : :
1483 : : /* Read data from the socket.
1484 : : * If the user is requesting/requiring authentication, authenticate
1485 : : * the socket. */
1486 [ + - ]: 36 : bytesRead = readDataFromSocket(sslSocket, requestFileName);
1487 [ - + ]: 35 : if (bytesRead < 0) // Error
1488 : 0 : goto cleanup;
1489 [ + + ]: 35 : if (bytesRead == 0) // No request -- not an error
1490 : : {
1491 : 1 : secStatus = SECSuccess;
1492 : 1 : goto cleanup;
1493 : : }
1494 : :
1495 : : #if 0 /* Don't authenticate after each transaction */
1496 : : if (REQUEST_CERT_ALL)
1497 : : {
1498 : : secStatus = authenticateSocket(sslSocket);
1499 : : if (secStatus != SECSuccess)
1500 : : goto cleanup;
1501 : : }
1502 : : #endif
1503 : :
1504 : : /* Unzip the request. */
1505 : 34 : secStatus = SECFailure;
1506 [ + - ][ + - ]: 34 : argv.push_back ("unzip");
[ + - ]
1507 [ + - ][ + - ]: 35 : argv.push_back ("-q");
[ + - ]
1508 [ + - ][ + - ]: 35 : argv.push_back ("-d");
[ + - ]
1509 [ + - ][ + - ]: 35 : argv.push_back (requestDirName);
[ + - ]
1510 [ + - ][ + - ]: 35 : argv.push_back (requestFileName);
[ + - ]
1511 [ + - ]: 35 : rc = stap_system (0, argv);
1512 [ - + ]: 35 : if (rc != 0)
1513 : : {
1514 [ # # ][ # # ]: 0 : server_error (_("Unable to extract client request"));
[ # # ]
1515 : 0 : goto cleanup;
1516 : : }
1517 : :
1518 : : /* Handle the request zip file. An error therein should still result
1519 : : in a response zip file (containing stderr etc.) so we don't have to
1520 : : have a result code here. */
1521 [ + - ][ + - ]: 35 : handleRequest(requestDirName, responseDirName, stapstderr);
[ + - ][ + - ]
[ + - ][ + - ]
[ + - ]
1522 : :
1523 : : /* Zip the response. */
1524 [ + - ]: 35 : argv.clear ();
1525 [ + - ][ + - ]: 35 : argv.push_back ("zip");
[ + - ]
1526 [ + - ][ + - ]: 35 : argv.push_back ("-q");
[ + - ]
1527 [ + - ][ + - ]: 35 : argv.push_back ("-r");
[ + - ]
1528 [ + - ][ + - ]: 35 : argv.push_back (responseFileName);
[ + - ]
1529 [ + - ][ + - ]: 35 : argv.push_back (".");
[ + - ]
1530 [ + - ][ + - ]: 35 : rc = spawn_and_wait (argv, NULL, NULL, NULL, responseDirName);
[ + - ]
1531 [ - + ]: 35 : if (rc != PR_SUCCESS)
1532 : : {
1533 [ # # ][ # # ]: 0 : server_error (_("Unable to compress server response"));
[ # # ]
1534 : 0 : goto cleanup;
1535 : : }
1536 : :
1537 [ + - ]: 35 : secStatus = writeDataToSocket (sslSocket, responseFileName);
1538 : :
1539 : : cleanup:
1540 [ + - ]: 36 : if (sslSocket)
1541 [ + - ][ - + ]: 36 : if (PR_Close (sslSocket) != PR_SUCCESS)
1542 : : {
1543 [ # # ][ # # ]: 0 : server_error (_("Error closing ssl socket"));
[ # # ]
1544 [ # # ]: 0 : nssError ();
1545 : : }
1546 : :
1547 [ + - ]: 36 : if (tmpdir[0])
1548 : : {
1549 : : // Remove the whole tmpdir and all that lies beneath, unless -k was specified.
1550 [ - + ]: 36 : if (keep_temp)
1551 [ # # ][ # # ]: 0 : log (_F("Keeping temporary directory %s", tmpdir));
[ # # ]
1552 : : else
1553 : : {
1554 [ + - ]: 36 : argv.clear ();
1555 [ + - ][ + - ]: 36 : argv.push_back ("rm");
[ + - ]
1556 [ + - ][ + - ]: 36 : argv.push_back ("-r");
[ + - ]
1557 [ + - ][ + - ]: 36 : argv.push_back (tmpdir);
[ + - ]
1558 [ + - ]: 36 : rc = stap_system (0, argv);
1559 [ - + ]: 36 : if (rc != 0)
1560 [ # # ][ # # ]: 0 : server_error (_("Error in tmpdir cleanup"));
[ # # ]
1561 : : }
1562 : : }
1563 : :
1564 [ - + ]: 36 : if (secStatus != SECSuccess)
1565 [ # # ][ # # ]: 0 : server_error (_("Error processing client request"));
[ # # ]
1566 : :
1567 : : // Log the end of the request.
1568 : : char buf[1024];
1569 [ + - ]: 36 : prStatus = PR_NetAddrToString (& addr, buf, sizeof (buf));
1570 [ + - ]: 36 : if (prStatus == PR_SUCCESS)
1571 : : {
1572 [ - + ]: 36 : if (addr.raw.family == PR_AF_INET)
1573 [ # # ][ # # ]: 0 : log (_F("Request from %s:%d complete", buf, addr.inet.port));
[ # # ]
1574 [ + - ]: 36 : else if (addr.raw.family == PR_AF_INET6)
1575 [ + - ][ + - ]: 36 : log (_F("Request from [%s]:%d complete", buf, addr.ipv6.port));
[ + - ]
1576 : : }
1577 : :
1578 : : /* Increment semephore to indicate this thread is finished. */
1579 : 36 : free(t_arg);
1580 [ + - ]: 36 : if (max_threads > 0)
1581 : : {
1582 : 36 : sem_post(&sem_client);
1583 : 36 : pthread_exit(0);
1584 : : }
1585 : : else
1586 [ # # ][ # # ]: 36 : return 0;
1587 : : }
1588 : :
1589 : : /* Function: int accept_connection()
1590 : : *
1591 : : * Purpose: Accept a connection to the socket.
1592 : : *
1593 : : */
1594 : : static SECStatus
1595 : 5 : accept_connections (PRFileDesc *listenSocket, CERTCertificate *cert)
1596 : : {
1597 : : PRNetAddr addr;
1598 : : PRFileDesc *tcpSocket;
1599 : : PRStatus prStatus;
1600 : : SECStatus secStatus;
1601 : : CERTCertDBHandle *dbHandle;
1602 : : pthread_t tid;
1603 : : thread_arg *t_arg;
1604 : :
1605 : :
1606 [ + - ]: 5 : dbHandle = CERT_GetDefaultCertDB ();
1607 : :
1608 : : // cert_db_path gets passed to nssPasswordCallback.
1609 [ + - ][ + - ]: 5 : SECKEYPrivateKey *privKey = PK11_FindKeyByAnyCert (cert, (void*)cert_db_path.c_str ());
1610 [ - + ]: 5 : if (privKey == NULL)
1611 : : {
1612 [ # # ][ # # ]: 0 : server_error (_("Unable to obtain certificate private key"));
[ # # ]
1613 [ # # ]: 0 : nssError ();
1614 : 0 : return SECFailure;
1615 : : }
1616 : :
1617 [ + + ]: 150 : while (pending_interrupts == 0)
1618 : : {
1619 : : /* Accept a connection to the socket. */
1620 [ + - ]: 145 : tcpSocket = PR_Accept (listenSocket, &addr, PR_INTERVAL_MIN);
1621 [ + + ]: 145 : if (tcpSocket == NULL)
1622 : : {
1623 [ + - ][ + - ]: 109 : if(PR_GetError() == PR_IO_TIMEOUT_ERROR)
1624 : 109 : continue;
1625 : : else
1626 : : {
1627 [ # # ][ # # ]: 0 : server_error (_("Error accepting client connection"));
[ # # ]
1628 : : break;
1629 : : }
1630 : : }
1631 : :
1632 : : /* Log the accepted connection. */
1633 : : char buf[1024];
1634 [ + - ]: 36 : prStatus = PR_NetAddrToString (&addr, buf, sizeof (buf));
1635 [ + - ]: 36 : if (prStatus == PR_SUCCESS)
1636 : : {
1637 [ - + ]: 36 : if (addr.raw.family == PR_AF_INET)
1638 [ # # ][ # # ]: 0 : log (_F("Accepted connection from %s:%d", buf, addr.inet.port));
[ # # ]
1639 [ + - ]: 36 : else if (addr.raw.family == PR_AF_INET6)
1640 [ + - ][ + - ]: 36 : log (_F("Accepted connection from [%s]:%d", buf, addr.ipv6.port));
[ + - ]
1641 : : }
1642 : :
1643 : : /* XXX: alarm() or somesuch to set a timeout. */
1644 : :
1645 : : /* Accepted the connection, now handle it. */
1646 : :
1647 : : /* Wait for a thread to finish if there are none available */
1648 [ + - ]: 36 : if(max_threads >0)
1649 : : {
1650 : : int idle_threads;
1651 : 36 : sem_getvalue(&sem_client, &idle_threads);
1652 [ - + ]: 36 : if(idle_threads <= 0)
1653 [ # # ][ # # ]: 0 : log(_("Server is overloaded. Processing times may be longer than normal."));
[ # # ]
1654 [ + + ]: 36 : else if (idle_threads == max_threads)
1655 [ + - ][ + - ]: 31 : log(_("Processing 1 request..."));
[ + - ]
1656 : : else
1657 [ + - ][ + - ]: 5 : log(_F("Processing %d concurrent requests...", ((int)max_threads - idle_threads) + 1));
[ + - ]
1658 : :
1659 [ + - ]: 36 : sem_wait(&sem_client);
1660 : : }
1661 : :
1662 : : /* Create the argument structure to pass to pthread_create
1663 : : * (or directly to handle_connection if max_threads == 0 */
1664 : 36 : t_arg = (thread_arg *)malloc(sizeof(*t_arg));
1665 [ - + ]: 36 : if (t_arg == 0)
1666 [ # # ][ # # ]: 0 : fatal(_("No memory available for new thread arg!"));
[ # # ]
1667 : 36 : t_arg->tcpSocket = tcpSocket;
1668 : 36 : t_arg->cert = cert;
1669 : 36 : t_arg->privKey = privKey;
1670 : 36 : t_arg->addr = addr;
1671 : :
1672 : : /* Handle the conncection */
1673 [ + - ]: 36 : if (max_threads > 0)
1674 : : /* Create the worker thread and handle the connection. */
1675 : 36 : pthread_create(&tid, NULL, handle_connection, t_arg);
1676 : : else
1677 : : /* Since max_threads == 0, don't spawn a new thread,
1678 : : * just handle in the current thread. */
1679 [ # # ]: 0 : handle_connection(t_arg);
1680 : :
1681 : : // If our certificate is no longer valid (e.g. has expired), then exit.
1682 : : secStatus = CERT_VerifyCertNow (dbHandle, cert, PR_TRUE/*checkSig*/,
1683 [ + - ]: 36 : certUsageSSLServer, NULL/*wincx*/);
1684 [ + - ]: 36 : if (secStatus != SECSuccess)
1685 : : {
1686 : : // Not an error. Exit the loop so a new cert can be generated.
1687 : : break;
1688 : : }
1689 : : }
1690 : :
1691 [ + - ]: 5 : SECKEY_DestroyPrivateKey (privKey);
1692 : 5 : return SECSuccess;
1693 : : }
1694 : :
1695 : : /* Function: void server_main()
1696 : : *
1697 : : * Purpose: This is the server's main function. It configures a socket
1698 : : * and listens to it.
1699 : : *
1700 : : */
1701 : : static SECStatus
1702 : 5 : server_main (PRFileDesc *listenSocket)
1703 : : {
1704 : : int idle_threads;
1705 : 5 : int timeout = 0;
1706 : :
1707 : : // Initialize NSS.
1708 [ + - ][ + - ]: 5 : SECStatus secStatus = nssInit (cert_db_path.c_str ());
1709 [ - + ]: 5 : if (secStatus != SECSuccess)
1710 : : {
1711 : : // Message already issued.
1712 : 0 : return secStatus;
1713 : : }
1714 : :
1715 : : // Preinitialized here due to jumps to the label 'done'.
1716 : 5 : CERTCertificate *cert = NULL;
1717 : 5 : bool serverCacheConfigured = false;
1718 : :
1719 : : // Enable cipher suites which are allowed by U.S. export regulations.
1720 : : // NB: The NSS docs say that SSL_ClearSessionCache is required for the new settings to take
1721 : : // effect, however, calling it puts NSS in a state where it will not shut down cleanly.
1722 : : // We need to be able to shut down NSS cleanly if we are to generate a new certificate when
1723 : : // ours expires. It should be noted however, thet SSL_ClearSessionCache only clears the
1724 : : // client cache, and we are a server.
1725 [ + - ]: 5 : secStatus = NSS_SetExportPolicy ();
1726 : : // SSL_ClearSessionCache ();
1727 [ - + ]: 5 : if (secStatus != SECSuccess)
1728 : : {
1729 [ # # ][ # # ]: 0 : server_error (_("Unable to set NSS export policy"));
[ # # ]
1730 [ # # ]: 0 : nssError ();
1731 : 0 : goto done;
1732 : : }
1733 : :
1734 : : // Configure the SSL session cache for a single process server with the default settings.
1735 [ + - ]: 5 : secStatus = SSL_ConfigServerSessionIDCache (0, 0, 0, NULL);
1736 [ - + ]: 5 : if (secStatus != SECSuccess)
1737 : : {
1738 [ # # ][ # # ]: 0 : server_error (_("Unable to configure SSL server session ID cache"));
[ # # ]
1739 [ # # ]: 0 : nssError ();
1740 : 0 : goto done;
1741 : : }
1742 : 5 : serverCacheConfigured = true;
1743 : :
1744 : : /* Get own certificate. */
1745 [ + - ][ + - ]: 5 : cert = PK11_FindCertFromNickname (server_cert_nickname (), NULL);
1746 [ - + ]: 5 : if (cert == NULL)
1747 : : {
1748 : 0 : server_error (_F("Unable to find our certificate in the database at %s",
1749 [ # # ][ # # ]: 0 : cert_db_path.c_str ()));
[ # # ][ # # ]
1750 [ # # ]: 0 : nssError ();
1751 : 0 : goto done;
1752 : : }
1753 : :
1754 : : // Tell the world that we're listening.
1755 [ + - ]: 5 : advertise_presence (cert);
1756 : :
1757 : : /* Handle connections to the socket. */
1758 [ + - ]: 5 : secStatus = accept_connections (listenSocket, cert);
1759 : :
1760 : : // Tell the world we're no longer listening.
1761 [ + - ]: 5 : unadvertise_presence ();
1762 : :
1763 : 5 : sem_getvalue(&sem_client, &idle_threads);
1764 : :
1765 : : /* Wait for requests to finish or the timeout to be reached.
1766 : : * If we got here from an interrupt, exit immediately if
1767 : : * the timeout is reached. Otherwise, wait indefinitiely
1768 : : * until the threads exit (or an interrupt is recieved).*/
1769 [ - + ]: 5 : if(idle_threads < max_threads)
1770 [ # # ][ # # ]: 0 : log(_F("Waiting for %d outstanding requests to complete...", (int)max_threads - idle_threads));
[ # # ]
1771 [ - + ]: 5 : while(idle_threads < max_threads)
1772 : : {
1773 [ # # ][ # # ]: 0 : if(pending_interrupts && timeout++ > CONCURRENCY_TIMEOUT_S)
[ # # ]
1774 : : {
1775 [ # # ][ # # ]: 0 : log(_("Timeout reached, exiting (forced)"));
[ # # ]
1776 [ # # ]: 0 : kill_stap_spawn (SIGTERM);
1777 [ # # ]: 0 : cleanup ();
1778 : 0 : _exit(0);
1779 : : }
1780 [ # # ]: 0 : sleep(1);
1781 : 0 : sem_getvalue(&sem_client, &idle_threads);
1782 : : }
1783 : :
1784 : : done:
1785 : : // Clean up
1786 [ + - ]: 5 : if (cert)
1787 [ + - ]: 5 : CERT_DestroyCertificate (cert);
1788 : :
1789 : : // Shutdown NSS
1790 [ + - ][ + - ]: 5 : if (serverCacheConfigured && SSL_ShutdownServerSessionIDCache () != SECSuccess)
[ - + ][ - + ]
1791 : : {
1792 [ # # ][ # # ]: 0 : server_error (_("Unable to shut down server session ID cache"));
[ # # ]
1793 [ # # ]: 0 : nssError ();
1794 : : }
1795 [ + - ][ + - ]: 5 : nssCleanup (cert_db_path.c_str ());
1796 : :
1797 : 5 : return secStatus;
1798 : : }
1799 : :
1800 : : static void
1801 : 5 : listen ()
1802 : : {
1803 : : // Create a new socket.
1804 [ + - ]: 5 : PRFileDesc *listenSocket = PR_OpenTCPSocket (PR_AF_INET6); // Accepts IPv4 too
1805 [ - + ]: 5 : if (listenSocket == NULL)
1806 : : {
1807 [ # # ][ # # ]: 0 : server_error (_("Error creating socket"));
[ # # ]
1808 [ # # ]: 0 : nssError ();
1809 : 5 : return;
1810 : : }
1811 : :
1812 : : // Set socket to be blocking - on some platforms the default is nonblocking.
1813 : : PRSocketOptionData socketOption;
1814 : 5 : socketOption.option = PR_SockOpt_Nonblocking;
1815 : 5 : socketOption.value.non_blocking = PR_FALSE;
1816 [ + - ]: 5 : PRStatus prStatus = PR_SetSocketOption (listenSocket, & socketOption);
1817 [ - + ]: 5 : if (prStatus != PR_SUCCESS)
1818 : : {
1819 [ # # ][ # # ]: 0 : server_error (_("Error setting socket properties"));
[ # # ]
1820 [ # # ]: 0 : nssError ();
1821 : 0 : goto done;
1822 : : }
1823 : :
1824 : : // Allow the socket address to be reused, in case we want the same port across a
1825 : : // 'service stap-server restart'
1826 : 5 : socketOption.option = PR_SockOpt_Reuseaddr;
1827 : 5 : socketOption.value.reuse_addr = PR_TRUE;
1828 [ + - ]: 5 : prStatus = PR_SetSocketOption (listenSocket, & socketOption);
1829 [ - + ]: 5 : if (prStatus != PR_SUCCESS)
1830 : : {
1831 [ # # ][ # # ]: 0 : server_error (_("Error setting socket properties"));
[ # # ]
1832 [ # # ]: 0 : nssError ();
1833 : 0 : goto done;
1834 : : }
1835 : :
1836 : : // Configure the network connection.
1837 : : PRNetAddr addr;
1838 : 5 : memset (& addr, 0, sizeof(addr));
1839 [ + - ]: 5 : prStatus = PR_InitializeNetAddr (PR_IpAddrAny, port, & addr);
1840 : 5 : addr.ipv6.family = PR_AF_INET6;
1841 : : #if 0
1842 : : // addr.inet.ip = PR_htonl(PR_INADDR_ANY);
1843 : : PR_StringToNetAddr ("::", & addr);
1844 : : // PR_StringToNetAddr ("fe80::5eff:35ff:fe07:55ca", & addr);
1845 : : // PR_StringToNetAddr ("::1", & addr);
1846 : : addr.ipv6.port = PR_htons (port);
1847 : : #endif
1848 : :
1849 : : // Bind the socket to an address. Retry if the selected port is busy, unless the port was
1850 : : // specified directly.
1851 : 0 : for (;;)
1852 : : {
1853 : : /* Bind the address to the listener socket. */
1854 [ + - ]: 5 : prStatus = PR_Bind (listenSocket, & addr);
1855 [ + - ]: 5 : if (prStatus == PR_SUCCESS)
1856 : 5 : break;
1857 : :
1858 : : // If the selected port is busy. Try another, but only if a specific port was not specified.
1859 [ # # ]: 0 : PRErrorCode errorNumber = PR_GetError ();
1860 [ # # # ]: 0 : switch (errorNumber)
1861 : : {
1862 : : case PR_ADDRESS_NOT_AVAILABLE_ERROR:
1863 [ # # ]: 0 : if (port == 0)
1864 : : {
1865 [ # # ][ # # ]: 0 : server_error (_F("Network port %hu is unavailable. Trying another port", port));
[ # # ]
1866 : 0 : continue;
1867 : : }
1868 : 0 : break;
1869 : : case PR_ADDRESS_IN_USE_ERROR:
1870 [ # # ]: 0 : if (port == 0)
1871 : : {
1872 [ # # ][ # # ]: 0 : server_error (_F("Network port %hu is busy. Trying another port", port));
[ # # ]
1873 : 0 : continue;
1874 : : }
1875 : 0 : break;
1876 : : default:
1877 : 0 : break;
1878 : : }
1879 [ # # ][ # # ]: 0 : server_error (_("Error setting socket address"));
[ # # ]
1880 [ # # ]: 0 : nssError ();
1881 : 0 : goto done;
1882 : : }
1883 : :
1884 : : // Query the socket for the port that was assigned.
1885 [ + - ]: 5 : prStatus = PR_GetSockName (listenSocket, &addr);
1886 [ - + ]: 5 : if (prStatus != PR_SUCCESS)
1887 : : {
1888 [ # # ][ # # ]: 0 : server_error (_("Unable to obtain socket address"));
[ # # ]
1889 [ # # ]: 0 : nssError ();
1890 : 0 : goto done;
1891 : : }
1892 : : char buf[1024];
1893 [ + - ]: 5 : prStatus = PR_NetAddrToString (&addr, buf, sizeof (buf));
1894 [ + - ]: 5 : port = PR_ntohs (addr.ipv6.port);
1895 [ + - ][ + - ]: 5 : log (_F("Using network address [%s]:%hu", buf, port));
[ + - ]
1896 : :
1897 [ + - ]: 5 : if (max_threads > 0)
1898 [ + - ][ + - ]: 5 : log (_F("Using a maximum of %ld threads", max_threads));
[ + - ]
1899 : : else
1900 [ # # ][ # # ]: 0 : log (_("Concurrency disabled"));
[ # # ]
1901 : :
1902 : : // Listen for connection on the socket. The second argument is the maximum size of the queue
1903 : : // for pending connections.
1904 [ + - ]: 5 : prStatus = PR_Listen (listenSocket, 5);
1905 [ - + ]: 5 : if (prStatus != PR_SUCCESS)
1906 : : {
1907 [ # # ][ # # ]: 0 : server_error (_("Error listening on socket"));
[ # # ]
1908 [ # # ]: 0 : nssError ();
1909 : 0 : goto done;
1910 : : }
1911 : :
1912 : : /* Initialize semephore with the maximum number of threads
1913 : : * defined by --max-threads. If it is not defined, the
1914 : : * default is the number of processors */
1915 : 5 : sem_init(&sem_client, 0, max_threads);
1916 : :
1917 : : // Loop forever. We check our certificate (and regenerate, if necessary) and then start the
1918 : : // server. The server will go down when our certificate is no longer valid (e.g. expired). We
1919 : : // then generate a new one and start the server again.
1920 [ + + ]: 10 : while(!pending_interrupts)
1921 : : {
1922 : : // Ensure that our certificate is valid. Generate a new one if not.
1923 [ + - ][ + - ]: 5 : if (check_cert (cert_db_path, server_cert_nickname (), use_db_password) != 0)
[ + - ][ + - ]
[ - + ]
1924 : : {
1925 : : // Message already issued
1926 : 0 : goto done;
1927 : : }
1928 : :
1929 : : // Ensure that our certificate is trusted by our local client.
1930 : : // Construct the client database path relative to the server database path.
1931 : : SECStatus secStatus = add_client_cert (server_cert_file (),
1932 [ + - ][ + - ]: 5 : local_client_cert_db_path ());
[ + - ][ + - ]
[ + - ]
1933 [ - + ]: 5 : if (secStatus != SECSuccess)
1934 : : {
1935 [ # # ][ # # ]: 0 : server_error (_("Unable to authorize certificate for the local client"));
[ # # ]
1936 : 0 : goto done;
1937 : : }
1938 : :
1939 : : // Launch the server.
1940 [ + - ]: 5 : secStatus = server_main (listenSocket);
1941 : : } // loop forever
1942 : :
1943 : : done:
1944 : 5 : sem_destroy(&sem_client); /*Not really necessary, as we are shutting down...but for correctness */
1945 [ + - ][ - + ]: 5 : if (PR_Close (listenSocket) != PR_SUCCESS)
1946 : : {
1947 [ # # ][ # # ]: 0 : server_error (_("Error closing listen socket"));
[ # # ]
1948 [ # # ]: 5 : nssError ();
1949 : : }
1950 : : }
1951 : :
1952 : : int
1953 : 5 : main (int argc, char **argv) {
1954 : 5 : initialize (argc, argv);
1955 : 5 : listen ();
1956 : 5 : cleanup ();
1957 : 5 : return 0;
1958 [ + - ][ + - ]: 15 : }
1959 : :
1960 : : /* vim: set sw=2 ts=8 cino=>4,n-2,{2,^-2,t0,(0,u0,w1,M1 : */
|