]> sourceware.org Git - systemtap.git/blame - stap-serverd.cxx
PR32209: Improve the fix per bugzilla comment #6
[systemtap.git] / stap-serverd.cxx
CommitLineData
aeb9cc10
DB
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
6cb00a60 6 Copyright (C) 2011-2018 Red Hat Inc.
aeb9cc10
DB
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
e8daaf60 19 along with this program. If not, see <http://www.gnu.org/licenses/>.
aeb9cc10
DB
20*/
21#include "config.h"
22
c89fe89c 23#include <fstream>
aeb9cc10
DB
24#include <string>
25#include <cerrno>
26#include <cassert>
fd14bd8f 27#include <climits>
e4e3d6b7 28#include <iostream>
e4e3d6b7 29#include <map>
636e55b8 30#include <thread>
aeb9cc10
DB
31
32extern "C" {
e14c865b 33#include <unistd.h>
aeb9cc10
DB
34#include <getopt.h>
35#include <wordexp.h>
36#include <glob.h>
37#include <fcntl.h>
38#include <sys/stat.h>
39#include <sys/utsname.h>
38452904
DB
40#include <sys/types.h>
41#include <pwd.h>
49398925 42#include <semaphore.h>
aeb9cc10
DB
43
44#include <nspr.h>
45#include <ssl.h>
46#include <nss.h>
47#include <keyhi.h>
e4e3d6b7 48#include <regex.h>
b3367f63 49#include <dirent.h>
86faa85b
DS
50#include <string.h>
51#include <sys/ioctl.h>
aeb9cc10
DB
52
53#if HAVE_AVAHI
54#include <avahi-client/publish.h>
55#include <avahi-common/alternative.h>
56a0fe43 56#include <avahi-common/thread-watch.h>
aeb9cc10
DB
57#include <avahi-common/malloc.h>
58#include <avahi-common/error.h>
1c82016e 59#include <avahi-common/domain.h>
86faa85b 60#include <sys/inotify.h>
aeb9cc10
DB
61#endif
62}
63
64#include "util.h"
65#include "nsscommon.h"
cc7c72cd 66#include "cscommon.h"
73f52eb4 67#include "cmdline.h"
aeb9cc10
DB
68
69using namespace std;
70
71static void cleanup ();
e7d52b3b 72static PRStatus spawn_and_wait (const vector<string> &argv, int *result,
5ed19be2 73 const char* fd0, const char* fd1, const char* fd2,
878b2f3f 74 const char *pwd, const vector<string>& envVec = vector<string> ());
aeb9cc10 75
a32d236c 76
aeb9cc10
DB
77/* getopt variables */
78extern int optind;
79
4a044a5e 80/* File scope statics. Set during argument parsing and initialization. */
aeb9cc10 81static bool use_db_password;
49398925
CM
82static unsigned short port;
83static long max_threads;
ed4873c2 84static size_t max_uncompressed_req_size;
c4798c0f 85static size_t max_compressed_req_size;
aeb9cc10
DB
86static string cert_db_path;
87static string stap_options;
3532c11d 88static map<string,string> kernel_build_tree; // Kernel version -> build tree
aeb9cc10
DB
89static string arch;
90static string cert_serial_number;
91static string B_options;
92static string I_options;
93static string R_option;
071de8a6 94static string D_options;
71a522b5 95static bool keep_temp;
95fe6e8d 96static string mok_path;
aeb9cc10 97
49398925 98sem_t sem_client;
26a39006 99static int pending_interrupts;
26a39006 100#define CONCURRENCY_TIMEOUT_S 3
16560657
DB
101
102// Message handling.
103// Server_error messages are printed to stderr and logged, if requested.
104static void
105server_error (const string &msg, int logit = true)
aeb9cc10 106{
16560657 107 cerr << msg << endl << flush;
aeb9cc10
DB
108 // Log it, but avoid repeated messages to the terminal.
109 if (logit && log_ok ())
110 log (msg);
111}
112
16560657
DB
113// client_error messages are treated as server errors and also printed to the client's stderr.
114static void
49398925 115client_error (const string &msg, string stapstderr)
16560657
DB
116{
117 server_error (msg);
118 if (! stapstderr.empty ())
119 {
120 ofstream errfile;
121 errfile.open (stapstderr.c_str (), ios_base::app);
122 if (! errfile.good ())
123 server_error (_F("Could not open client stderr file %s: %s", stapstderr.c_str (),
124 strerror (errno)));
125 else
126 errfile << "Server: " << msg << endl;
127 // NB: No need to close errfile
128 }
129}
130
131// Messages from the nss common code are treated as server errors.
132extern "C"
133void
134nsscommon_error (const char *msg, int logit)
135{
136 server_error (msg, logit);
137}
138
139// Fatal errors are treated as server errors but also result in termination
aeb9cc10
DB
140// of the server.
141static void
142fatal (const string &msg)
143{
16560657 144 server_error (msg);
aeb9cc10
DB
145 cleanup ();
146 exit (1);
147}
148
149// Argument handling
150static void
151process_a (const string &arg)
152{
153 arch = arg;
154 stap_options += " -a " + arg;
155}
156
157static void
158process_r (const string &arg)
159{
160 if (arg[0] == '/') // fully specified path
3532c11d 161 kernel_build_tree.insert({kernel_release_from_build_tree(arg), arg});
aeb9cc10 162 else
3532c11d 163 kernel_build_tree.insert({arg, "/lib/modules/" + arg + "/build" });
aeb9cc10
DB
164}
165
166static void
167process_log (const char *arg)
168{
169 start_log (arg);
170}
171
172static void
173parse_options (int argc, char **argv)
174{
73f52eb4
DB
175 // Examine the command line. This is the command line for us (stap-serverd) not the command
176 // line for spawned stap instances.
177 optind = 1;
aeb9cc10
DB
178 while (true)
179 {
aeb9cc10 180 char *num_endptr;
49398925 181 long port_tmp;
ed4873c2 182 long maxsize_tmp;
372c6458
DB
183 // NB: The values of these enumerators must not conflict with the values of ordinary
184 // characters, since those are returned by getopt_long for short options.
185 enum {
186 LONG_OPT_PORT = 256,
187 LONG_OPT_SSL,
188 LONG_OPT_LOG,
ed4873c2 189 LONG_OPT_MAXTHREADS,
88ce17cc
AJ
190 LONG_OPT_MAXREQSIZE = 254,
191 LONG_OPT_MAXCOMPRESSEDREQ = 255 /* need to set a value otherwise there are conflicts */
372c6458 192 };
aeb9cc10 193 static struct option long_options[] = {
372c6458
DB
194 { "port", 1, NULL, LONG_OPT_PORT },
195 { "ssl", 1, NULL, LONG_OPT_SSL },
196 { "log", 1, NULL, LONG_OPT_LOG },
197 { "max-threads", 1, NULL, LONG_OPT_MAXTHREADS },
ed4873c2 198 { "max-request-size", 1, NULL, LONG_OPT_MAXREQSIZE},
88ce17cc 199 { "max-compressed-request", 1, NULL, LONG_OPT_MAXCOMPRESSEDREQ},
aeb9cc10
DB
200 { NULL, 0, NULL, 0 }
201 };
071de8a6 202 int grc = getopt_long (argc, argv, "a:B:D:I:kPr:R:", long_options, NULL);
aeb9cc10
DB
203 if (grc < 0)
204 break;
205 switch (grc)
206 {
207 case 'a':
208 process_a (optarg);
209 break;
210 case 'B':
071de8a6
DB
211 B_options += string (" -") + (char)grc + optarg;
212 stap_options += string (" -") + (char)grc + optarg;
213 break;
214 case 'D':
215 D_options += string (" -") + (char)grc + optarg;
aeb9cc10
DB
216 stap_options += string (" -") + (char)grc + optarg;
217 break;
218 case 'I':
071de8a6 219 I_options += string (" -") + (char)grc + optarg;
aeb9cc10
DB
220 stap_options += string (" -") + (char)grc + optarg;
221 break;
71a522b5
DB
222 case 'k':
223 keep_temp = true;
224 break;
aeb9cc10
DB
225 case 'P':
226 use_db_password = true;
227 break;
228 case 'r':
229 process_r (optarg);
230 break;
231 case 'R':
071de8a6 232 R_option = string (" -") + (char)grc + optarg;
aeb9cc10
DB
233 stap_options += string (" -") + (char)grc + optarg;
234 break;
372c6458
DB
235 case LONG_OPT_PORT:
236 port_tmp = strtol (optarg, &num_endptr, 10);
237 if (*num_endptr != '\0')
238 fatal (_F("%s: cannot parse number '--port=%s'", argv[0], optarg));
239 else if (port_tmp < 0 || port_tmp > 65535)
240 fatal (_F("%s: invalid entry: port must be between 0 and 65535 '--port=%s'", argv[0],
241 optarg));
242 else
243 port = (unsigned short) port_tmp;
244 break;
245 case LONG_OPT_SSL:
246 cert_db_path = optarg;
247 break;
248 case LONG_OPT_LOG:
249 process_log (optarg);
250 break;
251 case LONG_OPT_MAXTHREADS:
252 max_threads = strtol (optarg, &num_endptr, 0);
253 if (*num_endptr != '\0')
254 fatal (_F("%s: cannot parse number '--max-threads=%s'", argv[0], optarg));
255 else if (max_threads < 0)
256 fatal (_F("%s: invalid entry: max threads must not be negative '--max-threads=%s'",
257 argv[0], optarg));
258 break;
ed4873c2
AJ
259 case LONG_OPT_MAXREQSIZE:
260 maxsize_tmp = strtoul(optarg, &num_endptr, 0); // store as a long for now
261 if (*num_endptr != '\0')
262 fatal (_F("%s: cannot parse number '--max-request-size=%s'", argv[0], optarg));
263 else if (maxsize_tmp < 1)
8c074dc4 264 fatal (_F("%s: invalid entry: max (uncompressed) request size must be greater than 0 '--max-request-size=%s'",
ed4873c2
AJ
265 argv[0], optarg));
266 max_uncompressed_req_size = (size_t) maxsize_tmp; // convert the long to an unsigned
267 break;
88ce17cc
AJ
268 case LONG_OPT_MAXCOMPRESSEDREQ:
269 maxsize_tmp = strtoul(optarg, &num_endptr, 0); // store as a long for now
270 if (*num_endptr != '\0')
271 fatal (_F("%s: cannot parse number '--max-compressed-request=%s'", argv[0], optarg));
272 else if (maxsize_tmp < 1)
8c074dc4 273 fatal (_F("%s: invalid entry: max compressed request size must be greater than 0 '--max-compressed-request=%s'",
88ce17cc
AJ
274 argv[0], optarg));
275 max_compressed_req_size = (size_t) maxsize_tmp; // convert the long to an unsigned
276 break;
aeb9cc10
DB
277 case '?':
278 // Invalid/unrecognized option given. Message has already been issued.
279 break;
280 default:
281 // Reached when one added a getopt option but not a corresponding switch/case:
282 if (optarg)
16560657 283 server_error (_F("%s: unhandled option '%c %s'", argv[0], (char)grc, optarg));
aeb9cc10 284 else
16560657 285 server_error (_F("%s: unhandled option '%c'", argv[0], (char)grc));
aeb9cc10 286 break;
aeb9cc10
DB
287 }
288 }
289
290 for (int i = optind; i < argc; i++)
16560657 291 server_error (_F("%s: unrecognized argument '%s'", argv[0], argv[i]));
aeb9cc10
DB
292}
293
3dc20443
DB
294static string
295server_cert_file ()
296{
297 return server_cert_db_path () + "/stap.cert";
298}
299
aeb9cc10
DB
300// Signal handling. When an interrupt is received, kill any spawned processes
301// and exit.
302extern "C"
303void
304handle_interrupt (int sig)
305{
26a39006 306 pending_interrupts++;
26a39006
CM
307 if(pending_interrupts >= 2)
308 {
309 log (_F("Received another signal %d, exiting (forced)", sig));
310 _exit(0);
311 }
5ed19be2 312 log (_F("Received signal %d, exiting", sig));
aeb9cc10
DB
313}
314
315static void
316setup_signals (sighandler_t handler)
317{
318 struct sigaction sa;
319
11c6c509 320 memset(&sa, 0, sizeof(sa));
aeb9cc10
DB
321 sa.sa_handler = handler;
322 sigemptyset (&sa.sa_mask);
323 if (handler != SIG_IGN)
324 {
325 sigaddset (&sa.sa_mask, SIGHUP);
326 sigaddset (&sa.sa_mask, SIGPIPE);
327 sigaddset (&sa.sa_mask, SIGINT);
328 sigaddset (&sa.sa_mask, SIGTERM);
329 sigaddset (&sa.sa_mask, SIGTTIN);
330 sigaddset (&sa.sa_mask, SIGTTOU);
039f8231 331 sigaddset (&sa.sa_mask, SIGXFSZ);
5ed19be2 332 sigaddset (&sa.sa_mask, SIGXCPU);
aeb9cc10
DB
333 }
334 sa.sa_flags = SA_RESTART;
335
336 sigaction (SIGHUP, &sa, NULL);
337 sigaction (SIGPIPE, &sa, NULL);
338 sigaction (SIGINT, &sa, NULL);
339 sigaction (SIGTERM, &sa, NULL);
340 sigaction (SIGTTIN, &sa, NULL);
341 sigaction (SIGTTOU, &sa, NULL);
039f8231 342 sigaction (SIGXFSZ, &sa, NULL);
5ed19be2 343 sigaction (SIGXCPU, &sa, NULL);
aeb9cc10
DB
344}
345
c419fab7
DS
346
347// Get the list of MOK fingerprints on the server. If
348// 'only_one_needed' is true, just return the first MOK.
349static void
350get_server_mok_fingerprints(vector<string> &mok_fingerprints, bool verbose,
351 bool only_one_needed)
352{
353 DIR *dirp;
354 struct dirent *direntp;
355 vector<string> temp;
356
357 // Clear the vector.
358 mok_fingerprints.clear ();
359
360 // The directory of machine owner keys (MOK) is optional, so if it
361 // doesn't exist, we don't worry about it.
362 dirp = opendir (mok_path.c_str ());
363 if (dirp == NULL)
364 {
365 // If the error isn't ENOENT (Directory does not exist), we've got
366 // a non-fatal error.
367 if (errno != ENOENT)
368 server_error (_F("Could not open server MOK directory %s: %s",
369 mok_path.c_str (), strerror (errno)));
370 return;
371 }
372
373 // Create a regular expression object to verify MOK fingerprints
374 // directory name.
375 regex_t checkre;
376 if ((regcomp (&checkre, "^[0-9a-f]{2}(:[0-9a-f]{2})+$",
377 REG_EXTENDED | REG_NOSUB) != 0))
378 {
379 // Not fatal, just ignore the MOK fingerprints.
380 server_error (_F("Error in MOK fingerprint regcomp: %s",
381 strerror (errno)));
382 closedir (dirp);
383 return;
384 }
385
386 // We've opened the directory, so read all the directory names from
387 // it.
388 while ((direntp = readdir (dirp)) != NULL)
389 {
390 // We're only interested in directories (of key files).
391 if (direntp->d_type != DT_DIR)
112095a2
QN
392 {
393 if (direntp->d_type == DT_UNKNOWN)
394 {
395 // If the filesystem doesn't support d_type, we'll have to
396 // call stat().
397 struct stat tmpstat;
32023754
FCE
398 int rc = stat((mok_path + "/" + direntp->d_name).c_str (), &tmpstat);
399 if (rc || !S_ISDIR(tmpstat.st_mode))
112095a2
QN
400 continue;
401 }
402 else
403 continue;
404 }
c419fab7
DS
405
406 // We've got a directory. If the directory name isn't in the right
407 // format for a MOK fingerprint, skip it.
408 if ((regexec (&checkre, direntp->d_name, (size_t) 0, NULL, 0) != 0))
409 continue;
410
411 // OK, we've got a directory name in the right format, so save it.
412 temp.push_back (string (direntp->d_name));
413 }
414 regfree (&checkre);
415 closedir (dirp);
416
417 // At this point, we've got a list of directories with names in the
418 // proper format. Make sure each directory contains a x509
419 // certificate and private key file.
420 vector<string>::const_iterator it;
421 for (it = temp.begin (); it != temp.end (); it++)
422 {
42be330a
SC
423 string mok_path = server_cert_db_path() + "/moks";
424 if (mok_dir_valid_p (*it, mok_path, true, server_error))
c419fab7
DS
425 {
426 // Save the info.
427 mok_fingerprints.push_back (*it);
428 if (verbose)
429 server_error (_F("Found MOK with fingerprint '%s'", it->c_str ()));
430 if (only_one_needed)
431 break;
432 }
433 }
434 return;
435}
436
aeb9cc10
DB
437#if HAVE_AVAHI
438static AvahiEntryGroup *avahi_group = NULL;
56a0fe43 439static AvahiThreadedPoll *avahi_threaded_poll = NULL;
aeb9cc10 440static char *avahi_service_name = NULL;
3c453875 441static const char * const avahi_service_tag = "_stap._tcp";
56a0fe43 442static AvahiClient *avahi_client = 0;
3c453875 443static int avahi_collisions = 0;
86faa85b
DS
444static int inotify_fd = -1;
445static AvahiWatch *avahi_inotify_watch = NULL;
aeb9cc10
DB
446
447static void create_services (AvahiClient *c);
448
3c453875
DB
449static int
450rename_service ()
451{
452 /*
453 * Each service must have a unique name on the local network.
454 * When there is a collision, we try to rename the service.
455 * However, we need to limit the number of attempts, since the
456 * service namespace could be maliciously flooded with service
457 * names designed to maximize collisions.
458 * Arbitrarily choose a limit of 65535, which is the number of
459 * TCP ports.
460 */
461 ++avahi_collisions;
462 if (avahi_collisions >= 65535) {
463 server_error (_F("Too many service name collisions for Avahi service %s",
464 avahi_service_tag));
465 return -EBUSY;
466 }
467
468 /*
469 * Use the avahi-supplied function to generate a new service name.
470 */
471 char *n = avahi_alternative_service_name(avahi_service_name);
472
473 server_error (_F("Avahi service name collision, renaming service '%s' to '%s'",
474 avahi_service_name, n));
475 avahi_free(avahi_service_name);
476 avahi_service_name = n;
477
478 return 0;
479}
480
aeb9cc10
DB
481static void
482entry_group_callback (
483 AvahiEntryGroup *g,
484 AvahiEntryGroupState state,
485 AVAHI_GCC_UNUSED void *userdata
486) {
487 assert(g == avahi_group || avahi_group == NULL);
488 avahi_group = g;
489
490 // Called whenever the entry group state changes.
491 switch (state)
492 {
493 case AVAHI_ENTRY_GROUP_ESTABLISHED:
494 // The entry group has been established successfully.
24a060a0 495 log (_F("Avahi service '%s' successfully established.", avahi_service_name));
aeb9cc10
DB
496 break;
497
3c453875
DB
498 case AVAHI_ENTRY_GROUP_COLLISION:
499 // A service name collision with a remote service happened.
500 // Unfortunately, we don't know which entry collided.
501 // We need to rename them all and recreate the services.
502 if (rename_service () == 0)
503 create_services (avahi_entry_group_get_client (g));
aeb9cc10 504 break;
aeb9cc10
DB
505
506 case AVAHI_ENTRY_GROUP_FAILURE:
24a060a0 507 // Some kind of failure happened.
16560657 508 server_error (_F("Avahi entry group failure: %s",
24a060a0 509 avahi_strerror (avahi_client_errno (avahi_entry_group_get_client (g)))));
aeb9cc10
DB
510 break;
511
512 case AVAHI_ENTRY_GROUP_UNCOMMITED:
513 case AVAHI_ENTRY_GROUP_REGISTERING:
514 break;
515 }
516}
517
518static void
86faa85b
DS
519create_services (AvahiClient *c)
520{
aeb9cc10
DB
521 assert (c);
522
3c453875 523 // Create a new entry group, if necessary, or reset the existing one.
aeb9cc10 524 if (! avahi_group)
aeb9cc10 525 {
3c453875 526 if (! (avahi_group = avahi_entry_group_new (c, entry_group_callback, NULL)))
3532c11d
FL
527 {
528 server_error (_F("avahi_entry_group_new () failed: %s",
529 avahi_strerror (avahi_client_errno (c))));
530 return;
531 }
3c453875
DB
532 }
533 else
534 avahi_entry_group_reset(avahi_group);
aeb9cc10 535
3c453875
DB
536 // Contruct the information needed for our service.
537 log (_F("Adding Avahi service '%s'", avahi_service_name));
538
4a48a4f9 539 AvahiStringList *strlst = NULL;
3532c11d
FL
540 int ret;
541 for (auto it = kernel_build_tree.cbegin(); it != kernel_build_tree.cend(); ++it)
542 {
543 // Create the txt tags that will be registered with our service.
544 string sysinfo = "sysinfo=" + it->first + " "+ arch;
545 string certinfo = "certinfo=" + cert_serial_number;
546 string version = string ("version=") + CURRENT_CS_PROTOCOL_VERSION;;
547 string optinfo = "optinfo=";
548 string separator;
549 // These option strings already have a leading space.
550 if (! R_option.empty ())
551 {
552 optinfo += R_option.substr(1);
553 separator = " ";
554 }
555 if (! B_options.empty ())
556 {
557 optinfo += separator + B_options.substr(1);
558 separator = " ";
559 }
560 if (! D_options.empty ())
561 {
562 optinfo += separator + D_options.substr(1);
563 separator = " ";
564 }
565 if (! I_options.empty ())
566 optinfo += separator + I_options.substr(1);
567
568 // Create an avahi string list with the info we have so far.
569 vector<string> mok_fingerprints;
570 strlst = avahi_string_list_new(sysinfo.c_str (),
571 optinfo.c_str (),
572 version.c_str (),
573 certinfo.c_str (), NULL);
574 if (strlst == NULL)
575 {
576 server_error (_("Failed to allocate string list"));
577 goto fail;
578 }
b3367f63 579
3532c11d
FL
580 // Add server MOK info, if available.
581 get_server_mok_fingerprints (mok_fingerprints, true, false);
582 if (! mok_fingerprints.empty())
b3367f63 583 {
3532c11d
FL
584 for (auto it = mok_fingerprints.cbegin(); it != mok_fingerprints.cend(); it++)
585 {
586 string tmp = "mok_info=" + *it;
587 strlst = avahi_string_list_add(strlst, tmp.c_str ());
588 if (strlst == NULL)
589 {
590 server_error (_("Failed to add a string to the list"));
591 goto fail;
592 }
593 }
594 }
b3367f63 595
3532c11d
FL
596 // We will now add our service to the entry group.
597 // Loop until no collisions.
598 for (;;) {
599 ret = avahi_entry_group_add_service_strlst (avahi_group,
600 AVAHI_IF_UNSPEC,
601 AVAHI_PROTO_UNSPEC,
602 (AvahiPublishFlags)0,
603 avahi_service_name,
604 avahi_service_tag,
605 NULL, NULL, port, strlst);
606 if (ret == AVAHI_OK)
607 break; // success!
608
609 if (ret == AVAHI_ERR_COLLISION)
610 {
611 // A service name collision with a local service happened.
612 // Pick a new name.
613 if (rename_service () < 0) {
614 // Too many collisions. Message already issued.
615 goto fail;
616 }
617 continue; // try again.
618 }
619
620 server_error (_F("Failed to add %s service: %s",
621 avahi_service_tag, avahi_strerror (ret)));
622 goto fail;
3c453875
DB
623 }
624
3532c11d 625 avahi_string_list_free(strlst);
4a48a4f9 626 strlst = NULL;
3532c11d 627 }
3c453875
DB
628
629 // Tell the server to register the service.
630 if ((ret = avahi_entry_group_commit (avahi_group)) < 0)
631 {
632 server_error (_F("Failed to commit avahi entry group: %s", avahi_strerror (ret)));
633 goto fail;
aeb9cc10 634 }
aeb9cc10
DB
635 return;
636
637 fail:
56a0fe43 638 avahi_entry_group_reset (avahi_group);
b3367f63 639 avahi_string_list_free(strlst);
aeb9cc10
DB
640}
641
24a060a0
DB
642static void avahi_cleanup_client () {
643 // This also frees the entry group, if any
644 if (avahi_client) {
645 avahi_client_free (avahi_client);
646 avahi_client = 0;
647 avahi_group = 0;
648 }
649}
650
aeb9cc10
DB
651static void
652client_callback (AvahiClient *c, AvahiClientState state, AVAHI_GCC_UNUSED void * userdata)
653{
654 assert(c);
655
656 // Called whenever the client or server state changes.
657 switch (state)
658 {
659 case AVAHI_CLIENT_S_RUNNING:
660 // The server has startup successfully and registered its host
661 // name on the network, so it's time to create our services.
662 create_services (c);
663 break;
664
665 case AVAHI_CLIENT_FAILURE:
16560657 666 server_error (_F("Avahi client failure: %s", avahi_strerror (avahi_client_errno (c))));
24a060a0
DB
667 if (avahi_client_errno (c) == AVAHI_ERR_DISCONNECTED)
668 {
669 // The client has been disconnected; probably because the avahi daemon has been
670 // restarted. We can free the client here and try to reconnect using a new one.
671 // Passing AVAHI_CLIENT_NO_FAIL allows the new client to be
672 // created, even if the avahi daemon is not running. Our service will be advertised
673 // if/when the daemon is started.
674 avahi_cleanup_client ();
675 int error;
676 avahi_client = avahi_client_new (avahi_threaded_poll_get (avahi_threaded_poll),
677 (AvahiClientFlags)AVAHI_CLIENT_NO_FAIL,
678 client_callback, NULL, & error);
679 }
aeb9cc10
DB
680 break;
681
682 case AVAHI_CLIENT_S_COLLISION:
683 // Let's drop our registered services. When the server is back
684 // in AVAHI_SERVER_RUNNING state we will register them
685 // again with the new host name.
686 // Fall through ...
687 case AVAHI_CLIENT_S_REGISTERING:
688 // The server records are now being established. This
689 // might be caused by a host name change. We need to wait
690 // for our own records to register until the host name is
691 // properly esatblished.
692 if (avahi_group)
693 avahi_entry_group_reset (avahi_group);
694 break;
695
696 case AVAHI_CLIENT_CONNECTING:
24a060a0
DB
697 // The avahi-daemon is not currently running. Our service will be advertised
698 // if/when the deamon is started.
699 server_error (_F("The Avahi daemon is not running. Avahi service '%s' will be established when the deamon is started", avahi_service_name));
aeb9cc10
DB
700 break;
701 }
702}
24a060a0 703
56a0fe43 704static void
dabd71bb 705inotify_callback (AvahiWatch *, int fd, AvahiWatchEvent, void *)
86faa85b 706{
2ee9d8cc
DS
707 struct inotify_event in_events[10];
708 ssize_t rc;
86faa85b
DS
709
710 // Drain the inotify file. Notice we don't really care what changed,
711 // we just needed to know that something changed.
2ee9d8cc
DS
712 do
713 {
714 rc = read (fd, in_events, sizeof (in_events));
715 } while (rc > 0);
86faa85b
DS
716
717 // Re-create the services.
718 if (avahi_client && (avahi_client_get_state (avahi_client)
719 == AVAHI_CLIENT_S_RUNNING))
720 create_services (avahi_client);
721}
722
723static void
724avahi_cleanup ()
725{
56a0fe43
DB
726 if (avahi_service_name)
727 log (_F("Removing Avahi service '%s'", avahi_service_name));
728
729 // Stop the avahi client, if it's running
730 if (avahi_threaded_poll)
731 avahi_threaded_poll_stop (avahi_threaded_poll);
732
733 // Clean up the avahi objects. The order of freeing these is significant.
24a060a0 734 avahi_cleanup_client ();
86faa85b
DS
735 if (avahi_inotify_watch)
736 {
737 const AvahiPoll *poll = avahi_threaded_poll_get (avahi_threaded_poll);
738 if (poll)
739 poll->watch_free (avahi_inotify_watch);
740 avahi_inotify_watch = NULL;
741 }
742 if (inotify_fd >= 0)
743 {
744 close (inotify_fd);
745 inotify_fd = -1;
746 }
56a0fe43
DB
747 if (avahi_threaded_poll) {
748 avahi_threaded_poll_free (avahi_threaded_poll);
749 avahi_threaded_poll = 0;
750 }
751 if (avahi_service_name) {
752 avahi_free (avahi_service_name);
753 avahi_service_name = 0;
754 }
755}
756
aeb9cc10 757// The entry point for the avahi client thread.
56a0fe43
DB
758static void
759avahi_publish_service (CERTCertificate *cert)
aeb9cc10 760{
3c453875 761 // Get the certificate serial number.
aeb9cc10 762 cert_serial_number = get_cert_serial_number (cert);
3c453875
DB
763
764 // Construct the Avahi service name.
765 char host[HOST_NAME_MAX + 1];
766 gethostname (host, sizeof(host));
767 host[sizeof(host) - 1] = '\0';
81f5fb74 768 string buf;
3c453875 769 buf = string ("Systemtap Compile Server on ") + host;
1c82016e
DB
770
771 // Make sure the service name is valid
772 const char *initial_service_name = buf.c_str ();
773 if (! avahi_is_valid_service_name (initial_service_name)) {
774 // The only restriction on service names is that the buffer must not exceed
775 // AVAHI_LABEL_MAX in size, which means that the name cannot be longer than
776 // AVAHI_LABEL_MAX-1 in length.
777 assert (strlen (initial_service_name) >= AVAHI_LABEL_MAX);
778 buf = buf.substr (0, AVAHI_LABEL_MAX - 1);
779 initial_service_name = buf.c_str ();
780 assert (avahi_is_valid_service_name (initial_service_name));
781 }
782 avahi_service_name = avahi_strdup (initial_service_name);
aeb9cc10 783
aeb9cc10 784 // Allocate main loop object.
56a0fe43 785 if (! (avahi_threaded_poll = avahi_threaded_poll_new ()))
aeb9cc10 786 {
16560657 787 server_error (_("Failed to create avahi threaded poll object."));
56a0fe43 788 return;
aeb9cc10
DB
789 }
790
24a060a0
DB
791 // Always allocate a new client. Passing AVAHI_CLIENT_NO_FAIL allows the client to be
792 // created, even if the avahi daemon is not running. Our service will be advertised
793 // if/when the daemon is started.
aeb9cc10 794 int error;
56a0fe43 795 avahi_client = avahi_client_new (avahi_threaded_poll_get (avahi_threaded_poll),
24a060a0 796 (AvahiClientFlags)AVAHI_CLIENT_NO_FAIL,
56a0fe43 797 client_callback, NULL, & error);
49dbe419 798 // Check whether creating the client object succeeded.
56a0fe43 799 if (! avahi_client)
aeb9cc10 800 {
16560657 801 server_error (_F("Failed to create avahi client: %s", avahi_strerror(error)));
56a0fe43 802 return;
aeb9cc10
DB
803 }
804
86faa85b 805 // Watch the server MOK directory for any changes.
2ee9d8cc
DS
806#if defined(IN_CLOEXEC) && defined(IN_NONBLOCK)
807 inotify_fd = inotify_init1 (IN_CLOEXEC|IN_NONBLOCK);
86faa85b
DS
808#else
809 if ((inotify_fd = inotify_init ()) >= 0)
2ee9d8cc
DS
810 {
811 fcntl(inotify_fd, F_SETFD, FD_CLOEXEC);
812 fcntl(inotify_fd, F_SETFL, O_NONBLOCK);
813 }
86faa85b
DS
814#endif
815 if (inotify_fd < 0)
816 server_error (_F("Failed to initialize inotify: %s", strerror (errno)));
817 else
818 {
2ee9d8cc
DS
819 // We want to watch for new or removed MOK directories
820 // underneath mok_path. But, to do that, mok_path must exist.
821 if (create_dir (mok_path.c_str (), 0755) != 0)
822 server_error (_F("Unable to find or create the MOK directory %s: %s",
823 mok_path.c_str (), strerror (errno)));
824 // Watch mok_path for changes.
825 else if (inotify_add_watch (inotify_fd, mok_path.c_str (),
86faa85b
DS
826#ifdef IN_ONLYDIR
827 IN_ONLYDIR|
828#endif
829 IN_CLOSE_WRITE|IN_DELETE|IN_DELETE_SELF|IN_MOVE)
830 < 0)
831 server_error (_F("Failed to add inotify watch: %s", strerror (errno)));
832 else
833 {
2ee9d8cc 834 // When mok_path changes, call inotify_callback().
86faa85b
DS
835 const AvahiPoll *poll = avahi_threaded_poll_get (avahi_threaded_poll);
836 if (!poll
837 || ! (avahi_inotify_watch = poll->watch_new (poll, inotify_fd,
838 AVAHI_WATCH_IN,
839 inotify_callback,
840 NULL)))
841 server_error (_("Failed to create inotify watcher"));
842 }
843 }
844
aeb9cc10 845 // Run the main loop.
56a0fe43
DB
846 avahi_threaded_poll_start (avahi_threaded_poll);
847
848 return;
aeb9cc10
DB
849}
850#endif // HAVE_AVAHI
851
852static void
853advertise_presence (CERTCertificate *cert __attribute ((unused)))
854{
855#if HAVE_AVAHI
56a0fe43 856 avahi_publish_service (cert);
aeb9cc10 857#else
16560657 858 server_error (_("Unable to advertise presence on the network. Avahi is not available"));
aeb9cc10
DB
859#endif
860}
861
862static void
863unadvertise_presence ()
864{
865#if HAVE_AVAHI
56a0fe43 866 avahi_cleanup ();
aeb9cc10
DB
867#endif
868}
869
b3367f63 870
b3367f63 871
b3367f63 872
aeb9cc10
DB
873static void
874initialize (int argc, char **argv) {
26a39006 875 pending_interrupts = 0;
aeb9cc10
DB
876 setup_signals (& handle_interrupt);
877
38452904
DB
878 // Seed the random number generator. Used to generate noise used during key generation.
879 srand (time (NULL));
880
881 // Initial values.
38452904
DB
882 use_db_password = false;
883 port = 0;
636e55b8 884 max_threads = thread::hardware_concurrency(); // Default to number of processors
8c074dc4
AJ
885 max_uncompressed_req_size = 50000; // 50 KB: default max uncompressed request size
886 max_compressed_req_size = 5000; // 5 KB: default max compressed request size
38452904
DB
887 keep_temp = false;
888 struct utsname utsname;
889 uname (& utsname);
3532c11d 890 kernel_build_tree.insert({utsname.release, "/lib/modules/" + string(utsname.release) + "/build"});
38452904
DB
891 arch = normalize_machine (utsname.machine);
892
893 // Parse the arguments. This also starts the server log, if any, and should be done before
894 // any messages are issued.
895 parse_options (argc, argv);
896
aeb9cc10 897 // PR11197: security prophylactics.
878b2f3f 898 // Reject use as root, except via a special environment variable.
aeb9cc10
DB
899 if (! getenv ("STAP_PR11197_OVERRIDE")) {
900 if (geteuid () == 0)
901 fatal ("For security reasons, invocation of stap-serverd as root is not supported.");
902 }
878b2f3f 903
38452904
DB
904 struct passwd *pw = getpwuid (geteuid ());
905 if (! pw)
906 fatal (_F("Unable to determine effective user name: %s", strerror (errno)));
907 string username = pw->pw_name;
aeb9cc10 908 pid_t pid = getpid ();
38452904 909 log (_F("===== compile server pid %d starting as %s =====", pid, username.c_str ()));
aeb9cc10
DB
910
911 // Where is the ssl certificate/key database?
912 if (cert_db_path.empty ())
913 cert_db_path = server_cert_db_path ();
914
915 // Make sure NSPR is initialized. Must be done before NSS is initialized
916 PR_Init (PR_SYSTEM_THREAD, PR_PRIORITY_NORMAL, 1);
917 /* Set the cert database password callback. */
918 PK11_SetPasswordFunc (nssPasswordCallback);
b3367f63 919
86faa85b
DS
920 // Where are the optional machine owner keys (MOK) this server
921 // knows about?
95fe6e8d 922 mok_path = server_cert_db_path() + "/moks";
aeb9cc10
DB
923}
924
925static void
926cleanup ()
927{
928 unadvertise_presence ();
929 end_log ();
930}
931
932/* Function: readDataFromSocket()
933 *
934 * Purpose: Read data from the socket into a temporary file.
935 *
936 */
937static PRInt32
938readDataFromSocket(PRFileDesc *sslSocket, const char *requestFileName)
939{
940 PRFileDesc *local_file_fd = 0;
941 PRInt32 numBytesExpected;
942 PRInt32 numBytesRead;
943 PRInt32 numBytesWritten;
4a044a5e 944 PRInt32 totalBytes = 0;
aeb9cc10
DB
945#define READ_BUFFER_SIZE 4096
946 char buffer[READ_BUFFER_SIZE];
947
5ab345f0 948 // Read the number of bytes to be received.
5ab345f0
DB
949 numBytesRead = PR_Read_Complete (sslSocket, & numBytesExpected,
950 (PRInt32)sizeof (numBytesExpected));
aeb9cc10
DB
951 if (numBytesRead == 0) /* EOF */
952 {
16560657 953 server_error (_("Error reading size of request file"));
aeb9cc10
DB
954 goto done;
955 }
956 if (numBytesRead < 0)
957 {
16560657 958 server_error (_("Error in PR_Read"));
aeb9cc10
DB
959 nssError ();
960 goto done;
961 }
962
963 /* Convert numBytesExpected from network byte order to host byte order. */
964 numBytesExpected = ntohl (numBytesExpected);
965
966 /* If 0 bytes are expected, then we were contacted only to obtain our certificate.
967 There is no client request. */
968 if (numBytesExpected == 0)
969 return 0;
970
c4798c0f
AJ
971 /* Impose a limit to prevent disk space consumption DoS */
972 if (numBytesExpected > (PRInt32) max_compressed_req_size)
973 {
974 server_error (_("Error size of (compressed) request file is too large"));
975 goto done;
976 }
977
aeb9cc10
DB
978 /* Open the output file. */
979 local_file_fd = PR_Open(requestFileName, PR_WRONLY | PR_CREATE_FILE | PR_TRUNCATE,
980 PR_IRUSR | PR_IWUSR);
981 if (local_file_fd == NULL)
982 {
16560657 983 server_error (_F("Could not open output file %s", requestFileName));
aeb9cc10
DB
984 nssError ();
985 return -1;
986 }
987
5ab345f0 988 // Read until EOF or until the expected number of bytes has been read.
aeb9cc10
DB
989 for (totalBytes = 0; totalBytes < numBytesExpected; totalBytes += numBytesRead)
990 {
5ab345f0
DB
991 // No need for PR_Read_Complete here, since we're already managing multiple
992 // reads to a fixed size buffer.
993 numBytesRead = PR_Read (sslSocket, buffer, READ_BUFFER_SIZE);
aeb9cc10
DB
994 if (numBytesRead == 0)
995 break; /* EOF */
996 if (numBytesRead < 0)
997 {
16560657 998 server_error (_("Error in PR_Read"));
aeb9cc10
DB
999 nssError ();
1000 goto done;
1001 }
1002
1003 /* Write to the request file. */
1004 numBytesWritten = PR_Write(local_file_fd, buffer, numBytesRead);
1005 if (numBytesWritten < 0 || (numBytesWritten != numBytesRead))
1006 {
16560657 1007 server_error (_F("Could not write to output file %s", requestFileName));
aeb9cc10
DB
1008 nssError ();
1009 goto done;
1010 }
1011 }
1012
1013 if (totalBytes != numBytesExpected)
1014 {
16560657 1015 server_error (_F("Expected %d bytes, got %d while reading client request from socket",
aeb9cc10
DB
1016 numBytesExpected, totalBytes));
1017 goto done;
1018 }
1019
1020 done:
1021 if (local_file_fd)
1022 PR_Close (local_file_fd);
1023 return totalBytes;
1024}
1025
1026/* Function: setupSSLSocket()
1027 *
1028 * Purpose: Configure a socket for SSL.
6cb00a60 1029 * In case of failure, clean up the incoming tcpSocket and/or any partially setup sslSocket.
aeb9cc10
DB
1030 *
1031 */
1032static PRFileDesc *
1033setupSSLSocket (PRFileDesc *tcpSocket, CERTCertificate *cert, SECKEYPrivateKey *privKey)
1034{
1035 PRFileDesc *sslSocket;
1036 SSLKEAType certKEA;
1037 SECStatus secStatus;
1038
1039 /* Inport the socket into SSL. */
1040 sslSocket = SSL_ImportFD (NULL, tcpSocket);
1041 if (sslSocket == NULL)
1042 {
16560657 1043 server_error (_("Could not import socket into SSL"));
6cb00a60 1044 goto error;
aeb9cc10
DB
1045 }
1046
1047 /* Set the appropriate flags. */
1048 secStatus = SSL_OptionSet (sslSocket, SSL_SECURITY, PR_TRUE);
1049 if (secStatus != SECSuccess)
1050 {
16560657 1051 server_error (_("Error setting SSL security for socket"));
6cb00a60 1052 goto error;
aeb9cc10
DB
1053 }
1054
1055 secStatus = SSL_OptionSet(sslSocket, SSL_HANDSHAKE_AS_SERVER, PR_TRUE);
1056 if (secStatus != SECSuccess)
1057 {
16560657 1058 server_error (_("Error setting handshake as server for socket"));
6cb00a60 1059 goto error;
aeb9cc10
DB
1060 }
1061
1062 secStatus = SSL_OptionSet(sslSocket, SSL_REQUEST_CERTIFICATE, PR_FALSE);
1063 if (secStatus != SECSuccess)
1064 {
16560657 1065 server_error (_("Error setting SSL client authentication mode for socket"));
6cb00a60 1066 goto error;
aeb9cc10
DB
1067 }
1068
1069 secStatus = SSL_OptionSet(sslSocket, SSL_REQUIRE_CERTIFICATE, PR_FALSE);
1070 if (secStatus != SECSuccess)
1071 {
16560657 1072 server_error (_("Error setting SSL client authentication mode for socket"));
6cb00a60 1073 goto error;
aeb9cc10
DB
1074 }
1075
1076 /* Set the appropriate callback routines. */
1077#if 0 /* use the default */
1078 secStatus = SSL_AuthCertificateHook (sslSocket, myAuthCertificate, CERT_GetDefaultCertDB());
1079 if (secStatus != SECSuccess)
1080 {
16560657 1081 server_error (_("Error in SSL_AuthCertificateHook"));
6cb00a60 1082 goto error;
aeb9cc10
DB
1083 }
1084#endif
1085#if 0 /* Use the default */
1086 secStatus = SSL_BadCertHook(sslSocket, (SSLBadCertHandler)myBadCertHandler, &certErr);
1087 if (secStatus != SECSuccess)
1088 {
16560657 1089 server_error (_("Error in SSL_BadCertHook"));
6cb00a60 1090 goto error;
aeb9cc10
DB
1091 }
1092#endif
1093#if 0 /* no handshake callback */
1094 secStatus = SSL_HandshakeCallback(sslSocket, myHandshakeCallback, NULL);
1095 if (secStatus != SECSuccess)
1096 {
16560657 1097 server_error (_("Error in SSL_HandshakeCallback"));
6cb00a60 1098 goto error;
aeb9cc10
DB
1099 }
1100#endif
1101
1102 certKEA = NSS_FindCertKEAType (cert);
1103
1104 secStatus = SSL_ConfigSecureServer (sslSocket, cert, privKey, certKEA);
1105 if (secStatus != SECSuccess)
1106 {
16560657 1107 server_error (_("Error configuring SSL server"));
6cb00a60 1108 goto error;
aeb9cc10
DB
1109 }
1110
1111 return sslSocket;
6cb00a60
FCE
1112
1113 error:
1114 nssError();
1115
1116 if (sslSocket) {
1117 if (PR_Close (sslSocket) != PR_SUCCESS) {
1118 server_error (_("Error closing ssl socket"));
1119 nssError ();
1120 }
1121 } else {
1122 if (PR_Close (tcpSocket) != PR_SUCCESS) {
1123 server_error (_("Error closing tcp/ssl socket"));
1124 nssError ();
1125 }
1126 }
1127
1128 return NULL;
aeb9cc10
DB
1129}
1130
1131#if 0 /* No client authentication (for now) and not authenticating after each transaction. */
1132/* Function: authenticateSocket()
1133 *
1134 * Purpose: Perform client authentication on the socket.
1135 *
1136 */
1137static SECStatus
1138authenticateSocket (PRFileDesc *sslSocket, PRBool requireCert)
1139{
1140 CERTCertificate *cert;
1141 SECStatus secStatus;
1142
1143 /* Returns NULL if client authentication is not enabled or if the
1144 * client had no certificate. */
1145 cert = SSL_PeerCertificate(sslSocket);
1146 if (cert)
1147 {
1148 /* Client had a certificate, so authentication is through. */
1149 CERT_DestroyCertificate(cert);
1150 return SECSuccess;
1151 }
1152
1153 /* Request client to authenticate itself. */
1154 secStatus = SSL_OptionSet(sslSocket, SSL_REQUEST_CERTIFICATE, PR_TRUE);
1155 if (secStatus != SECSuccess)
1156 {
16560657 1157 server_error (_("Error in SSL_OptionSet:SSL_REQUEST_CERTIFICATE"));
aeb9cc10
DB
1158 nssError ();
1159 return SECFailure;
1160 }
1161
1162 /* If desired, require client to authenticate itself. Note
1163 * SSL_REQUEST_CERTIFICATE must also be on, as above. */
1164 secStatus = SSL_OptionSet(sslSocket, SSL_REQUIRE_CERTIFICATE, requireCert);
1165 if (secStatus != SECSuccess)
1166 {
16560657 1167 server_error (_("Error in SSL_OptionSet:SSL_REQUIRE_CERTIFICATE"));
aeb9cc10
DB
1168 nssError ();
1169 return SECFailure;
1170 }
1171
1172 /* Having changed socket configuration parameters, redo handshake. */
1173 secStatus = SSL_ReHandshake(sslSocket, PR_TRUE);
1174 if (secStatus != SECSuccess)
1175 {
16560657 1176 server_error (_("Error in SSL_ReHandshake"));
aeb9cc10
DB
1177 nssError ();
1178 return SECFailure;
1179 }
1180
1181 /* Force the handshake to complete before moving on. */
1182 secStatus = SSL_ForceHandshake(sslSocket);
1183 if (secStatus != SECSuccess)
1184 {
16560657 1185 server_error (_("Error in SSL_ForceHandshake"));
aeb9cc10
DB
1186 nssError ();
1187 return SECFailure;
1188 }
1189
1190 return SECSuccess;
1191}
1192#endif /* No client authentication and not authenticating after each transaction. */
1193
1194/* Function: writeDataToSocket
1195 *
1196 * Purpose: Write the server's response back to the socket.
1197 *
1198 */
1199static SECStatus
1200writeDataToSocket(PRFileDesc *sslSocket, const char *responseFileName)
1201{
1202 PRFileDesc *local_file_fd = PR_Open (responseFileName, PR_RDONLY, 0);
1203 if (local_file_fd == NULL)
1204 {
16560657 1205 server_error (_F("Could not open input file %s", responseFileName));
aeb9cc10
DB
1206 nssError ();
1207 return SECFailure;
1208 }
1209
1210 /* Transmit the local file across the socket.
1211 */
1212 int numBytes = PR_TransmitFile (sslSocket, local_file_fd,
1213 NULL, 0,
1214 PR_TRANSMITFILE_KEEP_OPEN,
1215 PR_INTERVAL_NO_TIMEOUT);
1216
1217 /* Error in transmission. */
1218 SECStatus secStatus = SECSuccess;
1219 if (numBytes < 0)
1220 {
16560657 1221 server_error (_("Error writing response to socket"));
aeb9cc10
DB
1222 nssError ();
1223 secStatus = SECFailure;
1224 }
1225
1226 PR_Close (local_file_fd);
1227 return secStatus;
1228}
1229
cc7c72cd 1230static void
26a39006 1231get_stap_locale (const string &staplang, vector<string> &envVec, string stapstderr, cs_protocol_version *client_version)
cc7c72cd 1232{
c89fe89c 1233 // If the client version is < 1.6, then no file containing environment
cc7c72cd 1234 // variables defining the locale has been passed.
26a39006 1235 if (*client_version < "1.6")
cc7c72cd
DB
1236 return;
1237
1238 /* Go through each line of the file, verify it, then add it to the vector */
1239 ifstream langfile;
1240 langfile.open(staplang.c_str());
1241 if (!langfile.is_open())
1242 {
1243 // Not fatal. Proceed with the environment we have.
16560657 1244 server_error(_F("Unable to open file %s for reading: %s", staplang.c_str(),
cc7c72cd
DB
1245 strerror (errno)));
1246 return;
1247 }
1248
1249 /* Unpackage internationalization variables and verify their contents */
1250 map<string, string> envMap; /* To temporarily store the entire array of strings */
1251 string line;
1252 const set<string> &locVars = localization_variables();
1253
1254 /* Copy the global environ variable into the map */
9f4cd64d
CM
1255 if(environ != NULL)
1256 {
1257 for (unsigned i=0; environ[i]; i++)
1258 {
61019d8d 1259 string line = (string)environ[i];
9f4cd64d 1260
61019d8d
CM
1261 /* Find the first '=' sign */
1262 size_t pos = line.find("=");
1263
1264 /* Make sure it found an '=' sign */
1265 if(pos != string::npos)
1266 /* Everything before the '=' sign is the key, and everything after is the value. */
1267 envMap[line.substr(0, pos)] = line.substr(pos+1);
9f4cd64d
CM
1268 }
1269 }
cc7c72cd
DB
1270
1271 /* Create regular expression objects to verify lines read from file. Should not allow
1272 spaces, ctrl characters, etc */
1273 regex_t checkre;
4852f9d2 1274 if ((regcomp(&checkre, "^[a-zA-Z0-9@_.=-]*$", REG_EXTENDED | REG_NOSUB) != 0))
cc7c72cd
DB
1275 {
1276 // Not fatal. Proceed with the environment we have.
16560657 1277 server_error(_F("Error in regcomp: %s", strerror (errno)));
cc7c72cd
DB
1278 return;
1279 }
1280
1281 while (1)
1282 {
1283 getline(langfile, line);
1284 if (!langfile.good())
1285 break;
1286
1287 /* Extract key and value from the line. Note: value may contain "=". */
1288 string key;
1289 string value;
1290 size_t pos;
1291 pos = line.find("=");
1d7ae21b
FCE
1292 if (pos == string::npos)
1293 {
49398925 1294 client_error(_F("Localization key=value line '%s' cannot be parsed", line.c_str()), stapstderr);
1d7ae21b
FCE
1295 continue;
1296 }
cc7c72cd
DB
1297 key = line.substr(0, pos);
1298 pos++;
1299 value = line.substr(pos);
1300
1301 /* Make sure the key is found in the localization variables global set */
1302 if (locVars.find(key) == locVars.end())
1303 {
1304 // Not fatal. Just ignore it.
49398925 1305 client_error(_F("Localization key '%s' not found in global list", key.c_str()), stapstderr);
16560657 1306 continue;
cc7c72cd
DB
1307 }
1308
1309 /* Make sure the value does not contain illegal characters */
1310 if ((regexec(&checkre, value.c_str(), (size_t) 0, NULL, 0) != 0))
1311 {
1312 // Not fatal. Just ignore it.
49398925 1313 client_error(_F("Localization value '%s' contains illegal characters", value.c_str()), stapstderr);
16560657 1314 continue;
cc7c72cd
DB
1315 }
1316
1317 /* All is good, copy line into envMap, replacing if already there */
1318 envMap[key] = value;
1319 }
1320
1321 if (!langfile.eof())
1322 {
1323 // Not fatal. Proceed with what we have.
16560657 1324 server_error(_F("Error reading file %s: %s", staplang.c_str(), strerror (errno)));
cc7c72cd
DB
1325 }
1326
1327 regfree(&checkre);
1328
1329 /* Copy map into vector */
1330 for (map<string, string>::iterator it = envMap.begin(); it != envMap.end(); it++)
1331 envVec.push_back(it->first + "=" + it->second);
1332}
aeb9cc10 1333
b3367f63
DS
1334static void
1335get_client_mok_fingerprints (const string &filename,
1336 vector<string> &mok_fingerprints,
1337 string stapstderr,
1338 cs_protocol_version *client_version)
1339{
1340 // If the client version is < 1.6, then no file containing MOK
1341 // fingerprints could have been passed.
1342 if (*client_version < "1.6") {
1343 return;
1344 }
1345
1346 // Go through each line of the file and add it to the vector.
1347 ifstream file;
1348 file.open(filename.c_str());
1349 if (! file.is_open())
1350 // If the file isn't present, that's fine. It just means that the
1351 // module doesn't need to be signed.
1352 return;
1353
1354 // Create a regular expression object to verify lines read from the
1355 // file.
1356 regex_t checkre;
1357 if (regcomp(&checkre, "^([0-9a-f]{2}(:[0-9a-f]{2})+)$", REG_EXTENDED)
1358 != 0)
1359 {
1360 // Not fatal, just ignore the MOK fingerprints.
1361 server_error(_F("Error in MOK fingerprint regcomp: %s",
1362 strerror (errno)));
1363 return;
1364 }
1365
1366 // Unpack the MOK fingerprints. Notice we make sure the fingerprint
1367 // is in the right format, but that's all we can do at this
1368 // point. Later we'll check this client list against our server
1369 // list.
1370 string line;
1371 regmatch_t matches[3];
1372 while (getline (file, line))
1373 {
cd1418c7 1374 string fingerprint;
b3367f63 1375
3b02cd57
FCE
1376 if (line == "missing") // PR26665: needs signature, but has no useful mok key
1377 {
1378 // save it, so handleRequest() sends back a new MOK pubkey
1379 mok_fingerprints.push_back(line);
1380 continue;
1381 }
1382
b3367f63
DS
1383 if ((regexec(&checkre, line.c_str(), 3, matches, 0) != 0))
1384 {
1385 // Not fatal. Just ignore it.
3b02cd57 1386 client_error(_F("MOK fingerprint value '%s' isn't in the correct format",
b3367f63
DS
1387 line.c_str()), stapstderr);
1388 continue;
1389 }
1390
1391 // Save the fingerprint:
1392 // matches[0] is the range of the entire match
1393 // matches[1] is the entire fingerprint
1394 // matches[2] is a portion of the fingerprint
1395 if (matches[1].rm_so >= 0)
1396 fingerprint = line.substr(matches[1].rm_so,
1397 matches[1].rm_eo - matches[1].rm_so);
1398 if (! fingerprint.empty())
1399 mok_fingerprints.push_back(fingerprint);
1400 }
1401 regfree(&checkre);
1402}
1403
b3367f63 1404
820f2d22
DB
1405// Filter paths prefixed with the server's home directory from the given file.
1406//
1407static void
1408filter_response_file (const string &file_name, const string &responseDirName)
1409{
1192d243
DS
1410 // Filter the server's home directory name (unless it is "/")
1411 string dir = get_home_directory();
1412 if (dir != "/")
1413 {
1414 string swap = string ("s,") + get_home_directory () + ",<server>,g";
1415 vector<string> cmd { "sed", "-i", swap, file_name };
1416 (void) stap_system (0, cmd);
1417 }
1418
1419 // Filter the server's response directory name (unless it is "/")
1420 if (responseDirName != "/")
1421 {
1422 string swap = string ("s,") + responseDirName + ",<server>,g";
1423 vector<string> cmd = { "sed", "-i", swap, file_name };
1424 (void) stap_system (0, cmd);
1425 }
820f2d22
DB
1426}
1427
73f52eb4
DB
1428static privilege_t
1429getRequestedPrivilege (const vector<string> &stapargv)
1430{
1431 // The purpose of this function is to find the --privilege or --unprivileged option specified
1432 // by the user on the client side. We need to parse the command line completely, but we can
1433 // exit when we find the first --privilege or --unprivileged option, since stap does not allow
1434 // multiple privilege levels to specified on the same command line.
1435 //
1436 // Note that we need not do any options consistency checking since our spawned stap instance
1437 // will do that.
1438 //
1439 // Create an argv/argc for use by getopt_long.
1440 int argc = stapargv.size();
1441 char ** argv = new char *[argc + 1];
1442 for (unsigned i = 0; i < stapargv.size(); ++i)
1443 argv[i] = (char *)stapargv[i].c_str();
1444 argv[argc] = NULL;
1445
1446 privilege_t privilege = pr_highest; // Until specified otherwise.
1447 optind = 1;
1448 while (true)
1449 {
1450 // We need only allow getopt to parse the options until we find a
1451 // --privilege or --unprivileged option.
1452 int grc = getopt_long (argc, argv, STAP_SHORT_OPTIONS, stap_long_options, NULL);
1453 if (grc < 0)
1454 break;
1455 switch (grc)
1456 {
1457 default:
372c6458 1458 // We can ignore all options other than --privilege and --unprivileged.
73f52eb4 1459 break;
372c6458
DB
1460 case LONG_OPT_PRIVILEGE:
1461 if (strcmp (optarg, "stapdev") == 0)
1462 privilege = pr_stapdev;
1463 else if (strcmp (optarg, "stapsys") == 0)
1464 privilege = pr_stapsys;
1465 else if (strcmp (optarg, "stapusr") == 0)
1466 privilege = pr_stapusr;
1467 else
1468 {
1469 server_error (_F("Invalid argument '%s' for --privilege", optarg));
1470 privilege = pr_highest;
73f52eb4 1471 }
372c6458
DB
1472 // We have discovered the client side --privilege option. We can exit now since
1473 // stap only tolerates one privilege setting option.
1474 goto done; // break 2 switches and a loop
1475 case LONG_OPT_UNPRIVILEGED:
1476 privilege = pr_unprivileged;
1477 // We have discovered the client side --unprivileged option. We can exit now since
1478 // stap only tolerates one privilege setting option.
1479 goto done; // break 2 switches and a loop
73f52eb4
DB
1480 }
1481 }
1482 done:
1483 delete[] argv;
1484 return privilege;
1485}
1486
cd2af5d8 1487
aeb9cc10
DB
1488/* Run the translator on the data in the request directory, and produce output
1489 in the given output directory. */
1490static void
49398925 1491handleRequest (const string &requestDirName, const string &responseDirName, string stapstderr)
aeb9cc10 1492{
aeb9cc10 1493 vector<string> stapargv;
26a39006 1494 cs_protocol_version client_version = "1.0"; // Assumed until discovered otherwise
aeb9cc10
DB
1495 int rc;
1496 wordexp_t words;
1497 unsigned u;
1498 unsigned i;
1499 FILE* f;
a8e317b6 1500 int retlen;
aeb9cc10 1501
cc7c72cd
DB
1502 // Save the server version. Do this early, so the client knows what version of the server
1503 // it is dealing with, even if the request is not fully completed.
16560657 1504 string stapversion = responseDirName + "/version";
cc7c72cd
DB
1505 f = fopen (stapversion.c_str (), "w");
1506 if (f)
1507 {
c89fe89c 1508 fputs (CURRENT_CS_PROTOCOL_VERSION, f);
cc7c72cd
DB
1509 fclose(f);
1510 }
1511 else
16560657 1512 server_error (_F("Unable to open client version file %s", stapversion.c_str ()));
cc7c72cd
DB
1513
1514 // Get the client version. The default version is already set. Use it if we fail here.
16560657 1515 string filename = requestDirName + "/version";
cc7c72cd
DB
1516 if (file_exists (filename))
1517 read_from_file (filename, client_version);
c89fe89c 1518 log (_F("Client version is %s", client_version.v));
cc7c72cd
DB
1519
1520 // The name of the translator executable.
aeb9cc10
DB
1521 stapargv.push_back ((char *)(getenv ("SYSTEMTAP_STAP") ?: STAP_PREFIX "/bin/stap"));
1522
3532c11d
FL
1523 // Fetch the target kernel version from request and pass flag to stap
1524 string kernel_version;
1525 filename = requestDirName + "/sysinfo";
1526 ifstream versionfile(filename.c_str());
1527 if (versionfile >> kernel_version >> kernel_version) // Skip sysinfo: label
1528 {
1529 if (kernel_build_tree.find(kernel_version) != kernel_build_tree.end())
1530 {
1531 stapargv.push_back("-r" + kernel_version);
1532 }
1533 else
1534 {
1535 log(_F("Target kernel version %s is not supported by this server",
1536 kernel_version.c_str()));
1537 return;
1538 }
1539 }
1540 else
1541 {
1542 server_error(_("Unable to read sysinfo"));
1543 return;
1544 }
1545
aeb9cc10
DB
1546 /* Transcribe stap_options. We use plain wordexp(3), since these
1547 options are coming from the local trusted user, so malicious
1548 content is not a concern. */
1549 // TODO: Use tokenize here.
1550 rc = wordexp (stap_options.c_str (), & words, WRDE_NOCMD|WRDE_UNDEF);
305fac7d
CM
1551 if (rc)
1552 {
16560657 1553 server_error (_("Cannot parse stap options"));
aeb9cc10
DB
1554 return;
1555 }
1556
1557 for (u=0; u<words.we_wordc; u++)
1558 stapargv.push_back (words.we_wordv[u]);
1559
aeb9cc10
DB
1560 /* Process the saved command line arguments. Avoid quoting/unquoting errors by
1561 transcribing literally. */
16560657 1562 string new_staptmpdir = responseDirName + "/stap000000";
305fac7d
CM
1563 rc = mkdir(new_staptmpdir.c_str(), 0700);
1564 if (rc)
16560657 1565 server_error(_F("Could not create temporary directory %s", new_staptmpdir.c_str()));
305fac7d
CM
1566
1567 stapargv.push_back("--tmpdir=" + new_staptmpdir);
1568
1569 stapargv.push_back ("--client-options");
40ae3a75
AJ
1570
1571 string stap_opts = "";
aeb9cc10
DB
1572 for (i=1 ; ; i++)
1573 {
1574 char stapargfile[PATH_MAX];
1575 FILE* argfile;
1576 struct stat st;
1577 char *arg;
1578
a8e317b6
SC
1579 retlen = snprintf (stapargfile, PATH_MAX, "%s/argv%d", requestDirName.c_str (), i);
1580 if (retlen < 0 || retlen >= PATH_MAX)
1581 {
1582 server_error (_F("Error creating %s name", "path"));
1583 return;
1584 }
aeb9cc10
DB
1585
1586 rc = stat(stapargfile, & st);
1587 if (rc) break;
1588
1589 arg = (char *)malloc (st.st_size+1);
1590 if (!arg)
1591 {
16560657 1592 server_error (_("Out of memory"));
aeb9cc10
DB
1593 return;
1594 }
1595
1596 argfile = fopen(stapargfile, "r");
1597 if (! argfile)
1598 {
129b743c 1599 free(arg);
16560657 1600 server_error (_F("Error opening %s: %s", stapargfile, strerror (errno)));
aeb9cc10
DB
1601 return;
1602 }
1603
1604 rc = fread(arg, 1, st.st_size, argfile);
1605 if (rc != st.st_size)
1606 {
129b743c
PM
1607 free(arg);
1608 fclose(argfile);
16560657 1609 server_error (_F("Error reading %s: %s", stapargfile, strerror (errno)));
aeb9cc10
DB
1610 return;
1611 }
1612
1613 arg[st.st_size] = '\0';
1614 stapargv.push_back (arg);
40ae3a75
AJ
1615 stap_opts.append(arg);
1616 stap_opts.append(" ");
aeb9cc10
DB
1617 free (arg);
1618 fclose (argfile);
1619 }
40ae3a75 1620 log(_F("Options passed from the client: %s", stap_opts.c_str()));
aeb9cc10 1621
16560657 1622 string stapstdout = responseDirName + "/stdout";
aeb9cc10 1623
304d85ce
DB
1624 // NB: Before, when we did not fully parse the client's command line using getopt_long,
1625 // we used to insert a --privilege=XXX option here in case some other argument was mistaken
1626 // for a --privilege or --unprivileged option by our spawned stap. Since we now parse
1627 // the client's command line using getopt_long and share the getopt_long options
1628 // string and table with stap, this is no longer necessary. stap will parse the
1629 // command line identically to the way we have parsed it and will discover the same
1630 // privilege-setting option.
aeb9cc10 1631
cc7c72cd 1632 // Environment variables (possibly empty) to be passed to spawn_and_wait().
16560657 1633 string staplang = requestDirName + "/locale";
cc7c72cd 1634 vector<string> envVec;
26a39006 1635 get_stap_locale (staplang, envVec, stapstderr, &client_version);
e4e3d6b7 1636
b3367f63
DS
1637 // Machine owner keys (MOK) fingerprints (possibly nonexistent), to
1638 // be used as a list of valid keys that the module must be signed with.
1639 vector<string> client_mok_fingerprints;
b3367f63
DS
1640 get_client_mok_fingerprints(requestDirName + "/mok_fingerprints",
1641 client_mok_fingerprints, stapstderr,
1642 &client_version);
b3367f63 1643
e657df8a 1644
c419fab7
DS
1645 // If the client sent us MOK fingerprints, see if we have a matching
1646 // MOK on the server.
95fe6e8d 1647 string mok_fingerprint;
42be330a 1648 string mok_path = server_cert_db_path() + "/moks";
c419fab7 1649 if (! client_mok_fingerprints.empty())
e657df8a 1650 {
c419fab7 1651 // See if any of the client MOK fingerprints exist on the server.
b3367f63
DS
1652 vector<string>::const_iterator it;
1653 for (it = client_mok_fingerprints.begin();
1654 it != client_mok_fingerprints.end(); it++)
1655 {
42be330a 1656 if (mok_dir_valid_p (*it, mok_path, false, server_error))
b3367f63 1657 {
c419fab7 1658 mok_fingerprint = *it;
b3367f63
DS
1659 break;
1660 }
1661 }
1662
1663 // If the client requires signing, but we couldn't find a
1664 // matching machine owner key installed on the server, we can't
e657df8a
DS
1665 // build a signed module. But, the client may not have asked us
1666 // to create a module (for instance, the user could have done
1667 // 'stap -L syscall.open'). So, keep going until we know we need
1668 // to sign a module.
1669 }
b3367f63 1670
42be330a
SC
1671 if (! mok_fingerprint.empty ())
1672 stapargv.push_back("--sign-module=" + mok_path + "/" + mok_fingerprint);
1673
5ed19be2 1674 /* All ready, let's run the translator! */
e7d52b3b
JL
1675 int staprc;
1676 rc = spawn_and_wait(stapargv, &staprc, "/dev/null", stapstdout.c_str (),
1677 stapstderr.c_str (), requestDirName.c_str (), envVec);
1678 if (rc != PR_SUCCESS)
aeb9cc10 1679 {
e7d52b3b
JL
1680 server_error(_("Failed spawning translator"));
1681 return;
aeb9cc10
DB
1682 }
1683
b3367f63
DS
1684 // In unprivileged modes, if we have a module built, we need to sign
1685 // the sucker. We also might need to sign the module for secure
1686 // boot purposes.
304d85ce 1687 privilege_t privilege = getRequestedPrivilege (stapargv);
e657df8a
DS
1688 if (staprc == 0 && (pr_contains (privilege, pr_stapusr)
1689 || pr_contains (privilege, pr_stapsys)
1690 || ! client_mok_fingerprints.empty ()))
aeb9cc10 1691 {
305fac7d
CM
1692 glob_t globber;
1693 char pattern[PATH_MAX];
a8e317b6
SC
1694 int retlen;
1695
1696 retlen = snprintf (pattern, PATH_MAX, "%s/*.ko", new_staptmpdir.c_str());
1697 if (retlen < 0 || retlen >= PATH_MAX)
1698 {
1699 server_error (_F("Error creating %s name", "pattern"));
1700 return;
1701 }
1702
305fac7d
CM
1703 rc = glob (pattern, GLOB_ERR, NULL, &globber);
1704 if (rc)
16560657 1705 server_error (_F("Unable to find a module in %s", new_staptmpdir.c_str()));
305fac7d 1706 else if (globber.gl_pathc != 1)
03dd566c
DS
1707 {
1708 server_error (_F("Too many modules (%zu) in %s", globber.gl_pathc, new_staptmpdir.c_str()));
1709 globfree(&globber);
1710 }
305fac7d
CM
1711 else
1712 {
b3367f63
DS
1713 if (pr_contains (privilege, pr_stapusr)
1714 || pr_contains (privilege, pr_stapsys))
1715 sign_file (cert_db_path, server_cert_nickname(),
1716 globber.gl_pathv[0],
1717 string(globber.gl_pathv[0]) + ".sgn");
42be330a 1718 if (mok_fingerprint.empty() && ! client_mok_fingerprints.empty ())
e657df8a 1719 {
a32d236c
DS
1720 // If we're here, the client sent us MOK fingerprints
1721 // (since client_mok_fingerprints isn't empty), but we
1722 // don't have a matching MOK on the server (since
95fe6e8d
DS
1723 // mok_fingerprint is empty). So, we can't sign the
1724 // module.
a32d236c
DS
1725 client_error (_("No matching machine owner key (MOK) available on the server to sign the\n module."), stapstderr);
1726
3b02cd57
FCE
1727 // Well akksshually ... we could sign the new module with
1728 // this key preemptively ... and return the MOK public
1729 // key. But that wouldn't help the client machine,
1730 // because it won't be able to run the signed .ko until
1731 // a MOK enroll + UEFI reboot anyway.
1732
a32d236c
DS
1733 // Since we can't sign the module, send the client one
1734 // of our MOKs. If we don't have any, create one.
c419fab7
DS
1735 vector<string> mok_fingerprints;
1736 get_server_mok_fingerprints(mok_fingerprints, false, true);
1737 if (mok_fingerprints.empty ())
a32d236c 1738 {
cd2af5d8 1739 // Generate a new MOK.
2f228e21 1740 generate_mok(mok_fingerprint, server_error);
a32d236c
DS
1741 }
1742 else
1743 {
1744 // At this point we have at least one MOK on the
1745 // server. Send the public key down to the
c419fab7
DS
1746 // client.
1747 mok_fingerprint = *mok_fingerprints.begin ();
a32d236c
DS
1748 }
1749
95fe6e8d 1750 if (! mok_fingerprint.empty ())
a32d236c
DS
1751 {
1752 // Copy the public cert file to the response directory.
95fe6e8d
DS
1753 string mok_directory = mok_path + "/" + mok_fingerprint;
1754 string src = mok_directory + MOK_PUBLIC_CERT_FILE;
a32d236c
DS
1755 string dst = responseDirName + MOK_PUBLIC_CERT_FILE;
1756 if (copy_file (src, dst, true))
3b02cd57 1757 client_error ("The server has no machine owner key (MOK) in common with this\nsystem. Use the following command to import a server MOK into this\nsystem, then reboot:\n\n\t# sudo mokutil --import signing_key.x509", stapstderr);
a32d236c
DS
1758 else
1759 client_error ("The server has no machine owner key (MOK) in common with this\nsystem. The server failed to return a certificate.", stapstderr);
1760 }
1761 else
1762 {
1763 client_error ("The server has no machine owner keys (MOK) in common with this\nsystem. The server could not generate a new MOK.", stapstderr);
1764 }
e657df8a 1765
e657df8a
DS
1766 // If we couldn't sign the module, let's change the
1767 // staprc to 1, so that the client won't try to run the
1768 // resulting module, which wouldn't work.
1769 staprc = 1;
1770 }
03dd566c 1771 globfree(&globber);
305fac7d 1772 }
aeb9cc10
DB
1773 }
1774
e657df8a
DS
1775 // Save the RC (which might have gotten changed above).
1776 ofstream ofs((responseDirName + "/rc").c_str());
1777 ofs << staprc;
1778 ofs.close();
1779
7d26ee02
JS
1780 /* If uprobes.ko is required, it will have been built or cache-copied into
1781 * the temp directory. We need to pack it into the response where the client
71a522b5 1782 * can find it, and sign, if necessary, for unprivileged users.
7d26ee02
JS
1783 */
1784 string uprobes_ko = new_staptmpdir + "/uprobes/uprobes.ko";
1785 if (get_file_size(uprobes_ko) > 0)
1786 {
71a522b5 1787 /* uprobes.ko is required.
7d26ee02 1788 *
71a522b5
DB
1789 * It's already underneath the stap tmpdir, but older stap clients
1790 * don't know to look for it there, so, for these clients, we end up packing uprobes twice
1791 * into the zip. We could move instead of symlink.
7d26ee02 1792 */
71a522b5
DB
1793 string uprobes_response;
1794 if (client_version < "1.6")
1795 {
1796 uprobes_response = (string)responseDirName + "/uprobes.ko";
1797 rc = symlink(uprobes_ko.c_str(), uprobes_response.c_str());
1798 if (rc != 0)
1799 server_error (_F("Could not link to %s from %s",
1800 uprobes_ko.c_str(), uprobes_response.c_str()));
1801 }
1802 else
1803 uprobes_response = uprobes_ko;
7d26ee02
JS
1804
1805 /* In unprivileged mode, we need a signature on uprobes as well. */
f66bb29a 1806 if (! pr_contains (privilege, pr_stapdev))
305fac7d 1807 {
7d26ee02
JS
1808 sign_file (cert_db_path, server_cert_nickname(),
1809 uprobes_response, uprobes_response + ".sgn");
aeb9cc10 1810 }
e657df8a
DS
1811
1812 // Notice we're not giving an error message here if the client
1813 // requires signed modules. The error will have been generated
1814 // above on the systemtap module itself.
95fe6e8d 1815 if (! mok_fingerprint.empty ())
2f228e21 1816 {
42be330a
SC
1817 if (int rc = mok_sign_file (mok_fingerprint,
1818 mok_path,
1819 kernel_build_tree[kernel_version],
1820 uprobes_response))
2f228e21
SC
1821 client_error (_F("Running sign-file failed, rc = %d", rc), stapstderr);
1822 else
1823 cerr << _F("Module signed with MOK, fingerprint \"%s\"", //
1824 mok_fingerprint.c_str()) << endl;
1825 }
aeb9cc10
DB
1826 }
1827
1828 /* Free up all the arg string copies. Note that the first few were alloc'd
1829 by wordexp(), which wordfree() frees; others were hand-set to literal strings. */
1830 wordfree (& words);
1831
820f2d22
DB
1832 // Filter paths prefixed with the server's home directory from the stdout and stderr
1833 // files in the response.
1834 filter_response_file (stapstdout, responseDirName);
1835 filter_response_file (stapstderr, responseDirName);
1836
aeb9cc10
DB
1837 /* Sorry about the inconvenience. C string/file processing is such a pleasure. */
1838}
1839
1840
1841/* A front end for stap_spawn that handles stdin, stdout, stderr, switches to a working
1842 directory and returns overall success or failure. */
1843static PRStatus
e7d52b3b 1844spawn_and_wait (const vector<string> &argv, int *spawnrc,
5ed19be2 1845 const char* fd0, const char* fd1, const char* fd2,
878b2f3f 1846 const char *pwd, const vector<string>& envVec)
aeb9cc10
DB
1847{
1848 pid_t pid;
1849 int rc;
1850 posix_spawn_file_actions_t actions;
1851 int dotfd = -1;
1852
16560657 1853#define CHECKRC(msg) do { if (rc) { server_error (_(msg)); return PR_FAILURE; } } while (0)
aeb9cc10
DB
1854
1855 rc = posix_spawn_file_actions_init (& actions);
1856 CHECKRC ("Error in spawn file actions ctor");
1857 if (fd0) {
1858 rc = posix_spawn_file_actions_addopen(& actions, 0, fd0, O_RDONLY, 0600);
1859 CHECKRC ("Error in spawn file actions fd0");
1860 }
1861 if (fd1) {
1862 rc = posix_spawn_file_actions_addopen(& actions, 1, fd1, O_WRONLY|O_CREAT, 0600);
1863 CHECKRC ("Error in spawn file actions fd1");
1864 }
1865 if (fd2) {
16560657
DB
1866 // Use append mode for stderr because it gets written to in other places in the server.
1867 rc = posix_spawn_file_actions_addopen(& actions, 2, fd2, O_WRONLY|O_APPEND|O_CREAT, 0600);
aeb9cc10
DB
1868 CHECKRC ("Error in spawn file actions fd2");
1869 }
1870
1871 /* change temporarily to a directory if requested */
1872 if (pwd)
1873 {
1874 dotfd = open (".", O_RDONLY);
1875 if (dotfd < 0)
129b743c 1876 {
16560657 1877 server_error (_("Error in spawn getcwd"));
aeb9cc10
DB
1878 return PR_FAILURE;
1879 }
129b743c 1880
aeb9cc10 1881 rc = chdir (pwd);
129b743c
PM
1882 if (rc)
1883 {
1884 close(dotfd);
1885 server_error(_("Error in spawn chdir"));
1886 return PR_FAILURE;
1887 }
1888 }
1889
878b2f3f
CM
1890 pid = stap_spawn (0, argv, & actions, envVec);
1891 /* NB: don't react to pid==-1 right away; need to chdir back first. */
aeb9cc10
DB
1892
1893 if (pwd && dotfd >= 0)
1894 {
1895 int subrc;
1896 subrc = fchdir (dotfd);
1897 subrc |= close (dotfd);
1898 if (subrc)
16560657 1899 server_error (_("Error in spawn unchdir"));
aeb9cc10
DB
1900 }
1901
1902 if (pid == -1)
1903 {
16560657 1904 server_error (_F("Error in spawn: %s", strerror (errno)));
aeb9cc10
DB
1905 return PR_FAILURE;
1906 }
1907
e7d52b3b 1908 *spawnrc = stap_waitpid (0, pid);
13efed2a 1909 if (*spawnrc == -1) // something wrong with waitpid() call itself
aeb9cc10 1910 {
16560657 1911 server_error (_("Error in waitpid"));
aeb9cc10
DB
1912 return PR_FAILURE;
1913 }
1914
1915 rc = posix_spawn_file_actions_destroy (&actions);
1916 CHECKRC ("Error in spawn file actions dtor");
1917
1918 return PR_SUCCESS;
1919#undef CHECKRC
1920}
1921
c4798c0f 1922/* Given the path to the compressed request file, return 0 if the size of the
8c074dc4 1923 * uncompressed request is within the determined limit. */
7ac8a2e2 1924int
c4798c0f 1925check_uncompressed_request_size (const char * zip_file)
7ac8a2e2 1926{
a3ff9800 1927 ostringstream result;
a3ff9800 1928
8c074dc4 1929 // Generate the command to heck the uncompressed size
4c847338 1930 vector<string> args { "unzip", "-Zt", zip_file };
a3ff9800
AJ
1931 int rc = stap_system_read (0, args, result);
1932 if (rc != 0)
1933 {
42be330a 1934 server_error (_F("Unable to check the zipfile size. Error code: %d .", rc));
a3ff9800
AJ
1935 return rc;
1936 }
a3ff9800
AJ
1937
1938 // Parse the result from the unzip call, looking for the third token
1939 vector<string> toks;
1940 tokenize(result.str(), toks, " ");
1941 if (toks.size() < 3)
1942 {
1943 // Something went wrong and the format is probably not what we expect.
1944 server_error("Unable to check the uncompressed zipfile size. Output came in an unexpected format.");
1945 return -1;
1946 }
1947
1948 long uncomp_size = atol(toks[2].c_str());
ed4873c2 1949 if (uncomp_size < 1 || (unsigned)uncomp_size > max_uncompressed_req_size)
a3ff9800
AJ
1950 {
1951 server_error(_F("Uncompressed request size of %ld bytes is not within the expected range of 1 to %zu bytes.",
ed4873c2 1952 uncomp_size,max_uncompressed_req_size));
a3ff9800
AJ
1953 return -1;
1954 }
1955
1956 return 0; // If it got to this point, everthing went well.
7ac8a2e2
AJ
1957}
1958
49398925 1959/* Function: void *handle_connection()
aeb9cc10
DB
1960 *
1961 * Purpose: Handle a connection to a socket. Copy in request zip
1962 * file, process it, copy out response. Temporary directories are
1963 * created & destroyed here.
1964 */
49398925
CM
1965
1966void *
1967handle_connection (void *arg)
aeb9cc10
DB
1968{
1969 PRFileDesc * sslSocket = NULL;
1970 SECStatus secStatus = SECFailure;
0ec2c5bf 1971 PRStatus prStatus;
aeb9cc10
DB
1972 int rc;
1973 char *rc1;
305fac7d 1974 char tmpdir[PATH_MAX];
aeb9cc10
DB
1975 char requestFileName[PATH_MAX];
1976 char requestDirName[PATH_MAX];
1977 char responseDirName[PATH_MAX];
1978 char responseFileName[PATH_MAX];
49398925
CM
1979 string stapstderr; /* Cannot be global since we need a unique
1980 copy for each connection.*/
aeb9cc10
DB
1981 vector<string> argv;
1982 PRInt32 bytesRead;
a8e317b6 1983 int retlen;
aeb9cc10 1984
49398925
CM
1985 /* Detatch to avoid a memory leak */
1986 if(max_threads > 0)
1987 pthread_detach(pthread_self());
1988
1989 /* Unpack the arg */
1990 thread_arg *t_arg = (thread_arg *) arg;
1991 PRFileDesc *tcpSocket = t_arg->tcpSocket;
1992 CERTCertificate *cert = t_arg->cert;
1993 SECKEYPrivateKey *privKey = t_arg->privKey;
1994 PRNetAddr addr = t_arg->addr;
1995
aeb9cc10
DB
1996 tmpdir[0]='\0'; /* prevent cleanup-time /bin/rm of uninitialized directory */
1997
ba49e036 1998#if 0 // already done on the listenSocket
aeb9cc10 1999 /* Make sure the socket is blocking. */
ba49e036 2000 PRSocketOptionData socketOption;
aeb9cc10
DB
2001 socketOption.option = PR_SockOpt_Nonblocking;
2002 socketOption.value.non_blocking = PR_FALSE;
2003 PR_SetSocketOption (tcpSocket, &socketOption);
ba49e036 2004#endif
aeb9cc10
DB
2005 secStatus = SECFailure;
2006 sslSocket = setupSSLSocket (tcpSocket, cert, privKey);
2007 if (sslSocket == NULL)
2008 {
2009 // Message already issued.
2010 goto cleanup;
2011 }
2012
2013 secStatus = SSL_ResetHandshake(sslSocket, /* asServer */ PR_TRUE);
2014 if (secStatus != SECSuccess)
2015 {
16560657 2016 server_error (_("Error resetting SSL handshake"));
aeb9cc10
DB
2017 nssError ();
2018 goto cleanup;
2019 }
2020
2021#if 0 // The client authenticates the server, so the client initiates the handshake
2022 /* Force the handshake to complete before moving on. */
2023 secStatus = SSL_ForceHandshake(sslSocket);
2024 if (secStatus != SECSuccess)
2025 {
16560657 2026 server_error (_("Error forcing SSL handshake"));
aeb9cc10
DB
2027 nssError ();
2028 goto cleanup;
2029 }
2030#endif
2031
2032 secStatus = SECFailure;
a8e317b6
SC
2033 retlen = snprintf(tmpdir, PATH_MAX, "%s/stap-server.XXXXXX", getenv("TMPDIR") ?: "/tmp");
2034 if (retlen < 0 || retlen >= PATH_MAX)
2035 {
2036 server_error (_F("Error creating %s name", "temporary directory"));
2037 tmpdir[0]=0; /* prevent /bin/rm */
2038 goto cleanup;
2039 }
aeb9cc10
DB
2040 rc1 = mkdtemp(tmpdir);
2041 if (! rc1)
2042 {
16560657 2043 server_error (_F("Could not create temporary directory %s: %s", tmpdir, strerror(errno)));
aeb9cc10
DB
2044 tmpdir[0]=0; /* prevent /bin/rm */
2045 goto cleanup;
2046 }
2047
2048 /* Create a temporary files names and directories. */
a8e317b6
SC
2049 retlen = snprintf (requestFileName, PATH_MAX, "%s/request.zip", tmpdir);
2050 if (retlen < 0 || retlen >= PATH_MAX)
2051 {
2052 server_error (_F("Error creating %s name", "request.zip path"));
2053 goto cleanup;
2054 }
2055
2056 retlen = snprintf (requestDirName, PATH_MAX, "%s/request", tmpdir);
2057 if (retlen < 0 || retlen >= PATH_MAX)
2058 {
2059 server_error (_F("Error creating %s name", "request directory path"));
2060 goto cleanup;
2061 }
aeb9cc10 2062
aeb9cc10
DB
2063 rc = mkdir(requestDirName, 0700);
2064 if (rc)
2065 {
16560657 2066 server_error (_F("Could not create temporary directory %s: %s", requestDirName, strerror (errno)));
aeb9cc10
DB
2067 goto cleanup;
2068 }
2069
a8e317b6
SC
2070 retlen = snprintf (responseDirName, PATH_MAX, "%s/response", tmpdir);
2071 if (retlen < 0 || retlen >= PATH_MAX)
2072 {
2073 server_error (_F("Error creating %s name", "response directory path"));
2074 goto cleanup;
2075 }
2076
aeb9cc10
DB
2077 rc = mkdir(responseDirName, 0700);
2078 if (rc)
2079 {
16560657 2080 server_error (_F("Could not create temporary directory %s: %s", responseDirName, strerror (errno)));
aeb9cc10
DB
2081 goto cleanup;
2082 }
16560657
DB
2083 // Set this early, since it gets used for errors to be returned to the client.
2084 stapstderr = string(responseDirName) + "/stderr";
aeb9cc10 2085
a8e317b6
SC
2086 retlen = snprintf (responseFileName, PATH_MAX, "%s/response.zip", tmpdir);
2087 if (retlen < 0 || retlen >= PATH_MAX)
2088 {
2089 server_error (_F("Error creating %s name", "response.zip path"));
2090 goto cleanup;
2091 }
aeb9cc10
DB
2092
2093 /* Read data from the socket.
2094 * If the user is requesting/requiring authentication, authenticate
2095 * the socket. */
2096 bytesRead = readDataFromSocket(sslSocket, requestFileName);
2097 if (bytesRead < 0) // Error
2098 goto cleanup;
2099 if (bytesRead == 0) // No request -- not an error
2100 {
2101 secStatus = SECSuccess;
2102 goto cleanup;
2103 }
2104
2105#if 0 /* Don't authenticate after each transaction */
2106 if (REQUEST_CERT_ALL)
2107 {
2108 secStatus = authenticateSocket(sslSocket);
2109 if (secStatus != SECSuccess)
2110 goto cleanup;
2111 }
2112#endif
2113
7ac8a2e2
AJ
2114 /* Just before we do any kind of processing, we want to check that the request there will
2115 * be enough memory to unzip the file. */
c4798c0f 2116 if (check_uncompressed_request_size(requestFileName))
7ac8a2e2
AJ
2117 {
2118 goto cleanup;
2119 }
2120
aeb9cc10
DB
2121 /* Unzip the request. */
2122 secStatus = SECFailure;
4c847338 2123 argv = { "unzip", "-q", "-d", requestDirName, requestFileName };
aeb9cc10 2124 rc = stap_system (0, argv);
820f2d22 2125 if (rc != 0)
aeb9cc10 2126 {
16560657 2127 server_error (_("Unable to extract client request"));
aeb9cc10
DB
2128 goto cleanup;
2129 }
2130
2131 /* Handle the request zip file. An error therein should still result
2132 in a response zip file (containing stderr etc.) so we don't have to
2133 have a result code here. */
49398925 2134 handleRequest(requestDirName, responseDirName, stapstderr);
aeb9cc10
DB
2135
2136 /* Zip the response. */
e7d52b3b 2137 int ziprc;
4c847338 2138 argv = { "zip", "-q", "-r", responseFileName, "." };
e7d52b3b
JL
2139 rc = spawn_and_wait (argv, &ziprc, NULL, NULL, NULL, responseDirName);
2140 if (rc != PR_SUCCESS || ziprc != 0)
aeb9cc10 2141 {
16560657 2142 server_error (_("Unable to compress server response"));
aeb9cc10
DB
2143 goto cleanup;
2144 }
49398925 2145
aeb9cc10
DB
2146 secStatus = writeDataToSocket (sslSocket, responseFileName);
2147
2148cleanup:
2149 if (sslSocket)
2150 if (PR_Close (sslSocket) != PR_SUCCESS)
2151 {
16560657 2152 server_error (_("Error closing ssl socket"));
aeb9cc10
DB
2153 nssError ();
2154 }
71a522b5 2155
aeb9cc10
DB
2156 if (tmpdir[0])
2157 {
71a522b5
DB
2158 // Remove the whole tmpdir and all that lies beneath, unless -k was specified.
2159 if (keep_temp)
2160 log (_F("Keeping temporary directory %s", tmpdir));
2161 else
2162 {
4c847338 2163 argv = { "rm", "-r", tmpdir };
71a522b5 2164 rc = stap_system (0, argv);
820f2d22 2165 if (rc != 0)
71a522b5
DB
2166 server_error (_("Error in tmpdir cleanup"));
2167 }
aeb9cc10
DB
2168 }
2169
0ec2c5bf
DB
2170 if (secStatus != SECSuccess)
2171 server_error (_("Error processing client request"));
2172
2173 // Log the end of the request.
2174 char buf[1024];
2175 prStatus = PR_NetAddrToString (& addr, buf, sizeof (buf));
2176 if (prStatus == PR_SUCCESS)
2177 {
2178 if (addr.raw.family == PR_AF_INET)
2179 log (_F("Request from %s:%d complete", buf, addr.inet.port));
2180 else if (addr.raw.family == PR_AF_INET6)
2181 log (_F("Request from [%s]:%d complete", buf, addr.ipv6.port));
2182 }
2183
30321a38
FCE
2184 if (privKey)
2185 SECKEY_DestroyPrivateKey (privKey); /* Destroy our copy of the private key. */
2186
0ec2c5bf
DB
2187 /* Increment semephore to indicate this thread is finished. */
2188 free(t_arg);
2189 if (max_threads > 0)
2190 {
2191 sem_post(&sem_client);
2192 pthread_exit(0);
2193 }
2194 else
2195 return 0;
aeb9cc10
DB
2196}
2197
2198/* Function: int accept_connection()
2199 *
2200 * Purpose: Accept a connection to the socket.
2201 *
2202 */
2203static SECStatus
2204accept_connections (PRFileDesc *listenSocket, CERTCertificate *cert)
2205{
2206 PRNetAddr addr;
2207 PRFileDesc *tcpSocket;
0ec2c5bf 2208 PRStatus prStatus;
aeb9cc10
DB
2209 SECStatus secStatus;
2210 CERTCertDBHandle *dbHandle;
49398925 2211 pthread_t tid;
26a39006 2212 thread_arg *t_arg;
49398925 2213
aeb9cc10
DB
2214
2215 dbHandle = CERT_GetDefaultCertDB ();
2216
2217 // cert_db_path gets passed to nssPasswordCallback.
2218 SECKEYPrivateKey *privKey = PK11_FindKeyByAnyCert (cert, (void*)cert_db_path.c_str ());
2219 if (privKey == NULL)
2220 {
16560657 2221 server_error (_("Unable to obtain certificate private key"));
aeb9cc10
DB
2222 nssError ();
2223 return SECFailure;
2224 }
2225
26a39006 2226 while (pending_interrupts == 0)
aeb9cc10
DB
2227 {
2228 /* Accept a connection to the socket. */
26a39006 2229 tcpSocket = PR_Accept (listenSocket, &addr, PR_INTERVAL_MIN);
aeb9cc10 2230 if (tcpSocket == NULL)
26a39006
CM
2231 {
2232 if(PR_GetError() == PR_IO_TIMEOUT_ERROR)
2233 continue;
2234 else
2235 {
2236 server_error (_("Error accepting client connection"));
2237 break;
2238 }
2239 }
aeb9cc10
DB
2240
2241 /* Log the accepted connection. */
0ec2c5bf
DB
2242 char buf[1024];
2243 prStatus = PR_NetAddrToString (&addr, buf, sizeof (buf));
2244 if (prStatus == PR_SUCCESS)
2245 {
2246 if (addr.raw.family == PR_AF_INET)
2247 log (_F("Accepted connection from %s:%d", buf, addr.inet.port));
2248 else if (addr.raw.family == PR_AF_INET6)
2249 log (_F("Accepted connection from [%s]:%d", buf, addr.ipv6.port));
2250 }
aeb9cc10
DB
2251
2252 /* XXX: alarm() or somesuch to set a timeout. */
aeb9cc10
DB
2253
2254 /* Accepted the connection, now handle it. */
aeb9cc10 2255
49398925
CM
2256 /* Wait for a thread to finish if there are none available */
2257 if(max_threads >0)
2258 {
26a39006
CM
2259 int idle_threads;
2260 sem_getvalue(&sem_client, &idle_threads);
2261 if(idle_threads <= 0)
49398925 2262 log(_("Server is overloaded. Processing times may be longer than normal."));
26a39006 2263 else if (idle_threads == max_threads)
49398925
CM
2264 log(_("Processing 1 request..."));
2265 else
26a39006 2266 log(_F("Processing %d concurrent requests...", ((int)max_threads - idle_threads) + 1));
49398925
CM
2267
2268 sem_wait(&sem_client);
2269 }
2270
2271 /* Create the argument structure to pass to pthread_create
2272 * (or directly to handle_connection if max_threads == 0 */
26a39006
CM
2273 t_arg = (thread_arg *)malloc(sizeof(*t_arg));
2274 if (t_arg == 0)
2275 fatal(_("No memory available for new thread arg!"));
2276 t_arg->tcpSocket = tcpSocket;
2277 t_arg->cert = cert;
30321a38 2278 t_arg->privKey = SECKEY_CopyPrivateKey(privKey); /* pass by value */
26a39006 2279 t_arg->addr = addr;
49398925
CM
2280
2281 /* Handle the conncection */
848f7473 2282 int thread_rc = -1;
49398925
CM
2283 if (max_threads > 0)
2284 /* Create the worker thread and handle the connection. */
848f7473
FCE
2285 thread_rc = pthread_create(&tid, NULL, handle_connection, t_arg);
2286
2287 if (thread_rc != 0)
26a39006
CM
2288 /* Since max_threads == 0, don't spawn a new thread,
2289 * just handle in the current thread. */
2290 handle_connection(t_arg);
aeb9cc10
DB
2291
2292 // If our certificate is no longer valid (e.g. has expired), then exit.
2293 secStatus = CERT_VerifyCertNow (dbHandle, cert, PR_TRUE/*checkSig*/,
2294 certUsageSSLServer, NULL/*wincx*/);
2295 if (secStatus != SECSuccess)
2296 {
7e001983
FCE
2297 // Not a serious error. Exit the loop so a new cert can be generated.
2298 nssError ();
aeb9cc10
DB
2299 break;
2300 }
2301 }
2302
30321a38 2303 SECKEY_DestroyPrivateKey (privKey); /* Safe to destroy, even if thread still running. */
aeb9cc10
DB
2304 return SECSuccess;
2305}
2306
2307/* Function: void server_main()
2308 *
2309 * Purpose: This is the server's main function. It configures a socket
2310 * and listens to it.
2311 *
2312 */
2313static SECStatus
ba49e036 2314server_main (PRFileDesc *listenSocket)
aeb9cc10 2315{
26a39006
CM
2316 int idle_threads;
2317 int timeout = 0;
fddf715d
SC
2318 NSSInitContext *context;
2319 SECStatus secStatus;
26a39006 2320
ba49e036 2321 // Initialize NSS.
fddf715d
SC
2322 context = nssInitContext (cert_db_path.c_str ());
2323 if (context == NULL)
ba49e036
DB
2324 {
2325 // Message already issued.
fddf715d 2326 return SECFailure;
ba49e036
DB
2327 }
2328
2329 // Preinitialized here due to jumps to the label 'done'.
2330 CERTCertificate *cert = NULL;
2331 bool serverCacheConfigured = false;
2332
241e35f0 2333 // Enable all cipher suites.
ba49e036
DB
2334 // NB: The NSS docs say that SSL_ClearSessionCache is required for the new settings to take
2335 // effect, however, calling it puts NSS in a state where it will not shut down cleanly.
2336 // We need to be able to shut down NSS cleanly if we are to generate a new certificate when
2337 // ours expires. It should be noted however, thet SSL_ClearSessionCache only clears the
2338 // client cache, and we are a server.
f1b05046
FCE
2339 /* Some NSS versions don't do this correctly in NSS_SetDomesticPolicy. */
2340 do {
2341 const PRUint16 *cipher;
f2ff34ed 2342 for (cipher = SSL_GetImplementedCiphers(); *cipher != 0; ++cipher)
f1b05046
FCE
2343 SSL_CipherPolicySet(*cipher, SSL_ALLOWED);
2344 } while (0);
ba49e036 2345 // SSL_ClearSessionCache ();
ba49e036
DB
2346
2347 // Configure the SSL session cache for a single process server with the default settings.
2348 secStatus = SSL_ConfigServerSessionIDCache (0, 0, 0, NULL);
2349 if (secStatus != SECSuccess)
2350 {
16560657 2351 server_error (_("Unable to configure SSL server session ID cache"));
ba49e036
DB
2352 nssError ();
2353 goto done;
2354 }
2355 serverCacheConfigured = true;
2356
2357 /* Get own certificate. */
2358 cert = PK11_FindCertFromNickname (server_cert_nickname (), NULL);
2359 if (cert == NULL)
2360 {
16560657 2361 server_error (_F("Unable to find our certificate in the database at %s",
ba49e036
DB
2362 cert_db_path.c_str ()));
2363 nssError ();
2364 goto done;
2365 }
2366
2367 // Tell the world that we're listening.
2368 advertise_presence (cert);
2369
2370 /* Handle connections to the socket. */
2371 secStatus = accept_connections (listenSocket, cert);
2372
2373 // Tell the world we're no longer listening.
2374 unadvertise_presence ();
2375
26a39006
CM
2376 sem_getvalue(&sem_client, &idle_threads);
2377
2378 /* Wait for requests to finish or the timeout to be reached.
2379 * If we got here from an interrupt, exit immediately if
2380 * the timeout is reached. Otherwise, wait indefinitiely
2381 * until the threads exit (or an interrupt is recieved).*/
2382 if(idle_threads < max_threads)
2383 log(_F("Waiting for %d outstanding requests to complete...", (int)max_threads - idle_threads));
2384 while(idle_threads < max_threads)
2385 {
2386 if(pending_interrupts && timeout++ > CONCURRENCY_TIMEOUT_S)
2387 {
2388 log(_("Timeout reached, exiting (forced)"));
2754e7bb 2389 kill_stap_spawn (SIGTERM);
26a39006
CM
2390 cleanup ();
2391 _exit(0);
2392 }
2393 sleep(1);
2394 sem_getvalue(&sem_client, &idle_threads);
2395 }
2396
ba49e036
DB
2397 done:
2398 // Clean up
2399 if (cert)
2400 CERT_DestroyCertificate (cert);
2401
2402 // Shutdown NSS
2403 if (serverCacheConfigured && SSL_ShutdownServerSessionIDCache () != SECSuccess)
2404 {
16560657 2405 server_error (_("Unable to shut down server session ID cache"));
ba49e036
DB
2406 nssError ();
2407 }
fddf715d 2408 nssCleanup (cert_db_path.c_str (), context);
ba49e036
DB
2409
2410 return secStatus;
2411}
2412
2413static void
2414listen ()
2415{
2416 // Create a new socket.
0ec2c5bf 2417 PRFileDesc *listenSocket = PR_OpenTCPSocket (PR_AF_INET6); // Accepts IPv4 too
aeb9cc10
DB
2418 if (listenSocket == NULL)
2419 {
16560657 2420 server_error (_("Error creating socket"));
aeb9cc10 2421 nssError ();
ba49e036 2422 return;
aeb9cc10
DB
2423 }
2424
ba49e036
DB
2425 // Set socket to be blocking - on some platforms the default is nonblocking.
2426 PRSocketOptionData socketOption;
aeb9cc10
DB
2427 socketOption.option = PR_SockOpt_Nonblocking;
2428 socketOption.value.non_blocking = PR_FALSE;
ba49e036
DB
2429 PRStatus prStatus = PR_SetSocketOption (listenSocket, & socketOption);
2430 if (prStatus != PR_SUCCESS)
2431 {
16560657 2432 server_error (_("Error setting socket properties"));
ba49e036
DB
2433 nssError ();
2434 goto done;
2435 }
aeb9cc10 2436
ba49e036
DB
2437 // Allow the socket address to be reused, in case we want the same port across a
2438 // 'service stap-server restart'
2439 socketOption.option = PR_SockOpt_Reuseaddr;
2440 socketOption.value.reuse_addr = PR_TRUE;
2441 prStatus = PR_SetSocketOption (listenSocket, & socketOption);
aeb9cc10
DB
2442 if (prStatus != PR_SUCCESS)
2443 {
16560657 2444 server_error (_("Error setting socket properties"));
aeb9cc10
DB
2445 nssError ();
2446 goto done;
2447 }
2448
ba49e036
DB
2449 // Configure the network connection.
2450 PRNetAddr addr;
0ec2c5bf
DB
2451 memset (& addr, 0, sizeof(addr));
2452 prStatus = PR_InitializeNetAddr (PR_IpAddrAny, port, & addr);
2453 addr.ipv6.family = PR_AF_INET6;
2454#if 0
2455 // addr.inet.ip = PR_htonl(PR_INADDR_ANY);
2456 PR_StringToNetAddr ("::", & addr);
2457 // PR_StringToNetAddr ("fe80::5eff:35ff:fe07:55ca", & addr);
2458 // PR_StringToNetAddr ("::1", & addr);
2459 addr.ipv6.port = PR_htons (port);
2460#endif
aeb9cc10 2461
0ec2c5bf
DB
2462 // Bind the socket to an address. Retry if the selected port is busy, unless the port was
2463 // specified directly.
aeb9cc10
DB
2464 for (;;)
2465 {
aeb9cc10
DB
2466 /* Bind the address to the listener socket. */
2467 prStatus = PR_Bind (listenSocket, & addr);
2468 if (prStatus == PR_SUCCESS)
2469 break;
2470
0ec2c5bf 2471 // If the selected port is busy. Try another, but only if a specific port was not specified.
aeb9cc10
DB
2472 PRErrorCode errorNumber = PR_GetError ();
2473 switch (errorNumber)
2474 {
2475 case PR_ADDRESS_NOT_AVAILABLE_ERROR:
0ec2c5bf
DB
2476 if (port == 0)
2477 {
2478 server_error (_F("Network port %hu is unavailable. Trying another port", port));
2479 continue;
2480 }
2481 break;
aeb9cc10 2482 case PR_ADDRESS_IN_USE_ERROR:
0ec2c5bf
DB
2483 if (port == 0)
2484 {
2485 server_error (_F("Network port %hu is busy. Trying another port", port));
2486 continue;
2487 }
2488 break;
aeb9cc10 2489 default:
0ec2c5bf 2490 break;
aeb9cc10 2491 }
0ec2c5bf
DB
2492 server_error (_("Error setting socket address"));
2493 nssError ();
2494 goto done;
aeb9cc10
DB
2495 }
2496
2497 // Query the socket for the port that was assigned.
2498 prStatus = PR_GetSockName (listenSocket, &addr);
2499 if (prStatus != PR_SUCCESS)
2500 {
16560657 2501 server_error (_("Unable to obtain socket address"));
aeb9cc10
DB
2502 nssError ();
2503 goto done;
2504 }
0ec2c5bf
DB
2505 char buf[1024];
2506 prStatus = PR_NetAddrToString (&addr, buf, sizeof (buf));
2507 port = PR_ntohs (addr.ipv6.port);
2508 log (_F("Using network address [%s]:%hu", buf, port));
49398925
CM
2509
2510 if (max_threads > 0)
2511 log (_F("Using a maximum of %ld threads", max_threads));
2512 else
2513 log (_("Concurrency disabled"));
aeb9cc10 2514
ba49e036
DB
2515 // Listen for connection on the socket. The second argument is the maximum size of the queue
2516 // for pending connections.
aeb9cc10
DB
2517 prStatus = PR_Listen (listenSocket, 5);
2518 if (prStatus != PR_SUCCESS)
2519 {
16560657 2520 server_error (_("Error listening on socket"));
aeb9cc10
DB
2521 nssError ();
2522 goto done;
2523 }
2524
26a39006
CM
2525 /* Initialize semephore with the maximum number of threads
2526 * defined by --max-threads. If it is not defined, the
2527 * default is the number of processors */
2528 sem_init(&sem_client, 0, max_threads);
2529
ba49e036
DB
2530 // Loop forever. We check our certificate (and regenerate, if necessary) and then start the
2531 // server. The server will go down when our certificate is no longer valid (e.g. expired). We
2532 // then generate a new one and start the server again.
26a39006 2533 while(!pending_interrupts)
aeb9cc10
DB
2534 {
2535 // Ensure that our certificate is valid. Generate a new one if not.
2536 if (check_cert (cert_db_path, server_cert_nickname (), use_db_password) != 0)
2537 {
2538 // Message already issued
30321a38
FCE
2539 server_error (_("Cannot check/create certificate, giving up."));
2540 goto done;
aeb9cc10
DB
2541 }
2542
2543 // Ensure that our certificate is trusted by our local client.
2544 // Construct the client database path relative to the server database path.
2545 SECStatus secStatus = add_client_cert (server_cert_file (),
7209427d 2546 local_client_cert_db_path (), db_nssinit);
aeb9cc10
DB
2547 if (secStatus != SECSuccess)
2548 {
c981acd9
DB
2549 // Not fatal. Other clients may trust the server and trust can be added
2550 // for the local client in other ways.
16560657 2551 server_error (_("Unable to authorize certificate for the local client"));
aeb9cc10
DB
2552 }
2553
ba49e036
DB
2554 // Launch the server.
2555 secStatus = server_main (listenSocket);
aeb9cc10 2556 } // loop forever
ba49e036
DB
2557
2558 done:
26a39006 2559 sem_destroy(&sem_client); /*Not really necessary, as we are shutting down...but for correctness */
ba49e036
DB
2560 if (PR_Close (listenSocket) != PR_SUCCESS)
2561 {
16560657 2562 server_error (_("Error closing listen socket"));
ba49e036
DB
2563 nssError ();
2564 }
aeb9cc10
DB
2565}
2566
2567int
2568main (int argc, char **argv) {
2569 initialize (argc, argv);
2570 listen ();
2571 cleanup ();
2572 return 0;
2573}
7d26ee02
JS
2574
2575/* vim: set sw=2 ts=8 cino=>4,n-2,{2,^-2,t0,(0,u0,w1,M1 : */
This page took 0.629625 seconds and 6 git commands to generate.