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