]> sourceware.org Git - systemtap.git/blame - csclient.cxx
Define __NR_epoll_wait if not defined (PR17462)
[systemtap.git] / csclient.cxx
CommitLineData
2dce8c42
DB
1/*
2 Compile server client functions
6817f41a 3 Copyright (C) 2010-2014 Red Hat Inc.
2dce8c42
DB
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*/
c4fd15b4
DB
10
11// Completely disable the client if NSS is not available.
2dce8c42 12#include "config.h"
c4fd15b4 13#if HAVE_NSS
2dce8c42 14#include "session.h"
005df4e5 15#include "cscommon.h"
aa4d21c0 16#include "csclient.h"
2fad97fd 17#include "util.h"
0f5d597d 18#include "stap-probe.h"
2dce8c42 19
aa4d21c0 20#include <sys/times.h>
2dce8c42 21#include <vector>
2fad97fd 22#include <fstream>
cc7c72cd 23#include <sstream>
79efe7f8 24#include <cassert>
b0e5fa85
DB
25#include <cstdlib>
26#include <cstdio>
b0e5fa85 27#include <algorithm>
79efe7f8
FCE
28
29extern "C" {
e14c865b 30#include <unistd.h>
004a4482 31#include <linux/limits.h>
3eee0879 32#include <sys/time.h>
79efe7f8 33#include <glob.h>
1f9938b9 34#include <limits.h>
f88498d9 35#include <sys/socket.h>
1a7f7b3f 36#include <sys/stat.h>
0ec2c5bf 37#include <net/if.h>
f88498d9
DS
38#include <netdb.h>
39#include <arpa/inet.h>
0d1534e8 40#include <pwd.h>
79efe7f8 41}
2dce8c42
DB
42
43#if HAVE_AVAHI
44extern "C" {
2dce8c42
DB
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>
3eee0879 52}
b0e5fa85 53#endif // HAVE_AVAHI
2fad97fd 54
3eee0879 55extern "C" {
2fad97fd
DB
56#include <ssl.h>
57#include <nspr.h>
58#include <nss.h>
c77af0d0 59#include <certdb.h>
2fad97fd
DB
60#include <pk11pub.h>
61#include <prerror.h>
62#include <secerr.h>
63#include <sslerr.h>
aeb9cc10 64}
2fad97fd
DB
65
66#include "nsscommon.h"
2dce8c42 67
aeb9cc10
DB
68using namespace std;
69
c8f70b21 70#define STAP_CSC_01 _("WARNING: The domain name, %s, does not match the DNS name(s) on the server certificate:\n")
58a834b1
LB
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
a32d236c
DS
76#define MOK_PUBLIC_CERT_NAME "signing_key.x509"
77
0ec2c5bf 78static PRIPv6Addr &copyAddress (PRIPv6Addr &PRin6, const in6_addr &in6);
7f548800 79static PRNetAddr &copyNetAddr (PRNetAddr &x, const PRNetAddr &y);
0ec2c5bf 80bool operator!= (const PRNetAddr &x, const PRNetAddr &y);
7f548800 81bool operator== (const PRNetAddr &x, const PRNetAddr &y);
0ec2c5bf 82
aeb9cc10
DB
83extern "C"
84void
85nsscommon_error (const char *msg, int logit __attribute ((unused)))
86{
87 clog << msg << endl << flush;
88}
2dce8c42 89
b0e5fa85
DB
90// Information about compile servers.
91struct compile_server_info
92{
6817f41a 93 compile_server_info () : port(0), fully_specified(false)
0ec2c5bf
DB
94 {
95 memset (& address, 0, sizeof (address));
96 }
b0e5fa85 97
85717fe6 98 string host_name;
0ec2c5bf 99 PRNetAddr address;
6817f41a
DB
100 unsigned short port;
101 bool fully_specified;
85717fe6
DB
102 string version;
103 string sysinfo;
104 string certinfo;
cd1418c7 105 vector<string> mok_fingerprints;
b0e5fa85 106
7543625d
DB
107 bool empty () const
108 {
6817f41a 109 return this->host_name.empty () && ! this->hasAddress () && certinfo.empty ();
0ec2c5bf
DB
110 }
111 bool hasAddress () const
112 {
113 return this->address.raw.family != 0;
114 }
6817f41a 115 unsigned short setAddressPort (unsigned short port)
0ec2c5bf
DB
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);
6817f41a 121 assert (0);
0ec2c5bf 122 return 0;
7543625d 123 }
6817f41a
DB
124 bool isComplete () const
125 {
126 return this->hasAddress () && port != 0;
127 }
7543625d 128
b0e5fa85
DB
129 bool operator== (const compile_server_info &that) const
130 {
6817f41a
DB
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 ()))
7543625d 139 {
6817f41a
DB
140 if (this->host_name != that.host_name)
141 return false;
7543625d 142 }
7543625d 143
b0e5fa85 144 // Compare the other fields only if they have both been set.
6817f41a
DB
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)
b0e5fa85 150 return false;
85717fe6 151 if (! this->version.empty () && ! that.version.empty () &&
723adc5a 152 this->version != that.version)
85717fe6 153 return false;
b0e5fa85 154 if (! this->sysinfo.empty () && ! that.sysinfo.empty () &&
723adc5a 155 this->sysinfo != that.sysinfo)
b0e5fa85
DB
156 return false;
157 if (! this->certinfo.empty () && ! that.certinfo.empty () &&
723adc5a 158 this->certinfo != that.certinfo)
b0e5fa85 159 return false;
cd1418c7
DS
160 if (! this->mok_fingerprints.empty () && ! that.mok_fingerprints.empty ()
161 && this->mok_fingerprints != that.mok_fingerprints)
162 return false;
b3367f63 163
0ec2c5bf 164 return true; // They are equal
b0e5fa85 165 }
1d1e2086
DB
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 }
b0e5fa85
DB
176};
177
3d9dfde6 178ostream &operator<< (ostream &s, const compile_server_info &i);
34aa74ae 179ostream &operator<< (ostream &s, const vector<compile_server_info> &v);
3d9dfde6 180
005df4e5
DB
181static void
182preferred_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
10bd3fe7 193struct resolved_host // see also PR16326, PR16342
2436ada9
JL
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
eff14db3
JS
201struct 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;
7f548800 208 vector<compile_server_info> all_servers;
2436ada9 209 map<string,vector<resolved_host> > resolved_hosts;
eff14db3
JS
210};
211
b0e5fa85
DB
212// For filtering queries.
213enum 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.
eff14db3 223static compile_server_cache* cscache(systemtap_session& s);
b0e5fa85 224static void query_server_status (systemtap_session &s, const string &status_string);
b0e5fa85
DB
225
226static void get_server_info (systemtap_session &s, int pmask, vector<compile_server_info> &servers);
227static void get_all_server_info (systemtap_session &s, vector<compile_server_info> &servers);
228static void get_default_server_info (systemtap_session &s, vector<compile_server_info> &servers);
1a7f7b3f 229static void get_specified_server_info (systemtap_session &s, vector<compile_server_info> &servers, bool no_default = false);
7543625d
DB
230static void get_or_keep_online_server_info (systemtap_session &s, vector<compile_server_info> &servers, bool keep);
231static void get_or_keep_trusted_server_info (systemtap_session &s, vector<compile_server_info> &servers, bool keep);
232static void get_or_keep_signing_server_info (systemtap_session &s, vector<compile_server_info> &servers, bool keep);
233static void get_or_keep_compatible_server_info (systemtap_session &s, vector<compile_server_info> &servers, bool keep);
234static void keep_common_server_info (const compile_server_info &info_to_keep, vector<compile_server_info> &filtered_info);
b0e5fa85 235static void keep_common_server_info (const vector<compile_server_info> &info_to_keep, vector<compile_server_info> &filtered_info);
7543625d
DB
236static void keep_server_info_with_cert_and_port (systemtap_session &s, const compile_server_info &server, vector<compile_server_info> &servers);
237
b0e5fa85 238static void add_server_info (const compile_server_info &info, vector<compile_server_info>& list);
7543625d 239static void add_server_info (const vector<compile_server_info> &source, vector<compile_server_info> &target);
b0e5fa85 240static void merge_server_info (const compile_server_info &source, compile_server_info &target);
7543625d
DB
241#if 0 // not used right now
242static void merge_server_info (const compile_server_info &source, vector<compile_server_info> &target);
243static void merge_server_info (const vector<compile_server_info> &source, vector <compile_server_info> &target);
244#endif
245static void resolve_host (systemtap_session& s, compile_server_info &server, vector<compile_server_info> &servers);
b0e5fa85 246
fd14bd8f
DB
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
840e5073
DB
253// -----------------------------------------------------
254// NSS related code used by the compile server client
255// -----------------------------------------------------
6817f41a 256static void add_server_trust (systemtap_session &s, const string &cert_db_path, vector<compile_server_info> &server_list);
3d9dfde6
DB
257static void revoke_server_trust (systemtap_session &s, const string &cert_db_path, const vector<compile_server_info> &server_list);
258static void get_server_info_from_db (systemtap_session &s, vector<compile_server_info> &servers, const string &cert_db_path);
259
3dc20443
DB
260static string global_client_cert_db_path () {
261 return SYSCONFDIR "/systemtap/ssl/client";
262}
263
c77af0d0 264static string
aeb9cc10 265private_ssl_cert_db_path ()
c77af0d0 266{
aeb9cc10 267 return local_client_cert_db_path ();
c77af0d0
DB
268}
269
270static string
271global_ssl_cert_db_path ()
272{
aeb9cc10 273 return global_client_cert_db_path ();
c77af0d0
DB
274}
275
276static string
277signing_cert_db_path ()
278{
279 return SYSCONFDIR "/systemtap/staprun";
280}
840e5073 281
840e5073
DB
282/* Connection state. */
283typedef struct connectionState_t
284{
285 const char *hostName;
286 PRNetAddr addr;
840e5073
DB
287 const char *infileName;
288 const char *outfileName;
289 const char *trustNewServerMode;
290} connectionState_t;
291
292#if 0 /* No client authorization */
293static char *
294myPasswd(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. */
306static SECStatus
307trustNewServer (CERTCertificate *serverCert)
308{
309 SECStatus secStatus;
310 CERTCertTrust *trust = NULL;
aeb9cc10 311 PK11SlotInfo *slot = NULL;
840e5073
DB
312
313 /* Import the certificate. */
aeb9cc10
DB
314 slot = PK11_GetInternalKeySlot();
315 const char *nickname = server_cert_nickname ();
316 secStatus = PK11_ImportCert(slot, serverCert, CK_INVALID_HANDLE, nickname, PR_FALSE);
840e5073
DB
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);
840e5073
DB
333
334done:
aeb9cc10
DB
335 if (slot)
336 PK11_FreeSlot (slot);
840e5073
DB
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. */
345static SECStatus
346badCertHandler(void *arg, PRFileDesc *sslSocket)
347{
348 SECStatus secStatus;
349 PRErrorCode errorNumber;
c619c688 350 CERTCertificate *serverCert = NULL;
840e5073
DB
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.
7011c87d
DB
364 If we know the expected domain name, then issue a warning but,
365 in any case, accept the certificate. */
366 secStatus = SECSuccess;
367
840e5073 368 expected = SSL_RevealURL (sslSocket);
7011c87d
DB
369 if (expected == NULL || *expected == '\0')
370 break;
371
58a834b1 372 fprintf (stderr, STAP_CSC_01, expected);
840e5073
DB
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 {
58a834b1 383 fprintf (stderr, _("Unable to find alt name extension on the server certificate\n"));
840e5073
DB
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 {
58a834b1 392 fprintf (stderr, _("Out of memory\n"));
ec21642d 393 SECITEM_FreeItem(& subAltName, PR_FALSE);
840e5073
DB
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 {
58a834b1 401 fprintf (stderr, _("Unable to decode alt name extension on server certificate\n"));
840e5073
DB
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
840e5073
DB
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)
aeb9cc10
DB
443 {
444 secStatus = trustNewServer (serverCert);
aeb9cc10 445 }
840e5073
DB
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
c619c688
CM
458 if (serverCert != NULL)
459 {
460 CERT_DestroyCertificate (serverCert);
461 }
462
840e5073
DB
463 return secStatus;
464}
465
466static PRFileDesc *
467setupSSLSocket (connectionState_t *connectionState)
468{
469 PRFileDesc *tcpSocket;
470 PRFileDesc *sslSocket;
471 PRSocketOptionData socketOption;
472 PRStatus prStatus;
473 SECStatus secStatus;
474
0ec2c5bf 475 tcpSocket = PR_OpenTCPSocket(connectionState->addr.raw.family);
840e5073
DB
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
537static SECStatus
538handle_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
aeb9cc10
DB
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
840e5073
DB
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 {
58a834b1 570 fprintf (stderr, STAP_CSC_02,
840e5073
DB
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 {
58a834b1 578 fprintf (stderr, STAP_CSC_03, connectionState->infileName);
840e5073
DB
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) {
58a834b1 607 fprintf (stderr, _("Out of memory\n"));
840e5073
DB
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 {
58a834b1 615 fprintf (stderr, STAP_CSC_04, connectionState->outfileName);
840e5073
DB
616 return SECFailure;
617 }
618 while (PR_TRUE)
619 {
5ab345f0
DB
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);
840e5073
DB
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 {
58a834b1 636 fprintf (stderr, STAP_CSC_05, connectionState->outfileName);
840e5073
DB
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*/
651static SECStatus
652do_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
aeb9cc10
DB
693 // Connect to the server and make the request.
694 secStatus = handle_connection(sslSocket, connectionState);
840e5073
DB
695
696 done:
697 prStatus = PR_Close(sslSocket);
698 return secStatus;
699}
700
7f548800
DB
701static bool
702isIPv6LinkLocal (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
840e5073 711int
0ec2c5bf 712client_connect (const compile_server_info &server,
49dbe419
DB
713 const char* infileName, const char* outfileName,
714 const char* trustNewServer)
840e5073
DB
715{
716 SECStatus secStatus;
840e5073 717 PRErrorCode errorNumber;
840e5073
DB
718 int attempt;
719 int errCode = GENERAL_ERROR;
720 struct connectionState_t connectionState;
721
0ec2c5bf
DB
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;
840e5073
DB
726 connectionState.infileName = infileName;
727 connectionState.outfileName = outfileName;
728 connectionState.trustNewServerMode = trustNewServer;
729
840e5073
DB
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
aeb9cc10
DB
747 generate a new certificate. Return now and we'll try again. */
748 errCode = SERVER_CERT_EXPIRED_ERROR;
749 return errCode;
840e5073
DB
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}
eff14db3 763
aa4d21c0
DB
764int
765compile_server_client::passes_0_4 ()
766{
0f5d597d 767 PROBE1(stap, client__start, &s);
2fad97fd 768
aa4d21c0 769 // arguments parsed; get down to business
4e78c716 770 if (s.verbose || ! s.auto_server_msgs.empty ())
a46f9abe 771 clog << _("Using a compile server.") << endl;
aa4d21c0 772
aa4d21c0
DB
773 struct tms tms_before;
774 times (& tms_before);
775 struct timeval tv_before;
776 gettimeofday (&tv_before, NULL);
777
2fad97fd
DB
778 // Create the request package.
779 int rc = initialize ();
e19ebcf7 780 assert_no_interrupts();
985892de 781 if (rc != 0) goto done;
2fad97fd 782 rc = create_request ();
e19ebcf7 783 assert_no_interrupts();
985892de 784 if (rc != 0) goto done;
2fad97fd 785 rc = package_request ();
e19ebcf7 786 assert_no_interrupts();
985892de 787 if (rc != 0) goto done;
2fad97fd
DB
788
789 // Submit it to the server.
790 rc = find_and_connect_to_server ();
e19ebcf7 791 assert_no_interrupts();
985892de 792 if (rc != 0) goto done;
2fad97fd
DB
793
794 // Unpack and process the response.
795 rc = unpack_response ();
e19ebcf7 796 assert_no_interrupts();
985892de 797 if (rc != 0) goto done;
2fad97fd
DB
798 rc = process_response ();
799
800 done:
aa4d21c0
DB
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
da21dc88
DB
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
aa4d21c0
DB
845 // syntax errors, if any, are already printed
846 if (s.verbose)
847 {
a46f9abe
FCE
848 string ws = s.winning_server;
849 if (ws == "") ws = "?";
850 clog << _("Passes: via server ") << ws << " "
85007c04 851 << getmemusage()
aa4d21c0
DB
852 << TIMESPRINT
853 << endl;
854 }
bba368c5 855 if (rc && !s.dump_mode)
a46f9abe
FCE
856 {
857 clog << _("Passes: via server failed. Try again with another '-v' option.") << endl;
858 }
aa4d21c0 859
0f5d597d 860 PROBE1(stap, client__end, &s);
aa4d21c0
DB
861
862 return rc;
863}
864
2fad97fd
DB
865// Initialize a client/server session.
866int
867compile_server_client::initialize ()
868{
869 int rc = 0;
870
871 // Initialize session state
872 argc = 0;
873
c77af0d0 874 // Private location for server certificates.
aeb9cc10 875 private_ssl_dbs.push_back (private_ssl_cert_db_path ());
2fad97fd 876
c77af0d0
DB
877 // Additional public location.
878 public_ssl_dbs.push_back (global_ssl_cert_db_path ());
2fad97fd
DB
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);
aeb9cc10 886 clog << _("ERROR: cannot create temporary directory (\"")
2fad97fd
DB
887 << client_tmpdir << "\"): " << e
888 << endl;
889 }
890
891 return rc;
892}
893
894// Create the request package.
895int
896compile_server_client::create_request ()
897{
cc7c72cd 898 // Add the current protocol version.
c89fe89c 899 int rc = write_to_file (client_tmpdir + "/version", CURRENT_CS_PROTOCOL_VERSION);
cc7c72cd
DB
900 if (rc != 0)
901 return rc;
2fad97fd
DB
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);
aeb9cc10 914 clog << _("ERROR: cannot create temporary directory ")
2fad97fd
DB
915 << packaged_script_dir << ": " << e
916 << endl;
917 return rc;
918 }
919 rc = ! copy_file("/dev/stdin", packaged_script_dir + "/-");
f3fbabf2
DB
920 if (rc != 0)
921 return rc;
2fad97fd
DB
922
923 // Name the script in the packaged arguments.
f3fbabf2
DB
924 rc = add_package_arg ("script/-");
925 if (rc != 0)
926 return rc;
2fad97fd
DB
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);
f3fbabf2
DB
933 if (rc != 0)
934 return rc;
2fad97fd
DB
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 {
f3fbabf2
DB
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;
2fad97fd
DB
950 }
951 }
952
953 // Add other options.
f3fbabf2
DB
954 rc = add_package_args ();
955 if (rc != 0)
956 return rc;
2fad97fd
DB
957
958 // Add the sysinfo file
959 string sysinfo = "sysinfo: " + s.kernel_release + " " + s.architecture;
f3fbabf2 960 rc = write_to_file (client_tmpdir + "/sysinfo", sysinfo);
e4e3d6b7
CM
961 if (rc != 0)
962 return rc;
963
964 // Add localization data
965 rc = add_localization_variables();
2fad97fd 966
b3367f63 967 // Add the machine owner key (MOK) fingerprints file, if needed.
cd1418c7 968 if (! s.mok_fingerprints.empty())
b3367f63
DS
969 {
970 ostringstream fingerprints;
971 vector<string>::const_iterator it;
cd1418c7 972 for (it = s.mok_fingerprints.begin(); it != s.mok_fingerprints.end();
b3367f63
DS
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
2fad97fd
DB
982 return rc;
983}
984
985// Add the arguments specified on the command line to the server request
986// package, as appropriate.
f3fbabf2 987int
2fad97fd
DB
988compile_server_client::add_package_args ()
989{
fd20a70c 990 // stap arguments to be passed to the server.
f3fbabf2 991 int rc = 0;
2fad97fd
DB
992 unsigned limit = s.server_args.size();
993 for (unsigned i = 0; i < limit; ++i)
f3fbabf2
DB
994 {
995 rc = add_package_arg (s.server_args[i]);
996 if (rc != 0)
997 return rc;
998 }
fd20a70c
DB
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 }
f3fbabf2 1013 return rc;
2fad97fd
DB
1014}
1015
131f914e
DB
1016int
1017compile_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
2fad97fd
DB
1026// Symbolically link the given file or directory into the client's temp
1027// directory under the given subdirectory.
1028int
1029compile_server_client::include_file_or_directory (
5b314cd0 1030 const string &subdir, const string &path
2fad97fd
DB
1031)
1032{
62f9d949
DB
1033 // Must predeclare these because we do use 'goto done' to
1034 // exit from error situations.
2fad97fd 1035 vector<string> components;
62f9d949
DB
1036 string name;
1037 int rc;
2fad97fd 1038
62f9d949
DB
1039 // Canonicalize the given path and remove the leading /.
1040 string rpath;
1041 char *cpath = canonicalize_file_name (path.c_str ());
2fad97fd
DB
1042 if (! cpath)
1043 {
62f9d949
DB
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;
2fad97fd 1054 }
62f9d949 1055 else
2fad97fd 1056 {
62f9d949
DB
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
5b314cd0
DB
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
62f9d949
DB
1072 // First create the requested subdirectory.
1073 name = client_tmpdir + "/" + subdir;
2fad97fd
DB
1074 rc = create_dir (name.c_str ());
1075 if (rc) goto done;
2fad97fd 1076
62f9d949
DB
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 }
2fad97fd 1097
5b314cd0 1098 // Name this file or directory in the packaged arguments.
62f9d949 1099 rc = add_package_arg (subdir + "/" + rpath.substr (1));
2fad97fd
DB
1100
1101 done:
2fad97fd
DB
1102 if (rc != 0)
1103 {
1104 const char* e = strerror (errno);
aeb9cc10 1105 clog << "ERROR: unable to add "
62f9d949
DB
1106 << rpath
1107 << " to temp directory as "
2fad97fd
DB
1108 << name << ": " << e
1109 << endl;
1110 }
2fad97fd
DB
1111 return rc;
1112}
1113
e4e3d6b7
CM
1114// Add the localization variables to the server request
1115// package.
1116int
1117compile_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
2fad97fd
DB
1141// Package the client's temp directory into a form suitable for sending to the
1142// server.
1143int
1144compile_server_client::package_request ()
1145{
1146 // Package up the temporary directory into a zip file.
1147 client_zipfile = client_tmpdir + ".zip";
ff520ff4
JS
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);
2fad97fd
DB
1155 return rc;
1156}
1157
1158int
1159compile_server_client::find_and_connect_to_server ()
1160{
2e15a955 1161 // Accumulate info on the specified servers.
7543625d
DB
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.
0ec2c5bf 1168 vector<compile_server_info> server_list;
7543625d
DB
1169 for (vector<compile_server_info>::const_iterator i = specified_servers.begin ();
1170 i != specified_servers.end ();
1171 ++i)
1172 {
6817f41a
DB
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
7543625d 1175 // ip addresses and ports.
6817f41a 1176 if (i->hasAddress() && i->fully_specified)
7543625d
DB
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
6817f41a
DB
1186 // compatible and possibly trusted as signers as well.
1187 if (! i->fully_specified)
7543625d
DB
1188 {
1189 get_or_keep_compatible_server_info (s, online_servers, true/*keep*/);
f66bb29a 1190 if (! pr_contains (s.privilege, pr_stapdev))
7543625d
DB
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);
2fad97fd 1196
7543625d
DB
1197 // Add these servers (if any) to the server list.
1198 add_server_info (online_servers, server_list);
1199 }
1200 }
c77af0d0 1201
2e15a955
DB
1202 // Did we identify any potential servers?
1203 unsigned limit = server_list.size ();
1204 if (limit == 0)
2fad97fd 1205 {
4e78c716 1206 clog << _("Unable to find a suitable compile server. [man stap-server]") << endl;
34aa74ae
DB
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 }
2e15a955
DB
1231 return 1;
1232 }
1f9938b9 1233
1d1e2086
DB
1234 // Sort the list of servers into a preferred order.
1235 preferred_order (server_list);
1236
2e15a955 1237 // Now try each of the identified servers in turn.
7543625d 1238 int rc = compile_using_server (server_list);
aeb9cc10 1239 if (rc == SUCCESS)
7543625d 1240 return 0; // success!
2fad97fd 1241
aeb9cc10
DB
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 {
b2d017e6
DB
1247 if (s.verbose >= 2)
1248 clog << _("The server's certificate was expired. Trying again") << endl << flush;
aeb9cc10
DB
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
a46f9abe 1256 clog << _("Unable to connect to a server.") << endl;
7f548800
DB
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 }
aeb9cc10 1263 return 1; // Failure
2fad97fd 1264}
7543625d 1265
2fad97fd 1266int
7543625d 1267compile_server_client::compile_using_server (
6817f41a 1268 vector<compile_server_info> &servers
7543625d 1269)
2fad97fd 1270{
d389518f 1271 // Make sure NSPR is initialized. Must be done before NSS is initialized
b0e5fa85
DB
1272 s.NSPR_init ();
1273
1f9938b9 1274 // Attempt connection using each of the available client certificate
ed1719be
DB
1275 // databases. Assume the server certificate is invalid until proven otherwise.
1276 PR_SetError (SEC_ERROR_CA_CERT_INVALID, 0);
2fad97fd
DB
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 ());
aeb9cc10
DB
1280 int rc = GENERAL_ERROR; // assume failure
1281 bool serverCertExpired = false;
2fad97fd
DB
1282 for (i = dbs.begin (); i != dbs.end (); ++i)
1283 {
ed1719be
DB
1284 // Make sure the database directory exists. It is not an error if it
1285 // doesn't.
1286 if (! file_exists (*i))
1287 continue;
2fad97fd 1288
f3fbabf2
DB
1289#if 0 // no client authentication for now.
1290 // Set our password function callback.
2fad97fd
DB
1291 PK11_SetPasswordFunc (myPasswd);
1292#endif
1293
f3fbabf2 1294 // Initialize the NSS libraries.
7543625d 1295 const char *cert_dir = i->c_str ();
aeb9cc10 1296 SECStatus secStatus = nssInit (cert_dir);
2fad97fd
DB
1297 if (secStatus != SECSuccess)
1298 {
aeb9cc10
DB
1299 // Message already issued.
1300 continue; // try next database
2fad97fd
DB
1301 }
1302
241e35f0 1303 // Enable all cipher suites.
aeb9cc10 1304 // SSL_ClearSessionCache is required for the new settings to take effect.
f1b05046
FCE
1305 /* Some NSS versions don't do this correctly in NSS_SetDomesticPolicy. */
1306 do {
1307 const PRUint16 *cipher;
1308 for (cipher = SSL_ImplementedCiphers; *cipher != 0; ++cipher)
1309 SSL_CipherPolicySet(*cipher, SSL_ALLOWED);
1310 } while (0);
aeb9cc10 1311 SSL_ClearSessionCache ();
aeb9cc10 1312
2fad97fd 1313 server_zipfile = s.tmpdir + "/server.zip";
7543625d
DB
1314
1315 // Try each server in turn.
6817f41a 1316 for (vector<compile_server_info>::iterator j = servers.begin ();
7543625d
DB
1317 j != servers.end ();
1318 ++j)
1319 {
0ec2c5bf
DB
1320 // At a minimum we need an ip_address along with a port
1321 // number in order to contact the server.
6817f41a 1322 if (! j->hasAddress() || j->port == 0)
0ec2c5bf 1323 continue;
6817f41a
DB
1324 // Set the port within the address.
1325 j->setAddressPort (j->port);
0ec2c5bf 1326
b2d017e6 1327 if (s.verbose >= 2)
58a834b1
LB
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);
7543625d 1331
0ec2c5bf 1332 rc = client_connect (*j, client_zipfile.c_str(), server_zipfile.c_str (),
aeb9cc10
DB
1333 NULL/*trustNewServer_p*/);
1334 if (rc == SUCCESS)
7543625d 1335 {
0ec2c5bf 1336 s.winning_server = lex_cast(*j);
7543625d
DB
1337 break; // Success!
1338 }
d7cec9ad 1339
aeb9cc10
DB
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
b2d017e6 1349 if (s.verbose >= 2)
d7cec9ad 1350 {
58a834b1 1351 clog << _(" Unable to connect: ");
d7cec9ad 1352 nssError ();
7f548800
DB
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 }
d7cec9ad 1360 }
7543625d 1361 }
2e46c01a 1362
aeb9cc10
DB
1363 // SSL_ClearSessionCache is required before shutdown for client applications.
1364 SSL_ClearSessionCache ();
1365 nssCleanup (cert_dir);
2fad97fd 1366
ed1719be 1367 if (rc == SECSuccess)
b0e5fa85 1368 break; // Success!
2fad97fd
DB
1369 }
1370
aeb9cc10
DB
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 }
ed1719be 1377
b0e5fa85 1378 return rc;
2fad97fd
DB
1379}
1380
1381int
1382compile_server_client::unpack_response ()
1383{
f3fbabf2 1384 // Unzip the response package.
2fad97fd 1385 server_tmpdir = s.tmpdir + "/server";
18babaa9
JS
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);
2fad97fd 1391 int rc = stap_system (s.verbose, cmd);
f3fbabf2
DB
1392 if (rc != 0)
1393 {
aeb9cc10 1394 clog << _F("Unable to unzip the server response '%s'\n", server_zipfile.c_str());
cc7c72cd 1395 return rc;
f3fbabf2 1396 }
2fad97fd 1397
cc7c72cd 1398 // Determine the server protocol version.
cc7c72cd
DB
1399 string filename = server_tmpdir + "/version";
1400 if (file_exists (filename))
c89fe89c 1401 ::read_from_file (filename, server_version);
cc7c72cd 1402
c89fe89c 1403 // Warn about the shortcomings of this server, if it is down level.
71a522b5 1404 show_server_compatibility ();
cc7c72cd 1405
2fad97fd
DB
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??????";
b2d017e6 1410 if (s.verbose >= 3)
58a834b1 1411 clog << _F("Searching \"%s\"\n", filespec.c_str());
2fad97fd
DB
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 {
aeb9cc10 1417 clog << _("Incorrect number of files in server response") << endl;
cc7c72cd
DB
1418 rc = 1;
1419 goto done;
2fad97fd
DB
1420 }
1421
1422 assert (globbuf.gl_pathc == 1);
1423 string dirname = globbuf.gl_pathv[0];
b2d017e6 1424 if (s.verbose >= 3)
58a834b1 1425 clog << _(" found ") << dirname << endl;
2fad97fd
DB
1426
1427 filespec = dirname + "/*";
b2d017e6 1428 if (s.verbose >= 3)
58a834b1 1429 clog << _F("Searching \"%s\"\n", filespec.c_str());
2fad97fd
DB
1430 int r = glob(filespec.c_str (), GLOB_PERIOD, NULL, & globbuf);
1431 if (r != GLOB_NOSPACE && r != GLOB_ABORTED && r != GLOB_NOMATCH)
1432 {
2fad97fd
DB
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);
b2d017e6 1441 if (s.verbose >= 3)
58a834b1 1442 clog << _F(" found %s -- linking from %s", oldname.c_str(), newname.c_str());
f3fbabf2
DB
1443 rc = symlink (oldname.c_str (), newname.c_str ());
1444 if (rc != 0)
1445 {
aeb9cc10 1446 clog << _F("Unable to link '%s' to '%s':%s\n",
cc7c72cd 1447 oldname.c_str(), newname.c_str(), strerror(errno));
f3fbabf2
DB
1448 goto done;
1449 }
2fad97fd
DB
1450 }
1451 }
1452 }
1453
c89fe89c 1454 // If the server version is less that 1.6, remove the output line due to the synthetic
cc7c72cd 1455 // server-side -k. Look for a message containing the name of the temporary directory.
49dbe419 1456 // We can look for the English message since server versions before 1.6 do not support
cc7c72cd 1457 // localization.
c89fe89c 1458 if (server_version < "1.6")
cc7c72cd
DB
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
18babaa9
JS
1469 cmd.clear();
1470 cmd.push_back("sed");
1471 cmd.push_back("-i");
18babaa9
JS
1472 cmd.push_back("/^.*\\.ko$/ d");
1473 cmd.push_back(server_tmpdir + "/stdout");
f3fbabf2 1474 stap_system (s.verbose, cmd);
2fad97fd 1475
2fad97fd
DB
1476 done:
1477 globfree (& globbuf);
1478 return rc;
1479}
1480
1481int
1482compile_server_client::process_response ()
1483{
1484 // Pick up the results of running stap on the server.
2fad97fd 1485 string filename = server_tmpdir + "/rc";
f3fbabf2
DB
1486 int stap_rc;
1487 int rc = read_from_file (filename, stap_rc);
1488 if (rc != 0)
1489 return rc;
85007c04 1490 rc = stap_rc;
2fad97fd 1491
2fad97fd
DB
1492 if (s.last_pass >= 4)
1493 {
1494 // The server should have returned a module.
1495 string filespec = s.tmpdir + "/*.ko";
b2d017e6 1496 if (s.verbose >= 3)
58a834b1 1497 clog << _F("Searching \"%s\"\n", filespec.c_str());
f3fbabf2
DB
1498
1499 glob_t globbuf;
2fad97fd
DB
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)
aeb9cc10 1504 clog << _("Incorrect number of modules in server response") << endl;
f3fbabf2 1505 else
2fad97fd 1506 {
f3fbabf2
DB
1507 assert (globbuf.gl_pathc == 1);
1508 string modname = globbuf.gl_pathv[0];
b2d017e6 1509 if (s.verbose >= 3)
58a834b1 1510 clog << _(" found ") << modname << endl;
f3fbabf2
DB
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 }
474d17ad
DB
1521
1522 // If a uprobes.ko module was returned, then make note of it.
71a522b5
DB
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))
474d17ad
DB
1530 {
1531 s.need_uprobes = true;
71a522b5 1532 s.uprobes_path = uprobes_ko;
474d17ad 1533 }
2fad97fd
DB
1534 }
1535 }
1536 else if (s.have_script)
1537 {
85007c04 1538 if (rc == 0)
2fad97fd 1539 {
a46f9abe 1540 clog << _("No module was returned by the server.") << endl;
85007c04 1541 rc = 1;
2fad97fd
DB
1542 }
1543 }
f3fbabf2 1544 globfree (& globbuf);
2fad97fd
DB
1545 }
1546
a32d236c
DS
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
f3fbabf2 1556 // Output stdout and stderr.
2fad97fd 1557 filename = server_tmpdir + "/stderr";
aeb9cc10 1558 flush_to_stream (filename, clog);
2fad97fd
DB
1559
1560 filename = server_tmpdir + "/stdout";
f3fbabf2
DB
1561 flush_to_stream (filename, cout);
1562
1563 return rc;
1564}
1565
1566int
1567compile_server_client::read_from_file (const string &fname, int &data)
1568{
131f914e
DB
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 ())
f3fbabf2 1575 {
aeb9cc10 1576 clog << _F("Unable to open file '%s' for reading: ", fname.c_str());
131f914e 1577 goto error;
f3fbabf2
DB
1578 }
1579
131f914e
DB
1580 // Read the data;
1581 errno = 0;
1582 f >> data;
1583 if (f.fail ())
f3fbabf2 1584 {
aeb9cc10 1585 clog << _F("Unable to read from file '%s': ", fname.c_str());
131f914e 1586 goto error;
f3fbabf2
DB
1587 }
1588
920be590 1589 // NB: not necessary to f.close ();
131f914e
DB
1590 return 0; // Success
1591
1592 error:
1593 if (errno)
aeb9cc10 1594 clog << strerror (errno) << endl;
131f914e 1595 else
aeb9cc10 1596 clog << _("unknown error") << endl;
131f914e 1597 return 1; // Failure
f3fbabf2
DB
1598}
1599
cc7c72cd 1600template <class T>
f3fbabf2 1601int
cc7c72cd 1602compile_server_client::write_to_file (const string &fname, const T &data)
f3fbabf2 1603{
131f914e
DB
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 ())
f3fbabf2 1610 {
aeb9cc10 1611 clog << _F("Unable to open file '%s' for writing: ", fname.c_str());
131f914e 1612 goto error;
f3fbabf2
DB
1613 }
1614
131f914e
DB
1615 // Write the data;
1616 f << data;
1617 errno = 0;
1618 if (f.fail ())
f3fbabf2 1619 {
aeb9cc10 1620 clog << _F("Unable to write to file '%s': ", fname.c_str());
131f914e 1621 goto error;
f3fbabf2
DB
1622 }
1623
920be590 1624 // NB: not necessary to f.close ();
131f914e
DB
1625 return 0; // Success
1626
1627 error:
1628 if (errno)
aeb9cc10 1629 clog << strerror (errno) << endl;
131f914e 1630 else
aeb9cc10 1631 clog << _("unknown error") << endl;
131f914e 1632 return 1; // Failure
f3fbabf2
DB
1633}
1634
1635int
1636compile_server_client::flush_to_stream (const string &fname, ostream &o)
1637{
131f914e
DB
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 ())
f3fbabf2 1644 {
aeb9cc10 1645 clog << _F("Unable to open file '%s' for reading: ", fname.c_str());
131f914e 1646 goto error;
f3fbabf2
DB
1647 }
1648
131f914e 1649 // Stream the data
920be590
FCE
1650
1651 // NB: o << f.rdbuf() misbehaves for some reason, appearing to close o,
aeb9cc10 1652 // which is unfortunate if o == clog or cout.
920be590 1653 while (1)
f3fbabf2 1654 {
920be590
FCE
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;
f3fbabf2 1661 }
2fad97fd 1662
920be590 1663 // NB: not necessary to f.close ();
131f914e
DB
1664
1665 error:
1666 if (errno)
aeb9cc10 1667 clog << strerror (errno) << endl;
131f914e 1668 else
aeb9cc10 1669 clog << _("unknown error") << endl;
131f914e 1670 return 1; // Failure
2fad97fd 1671}
cc7c72cd
DB
1672
1673void
71a522b5 1674compile_server_client::show_server_compatibility () const
cc7c72cd 1675{
c89fe89c
DB
1676 // Locale sensitivity was added in version 1.6
1677 if (server_version < "1.6")
cc7c72cd 1678 {
c89fe89c 1679 clog << _F("Server protocol version is %s\n", server_version.v);
cc7c72cd
DB
1680 clog << _("The server does not use localization information passed by the client\n");
1681 }
1682}
1683
48e74294
DB
1684// Issue a status message for when a server's trust is already in place.
1685static void
1686trust_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)
85007c04 1692{
48e74294
DB
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");
0d1534e8 1697 else
48e74294
DB
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 }
85007c04 1705
48e74294
DB
1706 // Issue a message for each server in the list with the same certificate.
1707 unsigned limit = server_list.size ();
85007c04
DB
1708 for (unsigned i = 0; i < limit; ++i)
1709 {
48e74294 1710 if (server.certinfo != server_list[i].certinfo)
0d1534e8 1711 continue;
48e74294
DB
1712 clog << server_list[i] << _(" is already ");
1713 if (revoking)
1714 clog << _("untrusted ") << purpose << endl;
85007c04 1715 else
48e74294 1716 clog << _("trusted ") << purpose << endl;
85007c04 1717 }
2e15a955
DB
1718}
1719
48e74294 1720// Add the given servers to the given database of trusted servers.
b0e5fa85 1721static void
48e74294
DB
1722add_server_trust (
1723 systemtap_session &s,
1724 const string &cert_db_path,
6817f41a 1725 vector<compile_server_info> &server_list
48e74294 1726)
2e15a955 1727{
48e74294
DB
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);
2e15a955 1732
48e74294
DB
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 }
2e15a955 1740
48e74294
DB
1741 // Must predeclare this because of jumps to cleanup: below.
1742 vector<string> processed_certs;
7543625d 1743
d389518f
DB
1744 // Make sure NSPR is initialized. Must be done before NSS is initialized
1745 s.NSPR_init ();
1746
c77af0d0 1747 // Initialize the NSS libraries -- read/write
aeb9cc10 1748 SECStatus secStatus = nssInit (cert_db_path.c_str (), 1/*readwrite*/);
c77af0d0
DB
1749 if (secStatus != SECSuccess)
1750 {
aeb9cc10 1751 // Message already issued.
c77af0d0
DB
1752 goto cleanup;
1753 }
1754
241e35f0 1755 // Enable all cipher suites.
aeb9cc10 1756 // SSL_ClearSessionCache is required for the new settings to take effect.
f1b05046
FCE
1757 /* Some NSS versions don't do this correctly in NSS_SetDomesticPolicy. */
1758 do {
1759 const PRUint16 *cipher;
1760 for (cipher = SSL_ImplementedCiphers; *cipher != 0; ++cipher)
1761 SSL_CipherPolicySet(*cipher, SSL_ALLOWED);
1762 } while (0);
aeb9cc10 1763 SSL_ClearSessionCache ();
aeb9cc10 1764
c77af0d0
DB
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.
840e5073 1767 // client_connect will issue any error messages.
6817f41a 1768 for (vector<compile_server_info>::iterator server = server_list.begin();
c77af0d0
DB
1769 server != server_list.end ();
1770 ++server)
1771 {
7543625d
DB
1772 // Trust is based on certificates. We need only add trust in the
1773 // same certificate once.
fde1ca25
DB
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 ())
7543625d 1782 {
fde1ca25
DB
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 }
7543625d 1797 }
6817f41a 1798
0ec2c5bf 1799 // At a minimum we need an ip_address along with a port
d7cec9ad 1800 // number in order to contact the server.
6817f41a 1801 if (! server->hasAddress() || server->port == 0)
d7cec9ad 1802 continue;
6817f41a
DB
1803 // Set the port within the address.
1804 server->setAddressPort (server->port);
1805
0ec2c5bf 1806 int rc = client_connect (*server, NULL, NULL, "permanent");
aeb9cc10 1807 if (rc != SUCCESS)
7543625d 1808 {
aeb9cc10 1809 clog << _F("Unable to connect to %s", lex_cast(*server).c_str()) << endl;
7543625d 1810 nssError ();
7f548800
DB
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 }
7543625d 1818 }
c77af0d0
DB
1819 }
1820
1821 cleanup:
1a7f7b3f 1822 // Shutdown NSS.
aeb9cc10
DB
1823 // SSL_ClearSessionCache is required before shutdown for client applications.
1824 SSL_ClearSessionCache ();
1825 nssCleanup (cert_db_path.c_str ());
1a7f7b3f
DB
1826
1827 // Make sure the database files are readable.
1828 glob_t globbuf;
1829 string filespec = cert_db_path + "/*.db";
b2d017e6 1830 if (s.verbose >= 3)
58a834b1 1831 clog << _F("Searching \"%s\"\n", filespec.c_str());
1a7f7b3f
DB
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];
b2d017e6 1838 if (s.verbose >= 3)
58a834b1 1839 clog << _(" found ") << filename << endl;
1a7f7b3f
DB
1840
1841 if (chmod (filename.c_str (), 0644) != 0)
1842 {
2713ea24 1843 s.print_warning("Unable to change permissions on " + filename + ": ");
1a7f7b3f
DB
1844 perror ("");
1845 }
1846 }
1847 }
c77af0d0
DB
1848}
1849
1850// Remove the given servers from the given database of trusted servers.
b0e5fa85 1851static void
c77af0d0
DB
1852revoke_server_trust (
1853 systemtap_session &s,
1854 const string &cert_db_path,
1855 const vector<compile_server_info> &server_list
1856)
1857{
c77af0d0
DB
1858 // Make sure the given path exists.
1859 if (! file_exists (cert_db_path))
1860 {
7f548800 1861 if (s.verbose >= 5)
c77af0d0 1862 {
b2d017e6
DB
1863 clog << _F("Certificate database '%s' does not exist",
1864 cert_db_path.c_str()) << endl;
7543625d
DB
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*/);
c77af0d0
DB
1869 }
1870 return;
1871 }
1872
1873 // Must predeclare these because of jumps to cleanup: below.
c77af0d0
DB
1874 CERTCertDBHandle *handle;
1875 PRArenaPool *tmpArena = NULL;
1876 CERTCertList *certs = NULL;
1877 CERTCertificate *db_cert;
7543625d 1878 vector<string> processed_certs;
aeb9cc10 1879 const char *nickname;
c77af0d0 1880
d389518f
DB
1881 // Make sure NSPR is initialized. Must be done before NSS is initialized
1882 s.NSPR_init ();
1883
c77af0d0 1884 // Initialize the NSS libraries -- read/write
aeb9cc10 1885 SECStatus secStatus = nssInit (cert_db_path.c_str (), 1/*readwrite*/);
c77af0d0
DB
1886 if (secStatus != SECSuccess)
1887 {
aeb9cc10 1888 // Message already issued
c77af0d0
DB
1889 goto cleanup;
1890 }
c77af0d0
DB
1891 handle = CERT_GetDefaultCertDB();
1892
1893 // A memory pool to work in
1894 tmpArena = PORT_NewArena(DER_DEFAULT_CHUNKSIZE);
1895 if (! tmpArena)
1896 {
aeb9cc10 1897 clog << _("Out of memory:");
c77af0d0
DB
1898 nssError ();
1899 goto cleanup;
1900 }
1901
1902 // Iterate over the servers to become untrusted.
aeb9cc10 1903 nickname = server_cert_nickname ();
c77af0d0
DB
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.
d7cec9ad
DB
1910 if (server->certinfo.empty ())
1911 continue;
7543625d
DB
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);
c77af0d0
DB
1919
1920 // Search the client-side database of trusted servers.
aeb9cc10 1921 db_cert = PK11_FindCertFromNickname (nickname, NULL);
c77af0d0
DB
1922 if (! db_cert)
1923 {
1924 // No trusted servers. Not an error, but issue a status message.
b2d017e6 1925 if (s.verbose >= 2)
7543625d 1926 trust_already_in_place (*server, server_list, cert_db_path, true/*revoking*/);
c77af0d0
DB
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 {
aeb9cc10 1940 clog << _F("Unable to query certificate database %s: ",
58a834b1 1941 cert_db_path.c_str()) << endl;
c77af0d0
DB
1942 PORT_SetError (SEC_ERROR_LIBRARY_FAILURE);
1943 nssError ();
1944 goto cleanup;
1945 }
1946
48e74294
DB
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.
b2d017e6 1971 if (s.verbose >= 2)
48e74294
DB
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:
ef187c96 1989 assert(!certs);
48e74294
DB
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.
1997static void
1998get_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 {
7f548800 2007 if (s.verbose >= 5)
48e74294
DB
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 {
7f548800 2029 if (s.verbose >= 5)
48e74294
DB
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}
48e74294
DB
2103
2104// Utility Functions.
2105//-----------------------------------------------------------------------
2106ostream &operator<< (ostream &s, const compile_server_info &i)
2107{
7f548800
DB
2108 // Don't print empty information
2109 if (i.empty ())
2110 return s;
2111
48e74294
DB
2112 s << " host=";
2113 if (! i.host_name.empty ())
2114 s << i.host_name;
2115 else
2116 s << "unknown";
0ec2c5bf
DB
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 }
48e74294
DB
2140 else
2141 s << "offline";
2142 s << " port=";
6817f41a
DB
2143 if (i.port != 0)
2144 s << i.port;
48e74294 2145 else
0ec2c5bf 2146 s << "unknown";
48e74294
DB
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\"";
cd1418c7 2162 if (! i.mok_fingerprints.empty ())
b3367f63
DS
2163 {
2164 // FIXME: Yikes, this output is ugly. Perhaps the server output
2165 // needs a more structured approach.
cd1418c7
DS
2166 s << " mok_fingerprints=\"";
2167 vector<string>::const_iterator it;
2168 for (it = i.mok_fingerprints.begin (); it != i.mok_fingerprints.end ();
2169 it++)
b3367f63 2170 {
cd1418c7 2171 if (it != i.mok_fingerprints.begin ())
b3367f63 2172 s << ", ";
cd1418c7 2173 s << *it;
b3367f63
DS
2174 }
2175 s << "\"";
0ec2c5bf 2176 }
b3367f63
DS
2177 return s;
2178}
48e74294 2179
34aa74ae
DB
2180ostream &operator<< (ostream &s, const vector<compile_server_info> &v)
2181{
7f548800
DB
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 }
34aa74ae
DB
2194 return s;
2195}
2196
7f548800
DB
2197PRNetAddr &
2198copyNetAddr (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
0ec2c5bf 2214bool
7f548800 2215operator== (const PRNetAddr &x, const PRNetAddr &y)
0ec2c5bf
DB
2216{
2217 // Same address family?
2218 if (x.raw.family != y.raw.family)
7f548800 2219 return false;
0ec2c5bf 2220
0ec2c5bf
DB
2221 switch (x.raw.family)
2222 {
2223 case PR_AF_INET6:
7f548800
DB
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;
0ec2c5bf 2229 case PR_AF_INET:
7f548800 2230 return x.inet.ip == y.inet.ip;
0ec2c5bf
DB
2231 default:
2232 break;
2233 }
7f548800
DB
2234 return false;
2235}
2236
2237bool
2238operator!= (const PRNetAddr &x, const PRNetAddr &y)
2239{
2240 return !(x == y);
0ec2c5bf
DB
2241}
2242
2243static PRIPv6Addr &
2244copyAddress (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
48e74294
DB
2252// Return the default server specification, used when none is given on the
2253// command line.
2254static string
2255default_server_spec (const systemtap_session &s)
2256{
0ec2c5bf 2257 // If --privilege=X has been used, where X is not stapdev,
48e74294
DB
2258 // the default is online,trusted,compatible,signer
2259 // otherwise
f6be7c06 2260 // the default is online,trusted,compatible
48e74294
DB
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";
f66bb29a 2267 if (! pr_contains (s.privilege, pr_stapdev))
48e74294
DB
2268 working_string += ",signer";
2269 return working_string;
2270}
2271
2272static int
2273server_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 {
2713ea24
CM
2324 // XXX PR13274 needs-session to use print_warning()
2325 clog << _F("WARNING: unsupported compile server property: %s", property.c_str())
48e74294
DB
2326 << endl;
2327 }
2328 }
2329 return pmask;
2330}
2331
2332void
2333query_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
2340static void
2341query_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.
2393void
2394manage_server_trust (systemtap_session &s)
2395{
2396 // This function should do nothing if we don't have NSS.
48e74294
DB
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
2713ea24 2446 s.print_warning("Unrecognized server trust specification: " + *i);
48e74294
DB
2447 }
2448 if (error)
2449 return;
2450
2451 // Make sure NSPR is initialized
2452 s.NSPR_init ();
c77af0d0 2453
48e74294
DB
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*/);
c77af0d0 2457
48e74294
DB
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 }
c77af0d0 2465
48e74294
DB
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 }
c77af0d0 2485
48e74294
DB
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 ");
c77af0d0 2498 else
48e74294
DB
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')
c77af0d0 2515 {
48e74294
DB
2516 clog << _("Server trust unchanged") << endl;
2517 return;
c77af0d0 2518 }
48e74294 2519 }
c77af0d0 2520
48e74294
DB
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 }
48e74294 2542}
c77af0d0 2543
48e74294
DB
2544static compile_server_cache*
2545cscache(systemtap_session& s)
2546{
2547 if (!s.server_cache)
2548 s.server_cache = new compile_server_cache();
2549 return s.server_cache;
c77af0d0
DB
2550}
2551
b0e5fa85 2552static void
85007c04 2553get_server_info (
1f9938b9 2554 systemtap_session &s,
85007c04
DB
2555 int pmask,
2556 vector<compile_server_info> &servers
2557)
2558{
2559 // Get information on compile servers matching the requested criteria.
0d1534e8 2560 // The order of queries is significant. Accumulating queries must go first
7543625d
DB
2561 // followed by accumulating/filtering queries.
2562 bool keep = false;
2563 if (((pmask & compile_server_all)))
0d1534e8 2564 {
7543625d
DB
2565 get_all_server_info (s, servers);
2566 keep = true;
0d1534e8 2567 }
7543625d 2568 // Add the specified servers, if requested
2e15a955
DB
2569 if ((pmask & compile_server_specified))
2570 {
7543625d
DB
2571 get_specified_server_info (s, servers);
2572 keep = true;
2e15a955 2573 }
48e74294 2574 // Now filter or accumulate the list depending on whether a query has
7543625d 2575 // already been made.
85007c04
DB
2576 if ((pmask & compile_server_online))
2577 {
7543625d
DB
2578 get_or_keep_online_server_info (s, servers, keep);
2579 keep = true;
0d1534e8
DB
2580 }
2581 if ((pmask & compile_server_trusted))
2582 {
7543625d
DB
2583 get_or_keep_trusted_server_info (s, servers, keep);
2584 keep = true;
85007c04 2585 }
0d1534e8
DB
2586 if ((pmask & compile_server_signer))
2587 {
7543625d
DB
2588 get_or_keep_signing_server_info (s, servers, keep);
2589 keep = true;
0d1534e8 2590 }
85007c04
DB
2591 if ((pmask & compile_server_compatible))
2592 {
7543625d
DB
2593 get_or_keep_compatible_server_info (s, servers, keep);
2594 keep = true;
85007c04 2595 }
0d1534e8
DB
2596}
2597
2598// Get information about all online servers as well as servers trusted
2599// as SSL peers and servers trusted as signers.
b0e5fa85 2600static void
0d1534e8
DB
2601get_all_server_info (
2602 systemtap_session &s,
2603 vector<compile_server_info> &servers
2604)
2605{
7f548800
DB
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);
85007c04
DB
2624}
2625
b0e5fa85 2626static void
2e15a955
DB
2627get_default_server_info (
2628 systemtap_session &s,
2629 vector<compile_server_info> &servers
2630)
1f9938b9 2631{
7f548800
DB
2632 if (s.verbose >= 3)
2633 clog << _("Using the default servers") << endl;
2634
eff14db3
JS
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;
2e15a955 2638 if (default_servers.empty ())
1f9938b9 2639 {
c77af0d0
DB
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.
0d1534e8
DB
2643 int pmask = server_spec_to_pmask (default_server_spec (s));
2644 get_server_info (s, pmask, default_servers);
7f548800
DB
2645
2646 if (s.verbose >= 3)
2647 {
2648 clog << _("Default servers are:") << endl;
2649 clog << default_servers;
2650 }
2e15a955 2651 }
1f9938b9 2652
c77af0d0 2653 // Add the information, but not duplicates.
7543625d 2654 add_server_info (default_servers, servers);
2e15a955 2655}
1f9938b9 2656
49dbe419 2657static bool
0ec2c5bf 2658isPort (const char *pstr, compile_server_info &server_info)
49dbe419
DB
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 }
6817f41a
DB
2668 server_info.port = p;
2669 server_info.fully_specified = true;
49dbe419
DB
2670 return true;
2671}
2672
2673static bool
2674isIPv6 (const string &server, compile_server_info &server_info)
2675{
0ec2c5bf 2676 // An IPv6 address is 8 hex components separated by colons.
49dbe419 2677 // One contiguous block of zero segments in the address may be elided using ::.
0ec2c5bf 2678 // An interface may be specified by appending %IF_NAME to the address (e.g. %eth0).
0ec2c5bf
DB
2679 // A port may be specified by enclosing the ip address in [] and adding :<port>.
2680 // Allow a bracketed address without a port.
49dbe419 2681 assert (! server.empty());
0ec2c5bf
DB
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 }
49dbe419
DB
2698
2699 // Find out how many components there are. The maximum is 8
2700 unsigned empty = 0;
2701 vector<string> components;
0ec2c5bf 2702 tokenize_full (ip, components, ":");
49dbe419
DB
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.
0ec2c5bf 2708 string interface;
49dbe419
DB
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 }
0ec2c5bf
DB
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 }
49dbe419
DB
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
6817f41a
DB
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;
0ec2c5bf 2752
49dbe419 2753 // Examine the optional port
0ec2c5bf 2754 if (portIx != string::npos)
49dbe419 2755 {
0ec2c5bf
DB
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
49dbe419 2761
0ec2c5bf
DB
2762 port = port.substr (1);
2763 if (! isPort (port.c_str(), server_info))
2764 return false; // not a valid port
2765 }
49dbe419 2766 }
0ec2c5bf 2767 else
6817f41a 2768 server_info.port = 0;
0ec2c5bf 2769
49dbe419
DB
2770 return true; // valid IPv6 address.
2771}
2772
2773static bool
2774isIPv4 (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
6817f41a
DB
2786 // Separate the address from the port (if any).
2787 string addr;
49dbe419
DB
2788 string port;
2789 if (components.size() <= 1)
6817f41a 2790 addr = server;
49dbe419 2791 else {
6817f41a 2792 addr = components[0];
49dbe419
DB
2793 port = components[1];
2794 }
2795
6817f41a 2796 // Separate the address components.
49dbe419
DB
2797 // There must be exactly 4 components.
2798 components.clear ();
6817f41a 2799 tokenize (addr, components, ".");
49dbe419
DB
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;
36eb80f3 2810 long p = strtol (components[i].c_str(), & estr, 10);
49dbe419
DB
2811 if (errno != 0 || *estr != '\0' || p < 0 || p > 255)
2812 return false; // Not a valid IPv4 address
2813 }
2814
6817f41a
DB
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;
0ec2c5bf 2819
49dbe419
DB
2820 // Examine the optional port
2821 if (! port.empty ()) {
0ec2c5bf 2822 if (! isPort (port.c_str(), server_info))
49dbe419
DB
2823 return false; // not a valid port
2824 }
0ec2c5bf 2825 else
6817f41a 2826 server_info.port = 0;
49dbe419 2827
49dbe419
DB
2828 return true; // valid IPv4 address.
2829}
2830
2831static bool
2832isCertSerialNumber (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:
0ec2c5bf 2844 if (! isPort (components.back().c_str(), server_info))
49dbe419
DB
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
2858static bool
2859isDomain (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:
0ec2c5bf 2870 if (! isPort (components.back().c_str(), server_info))
49dbe419
DB
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
b0e5fa85 2884static void
2e15a955
DB
2885get_specified_server_info (
2886 systemtap_session &s,
1a7f7b3f
DB
2887 vector<compile_server_info> &servers,
2888 bool no_default
2e15a955
DB
2889)
2890{
eff14db3
JS
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;
2e15a955
DB
2894 if (specified_servers.empty ())
2895 {
0d1534e8
DB
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
0ec2c5bf 2900 // If --use-server was not specified at all, then return info for the
2e15a955
DB
2901 // default server list.
2902 if (s.specified_servers.empty ())
2903 {
7f548800
DB
2904 if (s.verbose >= 3)
2905 clog << _("No servers specified") << endl;
1a7f7b3f
DB
2906 if (! no_default)
2907 get_default_server_info (s, specified_servers);
2e15a955
DB
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 {
7543625d 2916 string &server = s.specified_servers[i];
0ec2c5bf
DB
2917
2918 // If no specific server(s) specified, then use the default servers.
2e15a955
DB
2919 if (server.empty ())
2920 {
7f548800
DB
2921 if (s.verbose >= 3)
2922 clog << _("No servers specified") << endl;
1a7f7b3f
DB
2923 if (! no_default)
2924 get_default_server_info (s, specified_servers);
7543625d 2925 continue;
2e15a955 2926 }
0ec2c5bf 2927
49dbe419
DB
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.
7543625d 2936 compile_server_info server_info;
6817f41a 2937 vector<compile_server_info> resolved_servers;
49dbe419 2938 if (isIPv6 (server, server_info) || isIPv4 (server, server_info) ||
6817f41a 2939 isCertSerialNumber (server, server_info))
7543625d 2940 {
6817f41a
DB
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))
49dbe419 2946 {
6817f41a
DB
2947 // Try to resolve the given name.
2948 resolve_host (s, server_info, resolved_servers);
49dbe419
DB
2949 }
2950 else
2951 {
2952 clog << _F("Invalid server specification for --use-server: %s", server.c_str())
2953 << endl;
6817f41a 2954 continue;
49dbe419 2955 }
6817f41a
DB
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);
2e15a955
DB
3000 } // Loop over --use-server options
3001 } // -- use-server specified
7f548800
DB
3002
3003 if (s.verbose >= 2)
3004 {
3005 clog << _("All specified servers:") << endl;
3006 clog << specified_servers;
3007 }
2e15a955
DB
3008 } // Server information is not cached
3009
c77af0d0 3010 // Add the information, but not duplicates.
7543625d 3011 add_server_info (specified_servers, servers);
1f9938b9
DB
3012}
3013
b0e5fa85 3014static void
0d1534e8 3015get_or_keep_trusted_server_info (
1f9938b9 3016 systemtap_session &s,
7543625d
DB
3017 vector<compile_server_info> &servers,
3018 bool keep
85007c04
DB
3019)
3020{
7543625d
DB
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
eff14db3
JS
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;
0d1534e8 3029 if (trusted_servers.empty ())
85007c04 3030 {
0d1534e8
DB
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
c77af0d0 3035 // Check the private database first.
aeb9cc10 3036 string cert_db_path = private_ssl_cert_db_path ();
c77af0d0 3037 get_server_info_from_db (s, trusted_servers, cert_db_path);
0d1534e8 3038
c77af0d0
DB
3039 // Now check the global database.
3040 cert_db_path = global_ssl_cert_db_path ();
0d1534e8 3041 get_server_info_from_db (s, trusted_servers, cert_db_path);
7f548800
DB
3042
3043 if (s.verbose >= 5)
3044 {
3045 clog << _("All servers trusted as ssl peers:") << endl;
3046 clog << trusted_servers;
3047 }
0d1534e8
DB
3048 } // Server information is not cached
3049
7543625d 3050 if (keep)
0d1534e8
DB
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 {
c77af0d0 3058 // Add the information, but not duplicates.
7543625d 3059 add_server_info (trusted_servers, servers);
0d1534e8
DB
3060 }
3061}
3062
b0e5fa85 3063static void
0d1534e8
DB
3064get_or_keep_signing_server_info (
3065 systemtap_session &s,
7543625d
DB
3066 vector<compile_server_info> &servers,
3067 bool keep
0d1534e8
DB
3068)
3069{
7543625d
DB
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
eff14db3
JS
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;
0d1534e8
DB
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
0d1534e8 3084 // For all users, check the global database.
c77af0d0 3085 string cert_db_path = signing_cert_db_path ();
0d1534e8 3086 get_server_info_from_db (s, signing_servers, cert_db_path);
7f548800
DB
3087
3088 if (s.verbose >= 5)
3089 {
3090 clog << _("All servers trusted as module signers:") << endl;
3091 clog << signing_servers;
3092 }
0d1534e8
DB
3093 } // Server information is not cached
3094
7543625d 3095 if (keep)
0d1534e8
DB
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 {
c77af0d0 3103 // Add the information, but not duplicates.
7543625d 3104 add_server_info (signing_servers, servers);
0d1534e8
DB
3105 }
3106}
3107
0d1534e8 3108
b0e5fa85 3109static void
7543625d 3110get_or_keep_compatible_server_info (
0d1534e8 3111 systemtap_session &s,
7543625d
DB
3112 vector<compile_server_info> &servers,
3113 bool keep
0d1534e8
DB
3114)
3115{
3116#if HAVE_AVAHI
7543625d
DB
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
0d1534e8
DB
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.
7543625d
DB
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!!
0d1534e8
DB
3138 for (unsigned i = 0; i < servers.size (); /**/)
3139 {
c77af0d0 3140 // Retain empty entries.
7543625d
DB
3141 assert (! servers[i].empty ());
3142
3143 // Check the target of the server.
3144 if (servers[i].sysinfo != s.kernel_release + " " + s.architecture)
85007c04 3145 {
7543625d
DB
3146 // Target platform mismatch.
3147 servers.erase (servers.begin () + i);
3148 continue;
85007c04
DB
3149 }
3150
b3367f63
DS
3151 // If the client requires secure boot signing, make sure the
3152 // server has the right MOK.
cd1418c7 3153 if (! s.mok_fingerprints.empty ())
b3367f63
DS
3154 {
3155 // This server has no MOKs.
cd1418c7 3156 if (servers[i].mok_fingerprints.empty ())
b3367f63
DS
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;
cd1418c7 3166 for (it = s.mok_fingerprints.begin(); it != s.mok_fingerprints.end(); it++)
b3367f63 3167 {
cd1418c7
DS
3168 if (find(servers[i].mok_fingerprints.begin(),
3169 servers[i].mok_fingerprints.end(), *it)
3170 != servers[i].mok_fingerprints.end ())
b3367f63
DS
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
85007c04
DB
3185 // The server is compatible. Leave it in the list.
3186 ++i;
3187 }
0d1534e8
DB
3188#else // ! HAVE_AVAHI
3189 // Without Avahi, we can't obtain the target platform of the server.
3190 // Issue a warning.
b2d017e6
DB
3191 if (s.verbose >= 2)
3192 clog << _("Unable to detect server compatibility without avahi") << endl;
7543625d
DB
3193 if (keep)
3194 servers.clear ();
0d1534e8 3195#endif
85007c04
DB
3196}
3197
b0e5fa85 3198static void
7543625d 3199keep_server_info_with_cert_and_port (
822a6a3d 3200 systemtap_session &,
7543625d
DB
3201 const compile_server_info &server,
3202 vector<compile_server_info> &servers
3203)
0d1534e8 3204{
7543625d 3205 assert (! server.certinfo.empty ());
0d1534e8 3206
7543625d
DB
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 (); /**/)
0d1534e8 3211 {
7543625d
DB
3212 // Retain empty entries.
3213 if (servers[i].empty ())
3214 {
3215 ++i;
3216 continue;
3217 }
3218 if (servers[i].certinfo == server.certinfo &&
6817f41a
DB
3219 (servers[i].port == 0 || server.port == 0 ||
3220 servers[i].port == server.port))
7543625d
DB
3221 {
3222 // If the server is not online, then use the specified
3223 // port, if any.
6817f41a
DB
3224 if (servers[i].port == 0)
3225 {
3226 servers[i].port = server.port;
3227 servers[i].fully_specified = server.fully_specified;
3228 }
7543625d
DB
3229 ++i;
3230 continue;
3231 }
3232 // The item does not match. Delete it.
3233 servers.erase (servers.begin () + i);
0d1534e8 3234 }
7543625d 3235}
0d1534e8 3236
0ec2c5bf 3237// Obtain missing host name or ip address, if any. Return 0 on success.
7543625d
DB
3238static void
3239resolve_host (
7f548800 3240 systemtap_session& s,
7543625d
DB
3241 compile_server_info &server,
3242 vector<compile_server_info> &resolved_servers
3243)
3244{
2436ada9
JL
3245 vector<resolved_host>& cached_hosts = cscache(s)->resolved_hosts[server.host_name];
3246 if (cached_hosts.empty ())
0ec2c5bf 3247 {
7f548800
DB
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;
2e15a955 3253
7f548800
DB
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);
7543625d 3259
7f548800
DB
3260 // Failure to resolve will result in an appropriate message later, if other methods fail.
3261 if (rc != 0)
49dbe419 3262 {
7f548800
DB
3263 if (s.verbose >= 6)
3264 clog << _F("%s not found: %s", lookup_name, gai_strerror (rc)) << endl;
49dbe419 3265 }
0ec2c5bf 3266 else
7f548800
DB
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 {
2436ada9 3272 PRNetAddr new_address;
7f548800
DB
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;
2436ada9
JL
3279 new_address.inet.family = PR_AF_INET;
3280 new_address.inet.ip = ip->sin_addr.s_addr;
7f548800
DB
3281 }
3282 else if (ai->ai_family == AF_INET6)
3283 {
3284 // IPv6 Address
3285 struct sockaddr_in6 *ip = (struct sockaddr_in6 *)ai->ai_addr;
2436ada9
JL
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);
7f548800
DB
3289 }
3290 else
3291 continue;
7543625d 3292
7f548800
DB
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);
2436ada9
JL
3297 if (status != 0)
3298 hbuf[0] = '\0';
2e15a955 3299
82c6d474 3300 cached_hosts.push_back(resolved_host(hbuf, new_address));
7f548800
DB
3301 }
3302 }
3303 if (addr_info)
3304 freeaddrinfo (addr_info); // free the linked list
2436ada9
JL
3305 }
3306
6817f41a 3307 // If no addresses were resolved, then return the info we were given.
2436ada9 3308 if (cached_hosts.empty())
6817f41a
DB
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;
2436ada9 3318
6817f41a
DB
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 }
7f548800 3335
6817f41a
DB
3336 if (s.verbose >= 6)
3337 {
3338 clog << _F("%s resolves to:", server.host_name.c_str()) << endl;
3339 clog << new_servers;
3340 }
2e15a955 3341
6817f41a
DB
3342 add_server_info (new_servers, resolved_servers);
3343 }
2e15a955
DB
3344}
3345
85007c04 3346#if HAVE_AVAHI
131f914e
DB
3347// Avahi API Callbacks.
3348//-----------------------------------------------------------------------
85007c04
DB
3349struct browsing_context {
3350 AvahiSimplePoll *simple_poll;
3351 AvahiClient *client;
3352 vector<compile_server_info> *servers;
3353};
3354
e5474ed5 3355// Get the value of the requested key from the Avahi string list.
0d1534e8 3356static string
e5474ed5 3357get_value_from_avahi_string_list (AvahiStringList *strlst, const string &key)
0d1534e8 3358{
e5474ed5
DS
3359 AvahiStringList *p = avahi_string_list_find (strlst, key.c_str ());
3360 if (p == NULL)
0d1534e8 3361 {
e5474ed5
DS
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);
0d1534e8
DB
3371 return "";
3372 }
3373
e5474ed5
DS
3374 string value = v;
3375 avahi_free (k);
3376 avahi_free (v);
3377 return value;
0d1534e8
DB
3378}
3379
3c04551e
DS
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.
3382static void
3383get_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
85007c04
DB
3407extern "C"
3408void resolve_callback(
3409 AvahiServiceResolver *r,
0ec2c5bf
DB
3410 AvahiIfIndex interface,
3411 AvahiProtocol protocol,
85007c04
DB
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,
822a6a3d 3420 AvahiLookupResultFlags /*flags*/,
0ec2c5bf
DB
3421 AVAHI_GCC_UNUSED void* userdata)
3422 {
3423 PRStatus prStatus;
85007c04
DB
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:
51c908ff
DB
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;
85007c04
DB
3437
3438 case AVAHI_RESOLVER_FOUND: {
49dbe419 3439 compile_server_info info;
0ec2c5bf
DB
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) {
18b89f52 3452 info.address.ipv6.family = PR_AF_INET6;
0ec2c5bf 3453 info.address.ipv6.scope_id = interface;
6817f41a 3454 info.port = port;
0ec2c5bf
DB
3455 }
3456 else if (protocol == AVAHI_PROTO_INET) {
18b89f52 3457 info.address.inet.family = PR_AF_INET;
6817f41a 3458 info.port = port;
49dbe419
DB
3459 }
3460 else
0ec2c5bf
DB
3461 break;
3462
3463 // Save the host name.
3464 info.host_name = host_name;
3465
3466 // Save the text tags.
e5474ed5
DS
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");
49dbe419
DB
3470 if (info.version.empty ())
3471 info.version = "1.0"; // default version is 1.0
49dbe419 3472
b3367f63
DS
3473 // The server might provide one or more MOK certificate's
3474 // info.
3c04551e
DS
3475 get_values_from_avahi_string_list(txt, "mok_info",
3476 info.mok_fingerprints);
b3367f63 3477
49dbe419
DB
3478 // Add this server to the list of discovered servers.
3479 add_server_info (info, *servers);
51c908ff
DB
3480 break;
3481 }
3482 default:
3483 break;
85007c04
DB
3484 }
3485
3486 avahi_service_resolver_free(r);
3487}
3488
3489extern "C"
3490void 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:
aeb9cc10 3510 clog << _F("Avahi browse failed: %s",
58a834b1
LB
3511 avahi_strerror(avahi_client_errno(avahi_service_browser_get_client(b))))
3512 << endl;
85007c04
DB
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))) {
aeb9cc10 3523 clog << _F("Failed to resolve service '%s': %s",
58a834b1 3524 name, avahi_strerror(avahi_client_errno(c))) << endl;
85007c04
DB
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
3535extern "C"
3536void 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) {
aeb9cc10 3544 clog << _F("Avahi Server connection failure: %s", avahi_strerror(avahi_client_errno(c))) << endl;
85007c04
DB
3545 avahi_simple_poll_quit(simple_poll);
3546 }
3547}
3548
3549extern "C"
3550void 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
b0e5fa85 3557static void
7543625d
DB
3558get_or_keep_online_server_info (
3559 systemtap_session &s,
3560 vector<compile_server_info> &servers,
3561 bool keep
3562)
85007c04 3563{
7543625d
DB
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
eff14db3
JS
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;
2e15a955
DB
3572 if (online_servers.empty ())
3573 {
0d1534e8
DB
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
2e15a955 3578 // Must predeclare these due to jumping on error to fail:
0ec2c5bf 3579 vector<compile_server_info> avahi_servers;
1f9938b9 3580
2e15a955
DB
3581 // Initialize.
3582 AvahiClient *client = NULL;
3583 AvahiServiceBrowser *sb = NULL;
85007c04 3584
2e15a955
DB
3585 // Allocate main loop object.
3586 AvahiSimplePoll *simple_poll;
3587 if (!(simple_poll = avahi_simple_poll_new()))
3588 {
aeb9cc10 3589 clog << _("Failed to create Avahi simple poll object") << endl;
2e15a955
DB
3590 goto fail;
3591 }
3592 browsing_context context;
3593 context.simple_poll = simple_poll;
0ec2c5bf 3594 context.servers = & avahi_servers;
2e15a955
DB
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 {
aeb9cc10 3605 clog << _F("Failed to create Avahi client: %s",
58a834b1 3606 avahi_strerror(error)) << endl;
2e15a955
DB
3607 goto fail;
3608 }
3609 context.client = client;
85007c04 3610
2e15a955
DB
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 {
aeb9cc10 3617 clog << _F("Failed to create Avahi service browser: %s",
58a834b1 3618 avahi_strerror(avahi_client_errno(client))) << endl;
2e15a955
DB
3619 goto fail;
3620 }
85007c04 3621
3e133fd8 3622 // Timeout after 0.5 seconds.
2e15a955
DB
3623 struct timeval tv;
3624 avahi_simple_poll_get(simple_poll)->timeout_new(
85007c04 3625 avahi_simple_poll_get(simple_poll),
3e133fd8 3626 avahi_elapse_time(&tv, 1000/2, 0),
2e15a955
DB
3627 timeout_callback,
3628 & context);
85007c04 3629
2e15a955
DB
3630 // Run the main loop.
3631 avahi_simple_poll_loop(simple_poll);
1f9938b9 3632
7f548800
DB
3633 if (s.verbose >= 6)
3634 {
3635 clog << _("Avahi reports the following servers online:") << endl;
3636 clog << avahi_servers;
3637 }
3638
0ec2c5bf
DB
3639 // Merge with the list of servers, as obtained by avahi.
3640 add_server_info (avahi_servers, online_servers);
3641
2e15a955
DB
3642 fail:
3643 // Cleanup.
24a060a0
DB
3644 if (client) {
3645 // Also frees the service browser
85007c04 3646 avahi_client_free(client);
24a060a0 3647 }
2e15a955 3648 if (simple_poll)
85007c04 3649 avahi_simple_poll_free(simple_poll);
0d1534e8
DB
3650#else // ! HAVE_AVAHI
3651 // Without Avahi, we can't detect online servers. Issue a warning.
b2d017e6
DB
3652 if (s.verbose >= 2)
3653 clog << _("Unable to detect online servers without avahi") << endl;
0d1534e8 3654#endif // ! HAVE_AVAHI
7f548800
DB
3655
3656 if (s.verbose >= 5)
3657 {
3658 clog << _("All online servers:") << endl;
3659 clog << online_servers;
3660 }
2e15a955
DB
3661 } // Server information is not cached.
3662
7543625d 3663 if (keep)
0d1534e8
DB
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 {
c77af0d0 3671 // Add the information, but not duplicates.
7543625d 3672 add_server_info (online_servers, servers);
0d1534e8 3673 }
85007c04 3674}
2e15a955 3675
0d1534e8
DB
3676// Add server info to a list, avoiding duplicates. Merge information from
3677// two duplicate items.
b0e5fa85 3678static void
2e15a955 3679add_server_info (
7543625d 3680 const compile_server_info &info, vector<compile_server_info>& target
2e15a955
DB
3681)
3682{
7543625d
DB
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)
2e15a955 3690 {
7543625d
DB
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.
3703static void
3704add_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.
3718static void
3719keep_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;
2e15a955 3744 }
2e15a955 3745}
0d1534e8
DB
3746
3747// Filter the second vector by keeping information in common with the first
3748// vector.
b0e5fa85 3749static void
0d1534e8
DB
3750keep_common_server_info (
3751 const vector<compile_server_info> &info_to_keep,
3752 vector<compile_server_info> &filtered_info
3753)
3754{
7543625d 3755 // The vector may change size as we go. Be careful!!
0d1534e8
DB
3756 for (unsigned i = 0; i < filtered_info.size (); /**/)
3757 {
7543625d
DB
3758 // Retain empty entries.
3759 if (filtered_info[i].empty ())
0d1534e8 3760 {
0d1534e8
DB
3761 ++i;
3762 continue;
3763 }
7543625d
DB
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 }
0d1534e8 3773
7543625d
DB
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);
0d1534e8
DB
3780 }
3781}
3782
3783// Merge two compile server info items.
b0e5fa85 3784static void
0d1534e8
DB
3785merge_server_info (
3786 const compile_server_info &source,
3787 compile_server_info &target
3788)
3789{
6817f41a
DB
3790 // Copy the host name if the source has one.
3791 if (! source.host_name.empty())
0d1534e8 3792 target.host_name = source.host_name;
7f548800
DB
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);
6817f41a
DB
3798 if (target.port == 0)
3799 {
3800 target.port = source.port;
3801 target.fully_specified = source.fully_specified;
3802 }
0d1534e8
DB
3803 if (target.sysinfo.empty ())
3804 target.sysinfo = source.sysinfo;
85717fe6
DB
3805 if (target.version.empty ())
3806 target.version = source.version;
0d1534e8
DB
3807 if (target.certinfo.empty ())
3808 target.certinfo = source.certinfo;
3809}
3810
7543625d
DB
3811#if 0 // not used right now
3812// Merge compile server info from one item into a vector.
b0e5fa85 3813static void
0d1534e8 3814merge_server_info (
7543625d 3815 const compile_server_info &source,
0d1534e8
DB
3816 vector<compile_server_info> &target
3817)
3818{
7543625d
DB
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 }
0d1534e8
DB
3826}
3827
7543625d 3828// Merge compile server from one vector into another.
b0e5fa85 3829static void
0d1534e8
DB
3830merge_server_info (
3831 const vector<compile_server_info> &source,
7543625d 3832 vector <compile_server_info> &target
0d1534e8
DB
3833)
3834{
3835 for (vector<compile_server_info>::const_iterator i = source.begin ();
7543625d
DB
3836 i != source.end ();
3837 ++i)
3838 merge_server_info (*i, target);
0d1534e8 3839}
7543625d 3840#endif
c4fd15b4 3841#endif // HAVE_NSS
2e46c01a
JS
3842
3843/* vim: set sw=2 ts=8 cino=>4,n-2,{2,^-2,t0,(0,u0,w1,M1 : */
This page took 0.494016 seconds and 5 git commands to generate.