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