]> sourceware.org Git - systemtap.git/blame_incremental - nss-server-info.cxx
NEWS: note .spec change re. -dtrace subrpm
[systemtap.git] / nss-server-info.cxx
... / ...
CommitLineData
1/*
2 Compile server client functions
3 Copyright (C) 2010-2018 Red Hat Inc.
4
5 This file is part of systemtap, and is free software. You can
6 redistribute it and/or modify it under the terms of the GNU General
7 Public License (GPL); either version 2, or (at your option) any
8 later version.
9*/
10
11// Completely disable the client if NSS is not available.
12#include "config.h"
13#if HAVE_NSS
14#include "session.h"
15#include "cscommon.h"
16#include "csclient.h"
17#include "client-nss.h"
18#include "util.h"
19#include "stap-probe.h"
20
21#include <sys/times.h>
22#include <vector>
23#include <fstream>
24#include <sstream>
25#include <cassert>
26#include <cstdlib>
27#include <cstdio>
28#include <algorithm>
29
30extern "C" {
31#include <unistd.h>
32#include <linux/limits.h>
33#include <sys/time.h>
34#include <glob.h>
35#include <limits.h>
36#include <sys/socket.h>
37#include <sys/stat.h>
38#include <net/if.h>
39#include <netdb.h>
40#include <arpa/inet.h>
41#include <pwd.h>
42}
43
44#if HAVE_AVAHI
45extern "C" {
46#include <avahi-client/client.h>
47#include <avahi-client/lookup.h>
48
49#include <avahi-common/simple-watch.h>
50#include <avahi-common/malloc.h>
51#include <avahi-common/error.h>
52#include <avahi-common/timeval.h>
53}
54#endif // HAVE_AVAHI
55
56extern "C" {
57#include <ssl.h>
58#include <nspr.h>
59#include <nss.h>
60#include <certdb.h>
61#include <pk11pub.h>
62#include <prerror.h>
63#include <secerr.h>
64#include <sslerr.h>
65}
66
67#include "nsscommon.h"
68
69using namespace std;
70
71static PRIPv6Addr &copyAddress (PRIPv6Addr &PRin6, const in6_addr &in6);
72static PRNetAddr &copyNetAddr (PRNetAddr &x, const PRNetAddr &y);
73bool operator!= (const PRNetAddr &x, const PRNetAddr &y);
74bool operator== (const PRNetAddr &x, const PRNetAddr &y);
75
76// Information about compile servers.
77compile_server_info::compile_server_info ()
78 : port(0), fully_specified(false)
79{
80 memset (& address, 0, sizeof (address));
81}
82
83bool
84compile_server_info::empty () const
85{
86 return (this->host_name.empty () && ! this->hasAddress ()
87 && certinfo.empty ());
88}
89
90bool
91compile_server_info::hasAddress () const
92{
93 return this->address.raw.family != 0;
94}
95
96unsigned short
97compile_server_info::setAddressPort (unsigned short port)
98{
99 if (this->address.raw.family == PR_AF_INET)
100 return this->address.inet.port = htons (port);
101 if (this->address.raw.family == PR_AF_INET6)
102 return this->address.ipv6.port = htons (port);
103 assert (0);
104 return 0;
105}
106
107bool
108compile_server_info::isComplete () const
109{
110 return this->hasAddress () && port != 0;
111}
112
113string
114compile_server_info::host_specification () const
115{
116 ostringstream host_spec;
117 if (!this->isComplete())
118 return host_spec.str();
119
120 if (! this->host_name.empty ())
121 host_spec << this->host_name;
122 else if (this->hasAddress())
123 {
124 PRStatus prStatus;
125 switch (this->address.raw.family)
126 {
127 case PR_AF_INET:
128 case PR_AF_INET6:
129 {
130#define MAX_NETADDR_SIZE 46 // from the NSPR API reference.
131 char buf[MAX_NETADDR_SIZE];
132 prStatus = PR_NetAddrToString(&this->address, buf, sizeof (buf));
133 if (prStatus == PR_SUCCESS) {
134 host_spec << buf;
135 break;
136 }
137 }
138 break;
139 default:
140 // Do nothing.
141 break;
142 }
143 }
144 if (! host_spec.str().empty())
145 host_spec << ":" << this->port;
146 return host_spec.str();
147}
148
149bool
150compile_server_info::operator== (const compile_server_info &that) const
151{
152 // If one item or the other has only a name, and possibly a port
153 // specified, then allow a match by name and port only. This is so
154 // that the user can specify names which are returned by avahi, but
155 // are not dns resolvable. Otherwise, we will ignore the host_name.
156 if ((! this->hasAddress() && this->version.empty () &&
157 this->sysinfo.empty () && this->certinfo.empty ()) ||
158 (! that.hasAddress() && that.version.empty () &&
159 that.sysinfo.empty () && that.certinfo.empty ()))
160 {
161 if (this->host_name != that.host_name)
162 return false;
163 }
164
165 // Compare the other fields only if they have both been set.
166 if (this->hasAddress() && that.hasAddress() && this->address != that.address)
167 return false;
168 if (this->port != 0 && that.port != 0 && this->port != that.port)
169 return false;
170 if (! this->version.empty () && ! that.version.empty ()
171 && this->version != that.version)
172 return false;
173 if (! this->sysinfo.empty () && ! that.sysinfo.empty ()
174 && this->sysinfo != that.sysinfo)
175 return false;
176 if (! this->certinfo.empty () && ! that.certinfo.empty ()
177 && this->certinfo != that.certinfo)
178 return false;
179 if (! this->mok_fingerprints.empty () && ! that.mok_fingerprints.empty ()
180 && this->mok_fingerprints != that.mok_fingerprints)
181 return false;
182
183 return true; // They are equal
184}
185
186// Used to sort servers by preference for order of contact. The
187// preferred server is "less" than the other one.
188bool compile_server_info::operator< (const compile_server_info &that) const
189{
190 // Prefer servers with a later (higher) version number.
191 cs_protocol_version this_version (this->version.c_str ());
192 cs_protocol_version that_version (that.version.c_str ());
193 return that_version < this_version;
194}
195
196ostream &operator<< (ostream &s, const compile_server_info &i);
197ostream &operator<< (ostream &s, const vector<compile_server_info> &v);
198
199static void
200preferred_order (vector<compile_server_info> &servers)
201{
202 // Sort the given list of servers into the preferred order for contacting.
203 // Don't bother if there are less than 2 servers in the list.
204 if (servers.size () < 2)
205 return;
206
207 // Sort the list using compile_server_info::operator<
208 sort (servers.begin (), servers.end ());
209}
210
211struct resolved_host // see also PR16326, PR16342
212{
213 string host_name;
214 PRNetAddr address;
215 resolved_host(string chost_name, PRNetAddr caddress):
216 host_name(chost_name), address(caddress) {}
217};
218
219struct compile_server_cache
220{
221 vector<compile_server_info> default_servers;
222 vector<compile_server_info> specified_servers;
223 vector<compile_server_info> trusted_servers;
224 vector<compile_server_info> signing_servers;
225 vector<compile_server_info> online_servers;
226 vector<compile_server_info> all_servers;
227 map<string,vector<resolved_host> > resolved_hosts;
228};
229
230// For filtering queries.
231enum compile_server_properties {
232 compile_server_all = 0x1,
233 compile_server_trusted = 0x2,
234 compile_server_online = 0x4,
235 compile_server_compatible = 0x8,
236 compile_server_signer = 0x10,
237 compile_server_specified = 0x20
238};
239
240// Static functions.
241static compile_server_cache* cscache(systemtap_session& s);
242static void
243query_server_status (systemtap_session &s, const string &status_string);
244
245static void
246get_server_info (systemtap_session &s, int pmask,
247 vector<compile_server_info> &servers);
248static void
249get_default_server_info (systemtap_session &s,
250 vector<compile_server_info> &servers);
251
252static void
253merge_server_info (const compile_server_info &source,
254 compile_server_info &target);
255
256static void
257resolve_host (systemtap_session& s, compile_server_info &server,
258 vector<compile_server_info> &servers);
259
260// -----------------------------------------------------
261// NSS related code used by the compile server client
262// -----------------------------------------------------
263static void
264add_server_trust (systemtap_session &s, const string &cert_db_path,
265 vector<compile_server_info> &server_list);
266static void
267revoke_server_trust (systemtap_session &s, const string &cert_db_path,
268 const vector<compile_server_info> &server_list);
269
270void
271get_server_info_from_db (systemtap_session &s,
272 vector<compile_server_info> &servers,
273 const string &cert_db_path);
274
275string
276global_client_cert_db_path ()
277{
278 return SYSCONFDIR "/systemtap/ssl/client";
279}
280
281static string
282private_ssl_cert_db_path ()
283{
284 return local_client_cert_db_path ();
285}
286
287string
288global_ssl_cert_db_path ()
289{
290 return global_client_cert_db_path ();
291}
292
293string
294signing_cert_db_path ()
295{
296 return SYSCONFDIR "/systemtap/staprun";
297}
298
299/* Connection state. */
300typedef struct connectionState_t
301{
302 const char *hostName;
303 PRNetAddr addr;
304 const char *infileName;
305 const char *outfileName;
306 const char *trustNewServerMode;
307} connectionState_t;
308
309/* Add the server's certificate to our database of trusted servers. */
310SECStatus
311nss_trustNewServer (CERTCertificate *serverCert)
312{
313 SECStatus secStatus;
314 CERTCertTrust *trust = NULL;
315 PK11SlotInfo *slot = NULL;
316
317 /* Import the certificate. */
318 slot = PK11_GetInternalKeySlot();
319 const char *nickname = server_cert_nickname ();
320 secStatus = PK11_ImportCert(slot, serverCert, CK_INVALID_HANDLE, nickname, PR_FALSE);
321 if (secStatus != SECSuccess)
322 goto done;
323
324 if (PK11_NeedUserInit(slot)) {
325 PK11_InitPin(slot, (char*)NULL, "");
326 }
327
328 /* Make it a trusted peer. */
329 trust = (CERTCertTrust *)PORT_ZAlloc(sizeof(CERTCertTrust));
330 if (! trust)
331 {
332 secStatus = SECFailure;
333 goto done;
334 }
335
336 secStatus = CERT_DecodeTrustString(trust, "P,P,P");
337 if (secStatus != SECSuccess)
338 goto done;
339
340 secStatus = CERT_ChangeCertTrust(CERT_GetDefaultCertDB(), serverCert, trust);
341
342done:
343 if (slot)
344 PK11_FreeSlot (slot);
345 if (trust)
346 PORT_Free(trust);
347 return secStatus;
348}
349
350static bool
351isIPv6LinkLocal (const PRNetAddr &address)
352{
353 // Link-local addresses are members of the address block fe80::
354 if (address.raw.family == PR_AF_INET6 &&
355 address.ipv6.ip.pr_s6_addr[0] == 0xfe && address.ipv6.ip.pr_s6_addr[1] == 0x80)
356 return true;
357 return false;
358}
359
360// Issue a status message for when a server's trust is already in place.
361static void
362trust_already_in_place (
363 const compile_server_info &server,
364 const vector<compile_server_info> &server_list,
365 const string cert_db_path,
366 bool revoking
367)
368{
369 // What level of trust?
370 string purpose;
371 if (cert_db_path == signing_cert_db_path ())
372 purpose = _("as a module signer for all users");
373 else
374 {
375 purpose = _("as an SSL peer");
376 if (cert_db_path == global_ssl_cert_db_path ())
377 purpose += _(" for all users");
378 else
379 purpose += _(" for the current user");
380 }
381
382 // Issue a message for each server in the list with the same certificate.
383 unsigned limit = server_list.size ();
384 for (unsigned i = 0; i < limit; ++i)
385 {
386 if (server.certinfo != server_list[i].certinfo)
387 continue;
388 clog << server_list[i] << _(" is already ");
389 if (revoking)
390 clog << _("untrusted ") << purpose << endl;
391 else
392 clog << _("trusted ") << purpose << endl;
393 }
394}
395
396// Add the given servers to the given database of trusted servers.
397static void
398add_server_trust (
399 systemtap_session &s,
400 const string &cert_db_path,
401 vector<compile_server_info> &server_list
402)
403{
404 // Get a list of servers already trusted. This opens the database, so do it
405 // before we open it for our own purposes.
406 vector<compile_server_info> already_trusted;
407 get_server_info_from_db (s, already_trusted, cert_db_path);
408
409 // Make sure the given path exists.
410 if (create_dir (cert_db_path.c_str (), 0755) != 0)
411 {
412 clog << _F("Unable to find or create the client certificate database directory %s: ", cert_db_path.c_str());
413 perror ("");
414 return;
415 }
416
417 // Must predeclare this because of jumps to cleanup: below.
418 vector<string> processed_certs;
419
420 client_backend *backend = nss_get_client_backend (s);
421 if (backend->initialize () != 0)
422 return;
423
424 // Make sure NSPR is initialized. Must be done before NSS is initialized
425 s.NSPR_init ();
426
427 // Initialize the NSS libraries -- read/write
428 SECStatus secStatus = nssInit (cert_db_path.c_str (), 1/*readwrite*/);
429 if (secStatus != SECSuccess)
430 {
431 // Message already issued.
432 goto cleanup;
433 }
434
435 // Enable all cipher suites.
436 // SSL_ClearSessionCache is required for the new settings to take effect.
437 /* Some NSS versions don't do this correctly in NSS_SetDomesticPolicy. */
438 do {
439 const PRUint16 *cipher;
440 for (cipher = SSL_GetImplementedCiphers(); *cipher != 0; ++cipher)
441 SSL_CipherPolicySet(*cipher, SSL_ALLOWED);
442 } while (0);
443 SSL_ClearSessionCache ();
444
445 // Iterate over the servers to become trusted. Contact each one and
446 // add it to the list of trusted servers if it is not already trusted.
447 // trust_server_info() will issue any error messages.
448 for (vector<compile_server_info>::iterator server = server_list.begin();
449 server != server_list.end ();
450 ++server)
451 {
452 // Trust is based on certificates. We need only add trust in the
453 // same certificate once.
454 //
455 // RHBZ 1075685: If the new server to be trusted is selected by address + port,
456 // and there is no avahi assistance available, or the server is not known
457 // to avahi, then its certificate serial number field will be empty. We
458 // therefore have no basis for comparing it to the serial numbers on already-trusted
459 // certificates. In this case, unconditionally contact the new server to obtain
460 // its certificate.
461 if (! server->certinfo.empty ())
462 {
463 // We need not contact the server if it has already been processed.
464 if (find (processed_certs.begin (), processed_certs.end (),
465 server->certinfo) != processed_certs.end ())
466 continue;
467 processed_certs.push_back (server->certinfo);
468
469 // We need not contact the server if it is already trusted.
470 if (find (already_trusted.begin (), already_trusted.end (), *server) !=
471 already_trusted.end ())
472 {
473 if (s.verbose >= 2)
474 trust_already_in_place (*server, server_list, cert_db_path, false/*revoking*/);
475 continue;
476 }
477 }
478
479 // At a minimum we need an ip_address along with a port
480 // number in order to contact an nss server.
481 if (s.http_servers.empty ())
482 {
483 if (! server->hasAddress() || server->port == 0)
484 continue;
485 // Set the port within the address.
486 server->setAddressPort (server->port);
487 }
488
489 int rc = backend->trust_server_info (*server);
490 if (rc != NSS_SUCCESS)
491 {
492 // Notice no space before the '%s'? The compile_server_info
493 // '<<' operator always outputs a space.
494 clog << _F("Unable to connect to%s", lex_cast(*server).c_str()) << endl;
495 nssError ();
496 // Additional information: if the address is IPv6 and is link-local, then it must
497 // have a scope_id.
498 if (isIPv6LinkLocal (server->address) && server->address.ipv6.scope_id == 0)
499 {
500 clog << _(" The address is an IPv6 link-local address with no scope specifier.")
501 << endl;
502 }
503 }
504 }
505
506 cleanup:
507 // Shutdown NSS.
508 // SSL_ClearSessionCache is required before shutdown for client applications.
509 SSL_ClearSessionCache ();
510 nssCleanup (cert_db_path.c_str (), NULL);
511
512 // Make sure the database files are readable.
513 glob_t globbuf;
514 string filespec = cert_db_path + "/*.db";
515 if (s.verbose >= 3)
516 clog << _F("Searching \"%s\"\n", filespec.c_str());
517 int r = glob (filespec.c_str (), 0, NULL, & globbuf);
518 if (r != GLOB_NOSPACE && r != GLOB_ABORTED && r != GLOB_NOMATCH)
519 {
520 for (unsigned i = 0; i < globbuf.gl_pathc; ++i)
521 {
522 string filename = globbuf.gl_pathv[i];
523 if (s.verbose >= 3)
524 clog << _(" found ") << filename << endl;
525
526 if (chmod (filename.c_str (), 0644) != 0)
527 {
528 s.print_warning("Unable to change permissions on " + filename + ": ");
529 perror ("");
530 }
531 }
532 globfree(& globbuf);
533 }
534}
535
536// Remove the given servers from the given database of trusted servers.
537static void
538revoke_server_trust (
539 systemtap_session &s,
540 const string &cert_db_path,
541 const vector<compile_server_info> &server_list
542)
543{
544 // Make sure the given path exists.
545 if (! file_exists (cert_db_path))
546 {
547 if (s.verbose >= 5)
548 {
549 clog << _F("Certificate database '%s' does not exist",
550 cert_db_path.c_str()) << endl;
551 for (vector<compile_server_info>::const_iterator server = server_list.begin();
552 server != server_list.end ();
553 ++server)
554 trust_already_in_place (*server, server_list, cert_db_path, true/*revoking*/);
555 }
556 return;
557 }
558
559 // Must predeclare these because of jumps to cleanup: below.
560 CERTCertDBHandle *handle;
561 PRArenaPool *tmpArena = NULL;
562 CERTCertList *certs = NULL;
563 CERTCertificate *db_cert;
564 vector<string> processed_certs;
565 const char *nickname;
566 SECStatus secStatus;
567
568 // Make sure NSPR is initialized. Must be done before NSS is initialized
569 s.NSPR_init ();
570
571 // Initialize the NSS libraries -- read/write
572 secStatus = nssInit (cert_db_path.c_str (), 1/*readwrite*/);
573 if (secStatus != SECSuccess)
574 {
575 // Message already issued
576 goto cleanup;
577 }
578 handle = CERT_GetDefaultCertDB();
579
580 // A memory pool to work in
581 tmpArena = PORT_NewArena(DER_DEFAULT_CHUNKSIZE);
582 if (! tmpArena)
583 {
584 clog << _("Out of memory:");
585 nssError ();
586 goto cleanup;
587 }
588
589 // Iterate over the servers to become untrusted.
590 nickname = server_cert_nickname ();
591 for (vector<compile_server_info>::const_iterator server = server_list.begin();
592 server != server_list.end ();
593 ++server)
594 {
595 // If the server's certificate serial number is unknown, then we can't
596 // match it with one in the database.
597 if (server->certinfo.empty ())
598 continue;
599
600 // Trust is based on certificates. We need only revoke trust in the same
601 // certificate once.
602 if (find (processed_certs.begin (), processed_certs.end (),
603 server->certinfo) != processed_certs.end ())
604 continue;
605 processed_certs.push_back (server->certinfo);
606
607 // Search the client-side database of trusted servers.
608 db_cert = PK11_FindCertFromNickname (nickname, NULL);
609 if (! db_cert)
610 {
611 // No trusted servers. Not an error, but issue a status message.
612 if (s.verbose >= 2)
613 trust_already_in_place (*server, server_list, cert_db_path, true/*revoking*/);
614 continue;
615 }
616
617 // Here, we have one cert with the desired nickname.
618 // Now, we will attempt to get a list of ALL certs
619 // with the same subject name as the cert we have. That list
620 // should contain, at a minimum, the one cert we have already found.
621 // If the list of certs is empty (NULL), the libraries have failed.
622 certs = CERT_CreateSubjectCertList (NULL, handle, & db_cert->derSubject,
623 PR_Now (), PR_FALSE);
624 CERT_DestroyCertificate (db_cert);
625 if (! certs)
626 {
627 clog << _F("Unable to query certificate database %s: ",
628 cert_db_path.c_str()) << endl;
629 PORT_SetError (SEC_ERROR_LIBRARY_FAILURE);
630 nssError ();
631 goto cleanup;
632 }
633
634 // Find the certificate matching the one belonging to our server.
635 CERTCertListNode *node;
636 for (node = CERT_LIST_HEAD (certs);
637 ! CERT_LIST_END (node, certs);
638 node = CERT_LIST_NEXT (node))
639 {
640 // The certificate we're working with.
641 db_cert = node->cert;
642
643 // Get the serial number.
644 string serialNumber = get_cert_serial_number (db_cert);
645
646 // Does the serial number match that of the current server?
647 if (serialNumber != server->certinfo)
648 continue; // goto next certificate
649
650 // All is ok! Remove the certificate from the database.
651 break;
652 } // Loop over certificates in the database
653
654 // Was a certificate matching the server found? */
655 if (CERT_LIST_END (node, certs))
656 {
657 // Not found. Server is already untrusted.
658 if (s.verbose >= 2)
659 trust_already_in_place (*server, server_list, cert_db_path, true/*revoking*/);
660 }
661 else
662 {
663 secStatus = SEC_DeletePermCertificate (db_cert);
664 if (secStatus != SECSuccess)
665 {
666 clog << _F("Unable to remove certificate from %s: ",
667 cert_db_path.c_str()) << endl;
668 nssError ();
669 }
670 }
671 CERT_DestroyCertList (certs);
672 certs = NULL;
673 } // Loop over servers
674
675 cleanup:
676 assert(!certs);
677 if (tmpArena)
678 PORT_FreeArena (tmpArena, PR_FALSE);
679
680 nssCleanup (cert_db_path.c_str (), NULL);
681}
682
683// Obtain information about servers from the certificates in the given database.
684void
685get_server_info_from_db (
686 systemtap_session &s,
687 vector<compile_server_info> &servers,
688 const string &cert_db_path
689 )
690{
691 NSSInitContext *context;
692
693 // Make sure the given path exists.
694 if (! file_exists (cert_db_path))
695 {
696 if (s.verbose >= 5)
697 clog << _F("Certificate database '%s' does not exist.",
698 cert_db_path.c_str()) << endl;
699 return;
700 }
701
702 // Make sure NSPR is initialized. Must be done before NSS is initialized
703 s.NSPR_init ();
704
705 // Initialize the NSS libraries -- readonly
706 context = nssInitContext (cert_db_path.c_str (), 0 /* readWrite */, 0 /* issueMessage */);
707 if (context == NULL)
708 {
709 // Message already issued.
710 return;
711 }
712
713 CERTCertList *certs = get_cert_list_from_db (server_cert_nickname ());
714 if (! certs)
715 {
716 if (s.verbose >= 5)
717 clog << _F("No certificate found in database %s", cert_db_path.c_str ()) << endl;
718 goto cleanup;
719 }
720
721 for (CERTCertListNode *node = CERT_LIST_HEAD (certs);
722 ! CERT_LIST_END (node, certs);
723 node = CERT_LIST_NEXT (node))
724 {
725 compile_server_info server_info;
726
727 // The certificate we're working with.
728 CERTCertificate *db_cert = node->cert;
729
730 if (get_host_name (db_cert, server_info.host_name) == false)
731 continue;
732
733 // Get the serial number.
734 server_info.certinfo = get_cert_serial_number (db_cert);
735
736 // Our results will at a minimum contain this server.
737 nss_add_server_info (server_info, servers);
738
739 // Augment the list by querying all online servers and keeping the ones
740 // with the same cert serial number.
741 vector<compile_server_info> online_servers;
742 nss_get_or_keep_online_server_info (s, online_servers, false/*keep*/);
743 nss_keep_server_info_with_cert_and_port (s, server_info, online_servers);
744 nss_add_server_info (online_servers, servers);
745 }
746
747 cleanup:
748 if (certs)
749 CERT_DestroyCertList (certs);
750
751 nssCleanup (cert_db_path.c_str (), context);
752}
753
754// Utility Functions.
755//-----------------------------------------------------------------------
756ostream &operator<< (ostream &s, const compile_server_info &i)
757{
758 // Don't print empty information
759 if (i.empty ())
760 return s;
761
762 s << " host=";
763 if (! i.host_name.empty ())
764 s << i.host_name;
765 else
766 s << "unknown";
767 s << " address=";
768 if (i.hasAddress())
769 {
770 PRStatus prStatus;
771 switch (i.address.raw.family)
772 {
773 case PR_AF_INET:
774 case PR_AF_INET6:
775 {
776#define MAX_NETADDR_SIZE 46 // from the NSPR API reference.
777 char buf[MAX_NETADDR_SIZE];
778 prStatus = PR_NetAddrToString(& i.address, buf, sizeof (buf));
779 if (prStatus == PR_SUCCESS) {
780 s << buf;
781 break;
782 }
783 }
784 // Fall through
785 default:
786 s << "offline";
787 break;
788 }
789 }
790 else
791 s << "offline";
792 s << " port=";
793 if (i.port != 0)
794 s << i.port;
795 else
796 s << "unknown";
797 s << " sysinfo=\"";
798 if (! i.sysinfo.empty ())
799 s << i.sysinfo << '"';
800 else
801 s << "unknown\"";
802 s << " version=";
803 if (! i.version.empty ())
804 s << i.version;
805 else
806 s << "unknown";
807 s << " certinfo=\"";
808 if (! i.certinfo.empty ())
809 s << i.certinfo << '"';
810 else
811 s << "unknown\"";
812 if (! i.mok_fingerprints.empty ())
813 {
814 // FIXME: Yikes, this output is ugly. Perhaps the server output
815 // needs a more structured approach.
816 s << " mok_fingerprints=\"";
817 vector<string>::const_iterator it;
818 for (it = i.mok_fingerprints.begin (); it != i.mok_fingerprints.end ();
819 it++)
820 {
821 if (it != i.mok_fingerprints.begin ())
822 s << ", ";
823 s << *it;
824 }
825 s << "\"";
826 }
827 return s;
828}
829
830ostream &operator<< (ostream &s, const vector<compile_server_info> &v)
831{
832 // Indicate an empty list.
833 if (v.size () == 0 || (v.size () == 1 && v[0].empty()))
834 s << "No Servers" << endl;
835 else
836 {
837 for (unsigned i = 0; i < v.size(); ++i)
838 {
839 // Don't print empty items.
840 if (! v[i].empty())
841 s << v[i] << endl;
842 }
843 }
844 return s;
845}
846
847PRNetAddr &
848copyNetAddr (PRNetAddr &x, const PRNetAddr &y)
849{
850 PRUint32 saveScope = 0;
851
852 // For IPv6 addresses, don't overwrite the scope_id of x unless x is uninitialized or it is 0.
853 if (x.raw.family == PR_AF_INET6)
854 saveScope = x.ipv6.scope_id;
855
856 x = y;
857
858 if (saveScope != 0)
859 x.ipv6.scope_id = saveScope;
860
861 return x;
862}
863
864bool
865operator== (const PRNetAddr &x, const PRNetAddr &y)
866{
867 // Same address family?
868 if (x.raw.family != y.raw.family)
869 return false;
870
871 switch (x.raw.family)
872 {
873 case PR_AF_INET6:
874 // If both scope ids are set, compare them.
875 if (x.ipv6.scope_id != 0 && y.ipv6.scope_id != 0 && x.ipv6.scope_id != y.ipv6.scope_id)
876 return false; // not equal
877 // Scope is not a factor. Compare the address bits
878 return memcmp (& x.ipv6.ip, & y.ipv6.ip, sizeof(x.ipv6.ip)) == 0;
879 case PR_AF_INET:
880 return x.inet.ip == y.inet.ip;
881 default:
882 break;
883 }
884 return false;
885}
886
887bool
888operator!= (const PRNetAddr &x, const PRNetAddr &y)
889{
890 return !(x == y);
891}
892
893static PRIPv6Addr &
894copyAddress (PRIPv6Addr &PRin6, const in6_addr &in6)
895{
896 // The NSPR type is a typedef of struct in6_addr, but C++ won't let us copy it
897 assert (sizeof (PRin6) == sizeof (in6));
898 memcpy (& PRin6, & in6, sizeof (PRin6));
899 return PRin6;
900}
901
902// Return the default server specification, used when none is given on the
903// command line.
904static string
905default_server_spec (const systemtap_session &s)
906{
907 // If --privilege=X has been used, where X is not stapdev,
908 // the default is online,trusted,compatible,signer
909 // otherwise
910 // the default is online,trusted,compatible
911 //
912 // Having said that,
913 // 'online' and 'compatible' will only succeed if we have avahi
914 // 'trusted' and 'signer' will only succeed if we have NSS
915 //
916 string working_string = "online,trusted,compatible";
917 if (! pr_contains (s.privilege, pr_stapdev))
918 working_string += ",signer";
919 return working_string;
920}
921
922static int
923server_spec_to_pmask (const string &server_spec)
924{
925 // Construct a mask of the server properties that have been requested.
926 // The available properties are:
927 // trusted - servers which are trusted SSL peers.
928 // online - online servers.
929 // compatible - servers which compile for the current kernel release
930 // and architecture.
931 // signer - servers which are trusted module signers.
932 // specified - servers which have been specified using --use-server=XXX.
933 // If no servers have been specified, then this is
934 // equivalent to --list-servers=trusted,online,compatible.
935 // all - all trusted servers, trusted module signers,
936 // servers currently online and specified servers.
937 string working_spec = server_spec;
938 vector<string> properties;
939 tokenize (working_spec, properties, ",");
940 int pmask = 0;
941 unsigned limit = properties.size ();
942 for (unsigned i = 0; i < limit; ++i)
943 {
944 const string &property = properties[i];
945 // Tolerate (and ignore) empty properties.
946 if (property.empty ())
947 continue;
948 if (property == "all")
949 {
950 pmask |= compile_server_all;
951 }
952 else if (property == "specified")
953 {
954 pmask |= compile_server_specified;
955 }
956 else if (property == "trusted")
957 {
958 pmask |= compile_server_trusted;
959 }
960 else if (property == "online")
961 {
962 pmask |= compile_server_online;
963 }
964 else if (property == "compatible")
965 {
966 pmask |= compile_server_compatible;
967 }
968 else if (property == "signer")
969 {
970 pmask |= compile_server_signer;
971 }
972 else
973 {
974 // XXX PR13274 needs-session to use print_warning()
975 clog << _F("WARNING: unsupported compile server property: %s", property.c_str())
976 << endl;
977 }
978 }
979 return pmask;
980}
981
982void
983nss_client_query_server_status (systemtap_session &s)
984{
985 unsigned limit = s.server_status_strings.size ();
986 for (unsigned i = 0; i < limit; ++i)
987 query_server_status (s, s.server_status_strings[i]);
988}
989
990static void
991query_server_status (systemtap_session &s, const string &status_string)
992{
993 // If this string is empty, then the default is "specified"
994 string working_string = status_string;
995 if (working_string.empty ())
996 working_string = "specified";
997
998 // If the query is "specified" and no servers have been specified
999 // (i.e. '--use-server' and/or '--use-http-server' not used or used
1000 // with no argument), then use the default query.
1001 //
1002 // TODO: This may not be necessary. The underlying queries should handle
1003 // "specified" properly.
1004 if (working_string == "specified"
1005 && (s.specified_servers.empty ()
1006 || (s.specified_servers.size () == 1
1007 && s.specified_servers[0].empty ()))
1008 && (s.http_servers.empty()
1009 || (s.http_servers.size () == 1
1010 && s.http_servers[0].empty ())))
1011 working_string = default_server_spec (s);
1012
1013 int pmask = server_spec_to_pmask (working_string);
1014
1015 // Now obtain a list of the servers which match the criteria.
1016 vector<compile_server_info> raw_servers;
1017 get_server_info (s, pmask, raw_servers);
1018
1019 // Augment the listing with as much information as possible by adding
1020 // information from known servers.
1021 vector<compile_server_info> servers;
1022 nss_get_all_server_info (s, servers);
1023 nss_keep_common_server_info (raw_servers, servers);
1024
1025 // Sort the list of servers into a preferred order.
1026 preferred_order (servers);
1027
1028 // Print the server information. Skip the empty entry at the head of the list.
1029 clog << _F("Systemtap Compile Server Status for '%s'", working_string.c_str()) << endl;
1030 bool found = false;
1031 unsigned limit = servers.size ();
1032 for (unsigned i = 0; i < limit; ++i)
1033 {
1034 assert (! servers[i].empty ());
1035 // Don't list servers with no cert information. They may not actually
1036 // exist.
1037 // TODO: Could try contacting the server and obtaining its cert
1038 if (servers[i].certinfo.empty ())
1039 continue;
1040 clog << servers[i] << endl;
1041 found = true;
1042 }
1043 if (! found)
1044 clog << _("No servers found") << endl;
1045}
1046
1047// Add or remove trust of the servers specified on the command line.
1048void
1049nss_client_manage_server_trust (systemtap_session &s)
1050{
1051 // This function should do nothing if we don't have NSS.
1052 // Nothing to do if --trust-servers was not specified.
1053 if (s.server_trust_spec.empty ())
1054 return;
1055
1056 // Break up and analyze the trust specification. Recognized components are:
1057 // ssl - trust the specified servers as ssl peers
1058 // signer - trust the specified servers as module signers
1059 // revoke - revoke the requested trust
1060 // all-users - apply/revoke the requested trust for all users
1061 // no-prompt - don't prompt the user for confirmation
1062 vector<string>components;
1063 tokenize (s.server_trust_spec, components, ",");
1064 bool ssl = false;
1065 bool signer = false;
1066 bool revoke = false;
1067 bool all_users = false;
1068 bool no_prompt = false;
1069 bool error = false;
1070 for (vector<string>::const_iterator i = components.begin ();
1071 i != components.end ();
1072 ++i)
1073 {
1074 if (*i == "ssl")
1075 ssl = true;
1076 else if (*i == "signer")
1077 {
1078 if (geteuid () != 0)
1079 {
1080 clog << _("Only root can specify 'signer' on --trust-servers") << endl;
1081 error = true;
1082 }
1083 else
1084 signer = true;
1085 }
1086 else if (*i == "revoke")
1087 revoke = true;
1088 else if (*i == "all-users")
1089 {
1090 if (geteuid () != 0)
1091 {
1092 clog << _("Only root can specify 'all-users' on --trust-servers") << endl;
1093 error = true;
1094 }
1095 else
1096 all_users = true;
1097 }
1098 else if (*i == "no-prompt")
1099 no_prompt = true;
1100 else
1101 s.print_warning("Unrecognized server trust specification: " + *i);
1102 }
1103 if (error)
1104 return;
1105
1106 // Make sure NSPR is initialized
1107 s.NSPR_init ();
1108
1109 // Now obtain the list of specified servers.
1110 vector<compile_server_info> server_list;
1111 nss_get_specified_server_info (s, server_list, true/*no_default*/);
1112
1113 // Did we identify any potential servers?
1114 unsigned limit = server_list.size ();
1115 if (limit == 0)
1116 {
1117 clog << _("No servers identified for trust") << endl;
1118 return;
1119 }
1120
1121 // Create a string representing the request in English.
1122 // If neither 'ssl' or 'signer' was specified, the default is 'ssl'.
1123 if (! ssl && ! signer)
1124 ssl = true;
1125 ostringstream trustString;
1126 if (ssl)
1127 {
1128 trustString << _("as an SSL peer");
1129 if (all_users)
1130 trustString << _(" for all users");
1131 else
1132 trustString << _(" for the current user");
1133 }
1134 if (signer)
1135 {
1136 if (ssl)
1137 trustString << _(" and ");
1138 trustString << _("as a module signer for all users");
1139 }
1140
1141 // Prompt the user to confirm what's about to happen.
1142 if (no_prompt)
1143 {
1144 if (revoke)
1145 clog << _("Revoking trust ");
1146 else
1147 clog << _("Adding trust ");
1148 }
1149 else
1150 {
1151 if (revoke)
1152 clog << _("Revoke trust ");
1153 else
1154 clog << _("Add trust ");
1155 }
1156 clog << _F("in the following servers %s", trustString.str().c_str());
1157 if (! no_prompt)
1158 clog << '?';
1159 clog << endl;
1160 for (unsigned i = 0; i < limit; ++i)
1161 clog << " " << server_list[i] << endl;
1162 if (! no_prompt)
1163 {
1164 clog << "[y/N] " << flush;
1165
1166 // Only carry out the operation if the response is "yes"
1167 string response;
1168 cin >> response;
1169 if (response[0] != 'y' && response [0] != 'Y')
1170 {
1171 clog << _("Server trust unchanged") << endl;
1172 return;
1173 }
1174 }
1175
1176 // Now add/revoke the requested trust.
1177 string cert_db_path;
1178 if (ssl)
1179 {
1180 if (all_users)
1181 cert_db_path = global_ssl_cert_db_path ();
1182 else
1183 cert_db_path = private_ssl_cert_db_path ();
1184 if (revoke)
1185 revoke_server_trust (s, cert_db_path, server_list);
1186 else
1187 add_server_trust (s, cert_db_path, server_list);
1188 }
1189 if (signer)
1190 {
1191 cert_db_path = signing_cert_db_path ();
1192 if (revoke)
1193 revoke_server_trust (s, cert_db_path, server_list);
1194 else
1195 add_server_trust (s, cert_db_path, server_list);
1196 }
1197}
1198
1199static compile_server_cache*
1200cscache(systemtap_session& s)
1201{
1202 if (!s.server_cache)
1203 s.server_cache = new compile_server_cache();
1204 return s.server_cache;
1205}
1206
1207static void
1208get_server_info (
1209 systemtap_session &s,
1210 int pmask,
1211 vector<compile_server_info> &servers
1212)
1213{
1214 // Get information on compile servers matching the requested criteria.
1215 // The order of queries is significant. Accumulating queries must go first
1216 // followed by accumulating/filtering queries.
1217 bool keep = false;
1218 if (((pmask & compile_server_all)))
1219 {
1220 nss_get_all_server_info (s, servers);
1221 keep = true;
1222 }
1223 // Add the specified servers, if requested
1224 if ((pmask & compile_server_specified))
1225 {
1226 nss_get_specified_server_info (s, servers);
1227 keep = true;
1228 }
1229 // Now filter or accumulate the list depending on whether a query has
1230 // already been made.
1231 if ((pmask & compile_server_online))
1232 {
1233 nss_get_or_keep_online_server_info (s, servers, keep);
1234 keep = true;
1235 }
1236 if ((pmask & compile_server_trusted))
1237 {
1238 nss_get_or_keep_trusted_server_info (s, servers, keep);
1239 keep = true;
1240 }
1241 if ((pmask & compile_server_signer))
1242 {
1243 nss_get_or_keep_signing_server_info (s, servers, keep);
1244 keep = true;
1245 }
1246 if ((pmask & compile_server_compatible))
1247 {
1248 nss_get_or_keep_compatible_server_info (s, servers, keep);
1249 keep = true;
1250 }
1251}
1252
1253// Get information about all online servers as well as servers trusted
1254// as SSL peers and servers trusted as signers.
1255void
1256nss_get_all_server_info (
1257 systemtap_session &s,
1258 vector<compile_server_info> &servers
1259)
1260{
1261 // We only need to obtain this once per session. This is a good thing(tm)
1262 // since obtaining this information is expensive.
1263 vector<compile_server_info>& all_servers = cscache(s)->all_servers;
1264 if (all_servers.empty ())
1265 {
1266 nss_get_or_keep_online_server_info (s, all_servers, false/*keep*/);
1267 nss_get_or_keep_trusted_server_info (s, all_servers, false/*keep*/);
1268 nss_get_or_keep_signing_server_info (s, all_servers, false/*keep*/);
1269
1270 if (s.verbose >= 4)
1271 {
1272 clog << _("All known servers:") << endl;
1273 clog << all_servers;
1274 }
1275 }
1276
1277 // Add the information, but not duplicates.
1278 nss_add_server_info (all_servers, servers);
1279}
1280
1281static void
1282get_default_server_info (
1283 systemtap_session &s,
1284 vector<compile_server_info> &servers
1285)
1286{
1287 if (s.verbose >= 3)
1288 clog << _("Using the default servers") << endl;
1289
1290 // We only need to obtain this once per session. This is a good thing(tm)
1291 // since obtaining this information is expensive.
1292 vector<compile_server_info>& default_servers = cscache(s)->default_servers;
1293 if (default_servers.empty ())
1294 {
1295 // Get the required information.
1296 // get_server_info will add an empty entry at the beginning to indicate
1297 // that the search has been performed, in case the search comes up empty.
1298 int pmask = server_spec_to_pmask (default_server_spec (s));
1299 get_server_info (s, pmask, default_servers);
1300
1301 if (s.verbose >= 3)
1302 {
1303 clog << _("Default servers are:") << endl;
1304 clog << default_servers;
1305 }
1306 }
1307
1308 // Add the information, but not duplicates.
1309 nss_add_server_info (default_servers, servers);
1310}
1311
1312static bool
1313isPort (const char *pstr, compile_server_info &server_info)
1314{
1315 errno = 0;
1316 char *estr;
1317 unsigned long p = strtoul (pstr, & estr, 10);
1318 if (errno != 0 || *estr != '\0' || p > USHRT_MAX)
1319 {
1320 clog << _F("Invalid port number specified: %s", pstr) << endl;
1321 return false;
1322 }
1323 server_info.port = p;
1324 server_info.fully_specified = true;
1325 return true;
1326}
1327
1328static bool
1329isIPv6 (const string &server, compile_server_info &server_info)
1330{
1331 // An IPv6 address is 8 hex components separated by colons.
1332 // One contiguous block of zero segments in the address may be elided using ::.
1333 // An interface may be specified by appending %IF_NAME to the address (e.g. %eth0).
1334 // A port may be specified by enclosing the ip address in [] and adding :<port>.
1335 // Allow a bracketed address without a port.
1336 assert (! server.empty());
1337 string ip;
1338 string::size_type portIx;
1339 if (server[0] == '[')
1340 {
1341 string::size_type endBracket = server.find (']');
1342 if (endBracket == string::npos)
1343 return false; // Not a valid IPv6 address
1344 // Extract the address.
1345 ip = server.substr (1, endBracket - 1);
1346 portIx = endBracket + 1;
1347 }
1348 else
1349 {
1350 ip = server;
1351 portIx = string::npos;
1352 }
1353
1354 // Find out how many components there are. The maximum is 8
1355 unsigned empty = 0;
1356 vector<string> components;
1357 tokenize_full (ip, components, ":");
1358 if (components.size() > 8)
1359 return false; // Not a valid IPv6 address
1360
1361 // The components must be either hex values between 0 and 0xffff, or must be empty.
1362 // There can be only one empty component.
1363 string interface;
1364 for (unsigned i = 0; i < components.size(); ++i)
1365 {
1366 if (components[i].empty())
1367 {
1368 if (++empty > 1)
1369 return false; // Not a valid IPv6 address
1370 }
1371 // If it's the final component, see if it specifies the interface. If so, strip it from the
1372 // component in order to simplify parsing. It still remains as part of the original ip address
1373 // string.
1374 if (i == components.size() - 1)
1375 {
1376 size_t ix = components[i].find ('%');
1377 if (ix != string::npos)
1378 {
1379 interface = components[i].substr(ix);
1380 components[i] = components[i].substr(0, ix);
1381 }
1382 }
1383 // Skip leading zeroes.
1384 unsigned j;
1385 for (j = 0; j < components[i].size(); ++j)
1386 {
1387 if (components[i][j] != '0')
1388 break;
1389 }
1390 // Max of 4 hex digits
1391 if (components[i].size() - j > 4)
1392 return false; // Not a valid IPv6 address
1393 for (/**/; j < components[i].size(); ++j)
1394 {
1395 if (! isxdigit (components[i][j]))
1396 return false; // Not a valid IPv6 address
1397 }
1398 }
1399 // If there is no empty component, then there must be exactly 8 components.
1400 if (! empty && components.size() != 8)
1401 return false; // Not a valid IPv6 address
1402
1403 // Try to convert the string to an address.
1404 PRStatus prStatus = PR_StringToNetAddr (ip.c_str(), & server_info.address);
1405 if (prStatus != PR_SUCCESS)
1406 return false;
1407
1408 // Examine the optional port
1409 if (portIx != string::npos)
1410 {
1411 string port = server.substr (portIx);
1412 if (port.size() != 0)
1413 {
1414 if (port.size() < 2 || port[0] != ':')
1415 return false; // Not a valid Port
1416
1417 port = port.substr (1);
1418 if (! isPort (port.c_str(), server_info))
1419 return false; // not a valid port
1420 }
1421 }
1422 else
1423 server_info.port = 0;
1424
1425 return true; // valid IPv6 address.
1426}
1427
1428static bool
1429isIPv4 (const string &server, compile_server_info &server_info)
1430{
1431 // An IPv4 address is 4 decimal components separated by periods with an
1432 // additional optional decimal port separated from the address by a colon.
1433 assert (! server.empty());
1434
1435 // Find out how many components there are. The maximum is 8
1436 vector<string> components;
1437 tokenize (server, components, ":");
1438 if (components.size() > 2)
1439 return false; // Not a valid IPv4 address
1440
1441 // Separate the address from the port (if any).
1442 string addr;
1443 string port;
1444 if (components.size() <= 1)
1445 addr = server;
1446 else {
1447 addr = components[0];
1448 port = components[1];
1449 }
1450
1451 // Separate the address components.
1452 // There must be exactly 4 components.
1453 components.clear ();
1454 tokenize (addr, components, ".");
1455 if (components.size() != 4)
1456 return false; // Not a valid IPv4 address
1457
1458 // The components must be decimal values between 0 and 255.
1459 for (unsigned i = 0; i < components.size(); ++i)
1460 {
1461 if (components[i].empty())
1462 return false; // Not a valid IPv4 address
1463 errno = 0;
1464 char *estr;
1465 long p = strtol (components[i].c_str(), & estr, 10);
1466 if (errno != 0 || *estr != '\0' || p < 0 || p > 255)
1467 return false; // Not a valid IPv4 address
1468 }
1469
1470 // Try to convert the string to an address.
1471 PRStatus prStatus = PR_StringToNetAddr (addr.c_str(), & server_info.address);
1472 if (prStatus != PR_SUCCESS)
1473 return false;
1474
1475 // Examine the optional port
1476 if (! port.empty ()) {
1477 if (! isPort (port.c_str(), server_info))
1478 return false; // not a valid port
1479 }
1480 else
1481 server_info.port = 0;
1482
1483 return true; // valid IPv4 address.
1484}
1485
1486static bool
1487isCertSerialNumber (const string &server, compile_server_info &server_info)
1488{
1489 // This function assumes that we have already ruled out the server spec being an IPv6 address.
1490 // Certificate serial numbers are 5 fields separated by colons plus an optional 6th decimal
1491 // field specifying a port.
1492 assert (! server.empty());
1493 string host = server;
1494 vector<string> components;
1495 tokenize (host, components, ":");
1496 switch (components.size ())
1497 {
1498 case 6:
1499 if (! isPort (components.back().c_str(), server_info))
1500 return false; // not a valid port
1501 host = host.substr (0, host.find_last_of (':'));
1502 // fall through
1503 case 5:
1504 server_info.certinfo = host;
1505 break;
1506 default:
1507 return false; // not a cert serial number
1508 }
1509
1510 return true; // valid cert serial number and optional port
1511}
1512
1513static bool
1514isDomain (const string &server, compile_server_info &server_info)
1515{
1516 // Accept one or two components separated by a colon. The first will be the domain name and
1517 // the second must a port number.
1518 assert (! server.empty());
1519 string host = server;
1520 vector<string> components;
1521 unsigned scheme_offset = 0;
1522 if (host.substr (0, 6) == "https:")
1523 scheme_offset = 1;
1524 tokenize (host, components, ":");
1525 switch (components.size ())
1526 {
1527 case 3:
1528 case 2:
1529 if (! isPort (components.back().c_str(), server_info))
1530 return false; // not a valid port
1531 host = components[(0 + scheme_offset)].substr (scheme_offset * 2);
1532 // fall through
1533 case 1:
1534 server_info.host_name = host;
1535 break;
1536 default:
1537 return false; // not a valid domain name
1538 }
1539
1540 return true;
1541}
1542
1543void
1544nss_get_specified_server_info (
1545 systemtap_session &s,
1546 vector<compile_server_info> &servers,
1547 bool no_default
1548)
1549{
1550 // We only need to obtain this once per session. This is a good thing(tm)
1551 // since obtaining this information is expensive.
1552 vector<compile_server_info>& specified_servers = cscache(s)->specified_servers;
1553 if (specified_servers.empty ())
1554 {
1555 // Maintain an empty entry to indicate that this search has been
1556 // performed, in case the search comes up empty.
1557 specified_servers.push_back (compile_server_info ());
1558
1559 // If --use-server was not specified at all, then return info for the
1560 // default server list.
1561 if (s.specified_servers.empty () && s.http_servers.empty ())
1562 {
1563 if (s.verbose >= 3)
1564 clog << _("No servers specified") << endl;
1565 if (! no_default)
1566 get_default_server_info (s, specified_servers);
1567 }
1568 else
1569 {
1570 // Iterate over the specified servers. For each
1571 // specification, add to the list of servers.
1572 vector<string> &server_list = (! s.http_servers.empty ()
1573 ? s.http_servers
1574 : s.specified_servers);
1575 client_backend *backend = nss_get_client_backend (s);
1576 if (backend->initialize () != 0)
1577 return;
1578
1579 for (auto it = server_list.begin (); it != server_list.end (); ++it)
1580 {
1581 string &server = *it;
1582
1583 // If no specific server(s) specified, then use the
1584 // default servers.
1585 if (server.empty ())
1586 {
1587 if (s.verbose >= 3)
1588 clog << _("No servers specified") << endl;
1589 if (! no_default)
1590 get_default_server_info (s, specified_servers);
1591 continue;
1592 }
1593
1594 // Determine what has been specified. Servers may be
1595 // specified by:
1596 // - domain{:port}
1597 // - certificate-serial-number{:port}
1598 // - IPv4-address{:port}
1599 // - IPv6-address{:port}
1600 // where items within {} are optional.
1601 //
1602 // Check for IPv6 addresses first. It reduces the amount
1603 // of checking necessary for certificate serial numbers.
1604 compile_server_info server_info;
1605 vector<compile_server_info> resolved_servers;
1606 if (isIPv6 (server, server_info)
1607 || isIPv4 (server, server_info)
1608 || isCertSerialNumber (server, server_info))
1609 {
1610 // An address or cert serial number has been specified.
1611 // No resolution is needed.
1612 resolved_servers.push_back (server_info);
1613 }
1614 else if (isDomain (server, server_info))
1615 {
1616 // Try to resolve the given name.
1617 resolve_host (s, server_info, resolved_servers);
1618 }
1619 else
1620 {
1621 clog << _F("Invalid server specification: %s", server.c_str())
1622 << endl;
1623 continue;
1624 }
1625
1626 // Now examine the server(s) identified and add them to
1627 // the list of specified servers.
1628 vector<compile_server_info> known_servers;
1629 vector<compile_server_info> new_servers;
1630 for (auto i = resolved_servers.begin();
1631 i != resolved_servers.end();
1632 ++i)
1633 {
1634 // If this item was fully specified, then just add it.
1635 if (i->fully_specified || ! s.http_servers.empty ()) {
1636 // In this instance, "fully specified" means
1637 // address and port. At this point we haven't
1638 // tried to contact the server to get online
1639 // information, certificate information,
1640 // etc. Certain server types need us to connect to
1641 // the server directly to get this information.
1642 // Assume http servers are fully specified
1643 if (backend)
1644 backend->fill_in_server_info (*i);
1645 nss_add_server_info (*i, new_servers);
1646 }
1647 else {
1648 // It was not fully specified, so we need additional info. Try
1649 // to get it by matching what we have against other known servers.
1650 if (known_servers.empty ())
1651 nss_get_all_server_info (s, known_servers);
1652
1653 // See if this server spec matches that of a known server
1654 vector<compile_server_info> matched_servers = known_servers;
1655 nss_keep_common_server_info (*i, matched_servers);
1656
1657 // If this server spec matches one or more known servers, then add the
1658 // augmented info to the specified_servers. Otherwise, if this server
1659 // spec is complete, then add it directly. Otherwise this server spec
1660 // is incomplete.
1661 if (! matched_servers.empty())
1662 nss_add_server_info (matched_servers, new_servers);
1663 else if (i->isComplete ())
1664 nss_add_server_info (*i, new_servers);
1665 else if (s.verbose >= 3)
1666 clog << _("Incomplete server spec: ") << *i << endl;
1667 }
1668 }
1669
1670 if (s.verbose >= 3)
1671 {
1672 clog << _F("Servers matching %s: ", server.c_str()) << endl;
1673 clog << new_servers;
1674 }
1675
1676 // Add the newly identified servers to the list.
1677 if (! new_servers.empty())
1678 nss_add_server_info (new_servers, specified_servers);
1679 } // Loop over --use-server options
1680 } // -- use-server specified
1681
1682 if (s.verbose >= 2)
1683 {
1684 clog << _("All specified servers:") << endl;
1685 clog << specified_servers;
1686 }
1687 } // Server information is not cached
1688
1689 // Add the information, but not duplicates.
1690 nss_add_server_info (specified_servers, servers);
1691}
1692
1693void
1694nss_get_or_keep_trusted_server_info (
1695 systemtap_session &s,
1696 vector<compile_server_info> &servers,
1697 bool keep
1698)
1699{
1700 // If we're filtering the list and it's already empty, then
1701 // there's nothing to do.
1702 if (keep && servers.empty ())
1703 return;
1704
1705 // We only need to obtain this once per session. This is a good thing(tm)
1706 // since obtaining this information is expensive.
1707 vector<compile_server_info>& trusted_servers = cscache(s)->trusted_servers;
1708 if (trusted_servers.empty ())
1709 {
1710 // Maintain an empty entry to indicate that this search has been
1711 // performed, in case the search comes up empty.
1712 trusted_servers.push_back (compile_server_info ());
1713
1714 // Check the private database first.
1715 string cert_db_path = private_ssl_cert_db_path ();
1716 get_server_info_from_db (s, trusted_servers, cert_db_path);
1717
1718 // Now check the global database.
1719 cert_db_path = global_ssl_cert_db_path ();
1720 get_server_info_from_db (s, trusted_servers, cert_db_path);
1721
1722 if (s.verbose >= 5)
1723 {
1724 clog << _("All servers trusted as ssl peers:") << endl;
1725 clog << trusted_servers;
1726 }
1727 } // Server information is not cached
1728
1729 if (keep)
1730 {
1731 // Filter the existing vector by keeping the information in common with
1732 // the trusted_server vector.
1733 nss_keep_common_server_info (trusted_servers, servers);
1734 }
1735 else
1736 {
1737 // Add the information, but not duplicates.
1738 nss_add_server_info (trusted_servers, servers);
1739 }
1740}
1741
1742void
1743nss_get_or_keep_signing_server_info (
1744 systemtap_session &s,
1745 vector<compile_server_info> &servers,
1746 bool keep
1747)
1748{
1749 // If we're filtering the list and it's already empty, then
1750 // there's nothing to do.
1751 if (keep && servers.empty ())
1752 return;
1753
1754 // We only need to obtain this once per session. This is a good thing(tm)
1755 // since obtaining this information is expensive.
1756 vector<compile_server_info>& signing_servers = cscache(s)->signing_servers;
1757 if (signing_servers.empty ())
1758 {
1759 // Maintain an empty entry to indicate that this search has been
1760 // performed, in case the search comes up empty.
1761 signing_servers.push_back (compile_server_info ());
1762
1763 // For all users, check the global database.
1764 string cert_db_path = signing_cert_db_path ();
1765 get_server_info_from_db (s, signing_servers, cert_db_path);
1766
1767 if (s.verbose >= 5)
1768 {
1769 clog << _("All servers trusted as module signers:") << endl;
1770 clog << signing_servers;
1771 }
1772 } // Server information is not cached
1773
1774 if (keep)
1775 {
1776 // Filter the existing vector by keeping the information in common with
1777 // the signing_server vector.
1778 nss_keep_common_server_info (signing_servers, servers);
1779 }
1780 else
1781 {
1782 // Add the information, but not duplicates.
1783 nss_add_server_info (signing_servers, servers);
1784 }
1785}
1786
1787void
1788nss_get_or_keep_compatible_server_info (
1789 systemtap_session &s,
1790 vector<compile_server_info> &servers,
1791 bool keep
1792)
1793{
1794#if HAVE_AVAHI
1795 // If we're filtering the list and it's already empty, then
1796 // there's nothing to do.
1797 if (keep && servers.empty ())
1798 return;
1799
1800 // Remove entries for servers incompatible with the host environment
1801 // from the given list of servers.
1802 // A compatible server compiles for the kernel release and architecture
1803 // of the host environment.
1804 //
1805 // Compatibility can only be determined for online servers. So, augment
1806 // and filter the information we have with information for online servers.
1807 vector<compile_server_info> online_servers;
1808 nss_get_or_keep_online_server_info (s, online_servers, false/*keep*/);
1809 if (keep)
1810 nss_keep_common_server_info (online_servers, servers);
1811 else
1812 nss_add_server_info (online_servers, servers);
1813
1814 // Now look to see which ones are compatible.
1815 // The vector can change size as we go, so be careful!!
1816 for (unsigned i = 0; i < servers.size (); /**/)
1817 {
1818 // Retain empty entries.
1819 assert (! servers[i].empty ());
1820
1821 // Check the target of the server.
1822 if (servers[i].sysinfo != s.kernel_release + " " + s.architecture
1823 && servers[i].sysinfo != s.architecture)
1824 {
1825 // Target platform mismatch.
1826 if (s.verbose > 3)
1827 clog << "Rejected server (platform): " << servers[i] << endl;
1828 servers.erase (servers.begin () + i);
1829 continue;
1830 }
1831
1832 // If the client requires secure boot signing, make sure the
1833 // server has the right MOK.
1834 if (! s.mok_fingerprints.empty ())
1835 {
1836 // Make sure the server has at least one MOK in common with
1837 // the client.
1838 vector<string>::const_iterator it;
1839 bool mok_found = false;
1840 for (it = s.mok_fingerprints.begin(); it != s.mok_fingerprints.end(); it++)
1841 {
1842 if ((*it == "missing") || // PR26665: see also session.cxx check_options()
1843 find(servers[i].mok_fingerprints.begin(),
1844 servers[i].mok_fingerprints.end(), *it)
1845 != servers[i].mok_fingerprints.end ())
1846 {
1847 mok_found = true;
1848 break;
1849 }
1850 }
1851
1852 // This server has no MOK in common with the client.
1853 if (! mok_found)
1854 {
1855 if (s.verbose > 3)
1856 clog << "Rejected server (common mok): " << servers[i] << endl;
1857 servers.erase (servers.begin () + i);
1858 continue;
1859 }
1860 }
1861
1862 // The server is compatible. Leave it in the list.
1863 if (s.verbose > 3)
1864 clog << "Accepted server: " << servers[i] << endl;
1865 ++i;
1866 }
1867#else // ! HAVE_AVAHI
1868 // Without Avahi, we can't obtain the target platform of the server.
1869 // Issue a warning.
1870 if (s.verbose >= 2)
1871 clog << _("Unable to detect server compatibility without avahi") << endl;
1872 if (keep)
1873 servers.clear ();
1874#endif
1875}
1876
1877void
1878nss_keep_server_info_with_cert_and_port (
1879 systemtap_session &,
1880 const compile_server_info &server,
1881 vector<compile_server_info> &servers
1882)
1883{
1884 assert (! server.certinfo.empty ());
1885
1886 // Search the list of servers for ones matching the
1887 // serial number specified.
1888 // The vector can change size as we go, so be careful!!
1889 for (unsigned i = 0; i < servers.size (); /**/)
1890 {
1891 // Retain empty entries.
1892 if (servers[i].empty ())
1893 {
1894 ++i;
1895 continue;
1896 }
1897 if (servers[i].certinfo == server.certinfo &&
1898 (servers[i].port == 0 || server.port == 0 ||
1899 servers[i].port == server.port))
1900 {
1901 // If the server is not online, then use the specified
1902 // port, if any.
1903 if (servers[i].port == 0)
1904 {
1905 servers[i].port = server.port;
1906 servers[i].fully_specified = server.fully_specified;
1907 }
1908 ++i;
1909 continue;
1910 }
1911 // The item does not match. Delete it.
1912 servers.erase (servers.begin () + i);
1913 }
1914}
1915
1916// Obtain missing host name or ip address, if any. Return 0 on success.
1917static void
1918resolve_host (
1919 systemtap_session& s,
1920 compile_server_info &server,
1921 vector<compile_server_info> &resolved_servers
1922)
1923{
1924 vector<resolved_host>& cached_hosts = cscache(s)->resolved_hosts[server.host_name];
1925 if (cached_hosts.empty ())
1926 {
1927 // The server's host_name member is a string containing either a host name or an ip address.
1928 // Either is acceptable for lookup.
1929 const char *lookup_name = server.host_name.c_str();
1930 if (s.verbose >= 6)
1931 clog << _F("Looking up %s", lookup_name) << endl;
1932
1933 struct addrinfo hints;
1934 memset(& hints, 0, sizeof (hints));
1935 hints.ai_family = AF_UNSPEC; // AF_INET or AF_INET6 to force version
1936 struct addrinfo *addr_info = 0;
1937 int rc = getaddrinfo (lookup_name, NULL, & hints, & addr_info);
1938
1939 // Failure to resolve will result in an appropriate message later, if other methods fail.
1940 if (rc != 0)
1941 {
1942 if (s.verbose >= 6)
1943 clog << _F("%s not found: %s", lookup_name, gai_strerror (rc)) << endl;
1944 }
1945 else
1946 {
1947 // Loop over the results collecting information.
1948 assert (addr_info);
1949 for (const struct addrinfo *ai = addr_info; ai != NULL; ai = ai->ai_next)
1950 {
1951 PRNetAddr new_address;
1952
1953 // We support IPv4 and IPv6, Ignore other protocols,
1954 if (ai->ai_family == AF_INET)
1955 {
1956 // IPv4 Address
1957 struct sockaddr_in *ip = (struct sockaddr_in *)ai->ai_addr;
1958 new_address.inet.family = PR_AF_INET;
1959 new_address.inet.ip = ip->sin_addr.s_addr;
1960 }
1961 else if (ai->ai_family == AF_INET6)
1962 {
1963 // IPv6 Address
1964 struct sockaddr_in6 *ip = (struct sockaddr_in6 *)ai->ai_addr;
1965 new_address.ipv6.family = PR_AF_INET6;
1966 new_address.ipv6.scope_id = ip->sin6_scope_id;
1967 copyAddress (new_address.ipv6.ip, ip->sin6_addr);
1968 }
1969 else
1970 continue;
1971
1972 // Try to obtain a host name. Otherwise, leave it empty.
1973 char hbuf[NI_MAXHOST];
1974 int status = getnameinfo (ai->ai_addr, ai->ai_addrlen, hbuf, sizeof (hbuf), NULL, 0,
1975 NI_NAMEREQD | NI_IDN);
1976 if (status != 0)
1977 hbuf[0] = '\0';
1978
1979 cached_hosts.push_back(resolved_host(hbuf, new_address));
1980 }
1981 }
1982 if (addr_info)
1983 freeaddrinfo (addr_info); // free the linked list
1984 }
1985
1986 // If no addresses were resolved, then return the info we were given.
1987 if (cached_hosts.empty())
1988 nss_add_server_info (server, resolved_servers);
1989 else {
1990 // We will add a new server for each address resolved
1991 vector<compile_server_info> new_servers;
1992 for (vector<resolved_host>::const_iterator it = cached_hosts.begin();
1993 it != cached_hosts.end(); ++it)
1994 {
1995 // Start with the info we were given
1996 compile_server_info new_server = server;
1997
1998 // NB: do not overwrite port info
1999 if (it->address.raw.family == AF_INET)
2000 {
2001 new_server.address.inet.family = PR_AF_INET;
2002 new_server.address.inet.ip = it->address.inet.ip;
2003 }
2004 else // AF_INET6
2005 {
2006 new_server.address.ipv6.family = PR_AF_INET6;
2007 new_server.address.ipv6.scope_id = it->address.ipv6.scope_id;
2008 new_server.address.ipv6.ip = it->address.ipv6.ip;
2009 }
2010 if (!it->host_name.empty())
2011 {
2012 new_server.unresolved_host_name = new_server.host_name;
2013 new_server.host_name = it->host_name;
2014 }
2015 nss_add_server_info (new_server, new_servers);
2016 }
2017
2018 if (s.verbose >= 6)
2019 {
2020 clog << _F("%s resolves to:", server.host_name.c_str()) << endl;
2021 clog << new_servers;
2022 }
2023
2024 nss_add_server_info (new_servers, resolved_servers);
2025 }
2026}
2027
2028#if HAVE_AVAHI
2029// Avahi API Callbacks.
2030//-----------------------------------------------------------------------
2031struct browsing_context {
2032 AvahiSimplePoll *simple_poll;
2033 AvahiClient *client;
2034 vector<compile_server_info> *servers;
2035};
2036
2037// Get the value of the requested key from the Avahi string list.
2038static string
2039get_value_from_avahi_string_list (AvahiStringList *strlst, const string &key)
2040{
2041 AvahiStringList *p = avahi_string_list_find (strlst, key.c_str ());
2042 if (p == NULL)
2043 {
2044 // Key not found.
2045 return "";
2046 }
2047
2048 char *k, *v;
2049 int rc = avahi_string_list_get_pair(p, &k, &v, NULL);
2050 if (rc < 0 || v == NULL)
2051 {
2052 avahi_free (k);
2053 return "";
2054 }
2055
2056 string value = v;
2057 avahi_free (k);
2058 avahi_free (v);
2059 return value;
2060}
2061
2062// Get a vector of values of the requested key from the Avahi string
2063// list. This is for multiple values having the same key.
2064static void
2065get_values_from_avahi_string_list (AvahiStringList *strlst, const string &key,
2066 vector<string> &value_vector)
2067{
2068 AvahiStringList *p;
2069
2070 value_vector.clear();
2071 p = avahi_string_list_find (strlst, key.c_str ());
2072 for (; p != NULL; p = avahi_string_list_get_next(p))
2073 {
2074 char *k, *v;
2075 int rc = avahi_string_list_get_pair(p, &k, &v, NULL);
2076 if (rc < 0 || v == NULL)
2077 {
2078 avahi_free (k);
2079 break;
2080 }
2081
2082 value_vector.push_back(v);
2083 avahi_free (k);
2084 avahi_free (v);
2085 }
2086 return;
2087}
2088
2089extern "C"
2090void resolve_callback(
2091 AvahiServiceResolver *r,
2092 AvahiIfIndex interface,
2093 AvahiProtocol protocol,
2094 AvahiResolverEvent event,
2095 const char *name,
2096 const char *type,
2097 const char *domain,
2098 const char *host_name,
2099 const AvahiAddress *address,
2100 uint16_t port,
2101 AvahiStringList *txt,
2102 AvahiLookupResultFlags /*flags*/,
2103 AVAHI_GCC_UNUSED void* userdata)
2104 {
2105 PRStatus prStatus;
2106
2107 assert(r);
2108 const browsing_context *context = (browsing_context *)userdata;
2109 vector<compile_server_info> *servers = context->servers;
2110
2111 // Called whenever a service has been resolved successfully or timed out.
2112
2113 switch (event) {
2114 case AVAHI_RESOLVER_FAILURE:
2115 clog << _F("Failed to resolve service '%s' of type '%s' in domain '%s': %s",
2116 name, type, domain,
2117 avahi_strerror(avahi_client_errno(avahi_service_resolver_get_client(r)))) << endl;
2118 break;
2119
2120 case AVAHI_RESOLVER_FOUND: {
2121 compile_server_info info;
2122
2123 // Decode the address.
2124 char a[AVAHI_ADDRESS_STR_MAX];
2125 avahi_address_snprint(a, sizeof(a), address);
2126 prStatus = PR_StringToNetAddr (a, & info.address);
2127 if (prStatus != PR_SUCCESS) {
2128 clog << _F("Invalid address '%s' from avahi", a) << endl;
2129 break;
2130 }
2131
2132 // We support both IPv4 and IPv6. Ignore other protocols.
2133 if (protocol == AVAHI_PROTO_INET6) {
2134 info.address.ipv6.family = PR_AF_INET6;
2135 info.address.ipv6.scope_id = interface;
2136 info.port = port;
2137 }
2138 else if (protocol == AVAHI_PROTO_INET) {
2139 info.address.inet.family = PR_AF_INET;
2140 info.port = port;
2141 }
2142 else
2143 break;
2144
2145 // Save the host name.
2146 info.host_name = host_name;
2147
2148 // Save the text tags.
2149 info.sysinfo = get_value_from_avahi_string_list (txt, "sysinfo");
2150 info.certinfo = get_value_from_avahi_string_list (txt, "certinfo");
2151 info.version = get_value_from_avahi_string_list (txt, "version");
2152 if (info.version.empty ())
2153 info.version = "1.0"; // default version is 1.0
2154
2155 // The server might provide one or more MOK certificate's
2156 // info.
2157 get_values_from_avahi_string_list(txt, "mok_info",
2158 info.mok_fingerprints);
2159
2160 // Add this server to the list of discovered servers.
2161 nss_add_server_info (info, *servers);
2162 break;
2163 }
2164 default:
2165 break;
2166 }
2167
2168 avahi_service_resolver_free(r);
2169}
2170
2171extern "C"
2172void browse_callback(
2173 AvahiServiceBrowser *b,
2174 AvahiIfIndex interface,
2175 AvahiProtocol protocol,
2176 AvahiBrowserEvent event,
2177 const char *name,
2178 const char *type,
2179 const char *domain,
2180 AVAHI_GCC_UNUSED AvahiLookupResultFlags flags,
2181 void* userdata) {
2182
2183 browsing_context *context = (browsing_context *)userdata;
2184 AvahiClient *c = context->client;
2185 AvahiSimplePoll *simple_poll = context->simple_poll;
2186 assert(b);
2187
2188 // Called whenever a new services becomes available on the LAN or is removed from the LAN.
2189
2190 switch (event) {
2191 case AVAHI_BROWSER_FAILURE:
2192 clog << _F("Avahi browse failed: %s",
2193 avahi_strerror(avahi_client_errno(avahi_service_browser_get_client(b))))
2194 << endl;
2195 avahi_simple_poll_quit(simple_poll);
2196 break;
2197
2198 case AVAHI_BROWSER_NEW:
2199 // We ignore the returned resolver object. In the callback
2200 // function we free it. If the server is terminated before
2201 // the callback function is called the server will free
2202 // the resolver for us.
2203 if (!(avahi_service_resolver_new(c, interface, protocol, name, type, domain,
2204 AVAHI_PROTO_UNSPEC, (AvahiLookupFlags)0, resolve_callback, context))) {
2205 clog << _F("Failed to resolve service '%s': %s",
2206 name, avahi_strerror(avahi_client_errno(c))) << endl;
2207 }
2208 break;
2209
2210 case AVAHI_BROWSER_REMOVE:
2211 case AVAHI_BROWSER_ALL_FOR_NOW:
2212 case AVAHI_BROWSER_CACHE_EXHAUSTED:
2213 break;
2214 }
2215}
2216
2217extern "C"
2218void client_callback(AvahiClient *c, AvahiClientState state, AVAHI_GCC_UNUSED void * userdata) {
2219 assert(c);
2220 browsing_context *context = (browsing_context *)userdata;
2221 AvahiSimplePoll *simple_poll = context->simple_poll;
2222
2223 // Called whenever the client or server state changes.
2224
2225 if (state == AVAHI_CLIENT_FAILURE) {
2226 clog << _F("Avahi Server connection failure: %s", avahi_strerror(avahi_client_errno(c))) << endl;
2227 avahi_simple_poll_quit(simple_poll);
2228 }
2229}
2230
2231extern "C"
2232void timeout_callback(AVAHI_GCC_UNUSED AvahiTimeout *e, AVAHI_GCC_UNUSED void *userdata) {
2233 browsing_context *context = (browsing_context *)userdata;
2234 AvahiSimplePoll *simple_poll = context->simple_poll;
2235 avahi_simple_poll_quit(simple_poll);
2236}
2237#endif // HAVE_AVAHI
2238
2239void
2240nss_get_or_keep_online_server_info (
2241 systemtap_session &s,
2242 vector<compile_server_info> &servers,
2243 bool keep
2244)
2245{
2246 // If we're filtering the list and it's already empty, then
2247 // there's nothing to do.
2248 if (keep && servers.empty ())
2249 return;
2250
2251 // We only need to obtain this once per session. This is a good thing(tm)
2252 // since obtaining this information is expensive.
2253 vector<compile_server_info>& online_servers = cscache(s)->online_servers;
2254 if (online_servers.empty () && s.http_servers.empty ())
2255 {
2256 // Maintain an empty entry to indicate that this search has been
2257 // performed, in case the search comes up empty.
2258 online_servers.push_back (compile_server_info ());
2259#if HAVE_AVAHI
2260 // Must predeclare these due to jumping on error to fail:
2261 vector<compile_server_info> avahi_servers;
2262
2263 // Initialize.
2264 AvahiClient *client = NULL;
2265 AvahiServiceBrowser *sb = NULL;
2266
2267 // Allocate main loop object.
2268 AvahiSimplePoll *simple_poll;
2269 if (!(simple_poll = avahi_simple_poll_new()))
2270 {
2271 clog << _("Failed to create Avahi simple poll object") << endl;
2272 goto fail;
2273 }
2274 browsing_context context;
2275 context.simple_poll = simple_poll;
2276 context.servers = & avahi_servers;
2277
2278 // Allocate a new Avahi client
2279 int error;
2280 client = avahi_client_new (avahi_simple_poll_get (simple_poll),
2281 (AvahiClientFlags)0,
2282 client_callback, & context, & error);
2283
2284 // Check whether creating the client object succeeded.
2285 if (! client)
2286 {
2287 clog << _F("Failed to create Avahi client: %s",
2288 avahi_strerror(error)) << endl;
2289 goto fail;
2290 }
2291 context.client = client;
2292
2293 // Create the service browser.
2294 if (!(sb = avahi_service_browser_new (client, AVAHI_IF_UNSPEC,
2295 AVAHI_PROTO_UNSPEC, "_stap._tcp",
2296 NULL, (AvahiLookupFlags)0,
2297 browse_callback, & context)))
2298 {
2299 clog << _F("Failed to create Avahi service browser: %s",
2300 avahi_strerror(avahi_client_errno(client))) << endl;
2301 goto fail;
2302 }
2303
2304 // Timeout after 0.5 seconds.
2305 struct timeval tv;
2306 avahi_simple_poll_get(simple_poll)->timeout_new(
2307 avahi_simple_poll_get(simple_poll),
2308 avahi_elapse_time(&tv, 1000/2, 0),
2309 timeout_callback,
2310 & context);
2311
2312 // Run the main loop.
2313 avahi_simple_poll_loop(simple_poll);
2314
2315 if (s.verbose >= 6)
2316 {
2317 clog << _("Avahi reports the following servers online:") << endl;
2318 clog << avahi_servers;
2319 }
2320
2321 // Merge with the list of servers, as obtained by avahi.
2322 nss_add_server_info (avahi_servers, online_servers);
2323
2324 fail:
2325 // Cleanup.
2326 if (client) {
2327 // Also frees the service browser
2328 avahi_client_free(client);
2329 }
2330 if (simple_poll)
2331 avahi_simple_poll_free(simple_poll);
2332#else // ! HAVE_AVAHI
2333 // Without Avahi, we can't detect online servers. Issue a warning.
2334 if (s.verbose >= 2)
2335 clog << _("Unable to detect online servers without avahi") << endl;
2336#endif // ! HAVE_AVAHI
2337
2338 if (s.verbose >= 5)
2339 {
2340 clog << _("All online servers:") << endl;
2341 clog << online_servers;
2342 }
2343 } // Server information is not cached.
2344
2345 if (!s.http_servers.empty ())
2346 {
2347 // http server does not depend on avahi, so discover which servers are online by
2348 // getting a list of potential servers and trying to connect to them
2349 vector<compile_server_info>& specified_servers = cscache(s)->specified_servers;
2350
2351 nss_get_specified_server_info (s, specified_servers);
2352
2353 for (vector<compile_server_info>::const_iterator i = specified_servers.begin ();
2354 i != specified_servers.end ();
2355 ++i)
2356 {
2357 client_backend *backend = nss_get_client_backend (s);
2358 if (backend)
2359 backend->fill_in_server_info ((compile_server_info&)*i);
2360 }
2361 }
2362
2363 if (keep)
2364 {
2365 // Filter the existing vector by keeping the information in common with
2366 // the online_server vector.
2367 nss_keep_common_server_info (online_servers, servers);
2368 }
2369 else
2370 {
2371 // Add the information, but not duplicates.
2372 nss_add_server_info (online_servers, servers);
2373 }
2374}
2375
2376// Add server info to a list, avoiding duplicates. Merge information from
2377// two duplicate items.
2378void
2379nss_add_server_info (
2380 const compile_server_info &info, vector<compile_server_info>& target
2381)
2382{
2383 if (info.empty ())
2384 return;
2385
2386 bool found = false;
2387 for (vector<compile_server_info>::iterator i = target.begin ();
2388 i != target.end ();
2389 ++i)
2390 {
2391 if (info == *i)
2392 {
2393 // Duplicate. Merge the two items.
2394 merge_server_info (info, *i);
2395 found = true;
2396 }
2397 }
2398 if (! found)
2399 target.push_back (info);
2400}
2401
2402// Add server info from one vector to another.
2403void
2404nss_add_server_info (
2405 const vector<compile_server_info> &source,
2406 vector<compile_server_info> &target
2407)
2408{
2409 for (vector<compile_server_info>::const_iterator i = source.begin ();
2410 i != source.end ();
2411 ++i)
2412 {
2413 nss_add_server_info (*i, target);
2414 }
2415}
2416
2417// Filter the vector by keeping information in common with the item.
2418void
2419nss_keep_common_server_info (
2420 const compile_server_info &info_to_keep,
2421 vector<compile_server_info> &filtered_info
2422)
2423{
2424 assert (! info_to_keep.empty ());
2425
2426 // The vector may change size as we go. Be careful!!
2427 for (unsigned i = 0; i < filtered_info.size (); /**/)
2428 {
2429 // Retain empty entries.
2430 if (filtered_info[i].empty ())
2431 {
2432 ++i;
2433 continue;
2434 }
2435 if (info_to_keep == filtered_info[i])
2436 {
2437 merge_server_info (info_to_keep, filtered_info[i]);
2438 ++i;
2439 continue;
2440 }
2441 // The item does not match. Delete it.
2442 filtered_info.erase (filtered_info.begin () + i);
2443 continue;
2444 }
2445}
2446
2447// Filter the second vector by keeping information in common with the first
2448// vector.
2449void
2450nss_keep_common_server_info (
2451 const vector<compile_server_info> &info_to_keep,
2452 vector<compile_server_info> &filtered_info
2453)
2454{
2455 // The vector may change size as we go. Be careful!!
2456 for (unsigned i = 0; i < filtered_info.size (); /**/)
2457 {
2458 // Retain empty entries.
2459 if (filtered_info[i].empty ())
2460 {
2461 ++i;
2462 continue;
2463 }
2464 bool found = false;
2465 for (unsigned j = 0; j < info_to_keep.size (); ++j)
2466 {
2467 if (filtered_info[i] == info_to_keep[j])
2468 {
2469 merge_server_info (info_to_keep[j], filtered_info[i]);
2470 found = true;
2471 }
2472 }
2473
2474 // If the item was not found. Delete it. Otherwise, advance to the next
2475 // item.
2476 if (found)
2477 ++i;
2478 else
2479 filtered_info.erase (filtered_info.begin () + i);
2480 }
2481}
2482
2483// Merge two compile server info items.
2484static void
2485merge_server_info (
2486 const compile_server_info &source,
2487 compile_server_info &target
2488)
2489{
2490 // Copy the host name if the source has one.
2491 if (! source.host_name.empty())
2492 target.host_name = source.host_name;
2493 // Copy the address unconditionally, if the source has an address, even if they are already
2494 // equal. The source address may be an IPv6 address with a scope_id that the target is missing.
2495 assert (! target.hasAddress () || ! source.hasAddress () || source.address == target.address);
2496 if (source.hasAddress ())
2497 copyNetAddr (target.address, source.address);
2498 if (target.port == 0)
2499 {
2500 target.port = source.port;
2501 target.fully_specified = source.fully_specified;
2502 }
2503 if (target.sysinfo.empty ())
2504 target.sysinfo = source.sysinfo;
2505 if (target.version.empty ())
2506 target.version = source.version;
2507 if (target.certinfo.empty ())
2508 target.certinfo = source.certinfo;
2509}
2510
2511void
2512nss_add_online_server_info (systemtap_session &s,
2513 const compile_server_info &info)
2514{
2515 nss_add_server_info (info, cscache(s)->online_servers);
2516}
2517#endif // HAVE_NSS
2518
2519/* vim: set sw=2 ts=8 cino=>4,n-2,{2,^-2,t0,(0,u0,w1,M1 : */
This page took 0.040302 seconds and 6 git commands to generate.