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