]> sourceware.org Git - systemtap.git/blob - csclient.cxx
Ensure use of the function argument values from function entry in lwtools
[systemtap.git] / csclient.cxx
1 /*
2 Compile server client functions
3 Copyright (C) 2010-2014 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 "util.h"
18 #include "stap-probe.h"
19
20 #include <sys/times.h>
21 #include <vector>
22 #include <fstream>
23 #include <sstream>
24 #include <cassert>
25 #include <cstdlib>
26 #include <cstdio>
27 #include <algorithm>
28
29 extern "C" {
30 #include <unistd.h>
31 #include <linux/limits.h>
32 #include <sys/time.h>
33 #include <glob.h>
34 #include <limits.h>
35 #include <sys/socket.h>
36 #include <sys/stat.h>
37 #include <net/if.h>
38 #include <netdb.h>
39 #include <arpa/inet.h>
40 #include <pwd.h>
41 }
42
43 #if HAVE_AVAHI
44 extern "C" {
45 #include <avahi-client/client.h>
46 #include <avahi-client/lookup.h>
47
48 #include <avahi-common/simple-watch.h>
49 #include <avahi-common/malloc.h>
50 #include <avahi-common/error.h>
51 #include <avahi-common/timeval.h>
52 }
53 #endif // HAVE_AVAHI
54
55 extern "C" {
56 #include <ssl.h>
57 #include <nspr.h>
58 #include <nss.h>
59 #include <certdb.h>
60 #include <pk11pub.h>
61 #include <prerror.h>
62 #include <secerr.h>
63 #include <sslerr.h>
64 }
65
66 #include "nsscommon.h"
67
68 using namespace std;
69
70 #define STAP_CSC_01 _("WARNING: The domain name, %s, does not match the DNS name(s) on the server certificate:\n")
71 #define STAP_CSC_02 _("could not find input file %s\n")
72 #define STAP_CSC_03 _("could not open input file %s\n")
73 #define STAP_CSC_04 _("Unable to open output file %s\n")
74 #define STAP_CSC_05 _("could not write to %s\n")
75
76 #define MOK_PUBLIC_CERT_NAME "signing_key.x509"
77
78 static PRIPv6Addr &copyAddress (PRIPv6Addr &PRin6, const in6_addr &in6);
79 static PRNetAddr &copyNetAddr (PRNetAddr &x, const PRNetAddr &y);
80 bool operator!= (const PRNetAddr &x, const PRNetAddr &y);
81 bool operator== (const PRNetAddr &x, const PRNetAddr &y);
82
83 extern "C"
84 void
85 nsscommon_error (const char *msg, int logit __attribute ((unused)))
86 {
87 clog << msg << endl << flush;
88 }
89
90 // Information about compile servers.
91 struct compile_server_info
92 {
93 compile_server_info () : port(0), fully_specified(false)
94 {
95 memset (& address, 0, sizeof (address));
96 }
97
98 string host_name;
99 PRNetAddr address;
100 unsigned short port;
101 bool fully_specified;
102 string version;
103 string sysinfo;
104 string certinfo;
105 vector<string> mok_fingerprints;
106
107 bool empty () const
108 {
109 return this->host_name.empty () && ! this->hasAddress () && certinfo.empty ();
110 }
111 bool hasAddress () const
112 {
113 return this->address.raw.family != 0;
114 }
115 unsigned short setAddressPort (unsigned short port)
116 {
117 if (this->address.raw.family == PR_AF_INET)
118 return this->address.inet.port = htons (port);
119 if (this->address.raw.family == PR_AF_INET6)
120 return this->address.ipv6.port = htons (port);
121 assert (0);
122 return 0;
123 }
124 bool isComplete () const
125 {
126 return this->hasAddress () && port != 0;
127 }
128
129 bool operator== (const compile_server_info &that) const
130 {
131 // If one item or the other has only a name, and possibly a port specified,
132 // then allow a match by name and port only. This is so that the user can specify
133 // names which are returned by avahi, but are not dns resolvable.
134 // Otherwise, we will ignore the host_name.
135 if ((! this->hasAddress() && this->version.empty () &&
136 this->sysinfo.empty () && this->certinfo.empty ()) ||
137 (! that.hasAddress() && that.version.empty () &&
138 that.sysinfo.empty () && that.certinfo.empty ()))
139 {
140 if (this->host_name != that.host_name)
141 return false;
142 }
143
144 // Compare the other fields only if they have both been set.
145 if (this->hasAddress() && that.hasAddress() &&
146 this->address != that.address)
147 return false;
148 if (this->port != 0 && that.port != 0 &&
149 this->port != that.port)
150 return false;
151 if (! this->version.empty () && ! that.version.empty () &&
152 this->version != that.version)
153 return false;
154 if (! this->sysinfo.empty () && ! that.sysinfo.empty () &&
155 this->sysinfo != that.sysinfo)
156 return false;
157 if (! this->certinfo.empty () && ! that.certinfo.empty () &&
158 this->certinfo != that.certinfo)
159 return false;
160 if (! this->mok_fingerprints.empty () && ! that.mok_fingerprints.empty ()
161 && this->mok_fingerprints != that.mok_fingerprints)
162 return false;
163
164 return true; // They are equal
165 }
166
167 // Used to sort servers by preference for order of contact. The preferred server is
168 // "less" than the other one.
169 bool operator< (const compile_server_info &that) const
170 {
171 // Prefer servers with a later (higher) version number.
172 cs_protocol_version this_version (this->version.c_str ());
173 cs_protocol_version that_version (that.version.c_str ());
174 return that_version < this_version;
175 }
176 };
177
178 ostream &operator<< (ostream &s, const compile_server_info &i);
179 ostream &operator<< (ostream &s, const vector<compile_server_info> &v);
180
181 static void
182 preferred_order (vector<compile_server_info> &servers)
183 {
184 // Sort the given list of servers into the preferred order for contacting.
185 // Don't bother if there are less than 2 servers in the list.
186 if (servers.size () < 2)
187 return;
188
189 // Sort the list using compile_server_info::operator<
190 sort (servers.begin (), servers.end ());
191 }
192
193 struct resolved_host // see also PR16326, PR16342
194 {
195 string host_name;
196 PRNetAddr address;
197 resolved_host(string chost_name, PRNetAddr caddress):
198 host_name(chost_name), address(caddress) {}
199 };
200
201 struct compile_server_cache
202 {
203 vector<compile_server_info> default_servers;
204 vector<compile_server_info> specified_servers;
205 vector<compile_server_info> trusted_servers;
206 vector<compile_server_info> signing_servers;
207 vector<compile_server_info> online_servers;
208 vector<compile_server_info> all_servers;
209 map<string,vector<resolved_host> > resolved_hosts;
210 };
211
212 // For filtering queries.
213 enum compile_server_properties {
214 compile_server_all = 0x1,
215 compile_server_trusted = 0x2,
216 compile_server_online = 0x4,
217 compile_server_compatible = 0x8,
218 compile_server_signer = 0x10,
219 compile_server_specified = 0x20
220 };
221
222 // Static functions.
223 static compile_server_cache* cscache(systemtap_session& s);
224 static void query_server_status (systemtap_session &s, const string &status_string);
225
226 static void get_server_info (systemtap_session &s, int pmask, vector<compile_server_info> &servers);
227 static void get_all_server_info (systemtap_session &s, vector<compile_server_info> &servers);
228 static void get_default_server_info (systemtap_session &s, vector<compile_server_info> &servers);
229 static void get_specified_server_info (systemtap_session &s, vector<compile_server_info> &servers, bool no_default = false);
230 static void get_or_keep_online_server_info (systemtap_session &s, vector<compile_server_info> &servers, bool keep);
231 static void get_or_keep_trusted_server_info (systemtap_session &s, vector<compile_server_info> &servers, bool keep);
232 static void get_or_keep_signing_server_info (systemtap_session &s, vector<compile_server_info> &servers, bool keep);
233 static void get_or_keep_compatible_server_info (systemtap_session &s, vector<compile_server_info> &servers, bool keep);
234 static void keep_common_server_info (const compile_server_info &info_to_keep, vector<compile_server_info> &filtered_info);
235 static void keep_common_server_info (const vector<compile_server_info> &info_to_keep, vector<compile_server_info> &filtered_info);
236 static void keep_server_info_with_cert_and_port (systemtap_session &s, const compile_server_info &server, vector<compile_server_info> &servers);
237
238 static void add_server_info (const compile_server_info &info, vector<compile_server_info>& list);
239 static void add_server_info (const vector<compile_server_info> &source, vector<compile_server_info> &target);
240 static void merge_server_info (const compile_server_info &source, compile_server_info &target);
241 #if 0 // not used right now
242 static void merge_server_info (const compile_server_info &source, vector<compile_server_info> &target);
243 static void merge_server_info (const vector<compile_server_info> &source, vector <compile_server_info> &target);
244 #endif
245 static void resolve_host (systemtap_session& s, compile_server_info &server, vector<compile_server_info> &servers);
246
247 /* Exit error codes */
248 #define SUCCESS 0
249 #define GENERAL_ERROR 1
250 #define CA_CERT_INVALID_ERROR 2
251 #define SERVER_CERT_EXPIRED_ERROR 3
252
253 // -----------------------------------------------------
254 // NSS related code used by the compile server client
255 // -----------------------------------------------------
256 static void add_server_trust (systemtap_session &s, const string &cert_db_path, vector<compile_server_info> &server_list);
257 static void revoke_server_trust (systemtap_session &s, const string &cert_db_path, const vector<compile_server_info> &server_list);
258 static void get_server_info_from_db (systemtap_session &s, vector<compile_server_info> &servers, const string &cert_db_path);
259
260 static string global_client_cert_db_path () {
261 return SYSCONFDIR "/systemtap/ssl/client";
262 }
263
264 static string
265 private_ssl_cert_db_path ()
266 {
267 return local_client_cert_db_path ();
268 }
269
270 static string
271 global_ssl_cert_db_path ()
272 {
273 return global_client_cert_db_path ();
274 }
275
276 static string
277 signing_cert_db_path ()
278 {
279 return SYSCONFDIR "/systemtap/staprun";
280 }
281
282 /* Connection state. */
283 typedef struct connectionState_t
284 {
285 const char *hostName;
286 PRNetAddr addr;
287 const char *infileName;
288 const char *outfileName;
289 const char *trustNewServerMode;
290 } connectionState_t;
291
292 #if 0 /* No client authorization */
293 static char *
294 myPasswd(PK11SlotInfo *info, PRBool retry, void *arg)
295 {
296 char * passwd = NULL;
297
298 if ( (!retry) && arg )
299 passwd = PORT_Strdup((char *)arg);
300
301 return passwd;
302 }
303 #endif
304
305 /* Add the server's certificate to our database of trusted servers. */
306 static SECStatus
307 trustNewServer (CERTCertificate *serverCert)
308 {
309 SECStatus secStatus;
310 CERTCertTrust *trust = NULL;
311 PK11SlotInfo *slot = NULL;
312
313 /* Import the certificate. */
314 slot = PK11_GetInternalKeySlot();
315 const char *nickname = server_cert_nickname ();
316 secStatus = PK11_ImportCert(slot, serverCert, CK_INVALID_HANDLE, nickname, PR_FALSE);
317 if (secStatus != SECSuccess)
318 goto done;
319
320 /* Make it a trusted peer. */
321 trust = (CERTCertTrust *)PORT_ZAlloc(sizeof(CERTCertTrust));
322 if (! trust)
323 {
324 secStatus = SECFailure;
325 goto done;
326 }
327
328 secStatus = CERT_DecodeTrustString(trust, "P,P,P");
329 if (secStatus != SECSuccess)
330 goto done;
331
332 secStatus = CERT_ChangeCertTrust(CERT_GetDefaultCertDB(), serverCert, trust);
333
334 done:
335 if (slot)
336 PK11_FreeSlot (slot);
337 if (trust)
338 PORT_Free(trust);
339 return secStatus;
340 }
341
342 /* Called when the server certificate verification fails. This gives us
343 the chance to trust the server anyway and add the certificate to the
344 local database. */
345 static SECStatus
346 badCertHandler(void *arg, PRFileDesc *sslSocket)
347 {
348 SECStatus secStatus;
349 PRErrorCode errorNumber;
350 CERTCertificate *serverCert = NULL;
351 SECItem subAltName;
352 PRArenaPool *tmpArena = NULL;
353 CERTGeneralName *nameList, *current;
354 char *expected = NULL;
355 const connectionState_t *connectionState = (connectionState_t *)arg;
356
357 errorNumber = PR_GetError ();
358 switch (errorNumber)
359 {
360 case SSL_ERROR_BAD_CERT_DOMAIN:
361 /* Since we administer our own client-side databases of trustworthy
362 certificates, we don't need the domain name(s) on the certificate to
363 match. If the cert is in our database, then we can trust it.
364 If we know the expected domain name, then issue a warning but,
365 in any case, accept the certificate. */
366 secStatus = SECSuccess;
367
368 expected = SSL_RevealURL (sslSocket);
369 if (expected == NULL || *expected == '\0')
370 break;
371
372 fprintf (stderr, STAP_CSC_01, expected);
373
374 /* List the DNS names from the server cert as part of the warning.
375 First, find the alt-name extension on the certificate. */
376 subAltName.data = NULL;
377 serverCert = SSL_PeerCertificate (sslSocket);
378 secStatus = CERT_FindCertExtension (serverCert,
379 SEC_OID_X509_SUBJECT_ALT_NAME,
380 & subAltName);
381 if (secStatus != SECSuccess || ! subAltName.data)
382 {
383 fprintf (stderr, _("Unable to find alt name extension on the server certificate\n"));
384 secStatus = SECSuccess; /* Not a fatal error */
385 break;
386 }
387
388 // Now, decode the extension.
389 tmpArena = PORT_NewArena(DER_DEFAULT_CHUNKSIZE);
390 if (! tmpArena)
391 {
392 fprintf (stderr, _("Out of memory\n"));
393 SECITEM_FreeItem(& subAltName, PR_FALSE);
394 secStatus = SECSuccess; /* Not a fatal error here */
395 break;
396 }
397 nameList = CERT_DecodeAltNameExtension (tmpArena, & subAltName);
398 SECITEM_FreeItem(& subAltName, PR_FALSE);
399 if (! nameList)
400 {
401 fprintf (stderr, _("Unable to decode alt name extension on server certificate\n"));
402 secStatus = SECSuccess; /* Not a fatal error */
403 break;
404 }
405
406 /* List the DNS names from the server cert as part of the warning.
407 The names are in a circular list. */
408 current = nameList;
409 do
410 {
411 /* Make sure this is a DNS name. */
412 if (current->type == certDNSName)
413 {
414 fprintf (stderr, " %.*s\n",
415 (int)current->name.other.len, current->name.other.data);
416 }
417 current = CERT_GetNextGeneralName (current);
418 }
419 while (current != nameList);
420
421 break;
422
423 case SEC_ERROR_CA_CERT_INVALID:
424 /* The server's certificate is not trusted. Should we trust it? */
425 secStatus = SECFailure; /* Do not trust by default. */
426 if (! connectionState->trustNewServerMode)
427 break;
428
429 /* Trust it for this session only? */
430 if (strcmp (connectionState->trustNewServerMode, "session") == 0)
431 {
432 secStatus = SECSuccess;
433 break;
434 }
435
436 /* Trust it permanently? */
437 if (strcmp (connectionState->trustNewServerMode, "permanent") == 0)
438 {
439 /* The user wants to trust this server. Get the server's certificate so
440 and add it to our database. */
441 serverCert = SSL_PeerCertificate (sslSocket);
442 if (serverCert != NULL)
443 {
444 secStatus = trustNewServer (serverCert);
445 }
446 }
447 break;
448 default:
449 secStatus = SECFailure; /* Do not trust this server */
450 break;
451 }
452
453 if (expected)
454 PORT_Free (expected);
455 if (tmpArena)
456 PORT_FreeArena (tmpArena, PR_FALSE);
457
458 if (serverCert != NULL)
459 {
460 CERT_DestroyCertificate (serverCert);
461 }
462
463 return secStatus;
464 }
465
466 static PRFileDesc *
467 setupSSLSocket (connectionState_t *connectionState)
468 {
469 PRFileDesc *tcpSocket;
470 PRFileDesc *sslSocket;
471 PRSocketOptionData socketOption;
472 PRStatus prStatus;
473 SECStatus secStatus;
474
475 tcpSocket = PR_OpenTCPSocket(connectionState->addr.raw.family);
476 if (tcpSocket == NULL)
477 goto loser;
478
479 /* Make the socket blocking. */
480 socketOption.option = PR_SockOpt_Nonblocking;
481 socketOption.value.non_blocking = PR_FALSE;
482
483 prStatus = PR_SetSocketOption(tcpSocket, &socketOption);
484 if (prStatus != PR_SUCCESS)
485 goto loser;
486
487 /* Import the socket into the SSL layer. */
488 sslSocket = SSL_ImportFD(NULL, tcpSocket);
489 if (!sslSocket)
490 goto loser;
491
492 /* Set configuration options. */
493 secStatus = SSL_OptionSet(sslSocket, SSL_SECURITY, PR_TRUE);
494 if (secStatus != SECSuccess)
495 goto loser;
496
497 secStatus = SSL_OptionSet(sslSocket, SSL_HANDSHAKE_AS_CLIENT, PR_TRUE);
498 if (secStatus != SECSuccess)
499 goto loser;
500
501 /* Set SSL callback routines. */
502 #if 0 /* no client authentication */
503 secStatus = SSL_GetClientAuthDataHook(sslSocket,
504 (SSLGetClientAuthData)myGetClientAuthData,
505 (void *)certNickname);
506 if (secStatus != SECSuccess)
507 goto loser;
508 #endif
509 #if 0 /* Use the default */
510 secStatus = SSL_AuthCertificateHook(sslSocket,
511 (SSLAuthCertificate)myAuthCertificate,
512 (void *)CERT_GetDefaultCertDB());
513 if (secStatus != SECSuccess)
514 goto loser;
515 #endif
516
517 secStatus = SSL_BadCertHook(sslSocket, (SSLBadCertHandler)badCertHandler,
518 connectionState);
519 if (secStatus != SECSuccess)
520 goto loser;
521
522 #if 0 /* No handshake callback */
523 secStatus = SSL_HandshakeCallback(sslSocket, myHandshakeCallback, NULL);
524 if (secStatus != SECSuccess)
525 goto loser;
526 #endif
527
528 return sslSocket;
529
530 loser:
531 if (tcpSocket)
532 PR_Close(tcpSocket);
533 return NULL;
534 }
535
536
537 static SECStatus
538 handle_connection (PRFileDesc *sslSocket, connectionState_t *connectionState)
539 {
540 PRInt32 numBytes;
541 char *readBuffer;
542 PRFileInfo info;
543 PRFileDesc *local_file_fd;
544 PRStatus prStatus;
545 SECStatus secStatus = SECSuccess;
546
547 #define READ_BUFFER_SIZE (60 * 1024)
548
549 /* If we don't have both the input and output file names, then we're
550 contacting this server only in order to establish trust. In this case send
551 0 as the file size and exit. */
552 if (! connectionState->infileName || ! connectionState->outfileName)
553 {
554 numBytes = htonl ((PRInt32)0);
555 numBytes = PR_Write (sslSocket, & numBytes, sizeof (numBytes));
556 if (numBytes < 0)
557 return SECFailure;
558 return SECSuccess;
559 }
560
561 /* read and send the data. */
562 /* Try to open the local file named.
563 * If successful, then write it to the server
564 */
565 prStatus = PR_GetFileInfo(connectionState->infileName, &info);
566 if (prStatus != PR_SUCCESS ||
567 info.type != PR_FILE_FILE ||
568 info.size < 0)
569 {
570 fprintf (stderr, STAP_CSC_02,
571 connectionState->infileName);
572 return SECFailure;
573 }
574
575 local_file_fd = PR_Open(connectionState->infileName, PR_RDONLY, 0);
576 if (local_file_fd == NULL)
577 {
578 fprintf (stderr, STAP_CSC_03, connectionState->infileName);
579 return SECFailure;
580 }
581
582 /* Send the file size first, so the server knows when it has the entire file. */
583 numBytes = htonl ((PRInt32)info.size);
584 numBytes = PR_Write(sslSocket, & numBytes, sizeof (numBytes));
585 if (numBytes < 0)
586 {
587 PR_Close(local_file_fd);
588 return SECFailure;
589 }
590
591 /* Transmit the local file across the socket. */
592 numBytes = PR_TransmitFile(sslSocket, local_file_fd,
593 NULL, 0,
594 PR_TRANSMITFILE_KEEP_OPEN,
595 PR_INTERVAL_NO_TIMEOUT);
596 if (numBytes < 0)
597 {
598 PR_Close(local_file_fd);
599 return SECFailure;
600 }
601
602 PR_Close(local_file_fd);
603
604 /* read until EOF */
605 readBuffer = (char *)PORT_Alloc(READ_BUFFER_SIZE);
606 if (! readBuffer) {
607 fprintf (stderr, _("Out of memory\n"));
608 return SECFailure;
609 }
610
611 local_file_fd = PR_Open(connectionState->outfileName, PR_WRONLY | PR_CREATE_FILE | PR_TRUNCATE,
612 PR_IRUSR | PR_IWUSR | PR_IRGRP | PR_IWGRP | PR_IROTH);
613 if (local_file_fd == NULL)
614 {
615 fprintf (stderr, STAP_CSC_04, connectionState->outfileName);
616 return SECFailure;
617 }
618 while (PR_TRUE)
619 {
620 // No need for PR_Read_Complete here, since we're already managing multiple
621 // reads to a fixed size buffer.
622 numBytes = PR_Read (sslSocket, readBuffer, READ_BUFFER_SIZE);
623 if (numBytes == 0)
624 break; /* EOF */
625
626 if (numBytes < 0)
627 {
628 secStatus = SECFailure;
629 break;
630 }
631
632 /* Write to output file */
633 numBytes = PR_Write(local_file_fd, readBuffer, numBytes);
634 if (numBytes < 0)
635 {
636 fprintf (stderr, STAP_CSC_05, connectionState->outfileName);
637 secStatus = SECFailure;
638 break;
639 }
640 }
641
642 PR_Free(readBuffer);
643 PR_Close(local_file_fd);
644
645 /* Caller closes the socket. */
646 return secStatus;
647 }
648
649 /* make the connection.
650 */
651 static SECStatus
652 do_connect (connectionState_t *connectionState)
653 {
654 PRFileDesc *sslSocket;
655 PRStatus prStatus;
656 SECStatus secStatus;
657
658 secStatus = SECSuccess;
659
660 /* Set up SSL secure socket. */
661 sslSocket = setupSSLSocket (connectionState);
662 if (sslSocket == NULL)
663 return SECFailure;
664
665 #if 0 /* no client authentication */
666 secStatus = SSL_SetPKCS11PinArg(sslSocket, password);
667 if (secStatus != SECSuccess)
668 goto done;
669 #endif
670
671 secStatus = SSL_SetURL(sslSocket, connectionState->hostName);
672 if (secStatus != SECSuccess)
673 goto done;
674
675 prStatus = PR_Connect(sslSocket, & connectionState->addr, PR_INTERVAL_NO_TIMEOUT);
676 if (prStatus != PR_SUCCESS)
677 {
678 secStatus = SECFailure;
679 goto done;
680 }
681
682 /* Established SSL connection, ready to send data. */
683 secStatus = SSL_ResetHandshake(sslSocket, /* asServer */ PR_FALSE);
684 if (secStatus != SECSuccess)
685 goto done;
686
687 /* This is normally done automatically on the first I/O operation,
688 but doing it here catches any authentication problems early. */
689 secStatus = SSL_ForceHandshake(sslSocket);
690 if (secStatus != SECSuccess)
691 goto done;
692
693 // Connect to the server and make the request.
694 secStatus = handle_connection(sslSocket, connectionState);
695
696 done:
697 prStatus = PR_Close(sslSocket);
698 return secStatus;
699 }
700
701 static bool
702 isIPv6LinkLocal (const PRNetAddr &address)
703 {
704 // Link-local addresses are members of the address block fe80::
705 if (address.raw.family == PR_AF_INET6 &&
706 address.ipv6.ip.pr_s6_addr[0] == 0xfe && address.ipv6.ip.pr_s6_addr[1] == 0x80)
707 return true;
708 return false;
709 }
710
711 int
712 client_connect (const compile_server_info &server,
713 const char* infileName, const char* outfileName,
714 const char* trustNewServer)
715 {
716 SECStatus secStatus;
717 PRErrorCode errorNumber;
718 int attempt;
719 int errCode = GENERAL_ERROR;
720 struct connectionState_t connectionState;
721
722 // Set up a connection state for use by NSS error callbacks.
723 memset (& connectionState, 0, sizeof (connectionState));
724 connectionState.hostName = server.host_name.c_str ();
725 connectionState.addr = server.address;
726 connectionState.infileName = infileName;
727 connectionState.outfileName = outfileName;
728 connectionState.trustNewServerMode = trustNewServer;
729
730 /* Some errors (see below) represent a situation in which trying again
731 should succeed. However, don't try forever. */
732 for (attempt = 0; attempt < 5; ++attempt)
733 {
734 secStatus = do_connect (& connectionState);
735 if (secStatus == SECSuccess)
736 return SUCCESS;
737
738 errorNumber = PR_GetError ();
739 switch (errorNumber)
740 {
741 case PR_CONNECT_RESET_ERROR:
742 /* Server was not ready. */
743 sleep (1);
744 break; /* Try again */
745 case SEC_ERROR_EXPIRED_CERTIFICATE:
746 /* The server's certificate has expired. It should
747 generate a new certificate. Return now and we'll try again. */
748 errCode = SERVER_CERT_EXPIRED_ERROR;
749 return errCode;
750 case SEC_ERROR_CA_CERT_INVALID:
751 /* The server's certificate is not trusted. The exit code must
752 reflect this. */
753 errCode = CA_CERT_INVALID_ERROR;
754 return errCode;
755 default:
756 /* This error is fatal. */
757 return errCode;
758 }
759 }
760
761 return errCode;
762 }
763
764 int
765 compile_server_client::passes_0_4 ()
766 {
767 PROBE1(stap, client__start, &s);
768
769 // arguments parsed; get down to business
770 if (s.verbose || ! s.auto_server_msgs.empty ())
771 clog << _("Using a compile server.") << endl;
772
773 struct tms tms_before;
774 times (& tms_before);
775 struct timeval tv_before;
776 gettimeofday (&tv_before, NULL);
777
778 // Create the request package.
779 int rc = initialize ();
780 assert_no_interrupts();
781 if (rc != 0) goto done;
782 rc = create_request ();
783 assert_no_interrupts();
784 if (rc != 0) goto done;
785 rc = package_request ();
786 assert_no_interrupts();
787 if (rc != 0) goto done;
788
789 // Submit it to the server.
790 rc = find_and_connect_to_server ();
791 assert_no_interrupts();
792 if (rc != 0) goto done;
793
794 // Unpack and process the response.
795 rc = unpack_response ();
796 assert_no_interrupts();
797 if (rc != 0) goto done;
798 rc = process_response ();
799
800 done:
801 struct tms tms_after;
802 times (& tms_after);
803 unsigned _sc_clk_tck = sysconf (_SC_CLK_TCK);
804 struct timeval tv_after;
805 gettimeofday (&tv_after, NULL);
806
807 #define TIMESPRINT "in " << \
808 (tms_after.tms_cutime + tms_after.tms_utime \
809 - tms_before.tms_cutime - tms_before.tms_utime) * 1000 / (_sc_clk_tck) << "usr/" \
810 << (tms_after.tms_cstime + tms_after.tms_stime \
811 - tms_before.tms_cstime - tms_before.tms_stime) * 1000 / (_sc_clk_tck) << "sys/" \
812 << ((tv_after.tv_sec - tv_before.tv_sec) * 1000 + \
813 ((long)tv_after.tv_usec - (long)tv_before.tv_usec) / 1000) << "real ms."
814
815 if (rc == 0)
816 {
817 // Save the module, if necessary.
818 if (s.last_pass == 4)
819 s.save_module = true;
820
821 // Copy module to the current directory.
822 if (! pending_interrupts)
823 {
824 if (s.save_module)
825 {
826 string module_src_path = s.tmpdir + "/" + s.module_filename();
827 string module_dest_path = s.module_filename();
828 copy_file (module_src_path, module_dest_path, s.verbose >= 3);
829 // Also copy the module signature, it it exists.
830 module_src_path += ".sgn";
831 if (file_exists (module_src_path))
832 {
833 module_dest_path += ".sgn";
834 copy_file(module_src_path, module_dest_path, s.verbose >= 3);
835 }
836 }
837 // Print the name of the module
838 if (s.last_pass == 4)
839 {
840 cout << s.module_filename() << endl;
841 }
842 }
843 }
844
845 // syntax errors, if any, are already printed
846 if (s.verbose)
847 {
848 string ws = s.winning_server;
849 if (ws == "") ws = "?";
850 clog << _("Passes: via server ") << ws << " "
851 << getmemusage()
852 << TIMESPRINT
853 << endl;
854 }
855 if (rc && !s.dump_mode)
856 {
857 clog << _("Passes: via server failed. Try again with another '-v' option.") << endl;
858 }
859
860 PROBE1(stap, client__end, &s);
861
862 return rc;
863 }
864
865 // Initialize a client/server session.
866 int
867 compile_server_client::initialize ()
868 {
869 int rc = 0;
870
871 // Initialize session state
872 argc = 0;
873
874 // Private location for server certificates.
875 private_ssl_dbs.push_back (private_ssl_cert_db_path ());
876
877 // Additional public location.
878 public_ssl_dbs.push_back (global_ssl_cert_db_path ());
879
880 // Create a temporary directory to package things in.
881 client_tmpdir = s.tmpdir + "/client";
882 rc = create_dir (client_tmpdir.c_str ());
883 if (rc != 0)
884 {
885 const char* e = strerror (errno);
886 clog << _("ERROR: cannot create temporary directory (\"")
887 << client_tmpdir << "\"): " << e
888 << endl;
889 }
890
891 return rc;
892 }
893
894 // Create the request package.
895 int
896 compile_server_client::create_request ()
897 {
898 // Add the current protocol version.
899 int rc = write_to_file (client_tmpdir + "/version", CURRENT_CS_PROTOCOL_VERSION);
900 if (rc != 0)
901 return rc;
902
903 // Add the script file or script option
904 if (s.script_file != "")
905 {
906 if (s.script_file == "-")
907 {
908 // Copy the script from stdin
909 string packaged_script_dir = client_tmpdir + "/script";
910 rc = create_dir (packaged_script_dir.c_str ());
911 if (rc != 0)
912 {
913 const char* e = strerror (errno);
914 clog << _("ERROR: cannot create temporary directory ")
915 << packaged_script_dir << ": " << e
916 << endl;
917 return rc;
918 }
919 rc = ! copy_file("/dev/stdin", packaged_script_dir + "/-");
920 if (rc != 0)
921 return rc;
922
923 // Name the script in the packaged arguments.
924 rc = add_package_arg ("script/-");
925 if (rc != 0)
926 return rc;
927 }
928 else
929 {
930 // Add the script to our package. This will also name the script
931 // in the packaged arguments.
932 rc = include_file_or_directory ("script", s.script_file);
933 if (rc != 0)
934 return rc;
935 }
936 }
937
938 // Add -I paths. Skip the default directory.
939 if (s.include_arg_start != -1)
940 {
941 unsigned limit = s.include_path.size ();
942 for (unsigned i = s.include_arg_start; i < limit; ++i)
943 {
944 rc = add_package_arg ("-I");
945 if (rc != 0)
946 return rc;
947 rc = include_file_or_directory ("tapset", s.include_path[i]);
948 if (rc != 0)
949 return rc;
950 }
951 }
952
953 // Add other options.
954 rc = add_package_args ();
955 if (rc != 0)
956 return rc;
957
958 // Add the sysinfo file
959 string sysinfo = "sysinfo: " + s.kernel_release + " " + s.architecture;
960 rc = write_to_file (client_tmpdir + "/sysinfo", sysinfo);
961 if (rc != 0)
962 return rc;
963
964 // Add localization data
965 rc = add_localization_variables();
966
967 // Add the machine owner key (MOK) fingerprints file, if needed.
968 if (! s.mok_fingerprints.empty())
969 {
970 ostringstream fingerprints;
971 vector<string>::const_iterator it;
972 for (it = s.mok_fingerprints.begin(); it != s.mok_fingerprints.end();
973 it++)
974 fingerprints << *it << endl;
975
976 rc = write_to_file(client_tmpdir + "/mok_fingerprints",
977 fingerprints.str());
978 if (rc != 0)
979 return rc;
980 }
981
982 return rc;
983 }
984
985 // Add the arguments specified on the command line to the server request
986 // package, as appropriate.
987 int
988 compile_server_client::add_package_args ()
989 {
990 // stap arguments to be passed to the server.
991 int rc = 0;
992 unsigned limit = s.server_args.size();
993 for (unsigned i = 0; i < limit; ++i)
994 {
995 rc = add_package_arg (s.server_args[i]);
996 if (rc != 0)
997 return rc;
998 }
999
1000 // Script arguments.
1001 limit = s.args.size();
1002 if (limit > 0) {
1003 rc = add_package_arg ("--");
1004 if (rc != 0)
1005 return rc;
1006 for (unsigned i = 0; i < limit; ++i)
1007 {
1008 rc = add_package_arg (s.args[i]);
1009 if (rc != 0)
1010 return rc;
1011 }
1012 }
1013 return rc;
1014 }
1015
1016 int
1017 compile_server_client::add_package_arg (const string &arg)
1018 {
1019 int rc = 0;
1020 ostringstream fname;
1021 fname << client_tmpdir << "/argv" << ++argc;
1022 write_to_file (fname.str (), arg); // NB: No terminating newline
1023 return rc;
1024 }
1025
1026 // Symbolically link the given file or directory into the client's temp
1027 // directory under the given subdirectory.
1028 int
1029 compile_server_client::include_file_or_directory (
1030 const string &subdir, const string &path
1031 )
1032 {
1033 // Must predeclare these because we do use 'goto done' to
1034 // exit from error situations.
1035 vector<string> components;
1036 string name;
1037 int rc;
1038
1039 // Canonicalize the given path and remove the leading /.
1040 string rpath;
1041 char *cpath = canonicalize_file_name (path.c_str ());
1042 if (! cpath)
1043 {
1044 // It can not be canonicalized. Use the name relative to
1045 // the current working directory and let the server deal with it.
1046 char cwd[PATH_MAX];
1047 if (getcwd (cwd, sizeof (cwd)) == NULL)
1048 {
1049 rpath = path;
1050 rc = 1;
1051 goto done;
1052 }
1053 rpath = string (cwd) + "/" + path;
1054 }
1055 else
1056 {
1057 // It can be canonicalized. Use the canonicalized name and add this
1058 // file or directory to the request package.
1059 rpath = cpath;
1060 free (cpath);
1061
1062 // Including / would require special handling in the code below and
1063 // is a bad idea anyway. Let's not allow it.
1064 if (rpath == "/")
1065 {
1066 if (rpath != path)
1067 clog << _F("%s resolves to %s\n", path.c_str (), rpath.c_str ());
1068 clog << _F("Unable to send %s to the server\n", path.c_str ());
1069 return 1;
1070 }
1071
1072 // First create the requested subdirectory.
1073 name = client_tmpdir + "/" + subdir;
1074 rc = create_dir (name.c_str ());
1075 if (rc) goto done;
1076
1077 // Now create each component of the path within the sub directory.
1078 assert (rpath[0] == '/');
1079 tokenize (rpath.substr (1), components, "/");
1080 assert (components.size () >= 1);
1081 unsigned i;
1082 for (i = 0; i < components.size() - 1; ++i)
1083 {
1084 if (components[i].empty ())
1085 continue; // embedded '//'
1086 name += "/" + components[i];
1087 rc = create_dir (name.c_str ());
1088 if (rc) goto done;
1089 }
1090
1091 // Now make a symbolic link to the actual file or directory.
1092 assert (i == components.size () - 1);
1093 name += "/" + components[i];
1094 rc = symlink (rpath.c_str (), name.c_str ());
1095 if (rc) goto done;
1096 }
1097
1098 // Name this file or directory in the packaged arguments.
1099 rc = add_package_arg (subdir + "/" + rpath.substr (1));
1100
1101 done:
1102 if (rc != 0)
1103 {
1104 const char* e = strerror (errno);
1105 clog << "ERROR: unable to add "
1106 << rpath
1107 << " to temp directory as "
1108 << name << ": " << e
1109 << endl;
1110 }
1111 return rc;
1112 }
1113
1114 // Add the localization variables to the server request
1115 // package.
1116 int
1117 compile_server_client::add_localization_variables()
1118 {
1119 int rc;
1120 string envVar;
1121 string fname;
1122
1123 const set<string> &locVars = localization_variables();
1124 set<string>::iterator it;
1125
1126 /* Note: We don't have to check for the contents of the environment
1127 * variables here, since they will be checked extensively on the
1128 * server.
1129 */
1130 for (it = locVars.begin(); it != locVars.end(); it++)
1131 {
1132 char* var = getenv((*it).c_str());
1133 if (var)
1134 envVar += *it + "=" + (string)var + "\n";
1135 }
1136 fname = client_tmpdir + "/locale";
1137 rc = write_to_file(fname, envVar);
1138 return rc;
1139 }
1140
1141 // Package the client's temp directory into a form suitable for sending to the
1142 // server.
1143 int
1144 compile_server_client::package_request ()
1145 {
1146 // Package up the temporary directory into a zip file.
1147 client_zipfile = client_tmpdir + ".zip";
1148 string cmd = "cd " + cmdstr_quoted(client_tmpdir) + " && zip -qr "
1149 + cmdstr_quoted(client_zipfile) + " *";
1150 vector<string> sh_cmd;
1151 sh_cmd.push_back("sh");
1152 sh_cmd.push_back("-c");
1153 sh_cmd.push_back(cmd);
1154 int rc = stap_system (s.verbose, sh_cmd);
1155 return rc;
1156 }
1157
1158 int
1159 compile_server_client::find_and_connect_to_server ()
1160 {
1161 // Accumulate info on the specified servers.
1162 vector<compile_server_info> specified_servers;
1163 get_specified_server_info (s, specified_servers);
1164
1165 // Examine the specified servers to make sure that each has been resolved
1166 // with a host name, ip address and port. If not, try to obtain this
1167 // information by examining online servers.
1168 vector<compile_server_info> server_list;
1169 for (vector<compile_server_info>::const_iterator i = specified_servers.begin ();
1170 i != specified_servers.end ();
1171 ++i)
1172 {
1173 // If we have an ip address and were given a port number, then just use the one we've
1174 // been given. Otherwise, check for matching compatible online servers and try their
1175 // ip addresses and ports.
1176 if (i->hasAddress() && i->fully_specified)
1177 add_server_info (*i, server_list);
1178 else
1179 {
1180 // Obtain a list of online servers.
1181 vector<compile_server_info> online_servers;
1182 get_or_keep_online_server_info (s, online_servers, false/*keep*/);
1183
1184 // If no specific server (port) has been specified,
1185 // then we'll need the servers to be
1186 // compatible and possibly trusted as signers as well.
1187 if (! i->fully_specified)
1188 {
1189 get_or_keep_compatible_server_info (s, online_servers, true/*keep*/);
1190 if (! pr_contains (s.privilege, pr_stapdev))
1191 get_or_keep_signing_server_info (s, online_servers, true/*keep*/);
1192 }
1193
1194 // Keep the ones (if any) which match our server.
1195 keep_common_server_info (*i, online_servers);
1196
1197 // Add these servers (if any) to the server list.
1198 add_server_info (online_servers, server_list);
1199 }
1200 }
1201
1202 // Did we identify any potential servers?
1203 unsigned limit = server_list.size ();
1204 if (limit == 0)
1205 {
1206 clog << _("Unable to find a suitable compile server. [man stap-server]") << endl;
1207
1208 // Try to explain why.
1209 vector<compile_server_info> online_servers;
1210 get_or_keep_online_server_info (s, online_servers, false/*keep*/);
1211 if (online_servers.empty ())
1212 clog << _("No servers online to select from.") << endl;
1213 else
1214 {
1215 clog << _("The following servers are online:") << endl;
1216 clog << online_servers;
1217 if (! specified_servers.empty ())
1218 {
1219 clog << _("The following servers were requested:") << endl;
1220 clog << specified_servers;
1221 }
1222 else
1223 {
1224 string criteria = "online,trusted,compatible";
1225 if (! pr_contains (s.privilege, pr_stapdev))
1226 criteria += ",signer";
1227 clog << _F("No servers matched the selection criteria of %s.", criteria.c_str())
1228 << endl;
1229 }
1230 }
1231 return 1;
1232 }
1233
1234 // Sort the list of servers into a preferred order.
1235 preferred_order (server_list);
1236
1237 // Now try each of the identified servers in turn.
1238 int rc = compile_using_server (server_list);
1239 if (rc == SUCCESS)
1240 return 0; // success!
1241
1242 // If the error was that a server's cert was expired, try again. This is because the server
1243 // should generate a new cert which may be automatically trusted by us if it is our server.
1244 // Give the server a chance to do this before retrying.
1245 if (rc == SERVER_CERT_EXPIRED_ERROR)
1246 {
1247 if (s.verbose >= 2)
1248 clog << _("The server's certificate was expired. Trying again") << endl << flush;
1249 sleep (2);
1250 rc = compile_using_server (server_list);
1251 if (rc == SUCCESS)
1252 return 0; // success!
1253 }
1254
1255 // We were unable to use any available server
1256 clog << _("Unable to connect to a server.") << endl;
1257 if (s.verbose == 1)
1258 {
1259 // This information is redundant at higher verbosity levels.
1260 clog << _("The following servers were tried:") << endl;
1261 clog << server_list;
1262 }
1263 return 1; // Failure
1264 }
1265
1266 int
1267 compile_server_client::compile_using_server (
1268 vector<compile_server_info> &servers
1269 )
1270 {
1271 // Make sure NSPR is initialized. Must be done before NSS is initialized
1272 s.NSPR_init ();
1273
1274 // Attempt connection using each of the available client certificate
1275 // databases. Assume the server certificate is invalid until proven otherwise.
1276 PR_SetError (SEC_ERROR_CA_CERT_INVALID, 0);
1277 vector<string> dbs = private_ssl_dbs;
1278 vector<string>::iterator i = dbs.end();
1279 dbs.insert (i, public_ssl_dbs.begin (), public_ssl_dbs.end ());
1280 int rc = GENERAL_ERROR; // assume failure
1281 bool serverCertExpired = false;
1282 for (i = dbs.begin (); i != dbs.end (); ++i)
1283 {
1284 // Make sure the database directory exists. It is not an error if it
1285 // doesn't.
1286 if (! file_exists (*i))
1287 continue;
1288
1289 #if 0 // no client authentication for now.
1290 // Set our password function callback.
1291 PK11_SetPasswordFunc (myPasswd);
1292 #endif
1293
1294 // Initialize the NSS libraries.
1295 const char *cert_dir = i->c_str ();
1296 SECStatus secStatus = nssInit (cert_dir);
1297 if (secStatus != SECSuccess)
1298 {
1299 // Message already issued.
1300 continue; // try next database
1301 }
1302
1303 // Enable all cipher suites.
1304 // SSL_ClearSessionCache is required for the new settings to take effect.
1305 /* Some NSS versions don't do this correctly in NSS_SetDomesticPolicy. */
1306 do {
1307 const PRUint16 *cipher;
1308 for (cipher = SSL_GetImplementedCiphers(); *cipher != 0; ++cipher)
1309 SSL_CipherPolicySet(*cipher, SSL_ALLOWED);
1310 } while (0);
1311 SSL_ClearSessionCache ();
1312
1313 server_zipfile = s.tmpdir + "/server.zip";
1314
1315 // Try each server in turn.
1316 for (vector<compile_server_info>::iterator j = servers.begin ();
1317 j != servers.end ();
1318 ++j)
1319 {
1320 // At a minimum we need an ip_address along with a port
1321 // number in order to contact the server.
1322 if (! j->hasAddress() || j->port == 0)
1323 continue;
1324 // Set the port within the address.
1325 j->setAddressPort (j->port);
1326
1327 if (s.verbose >= 2)
1328 clog << _F("Attempting SSL connection with %s\n"
1329 " using certificates from the database in %s\n",
1330 lex_cast(*j).c_str(), cert_dir);
1331
1332 rc = client_connect (*j, client_zipfile.c_str(), server_zipfile.c_str (),
1333 NULL/*trustNewServer_p*/);
1334 if (rc == SUCCESS)
1335 {
1336 s.winning_server = lex_cast(*j);
1337 break; // Success!
1338 }
1339
1340 // Server cert has expired. Try other servers and/or databases, but take note because
1341 // server should generate a new certificate. If no other servers succeed, we'll try again
1342 // in case the new cert works.
1343 if (rc == SERVER_CERT_EXPIRED_ERROR)
1344 {
1345 serverCertExpired = true;
1346 continue;
1347 }
1348
1349 if (s.verbose >= 2)
1350 {
1351 clog << _(" Unable to connect: ");
1352 nssError ();
1353 // Additional information: if the address is IPv6 and is link-local, then it must
1354 // have a scope_id.
1355 if (isIPv6LinkLocal (j->address) && j->address.ipv6.scope_id == 0)
1356 {
1357 clog << _(" The address is an IPv6 link-local address with no scope specifier.")
1358 << endl;
1359 }
1360 }
1361 }
1362
1363 // SSL_ClearSessionCache is required before shutdown for client applications.
1364 SSL_ClearSessionCache ();
1365 nssCleanup (cert_dir);
1366
1367 if (rc == SECSuccess)
1368 break; // Success!
1369 }
1370
1371 // Indicate whether a server cert was expired, so we can try again, if desired.
1372 if (rc != SUCCESS)
1373 {
1374 if (serverCertExpired)
1375 rc = SERVER_CERT_EXPIRED_ERROR;
1376 }
1377
1378 return rc;
1379 }
1380
1381 int
1382 compile_server_client::unpack_response ()
1383 {
1384 // Unzip the response package.
1385 server_tmpdir = s.tmpdir + "/server";
1386 vector<string> cmd;
1387 cmd.push_back("unzip");
1388 cmd.push_back("-qd");
1389 cmd.push_back(server_tmpdir);
1390 cmd.push_back(server_zipfile);
1391 int rc = stap_system (s.verbose, cmd);
1392 if (rc != 0)
1393 {
1394 clog << _F("Unable to unzip the server response '%s'\n", server_zipfile.c_str());
1395 return rc;
1396 }
1397
1398 // Determine the server protocol version.
1399 string filename = server_tmpdir + "/version";
1400 if (file_exists (filename))
1401 ::read_from_file (filename, server_version);
1402
1403 // Warn about the shortcomings of this server, if it is down level.
1404 show_server_compatibility ();
1405
1406 // If the server's response contains a systemtap temp directory, move
1407 // its contents to our temp directory.
1408 glob_t globbuf;
1409 string filespec = server_tmpdir + "/stap??????";
1410 if (s.verbose >= 3)
1411 clog << _F("Searching \"%s\"\n", filespec.c_str());
1412 int r = glob(filespec.c_str (), 0, NULL, & globbuf);
1413 if (r != GLOB_NOSPACE && r != GLOB_ABORTED && r != GLOB_NOMATCH)
1414 {
1415 if (globbuf.gl_pathc > 1)
1416 {
1417 clog << _("Incorrect number of files in server response") << endl;
1418 rc = 1;
1419 goto done;
1420 }
1421
1422 assert (globbuf.gl_pathc == 1);
1423 string dirname = globbuf.gl_pathv[0];
1424 if (s.verbose >= 3)
1425 clog << _(" found ") << dirname << endl;
1426
1427 filespec = dirname + "/*";
1428 if (s.verbose >= 3)
1429 clog << _F("Searching \"%s\"\n", filespec.c_str());
1430 int r = glob(filespec.c_str (), GLOB_PERIOD, NULL, & globbuf);
1431 if (r != GLOB_NOSPACE && r != GLOB_ABORTED && r != GLOB_NOMATCH)
1432 {
1433 unsigned prefix_len = dirname.size () + 1;
1434 for (unsigned i = 0; i < globbuf.gl_pathc; ++i)
1435 {
1436 string oldname = globbuf.gl_pathv[i];
1437 if (oldname.substr (oldname.size () - 2) == "/." ||
1438 oldname.substr (oldname.size () - 3) == "/..")
1439 continue;
1440 string newname = s.tmpdir + "/" + oldname.substr (prefix_len);
1441 if (s.verbose >= 3)
1442 clog << _F(" found %s -- linking from %s", oldname.c_str(), newname.c_str());
1443 rc = symlink (oldname.c_str (), newname.c_str ());
1444 if (rc != 0)
1445 {
1446 clog << _F("Unable to link '%s' to '%s':%s\n",
1447 oldname.c_str(), newname.c_str(), strerror(errno));
1448 goto done;
1449 }
1450 }
1451 }
1452 }
1453
1454 // If the server version is less that 1.6, remove the output line due to the synthetic
1455 // server-side -k. Look for a message containing the name of the temporary directory.
1456 // We can look for the English message since server versions before 1.6 do not support
1457 // localization.
1458 if (server_version < "1.6")
1459 {
1460 cmd.clear();
1461 cmd.push_back("sed");
1462 cmd.push_back("-i");
1463 cmd.push_back("/^Keeping temporary directory.*/ d");
1464 cmd.push_back(server_tmpdir + "/stderr");
1465 stap_system (s.verbose, cmd);
1466 }
1467
1468 // Remove the output line due to the synthetic server-side -p4
1469 cmd.clear();
1470 cmd.push_back("sed");
1471 cmd.push_back("-i");
1472 cmd.push_back("/^.*\\.ko$/ d");
1473 cmd.push_back(server_tmpdir + "/stdout");
1474 stap_system (s.verbose, cmd);
1475
1476 done:
1477 globfree (& globbuf);
1478 return rc;
1479 }
1480
1481 int
1482 compile_server_client::process_response ()
1483 {
1484 // Pick up the results of running stap on the server.
1485 string filename = server_tmpdir + "/rc";
1486 int stap_rc;
1487 int rc = read_from_file (filename, stap_rc);
1488 if (rc != 0)
1489 return rc;
1490 rc = stap_rc;
1491
1492 if (s.last_pass >= 4)
1493 {
1494 // The server should have returned a module.
1495 string filespec = s.tmpdir + "/*.ko";
1496 if (s.verbose >= 3)
1497 clog << _F("Searching \"%s\"\n", filespec.c_str());
1498
1499 glob_t globbuf;
1500 int r = glob(filespec.c_str (), 0, NULL, & globbuf);
1501 if (r != GLOB_NOSPACE && r != GLOB_ABORTED && r != GLOB_NOMATCH)
1502 {
1503 if (globbuf.gl_pathc > 1)
1504 clog << _("Incorrect number of modules in server response") << endl;
1505 else
1506 {
1507 assert (globbuf.gl_pathc == 1);
1508 string modname = globbuf.gl_pathv[0];
1509 if (s.verbose >= 3)
1510 clog << _(" found ") << modname << endl;
1511
1512 // If a module name was not specified by the user, then set it to
1513 // be the one generated by the server.
1514 if (! s.save_module)
1515 {
1516 vector<string> components;
1517 tokenize (modname, components, "/");
1518 s.module_name = components.back ();
1519 s.module_name.erase(s.module_name.size() - 3);
1520 }
1521
1522 // If a uprobes.ko module was returned, then make note of it.
1523 string uprobes_ko;
1524 if (server_version < "1.6")
1525 uprobes_ko = s.tmpdir + "/server/uprobes.ko";
1526 else
1527 uprobes_ko = s.tmpdir + "/uprobes/uprobes.ko";
1528
1529 if (file_exists (uprobes_ko))
1530 {
1531 s.need_uprobes = true;
1532 s.uprobes_path = uprobes_ko;
1533 }
1534 }
1535 }
1536 else if (s.have_script)
1537 {
1538 if (rc == 0)
1539 {
1540 clog << _("No module was returned by the server.") << endl;
1541 rc = 1;
1542 }
1543 }
1544 globfree (& globbuf);
1545 }
1546
1547 // If the server returned a MOK certificate, copy it to the user's
1548 // current directory.
1549 string server_MOK_public_cert = s.tmpdir + "/server/" MOK_PUBLIC_CERT_NAME;
1550 if (file_exists (server_MOK_public_cert))
1551 {
1552 string dst = MOK_PUBLIC_CERT_NAME;
1553 copy_file (server_MOK_public_cert, dst, (s.verbose >= 3));
1554 }
1555
1556 // Output stdout and stderr.
1557 filename = server_tmpdir + "/stderr";
1558 flush_to_stream (filename, clog);
1559
1560 filename = server_tmpdir + "/stdout";
1561 flush_to_stream (filename, cout);
1562
1563 return rc;
1564 }
1565
1566 int
1567 compile_server_client::read_from_file (const string &fname, int &data)
1568 {
1569 // C++ streams may not set errno in the even of a failure. However if we
1570 // set it to 0 before each operation and it gets set during the operation,
1571 // then we can use its value in order to determine what happened.
1572 errno = 0;
1573 ifstream f (fname.c_str ());
1574 if (! f.good ())
1575 {
1576 clog << _F("Unable to open file '%s' for reading: ", fname.c_str());
1577 goto error;
1578 }
1579
1580 // Read the data;
1581 errno = 0;
1582 f >> data;
1583 if (f.fail ())
1584 {
1585 clog << _F("Unable to read from file '%s': ", fname.c_str());
1586 goto error;
1587 }
1588
1589 // NB: not necessary to f.close ();
1590 return 0; // Success
1591
1592 error:
1593 if (errno)
1594 clog << strerror (errno) << endl;
1595 else
1596 clog << _("unknown error") << endl;
1597 return 1; // Failure
1598 }
1599
1600 template <class T>
1601 int
1602 compile_server_client::write_to_file (const string &fname, const T &data)
1603 {
1604 // C++ streams may not set errno in the even of a failure. However if we
1605 // set it to 0 before each operation and it gets set during the operation,
1606 // then we can use its value in order to determine what happened.
1607 errno = 0;
1608 ofstream f (fname.c_str ());
1609 if (! f.good ())
1610 {
1611 clog << _F("Unable to open file '%s' for writing: ", fname.c_str());
1612 goto error;
1613 }
1614
1615 // Write the data;
1616 f << data;
1617 errno = 0;
1618 if (f.fail ())
1619 {
1620 clog << _F("Unable to write to file '%s': ", fname.c_str());
1621 goto error;
1622 }
1623
1624 // NB: not necessary to f.close ();
1625 return 0; // Success
1626
1627 error:
1628 if (errno)
1629 clog << strerror (errno) << endl;
1630 else
1631 clog << _("unknown error") << endl;
1632 return 1; // Failure
1633 }
1634
1635 int
1636 compile_server_client::flush_to_stream (const string &fname, ostream &o)
1637 {
1638 // C++ streams may not set errno in the even of a failure. However if we
1639 // set it to 0 before each operation and it gets set during the operation,
1640 // then we can use its value in order to determine what happened.
1641 errno = 0;
1642 ifstream f (fname.c_str ());
1643 if (! f.good ())
1644 {
1645 clog << _F("Unable to open file '%s' for reading: ", fname.c_str());
1646 goto error;
1647 }
1648
1649 // Stream the data
1650
1651 // NB: o << f.rdbuf() misbehaves for some reason, appearing to close o,
1652 // which is unfortunate if o == clog or cout.
1653 while (1)
1654 {
1655 errno = 0;
1656 int c = f.get();
1657 if (f.eof ()) return 0; // normal exit
1658 if (! f.good()) break;
1659 o.put(c);
1660 if (! o.good()) break;
1661 }
1662
1663 // NB: not necessary to f.close ();
1664
1665 error:
1666 if (errno)
1667 clog << strerror (errno) << endl;
1668 else
1669 clog << _("unknown error") << endl;
1670 return 1; // Failure
1671 }
1672
1673 void
1674 compile_server_client::show_server_compatibility () const
1675 {
1676 // Locale sensitivity was added in version 1.6
1677 if (server_version < "1.6")
1678 {
1679 clog << _F("Server protocol version is %s\n", server_version.v);
1680 clog << _("The server does not use localization information passed by the client\n");
1681 }
1682 }
1683
1684 // Issue a status message for when a server's trust is already in place.
1685 static void
1686 trust_already_in_place (
1687 const compile_server_info &server,
1688 const vector<compile_server_info> &server_list,
1689 const string cert_db_path,
1690 bool revoking
1691 )
1692 {
1693 // What level of trust?
1694 string purpose;
1695 if (cert_db_path == signing_cert_db_path ())
1696 purpose = _("as a module signer for all users");
1697 else
1698 {
1699 purpose = _("as an SSL peer");
1700 if (cert_db_path == global_ssl_cert_db_path ())
1701 purpose += _(" for all users");
1702 else
1703 purpose += _(" for the current user");
1704 }
1705
1706 // Issue a message for each server in the list with the same certificate.
1707 unsigned limit = server_list.size ();
1708 for (unsigned i = 0; i < limit; ++i)
1709 {
1710 if (server.certinfo != server_list[i].certinfo)
1711 continue;
1712 clog << server_list[i] << _(" is already ");
1713 if (revoking)
1714 clog << _("untrusted ") << purpose << endl;
1715 else
1716 clog << _("trusted ") << purpose << endl;
1717 }
1718 }
1719
1720 // Add the given servers to the given database of trusted servers.
1721 static void
1722 add_server_trust (
1723 systemtap_session &s,
1724 const string &cert_db_path,
1725 vector<compile_server_info> &server_list
1726 )
1727 {
1728 // Get a list of servers already trusted. This opens the database, so do it
1729 // before we open it for our own purposes.
1730 vector<compile_server_info> already_trusted;
1731 get_server_info_from_db (s, already_trusted, cert_db_path);
1732
1733 // Make sure the given path exists.
1734 if (create_dir (cert_db_path.c_str (), 0755) != 0)
1735 {
1736 clog << _F("Unable to find or create the client certificate database directory %s: ", cert_db_path.c_str());
1737 perror ("");
1738 return;
1739 }
1740
1741 // Must predeclare this because of jumps to cleanup: below.
1742 vector<string> processed_certs;
1743
1744 // Make sure NSPR is initialized. Must be done before NSS is initialized
1745 s.NSPR_init ();
1746
1747 // Initialize the NSS libraries -- read/write
1748 SECStatus secStatus = nssInit (cert_db_path.c_str (), 1/*readwrite*/);
1749 if (secStatus != SECSuccess)
1750 {
1751 // Message already issued.
1752 goto cleanup;
1753 }
1754
1755 // Enable all cipher suites.
1756 // SSL_ClearSessionCache is required for the new settings to take effect.
1757 /* Some NSS versions don't do this correctly in NSS_SetDomesticPolicy. */
1758 do {
1759 const PRUint16 *cipher;
1760 for (cipher = SSL_GetImplementedCiphers(); *cipher != 0; ++cipher)
1761 SSL_CipherPolicySet(*cipher, SSL_ALLOWED);
1762 } while (0);
1763 SSL_ClearSessionCache ();
1764
1765 // Iterate over the servers to become trusted. Contact each one and
1766 // add it to the list of trusted servers if it is not already trusted.
1767 // client_connect will issue any error messages.
1768 for (vector<compile_server_info>::iterator server = server_list.begin();
1769 server != server_list.end ();
1770 ++server)
1771 {
1772 // Trust is based on certificates. We need only add trust in the
1773 // same certificate once.
1774 //
1775 // RHBZ 1075685: If the new server to be trusted is selected by address + port,
1776 // and there is no avahi assistance available, or the server is not known
1777 // to avahi, then its certificate serial number field will be empty. We
1778 // therefore have no basis for comparing it to the serial numbers on already-trusted
1779 // certificates. In this case, unconditionally contact the new server to obtain
1780 // its certificate.
1781 if (! server->certinfo.empty ())
1782 {
1783 // We need not contact the server if it has already been processed.
1784 if (find (processed_certs.begin (), processed_certs.end (),
1785 server->certinfo) != processed_certs.end ())
1786 continue;
1787 processed_certs.push_back (server->certinfo);
1788
1789 // We need not contact the server if it is already trusted.
1790 if (find (already_trusted.begin (), already_trusted.end (), *server) !=
1791 already_trusted.end ())
1792 {
1793 if (s.verbose >= 2)
1794 trust_already_in_place (*server, server_list, cert_db_path, false/*revoking*/);
1795 continue;
1796 }
1797 }
1798
1799 // At a minimum we need an ip_address along with a port
1800 // number in order to contact the server.
1801 if (! server->hasAddress() || server->port == 0)
1802 continue;
1803 // Set the port within the address.
1804 server->setAddressPort (server->port);
1805
1806 int rc = client_connect (*server, NULL, NULL, "permanent");
1807 if (rc != SUCCESS)
1808 {
1809 clog << _F("Unable to connect to %s", lex_cast(*server).c_str()) << endl;
1810 nssError ();
1811 // Additional information: if the address is IPv6 and is link-local, then it must
1812 // have a scope_id.
1813 if (isIPv6LinkLocal (server->address) && server->address.ipv6.scope_id == 0)
1814 {
1815 clog << _(" The address is an IPv6 link-local address with no scope specifier.")
1816 << endl;
1817 }
1818 }
1819 }
1820
1821 cleanup:
1822 // Shutdown NSS.
1823 // SSL_ClearSessionCache is required before shutdown for client applications.
1824 SSL_ClearSessionCache ();
1825 nssCleanup (cert_db_path.c_str ());
1826
1827 // Make sure the database files are readable.
1828 glob_t globbuf;
1829 string filespec = cert_db_path + "/*.db";
1830 if (s.verbose >= 3)
1831 clog << _F("Searching \"%s\"\n", filespec.c_str());
1832 int r = glob (filespec.c_str (), 0, NULL, & globbuf);
1833 if (r != GLOB_NOSPACE && r != GLOB_ABORTED && r != GLOB_NOMATCH)
1834 {
1835 for (unsigned i = 0; i < globbuf.gl_pathc; ++i)
1836 {
1837 string filename = globbuf.gl_pathv[i];
1838 if (s.verbose >= 3)
1839 clog << _(" found ") << filename << endl;
1840
1841 if (chmod (filename.c_str (), 0644) != 0)
1842 {
1843 s.print_warning("Unable to change permissions on " + filename + ": ");
1844 perror ("");
1845 }
1846 }
1847 }
1848 }
1849
1850 // Remove the given servers from the given database of trusted servers.
1851 static void
1852 revoke_server_trust (
1853 systemtap_session &s,
1854 const string &cert_db_path,
1855 const vector<compile_server_info> &server_list
1856 )
1857 {
1858 // Make sure the given path exists.
1859 if (! file_exists (cert_db_path))
1860 {
1861 if (s.verbose >= 5)
1862 {
1863 clog << _F("Certificate database '%s' does not exist",
1864 cert_db_path.c_str()) << endl;
1865 for (vector<compile_server_info>::const_iterator server = server_list.begin();
1866 server != server_list.end ();
1867 ++server)
1868 trust_already_in_place (*server, server_list, cert_db_path, true/*revoking*/);
1869 }
1870 return;
1871 }
1872
1873 // Must predeclare these because of jumps to cleanup: below.
1874 CERTCertDBHandle *handle;
1875 PRArenaPool *tmpArena = NULL;
1876 CERTCertList *certs = NULL;
1877 CERTCertificate *db_cert;
1878 vector<string> processed_certs;
1879 const char *nickname;
1880
1881 // Make sure NSPR is initialized. Must be done before NSS is initialized
1882 s.NSPR_init ();
1883
1884 // Initialize the NSS libraries -- read/write
1885 SECStatus secStatus = nssInit (cert_db_path.c_str (), 1/*readwrite*/);
1886 if (secStatus != SECSuccess)
1887 {
1888 // Message already issued
1889 goto cleanup;
1890 }
1891 handle = CERT_GetDefaultCertDB();
1892
1893 // A memory pool to work in
1894 tmpArena = PORT_NewArena(DER_DEFAULT_CHUNKSIZE);
1895 if (! tmpArena)
1896 {
1897 clog << _("Out of memory:");
1898 nssError ();
1899 goto cleanup;
1900 }
1901
1902 // Iterate over the servers to become untrusted.
1903 nickname = server_cert_nickname ();
1904 for (vector<compile_server_info>::const_iterator server = server_list.begin();
1905 server != server_list.end ();
1906 ++server)
1907 {
1908 // If the server's certificate serial number is unknown, then we can't
1909 // match it with one in the database.
1910 if (server->certinfo.empty ())
1911 continue;
1912
1913 // Trust is based on certificates. We need only revoke trust in the same
1914 // certificate once.
1915 if (find (processed_certs.begin (), processed_certs.end (),
1916 server->certinfo) != processed_certs.end ())
1917 continue;
1918 processed_certs.push_back (server->certinfo);
1919
1920 // Search the client-side database of trusted servers.
1921 db_cert = PK11_FindCertFromNickname (nickname, NULL);
1922 if (! db_cert)
1923 {
1924 // No trusted servers. Not an error, but issue a status message.
1925 if (s.verbose >= 2)
1926 trust_already_in_place (*server, server_list, cert_db_path, true/*revoking*/);
1927 continue;
1928 }
1929
1930 // Here, we have one cert with the desired nickname.
1931 // Now, we will attempt to get a list of ALL certs
1932 // with the same subject name as the cert we have. That list
1933 // should contain, at a minimum, the one cert we have already found.
1934 // If the list of certs is empty (NULL), the libraries have failed.
1935 certs = CERT_CreateSubjectCertList (NULL, handle, & db_cert->derSubject,
1936 PR_Now (), PR_FALSE);
1937 CERT_DestroyCertificate (db_cert);
1938 if (! certs)
1939 {
1940 clog << _F("Unable to query certificate database %s: ",
1941 cert_db_path.c_str()) << endl;
1942 PORT_SetError (SEC_ERROR_LIBRARY_FAILURE);
1943 nssError ();
1944 goto cleanup;
1945 }
1946
1947 // Find the certificate matching the one belonging to our server.
1948 CERTCertListNode *node;
1949 for (node = CERT_LIST_HEAD (certs);
1950 ! CERT_LIST_END (node, certs);
1951 node = CERT_LIST_NEXT (node))
1952 {
1953 // The certificate we're working with.
1954 db_cert = node->cert;
1955
1956 // Get the serial number.
1957 string serialNumber = get_cert_serial_number (db_cert);
1958
1959 // Does the serial number match that of the current server?
1960 if (serialNumber != server->certinfo)
1961 continue; // goto next certificate
1962
1963 // All is ok! Remove the certificate from the database.
1964 break;
1965 } // Loop over certificates in the database
1966
1967 // Was a certificate matching the server found? */
1968 if (CERT_LIST_END (node, certs))
1969 {
1970 // Not found. Server is already untrusted.
1971 if (s.verbose >= 2)
1972 trust_already_in_place (*server, server_list, cert_db_path, true/*revoking*/);
1973 }
1974 else
1975 {
1976 secStatus = SEC_DeletePermCertificate (db_cert);
1977 if (secStatus != SECSuccess)
1978 {
1979 clog << _F("Unable to remove certificate from %s: ",
1980 cert_db_path.c_str()) << endl;
1981 nssError ();
1982 }
1983 }
1984 CERT_DestroyCertList (certs);
1985 certs = NULL;
1986 } // Loop over servers
1987
1988 cleanup:
1989 assert(!certs);
1990 if (tmpArena)
1991 PORT_FreeArena (tmpArena, PR_FALSE);
1992
1993 nssCleanup (cert_db_path.c_str ());
1994 }
1995
1996 // Obtain information about servers from the certificates in the given database.
1997 static void
1998 get_server_info_from_db (
1999 systemtap_session &s,
2000 vector<compile_server_info> &servers,
2001 const string &cert_db_path
2002 )
2003 {
2004 // Make sure the given path exists.
2005 if (! file_exists (cert_db_path))
2006 {
2007 if (s.verbose >= 5)
2008 clog << _F("Certificate database '%s' does not exist.",
2009 cert_db_path.c_str()) << endl;
2010 return;
2011 }
2012
2013 // Make sure NSPR is initialized. Must be done before NSS is initialized
2014 s.NSPR_init ();
2015
2016 // Initialize the NSS libraries -- readonly
2017 SECStatus secStatus = nssInit (cert_db_path.c_str ());
2018 if (secStatus != SECSuccess)
2019 {
2020 // Message already issued.
2021 return;
2022 }
2023
2024 // Must predeclare this because of jumps to cleanup: below.
2025 PRArenaPool *tmpArena = NULL;
2026 CERTCertList *certs = get_cert_list_from_db (server_cert_nickname ());
2027 if (! certs)
2028 {
2029 if (s.verbose >= 5)
2030 clog << _F("No certificate found in database %s", cert_db_path.c_str ()) << endl;
2031 goto cleanup;
2032 }
2033
2034 // A memory pool to work in
2035 tmpArena = PORT_NewArena(DER_DEFAULT_CHUNKSIZE);
2036 if (! tmpArena)
2037 {
2038 clog << _("Out of memory:");
2039 nssError ();
2040 goto cleanup;
2041 }
2042 for (CERTCertListNode *node = CERT_LIST_HEAD (certs);
2043 ! CERT_LIST_END (node, certs);
2044 node = CERT_LIST_NEXT (node))
2045 {
2046 compile_server_info server_info;
2047
2048 // The certificate we're working with.
2049 CERTCertificate *db_cert = node->cert;
2050
2051 // Get the host name. It is in the alt-name extension of the
2052 // certificate.
2053 SECItem subAltName;
2054 subAltName.data = NULL;
2055 secStatus = CERT_FindCertExtension (db_cert,
2056 SEC_OID_X509_SUBJECT_ALT_NAME,
2057 & subAltName);
2058 if (secStatus != SECSuccess || ! subAltName.data)
2059 {
2060 clog << _("Unable to find alt name extension on server certificate: ") << endl;
2061 nssError ();
2062 continue;
2063 }
2064
2065 // Decode the extension.
2066 CERTGeneralName *nameList = CERT_DecodeAltNameExtension (tmpArena, & subAltName);
2067 SECITEM_FreeItem(& subAltName, PR_FALSE);
2068 if (! nameList)
2069 {
2070 clog << _("Unable to decode alt name extension on server certificate: ") << endl;
2071 nssError ();
2072 continue;
2073 }
2074
2075 // We're interested in the first alternate name.
2076 assert (nameList->type == certDNSName);
2077 server_info.host_name = string ((const char *)nameList->name.other.data,
2078 nameList->name.other.len);
2079 // Don't free nameList. It's part of the tmpArena.
2080
2081 // Get the serial number.
2082 server_info.certinfo = get_cert_serial_number (db_cert);
2083
2084 // Our results will at a minimum contain this server.
2085 add_server_info (server_info, servers);
2086
2087 // Augment the list by querying all online servers and keeping the ones
2088 // with the same cert serial number.
2089 vector<compile_server_info> online_servers;
2090 get_or_keep_online_server_info (s, online_servers, false/*keep*/);
2091 keep_server_info_with_cert_and_port (s, server_info, online_servers);
2092 add_server_info (online_servers, servers);
2093 }
2094
2095 cleanup:
2096 if (certs)
2097 CERT_DestroyCertList (certs);
2098 if (tmpArena)
2099 PORT_FreeArena (tmpArena, PR_FALSE);
2100
2101 nssCleanup (cert_db_path.c_str ());
2102 }
2103
2104 // Utility Functions.
2105 //-----------------------------------------------------------------------
2106 ostream &operator<< (ostream &s, const compile_server_info &i)
2107 {
2108 // Don't print empty information
2109 if (i.empty ())
2110 return s;
2111
2112 s << " host=";
2113 if (! i.host_name.empty ())
2114 s << i.host_name;
2115 else
2116 s << "unknown";
2117 s << " address=";
2118 if (i.hasAddress())
2119 {
2120 PRStatus prStatus;
2121 switch (i.address.raw.family)
2122 {
2123 case PR_AF_INET:
2124 case PR_AF_INET6:
2125 {
2126 #define MAX_NETADDR_SIZE 46 // from the NSPR API reference.
2127 char buf[MAX_NETADDR_SIZE];
2128 prStatus = PR_NetAddrToString(& i.address, buf, sizeof (buf));
2129 if (prStatus == PR_SUCCESS) {
2130 s << buf;
2131 break;
2132 }
2133 }
2134 // Fall through
2135 default:
2136 s << "offline";
2137 break;
2138 }
2139 }
2140 else
2141 s << "offline";
2142 s << " port=";
2143 if (i.port != 0)
2144 s << i.port;
2145 else
2146 s << "unknown";
2147 s << " sysinfo=\"";
2148 if (! i.sysinfo.empty ())
2149 s << i.sysinfo << '"';
2150 else
2151 s << "unknown\"";
2152 s << " version=";
2153 if (! i.version.empty ())
2154 s << i.version;
2155 else
2156 s << "unknown";
2157 s << " certinfo=\"";
2158 if (! i.certinfo.empty ())
2159 s << i.certinfo << '"';
2160 else
2161 s << "unknown\"";
2162 if (! i.mok_fingerprints.empty ())
2163 {
2164 // FIXME: Yikes, this output is ugly. Perhaps the server output
2165 // needs a more structured approach.
2166 s << " mok_fingerprints=\"";
2167 vector<string>::const_iterator it;
2168 for (it = i.mok_fingerprints.begin (); it != i.mok_fingerprints.end ();
2169 it++)
2170 {
2171 if (it != i.mok_fingerprints.begin ())
2172 s << ", ";
2173 s << *it;
2174 }
2175 s << "\"";
2176 }
2177 return s;
2178 }
2179
2180 ostream &operator<< (ostream &s, const vector<compile_server_info> &v)
2181 {
2182 // Indicate an empty list.
2183 if (v.size () == 0 || (v.size () == 1 && v[0].empty()))
2184 s << "No Servers" << endl;
2185 else
2186 {
2187 for (unsigned i = 0; i < v.size(); ++i)
2188 {
2189 // Don't print empty items.
2190 if (! v[i].empty())
2191 s << v[i] << endl;
2192 }
2193 }
2194 return s;
2195 }
2196
2197 PRNetAddr &
2198 copyNetAddr (PRNetAddr &x, const PRNetAddr &y)
2199 {
2200 PRUint32 saveScope = 0;
2201
2202 // For IPv6 addresses, don't overwrite the scope_id of x unless x is uninitialized or it is 0.
2203 if (x.raw.family == PR_AF_INET6)
2204 saveScope = x.ipv6.scope_id;
2205
2206 x = y;
2207
2208 if (saveScope != 0)
2209 x.ipv6.scope_id = saveScope;
2210
2211 return x;
2212 }
2213
2214 bool
2215 operator== (const PRNetAddr &x, const PRNetAddr &y)
2216 {
2217 // Same address family?
2218 if (x.raw.family != y.raw.family)
2219 return false;
2220
2221 switch (x.raw.family)
2222 {
2223 case PR_AF_INET6:
2224 // If both scope ids are set, compare them.
2225 if (x.ipv6.scope_id != 0 && y.ipv6.scope_id != 0 && x.ipv6.scope_id != y.ipv6.scope_id)
2226 return false; // not equal
2227 // Scope is not a factor. Compare the address bits
2228 return memcmp (& x.ipv6.ip, & y.ipv6.ip, sizeof(x.ipv6.ip)) == 0;
2229 case PR_AF_INET:
2230 return x.inet.ip == y.inet.ip;
2231 default:
2232 break;
2233 }
2234 return false;
2235 }
2236
2237 bool
2238 operator!= (const PRNetAddr &x, const PRNetAddr &y)
2239 {
2240 return !(x == y);
2241 }
2242
2243 static PRIPv6Addr &
2244 copyAddress (PRIPv6Addr &PRin6, const in6_addr &in6)
2245 {
2246 // The NSPR type is a typedef of struct in6_addr, but C++ won't let us copy it
2247 assert (sizeof (PRin6) == sizeof (in6));
2248 memcpy (& PRin6, & in6, sizeof (PRin6));
2249 return PRin6;
2250 }
2251
2252 // Return the default server specification, used when none is given on the
2253 // command line.
2254 static string
2255 default_server_spec (const systemtap_session &s)
2256 {
2257 // If --privilege=X has been used, where X is not stapdev,
2258 // the default is online,trusted,compatible,signer
2259 // otherwise
2260 // the default is online,trusted,compatible
2261 //
2262 // Having said that,
2263 // 'online' and 'compatible' will only succeed if we have avahi
2264 // 'trusted' and 'signer' will only succeed if we have NSS
2265 //
2266 string working_string = "online,trusted,compatible";
2267 if (! pr_contains (s.privilege, pr_stapdev))
2268 working_string += ",signer";
2269 return working_string;
2270 }
2271
2272 static int
2273 server_spec_to_pmask (const string &server_spec)
2274 {
2275 // Construct a mask of the server properties that have been requested.
2276 // The available properties are:
2277 // trusted - servers which are trusted SSL peers.
2278 // online - online servers.
2279 // compatible - servers which compile for the current kernel release
2280 // and architecture.
2281 // signer - servers which are trusted module signers.
2282 // specified - servers which have been specified using --use-server=XXX.
2283 // If no servers have been specified, then this is
2284 // equivalent to --list-servers=trusted,online,compatible.
2285 // all - all trusted servers, trusted module signers,
2286 // servers currently online and specified servers.
2287 string working_spec = server_spec;
2288 vector<string> properties;
2289 tokenize (working_spec, properties, ",");
2290 int pmask = 0;
2291 unsigned limit = properties.size ();
2292 for (unsigned i = 0; i < limit; ++i)
2293 {
2294 const string &property = properties[i];
2295 // Tolerate (and ignore) empty properties.
2296 if (property.empty ())
2297 continue;
2298 if (property == "all")
2299 {
2300 pmask |= compile_server_all;
2301 }
2302 else if (property == "specified")
2303 {
2304 pmask |= compile_server_specified;
2305 }
2306 else if (property == "trusted")
2307 {
2308 pmask |= compile_server_trusted;
2309 }
2310 else if (property == "online")
2311 {
2312 pmask |= compile_server_online;
2313 }
2314 else if (property == "compatible")
2315 {
2316 pmask |= compile_server_compatible;
2317 }
2318 else if (property == "signer")
2319 {
2320 pmask |= compile_server_signer;
2321 }
2322 else
2323 {
2324 // XXX PR13274 needs-session to use print_warning()
2325 clog << _F("WARNING: unsupported compile server property: %s", property.c_str())
2326 << endl;
2327 }
2328 }
2329 return pmask;
2330 }
2331
2332 void
2333 query_server_status (systemtap_session &s)
2334 {
2335 unsigned limit = s.server_status_strings.size ();
2336 for (unsigned i = 0; i < limit; ++i)
2337 query_server_status (s, s.server_status_strings[i]);
2338 }
2339
2340 static void
2341 query_server_status (systemtap_session &s, const string &status_string)
2342 {
2343 // If this string is empty, then the default is "specified"
2344 string working_string = status_string;
2345 if (working_string.empty ())
2346 working_string = "specified";
2347
2348 // If the query is "specified" and no servers have been specified
2349 // (i.e. --use-server not used or used with no argument), then
2350 // use the default query.
2351 // TODO: This may not be necessary. The underlying queries should handle
2352 // "specified" properly.
2353 if (working_string == "specified" &&
2354 (s.specified_servers.empty () ||
2355 (s.specified_servers.size () == 1 && s.specified_servers[0].empty ())))
2356 working_string = default_server_spec (s);
2357
2358 int pmask = server_spec_to_pmask (working_string);
2359
2360 // Now obtain a list of the servers which match the criteria.
2361 vector<compile_server_info> raw_servers;
2362 get_server_info (s, pmask, raw_servers);
2363
2364 // Augment the listing with as much information as possible by adding
2365 // information from known servers.
2366 vector<compile_server_info> servers;
2367 get_all_server_info (s, servers);
2368 keep_common_server_info (raw_servers, servers);
2369
2370 // Sort the list of servers into a preferred order.
2371 preferred_order (servers);
2372
2373 // Print the server information. Skip the empty entry at the head of the list.
2374 clog << _F("Systemtap Compile Server Status for '%s'", working_string.c_str()) << endl;
2375 bool found = false;
2376 unsigned limit = servers.size ();
2377 for (unsigned i = 0; i < limit; ++i)
2378 {
2379 assert (! servers[i].empty ());
2380 // Don't list servers with no cert information. They may not actually
2381 // exist.
2382 // TODO: Could try contacting the server and obtaining its cert
2383 if (servers[i].certinfo.empty ())
2384 continue;
2385 clog << servers[i] << endl;
2386 found = true;
2387 }
2388 if (! found)
2389 clog << _("No servers found") << endl;
2390 }
2391
2392 // Add or remove trust of the servers specified on the command line.
2393 void
2394 manage_server_trust (systemtap_session &s)
2395 {
2396 // This function should do nothing if we don't have NSS.
2397 // Nothing to do if --trust-servers was not specified.
2398 if (s.server_trust_spec.empty ())
2399 return;
2400
2401 // Break up and analyze the trust specification. Recognized components are:
2402 // ssl - trust the specified servers as ssl peers
2403 // signer - trust the specified servers as module signers
2404 // revoke - revoke the requested trust
2405 // all-users - apply/revoke the requested trust for all users
2406 // no-prompt - don't prompt the user for confirmation
2407 vector<string>components;
2408 tokenize (s.server_trust_spec, components, ",");
2409 bool ssl = false;
2410 bool signer = false;
2411 bool revoke = false;
2412 bool all_users = false;
2413 bool no_prompt = false;
2414 bool error = false;
2415 for (vector<string>::const_iterator i = components.begin ();
2416 i != components.end ();
2417 ++i)
2418 {
2419 if (*i == "ssl")
2420 ssl = true;
2421 else if (*i == "signer")
2422 {
2423 if (geteuid () != 0)
2424 {
2425 clog << _("Only root can specify 'signer' on --trust-servers") << endl;
2426 error = true;
2427 }
2428 else
2429 signer = true;
2430 }
2431 else if (*i == "revoke")
2432 revoke = true;
2433 else if (*i == "all-users")
2434 {
2435 if (geteuid () != 0)
2436 {
2437 clog << _("Only root can specify 'all-users' on --trust-servers") << endl;
2438 error = true;
2439 }
2440 else
2441 all_users = true;
2442 }
2443 else if (*i == "no-prompt")
2444 no_prompt = true;
2445 else
2446 s.print_warning("Unrecognized server trust specification: " + *i);
2447 }
2448 if (error)
2449 return;
2450
2451 // Make sure NSPR is initialized
2452 s.NSPR_init ();
2453
2454 // Now obtain the list of specified servers.
2455 vector<compile_server_info> server_list;
2456 get_specified_server_info (s, server_list, true/*no_default*/);
2457
2458 // Did we identify any potential servers?
2459 unsigned limit = server_list.size ();
2460 if (limit == 0)
2461 {
2462 clog << _("No servers identified for trust") << endl;
2463 return;
2464 }
2465
2466 // Create a string representing the request in English.
2467 // If neither 'ssl' or 'signer' was specified, the default is 'ssl'.
2468 if (! ssl && ! signer)
2469 ssl = true;
2470 ostringstream trustString;
2471 if (ssl)
2472 {
2473 trustString << _("as an SSL peer");
2474 if (all_users)
2475 trustString << _(" for all users");
2476 else
2477 trustString << _(" for the current user");
2478 }
2479 if (signer)
2480 {
2481 if (ssl)
2482 trustString << _(" and ");
2483 trustString << _("as a module signer for all users");
2484 }
2485
2486 // Prompt the user to confirm what's about to happen.
2487 if (no_prompt)
2488 {
2489 if (revoke)
2490 clog << _("Revoking trust ");
2491 else
2492 clog << _("Adding trust ");
2493 }
2494 else
2495 {
2496 if (revoke)
2497 clog << _("Revoke trust ");
2498 else
2499 clog << _("Add trust ");
2500 }
2501 clog << _F("in the following servers %s", trustString.str().c_str());
2502 if (! no_prompt)
2503 clog << '?';
2504 clog << endl;
2505 for (unsigned i = 0; i < limit; ++i)
2506 clog << " " << server_list[i] << endl;
2507 if (! no_prompt)
2508 {
2509 clog << "[y/N] " << flush;
2510
2511 // Only carry out the operation if the response is "yes"
2512 string response;
2513 cin >> response;
2514 if (response[0] != 'y' && response [0] != 'Y')
2515 {
2516 clog << _("Server trust unchanged") << endl;
2517 return;
2518 }
2519 }
2520
2521 // Now add/revoke the requested trust.
2522 string cert_db_path;
2523 if (ssl)
2524 {
2525 if (all_users)
2526 cert_db_path = global_ssl_cert_db_path ();
2527 else
2528 cert_db_path = private_ssl_cert_db_path ();
2529 if (revoke)
2530 revoke_server_trust (s, cert_db_path, server_list);
2531 else
2532 add_server_trust (s, cert_db_path, server_list);
2533 }
2534 if (signer)
2535 {
2536 cert_db_path = signing_cert_db_path ();
2537 if (revoke)
2538 revoke_server_trust (s, cert_db_path, server_list);
2539 else
2540 add_server_trust (s, cert_db_path, server_list);
2541 }
2542 }
2543
2544 static compile_server_cache*
2545 cscache(systemtap_session& s)
2546 {
2547 if (!s.server_cache)
2548 s.server_cache = new compile_server_cache();
2549 return s.server_cache;
2550 }
2551
2552 static void
2553 get_server_info (
2554 systemtap_session &s,
2555 int pmask,
2556 vector<compile_server_info> &servers
2557 )
2558 {
2559 // Get information on compile servers matching the requested criteria.
2560 // The order of queries is significant. Accumulating queries must go first
2561 // followed by accumulating/filtering queries.
2562 bool keep = false;
2563 if (((pmask & compile_server_all)))
2564 {
2565 get_all_server_info (s, servers);
2566 keep = true;
2567 }
2568 // Add the specified servers, if requested
2569 if ((pmask & compile_server_specified))
2570 {
2571 get_specified_server_info (s, servers);
2572 keep = true;
2573 }
2574 // Now filter or accumulate the list depending on whether a query has
2575 // already been made.
2576 if ((pmask & compile_server_online))
2577 {
2578 get_or_keep_online_server_info (s, servers, keep);
2579 keep = true;
2580 }
2581 if ((pmask & compile_server_trusted))
2582 {
2583 get_or_keep_trusted_server_info (s, servers, keep);
2584 keep = true;
2585 }
2586 if ((pmask & compile_server_signer))
2587 {
2588 get_or_keep_signing_server_info (s, servers, keep);
2589 keep = true;
2590 }
2591 if ((pmask & compile_server_compatible))
2592 {
2593 get_or_keep_compatible_server_info (s, servers, keep);
2594 keep = true;
2595 }
2596 }
2597
2598 // Get information about all online servers as well as servers trusted
2599 // as SSL peers and servers trusted as signers.
2600 static void
2601 get_all_server_info (
2602 systemtap_session &s,
2603 vector<compile_server_info> &servers
2604 )
2605 {
2606 // We only need to obtain this once per session. This is a good thing(tm)
2607 // since obtaining this information is expensive.
2608 vector<compile_server_info>& all_servers = cscache(s)->all_servers;
2609 if (all_servers.empty ())
2610 {
2611 get_or_keep_online_server_info (s, all_servers, false/*keep*/);
2612 get_or_keep_trusted_server_info (s, all_servers, false/*keep*/);
2613 get_or_keep_signing_server_info (s, all_servers, false/*keep*/);
2614
2615 if (s.verbose >= 4)
2616 {
2617 clog << _("All known servers:") << endl;
2618 clog << all_servers;
2619 }
2620 }
2621
2622 // Add the information, but not duplicates.
2623 add_server_info (all_servers, servers);
2624 }
2625
2626 static void
2627 get_default_server_info (
2628 systemtap_session &s,
2629 vector<compile_server_info> &servers
2630 )
2631 {
2632 if (s.verbose >= 3)
2633 clog << _("Using the default servers") << endl;
2634
2635 // We only need to obtain this once per session. This is a good thing(tm)
2636 // since obtaining this information is expensive.
2637 vector<compile_server_info>& default_servers = cscache(s)->default_servers;
2638 if (default_servers.empty ())
2639 {
2640 // Get the required information.
2641 // get_server_info will add an empty entry at the beginning to indicate
2642 // that the search has been performed, in case the search comes up empty.
2643 int pmask = server_spec_to_pmask (default_server_spec (s));
2644 get_server_info (s, pmask, default_servers);
2645
2646 if (s.verbose >= 3)
2647 {
2648 clog << _("Default servers are:") << endl;
2649 clog << default_servers;
2650 }
2651 }
2652
2653 // Add the information, but not duplicates.
2654 add_server_info (default_servers, servers);
2655 }
2656
2657 static bool
2658 isPort (const char *pstr, compile_server_info &server_info)
2659 {
2660 errno = 0;
2661 char *estr;
2662 unsigned long p = strtoul (pstr, & estr, 10);
2663 if (errno != 0 || *estr != '\0' || p > USHRT_MAX)
2664 {
2665 clog << _F("Invalid port number specified: %s", pstr) << endl;
2666 return false;
2667 }
2668 server_info.port = p;
2669 server_info.fully_specified = true;
2670 return true;
2671 }
2672
2673 static bool
2674 isIPv6 (const string &server, compile_server_info &server_info)
2675 {
2676 // An IPv6 address is 8 hex components separated by colons.
2677 // One contiguous block of zero segments in the address may be elided using ::.
2678 // An interface may be specified by appending %IF_NAME to the address (e.g. %eth0).
2679 // A port may be specified by enclosing the ip address in [] and adding :<port>.
2680 // Allow a bracketed address without a port.
2681 assert (! server.empty());
2682 string ip;
2683 string::size_type portIx;
2684 if (server[0] == '[')
2685 {
2686 string::size_type endBracket = server.find (']');
2687 if (endBracket == string::npos)
2688 return false; // Not a valid IPv6 address
2689 // Extract the address.
2690 ip = server.substr (1, endBracket - 1);
2691 portIx = endBracket + 1;
2692 }
2693 else
2694 {
2695 ip = server;
2696 portIx = string::npos;
2697 }
2698
2699 // Find out how many components there are. The maximum is 8
2700 unsigned empty = 0;
2701 vector<string> components;
2702 tokenize_full (ip, components, ":");
2703 if (components.size() > 8)
2704 return false; // Not a valid IPv6 address
2705
2706 // The components must be either hex values between 0 and 0xffff, or must be empty.
2707 // There can be only one empty component.
2708 string interface;
2709 for (unsigned i = 0; i < components.size(); ++i)
2710 {
2711 if (components[i].empty())
2712 {
2713 if (++empty > 1)
2714 return false; // Not a valid IPv6 address
2715 }
2716 // If it's the final component, see if it specifies the interface. If so, strip it from the
2717 // component in order to simplify parsing. It still remains as part of the original ip address
2718 // string.
2719 if (i == components.size() - 1)
2720 {
2721 size_t ix = components[i].find ('%');
2722 if (ix != string::npos)
2723 {
2724 interface = components[i].substr(ix);
2725 components[i] = components[i].substr(0, ix);
2726 }
2727 }
2728 // Skip leading zeroes.
2729 unsigned j;
2730 for (j = 0; j < components[i].size(); ++j)
2731 {
2732 if (components[i][j] != '0')
2733 break;
2734 }
2735 // Max of 4 hex digits
2736 if (components[i].size() - j > 4)
2737 return false; // Not a valid IPv6 address
2738 for (/**/; j < components[i].size(); ++j)
2739 {
2740 if (! isxdigit (components[i][j]))
2741 return false; // Not a valid IPv6 address
2742 }
2743 }
2744 // If there is no empty component, then there must be exactly 8 components.
2745 if (! empty && components.size() != 8)
2746 return false; // Not a valid IPv6 address
2747
2748 // Try to convert the string to an address.
2749 PRStatus prStatus = PR_StringToNetAddr (ip.c_str(), & server_info.address);
2750 if (prStatus != PR_SUCCESS)
2751 return false;
2752
2753 // Examine the optional port
2754 if (portIx != string::npos)
2755 {
2756 string port = server.substr (portIx);
2757 if (port.size() != 0)
2758 {
2759 if (port.size() < 2 || port[0] != ':')
2760 return false; // Not a valid Port
2761
2762 port = port.substr (1);
2763 if (! isPort (port.c_str(), server_info))
2764 return false; // not a valid port
2765 }
2766 }
2767 else
2768 server_info.port = 0;
2769
2770 return true; // valid IPv6 address.
2771 }
2772
2773 static bool
2774 isIPv4 (const string &server, compile_server_info &server_info)
2775 {
2776 // An IPv4 address is 4 decimal components separated by periods with an
2777 // additional optional decimal port separated from the address by a colon.
2778 assert (! server.empty());
2779
2780 // Find out how many components there are. The maximum is 8
2781 vector<string> components;
2782 tokenize (server, components, ":");
2783 if (components.size() > 2)
2784 return false; // Not a valid IPv4 address
2785
2786 // Separate the address from the port (if any).
2787 string addr;
2788 string port;
2789 if (components.size() <= 1)
2790 addr = server;
2791 else {
2792 addr = components[0];
2793 port = components[1];
2794 }
2795
2796 // Separate the address components.
2797 // There must be exactly 4 components.
2798 components.clear ();
2799 tokenize (addr, components, ".");
2800 if (components.size() != 4)
2801 return false; // Not a valid IPv4 address
2802
2803 // The components must be decimal values between 0 and 255.
2804 for (unsigned i = 0; i < components.size(); ++i)
2805 {
2806 if (components[i].empty())
2807 return false; // Not a valid IPv4 address
2808 errno = 0;
2809 char *estr;
2810 long p = strtol (components[i].c_str(), & estr, 10);
2811 if (errno != 0 || *estr != '\0' || p < 0 || p > 255)
2812 return false; // Not a valid IPv4 address
2813 }
2814
2815 // Try to convert the string to an address.
2816 PRStatus prStatus = PR_StringToNetAddr (addr.c_str(), & server_info.address);
2817 if (prStatus != PR_SUCCESS)
2818 return false;
2819
2820 // Examine the optional port
2821 if (! port.empty ()) {
2822 if (! isPort (port.c_str(), server_info))
2823 return false; // not a valid port
2824 }
2825 else
2826 server_info.port = 0;
2827
2828 return true; // valid IPv4 address.
2829 }
2830
2831 static bool
2832 isCertSerialNumber (const string &server, compile_server_info &server_info)
2833 {
2834 // This function assumes that we have already ruled out the server spec being an IPv6 address.
2835 // Certificate serial numbers are 5 fields separated by colons plus an optional 6th decimal
2836 // field specifying a port.
2837 assert (! server.empty());
2838 string host = server;
2839 vector<string> components;
2840 tokenize (host, components, ":");
2841 switch (components.size ())
2842 {
2843 case 6:
2844 if (! isPort (components.back().c_str(), server_info))
2845 return false; // not a valid port
2846 host = host.substr (0, host.find_last_of (':'));
2847 // fall through
2848 case 5:
2849 server_info.certinfo = host;
2850 break;
2851 default:
2852 return false; // not a cert serial number
2853 }
2854
2855 return true; // valid cert serial number and optional port
2856 }
2857
2858 static bool
2859 isDomain (const string &server, compile_server_info &server_info)
2860 {
2861 // Accept one or two components separated by a colon. The first will be the domain name and
2862 // the second must a port number.
2863 assert (! server.empty());
2864 string host = server;
2865 vector<string> components;
2866 tokenize (host, components, ":");
2867 switch (components.size ())
2868 {
2869 case 2:
2870 if (! isPort (components.back().c_str(), server_info))
2871 return false; // not a valid port
2872 host = host.substr (0, host.find_last_of (':'));
2873 // fall through
2874 case 1:
2875 server_info.host_name = host;
2876 break;
2877 default:
2878 return false; // not a valid domain name
2879 }
2880
2881 return true;
2882 }
2883
2884 static void
2885 get_specified_server_info (
2886 systemtap_session &s,
2887 vector<compile_server_info> &servers,
2888 bool no_default
2889 )
2890 {
2891 // We only need to obtain this once per session. This is a good thing(tm)
2892 // since obtaining this information is expensive.
2893 vector<compile_server_info>& specified_servers = cscache(s)->specified_servers;
2894 if (specified_servers.empty ())
2895 {
2896 // Maintain an empty entry to indicate that this search has been
2897 // performed, in case the search comes up empty.
2898 specified_servers.push_back (compile_server_info ());
2899
2900 // If --use-server was not specified at all, then return info for the
2901 // default server list.
2902 if (s.specified_servers.empty ())
2903 {
2904 if (s.verbose >= 3)
2905 clog << _("No servers specified") << endl;
2906 if (! no_default)
2907 get_default_server_info (s, specified_servers);
2908 }
2909 else
2910 {
2911 // Iterate over the specified servers. For each specification, add to
2912 // the list of servers.
2913 unsigned num_specified_servers = s.specified_servers.size ();
2914 for (unsigned i = 0; i < num_specified_servers; ++i)
2915 {
2916 string &server = s.specified_servers[i];
2917
2918 // If no specific server(s) specified, then use the default servers.
2919 if (server.empty ())
2920 {
2921 if (s.verbose >= 3)
2922 clog << _("No servers specified") << endl;
2923 if (! no_default)
2924 get_default_server_info (s, specified_servers);
2925 continue;
2926 }
2927
2928 // Determine what has been specified. Servers may be specified by:
2929 // - domain{:port}
2930 // - certificate-serial-number{:port}
2931 // - IPv4-address{:port}
2932 // - IPv6-address{:port}
2933 // where items within {} are optional.
2934 // Check for IPv6 addresses first. It reduces the amount of checking necessary for
2935 // certificate serial numbers.
2936 compile_server_info server_info;
2937 vector<compile_server_info> resolved_servers;
2938 if (isIPv6 (server, server_info) || isIPv4 (server, server_info) ||
2939 isCertSerialNumber (server, server_info))
2940 {
2941 // An address or cert serial number has been specified.
2942 // No resolution is needed.
2943 resolved_servers.push_back (server_info);
2944 }
2945 else if (isDomain (server, server_info))
2946 {
2947 // Try to resolve the given name.
2948 resolve_host (s, server_info, resolved_servers);
2949 }
2950 else
2951 {
2952 clog << _F("Invalid server specification for --use-server: %s", server.c_str())
2953 << endl;
2954 continue;
2955 }
2956
2957 // Now examine the server(s) identified and add them to the list of specified
2958 // servers.
2959 vector<compile_server_info> known_servers;
2960 vector<compile_server_info> new_servers;
2961 for (vector<compile_server_info>::iterator i = resolved_servers.begin();
2962 i != resolved_servers.end();
2963 ++i)
2964 {
2965 // If this item was fully specified, then just add it.
2966 if (i->fully_specified)
2967 add_server_info (*i, new_servers);
2968 else {
2969 // It was not fully specified, so we need additional info. Try
2970 // to get it by matching what we have against other known servers.
2971 if (known_servers.empty ())
2972 get_all_server_info (s, known_servers);
2973
2974 // See if this server spec matches that of a known server
2975 vector<compile_server_info> matched_servers = known_servers;
2976 keep_common_server_info (*i, matched_servers);
2977
2978 // If this server spec matches one or more known servers, then add the
2979 // augmented info to the specified_servers. Otherwise, if this server
2980 // spec is complete, then add it directly. Otherwise this server spec
2981 // is incomplete.
2982 if (! matched_servers.empty())
2983 add_server_info (matched_servers, new_servers);
2984 else if (i->isComplete ())
2985 add_server_info (*i, new_servers);
2986 else if (s.verbose >= 3)
2987 clog << _("Incomplete server spec: ") << *i << endl;
2988 }
2989 }
2990
2991 if (s.verbose >= 3)
2992 {
2993 clog << _F("Servers matching %s: ", server.c_str()) << endl;
2994 clog << new_servers;
2995 }
2996
2997 // Add the newly identified servers to the list.
2998 if (! new_servers.empty())
2999 add_server_info (new_servers, specified_servers);
3000 } // Loop over --use-server options
3001 } // -- use-server specified
3002
3003 if (s.verbose >= 2)
3004 {
3005 clog << _("All specified servers:") << endl;
3006 clog << specified_servers;
3007 }
3008 } // Server information is not cached
3009
3010 // Add the information, but not duplicates.
3011 add_server_info (specified_servers, servers);
3012 }
3013
3014 static void
3015 get_or_keep_trusted_server_info (
3016 systemtap_session &s,
3017 vector<compile_server_info> &servers,
3018 bool keep
3019 )
3020 {
3021 // If we're filtering the list and it's already empty, then
3022 // there's nothing to do.
3023 if (keep && servers.empty ())
3024 return;
3025
3026 // We only need to obtain this once per session. This is a good thing(tm)
3027 // since obtaining this information is expensive.
3028 vector<compile_server_info>& trusted_servers = cscache(s)->trusted_servers;
3029 if (trusted_servers.empty ())
3030 {
3031 // Maintain an empty entry to indicate that this search has been
3032 // performed, in case the search comes up empty.
3033 trusted_servers.push_back (compile_server_info ());
3034
3035 // Check the private database first.
3036 string cert_db_path = private_ssl_cert_db_path ();
3037 get_server_info_from_db (s, trusted_servers, cert_db_path);
3038
3039 // Now check the global database.
3040 cert_db_path = global_ssl_cert_db_path ();
3041 get_server_info_from_db (s, trusted_servers, cert_db_path);
3042
3043 if (s.verbose >= 5)
3044 {
3045 clog << _("All servers trusted as ssl peers:") << endl;
3046 clog << trusted_servers;
3047 }
3048 } // Server information is not cached
3049
3050 if (keep)
3051 {
3052 // Filter the existing vector by keeping the information in common with
3053 // the trusted_server vector.
3054 keep_common_server_info (trusted_servers, servers);
3055 }
3056 else
3057 {
3058 // Add the information, but not duplicates.
3059 add_server_info (trusted_servers, servers);
3060 }
3061 }
3062
3063 static void
3064 get_or_keep_signing_server_info (
3065 systemtap_session &s,
3066 vector<compile_server_info> &servers,
3067 bool keep
3068 )
3069 {
3070 // If we're filtering the list and it's already empty, then
3071 // there's nothing to do.
3072 if (keep && servers.empty ())
3073 return;
3074
3075 // We only need to obtain this once per session. This is a good thing(tm)
3076 // since obtaining this information is expensive.
3077 vector<compile_server_info>& signing_servers = cscache(s)->signing_servers;
3078 if (signing_servers.empty ())
3079 {
3080 // Maintain an empty entry to indicate that this search has been
3081 // performed, in case the search comes up empty.
3082 signing_servers.push_back (compile_server_info ());
3083
3084 // For all users, check the global database.
3085 string cert_db_path = signing_cert_db_path ();
3086 get_server_info_from_db (s, signing_servers, cert_db_path);
3087
3088 if (s.verbose >= 5)
3089 {
3090 clog << _("All servers trusted as module signers:") << endl;
3091 clog << signing_servers;
3092 }
3093 } // Server information is not cached
3094
3095 if (keep)
3096 {
3097 // Filter the existing vector by keeping the information in common with
3098 // the signing_server vector.
3099 keep_common_server_info (signing_servers, servers);
3100 }
3101 else
3102 {
3103 // Add the information, but not duplicates.
3104 add_server_info (signing_servers, servers);
3105 }
3106 }
3107
3108
3109 static void
3110 get_or_keep_compatible_server_info (
3111 systemtap_session &s,
3112 vector<compile_server_info> &servers,
3113 bool keep
3114 )
3115 {
3116 #if HAVE_AVAHI
3117 // If we're filtering the list and it's already empty, then
3118 // there's nothing to do.
3119 if (keep && servers.empty ())
3120 return;
3121
3122 // Remove entries for servers incompatible with the host environment
3123 // from the given list of servers.
3124 // A compatible server compiles for the kernel release and architecture
3125 // of the host environment.
3126 //
3127 // Compatibility can only be determined for online servers. So, augment
3128 // and filter the information we have with information for online servers.
3129 vector<compile_server_info> online_servers;
3130 get_or_keep_online_server_info (s, online_servers, false/*keep*/);
3131 if (keep)
3132 keep_common_server_info (online_servers, servers);
3133 else
3134 add_server_info (online_servers, servers);
3135
3136 // Now look to see which ones are compatible.
3137 // The vector can change size as we go, so be careful!!
3138 for (unsigned i = 0; i < servers.size (); /**/)
3139 {
3140 // Retain empty entries.
3141 assert (! servers[i].empty ());
3142
3143 // Check the target of the server.
3144 if (servers[i].sysinfo != s.kernel_release + " " + s.architecture)
3145 {
3146 // Target platform mismatch.
3147 servers.erase (servers.begin () + i);
3148 continue;
3149 }
3150
3151 // If the client requires secure boot signing, make sure the
3152 // server has the right MOK.
3153 if (! s.mok_fingerprints.empty ())
3154 {
3155 // This server has no MOKs.
3156 if (servers[i].mok_fingerprints.empty ())
3157 {
3158 servers.erase (servers.begin () + i);
3159 continue;
3160 }
3161
3162 // Make sure the server has at least one MOK in common with
3163 // the client.
3164 vector<string>::const_iterator it;
3165 bool mok_found = false;
3166 for (it = s.mok_fingerprints.begin(); it != s.mok_fingerprints.end(); it++)
3167 {
3168 if (find(servers[i].mok_fingerprints.begin(),
3169 servers[i].mok_fingerprints.end(), *it)
3170 != servers[i].mok_fingerprints.end ())
3171 {
3172 mok_found = true;
3173 break;
3174 }
3175 }
3176
3177 // This server has no MOK in common with the client.
3178 if (! mok_found)
3179 {
3180 servers.erase (servers.begin () + i);
3181 continue;
3182 }
3183 }
3184
3185 // The server is compatible. Leave it in the list.
3186 ++i;
3187 }
3188 #else // ! HAVE_AVAHI
3189 // Without Avahi, we can't obtain the target platform of the server.
3190 // Issue a warning.
3191 if (s.verbose >= 2)
3192 clog << _("Unable to detect server compatibility without avahi") << endl;
3193 if (keep)
3194 servers.clear ();
3195 #endif
3196 }
3197
3198 static void
3199 keep_server_info_with_cert_and_port (
3200 systemtap_session &,
3201 const compile_server_info &server,
3202 vector<compile_server_info> &servers
3203 )
3204 {
3205 assert (! server.certinfo.empty ());
3206
3207 // Search the list of servers for ones matching the
3208 // serial number specified.
3209 // The vector can change size as we go, so be careful!!
3210 for (unsigned i = 0; i < servers.size (); /**/)
3211 {
3212 // Retain empty entries.
3213 if (servers[i].empty ())
3214 {
3215 ++i;
3216 continue;
3217 }
3218 if (servers[i].certinfo == server.certinfo &&
3219 (servers[i].port == 0 || server.port == 0 ||
3220 servers[i].port == server.port))
3221 {
3222 // If the server is not online, then use the specified
3223 // port, if any.
3224 if (servers[i].port == 0)
3225 {
3226 servers[i].port = server.port;
3227 servers[i].fully_specified = server.fully_specified;
3228 }
3229 ++i;
3230 continue;
3231 }
3232 // The item does not match. Delete it.
3233 servers.erase (servers.begin () + i);
3234 }
3235 }
3236
3237 // Obtain missing host name or ip address, if any. Return 0 on success.
3238 static void
3239 resolve_host (
3240 systemtap_session& s,
3241 compile_server_info &server,
3242 vector<compile_server_info> &resolved_servers
3243 )
3244 {
3245 vector<resolved_host>& cached_hosts = cscache(s)->resolved_hosts[server.host_name];
3246 if (cached_hosts.empty ())
3247 {
3248 // The server's host_name member is a string containing either a host name or an ip address.
3249 // Either is acceptable for lookup.
3250 const char *lookup_name = server.host_name.c_str();
3251 if (s.verbose >= 6)
3252 clog << _F("Looking up %s", lookup_name) << endl;
3253
3254 struct addrinfo hints;
3255 memset(& hints, 0, sizeof (hints));
3256 hints.ai_family = AF_UNSPEC; // AF_INET or AF_INET6 to force version
3257 struct addrinfo *addr_info = 0;
3258 int rc = getaddrinfo (lookup_name, NULL, & hints, & addr_info);
3259
3260 // Failure to resolve will result in an appropriate message later, if other methods fail.
3261 if (rc != 0)
3262 {
3263 if (s.verbose >= 6)
3264 clog << _F("%s not found: %s", lookup_name, gai_strerror (rc)) << endl;
3265 }
3266 else
3267 {
3268 // Loop over the results collecting information.
3269 assert (addr_info);
3270 for (const struct addrinfo *ai = addr_info; ai != NULL; ai = ai->ai_next)
3271 {
3272 PRNetAddr new_address;
3273
3274 // We support IPv4 and IPv6, Ignore other protocols,
3275 if (ai->ai_family == AF_INET)
3276 {
3277 // IPv4 Address
3278 struct sockaddr_in *ip = (struct sockaddr_in *)ai->ai_addr;
3279 new_address.inet.family = PR_AF_INET;
3280 new_address.inet.ip = ip->sin_addr.s_addr;
3281 }
3282 else if (ai->ai_family == AF_INET6)
3283 {
3284 // IPv6 Address
3285 struct sockaddr_in6 *ip = (struct sockaddr_in6 *)ai->ai_addr;
3286 new_address.ipv6.family = PR_AF_INET6;
3287 new_address.ipv6.scope_id = ip->sin6_scope_id;
3288 copyAddress (new_address.ipv6.ip, ip->sin6_addr);
3289 }
3290 else
3291 continue;
3292
3293 // Try to obtain a host name. Otherwise, leave it empty.
3294 char hbuf[NI_MAXHOST];
3295 int status = getnameinfo (ai->ai_addr, ai->ai_addrlen, hbuf, sizeof (hbuf), NULL, 0,
3296 NI_NAMEREQD | NI_IDN);
3297 if (status != 0)
3298 hbuf[0] = '\0';
3299
3300 cached_hosts.push_back(resolved_host(hbuf, new_address));
3301 }
3302 }
3303 if (addr_info)
3304 freeaddrinfo (addr_info); // free the linked list
3305 }
3306
3307 // If no addresses were resolved, then return the info we were given.
3308 if (cached_hosts.empty())
3309 add_server_info (server, resolved_servers);
3310 else {
3311 // We will add a new server for each address resolved
3312 vector<compile_server_info> new_servers;
3313 for (vector<resolved_host>::const_iterator it = cached_hosts.begin();
3314 it != cached_hosts.end(); ++it)
3315 {
3316 // Start with the info we were given
3317 compile_server_info new_server = server;
3318
3319 // NB: do not overwrite port info
3320 if (it->address.raw.family == AF_INET)
3321 {
3322 new_server.address.inet.family = PR_AF_INET;
3323 new_server.address.inet.ip = it->address.inet.ip;
3324 }
3325 else // AF_INET6
3326 {
3327 new_server.address.ipv6.family = PR_AF_INET6;
3328 new_server.address.ipv6.scope_id = it->address.ipv6.scope_id;
3329 new_server.address.ipv6.ip = it->address.ipv6.ip;
3330 }
3331 if (!it->host_name.empty())
3332 new_server.host_name = it->host_name;
3333 add_server_info (new_server, new_servers);
3334 }
3335
3336 if (s.verbose >= 6)
3337 {
3338 clog << _F("%s resolves to:", server.host_name.c_str()) << endl;
3339 clog << new_servers;
3340 }
3341
3342 add_server_info (new_servers, resolved_servers);
3343 }
3344 }
3345
3346 #if HAVE_AVAHI
3347 // Avahi API Callbacks.
3348 //-----------------------------------------------------------------------
3349 struct browsing_context {
3350 AvahiSimplePoll *simple_poll;
3351 AvahiClient *client;
3352 vector<compile_server_info> *servers;
3353 };
3354
3355 // Get the value of the requested key from the Avahi string list.
3356 static string
3357 get_value_from_avahi_string_list (AvahiStringList *strlst, const string &key)
3358 {
3359 AvahiStringList *p = avahi_string_list_find (strlst, key.c_str ());
3360 if (p == NULL)
3361 {
3362 // Key not found.
3363 return "";
3364 }
3365
3366 char *k, *v;
3367 int rc = avahi_string_list_get_pair(p, &k, &v, NULL);
3368 if (rc < 0 || v == NULL)
3369 {
3370 avahi_free (k);
3371 return "";
3372 }
3373
3374 string value = v;
3375 avahi_free (k);
3376 avahi_free (v);
3377 return value;
3378 }
3379
3380 // Get a vector of values of the requested key from the Avahi string
3381 // list. This is for multiple values having the same key.
3382 static void
3383 get_values_from_avahi_string_list (AvahiStringList *strlst, const string &key,
3384 vector<string> &value_vector)
3385 {
3386 AvahiStringList *p;
3387
3388 value_vector.clear();
3389 p = avahi_string_list_find (strlst, key.c_str ());
3390 for (; p != NULL; p = avahi_string_list_get_next(p))
3391 {
3392 char *k, *v;
3393 int rc = avahi_string_list_get_pair(p, &k, &v, NULL);
3394 if (rc < 0 || v == NULL)
3395 {
3396 avahi_free (k);
3397 break;
3398 }
3399
3400 value_vector.push_back(v);
3401 avahi_free (k);
3402 avahi_free (v);
3403 }
3404 return;
3405 }
3406
3407 extern "C"
3408 void resolve_callback(
3409 AvahiServiceResolver *r,
3410 AvahiIfIndex interface,
3411 AvahiProtocol protocol,
3412 AvahiResolverEvent event,
3413 const char *name,
3414 const char *type,
3415 const char *domain,
3416 const char *host_name,
3417 const AvahiAddress *address,
3418 uint16_t port,
3419 AvahiStringList *txt,
3420 AvahiLookupResultFlags /*flags*/,
3421 AVAHI_GCC_UNUSED void* userdata)
3422 {
3423 PRStatus prStatus;
3424
3425 assert(r);
3426 const browsing_context *context = (browsing_context *)userdata;
3427 vector<compile_server_info> *servers = context->servers;
3428
3429 // Called whenever a service has been resolved successfully or timed out.
3430
3431 switch (event) {
3432 case AVAHI_RESOLVER_FAILURE:
3433 clog << _F("Failed to resolve service '%s' of type '%s' in domain '%s': %s",
3434 name, type, domain,
3435 avahi_strerror(avahi_client_errno(avahi_service_resolver_get_client(r)))) << endl;
3436 break;
3437
3438 case AVAHI_RESOLVER_FOUND: {
3439 compile_server_info info;
3440
3441 // Decode the address.
3442 char a[AVAHI_ADDRESS_STR_MAX];
3443 avahi_address_snprint(a, sizeof(a), address);
3444 prStatus = PR_StringToNetAddr (a, & info.address);
3445 if (prStatus != PR_SUCCESS) {
3446 clog << _F("Invalid address '%s' from avahi", a) << endl;
3447 break;
3448 }
3449
3450 // We support both IPv4 and IPv6. Ignore other protocols.
3451 if (protocol == AVAHI_PROTO_INET6) {
3452 info.address.ipv6.family = PR_AF_INET6;
3453 info.address.ipv6.scope_id = interface;
3454 info.port = port;
3455 }
3456 else if (protocol == AVAHI_PROTO_INET) {
3457 info.address.inet.family = PR_AF_INET;
3458 info.port = port;
3459 }
3460 else
3461 break;
3462
3463 // Save the host name.
3464 info.host_name = host_name;
3465
3466 // Save the text tags.
3467 info.sysinfo = get_value_from_avahi_string_list (txt, "sysinfo");
3468 info.certinfo = get_value_from_avahi_string_list (txt, "certinfo");
3469 info.version = get_value_from_avahi_string_list (txt, "version");
3470 if (info.version.empty ())
3471 info.version = "1.0"; // default version is 1.0
3472
3473 // The server might provide one or more MOK certificate's
3474 // info.
3475 get_values_from_avahi_string_list(txt, "mok_info",
3476 info.mok_fingerprints);
3477
3478 // Add this server to the list of discovered servers.
3479 add_server_info (info, *servers);
3480 break;
3481 }
3482 default:
3483 break;
3484 }
3485
3486 avahi_service_resolver_free(r);
3487 }
3488
3489 extern "C"
3490 void browse_callback(
3491 AvahiServiceBrowser *b,
3492 AvahiIfIndex interface,
3493 AvahiProtocol protocol,
3494 AvahiBrowserEvent event,
3495 const char *name,
3496 const char *type,
3497 const char *domain,
3498 AVAHI_GCC_UNUSED AvahiLookupResultFlags flags,
3499 void* userdata) {
3500
3501 browsing_context *context = (browsing_context *)userdata;
3502 AvahiClient *c = context->client;
3503 AvahiSimplePoll *simple_poll = context->simple_poll;
3504 assert(b);
3505
3506 // Called whenever a new services becomes available on the LAN or is removed from the LAN.
3507
3508 switch (event) {
3509 case AVAHI_BROWSER_FAILURE:
3510 clog << _F("Avahi browse failed: %s",
3511 avahi_strerror(avahi_client_errno(avahi_service_browser_get_client(b))))
3512 << endl;
3513 avahi_simple_poll_quit(simple_poll);
3514 break;
3515
3516 case AVAHI_BROWSER_NEW:
3517 // We ignore the returned resolver object. In the callback
3518 // function we free it. If the server is terminated before
3519 // the callback function is called the server will free
3520 // the resolver for us.
3521 if (!(avahi_service_resolver_new(c, interface, protocol, name, type, domain,
3522 AVAHI_PROTO_UNSPEC, (AvahiLookupFlags)0, resolve_callback, context))) {
3523 clog << _F("Failed to resolve service '%s': %s",
3524 name, avahi_strerror(avahi_client_errno(c))) << endl;
3525 }
3526 break;
3527
3528 case AVAHI_BROWSER_REMOVE:
3529 case AVAHI_BROWSER_ALL_FOR_NOW:
3530 case AVAHI_BROWSER_CACHE_EXHAUSTED:
3531 break;
3532 }
3533 }
3534
3535 extern "C"
3536 void client_callback(AvahiClient *c, AvahiClientState state, AVAHI_GCC_UNUSED void * userdata) {
3537 assert(c);
3538 browsing_context *context = (browsing_context *)userdata;
3539 AvahiSimplePoll *simple_poll = context->simple_poll;
3540
3541 // Called whenever the client or server state changes.
3542
3543 if (state == AVAHI_CLIENT_FAILURE) {
3544 clog << _F("Avahi Server connection failure: %s", avahi_strerror(avahi_client_errno(c))) << endl;
3545 avahi_simple_poll_quit(simple_poll);
3546 }
3547 }
3548
3549 extern "C"
3550 void timeout_callback(AVAHI_GCC_UNUSED AvahiTimeout *e, AVAHI_GCC_UNUSED void *userdata) {
3551 browsing_context *context = (browsing_context *)userdata;
3552 AvahiSimplePoll *simple_poll = context->simple_poll;
3553 avahi_simple_poll_quit(simple_poll);
3554 }
3555 #endif // HAVE_AVAHI
3556
3557 static void
3558 get_or_keep_online_server_info (
3559 systemtap_session &s,
3560 vector<compile_server_info> &servers,
3561 bool keep
3562 )
3563 {
3564 // If we're filtering the list and it's already empty, then
3565 // there's nothing to do.
3566 if (keep && servers.empty ())
3567 return;
3568
3569 // We only need to obtain this once per session. This is a good thing(tm)
3570 // since obtaining this information is expensive.
3571 vector<compile_server_info>& online_servers = cscache(s)->online_servers;
3572 if (online_servers.empty ())
3573 {
3574 // Maintain an empty entry to indicate that this search has been
3575 // performed, in case the search comes up empty.
3576 online_servers.push_back (compile_server_info ());
3577 #if HAVE_AVAHI
3578 // Must predeclare these due to jumping on error to fail:
3579 vector<compile_server_info> avahi_servers;
3580
3581 // Initialize.
3582 AvahiClient *client = NULL;
3583 AvahiServiceBrowser *sb = NULL;
3584
3585 // Allocate main loop object.
3586 AvahiSimplePoll *simple_poll;
3587 if (!(simple_poll = avahi_simple_poll_new()))
3588 {
3589 clog << _("Failed to create Avahi simple poll object") << endl;
3590 goto fail;
3591 }
3592 browsing_context context;
3593 context.simple_poll = simple_poll;
3594 context.servers = & avahi_servers;
3595
3596 // Allocate a new Avahi client
3597 int error;
3598 client = avahi_client_new (avahi_simple_poll_get (simple_poll),
3599 (AvahiClientFlags)0,
3600 client_callback, & context, & error);
3601
3602 // Check whether creating the client object succeeded.
3603 if (! client)
3604 {
3605 clog << _F("Failed to create Avahi client: %s",
3606 avahi_strerror(error)) << endl;
3607 goto fail;
3608 }
3609 context.client = client;
3610
3611 // Create the service browser.
3612 if (!(sb = avahi_service_browser_new (client, AVAHI_IF_UNSPEC,
3613 AVAHI_PROTO_UNSPEC, "_stap._tcp",
3614 NULL, (AvahiLookupFlags)0,
3615 browse_callback, & context)))
3616 {
3617 clog << _F("Failed to create Avahi service browser: %s",
3618 avahi_strerror(avahi_client_errno(client))) << endl;
3619 goto fail;
3620 }
3621
3622 // Timeout after 0.5 seconds.
3623 struct timeval tv;
3624 avahi_simple_poll_get(simple_poll)->timeout_new(
3625 avahi_simple_poll_get(simple_poll),
3626 avahi_elapse_time(&tv, 1000/2, 0),
3627 timeout_callback,
3628 & context);
3629
3630 // Run the main loop.
3631 avahi_simple_poll_loop(simple_poll);
3632
3633 if (s.verbose >= 6)
3634 {
3635 clog << _("Avahi reports the following servers online:") << endl;
3636 clog << avahi_servers;
3637 }
3638
3639 // Merge with the list of servers, as obtained by avahi.
3640 add_server_info (avahi_servers, online_servers);
3641
3642 fail:
3643 // Cleanup.
3644 if (client) {
3645 // Also frees the service browser
3646 avahi_client_free(client);
3647 }
3648 if (simple_poll)
3649 avahi_simple_poll_free(simple_poll);
3650 #else // ! HAVE_AVAHI
3651 // Without Avahi, we can't detect online servers. Issue a warning.
3652 if (s.verbose >= 2)
3653 clog << _("Unable to detect online servers without avahi") << endl;
3654 #endif // ! HAVE_AVAHI
3655
3656 if (s.verbose >= 5)
3657 {
3658 clog << _("All online servers:") << endl;
3659 clog << online_servers;
3660 }
3661 } // Server information is not cached.
3662
3663 if (keep)
3664 {
3665 // Filter the existing vector by keeping the information in common with
3666 // the online_server vector.
3667 keep_common_server_info (online_servers, servers);
3668 }
3669 else
3670 {
3671 // Add the information, but not duplicates.
3672 add_server_info (online_servers, servers);
3673 }
3674 }
3675
3676 // Add server info to a list, avoiding duplicates. Merge information from
3677 // two duplicate items.
3678 static void
3679 add_server_info (
3680 const compile_server_info &info, vector<compile_server_info>& target
3681 )
3682 {
3683 if (info.empty ())
3684 return;
3685
3686 bool found = false;
3687 for (vector<compile_server_info>::iterator i = target.begin ();
3688 i != target.end ();
3689 ++i)
3690 {
3691 if (info == *i)
3692 {
3693 // Duplicate. Merge the two items.
3694 merge_server_info (info, *i);
3695 found = true;
3696 }
3697 }
3698 if (! found)
3699 target.push_back (info);
3700 }
3701
3702 // Add server info from one vector to another.
3703 static void
3704 add_server_info (
3705 const vector<compile_server_info> &source,
3706 vector<compile_server_info> &target
3707 )
3708 {
3709 for (vector<compile_server_info>::const_iterator i = source.begin ();
3710 i != source.end ();
3711 ++i)
3712 {
3713 add_server_info (*i, target);
3714 }
3715 }
3716
3717 // Filter the vector by keeping information in common with the item.
3718 static void
3719 keep_common_server_info (
3720 const compile_server_info &info_to_keep,
3721 vector<compile_server_info> &filtered_info
3722 )
3723 {
3724 assert (! info_to_keep.empty ());
3725
3726 // The vector may change size as we go. Be careful!!
3727 for (unsigned i = 0; i < filtered_info.size (); /**/)
3728 {
3729 // Retain empty entries.
3730 if (filtered_info[i].empty ())
3731 {
3732 ++i;
3733 continue;
3734 }
3735 if (info_to_keep == filtered_info[i])
3736 {
3737 merge_server_info (info_to_keep, filtered_info[i]);
3738 ++i;
3739 continue;
3740 }
3741 // The item does not match. Delete it.
3742 filtered_info.erase (filtered_info.begin () + i);
3743 continue;
3744 }
3745 }
3746
3747 // Filter the second vector by keeping information in common with the first
3748 // vector.
3749 static void
3750 keep_common_server_info (
3751 const vector<compile_server_info> &info_to_keep,
3752 vector<compile_server_info> &filtered_info
3753 )
3754 {
3755 // The vector may change size as we go. Be careful!!
3756 for (unsigned i = 0; i < filtered_info.size (); /**/)
3757 {
3758 // Retain empty entries.
3759 if (filtered_info[i].empty ())
3760 {
3761 ++i;
3762 continue;
3763 }
3764 bool found = false;
3765 for (unsigned j = 0; j < info_to_keep.size (); ++j)
3766 {
3767 if (filtered_info[i] == info_to_keep[j])
3768 {
3769 merge_server_info (info_to_keep[j], filtered_info[i]);
3770 found = true;
3771 }
3772 }
3773
3774 // If the item was not found. Delete it. Otherwise, advance to the next
3775 // item.
3776 if (found)
3777 ++i;
3778 else
3779 filtered_info.erase (filtered_info.begin () + i);
3780 }
3781 }
3782
3783 // Merge two compile server info items.
3784 static void
3785 merge_server_info (
3786 const compile_server_info &source,
3787 compile_server_info &target
3788 )
3789 {
3790 // Copy the host name if the source has one.
3791 if (! source.host_name.empty())
3792 target.host_name = source.host_name;
3793 // Copy the address unconditionally, if the source has an address, even if they are already
3794 // equal. The source address may be an IPv6 address with a scope_id that the target is missing.
3795 assert (! target.hasAddress () || ! source.hasAddress () || source.address == target.address);
3796 if (source.hasAddress ())
3797 copyNetAddr (target.address, source.address);
3798 if (target.port == 0)
3799 {
3800 target.port = source.port;
3801 target.fully_specified = source.fully_specified;
3802 }
3803 if (target.sysinfo.empty ())
3804 target.sysinfo = source.sysinfo;
3805 if (target.version.empty ())
3806 target.version = source.version;
3807 if (target.certinfo.empty ())
3808 target.certinfo = source.certinfo;
3809 }
3810
3811 #if 0 // not used right now
3812 // Merge compile server info from one item into a vector.
3813 static void
3814 merge_server_info (
3815 const compile_server_info &source,
3816 vector<compile_server_info> &target
3817 )
3818 {
3819 for (vector<compile_server_info>::iterator i = target.begin ();
3820 i != target.end ();
3821 ++i)
3822 {
3823 if (source == *i)
3824 merge_server_info (source, *i);
3825 }
3826 }
3827
3828 // Merge compile server from one vector into another.
3829 static void
3830 merge_server_info (
3831 const vector<compile_server_info> &source,
3832 vector <compile_server_info> &target
3833 )
3834 {
3835 for (vector<compile_server_info>::const_iterator i = source.begin ();
3836 i != source.end ();
3837 ++i)
3838 merge_server_info (*i, target);
3839 }
3840 #endif
3841 #endif // HAVE_NSS
3842
3843 /* vim: set sw=2 ts=8 cino=>4,n-2,{2,^-2,t0,(0,u0,w1,M1 : */
This page took 0.202935 seconds and 5 git commands to generate.