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