]>
Commit | Line | Data |
---|---|---|
2dce8c42 DB |
1 | /* |
2 | Compile server client functions | |
3 | Copyright (C) 2010 Red Hat Inc. | |
4 | ||
5 | This file is part of systemtap, and is free software. You can | |
6 | redistribute it and/or modify it under the terms of the GNU General | |
7 | Public License (GPL); either version 2, or (at your option) any | |
8 | later version. | |
9 | */ | |
10 | #include "config.h" | |
11 | #include "session.h" | |
aa4d21c0 | 12 | #include "csclient.h" |
2fad97fd | 13 | #include "util.h" |
aa4d21c0 | 14 | #include "sys/sdt.h" |
2dce8c42 | 15 | |
aa4d21c0 | 16 | #include <sys/times.h> |
2dce8c42 | 17 | #include <vector> |
2fad97fd DB |
18 | #include <fstream> |
19 | #include <sstream> | |
79efe7f8 | 20 | #include <cassert> |
b0e5fa85 DB |
21 | #include <cstdlib> |
22 | #include <cstdio> | |
23 | #include <iomanip> | |
24 | #include <algorithm> | |
79efe7f8 FCE |
25 | |
26 | extern "C" { | |
004a4482 | 27 | #include <linux/limits.h> |
3eee0879 | 28 | #include <sys/time.h> |
79efe7f8 | 29 | #include <glob.h> |
1f9938b9 | 30 | #include <limits.h> |
f88498d9 DS |
31 | #include <sys/socket.h> |
32 | #include <netdb.h> | |
33 | #include <arpa/inet.h> | |
0d1534e8 | 34 | #include <pwd.h> |
79efe7f8 | 35 | } |
2dce8c42 DB |
36 | |
37 | #if HAVE_AVAHI | |
38 | extern "C" { | |
2dce8c42 DB |
39 | #include <avahi-client/client.h> |
40 | #include <avahi-client/lookup.h> | |
41 | ||
42 | #include <avahi-common/simple-watch.h> | |
43 | #include <avahi-common/malloc.h> | |
44 | #include <avahi-common/error.h> | |
45 | #include <avahi-common/timeval.h> | |
3eee0879 | 46 | } |
b0e5fa85 | 47 | #endif // HAVE_AVAHI |
2fad97fd DB |
48 | |
49 | #if HAVE_NSS | |
3eee0879 | 50 | extern "C" { |
2fad97fd DB |
51 | #include <ssl.h> |
52 | #include <nspr.h> | |
53 | #include <nss.h> | |
c77af0d0 | 54 | #include <certdb.h> |
2fad97fd DB |
55 | #include <pk11pub.h> |
56 | #include <prerror.h> | |
57 | #include <secerr.h> | |
58 | #include <sslerr.h> | |
59 | ||
60 | #include "nsscommon.h" | |
2dce8c42 | 61 | } |
c77af0d0 | 62 | #endif // HAVE_NSS |
2dce8c42 | 63 | |
aa4d21c0 | 64 | using namespace std; |
2dce8c42 | 65 | |
b0e5fa85 DB |
66 | // Information about compile servers. |
67 | struct compile_server_info | |
68 | { | |
69 | compile_server_info () : port (0) {} | |
70 | ||
71 | std::string host_name; | |
72 | std::string ip_address; | |
73 | unsigned short port; | |
74 | std::string sysinfo; | |
75 | std::string certinfo; | |
76 | ||
77 | bool operator== (const compile_server_info &that) const | |
78 | { | |
79 | // The host name must always match. | |
80 | if (this->host_name != that.host_name) | |
81 | return false; | |
82 | // Compare the other fields only if they have both been set. | |
83 | if (this->port != 0 && that.port != 0 && this->port != that.port) | |
84 | return false; | |
85 | if (! this->sysinfo.empty () && ! that.sysinfo.empty () && | |
86 | this->sysinfo != that.sysinfo) | |
87 | return false; | |
88 | if (! this->certinfo.empty () && ! that.certinfo.empty () && | |
89 | this->certinfo != that.certinfo) | |
90 | return false; | |
91 | return true; | |
92 | } | |
93 | }; | |
94 | ||
95 | // For filtering queries. | |
96 | enum compile_server_properties { | |
97 | compile_server_all = 0x1, | |
98 | compile_server_trusted = 0x2, | |
99 | compile_server_online = 0x4, | |
100 | compile_server_compatible = 0x8, | |
101 | compile_server_signer = 0x10, | |
102 | compile_server_specified = 0x20 | |
103 | }; | |
104 | ||
105 | // Static functions. | |
106 | static void query_server_status (systemtap_session &s, const string &status_string); | |
107 | static void add_server_trust (systemtap_session &s, const string &cert_db_path, const vector<compile_server_info> &server_list); | |
108 | static void revoke_server_trust (systemtap_session &s, const string &cert_db_path, const vector<compile_server_info> &server_list); | |
109 | ||
110 | static void get_server_info (systemtap_session &s, int pmask, vector<compile_server_info> &servers); | |
111 | static void get_all_server_info (systemtap_session &s, vector<compile_server_info> &servers); | |
112 | static void get_default_server_info (systemtap_session &s, vector<compile_server_info> &servers); | |
113 | static void get_specified_server_info (systemtap_session &s, vector<compile_server_info> &servers); | |
114 | static void get_or_keep_online_server_info (systemtap_session &s, vector<compile_server_info> &servers); | |
115 | static void get_or_keep_trusted_server_info (systemtap_session &s, vector<compile_server_info> &servers); | |
116 | static void get_or_keep_signing_server_info (systemtap_session &s, vector<compile_server_info> &servers); | |
117 | static void keep_compatible_server_info (systemtap_session &s, vector<compile_server_info> &servers); | |
118 | static void keep_common_server_info (const vector<compile_server_info> &info_to_keep, vector<compile_server_info> &filtered_info); | |
119 | static void get_server_info_from_db (systemtap_session &s, vector<compile_server_info> &servers, const string &cert_db_path); | |
120 | static void add_server_info (const compile_server_info &info, vector<compile_server_info>& list); | |
121 | static void merge_server_info (const compile_server_info &source, compile_server_info &target); | |
122 | static void merge_server_info (const vector<compile_server_info> &source, vector<compile_server_info> &target); | |
123 | static void merge_server_info (const vector<compile_server_info> &source, compile_server_info &target); | |
124 | ||
125 | static void resolve_server (systemtap_session& s, compile_server_info &server_info); | |
126 | static int resolve_host_name (systemtap_session& s, string &host_name, string *ip_address = NULL); | |
127 | ||
128 | ostream &operator<< (ostream &s, const compile_server_info &i); | |
129 | ||
c77af0d0 | 130 | #if HAVE_NSS |
b0e5fa85 DB |
131 | static const char *server_cert_nickname = "stap-server"; |
132 | ||
c77af0d0 DB |
133 | static string |
134 | private_ssl_cert_db_path (const systemtap_session &s) | |
135 | { | |
136 | return s.data_path + "/ssl/client"; | |
137 | } | |
138 | ||
139 | static string | |
140 | global_ssl_cert_db_path () | |
141 | { | |
142 | return SYSCONFDIR "/systemtap/ssl/client"; | |
143 | } | |
144 | ||
145 | static string | |
146 | signing_cert_db_path () | |
147 | { | |
148 | return SYSCONFDIR "/systemtap/staprun"; | |
149 | } | |
150 | #endif // HAVE_NSS | |
151 | ||
aa4d21c0 DB |
152 | int |
153 | compile_server_client::passes_0_4 () | |
154 | { | |
2fad97fd DB |
155 | STAP_PROBE1(stap, client__start, &s); |
156 | ||
157 | #if ! HAVE_NSS | |
158 | // This code will never be called, if we don't have NSS, but it must still | |
159 | // compile. | |
160 | int rc = 1; // Failure | |
161 | #else | |
aa4d21c0 DB |
162 | // arguments parsed; get down to business |
163 | if (s.verbose > 1) | |
164 | clog << "Using a compile server" << endl; | |
165 | ||
aa4d21c0 DB |
166 | struct tms tms_before; |
167 | times (& tms_before); | |
168 | struct timeval tv_before; | |
169 | gettimeofday (&tv_before, NULL); | |
170 | ||
2fad97fd DB |
171 | // Create the request package. |
172 | int rc = initialize (); | |
85007c04 | 173 | if (rc != 0 || pending_interrupts) goto done; |
2fad97fd | 174 | rc = create_request (); |
85007c04 | 175 | if (rc != 0 || pending_interrupts) goto done; |
2fad97fd | 176 | rc = package_request (); |
85007c04 | 177 | if (rc != 0 || pending_interrupts) goto done; |
2fad97fd DB |
178 | |
179 | // Submit it to the server. | |
180 | rc = find_and_connect_to_server (); | |
85007c04 | 181 | if (rc != 0 || pending_interrupts) goto done; |
2fad97fd DB |
182 | |
183 | // Unpack and process the response. | |
184 | rc = unpack_response (); | |
85007c04 | 185 | if (rc != 0 || pending_interrupts) goto done; |
2fad97fd DB |
186 | rc = process_response (); |
187 | ||
f3fbabf2 DB |
188 | if (rc == 0 && s.last_pass == 4) |
189 | { | |
190 | cout << s.module_name + ".ko"; | |
191 | cout << endl; | |
192 | } | |
193 | ||
2fad97fd | 194 | done: |
aa4d21c0 DB |
195 | struct tms tms_after; |
196 | times (& tms_after); | |
197 | unsigned _sc_clk_tck = sysconf (_SC_CLK_TCK); | |
198 | struct timeval tv_after; | |
199 | gettimeofday (&tv_after, NULL); | |
200 | ||
201 | #define TIMESPRINT "in " << \ | |
202 | (tms_after.tms_cutime + tms_after.tms_utime \ | |
203 | - tms_before.tms_cutime - tms_before.tms_utime) * 1000 / (_sc_clk_tck) << "usr/" \ | |
204 | << (tms_after.tms_cstime + tms_after.tms_stime \ | |
205 | - tms_before.tms_cstime - tms_before.tms_stime) * 1000 / (_sc_clk_tck) << "sys/" \ | |
206 | << ((tv_after.tv_sec - tv_before.tv_sec) * 1000 + \ | |
207 | ((long)tv_after.tv_usec - (long)tv_before.tv_usec) / 1000) << "real ms." | |
208 | ||
209 | // syntax errors, if any, are already printed | |
210 | if (s.verbose) | |
211 | { | |
920be590 | 212 | clog << "Passes: via server " << s.winning_server << " " |
85007c04 | 213 | << getmemusage() |
aa4d21c0 DB |
214 | << TIMESPRINT |
215 | << endl; | |
216 | } | |
217 | ||
85007c04 | 218 | if (rc == 0) |
f3fbabf2 | 219 | { |
85007c04 DB |
220 | // Save the module, if necessary. |
221 | if (s.last_pass == 4) | |
222 | s.save_module = true; | |
223 | ||
224 | // Copy module to the current directory. | |
225 | if (s.save_module && ! pending_interrupts) | |
226 | { | |
227 | string module_src_path = s.tmpdir + "/" + s.module_name + ".ko"; | |
228 | string module_dest_path = s.module_name + ".ko"; | |
229 | copy_file (module_src_path, module_dest_path, s.verbose > 1); | |
334df419 DB |
230 | // Also copy the module signature, it it exists. |
231 | module_src_path += ".sgn"; | |
232 | if (file_exists (module_src_path)) | |
233 | { | |
234 | module_dest_path += ".sgn"; | |
235 | copy_file(module_src_path, module_dest_path, s.verbose > 1); | |
236 | } | |
85007c04 | 237 | } |
f3fbabf2 DB |
238 | } |
239 | ||
aa4d21c0 DB |
240 | STAP_PROBE1(stap, client__end, &s); |
241 | ||
242 | return rc; | |
b0e5fa85 | 243 | #endif // HAVE_NSS |
aa4d21c0 DB |
244 | } |
245 | ||
2fad97fd DB |
246 | // Initialize a client/server session. |
247 | int | |
248 | compile_server_client::initialize () | |
249 | { | |
250 | int rc = 0; | |
251 | ||
252 | // Initialize session state | |
253 | argc = 0; | |
254 | ||
c77af0d0 DB |
255 | // Private location for server certificates. |
256 | private_ssl_dbs.push_back (private_ssl_cert_db_path (s)); | |
2fad97fd | 257 | |
c77af0d0 DB |
258 | // Additional public location. |
259 | public_ssl_dbs.push_back (global_ssl_cert_db_path ()); | |
2fad97fd DB |
260 | |
261 | // Create a temporary directory to package things in. | |
262 | client_tmpdir = s.tmpdir + "/client"; | |
263 | rc = create_dir (client_tmpdir.c_str ()); | |
264 | if (rc != 0) | |
265 | { | |
266 | const char* e = strerror (errno); | |
267 | cerr << "ERROR: cannot create temporary directory (\"" | |
268 | << client_tmpdir << "\"): " << e | |
269 | << endl; | |
270 | } | |
271 | ||
272 | return rc; | |
273 | } | |
274 | ||
275 | // Create the request package. | |
276 | int | |
277 | compile_server_client::create_request () | |
278 | { | |
279 | int rc = 0; | |
280 | ||
281 | // Add the script file or script option | |
282 | if (s.script_file != "") | |
283 | { | |
284 | if (s.script_file == "-") | |
285 | { | |
286 | // Copy the script from stdin | |
287 | string packaged_script_dir = client_tmpdir + "/script"; | |
288 | rc = create_dir (packaged_script_dir.c_str ()); | |
289 | if (rc != 0) | |
290 | { | |
291 | const char* e = strerror (errno); | |
292 | cerr << "ERROR: cannot create temporary directory " | |
293 | << packaged_script_dir << ": " << e | |
294 | << endl; | |
295 | return rc; | |
296 | } | |
297 | rc = ! copy_file("/dev/stdin", packaged_script_dir + "/-"); | |
f3fbabf2 DB |
298 | if (rc != 0) |
299 | return rc; | |
2fad97fd DB |
300 | |
301 | // Name the script in the packaged arguments. | |
f3fbabf2 DB |
302 | rc = add_package_arg ("script/-"); |
303 | if (rc != 0) | |
304 | return rc; | |
2fad97fd DB |
305 | } |
306 | else | |
307 | { | |
308 | // Add the script to our package. This will also name the script | |
309 | // in the packaged arguments. | |
310 | rc = include_file_or_directory ("script", s.script_file); | |
f3fbabf2 DB |
311 | if (rc != 0) |
312 | return rc; | |
2fad97fd DB |
313 | } |
314 | } | |
315 | ||
316 | // Add -I paths. Skip the default directory. | |
317 | if (s.include_arg_start != -1) | |
318 | { | |
319 | unsigned limit = s.include_path.size (); | |
320 | for (unsigned i = s.include_arg_start; i < limit; ++i) | |
321 | { | |
f3fbabf2 DB |
322 | rc = add_package_arg ("-I"); |
323 | if (rc != 0) | |
324 | return rc; | |
325 | rc = include_file_or_directory ("tapset", s.include_path[i]); | |
326 | if (rc != 0) | |
327 | return rc; | |
2fad97fd DB |
328 | } |
329 | } | |
330 | ||
331 | // Add other options. | |
f3fbabf2 DB |
332 | rc = add_package_args (); |
333 | if (rc != 0) | |
334 | return rc; | |
2fad97fd DB |
335 | |
336 | // Add the sysinfo file | |
337 | string sysinfo = "sysinfo: " + s.kernel_release + " " + s.architecture; | |
f3fbabf2 | 338 | rc = write_to_file (client_tmpdir + "/sysinfo", sysinfo); |
2fad97fd DB |
339 | |
340 | return rc; | |
341 | } | |
342 | ||
343 | // Add the arguments specified on the command line to the server request | |
344 | // package, as appropriate. | |
f3fbabf2 | 345 | int |
2fad97fd DB |
346 | compile_server_client::add_package_args () |
347 | { | |
fd20a70c | 348 | // stap arguments to be passed to the server. |
f3fbabf2 | 349 | int rc = 0; |
2fad97fd DB |
350 | unsigned limit = s.server_args.size(); |
351 | for (unsigned i = 0; i < limit; ++i) | |
f3fbabf2 DB |
352 | { |
353 | rc = add_package_arg (s.server_args[i]); | |
354 | if (rc != 0) | |
355 | return rc; | |
356 | } | |
fd20a70c DB |
357 | |
358 | // Script arguments. | |
359 | limit = s.args.size(); | |
360 | if (limit > 0) { | |
361 | rc = add_package_arg ("--"); | |
362 | if (rc != 0) | |
363 | return rc; | |
364 | for (unsigned i = 0; i < limit; ++i) | |
365 | { | |
366 | rc = add_package_arg (s.args[i]); | |
367 | if (rc != 0) | |
368 | return rc; | |
369 | } | |
370 | } | |
f3fbabf2 | 371 | return rc; |
2fad97fd DB |
372 | } |
373 | ||
131f914e DB |
374 | int |
375 | compile_server_client::add_package_arg (const string &arg) | |
376 | { | |
377 | int rc = 0; | |
378 | ostringstream fname; | |
379 | fname << client_tmpdir << "/argv" << ++argc; | |
380 | write_to_file (fname.str (), arg); // NB: No terminating newline | |
381 | return rc; | |
382 | } | |
383 | ||
2fad97fd DB |
384 | // Symbolically link the given file or directory into the client's temp |
385 | // directory under the given subdirectory. | |
386 | int | |
387 | compile_server_client::include_file_or_directory ( | |
388 | const string &subdir, const string &path, const char *option | |
389 | ) | |
390 | { | |
62f9d949 DB |
391 | // Must predeclare these because we do use 'goto done' to |
392 | // exit from error situations. | |
2fad97fd | 393 | vector<string> components; |
62f9d949 DB |
394 | string name; |
395 | int rc; | |
2fad97fd | 396 | |
62f9d949 DB |
397 | // Canonicalize the given path and remove the leading /. |
398 | string rpath; | |
399 | char *cpath = canonicalize_file_name (path.c_str ()); | |
2fad97fd DB |
400 | if (! cpath) |
401 | { | |
62f9d949 DB |
402 | // It can not be canonicalized. Use the name relative to |
403 | // the current working directory and let the server deal with it. | |
404 | char cwd[PATH_MAX]; | |
405 | if (getcwd (cwd, sizeof (cwd)) == NULL) | |
406 | { | |
407 | rpath = path; | |
408 | rc = 1; | |
409 | goto done; | |
410 | } | |
411 | rpath = string (cwd) + "/" + path; | |
2fad97fd | 412 | } |
62f9d949 | 413 | else |
2fad97fd | 414 | { |
62f9d949 DB |
415 | // It can be canonicalized. Use the canonicalized name and add this |
416 | // file or directory to the request package. | |
417 | rpath = cpath; | |
418 | free (cpath); | |
419 | ||
420 | // First create the requested subdirectory. | |
421 | name = client_tmpdir + "/" + subdir; | |
2fad97fd DB |
422 | rc = create_dir (name.c_str ()); |
423 | if (rc) goto done; | |
2fad97fd | 424 | |
62f9d949 DB |
425 | // Now create each component of the path within the sub directory. |
426 | assert (rpath[0] == '/'); | |
427 | tokenize (rpath.substr (1), components, "/"); | |
428 | assert (components.size () >= 1); | |
429 | unsigned i; | |
430 | for (i = 0; i < components.size() - 1; ++i) | |
431 | { | |
432 | if (components[i].empty ()) | |
433 | continue; // embedded '//' | |
434 | name += "/" + components[i]; | |
435 | rc = create_dir (name.c_str ()); | |
436 | if (rc) goto done; | |
437 | } | |
438 | ||
439 | // Now make a symbolic link to the actual file or directory. | |
440 | assert (i == components.size () - 1); | |
441 | name += "/" + components[i]; | |
442 | rc = symlink (rpath.c_str (), name.c_str ()); | |
443 | if (rc) goto done; | |
444 | } | |
2fad97fd DB |
445 | |
446 | // Name this file or directory in the packaged arguments along with any | |
447 | // associated option. | |
448 | if (option) | |
f3fbabf2 DB |
449 | { |
450 | rc = add_package_arg (option); | |
451 | if (rc) goto done; | |
452 | } | |
453 | ||
62f9d949 | 454 | rc = add_package_arg (subdir + "/" + rpath.substr (1)); |
2fad97fd DB |
455 | |
456 | done: | |
2fad97fd DB |
457 | if (rc != 0) |
458 | { | |
459 | const char* e = strerror (errno); | |
62f9d949 DB |
460 | cerr << "ERROR: unable to add " |
461 | << rpath | |
462 | << " to temp directory as " | |
2fad97fd DB |
463 | << name << ": " << e |
464 | << endl; | |
465 | } | |
2fad97fd DB |
466 | return rc; |
467 | } | |
468 | ||
2fad97fd DB |
469 | // Package the client's temp directory into a form suitable for sending to the |
470 | // server. | |
471 | int | |
472 | compile_server_client::package_request () | |
473 | { | |
474 | // Package up the temporary directory into a zip file. | |
475 | client_zipfile = client_tmpdir + ".zip"; | |
476 | string cmd = "cd " + client_tmpdir + " && zip -qr " + client_zipfile + " *"; | |
477 | int rc = stap_system (s.verbose, cmd); | |
2fad97fd DB |
478 | return rc; |
479 | } | |
480 | ||
481 | int | |
482 | compile_server_client::find_and_connect_to_server () | |
483 | { | |
2e15a955 DB |
484 | // Accumulate info on the specified servers. |
485 | vector<compile_server_info> server_list; | |
486 | get_specified_server_info (s, server_list); | |
2fad97fd | 487 | |
c77af0d0 DB |
488 | // Ignore the empty first entry. |
489 | assert (! server_list.empty ()); | |
490 | assert (server_list[0].host_name.empty ()); | |
491 | server_list.erase (server_list.begin (), server_list.begin ()); | |
492 | ||
2e15a955 DB |
493 | // Did we identify any potential servers? |
494 | unsigned limit = server_list.size (); | |
495 | if (limit == 0) | |
2fad97fd | 496 | { |
2e15a955 DB |
497 | cerr << "Unable to find a server" << endl; |
498 | return 1; | |
499 | } | |
1f9938b9 | 500 | |
2e15a955 DB |
501 | // Now try each of the identified servers in turn. |
502 | for (unsigned i = 0; i < limit; ++i) | |
503 | { | |
504 | int rc = compile_using_server (server_list[i]); | |
505 | if (rc == 0) | |
1f9938b9 | 506 | { |
2e15a955 DB |
507 | s.winning_server = |
508 | server_list[i].host_name + string(" [") + | |
509 | server_list[i].ip_address + string(":") + | |
510 | lex_cast(server_list[i].port) + string("]"); | |
511 | return 0; // success! | |
1f9938b9 | 512 | } |
2e15a955 | 513 | } |
2fad97fd | 514 | |
2e15a955 | 515 | cerr << "Unable to connect to a server" << endl; |
2fad97fd DB |
516 | return 1; // Failure |
517 | } | |
518 | ||
f3fbabf2 | 519 | // Temporary until the stap-client-connect program goes away. |
2fad97fd DB |
520 | extern "C" |
521 | int | |
522 | client_main (const char *hostName, unsigned short port, | |
c77af0d0 DB |
523 | const char* infileName, const char* outfileName, |
524 | const char* trustNewServer); | |
2fad97fd DB |
525 | |
526 | int | |
527 | compile_server_client::compile_using_server (const compile_server_info &server) | |
528 | { | |
f3fbabf2 | 529 | // This code will never be called if we don't have NSS, but it must still |
2fad97fd DB |
530 | // compile. |
531 | #if HAVE_NSS | |
0d1534e8 DB |
532 | // We cannot contact the server if we don't have the port number. |
533 | if (server.port == 0) | |
534 | return 1; // Failure | |
535 | ||
b0e5fa85 DB |
536 | // Make sure NSPR is initialized |
537 | s.NSPR_init (); | |
538 | ||
1f9938b9 | 539 | // Attempt connection using each of the available client certificate |
2fad97fd DB |
540 | // databases. |
541 | vector<string> dbs = private_ssl_dbs; | |
542 | vector<string>::iterator i = dbs.end(); | |
543 | dbs.insert (i, public_ssl_dbs.begin (), public_ssl_dbs.end ()); | |
b0e5fa85 | 544 | int rc = 1; // assume failure |
2fad97fd DB |
545 | for (i = dbs.begin (); i != dbs.end (); ++i) |
546 | { | |
547 | const char *cert_dir = i->c_str (); | |
548 | ||
549 | if (s.verbose > 1) | |
1f9938b9 | 550 | clog << "Attempting SSL connection with " << server << endl |
2fad97fd DB |
551 | << " using certificates from the database in " << cert_dir |
552 | << endl; | |
2fad97fd | 553 | |
f3fbabf2 DB |
554 | #if 0 // no client authentication for now. |
555 | // Set our password function callback. | |
2fad97fd DB |
556 | PK11_SetPasswordFunc (myPasswd); |
557 | #endif | |
558 | ||
f3fbabf2 | 559 | // Initialize the NSS libraries. |
2fad97fd DB |
560 | SECStatus secStatus = NSS_InitReadWrite (cert_dir); |
561 | if (secStatus != SECSuccess) | |
562 | { | |
f3fbabf2 | 563 | // Try it again, readonly. |
2fad97fd DB |
564 | secStatus = NSS_Init(cert_dir); |
565 | if (secStatus != SECSuccess) | |
566 | { | |
567 | cerr << "Error initializing NSS" << endl; | |
b0e5fa85 DB |
568 | nssError (); |
569 | NSS_Shutdown(); | |
570 | continue; // try next database | |
2fad97fd DB |
571 | } |
572 | } | |
573 | ||
f3fbabf2 | 574 | // All cipher suites except RSA_NULL_MD5 are enabled by Domestic Policy. |
2fad97fd DB |
575 | NSS_SetDomesticPolicy (); |
576 | ||
577 | server_zipfile = s.tmpdir + "/server.zip"; | |
b0e5fa85 DB |
578 | rc = client_main (server.host_name.c_str (), server.port, |
579 | client_zipfile.c_str(), server_zipfile.c_str (), | |
580 | NULL/*trustNewServer_p*/); | |
581 | ||
2fad97fd | 582 | NSS_Shutdown(); |
2fad97fd DB |
583 | |
584 | if (rc == 0) | |
b0e5fa85 | 585 | break; // Success! |
2fad97fd DB |
586 | } |
587 | ||
b0e5fa85 | 588 | return rc; |
2fad97fd DB |
589 | #endif // HAVE_NSS |
590 | ||
591 | return 1; // Failure | |
592 | } | |
593 | ||
594 | int | |
595 | compile_server_client::unpack_response () | |
596 | { | |
f3fbabf2 | 597 | // Unzip the response package. |
2fad97fd DB |
598 | server_tmpdir = s.tmpdir + "/server"; |
599 | string cmd = "unzip -qd " + server_tmpdir + " " + server_zipfile; | |
600 | int rc = stap_system (s.verbose, cmd); | |
f3fbabf2 DB |
601 | if (rc != 0) |
602 | { | |
603 | cerr << "Unable to unzip the server reponse '" << server_zipfile << '\'' | |
604 | << endl; | |
605 | } | |
2fad97fd DB |
606 | |
607 | // If the server's response contains a systemtap temp directory, move | |
608 | // its contents to our temp directory. | |
609 | glob_t globbuf; | |
610 | string filespec = server_tmpdir + "/stap??????"; | |
1f9938b9 | 611 | if (s.verbose > 2) |
2fad97fd DB |
612 | clog << "Searching \"" << filespec << "\"" << endl; |
613 | int r = glob(filespec.c_str (), 0, NULL, & globbuf); | |
614 | if (r != GLOB_NOSPACE && r != GLOB_ABORTED && r != GLOB_NOMATCH) | |
615 | { | |
616 | if (globbuf.gl_pathc > 1) | |
617 | { | |
618 | cerr << "Incorrect number of files in server response" << endl; | |
f3fbabf2 | 619 | rc = 1; goto done; |
2fad97fd DB |
620 | } |
621 | ||
622 | assert (globbuf.gl_pathc == 1); | |
623 | string dirname = globbuf.gl_pathv[0]; | |
1f9938b9 | 624 | if (s.verbose > 2) |
2fad97fd DB |
625 | clog << " found " << dirname << endl; |
626 | ||
627 | filespec = dirname + "/*"; | |
1f9938b9 | 628 | if (s.verbose > 2) |
2fad97fd DB |
629 | clog << "Searching \"" << filespec << "\"" << endl; |
630 | int r = glob(filespec.c_str (), GLOB_PERIOD, NULL, & globbuf); | |
631 | if (r != GLOB_NOSPACE && r != GLOB_ABORTED && r != GLOB_NOMATCH) | |
632 | { | |
2fad97fd DB |
633 | unsigned prefix_len = dirname.size () + 1; |
634 | for (unsigned i = 0; i < globbuf.gl_pathc; ++i) | |
635 | { | |
636 | string oldname = globbuf.gl_pathv[i]; | |
637 | if (oldname.substr (oldname.size () - 2) == "/." || | |
638 | oldname.substr (oldname.size () - 3) == "/..") | |
639 | continue; | |
640 | string newname = s.tmpdir + "/" + oldname.substr (prefix_len); | |
1f9938b9 | 641 | if (s.verbose > 2) |
2fad97fd DB |
642 | clog << " found " << oldname |
643 | << " -- linking from " << newname << endl; | |
f3fbabf2 DB |
644 | rc = symlink (oldname.c_str (), newname.c_str ()); |
645 | if (rc != 0) | |
646 | { | |
647 | cerr << "Unable to link '" << oldname | |
648 | << "' to '" << newname << "': " | |
649 | << strerror (errno) << endl; | |
650 | goto done; | |
651 | } | |
2fad97fd DB |
652 | } |
653 | } | |
654 | } | |
655 | ||
f3fbabf2 DB |
656 | // Remove the output line due to the synthetic server-side -k |
657 | cmd = "sed -i '/^Keeping temporary directory.*/ d' " + | |
658 | server_tmpdir + "/stderr"; | |
659 | stap_system (s.verbose, cmd); | |
2fad97fd | 660 | |
f3fbabf2 DB |
661 | // Remove the output line due to the synthetic server-side -p4 |
662 | cmd = "sed -i '/^.*\\.ko$/ d' " + server_tmpdir + "/stdout"; | |
663 | stap_system (s.verbose, cmd); | |
2fad97fd | 664 | |
2fad97fd DB |
665 | done: |
666 | globfree (& globbuf); | |
667 | return rc; | |
668 | } | |
669 | ||
670 | int | |
671 | compile_server_client::process_response () | |
672 | { | |
673 | // Pick up the results of running stap on the server. | |
2fad97fd | 674 | string filename = server_tmpdir + "/rc"; |
f3fbabf2 DB |
675 | int stap_rc; |
676 | int rc = read_from_file (filename, stap_rc); | |
677 | if (rc != 0) | |
678 | return rc; | |
85007c04 | 679 | rc = stap_rc; |
2fad97fd | 680 | |
2fad97fd DB |
681 | if (s.last_pass >= 4) |
682 | { | |
683 | // The server should have returned a module. | |
684 | string filespec = s.tmpdir + "/*.ko"; | |
1f9938b9 | 685 | if (s.verbose > 2) |
2fad97fd | 686 | clog << "Searching \"" << filespec << "\"" << endl; |
f3fbabf2 DB |
687 | |
688 | glob_t globbuf; | |
2fad97fd DB |
689 | int r = glob(filespec.c_str (), 0, NULL, & globbuf); |
690 | if (r != GLOB_NOSPACE && r != GLOB_ABORTED && r != GLOB_NOMATCH) | |
691 | { | |
692 | if (globbuf.gl_pathc > 1) | |
f3fbabf2 DB |
693 | cerr << "Incorrect number of modules in server response" << endl; |
694 | else | |
2fad97fd | 695 | { |
f3fbabf2 DB |
696 | assert (globbuf.gl_pathc == 1); |
697 | string modname = globbuf.gl_pathv[0]; | |
1f9938b9 | 698 | if (s.verbose > 2) |
f3fbabf2 DB |
699 | clog << " found " << modname << endl; |
700 | ||
701 | // If a module name was not specified by the user, then set it to | |
702 | // be the one generated by the server. | |
703 | if (! s.save_module) | |
704 | { | |
705 | vector<string> components; | |
706 | tokenize (modname, components, "/"); | |
707 | s.module_name = components.back (); | |
708 | s.module_name.erase(s.module_name.size() - 3); | |
709 | } | |
2fad97fd DB |
710 | } |
711 | } | |
712 | else if (s.have_script) | |
713 | { | |
85007c04 | 714 | if (rc == 0) |
2fad97fd DB |
715 | { |
716 | cerr << "No module was returned by the server" << endl; | |
85007c04 | 717 | rc = 1; |
2fad97fd DB |
718 | } |
719 | } | |
f3fbabf2 | 720 | globfree (& globbuf); |
2fad97fd DB |
721 | } |
722 | ||
f3fbabf2 | 723 | // Output stdout and stderr. |
2fad97fd | 724 | filename = server_tmpdir + "/stderr"; |
f3fbabf2 | 725 | flush_to_stream (filename, cerr); |
2fad97fd DB |
726 | |
727 | filename = server_tmpdir + "/stdout"; | |
f3fbabf2 DB |
728 | flush_to_stream (filename, cout); |
729 | ||
730 | return rc; | |
731 | } | |
732 | ||
733 | int | |
734 | compile_server_client::read_from_file (const string &fname, int &data) | |
735 | { | |
131f914e DB |
736 | // C++ streams may not set errno in the even of a failure. However if we |
737 | // set it to 0 before each operation and it gets set during the operation, | |
738 | // then we can use its value in order to determine what happened. | |
739 | errno = 0; | |
740 | ifstream f (fname.c_str ()); | |
741 | if (! f.good ()) | |
f3fbabf2 | 742 | { |
131f914e DB |
743 | cerr << "Unable to open file '" << fname << "' for reading: "; |
744 | goto error; | |
f3fbabf2 DB |
745 | } |
746 | ||
131f914e DB |
747 | // Read the data; |
748 | errno = 0; | |
749 | f >> data; | |
750 | if (f.fail ()) | |
f3fbabf2 | 751 | { |
131f914e DB |
752 | cerr << "Unable to read from file '" << fname << "': "; |
753 | goto error; | |
f3fbabf2 DB |
754 | } |
755 | ||
920be590 | 756 | // NB: not necessary to f.close (); |
131f914e DB |
757 | return 0; // Success |
758 | ||
759 | error: | |
760 | if (errno) | |
761 | cerr << strerror (errno) << endl; | |
762 | else | |
763 | cerr << "unknown error" << endl; | |
764 | return 1; // Failure | |
f3fbabf2 DB |
765 | } |
766 | ||
767 | int | |
768 | compile_server_client::write_to_file (const string &fname, const string &data) | |
769 | { | |
131f914e DB |
770 | // C++ streams may not set errno in the even of a failure. However if we |
771 | // set it to 0 before each operation and it gets set during the operation, | |
772 | // then we can use its value in order to determine what happened. | |
773 | errno = 0; | |
774 | ofstream f (fname.c_str ()); | |
775 | if (! f.good ()) | |
f3fbabf2 | 776 | { |
131f914e DB |
777 | cerr << "Unable to open file '" << fname << "' for writing: "; |
778 | goto error; | |
f3fbabf2 DB |
779 | } |
780 | ||
131f914e DB |
781 | // Write the data; |
782 | f << data; | |
783 | errno = 0; | |
784 | if (f.fail ()) | |
f3fbabf2 | 785 | { |
131f914e DB |
786 | cerr << "Unable to write to file '" << fname << "': "; |
787 | goto error; | |
f3fbabf2 DB |
788 | } |
789 | ||
920be590 | 790 | // NB: not necessary to f.close (); |
131f914e DB |
791 | return 0; // Success |
792 | ||
793 | error: | |
794 | if (errno) | |
795 | cerr << strerror (errno) << endl; | |
796 | else | |
797 | cerr << "unknown error" << endl; | |
798 | return 1; // Failure | |
f3fbabf2 DB |
799 | } |
800 | ||
801 | int | |
802 | compile_server_client::flush_to_stream (const string &fname, ostream &o) | |
803 | { | |
131f914e DB |
804 | // C++ streams may not set errno in the even of a failure. However if we |
805 | // set it to 0 before each operation and it gets set during the operation, | |
806 | // then we can use its value in order to determine what happened. | |
807 | errno = 0; | |
808 | ifstream f (fname.c_str ()); | |
809 | if (! f.good ()) | |
f3fbabf2 | 810 | { |
131f914e DB |
811 | cerr << "Unable to open file '" << fname << "' for reading: "; |
812 | goto error; | |
f3fbabf2 DB |
813 | } |
814 | ||
131f914e | 815 | // Stream the data |
920be590 FCE |
816 | |
817 | // NB: o << f.rdbuf() misbehaves for some reason, appearing to close o, | |
818 | // which is unfortunate if o == cerr or cout. | |
819 | while (1) | |
f3fbabf2 | 820 | { |
920be590 FCE |
821 | errno = 0; |
822 | int c = f.get(); | |
823 | if (f.eof ()) return 0; // normal exit | |
824 | if (! f.good()) break; | |
825 | o.put(c); | |
826 | if (! o.good()) break; | |
f3fbabf2 | 827 | } |
2fad97fd | 828 | |
920be590 | 829 | // NB: not necessary to f.close (); |
131f914e DB |
830 | |
831 | error: | |
832 | if (errno) | |
833 | cerr << strerror (errno) << endl; | |
834 | else | |
835 | cerr << "unknown error" << endl; | |
836 | return 1; // Failure | |
2fad97fd | 837 | } |
131f914e | 838 | |
85007c04 | 839 | // Utility Functions. |
131f914e | 840 | //----------------------------------------------------------------------- |
b0e5fa85 | 841 | ostream &operator<< (ostream &s, const compile_server_info &i) |
85007c04 | 842 | { |
1f9938b9 | 843 | s << i.host_name; |
0d1534e8 | 844 | s << " ip="; |
1f9938b9 | 845 | if (! i.ip_address.empty ()) |
0d1534e8 DB |
846 | s << i.ip_address; |
847 | else | |
848 | s << "unknown"; | |
849 | s << " port="; | |
1f9938b9 | 850 | if (i.port != 0) |
0d1534e8 DB |
851 | s << i.port; |
852 | else | |
853 | s << "unknown"; | |
854 | s << " sysinfo=\""; | |
1f9938b9 | 855 | if (! i.sysinfo.empty ()) |
0d1534e8 | 856 | s << i.sysinfo << '"'; |
2e15a955 | 857 | else |
0d1534e8 DB |
858 | s << "unknown\""; |
859 | s << " certinfo=\""; | |
860 | if (! i.certinfo.empty ()) | |
861 | s << i.certinfo << '"'; | |
862 | else | |
863 | s << "unknown\""; | |
1f9938b9 | 864 | return s; |
85007c04 DB |
865 | } |
866 | ||
2e15a955 DB |
867 | // Return the default server specification, used when none is given on the |
868 | // command line. | |
869 | static string | |
870 | default_server_spec (const systemtap_session &s) | |
85007c04 | 871 | { |
eb3a0eee | 872 | // If the --use-server option has been used |
85007c04 DB |
873 | // the default is 'specified' |
874 | // otherwise if the --unprivileged has been used | |
875 | // the default is online,trusted,compatible,signer | |
876 | // otherwise | |
1e7630bf | 877 | // the default is online,compatible |
85007c04 DB |
878 | // |
879 | // Having said that, | |
0d1534e8 DB |
880 | // 'online' and 'compatible' will only succeed if we have avahi |
881 | // 'trusted' and 'signer' will only succeed if we have NSS | |
882 | // | |
883 | string working_string = "online,trusted,compatible"; | |
2e15a955 | 884 | if (s.unprivileged) |
0d1534e8 | 885 | working_string += ",signer"; |
2e15a955 DB |
886 | return working_string; |
887 | } | |
85007c04 | 888 | |
2e15a955 DB |
889 | static int |
890 | server_spec_to_pmask (const string &server_spec) | |
891 | { | |
85007c04 DB |
892 | // Construct a mask of the server properties that have been requested. |
893 | // The available properties are: | |
0d1534e8 DB |
894 | // trusted - servers which are trusted SSL peers. |
895 | // online - online servers. | |
85007c04 DB |
896 | // compatible - servers which compile for the current kernel release |
897 | // and architecture. | |
898 | // signer - servers which are trusted module signers. | |
eb3a0eee | 899 | // specified - servers which have been specified using --use-server=XXX. |
85007c04 | 900 | // If no servers have been specified, then this is |
eb3a0eee | 901 | // equivalent to --list-servers=trusted,online,compatible. |
0d1534e8 DB |
902 | // all - all trusted servers, trusted module signers, |
903 | // servers currently online and specified servers. | |
904 | string working_spec = server_spec; | |
85007c04 | 905 | vector<string> properties; |
0d1534e8 | 906 | tokenize (working_spec, properties, ","); |
85007c04 DB |
907 | int pmask = 0; |
908 | unsigned limit = properties.size (); | |
909 | for (unsigned i = 0; i < limit; ++i) | |
910 | { | |
911 | const string &property = properties[i]; | |
0d1534e8 DB |
912 | // Tolerate (and ignore) empty properties. |
913 | if (property.empty ()) | |
914 | continue; | |
85007c04 DB |
915 | if (property == "all") |
916 | { | |
0d1534e8 | 917 | pmask |= compile_server_all; |
85007c04 DB |
918 | } |
919 | else if (property == "specified") | |
920 | { | |
921 | pmask |= compile_server_specified; | |
922 | } | |
85007c04 DB |
923 | else if (property == "trusted") |
924 | { | |
925 | pmask |= compile_server_trusted; | |
926 | } | |
85007c04 DB |
927 | else if (property == "online") |
928 | { | |
929 | pmask |= compile_server_online; | |
930 | } | |
85007c04 DB |
931 | else if (property == "compatible") |
932 | { | |
933 | pmask |= compile_server_compatible; | |
934 | } | |
85007c04 DB |
935 | else if (property == "signer") |
936 | { | |
937 | pmask |= compile_server_signer; | |
938 | } | |
85007c04 DB |
939 | else |
940 | { | |
941 | cerr << "Warning: unsupported compile server property: " << property | |
942 | << endl; | |
943 | } | |
944 | } | |
2e15a955 DB |
945 | return pmask; |
946 | } | |
947 | ||
948 | void | |
949 | query_server_status (systemtap_session &s) | |
950 | { | |
b0e5fa85 DB |
951 | // Make sure NSPR is initialized |
952 | s.NSPR_init (); | |
953 | ||
2e15a955 DB |
954 | unsigned limit = s.server_status_strings.size (); |
955 | for (unsigned i = 0; i < limit; ++i) | |
956 | query_server_status (s, s.server_status_strings[i]); | |
957 | } | |
958 | ||
b0e5fa85 | 959 | static void |
2e15a955 DB |
960 | query_server_status (systemtap_session &s, const string &status_string) |
961 | { | |
2e15a955 DB |
962 | // If this string is empty, then the default is "specified" |
963 | string working_string = status_string; | |
964 | if (working_string.empty ()) | |
965 | working_string = "specified"; | |
966 | ||
0d1534e8 DB |
967 | // If the query is "specified" and no servers have been specified |
968 | // (i.e. --use-server not used or used once with no argument), then | |
969 | // use the default query. | |
970 | if (working_string == "specified" && | |
971 | (s.specified_servers.empty () || | |
972 | (s.specified_servers.size () == 1 && s.specified_servers[0].empty ()))) | |
2e15a955 DB |
973 | working_string = default_server_spec (s); |
974 | ||
2e15a955 | 975 | int pmask = server_spec_to_pmask (working_string); |
85007c04 DB |
976 | |
977 | // Now obtain a list of the servers which match the criteria. | |
978 | vector<compile_server_info> servers; | |
979 | get_server_info (s, pmask, servers); | |
980 | ||
c77af0d0 | 981 | // Print the server information. Skip tge emoty entry at the head of the list. |
0d1534e8 DB |
982 | clog << "Systemtap Compile Server Status for '" << working_string << '\'' |
983 | << endl; | |
2e15a955 | 984 | unsigned limit = servers.size (); |
c77af0d0 | 985 | for (unsigned i = 1; i < limit; ++i) |
85007c04 DB |
986 | { |
987 | clog << servers[i] << endl; | |
988 | } | |
85007c04 DB |
989 | } |
990 | ||
c77af0d0 DB |
991 | // Add or remove trust of the servers specified on the command line. |
992 | void | |
993 | manage_server_trust (systemtap_session &s) | |
994 | { | |
b0e5fa85 DB |
995 | // This function will never be called if we don't have NSS, but it must |
996 | // still compile. | |
c77af0d0 DB |
997 | #if HAVE_NSS |
998 | // Nothing to do if --trust-servers was not specified. | |
999 | if (s.server_trust_spec.empty ()) | |
1000 | return; | |
1001 | ||
1002 | // Break up and analyze the trust specification. Recognized components are: | |
1003 | // ssl - trust the specified servers as ssl peers | |
1004 | // signer - trust the specified servers as module signers | |
1005 | // revoke - revoke the requested trust | |
1006 | // all-users - apply/revoke the requested trust for all users | |
1007 | vector<string>components; | |
1008 | tokenize (s.server_trust_spec, components, ","); | |
1009 | bool ssl = false; | |
1010 | bool signer = false; | |
1011 | bool revoke = false; | |
1012 | bool all_users = false; | |
1013 | bool error = false; | |
1014 | for (vector<string>::const_iterator i = components.begin (); | |
1015 | i != components.end (); | |
1016 | ++i) | |
1017 | { | |
1018 | if (*i == "ssl") | |
1019 | ssl = true; | |
1020 | else if (*i == "signer") | |
1021 | { | |
1022 | if (geteuid () != 0) | |
1023 | { | |
1024 | cerr << "Only root can specify 'signer' on --trust-servers" << endl; | |
1025 | error = true; | |
1026 | } | |
1027 | else | |
1028 | signer = true; | |
1029 | } | |
1030 | else if (*i == "revoke") | |
1031 | revoke = true; | |
1032 | else if (*i == "all-users") | |
1033 | { | |
1034 | if (geteuid () != 0) | |
1035 | { | |
1036 | cerr << "Only root can specify 'all-users' on --trust-servers" << endl; | |
1037 | error = true; | |
1038 | } | |
1039 | else | |
1040 | all_users = true; | |
1041 | } | |
1042 | else | |
1043 | cerr << "Warning: Unrecognized server trust specification: " << *i | |
1044 | << endl; | |
1045 | } | |
1046 | if (error) | |
1047 | return; | |
1048 | ||
b0e5fa85 DB |
1049 | // Make sure NSPR is initialized |
1050 | s.NSPR_init (); | |
1051 | ||
c77af0d0 DB |
1052 | // Now obtain the list of specified servers. |
1053 | vector<compile_server_info> server_list; | |
1054 | get_specified_server_info (s, server_list); | |
1055 | ||
1056 | // Ignore the empty first entry. | |
1057 | assert (! server_list.empty ()); | |
1058 | assert (server_list[0].host_name.empty ()); | |
334df419 | 1059 | server_list.erase (server_list.begin ()); |
c77af0d0 DB |
1060 | |
1061 | // Did we identify any potential servers? Ignore the empty first entry. | |
1062 | unsigned limit = server_list.size (); | |
1063 | if (limit == 0) | |
1064 | { | |
1065 | cerr << "No servers identified for trust" << endl; | |
1066 | return; | |
1067 | } | |
1068 | ||
1069 | // Create a string representing the request in English. | |
1070 | // If neither 'ssl' or 'signer' was specified, the default is 'ssl'. | |
1071 | if (! ssl && ! signer) | |
1072 | ssl = true; | |
1073 | ostringstream trustString; | |
1074 | if (ssl) | |
1075 | { | |
1076 | trustString << "as an SSL peer"; | |
1077 | if (all_users) | |
1078 | trustString << " for all users"; | |
1079 | else | |
1080 | trustString << " for the current user"; | |
1081 | } | |
1082 | if (signer) | |
1083 | { | |
1084 | if (ssl) | |
1085 | trustString << " and "; | |
1086 | trustString << "as a module signer for all users"; | |
1087 | } | |
1088 | ||
1089 | // Prompt the user to confirm what's about to happen. | |
1090 | if (revoke) | |
1091 | clog << "Revoke trust "; | |
1092 | else | |
1093 | clog << "Add trust "; | |
1094 | clog << "in the following servers " << trustString.str () << '?' << endl; | |
1095 | for (unsigned i = 0; i < limit; ++i) | |
1096 | clog << " " << server_list[i] << endl; | |
334df419 DB |
1097 | clog << "[y/N] " << flush; |
1098 | ||
1099 | // Only carry out the operation if the response is "yes" | |
1100 | string response; | |
1101 | cin >> response; | |
1102 | if (response[0] != 'y' && response [0] != 'Y') | |
1103 | { | |
1104 | clog << "Server trust unchanged" << endl; | |
1105 | return; | |
1106 | } | |
c77af0d0 DB |
1107 | |
1108 | // Now add/revoke the requested trust. | |
1109 | string cert_db_path; | |
1110 | if (ssl) | |
1111 | { | |
1112 | if (all_users) | |
1113 | cert_db_path = global_ssl_cert_db_path (); | |
1114 | else | |
1115 | cert_db_path = private_ssl_cert_db_path (s); | |
1116 | if (revoke) | |
1117 | revoke_server_trust (s, cert_db_path, server_list); | |
1118 | else | |
1119 | add_server_trust (s, cert_db_path, server_list); | |
1120 | } | |
1121 | if (signer) | |
1122 | { | |
1123 | cert_db_path = signing_cert_db_path (); | |
1124 | if (revoke) | |
1125 | revoke_server_trust (s, cert_db_path, server_list); | |
1126 | else | |
1127 | add_server_trust (s, cert_db_path, server_list); | |
1128 | } | |
1129 | #endif // HAVE_NSS | |
1130 | } | |
1131 | ||
1132 | // Issue a status message for when a server's trust is already in place. | |
1133 | static void | |
1134 | trust_already_in_place ( | |
1135 | const compile_server_info &server, | |
1136 | const string cert_db_path, | |
1137 | bool revoking | |
1138 | ) | |
1139 | { | |
1140 | string purpose; | |
1141 | if (cert_db_path == signing_cert_db_path ()) | |
1142 | purpose = "as a module signer for all users"; | |
1143 | else | |
1144 | { | |
1145 | purpose = "as an SSL peer"; | |
1146 | if (cert_db_path == global_ssl_cert_db_path ()) | |
1147 | purpose += " for all users"; | |
1148 | else | |
1149 | purpose += " for the current user"; | |
1150 | } | |
1151 | ||
1152 | clog << server << " is already "; | |
1153 | if (revoking) | |
1154 | clog << "un"; | |
1155 | clog << "trusted " << purpose << endl; | |
1156 | } | |
1157 | ||
1158 | // Add the given servers to the given database of trusted servers. | |
b0e5fa85 | 1159 | static void |
c77af0d0 DB |
1160 | add_server_trust ( |
1161 | systemtap_session &s, | |
1162 | const string &cert_db_path, | |
1163 | const vector<compile_server_info> &server_list | |
1164 | ) | |
1165 | { | |
1166 | // This function will never be called without NSS, but it must still compile. | |
1167 | #if HAVE_NSS | |
1168 | // Make sure the given path exists. | |
1169 | if (create_dir (cert_db_path.c_str (), 0755) != 0) | |
1170 | { | |
1171 | cerr << "Unable to find or create the client certificate database directory " | |
1172 | << cert_db_path << ": "; | |
1173 | perror (""); | |
1174 | return; | |
1175 | } | |
1176 | ||
c77af0d0 DB |
1177 | // Initialize the NSS libraries -- read/write |
1178 | SECStatus secStatus = NSS_InitReadWrite (cert_db_path.c_str ()); | |
1179 | if (secStatus != SECSuccess) | |
1180 | { | |
1181 | cerr << "Error initializing NSS for " << cert_db_path << endl; | |
1182 | nssError (); | |
1183 | goto cleanup; | |
1184 | } | |
1185 | ||
1186 | // All cipher suites except RSA_NULL_MD5 are enabled by Domestic Policy. | |
1187 | NSS_SetDomesticPolicy (); | |
1188 | ||
1189 | // Iterate over the servers to become trusted. Contact each one and | |
1190 | // add it to the list of trusted servers if it is not already trusted. | |
1191 | // client_main will issue any error messages. | |
1192 | for (vector<compile_server_info>::const_iterator server = server_list.begin(); | |
1193 | server != server_list.end (); | |
1194 | ++server) | |
1195 | { | |
1196 | client_main (server->host_name.c_str (), server->port, | |
1197 | NULL, NULL, "permanent"); | |
1198 | } | |
1199 | ||
1200 | cleanup: | |
1201 | NSS_Shutdown (); | |
c77af0d0 DB |
1202 | #endif // HAVE_NSS |
1203 | } | |
1204 | ||
1205 | // Remove the given servers from the given database of trusted servers. | |
b0e5fa85 | 1206 | static void |
c77af0d0 DB |
1207 | revoke_server_trust ( |
1208 | systemtap_session &s, | |
1209 | const string &cert_db_path, | |
1210 | const vector<compile_server_info> &server_list | |
1211 | ) | |
1212 | { | |
1213 | // This function will never be called without NSS, but it must still compile. | |
1214 | #if HAVE_NSS | |
1215 | // Make sure the given path exists. | |
1216 | if (! file_exists (cert_db_path)) | |
1217 | { | |
1218 | if (s.verbose > 1) | |
1219 | cerr << "Certificate database '" << cert_db_path << "' does not exist." | |
1220 | << endl; | |
1221 | for (vector<compile_server_info>::const_iterator server = server_list.begin(); | |
1222 | server != server_list.end (); | |
1223 | ++server) | |
1224 | { | |
1225 | trust_already_in_place (*server, cert_db_path, true/*revoking*/); | |
1226 | } | |
1227 | return; | |
1228 | } | |
1229 | ||
1230 | // Must predeclare these because of jumps to cleanup: below. | |
1231 | PK11SlotInfo *slot = NULL; | |
1232 | CERTCertDBHandle *handle; | |
1233 | PRArenaPool *tmpArena = NULL; | |
1234 | CERTCertList *certs = NULL; | |
1235 | CERTCertificate *db_cert; | |
1236 | ||
c77af0d0 DB |
1237 | // Initialize the NSS libraries -- read/write |
1238 | SECStatus secStatus = NSS_InitReadWrite (cert_db_path.c_str ()); | |
1239 | if (secStatus != SECSuccess) | |
1240 | { | |
1241 | cerr << "Error initializing NSS for " << cert_db_path << endl; | |
1242 | nssError (); | |
1243 | goto cleanup; | |
1244 | } | |
1245 | slot = PK11_GetInternalKeySlot (); | |
1246 | handle = CERT_GetDefaultCertDB(); | |
1247 | ||
1248 | // A memory pool to work in | |
1249 | tmpArena = PORT_NewArena(DER_DEFAULT_CHUNKSIZE); | |
1250 | if (! tmpArena) | |
1251 | { | |
1252 | cerr << "Out of memory:"; | |
1253 | nssError (); | |
1254 | goto cleanup; | |
1255 | } | |
1256 | ||
1257 | // Iterate over the servers to become untrusted. | |
1258 | for (vector<compile_server_info>::const_iterator server = server_list.begin(); | |
1259 | server != server_list.end (); | |
1260 | ++server) | |
1261 | { | |
1262 | // If the server's certificate serial number is unknown, then we can't | |
1263 | // match it with one in the database. | |
1264 | if (server->certinfo.empty ()) | |
1265 | { | |
1266 | cerr << "Unable to obtain certificate information for server " | |
1267 | << *server << endl; | |
1268 | continue; | |
1269 | } | |
1270 | ||
1271 | // Search the client-side database of trusted servers. | |
1272 | db_cert = PK11_FindCertFromNickname (server_cert_nickname, NULL); | |
1273 | if (! db_cert) | |
1274 | { | |
1275 | // No trusted servers. Not an error, but issue a status message. | |
1276 | trust_already_in_place (*server, cert_db_path, true/*revoking*/); | |
1277 | continue; | |
1278 | } | |
1279 | ||
1280 | // Here, we have one cert with the desired nickname. | |
1281 | // Now, we will attempt to get a list of ALL certs | |
1282 | // with the same subject name as the cert we have. That list | |
1283 | // should contain, at a minimum, the one cert we have already found. | |
1284 | // If the list of certs is empty (NULL), the libraries have failed. | |
1285 | certs = CERT_CreateSubjectCertList (NULL, handle, & db_cert->derSubject, | |
1286 | PR_Now (), PR_FALSE); | |
1287 | CERT_DestroyCertificate (db_cert); | |
1288 | if (! certs) | |
1289 | { | |
1290 | cerr << "Unable to query certificate database " << cert_db_path | |
1291 | << ": " << endl; | |
1292 | PORT_SetError (SEC_ERROR_LIBRARY_FAILURE); | |
1293 | nssError (); | |
1294 | goto cleanup; | |
1295 | } | |
1296 | ||
1297 | // Find the certificate matching the one belonging to our server. | |
1298 | CERTCertListNode *node; | |
1299 | for (node = CERT_LIST_HEAD (certs); | |
1300 | ! CERT_LIST_END (node, certs); | |
1301 | node = CERT_LIST_NEXT (node)) | |
1302 | { | |
1303 | // The certificate we're working with. | |
1304 | db_cert = node->cert; | |
1305 | ||
1306 | // Get the serial number. | |
1307 | ostringstream serialNumber; | |
334df419 | 1308 | serialNumber << hex << setfill('0') << right; |
c77af0d0 DB |
1309 | for (unsigned i = 0; i < db_cert->serialNumber.len; ++i) |
1310 | { | |
1311 | if (i > 0) | |
1312 | serialNumber << ':'; | |
334df419 | 1313 | serialNumber << setw(2) << (unsigned)db_cert->serialNumber.data[i]; |
c77af0d0 DB |
1314 | } |
1315 | ||
1316 | // Does the serial number match that of the current server? | |
1317 | if (serialNumber.str () != server->certinfo) | |
1318 | continue; // goto next certificate | |
1319 | ||
1320 | // As a sanity check, make sure the host name on the certificate | |
1321 | // matches that of our server. | |
1322 | // The host name is in the alt-name extension of the certificate. | |
1323 | SECItem subAltName; | |
1324 | subAltName.data = NULL; | |
1325 | secStatus = CERT_FindCertExtension (db_cert, | |
1326 | SEC_OID_X509_SUBJECT_ALT_NAME, | |
1327 | & subAltName); | |
1328 | if (secStatus != SECSuccess || ! subAltName.data) | |
1329 | { | |
1330 | cerr << "Unable to find alt name extension on server certificate: "; | |
1331 | nssError (); | |
1332 | continue; | |
1333 | } | |
1334 | ||
1335 | // Decode the extension. | |
1336 | CERTGeneralName *nameList = CERT_DecodeAltNameExtension (tmpArena, & subAltName); | |
1337 | SECITEM_FreeItem(& subAltName, PR_FALSE); | |
1338 | if (! nameList) | |
1339 | { | |
1340 | cerr << "Unable to decode alt name extension on server certificate: "; | |
1341 | nssError (); | |
1342 | continue; | |
1343 | } | |
1344 | ||
1345 | // We're interested in the first alternate name. | |
1346 | assert (nameList->type == certDNSName); | |
1347 | string host_name = string ((const char *)nameList->name.other.data, | |
1348 | nameList->name.other.len); | |
1349 | // Don't free nameList. It's part of the tmpArena. | |
1350 | ||
1351 | // Now finally compare the host names. | |
1352 | if (host_name != server->host_name) | |
1353 | { | |
1354 | cerr << "Host name '" << host_name | |
1355 | << "' on certificate does not match the host name '" | |
1356 | << server->host_name << "' of the server" | |
1357 | << endl; | |
1358 | continue; | |
1359 | } | |
1360 | ||
1361 | // All is ok! Remove the certificate from the database. | |
1362 | break; | |
1363 | } // Loop over certificates in the database | |
1364 | ||
1365 | // Was a certificate matching the server found? */ | |
1366 | if (CERT_LIST_END (node, certs)) | |
1367 | { | |
1368 | // Not found. Server is already untrusted. | |
1369 | trust_already_in_place (*server, cert_db_path, true/*revoking*/); | |
1370 | } | |
1371 | else | |
1372 | { | |
1373 | secStatus = SEC_DeletePermCertificate (db_cert); | |
1374 | if (secStatus != SECSuccess) | |
1375 | { | |
1376 | cerr << "Unable to remove certificate from " << cert_db_path | |
1377 | << ": " << endl; | |
1378 | nssError (); | |
1379 | } | |
1380 | } | |
1381 | CERT_DestroyCertList (certs); | |
1382 | certs = NULL; | |
1383 | } // Loop over servers | |
1384 | ||
1385 | cleanup: | |
1386 | if (certs) | |
1387 | CERT_DestroyCertList (certs); | |
1388 | if (slot) | |
1389 | PK11_FreeSlot (slot); | |
1390 | if (tmpArena) | |
1391 | PORT_FreeArena (tmpArena, PR_FALSE); | |
1392 | ||
1393 | NSS_Shutdown (); | |
c77af0d0 DB |
1394 | #endif // HAVE_NSS |
1395 | } | |
1396 | ||
b0e5fa85 | 1397 | static void |
85007c04 | 1398 | get_server_info ( |
1f9938b9 | 1399 | systemtap_session &s, |
85007c04 DB |
1400 | int pmask, |
1401 | vector<compile_server_info> &servers | |
1402 | ) | |
1403 | { | |
1404 | // Get information on compile servers matching the requested criteria. | |
0d1534e8 DB |
1405 | // The order of queries is significant. Accumulating queries must go first |
1406 | // followed by accumulating/filering queries followed by filtering queries. | |
1407 | // We start with an empty vector. | |
1408 | // These queries accumulate server information. | |
1409 | vector<compile_server_info> temp; | |
1410 | if ((pmask & compile_server_all)) | |
1411 | { | |
1412 | get_all_server_info (s, temp); | |
1413 | } | |
2e15a955 DB |
1414 | if ((pmask & compile_server_specified)) |
1415 | { | |
0d1534e8 | 1416 | get_specified_server_info (s, temp); |
2e15a955 | 1417 | } |
0d1534e8 DB |
1418 | // These queries filter server information if the vector is not empty and |
1419 | // accumulate it otherwise. | |
85007c04 DB |
1420 | if ((pmask & compile_server_online)) |
1421 | { | |
0d1534e8 DB |
1422 | get_or_keep_online_server_info (s, temp); |
1423 | } | |
1424 | if ((pmask & compile_server_trusted)) | |
1425 | { | |
1426 | get_or_keep_trusted_server_info (s, temp); | |
85007c04 | 1427 | } |
0d1534e8 DB |
1428 | if ((pmask & compile_server_signer)) |
1429 | { | |
1430 | get_or_keep_signing_server_info (s, temp); | |
1431 | } | |
1432 | // This query filters server information. | |
85007c04 DB |
1433 | if ((pmask & compile_server_compatible)) |
1434 | { | |
0d1534e8 | 1435 | keep_compatible_server_info (s, temp); |
85007c04 | 1436 | } |
0d1534e8 DB |
1437 | |
1438 | // Now add the collected information to the target vector. | |
1439 | merge_server_info (temp, servers); | |
1440 | } | |
1441 | ||
1442 | // Get information about all online servers as well as servers trusted | |
1443 | // as SSL peers and servers trusted as signers. | |
b0e5fa85 | 1444 | static void |
0d1534e8 DB |
1445 | get_all_server_info ( |
1446 | systemtap_session &s, | |
1447 | vector<compile_server_info> &servers | |
1448 | ) | |
1449 | { | |
1450 | // The get_or_keep_XXXX_server_info functions filter the vector | |
1451 | // if it is not empty. So use an empty vector for each query. | |
1452 | vector<compile_server_info> temp; | |
1453 | get_or_keep_online_server_info (s, temp); | |
1454 | merge_server_info (temp, servers); | |
1455 | ||
1456 | temp.clear (); | |
1457 | get_or_keep_trusted_server_info (s, temp); | |
1458 | merge_server_info (temp, servers); | |
1459 | ||
1460 | temp.clear (); | |
1461 | get_or_keep_signing_server_info (s, temp); | |
1462 | merge_server_info (temp, servers); | |
85007c04 DB |
1463 | } |
1464 | ||
b0e5fa85 | 1465 | static void |
2e15a955 DB |
1466 | get_default_server_info ( |
1467 | systemtap_session &s, | |
1468 | vector<compile_server_info> &servers | |
1469 | ) | |
1f9938b9 | 1470 | { |
2e15a955 DB |
1471 | // We only need to obtain this once. This is a good thing(tm) since |
1472 | // obtaining this information is expensive. | |
1473 | static vector<compile_server_info> default_servers; | |
1474 | if (default_servers.empty ()) | |
1f9938b9 | 1475 | { |
c77af0d0 DB |
1476 | // Get the required information. |
1477 | // get_server_info will add an empty entry at the beginning to indicate | |
1478 | // that the search has been performed, in case the search comes up empty. | |
0d1534e8 DB |
1479 | int pmask = server_spec_to_pmask (default_server_spec (s)); |
1480 | get_server_info (s, pmask, default_servers); | |
2e15a955 | 1481 | } |
1f9938b9 | 1482 | |
c77af0d0 | 1483 | // Add the information, but not duplicates. |
2e15a955 | 1484 | unsigned limit = default_servers.size (); |
c77af0d0 | 1485 | for (unsigned i = 0; i < limit; ++i) |
2e15a955 DB |
1486 | add_server_info (default_servers[i], servers); |
1487 | } | |
1f9938b9 | 1488 | |
b0e5fa85 | 1489 | static void |
2e15a955 DB |
1490 | get_specified_server_info ( |
1491 | systemtap_session &s, | |
1492 | vector<compile_server_info> &servers | |
1493 | ) | |
1494 | { | |
1495 | // We only need to obtain this once. This is a good thing(tm) since | |
1496 | // obtaining this information is expensive. | |
1497 | static vector<compile_server_info> specified_servers; | |
1498 | if (specified_servers.empty ()) | |
1499 | { | |
0d1534e8 DB |
1500 | // Maintain an empty entry to indicate that this search has been |
1501 | // performed, in case the search comes up empty. | |
1502 | specified_servers.push_back (compile_server_info ()); | |
1503 | ||
2e15a955 DB |
1504 | // If --use-servers was not specified at all, then return info for the |
1505 | // default server list. | |
1506 | if (s.specified_servers.empty ()) | |
1507 | { | |
1508 | get_default_server_info (s, specified_servers); | |
1509 | } | |
1510 | else | |
1511 | { | |
1512 | // Iterate over the specified servers. For each specification, add to | |
1513 | // the list of servers. | |
1514 | unsigned num_specified_servers = s.specified_servers.size (); | |
1515 | for (unsigned i = 0; i < num_specified_servers; ++i) | |
1516 | { | |
1517 | const string &server = s.specified_servers[i]; | |
1518 | if (server.empty ()) | |
1519 | { | |
1520 | // No server specified. Use the default servers. | |
1521 | get_default_server_info (s, specified_servers); | |
1522 | } | |
1523 | else | |
1524 | { | |
1525 | // Work with the specified server | |
1526 | compile_server_info server_info; | |
1f9938b9 | 1527 | |
2e15a955 DB |
1528 | // See if a port was specified (:n suffix) |
1529 | vector<string> components; | |
1530 | tokenize (server, components, ":"); | |
1531 | if (components.size () > 2) | |
1532 | { | |
334df419 DB |
1533 | // Treat it as a certificate serial number. Look for |
1534 | // all known servers with this serial number. | |
0d1534e8 DB |
1535 | vector<compile_server_info> all_servers; |
1536 | get_all_server_info (s, all_servers); | |
2e15a955 | 1537 | |
334df419 DB |
1538 | // Search the list of servers for ones matching the |
1539 | // serial number specified. | |
2e15a955 | 1540 | unsigned found = 0; |
0d1534e8 DB |
1541 | unsigned limit = all_servers.size (); |
1542 | for (unsigned j = 0; j < limit; ++j) | |
2e15a955 | 1543 | { |
334df419 | 1544 | if (server == all_servers[j].certinfo) |
2e15a955 | 1545 | { |
0d1534e8 | 1546 | add_server_info (all_servers[j], specified_servers); |
2e15a955 DB |
1547 | ++found; |
1548 | } | |
1549 | } | |
334df419 | 1550 | // Did we find one? |
2e15a955 | 1551 | if (s.verbose && found == 0) |
334df419 DB |
1552 | cerr << "No server matching " << server << " found" |
1553 | << endl; | |
1554 | } // specified by cert serial number | |
1555 | else { | |
1556 | // Not specified by serial number. Treat it as host name | |
1557 | // and optional port number. | |
1558 | if (components.size () == 2) | |
1559 | { | |
1560 | // Obtain the port number. | |
1561 | const char *pstr = components.back ().c_str (); | |
1562 | char *estr; | |
1563 | errno = 0; | |
1564 | unsigned long port = strtoul (pstr, & estr, 10); | |
1565 | if (errno == 0 && *estr == '\0' && port <= USHRT_MAX) | |
1566 | server_info.port = port; | |
1567 | else | |
1568 | { | |
1569 | cerr << "Invalid port number specified: " | |
1570 | << components.back () | |
1571 | << endl; | |
1572 | continue; | |
1573 | } | |
1574 | } | |
1575 | ||
1576 | // Obtain the host name. | |
1577 | server_info.host_name = components.front (); | |
1578 | ||
1579 | // Was a port specified? | |
1580 | if (server_info.port != 0) | |
1581 | { | |
1582 | // A specific server was specified. | |
1583 | // Resolve the server. It's not an error if it fails. | |
1584 | // Just less info gathered. | |
1585 | resolve_server (s, server_info); | |
1586 | add_server_info (server_info, specified_servers); | |
1587 | } | |
1588 | else | |
1589 | { | |
1590 | // No port was specified, so find all known servers | |
1591 | // on the specified host. | |
1592 | resolve_host_name (s, server_info.host_name); | |
1593 | vector<compile_server_info> all_servers; | |
1594 | get_all_server_info (s, all_servers); | |
1595 | ||
1596 | // Search the list of servers for ones matching the | |
1597 | // one specified and obtain the port numbers. | |
1598 | unsigned found = 0; | |
1599 | unsigned limit = all_servers.size (); | |
1600 | for (unsigned j = 0; j < limit; ++j) | |
1601 | { | |
1602 | if (server_info == all_servers[j]) | |
1603 | { | |
1604 | add_server_info (all_servers[j], specified_servers); | |
1605 | ++found; | |
1606 | } | |
1607 | } | |
1608 | // Do we have a port number now? | |
1609 | if (s.verbose && found == 0) | |
1610 | cerr << "No server matching " << server << " found" | |
1611 | << endl; | |
1612 | } // No port specified | |
1613 | } // Not specified by cert serial number | |
2e15a955 DB |
1614 | } // Specified server. |
1615 | } // Loop over --use-server options | |
1616 | } // -- use-server specified | |
2e15a955 DB |
1617 | } // Server information is not cached |
1618 | ||
c77af0d0 | 1619 | // Add the information, but not duplicates. |
2e15a955 | 1620 | unsigned limit = specified_servers.size (); |
c77af0d0 | 1621 | for (unsigned i = 0; i < limit; ++i) |
2e15a955 | 1622 | add_server_info (specified_servers[i], servers); |
1f9938b9 DB |
1623 | } |
1624 | ||
b0e5fa85 | 1625 | static void |
0d1534e8 | 1626 | get_or_keep_trusted_server_info ( |
1f9938b9 | 1627 | systemtap_session &s, |
85007c04 DB |
1628 | vector<compile_server_info> &servers |
1629 | ) | |
1630 | { | |
0d1534e8 DB |
1631 | // We only need to obtain this once. This is a good thing(tm) since |
1632 | // obtaining this information is expensive. | |
1633 | static vector<compile_server_info> trusted_servers; | |
1634 | if (trusted_servers.empty ()) | |
85007c04 | 1635 | { |
0d1534e8 DB |
1636 | // Maintain an empty entry to indicate that this search has been |
1637 | // performed, in case the search comes up empty. | |
1638 | trusted_servers.push_back (compile_server_info ()); | |
1639 | ||
1640 | #if HAVE_NSS | |
c77af0d0 DB |
1641 | // Check the private database first. |
1642 | string cert_db_path = private_ssl_cert_db_path (s); | |
1643 | get_server_info_from_db (s, trusted_servers, cert_db_path); | |
0d1534e8 | 1644 | |
c77af0d0 DB |
1645 | // Now check the global database. |
1646 | cert_db_path = global_ssl_cert_db_path (); | |
0d1534e8 | 1647 | get_server_info_from_db (s, trusted_servers, cert_db_path); |
0d1534e8 DB |
1648 | #else // ! HAVE_NSS |
1649 | // Without NSS, we can't determine whether a server is trusted. | |
1650 | // Issue a warning. | |
1651 | if (s.verbose) | |
1652 | clog << "Unable to determine server trust as an SSL peer" << endl; | |
1653 | #endif // ! HAVE_NSS | |
1654 | } // Server information is not cached | |
1655 | ||
1656 | if (! servers.empty ()) | |
1657 | { | |
1658 | // Filter the existing vector by keeping the information in common with | |
1659 | // the trusted_server vector. | |
1660 | keep_common_server_info (trusted_servers, servers); | |
1661 | } | |
1662 | else | |
1663 | { | |
c77af0d0 | 1664 | // Add the information, but not duplicates. |
0d1534e8 | 1665 | unsigned limit = trusted_servers.size (); |
c77af0d0 | 1666 | for (unsigned i = 0; i < limit; ++i) |
0d1534e8 DB |
1667 | add_server_info (trusted_servers[i], servers); |
1668 | } | |
1669 | } | |
1670 | ||
b0e5fa85 | 1671 | static void |
0d1534e8 DB |
1672 | get_or_keep_signing_server_info ( |
1673 | systemtap_session &s, | |
1674 | vector<compile_server_info> &servers | |
1675 | ) | |
1676 | { | |
1677 | // We only need to obtain this once. This is a good thing(tm) since | |
1678 | // obtaining this information is expensive. | |
1679 | static vector<compile_server_info> signing_servers; | |
1680 | if (signing_servers.empty ()) | |
1681 | { | |
1682 | // Maintain an empty entry to indicate that this search has been | |
1683 | // performed, in case the search comes up empty. | |
1684 | signing_servers.push_back (compile_server_info ()); | |
1685 | ||
1686 | #if HAVE_NSS | |
0d1534e8 | 1687 | // For all users, check the global database. |
c77af0d0 | 1688 | string cert_db_path = signing_cert_db_path (); |
0d1534e8 | 1689 | get_server_info_from_db (s, signing_servers, cert_db_path); |
0d1534e8 DB |
1690 | #else // ! HAVE_NSS |
1691 | // Without NSS, we can't determine whether a server is a trusted | |
1692 | // signer. Issue a warning. | |
1693 | if (s.verbose) | |
1694 | clog << "Unable to determine server trust as a module signer" << endl; | |
1695 | #endif // ! HAVE_NSS | |
1696 | } // Server information is not cached | |
1697 | ||
1698 | if (! servers.empty ()) | |
1699 | { | |
1700 | // Filter the existing vector by keeping the information in common with | |
1701 | // the signing_server vector. | |
1702 | keep_common_server_info (signing_servers, servers); | |
1703 | } | |
1704 | else | |
1705 | { | |
c77af0d0 | 1706 | // Add the information, but not duplicates. |
0d1534e8 | 1707 | unsigned limit = signing_servers.size (); |
c77af0d0 | 1708 | for (unsigned i = 0; i < limit; ++i) |
0d1534e8 DB |
1709 | add_server_info (signing_servers[i], servers); |
1710 | } | |
1711 | } | |
1712 | ||
1713 | // Obtain information about servers from the certificates in the given database. | |
b0e5fa85 | 1714 | static void |
0d1534e8 DB |
1715 | get_server_info_from_db ( |
1716 | systemtap_session &s, | |
1717 | vector<compile_server_info> &servers, | |
1718 | const string &cert_db_path | |
1719 | ) | |
1720 | { | |
b0e5fa85 DB |
1721 | // This function will never be called if we don't have NSS, but it must |
1722 | // still compile. | |
0d1534e8 DB |
1723 | #if HAVE_NSS |
1724 | // Make sure the given path exists. | |
1725 | if (! file_exists (cert_db_path)) | |
1726 | { | |
1727 | if (s.verbose > 1) | |
1728 | cerr << "Certificate database '" << cert_db_path << "' does not exist." | |
1729 | << endl; | |
1730 | return; | |
1731 | } | |
1732 | ||
1733 | // Must predeclare these because of jumps to cleanup: below. | |
0d1534e8 | 1734 | PK11SlotInfo *slot = NULL; |
0d1534e8 | 1735 | CERTCertDBHandle *handle; |
c77af0d0 DB |
1736 | PRArenaPool *tmpArena = NULL; |
1737 | CERTCertList *certs = NULL; | |
1738 | CERTCertificate *db_cert; | |
0d1534e8 DB |
1739 | vector<compile_server_info> temp_list; |
1740 | ||
1741 | // Initialize the NSS libraries -- readonly | |
1742 | SECStatus secStatus = NSS_Init (cert_db_path.c_str ()); | |
1743 | if (secStatus != SECSuccess) | |
1744 | { | |
1745 | cerr << "Error initializing NSS for " << cert_db_path << endl; | |
1746 | nssError (); | |
1747 | goto cleanup; | |
1748 | } | |
1749 | ||
1750 | // Search the client-side database of trusted servers. | |
1751 | slot = PK11_GetInternalKeySlot (); | |
1752 | handle = CERT_GetDefaultCertDB(); | |
c77af0d0 DB |
1753 | db_cert = PK11_FindCertFromNickname (server_cert_nickname, NULL); |
1754 | if (! db_cert) | |
0d1534e8 DB |
1755 | { |
1756 | // No trusted servers. Not an error. Just an empty list returned. | |
1757 | goto cleanup; | |
1758 | } | |
1759 | ||
1760 | // Here, we have one cert with the desired nickname. | |
1761 | // Now, we will attempt to get a list of ALL certs | |
1762 | // with the same subject name as the cert we have. That list | |
1763 | // should contain, at a minimum, the one cert we have already found. | |
1764 | // If the list of certs is empty (NULL), the libraries have failed. | |
c77af0d0 | 1765 | certs = CERT_CreateSubjectCertList (NULL, handle, & db_cert->derSubject, |
0d1534e8 | 1766 | PR_Now (), PR_FALSE); |
c77af0d0 | 1767 | CERT_DestroyCertificate (db_cert); |
0d1534e8 DB |
1768 | if (! certs) |
1769 | { | |
1770 | cerr << "Unable to query client certificate database: " << endl; | |
1771 | PORT_SetError (SEC_ERROR_LIBRARY_FAILURE); | |
1772 | nssError (); | |
1773 | goto cleanup; | |
1774 | } | |
1775 | ||
1776 | // A memory pool to work in | |
1777 | tmpArena = PORT_NewArena(DER_DEFAULT_CHUNKSIZE); | |
1778 | if (! tmpArena) | |
1779 | { | |
1780 | cerr << "Out of memory:"; | |
1781 | nssError (); | |
1782 | goto cleanup; | |
1783 | } | |
1784 | for (CERTCertListNode *node = CERT_LIST_HEAD (certs); | |
1785 | ! CERT_LIST_END (node, certs); | |
1786 | node = CERT_LIST_NEXT (node)) | |
1787 | { | |
1788 | compile_server_info server_info; | |
1789 | ||
1790 | // The certificate we're working with. | |
c77af0d0 | 1791 | db_cert = node->cert; |
0d1534e8 DB |
1792 | |
1793 | // Get the host name. It is in the alt-name extension of the | |
1794 | // certificate. | |
1795 | SECItem subAltName; | |
1796 | subAltName.data = NULL; | |
c77af0d0 | 1797 | secStatus = CERT_FindCertExtension (db_cert, |
0d1534e8 DB |
1798 | SEC_OID_X509_SUBJECT_ALT_NAME, |
1799 | & subAltName); | |
1800 | if (secStatus != SECSuccess || ! subAltName.data) | |
85007c04 | 1801 | { |
0d1534e8 DB |
1802 | cerr << "Unable to find alt name extension on server certificate: " << endl; |
1803 | nssError (); | |
85007c04 DB |
1804 | continue; |
1805 | } | |
85007c04 | 1806 | |
0d1534e8 DB |
1807 | // Decode the extension. |
1808 | CERTGeneralName *nameList = CERT_DecodeAltNameExtension (tmpArena, & subAltName); | |
1809 | SECITEM_FreeItem(& subAltName, PR_FALSE); | |
1810 | if (! nameList) | |
85007c04 | 1811 | { |
0d1534e8 DB |
1812 | cerr << "Unable to decode alt name extension on server certificate: " << endl; |
1813 | nssError (); | |
85007c04 DB |
1814 | continue; |
1815 | } | |
0d1534e8 DB |
1816 | |
1817 | // We're interested in the first alternate name. | |
1818 | assert (nameList->type == certDNSName); | |
1819 | server_info.host_name = string ((const char *)nameList->name.other.data, | |
1820 | nameList->name.other.len); | |
1821 | // Don't free nameList. It's part of the tmpArena. | |
1822 | ||
1823 | // Get the serial number. | |
1824 | ostringstream field; | |
334df419 | 1825 | field << hex << setfill('0') << right; |
c77af0d0 | 1826 | for (unsigned i = 0; i < db_cert->serialNumber.len; ++i) |
0d1534e8 DB |
1827 | { |
1828 | if (i > 0) | |
1829 | field << ':'; | |
334df419 | 1830 | field << setw(2) << (unsigned)db_cert->serialNumber.data[i]; |
0d1534e8 DB |
1831 | } |
1832 | server_info.certinfo = field.str (); | |
1833 | ||
1834 | // We can't call resolve_server while NSS is active on a database | |
1835 | // since it may recursively call this function for another database. | |
1836 | // So, keep a temporary list of the server info discovered and then | |
1837 | // make a pass over it later resolving each server. | |
1838 | add_server_info (server_info, temp_list); | |
1839 | } | |
1840 | ||
1841 | cleanup: | |
1842 | if (certs) | |
1843 | CERT_DestroyCertList (certs); | |
1844 | if (slot) | |
1845 | PK11_FreeSlot (slot); | |
1846 | if (tmpArena) | |
1847 | PORT_FreeArena (tmpArena, PR_FALSE); | |
1848 | ||
1849 | NSS_Shutdown (); | |
1850 | ||
1851 | // Resolve each server discovered in order to eliminate duplicates when | |
1852 | // adding them to the target verctor. | |
1853 | // This must be done after NSS_Shutdown since resolve_server can call this | |
1854 | // function recursively. | |
1855 | for (vector<compile_server_info>::iterator i = temp_list.begin (); | |
1856 | i != temp_list.end (); | |
1857 | ++i) | |
1858 | { | |
1859 | resolve_server (s, *i); | |
1860 | add_server_info (*i, servers); | |
1861 | } | |
1862 | #endif // HAVE_NSS | |
1863 | } | |
1864 | ||
b0e5fa85 | 1865 | static void |
0d1534e8 DB |
1866 | keep_compatible_server_info ( |
1867 | systemtap_session &s, | |
1868 | vector<compile_server_info> &servers | |
1869 | ) | |
1870 | { | |
1871 | #if HAVE_AVAHI | |
1872 | // Remove entries for servers incompatible with the host environment | |
1873 | // from the given list of servers. | |
1874 | // A compatible server compiles for the kernel release and architecture | |
1875 | // of the host environment. | |
1876 | for (unsigned i = 0; i < servers.size (); /**/) | |
1877 | { | |
c77af0d0 DB |
1878 | // Retain empty entries. |
1879 | if (! servers[i].host_name.empty ()) | |
85007c04 | 1880 | { |
c77af0d0 DB |
1881 | // Check the target of the server. |
1882 | if (servers[i].sysinfo != s.kernel_release + " " + s.architecture) | |
1883 | { | |
1884 | // Target platform mismatch. | |
1885 | servers.erase (servers.begin () + i); | |
1886 | continue; | |
1887 | } | |
85007c04 DB |
1888 | } |
1889 | ||
1890 | // The server is compatible. Leave it in the list. | |
1891 | ++i; | |
1892 | } | |
0d1534e8 DB |
1893 | #else // ! HAVE_AVAHI |
1894 | // Without Avahi, we can't obtain the target platform of the server. | |
1895 | // Issue a warning. | |
1896 | if (s.verbose) | |
1897 | clog << "Unable to detect server compatibility" << endl; | |
1898 | servers.clear (); | |
1899 | #endif | |
85007c04 DB |
1900 | } |
1901 | ||
0d1534e8 DB |
1902 | // Attempt to obtain complete information about this server from the local |
1903 | // name service and/or from online server discovery. | |
b0e5fa85 DB |
1904 | static void |
1905 | resolve_server (systemtap_session& s, compile_server_info &server_info) | |
0d1534e8 DB |
1906 | { |
1907 | // Attempt to resolve the host name and ip. | |
1908 | int status = resolve_host_name (s, server_info.host_name, | |
1909 | & server_info.ip_address); | |
1910 | if (status != 0) | |
b0e5fa85 | 1911 | return; // message already issued. |
0d1534e8 DB |
1912 | |
1913 | // See if we can discover the target of this server. | |
1914 | if (server_info.sysinfo.empty ()) | |
1915 | { | |
1916 | vector<compile_server_info> online_servers; | |
1917 | get_or_keep_online_server_info (s, online_servers); | |
1918 | merge_server_info (online_servers, server_info); | |
1919 | } | |
1920 | ||
1921 | // See if we can discover the certificate of this server. Look in | |
1922 | // the databases of trusted peers and trusted signers. | |
1923 | if (server_info.certinfo.empty ()) | |
1924 | { | |
1925 | vector<compile_server_info> trusted_servers; | |
1926 | get_or_keep_trusted_server_info (s, trusted_servers); | |
1927 | merge_server_info (trusted_servers, server_info); | |
1928 | } | |
1929 | if (server_info.certinfo.empty ()) | |
1930 | { | |
1931 | vector<compile_server_info> signing_servers; | |
1932 | get_or_keep_signing_server_info (s, signing_servers); | |
1933 | merge_server_info (signing_servers, server_info); | |
1934 | } | |
0d1534e8 DB |
1935 | } |
1936 | ||
1937 | // Obtain the canonical host name and, if requested, ip address of a host. | |
b0e5fa85 DB |
1938 | static int |
1939 | resolve_host_name ( | |
0d1534e8 DB |
1940 | systemtap_session& s, |
1941 | string &host_name, | |
1942 | string *ip_address | |
1943 | ) | |
2e15a955 DB |
1944 | { |
1945 | struct addrinfo hints; | |
1946 | memset(& hints, 0, sizeof (hints)); | |
1947 | hints.ai_family = AF_INET; // AF_UNSPEC or AF_INET6 to force version | |
1948 | struct addrinfo *res; | |
0d1534e8 | 1949 | int status = getaddrinfo(host_name.c_str(), NULL, & hints, & res); |
2e15a955 DB |
1950 | if (status != 0) |
1951 | goto error; | |
1952 | ||
1953 | // Obtain the ip address and canonical name of the resolved server. | |
1954 | assert (res); | |
1955 | for (const struct addrinfo *p = res; p != NULL; p = p->ai_next) | |
1956 | { | |
1957 | if (p->ai_family != AF_INET) | |
1958 | continue; // Not an IPv4 address | |
1959 | ||
0d1534e8 | 1960 | // Get the canonical name. |
2e15a955 DB |
1961 | char hbuf[NI_MAXHOST]; |
1962 | status = getnameinfo (p->ai_addr, sizeof (*p->ai_addr), | |
1963 | hbuf, sizeof (hbuf), NULL, 0, | |
1964 | NI_NAMEREQD | NI_IDN); | |
1965 | if (status != 0) | |
1966 | continue; | |
0d1534e8 DB |
1967 | host_name = hbuf; |
1968 | ||
1969 | // Get the ip address, if requested | |
1970 | if (ip_address) | |
1971 | { | |
1972 | // get the pointer to the address itself, | |
1973 | struct sockaddr_in *ipv4 = (struct sockaddr_in *)p->ai_addr; | |
1974 | void *addr = & ipv4->sin_addr; | |
1975 | ||
1976 | // convert the IP to a string. | |
1977 | char ipstr[INET_ADDRSTRLEN]; | |
1978 | inet_ntop(p->ai_family, addr, ipstr, sizeof (ipstr)); | |
1979 | *ip_address = ipstr; | |
1980 | } | |
2e15a955 | 1981 | |
2e15a955 DB |
1982 | break; // Use the info from the first IPv4 result. |
1983 | } | |
1984 | freeaddrinfo(res); // free the linked list | |
1985 | ||
1986 | if (status != 0) | |
1987 | goto error; | |
1988 | ||
1989 | return 0; | |
1990 | ||
1991 | error: | |
0d1534e8 | 1992 | if (s.verbose > 1) |
2e15a955 | 1993 | { |
0d1534e8 | 1994 | clog << "Unable to resolve host name " << host_name |
2e15a955 DB |
1995 | << ": " << gai_strerror(status) |
1996 | << endl; | |
1997 | } | |
0d1534e8 | 1998 | return status; |
2e15a955 DB |
1999 | } |
2000 | ||
85007c04 | 2001 | #if HAVE_AVAHI |
131f914e DB |
2002 | // Avahi API Callbacks. |
2003 | //----------------------------------------------------------------------- | |
85007c04 DB |
2004 | struct browsing_context { |
2005 | AvahiSimplePoll *simple_poll; | |
2006 | AvahiClient *client; | |
2007 | vector<compile_server_info> *servers; | |
2008 | }; | |
2009 | ||
0d1534e8 DB |
2010 | static string |
2011 | extract_field_from_avahi_txt (const string &label, const string &txt) | |
2012 | { | |
2013 | // Extract the requested field from the Avahi TXT. | |
2014 | string prefix = "\"" + label; | |
2015 | size_t ix = txt.find (prefix); | |
2016 | if (ix == string::npos) | |
2017 | { | |
2018 | // Label not found. | |
2019 | return ""; | |
2020 | } | |
2021 | ||
2022 | // This is the start of the field. | |
2023 | string field = txt.substr (ix + prefix.size ()); | |
2024 | ||
2025 | // Find the end of the field. | |
2026 | ix = field.find('"'); | |
2027 | if (ix != string::npos) | |
2028 | field = field.substr (0, ix); | |
2029 | ||
2030 | return field; | |
2031 | } | |
2032 | ||
85007c04 DB |
2033 | extern "C" |
2034 | void resolve_callback( | |
2035 | AvahiServiceResolver *r, | |
2036 | AVAHI_GCC_UNUSED AvahiIfIndex interface, | |
2037 | AVAHI_GCC_UNUSED AvahiProtocol protocol, | |
2038 | AvahiResolverEvent event, | |
2039 | const char *name, | |
2040 | const char *type, | |
2041 | const char *domain, | |
2042 | const char *host_name, | |
2043 | const AvahiAddress *address, | |
2044 | uint16_t port, | |
2045 | AvahiStringList *txt, | |
2046 | AvahiLookupResultFlags flags, | |
2047 | AVAHI_GCC_UNUSED void* userdata) { | |
2048 | ||
2049 | assert(r); | |
2050 | const browsing_context *context = (browsing_context *)userdata; | |
2051 | vector<compile_server_info> *servers = context->servers; | |
2052 | ||
2053 | // Called whenever a service has been resolved successfully or timed out. | |
2054 | ||
2055 | switch (event) { | |
2056 | case AVAHI_RESOLVER_FAILURE: | |
2057 | cerr << "Failed to resolve service '" << name | |
2058 | << "' of type '" << type | |
2059 | << "' in domain '" << domain | |
2060 | << "': " << avahi_strerror(avahi_client_errno(avahi_service_resolver_get_client(r))) | |
2061 | << endl; | |
2062 | break; | |
2063 | ||
2064 | case AVAHI_RESOLVER_FOUND: { | |
2065 | char a[AVAHI_ADDRESS_STR_MAX], *t; | |
2066 | avahi_address_snprint(a, sizeof(a), address); | |
2067 | t = avahi_string_list_to_string(txt); | |
2068 | ||
2069 | // Save the information of interest. | |
2070 | compile_server_info info; | |
0d1534e8 | 2071 | info.host_name = host_name; |
85007c04 DB |
2072 | info.ip_address = strdup (a); |
2073 | info.port = port; | |
0d1534e8 DB |
2074 | info.sysinfo = extract_field_from_avahi_txt ("sysinfo=", t); |
2075 | info.certinfo = extract_field_from_avahi_txt ("certinfo=", t); | |
85007c04 DB |
2076 | |
2077 | // Add this server to the list of discovered servers. | |
2e15a955 | 2078 | add_server_info (info, *servers); |
85007c04 DB |
2079 | |
2080 | avahi_free(t); | |
2081 | } | |
2082 | } | |
2083 | ||
2084 | avahi_service_resolver_free(r); | |
2085 | } | |
2086 | ||
2087 | extern "C" | |
2088 | void browse_callback( | |
2089 | AvahiServiceBrowser *b, | |
2090 | AvahiIfIndex interface, | |
2091 | AvahiProtocol protocol, | |
2092 | AvahiBrowserEvent event, | |
2093 | const char *name, | |
2094 | const char *type, | |
2095 | const char *domain, | |
2096 | AVAHI_GCC_UNUSED AvahiLookupResultFlags flags, | |
2097 | void* userdata) { | |
2098 | ||
2099 | browsing_context *context = (browsing_context *)userdata; | |
2100 | AvahiClient *c = context->client; | |
2101 | AvahiSimplePoll *simple_poll = context->simple_poll; | |
2102 | assert(b); | |
2103 | ||
2104 | // Called whenever a new services becomes available on the LAN or is removed from the LAN. | |
2105 | ||
2106 | switch (event) { | |
2107 | case AVAHI_BROWSER_FAILURE: | |
2108 | cerr << "Avahi browse failed: " | |
2109 | << avahi_strerror(avahi_client_errno(avahi_service_browser_get_client(b))) | |
2110 | << endl; | |
2111 | avahi_simple_poll_quit(simple_poll); | |
2112 | break; | |
2113 | ||
2114 | case AVAHI_BROWSER_NEW: | |
2115 | // We ignore the returned resolver object. In the callback | |
2116 | // function we free it. If the server is terminated before | |
2117 | // the callback function is called the server will free | |
2118 | // the resolver for us. | |
2119 | if (!(avahi_service_resolver_new(c, interface, protocol, name, type, domain, | |
2120 | AVAHI_PROTO_UNSPEC, (AvahiLookupFlags)0, resolve_callback, context))) { | |
2121 | cerr << "Failed to resolve service '" << name | |
2122 | << "': " << avahi_strerror(avahi_client_errno(c)) | |
2123 | << endl; | |
2124 | } | |
2125 | break; | |
2126 | ||
2127 | case AVAHI_BROWSER_REMOVE: | |
2128 | case AVAHI_BROWSER_ALL_FOR_NOW: | |
2129 | case AVAHI_BROWSER_CACHE_EXHAUSTED: | |
2130 | break; | |
2131 | } | |
2132 | } | |
2133 | ||
2134 | extern "C" | |
2135 | void client_callback(AvahiClient *c, AvahiClientState state, AVAHI_GCC_UNUSED void * userdata) { | |
2136 | assert(c); | |
2137 | browsing_context *context = (browsing_context *)userdata; | |
2138 | AvahiSimplePoll *simple_poll = context->simple_poll; | |
2139 | ||
2140 | // Called whenever the client or server state changes. | |
2141 | ||
2142 | if (state == AVAHI_CLIENT_FAILURE) { | |
2143 | cerr << "Avahi Server connection failure: " | |
2144 | << avahi_strerror(avahi_client_errno(c)) | |
2145 | << endl; | |
2146 | avahi_simple_poll_quit(simple_poll); | |
2147 | } | |
2148 | } | |
2149 | ||
2150 | extern "C" | |
2151 | void timeout_callback(AVAHI_GCC_UNUSED AvahiTimeout *e, AVAHI_GCC_UNUSED void *userdata) { | |
2152 | browsing_context *context = (browsing_context *)userdata; | |
2153 | AvahiSimplePoll *simple_poll = context->simple_poll; | |
2154 | avahi_simple_poll_quit(simple_poll); | |
2155 | } | |
2156 | #endif // HAVE_AVAHI | |
2157 | ||
b0e5fa85 | 2158 | static void |
0d1534e8 DB |
2159 | get_or_keep_online_server_info (systemtap_session &s, |
2160 | vector<compile_server_info> &servers) | |
85007c04 | 2161 | { |
2e15a955 DB |
2162 | // We only need to obtain this once. This is a good thing(tm) since |
2163 | // obtaining this information is expensive. | |
2164 | static vector<compile_server_info> online_servers; | |
2165 | if (online_servers.empty ()) | |
2166 | { | |
0d1534e8 DB |
2167 | // Maintain an empty entry to indicate that this search has been |
2168 | // performed, in case the search comes up empty. | |
2169 | online_servers.push_back (compile_server_info ()); | |
2170 | #if HAVE_AVAHI | |
2e15a955 DB |
2171 | // Must predeclare these due to jumping on error to fail: |
2172 | unsigned limit; | |
2173 | vector<compile_server_info> raw_servers; | |
1f9938b9 | 2174 | |
2e15a955 DB |
2175 | // Initialize. |
2176 | AvahiClient *client = NULL; | |
2177 | AvahiServiceBrowser *sb = NULL; | |
85007c04 | 2178 | |
2e15a955 DB |
2179 | // Allocate main loop object. |
2180 | AvahiSimplePoll *simple_poll; | |
2181 | if (!(simple_poll = avahi_simple_poll_new())) | |
2182 | { | |
2183 | cerr << "Failed to create Avahi simple poll object" << endl; | |
2184 | goto fail; | |
2185 | } | |
2186 | browsing_context context; | |
2187 | context.simple_poll = simple_poll; | |
2188 | context.servers = & raw_servers; | |
2189 | ||
2190 | // Allocate a new Avahi client | |
2191 | int error; | |
2192 | client = avahi_client_new (avahi_simple_poll_get (simple_poll), | |
2193 | (AvahiClientFlags)0, | |
2194 | client_callback, & context, & error); | |
2195 | ||
2196 | // Check whether creating the client object succeeded. | |
2197 | if (! client) | |
2198 | { | |
2199 | cerr << "Failed to create Avahi client: " | |
2200 | << avahi_strerror(error) | |
2201 | << endl; | |
2202 | goto fail; | |
2203 | } | |
2204 | context.client = client; | |
85007c04 | 2205 | |
2e15a955 DB |
2206 | // Create the service browser. |
2207 | if (!(sb = avahi_service_browser_new (client, AVAHI_IF_UNSPEC, | |
2208 | AVAHI_PROTO_UNSPEC, "_stap._tcp", | |
2209 | NULL, (AvahiLookupFlags)0, | |
2210 | browse_callback, & context))) | |
2211 | { | |
2212 | cerr << "Failed to create Avahi service browser: " | |
2213 | << avahi_strerror(avahi_client_errno(client)) | |
2214 | << endl; | |
2215 | goto fail; | |
2216 | } | |
85007c04 | 2217 | |
2e15a955 DB |
2218 | // Timeout after 2 seconds. |
2219 | struct timeval tv; | |
2220 | avahi_simple_poll_get(simple_poll)->timeout_new( | |
85007c04 | 2221 | avahi_simple_poll_get(simple_poll), |
2e15a955 DB |
2222 | avahi_elapse_time(&tv, 1000*2, 0), |
2223 | timeout_callback, | |
2224 | & context); | |
85007c04 | 2225 | |
2e15a955 DB |
2226 | // Run the main loop. |
2227 | avahi_simple_poll_loop(simple_poll); | |
1f9938b9 | 2228 | |
2e15a955 DB |
2229 | // Resolve each server discovered and eliminate duplicates. |
2230 | limit = raw_servers.size (); | |
2231 | for (unsigned i = 0; i < limit; ++i) | |
2232 | { | |
2233 | compile_server_info &raw_server = raw_servers[i]; | |
2234 | ||
2235 | // Delete the domain, if it is '.local' | |
2236 | string &host_name = raw_server.host_name; | |
2237 | string::size_type dot_index = host_name.find ('.'); | |
2238 | assert (dot_index != 0); | |
2239 | string domain = host_name.substr (dot_index + 1); | |
2240 | if (domain == "local") | |
2241 | host_name = host_name.substr (0, dot_index); | |
2242 | ||
2243 | // Now resolve the server (name, ip address, etc) | |
2244 | // Not an error if it fails. Just less info gathered. | |
2245 | resolve_server (s, raw_server); | |
2246 | ||
2247 | // Add it to the list of servers, unless it is duplicate. | |
2248 | add_server_info (raw_server, online_servers); | |
2249 | } | |
1f9938b9 | 2250 | |
2e15a955 DB |
2251 | fail: |
2252 | // Cleanup. | |
2253 | if (sb) | |
85007c04 DB |
2254 | avahi_service_browser_free(sb); |
2255 | ||
2e15a955 | 2256 | if (client) |
85007c04 DB |
2257 | avahi_client_free(client); |
2258 | ||
2e15a955 | 2259 | if (simple_poll) |
85007c04 | 2260 | avahi_simple_poll_free(simple_poll); |
0d1534e8 DB |
2261 | #else // ! HAVE_AVAHI |
2262 | // Without Avahi, we can't detect online servers. Issue a warning. | |
2263 | if (s.verbose) | |
2264 | clog << "Unable to detect online servers" << endl; | |
2265 | #endif // ! HAVE_AVAHI | |
2e15a955 DB |
2266 | } // Server information is not cached. |
2267 | ||
0d1534e8 DB |
2268 | if (! servers.empty ()) |
2269 | { | |
2270 | // Filter the existing vector by keeping the information in common with | |
2271 | // the online_server vector. | |
2272 | keep_common_server_info (online_servers, servers); | |
2273 | } | |
2274 | else | |
2275 | { | |
c77af0d0 | 2276 | // Add the information, but not duplicates. |
0d1534e8 | 2277 | unsigned limit = online_servers.size (); |
c77af0d0 | 2278 | for (unsigned i = 0; i < limit; ++i) |
0d1534e8 DB |
2279 | add_server_info (online_servers[i], servers); |
2280 | } | |
85007c04 | 2281 | } |
2e15a955 | 2282 | |
0d1534e8 DB |
2283 | // Add server info to a list, avoiding duplicates. Merge information from |
2284 | // two duplicate items. | |
b0e5fa85 | 2285 | static void |
2e15a955 DB |
2286 | add_server_info ( |
2287 | const compile_server_info &info, vector<compile_server_info>& list | |
2288 | ) | |
2289 | { | |
0d1534e8 DB |
2290 | vector<compile_server_info>::iterator s = |
2291 | find (list.begin (), list.end (), info); | |
2292 | if (s != list.end ()) | |
2e15a955 | 2293 | { |
0d1534e8 DB |
2294 | // Duplicate. Merge the two items. |
2295 | merge_server_info (info, *s); | |
2296 | return; | |
2e15a955 DB |
2297 | } |
2298 | list.push_back (info); | |
2299 | } | |
0d1534e8 DB |
2300 | |
2301 | // Filter the second vector by keeping information in common with the first | |
2302 | // vector. | |
b0e5fa85 | 2303 | static void |
0d1534e8 DB |
2304 | keep_common_server_info ( |
2305 | const vector<compile_server_info> &info_to_keep, | |
2306 | vector<compile_server_info> &filtered_info | |
2307 | ) | |
2308 | { | |
2309 | for (unsigned i = 0; i < filtered_info.size (); /**/) | |
2310 | { | |
2311 | const vector<compile_server_info>::const_iterator j = | |
2312 | find (info_to_keep.begin (), info_to_keep.end (), filtered_info[i]); | |
2313 | // Was the item found? | |
2314 | if (j != info_to_keep.end ()) | |
2315 | { | |
2316 | // This item was found in the info to be kept. Keep it and merge | |
2317 | // the information. | |
2318 | merge_server_info (*j, filtered_info[i]); | |
2319 | ++i; | |
2320 | continue; | |
2321 | } | |
2322 | ||
2323 | // The item was not found. Delete it. | |
2324 | filtered_info.erase (filtered_info.begin () + i); | |
2325 | } | |
2326 | } | |
2327 | ||
2328 | // Merge two compile server info items. | |
b0e5fa85 | 2329 | static void |
0d1534e8 DB |
2330 | merge_server_info ( |
2331 | const compile_server_info &source, | |
2332 | compile_server_info &target | |
2333 | ) | |
2334 | { | |
2335 | if (target.host_name.empty ()) | |
2336 | target.host_name = source.host_name; | |
2337 | if (target.ip_address.empty ()) | |
2338 | target.ip_address = source.ip_address; | |
2339 | if (target.port == 0) | |
2340 | target.port = source.port; | |
2341 | if (target.sysinfo.empty ()) | |
2342 | target.sysinfo = source.sysinfo; | |
2343 | if (target.certinfo.empty ()) | |
2344 | target.certinfo = source.certinfo; | |
2345 | } | |
2346 | ||
2347 | // Merge two compile server info vectors. | |
b0e5fa85 | 2348 | static void |
0d1534e8 DB |
2349 | merge_server_info ( |
2350 | const vector<compile_server_info> &source, | |
2351 | vector<compile_server_info> &target | |
2352 | ) | |
2353 | { | |
2354 | for (vector<compile_server_info>::const_iterator i = source.begin (); | |
2355 | i != source.end (); | |
2356 | ++i) | |
2357 | add_server_info (*i, target); | |
2358 | } | |
2359 | ||
2360 | // Merge info from a compile server info vector into a compile | |
2361 | // server info item. | |
b0e5fa85 | 2362 | static void |
0d1534e8 DB |
2363 | merge_server_info ( |
2364 | const vector<compile_server_info> &source, | |
2365 | compile_server_info &target | |
2366 | ) | |
2367 | { | |
2368 | for (vector<compile_server_info>::const_iterator i = source.begin (); | |
2369 | i != source.end (); | |
2370 | ++i) | |
2371 | { | |
2372 | if (*i == target) | |
2373 | { | |
2374 | merge_server_info (*i, target); | |
2375 | break; | |
2376 | } | |
2377 | } | |
2378 | } |