]> sourceware.org Git - systemtap.git/blob - client-http.cxx
1711d8d443d46c87fa25659d24185ac3a3a804a0
[systemtap.git] / client-http.cxx
1 // -*- C++ -*-
2 // Copyright (C) 2017, 2018 Red Hat Inc.
3 //
4 // This file is part of systemtap, and is free software. You can
5 // redistribute it and/or modify it under the terms of the GNU General
6 // Public License (GPL); either version 2, or (at your option) any
7 // later version.
8
9 #include "config.h"
10
11 #ifdef HAVE_HTTP_SUPPORT
12 #include "session.h"
13 #include "client-http.h"
14 #include "util.h"
15 #include "staptree.h"
16 #include "elaborate.h"
17 #include "nsscommon.h"
18
19 #include <iostream>
20 #include <sstream>
21 #include <fstream>
22 #include <map>
23 #include <vector>
24
25
26 extern "C" {
27 #include <string.h>
28 #include <curl/curl.h>
29 #include <curl/easy.h>
30 #include <json-c/json.h>
31 #include <sys/stat.h>
32 #include <rpm/rpmlib.h>
33 #include <rpm/header.h>
34 #include <rpm/rpmts.h>
35 #include <rpm/rpmdb.h>
36 #include <search.h>
37 #include <elfutils/libdwfl.h>
38 #include <elfutils/libdw.h>
39 #include <fcntl.h>
40 #include <nss3/nss.h>
41 }
42
43 using namespace std;
44
45
46 class http_client
47 {
48 public:
49 http_client (systemtap_session &s):
50 root(0),
51 s(s),
52 curl(0),
53 retry(0),
54 location(nullptr) { }
55 ~http_client () {if (curl) curl_easy_cleanup(curl);
56 remove_file_or_dir (pem_cert_file.c_str());}
57
58 json_object *root;
59 std::map<std::string, std::string> header_values;
60 std::vector<std::tuple<std::string, std::string>> env_vars;
61 enum download_type {json_type, file_type};
62 std::string pem_cert_file;
63 std::string host;
64 enum cert_type {signer_trust, ssl_trust};
65
66
67 bool download (const std::string & url, enum download_type type, bool report_errors, bool cleanup);
68 bool download_pem_cert (const std::string & url, std::string & certs);
69 bool post (const string & url, vector<tuple<string, string>> & request_parameters);
70 void add_file (std::string filename);
71 void add_module (std::string module);
72 void get_header_field (const std::string & data, const std::string & field);
73 static size_t get_data_shim (void *ptr, size_t size, size_t nitems, void *client);
74 static size_t get_file (void *ptr, size_t size, size_t nitems, FILE * stream);
75 static size_t get_header_shim (void *ptr, size_t size, size_t nitems, void *client);
76 std::string get_rpmname (std::string & pathname);
77 void get_buildid (string fname);
78 void get_kernel_buildid (void);
79 long get_response_code (void);
80 bool add_server_cert_to_client (std::string & tmpdir, db_init_types db_init_type);
81 static int trace (CURL *, curl_infotype type, unsigned char *data, size_t size, void *);
82 bool delete_op (const std::string & url);
83 bool check_trust (enum cert_type, vector<compile_server_info> &specified_servers);
84
85 private:
86 size_t get_header (void *ptr, size_t size, size_t nitems);
87 size_t get_data (void *ptr, size_t size, size_t nitems);
88 static int process_buildid_shim (Dwfl_Module *dwflmod, void **userdata, const char *name,
89 Dwarf_Addr base, void *client);
90 int process_buildid (Dwfl_Module *dwflmod);
91 std::vector<std::string> files;
92 std::vector<std::string> modules;
93 std::vector<std::tuple<std::string, std::string>> buildids;
94 systemtap_session &s;
95 CURL *curl;
96 int retry;
97 std::string *location;
98 std::string buildid;
99 };
100
101 // TODO is there a better way than making this static?
102 static http_client *http;
103
104
105 size_t
106 http_client::get_data_shim (void *ptr, size_t size, size_t nitems, void *client)
107 {
108 http_client *http = static_cast<http_client *>(client);
109
110 return http->get_data (ptr, size, nitems);
111 }
112
113 // Parse the json data at PTR having SIZE and NITEMS into root
114
115 size_t
116 http_client::get_data (void *ptr, size_t size, size_t nitems)
117 {
118 string data ((const char *) ptr, (size_t) size * nitems);
119
120 // Process the JSON data.
121 if (data.front () == '{')
122 {
123 enum json_tokener_error json_error;
124 root = json_tokener_parse_verbose (data.c_str(), &json_error);
125
126 if (root == NULL)
127 throw SEMANTIC_ERROR (json_tokener_error_desc (json_error));
128 }
129 else
130 {
131 clog << "Malformed JSON data: '" << data << "'" << endl;
132 }
133 return size * nitems;
134 }
135
136
137 size_t
138 http_client::get_header_shim (void *ptr, size_t size, size_t nitems, void *client)
139 {
140 http_client *http = static_cast<http_client *>(client);
141
142 return http->get_header (ptr, size, nitems);
143 }
144
145
146 // Extract header values at PTR having SIZE and NITEMS into header_values
147
148 size_t
149 http_client::get_header (void *ptr, size_t size, size_t nitems)
150 {
151 string data ((const char *) ptr, (size_t) size * nitems);
152
153 unsigned long colon = data.find(':');
154 if (colon != string::npos)
155 {
156 string key = data.substr (0, colon);
157 string value = data.substr (colon + 2, data.length() - colon - 4);
158 header_values[key] = value;
159 }
160
161 return size * nitems;
162 }
163
164
165 // Put the data, e.g. <module>.ko at PTR having SIZE and NITEMS into STREAM
166
167 size_t
168 http_client::get_file (void *ptr, size_t size, size_t nitems, std::FILE * stream)
169 {
170 size_t written;
171 written = fwrite (ptr, size, nitems, stream);
172 std::fflush (stream);
173 return written;
174 }
175
176
177 // Trace sent and received packets
178
179 int
180 http_client::trace(CURL *, curl_infotype type, unsigned char *data, size_t size, void *)
181 {
182 string text;
183
184 switch(type)
185 {
186 case CURLINFO_TEXT:
187 clog << "== Info: " << data;
188 return 0;
189
190 case CURLINFO_HEADER_OUT:
191 text = "=> Send header";
192 break;
193 case CURLINFO_DATA_OUT:
194 text = "=> Send data";
195 break;
196 case CURLINFO_HEADER_IN:
197 text = "<= Recv header";
198 break;
199 case CURLINFO_DATA_IN:
200 text = "<= Recv data";
201 break;
202 default:
203 return 0;
204 }
205
206 size_t i;
207 size_t c;
208
209 const unsigned int width = 64;
210 // Packet contents exceeding this size are probably downloaded file components
211 const unsigned int max_size = 0x2000;
212
213 clog << text << " " << size << " bytes (" << showbase << hex << size << ")" << dec << noshowbase << endl;
214
215 if (size > max_size)
216 return 0;
217
218 for (i = 0; i < size; i += width)
219 {
220 clog << setw(4) << setfill('0') << hex << i << dec << setfill(' ') << ": ";
221
222 for (c = 0; (c < width) && (i + c < size); c++)
223 {
224 if ((i + c + 1 < size) && data[i + c] == '\r' && data[i + c + 1] == '\n')
225 {
226 i += (c + 2 - width);
227 break;
228 }
229
230 clog << (char)(isprint (data[i + c]) ? data[i + c] : '.');
231 if ((i + c + 2 < size) && data[i + c + 1] == '\r' && data[i + c + 2] == '\n')
232 {
233 i += (c + 3 - width);
234 break;
235 }
236 }
237 clog << endl;
238 }
239
240 return 0;
241 }
242
243
244 // Read the certificate bundle corresponding to 'url into 'certs'
245
246 bool
247 http_client::download_pem_cert (const std::string & url, string & certs)
248 {
249 CURL *dpc_curl;
250 CURLcode res;
251 bool have_certs = false;
252 struct curl_certinfo *certinfo;
253 struct curl_slist *headers = NULL;
254
255 // Get the certificate info for the url
256 dpc_curl = curl_easy_init ();
257
258 curl_easy_setopt(dpc_curl, CURLOPT_URL, url.c_str());
259 curl_easy_setopt(dpc_curl, CURLOPT_SSL_VERIFYPEER, 0L);
260 curl_easy_setopt(dpc_curl, CURLOPT_SSL_VERIFYHOST, 0L);
261 curl_easy_setopt(dpc_curl, CURLOPT_VERBOSE, 0L);
262
263 curl_easy_setopt (dpc_curl, CURLOPT_ACCEPT_ENCODING, "deflate");
264 headers = curl_slist_append (headers, "Accept: */*");
265 headers = curl_slist_append (headers, "Content-Type: text/html");
266 curl_easy_setopt (dpc_curl, CURLOPT_HTTPHEADER, headers);
267 curl_easy_setopt (dpc_curl, CURLOPT_HTTPGET, 1);
268 // older versions of curl don't support CURLINFO_CERTINFO reliably
269 // so use server return cert info as a backup
270 curl_easy_setopt (dpc_curl, CURLOPT_WRITEDATA, http);
271 curl_easy_setopt (dpc_curl, CURLOPT_WRITEFUNCTION,
272 http_client::get_data_shim);
273
274 if (s.verbose > 2)
275 {
276 curl_easy_setopt (dpc_curl, CURLOPT_VERBOSE, 1L);
277 curl_easy_setopt (dpc_curl, CURLOPT_DEBUGFUNCTION, trace);
278 }
279 curl_easy_setopt(dpc_curl, CURLOPT_CERTINFO, 1L);
280
281 res = curl_easy_perform (dpc_curl);
282
283 if (res)
284 return false;
285
286 res = curl_easy_getinfo (dpc_curl, CURLINFO_CERTINFO, &certinfo);
287
288 // Create a certificate bundle from the certificate info
289 if (!res && certinfo->num_of_certs > 0)
290 {
291 for (int i = 0; i < certinfo->num_of_certs; i++)
292 {
293 struct curl_slist *slist;
294
295 for (slist = certinfo->certinfo[i]; slist; slist = slist->next)
296 {
297 string one_cert;
298 string slist_data = string (slist->data);
299 size_t cert_begin, cert_end;
300 if ((cert_begin = slist_data.find("-----BEGIN CERTIFICATE-----")) == string::npos)
301 continue;
302 if ((cert_end = slist_data.find("-----END CERTIFICATE-----", cert_begin)) == string::npos)
303 continue;
304 certs += string (slist_data.substr(cert_begin, cert_end - cert_begin + 28));
305 have_certs = true;
306 }
307 }
308 }
309 else if (!res && certinfo->num_of_certs == 0)
310 {
311 json_object *cert_obj;
312 // Get the certificate returned by the server
313 if (json_object_object_get_ex (root, "certificate", &cert_obj))
314 {
315 certs = string (json_object_get_string(cert_obj));
316 have_certs = true;
317 }
318 }
319
320
321 if (s.verbose >= 2)
322 clog << "Server returned certificate chain with: " << certinfo->num_of_certs << " members" << endl;
323
324 curl_easy_cleanup(dpc_curl);
325 curl_global_cleanup();
326
327 return have_certs;
328 }
329
330
331 bool
332 http_client::download (const std::string & url, http_client::download_type type, bool report_errors, bool cleanup)
333 {
334 struct curl_slist *headers = NULL;
335
336 if (curl)
337 curl_easy_reset (curl);
338 else
339 curl = curl_easy_init ();
340
341 if (s.verbose > 2)
342 {
343 curl_easy_setopt (curl, CURLOPT_VERBOSE, 1L);
344 curl_easy_setopt (curl, CURLOPT_DEBUGFUNCTION, trace);
345 }
346 curl_easy_setopt (curl, CURLOPT_URL, url.c_str ());
347 curl_easy_setopt (curl, CURLOPT_NOSIGNAL, 1); //Prevent "longjmp causes uninitialized stack frame" bug
348 curl_easy_setopt (curl, CURLOPT_ACCEPT_ENCODING, "deflate");
349 headers = curl_slist_append (headers, "Accept: */*");
350 headers = curl_slist_append (headers, "Content-Type: text/html");
351 curl_easy_setopt (curl, CURLOPT_HTTPHEADER, headers);
352 curl_easy_setopt (curl, CURLOPT_HTTPGET, 1);
353 curl_easy_setopt (curl, CURLOPT_SSLCERTTYPE, "PEM");
354 curl_easy_setopt (curl, CURLOPT_SSL_VERIFYPEER, 1L);
355 // Enabling verifyhost causes a mismatch between "hostname" in the
356 // server cert db and "hostname.domain" given as the client url
357 curl_easy_setopt (curl, CURLOPT_SSL_VERIFYHOST, 0L);
358 curl_easy_setopt (curl, CURLOPT_CAINFO, pem_cert_file.c_str());
359
360 if (type == json_type)
361 {
362 curl_easy_setopt (curl, CURLOPT_WRITEDATA, http);
363 curl_easy_setopt (curl, CURLOPT_WRITEFUNCTION,
364 http_client::get_data_shim);
365 }
366 else if (type == file_type)
367 {
368 std::string filename = url;
369 std::string filepath;
370
371 if (filename.back() == '/')
372 filename.erase(filename.length()-1);
373 filepath = s.tmpdir + "/" + filename.substr (filename.rfind ('/')+1);
374
375 if (s.verbose >= 3)
376 clog << "Downloaded " + filepath << endl;
377 std::FILE *File = std::fopen (filepath.c_str(), "wb");
378 curl_easy_setopt (curl, CURLOPT_WRITEDATA, File);
379 curl_easy_setopt (curl, CURLOPT_WRITEFUNCTION, http_client::get_file);
380 }
381 curl_easy_setopt (curl, CURLOPT_HEADERDATA, http);
382 curl_easy_setopt (curl, CURLOPT_HEADERFUNCTION, http_client::get_header_shim);
383
384 CURLcode res = curl_easy_perform (curl);
385
386 if (cleanup)
387 {
388 curl_easy_cleanup(curl);
389 curl_global_cleanup();
390 curl = 0;
391 }
392
393 if (res != CURLE_OK && res != CURLE_GOT_NOTHING)
394 {
395 if (report_errors)
396 clog << curl_easy_strerror (res) << ' ' << url << endl;
397 return false;
398 }
399 else
400 return true;
401 }
402
403
404 // Get the rpm corresponding to SEARCH_FILE
405
406 std::string
407 http_client::get_rpmname (std::string &search_file)
408 {
409 rpmts ts = NULL;
410 Header hdr;
411 rpmdbMatchIterator mi;
412 rpmtd td;
413
414 td = rpmtdNew ();
415 ts = rpmtsCreate ();
416
417 rpmReadConfigFiles (NULL, NULL);
418
419 int metrics[] =
420 { RPMTAG_NAME, RPMTAG_EVR, RPMTAG_ARCH, RPMTAG_FILENAMES, };
421
422 struct
423 {
424 string name;
425 string evr;
426 string arch;
427 } rpmhdr;
428
429 bool found = false;
430 mi = rpmtsInitIterator (ts, RPMDBI_PACKAGES, NULL, 0);
431 while (NULL != (hdr = rpmdbNextIterator (mi)))
432 {
433 hdr = headerLink(hdr);
434 for (unsigned int i = 0; i < (sizeof (metrics) / sizeof (int)); i++)
435 {
436 headerGet (hdr, metrics[i], td, HEADERGET_EXT);
437 switch (td->type)
438 {
439 case RPM_STRING_TYPE:
440 {
441 const char *rpmval = rpmtdGetString (td);
442 switch (metrics[i])
443 {
444 case RPMTAG_NAME:
445 rpmhdr.name = rpmval;
446 break;
447 case RPMTAG_EVR:
448 rpmhdr.evr = rpmval;
449 break;
450 case RPMTAG_ARCH:
451 rpmhdr.arch = rpmval;
452 break;
453 }
454 break;
455 }
456 case RPM_STRING_ARRAY_TYPE:
457 while (rpmtdNext(td) >= 0)
458 {
459 const char *rpmval = rpmtdGetString (td);
460 if (strcmp (rpmval, search_file.c_str()) == 0)
461 {
462 found = true;
463 break;
464 }
465 }
466 break;
467 }
468 rpmtdFreeData (td);
469 rpmtdReset (td);
470 }
471 headerFree (hdr);
472 if (found)
473 break;
474 }
475
476 rpmFreeCrypto ();
477 rpmdbFreeIterator (mi);
478 rpmtsFree (ts);
479 rpmtdFree (td);
480
481 if (found)
482 {
483 return rpmhdr.name + "-" + rpmhdr.evr + "." + rpmhdr.arch;
484 }
485
486 // There wasn't an rpm that contains SEARCH_FILE. Return the empty
487 // string.
488 return "";
489 }
490
491
492 // Put the buildid for DWFLMOD into buildids
493
494 int
495 http_client::process_buildid (Dwfl_Module *dwflmod)
496 {
497 const char *fname;
498 dwfl_module_info (dwflmod, NULL, NULL, NULL, NULL, NULL, &fname, NULL);
499
500 GElf_Addr bias;
501 int build_id_len = 0;
502 unsigned char *build_id_bits;
503 GElf_Addr build_id_vaddr;
504 string build_id;
505 char *result = NULL;
506 int code;
507
508 dwfl_module_getelf (dwflmod, &bias);
509 build_id_len = dwfl_module_build_id (dwflmod,
510 (const unsigned char **)&build_id_bits,
511 &build_id_vaddr);
512
513 for (int i = 0; i < build_id_len; i++)
514 {
515 if (result)
516 code = asprintf (&result, "%s%02x", result, *(build_id_bits+i));
517 else
518 code = asprintf (&result, "%02x", *(build_id_bits+i));
519 if (code < 0)
520 return 1;
521 }
522
523 http->buildids.push_back(make_tuple(fname, result));
524
525 return DWARF_CB_OK;
526 }
527
528
529 int
530 http_client::process_buildid_shim (Dwfl_Module *dwflmod,
531 void **userdata __attribute__ ((unused)),
532 const char *name __attribute__ ((unused)),
533 Dwarf_Addr base __attribute__ ((unused)),
534 void *client)
535 {
536 http_client *http = static_cast<http_client *>(client);
537
538 return http->process_buildid (dwflmod);
539 }
540
541
542 // Do the setup for getting the buildid for FNAME
543
544 void
545 http_client::get_buildid (string fname)
546 {
547 int fd;
548
549 if ((fd = open (fname.c_str(), O_RDONLY)) < 0)
550 {
551 clog << "can't open " << fname;
552 return;
553 }
554
555 static const Dwfl_Callbacks callbacks =
556 {
557 dwfl_build_id_find_elf,
558 dwfl_standard_find_debuginfo,
559 dwfl_offline_section_address,
560 NULL
561 };
562 Dwfl *dwfl = dwfl_begin (&callbacks);
563
564 if (dwfl == NULL)
565 return;
566
567 if (dwfl_report_offline (dwfl, fname.c_str(), fname.c_str(), fd) == NULL)
568 return;
569 else
570 {
571 dwfl_report_end (dwfl, NULL, NULL);
572 dwfl_getmodules (dwfl, process_buildid_shim, http, 0);
573 }
574 dwfl_end (dwfl);
575 close (fd);
576 }
577
578
579 void
580 http_client::get_kernel_buildid (void)
581 {
582 const char *notesfile = "/sys/kernel/notes";
583 int fd = open (notesfile, O_RDONLY);
584 if (fd < 0)
585 return;
586
587 union
588 {
589 GElf_Nhdr nhdr;
590 unsigned char data[8192];
591 } buf;
592
593 ssize_t n = read (fd, buf.data, sizeof buf);
594 close (fd);
595
596 if (n <= 0)
597 return;
598
599 unsigned char *p = buf.data;
600 while (p < &buf.data[n])
601 {
602 /* No translation required since we are reading the native kernel. */
603 GElf_Nhdr *nhdr = (GElf_Nhdr *) p;
604 p += sizeof *nhdr;
605 unsigned char *name = p;
606 p += (nhdr->n_namesz + 3) & -4U;
607 unsigned char *bits = p;
608 p += (nhdr->n_descsz + 3) & -4U;
609
610 if (p <= &buf.data[n]
611 && nhdr->n_type == NT_GNU_BUILD_ID
612 && nhdr->n_namesz == sizeof "GNU"
613 && !memcmp (name, "GNU", sizeof "GNU"))
614 {
615 char *result = NULL;
616 int code;
617
618 for (unsigned int i = 0; i < nhdr->n_descsz; i++)
619 {
620 if (result)
621 code = asprintf (&result, "%s%02x", result, *(bits+i));
622 else
623 code = asprintf (&result, "%02x", *(bits+i));
624 if (code < 0)
625 return;
626 }
627 http->buildids.push_back(make_tuple("kernel", result));
628 break;
629 }
630 }
631 }
632
633
634 long
635 http_client::get_response_code (void)
636 {
637 long response_code = 0;
638 curl_easy_getinfo (curl, CURLINFO_RESPONSE_CODE, &response_code);
639 return response_code;
640 }
641
642
643 // Post REQUEST_PARAMETERS, files, modules, buildids to URL
644
645 bool
646 http_client::post (const string & url,
647 vector<tuple<string, string>> & request_parameters)
648 {
649 struct curl_slist *headers = NULL;
650 int still_running = false;
651 struct curl_httppost *formpost = NULL;
652 struct curl_httppost *lastptr = NULL;
653 struct json_object *jobj = json_object_new_object();
654
655 // Add parameter info
656 // "cmd_args": ["script\/\/path\/linetimes.stp","-v","-v",
657 // "-c\/path\/bench.x","--","process(\"\/path\/bench.x\")","main"]
658
659 string previous_parm_type;
660 string previous_json_data;
661 auto it = request_parameters.begin ();
662 while (it != request_parameters.end ())
663 {
664 string parm_type = get<0>(*it);
665 string parm_data = get<1>(*it);
666 struct json_object *json_data = json_object_new_string(parm_data.c_str());
667 if (parm_type == previous_parm_type)
668 {
669 // convert original singleton to an array
670 struct json_object *jarr = json_object_new_array();
671 json_data = json_object_new_string(previous_json_data.c_str());
672 json_object_array_add(jarr, json_data);
673 while (parm_type == previous_parm_type)
674 {
675 json_data = json_object_new_string(parm_data.c_str());
676 json_object_array_add(jarr, json_data);
677 previous_parm_type = parm_type;
678 previous_json_data = parm_data;
679 it++;
680 parm_type = get<0>(*it);
681 parm_data = get<1>(*it);
682 }
683 json_object_object_add(jobj, previous_parm_type.c_str(), jarr);
684 continue;
685 }
686 else
687 json_object_object_add(jobj, parm_type.c_str(), json_data);
688 previous_parm_type = parm_type;
689 previous_json_data = parm_data;
690 it++;
691 }
692
693 // Fill in the file upload field; libcurl will load data from the
694 // given file name.
695 for (auto it = files.begin (); it != files.end (); ++it)
696 {
697 string filename = (*it);
698 string filebase = basename (filename.c_str());
699
700 curl_formadd (&formpost, &lastptr,
701 CURLFORM_COPYNAME, filebase.c_str(),
702 CURLFORM_FILE, filename.c_str(),
703 CURLFORM_END);
704 curl_formadd (&formpost, &lastptr,
705 CURLFORM_COPYNAME, "files",
706 CURLFORM_COPYCONTENTS, filename.c_str(),
707 CURLFORM_END);
708 }
709
710 // Add package info
711 // "file_info": [ { "file_pkg": "kernel-4.14.0-0.rc4.git4.1.fc28.x86_64",
712 // "file_name": "kernel",
713 // "build_id": "ef7210ee3a447c798c3548102b82665f03ef241f" },
714 // { "file_pkg": "foo-1.1.x86_64",
715 // "file_name": "/usr/bin/foo",
716 // "build_id": "deadbeef" }
717 // ]
718
719 int bid_idx = 0;
720
721 struct json_object *jarr = json_object_new_array();
722 for (auto it = modules.begin (); it != modules.end (); ++it, ++bid_idx)
723 {
724 struct json_object *jfobj = json_object_new_object();
725 string pkg = (*it);
726 string name = std::get<0>(buildids[bid_idx]);
727 string build_id = std::get<1>(buildids[bid_idx]);
728
729 json_object_object_add (jfobj, "file_name", json_object_new_string (name.c_str()));
730 json_object_object_add (jfobj, "file_pkg", json_object_new_string (pkg.c_str()));
731 json_object_object_add (jfobj, "build_id", json_object_new_string (build_id.c_str()));
732 json_object_array_add (jarr, jfobj);
733 }
734 json_object_object_add(jobj, "file_info", jarr);
735
736
737 // Add environment variables info
738 // "env_vars": {"LANG":"en_US.UTF-8","LC_MESSAGES":"en_US.UTF-8"}
739
740 if (! http->env_vars.empty())
741 {
742 struct json_object *jlvobj = json_object_new_object();
743 for (auto i = http->env_vars.begin();
744 i != http->env_vars.end();
745 ++i)
746 {
747 string name = get<0>(*i);
748 string value = get<1>(*i);
749 json_object_object_add (jlvobj, name.c_str(), json_object_new_string(value.c_str()));
750 }
751 if (http->env_vars.size())
752 json_object_object_add (jobj, "env_vars", jlvobj);
753 }
754
755 curl_formadd (&formpost, &lastptr,
756 CURLFORM_COPYNAME, "command_environment",
757 CURLFORM_CONTENTTYPE, "application/json",
758 CURLFORM_COPYCONTENTS,
759 json_object_to_json_string_ext (jobj, JSON_C_TO_STRING_PLAIN),
760 CURLFORM_END);
761 json_object_put(jobj);
762
763 headers = curl_slist_append (headers, "Expect:");
764
765 curl_easy_setopt (curl, CURLOPT_URL, url.c_str());
766 curl_easy_setopt (curl, CURLOPT_HTTPHEADER, headers);
767 curl_easy_setopt (curl, CURLOPT_HTTPPOST, formpost);
768
769 CURLM *multi_handle = curl_multi_init();
770 curl_multi_add_handle (multi_handle, curl);
771 curl_multi_perform (multi_handle, &still_running);
772 do {
773 struct timeval timeout;
774 int rc; // select() return code
775 CURLMcode mc; // curl_multi_fdset() return code
776
777 fd_set fdread;
778 fd_set fdwrite;
779 fd_set fdexcep;
780 int maxfd = -1;
781
782 long curl_timeo = -1;
783
784 FD_ZERO (&fdread);
785 FD_ZERO (&fdwrite);
786 FD_ZERO (&fdexcep);
787
788 // set a suitable timeout to play around with
789 timeout.tv_sec = 1;
790 timeout.tv_usec = 0;
791
792 curl_multi_timeout (multi_handle, &curl_timeo);
793 if (curl_timeo >= 0)
794 {
795 timeout.tv_sec = curl_timeo / 1000;
796 if (timeout.tv_sec > 1)
797 timeout.tv_sec = 1;
798 else
799 timeout.tv_usec = (curl_timeo % 1000) * 1000;
800 }
801
802 // get file descriptors from the transfers
803 mc = curl_multi_fdset (multi_handle, &fdread, &fdwrite, &fdexcep, &maxfd);
804
805 if (mc != CURLM_OK)
806 {
807 clog << "curl_multi_fdset() failed" << curl_multi_strerror (mc) << endl;
808 return false;
809 }
810
811 /* On success the value of maxfd is guaranteed to be >= -1. We call
812 select(maxfd + 1, ...); specially in case of (maxfd == -1) there are
813 no fds ready yet so we call select(0, ...)to sleep 100ms,
814 the minimum suggested value */
815
816 if (maxfd == -1)
817 {
818 struct timeval wait = { 0, 100 * 1000 }; // 100ms
819 rc = select (0, NULL, NULL, NULL, &wait);
820 }
821 else
822 rc = select (maxfd+1, &fdread, &fdwrite, &fdexcep, &timeout);
823
824 switch (rc)
825 {
826 case -1:
827 /* select error */
828 break;
829 case 0:
830 default:
831 curl_multi_perform (multi_handle, &still_running);
832 break;
833 }
834 } while (still_running);
835
836 curl_multi_cleanup (multi_handle);
837 curl_formfree (formpost);
838 curl_slist_free_all (headers);
839
840 return true;
841 }
842
843
844 // Add FILE to files
845 void
846 http_client::add_file (std::string filename)
847 {
848 files.push_back (filename);
849 }
850
851
852 // Add MODULE to modules
853
854 void
855 http_client::add_module (std::string module)
856 {
857 modules.push_back (module);
858 }
859
860
861 // Add the server certificate to the client certificate database
862
863 bool
864 http_client::add_server_cert_to_client (string &tmpdir, db_init_types db_init_type)
865 {
866 const char *certificate;
867 json_object *cert_obj;
868
869 // Get the certificate returned by the server
870 if (json_object_object_get_ex (root, "certificate", &cert_obj))
871 certificate = json_object_get_string(cert_obj);
872 else
873 return false;
874 string pem_tmp = tmpdir + "pemXXXXXX";
875 int fd = mkstemp ((char*)pem_tmp.c_str());
876 close(fd);
877 std::ofstream pem_out(pem_tmp);
878 pem_out << certificate;
879 pem_out.close();
880
881 // Add the certificate to the client nss certificate database
882 if (add_client_cert(pem_tmp, local_client_cert_db_path(), db_init_type) == SECSuccess)
883 {
884 remove_file_or_dir (pem_tmp.c_str());
885 return true;
886 }
887 return false;
888 }
889
890 // Ask the server to delete a URL.
891
892 bool
893 http_client::delete_op (const std::string & url)
894 {
895 curl = curl_easy_init ();
896 if (s.verbose > 2)
897 {
898 curl_easy_setopt (curl, CURLOPT_VERBOSE, 1L);
899 curl_easy_setopt (curl, CURLOPT_DEBUGFUNCTION, trace);
900 }
901 curl_easy_setopt (curl, CURLOPT_URL, url.c_str ());
902 curl_easy_setopt (curl, CURLOPT_NOSIGNAL, 1); //Prevent "longjmp causes uninitialized stack frame" bug
903 curl_easy_setopt(curl, CURLOPT_CUSTOMREQUEST, "DELETE");
904
905 CURLcode res = curl_easy_perform (curl);
906 if (res != CURLE_OK)
907 {
908 clog << "curl_easy_perform() failed: " << curl_easy_strerror (res)
909 << endl;
910 return false;
911 }
912 return true;
913 }
914
915
916 // Can a certificate having trust 'this_cert_trust' be handled by the corresponding host
917
918 bool
919 http_client::check_trust (enum cert_type this_cert_type, vector<compile_server_info> &specified_servers)
920 {
921 vector<compile_server_info> server_list;
922
923 string cert_db_path;
924 string cert_string;
925
926
927 if (this_cert_type == signer_trust)
928 {
929 cert_db_path = signing_cert_db_path ();
930 cert_string = "signing";
931 }
932 else if (this_cert_type == ssl_trust)
933 {
934 cert_db_path = local_client_cert_db_path ();
935 cert_string = "trusted";
936 }
937
938 get_server_info_from_db (s, server_list, cert_db_path);
939 vector<compile_server_info>::iterator i = specified_servers.begin ();
940 while (i != specified_servers.end ())
941 {
942 bool have_match = false;
943 for (vector<compile_server_info>::const_iterator j = server_list.begin ();
944 j != server_list.end ();
945 ++j)
946 {
947 if (j->host_name == i->host_name
948 || j->host_name == i->unresolved_host_name.substr(0,j->host_name.length()))
949 {
950 have_match = true;
951 break;
952 }
953 }
954 if (!have_match)
955 i = specified_servers.erase (i);
956 else
957 ++i;
958 }
959 if (specified_servers.size() == 0)
960 {
961 clog << "No matching " << cert_string << " server";
962 if (server_list.size() > 0)
963 clog << "; the " << cert_string << " servers are\n" << server_list;
964 clog << endl;
965 return false;
966 }
967 else
968 return true;
969 }
970
971
972 http_client_backend::http_client_backend (systemtap_session &s)
973 : client_backend(s), files_seen(false)
974 {
975 server_tmpdir = s.tmpdir;
976 }
977
978 int
979 http_client_backend::initialize ()
980 {
981 if (!http)
982 http = new http_client (s);
983 request_parameters.clear();
984
985 return 0;
986 }
987
988 // Symbolically link the given file or directory into the client's temp
989 // directory under the given subdirectory.
990 //
991 // We need to do this even for the http client/server so that we can
992 // fully handle systemtap's complexity. A tricky example of this
993 // complexity would be something like "stap -I tapset_dir script.stp",
994 // where "tapset_dir" is empty. You can transfer files with a POST,
995 // but you can't really indicate an empty directory.
996 //
997 // So, we'll handle this like the NSS client does - build up a
998 // directory of all the files we need to transfer over to the server
999 // and zip it up and send the one zip file.
1000 int
1001 http_client_backend::include_file_or_directory (const string &subdir,
1002 const string &path,
1003 const bool add_arg)
1004 {
1005 // Must predeclare these because we do use 'goto done' to
1006 // exit from error situations.
1007 vector<string> components;
1008 string name;
1009 int rc = 0;
1010
1011 // Canonicalize the given path and remove the leading /.
1012 string rpath;
1013 char *cpath = canonicalize_file_name (path.c_str ());
1014 if (! cpath)
1015 {
1016 // It can not be canonicalized. Use the name relative to
1017 // the current working directory and let the server deal with it.
1018 char cwd[PATH_MAX];
1019 if (getcwd (cwd, sizeof (cwd)) == NULL)
1020 {
1021 rpath = path;
1022 rc = 1;
1023 goto done;
1024 }
1025 rpath = string (cwd) + "/" + path;
1026 }
1027 else
1028 {
1029 // It can be canonicalized. Use the canonicalized name and add this
1030 // file or directory to the request package.
1031 rpath = cpath;
1032 free (cpath);
1033
1034 // Including / would require special handling in the code below and
1035 // is a bad idea anyway. Let's not allow it.
1036 if (rpath == "/")
1037 {
1038 if (rpath != path)
1039 clog << _F("%s resolves to %s\n", path.c_str (), rpath.c_str ());
1040 clog << _F("Unable to send %s to the server\n", path.c_str ());
1041 return 1;
1042 }
1043
1044 // First create the requested subdirectory (if there is one).
1045 if (! subdir.empty())
1046 {
1047 name = client_tmpdir + "/" + subdir;
1048 rc = create_dir (name.c_str ());
1049 if (rc) goto done;
1050 }
1051 else
1052 {
1053 name = client_tmpdir;
1054 }
1055
1056 // Now create each component of the path within the sub directory.
1057 assert (rpath[0] == '/');
1058 tokenize (rpath.substr (1), components, "/");
1059 assert (components.size () >= 1);
1060 unsigned i;
1061 for (i = 0; i < components.size() - 1; ++i)
1062 {
1063 if (components[i].empty ())
1064 continue; // embedded '//'
1065 name += "/" + components[i];
1066 rc = create_dir (name.c_str ());
1067 if (rc) goto done;
1068 }
1069
1070 // Now make a symbolic link to the actual file or directory.
1071 assert (i == components.size () - 1);
1072 name += "/" + components[i];
1073 rc = symlink (rpath.c_str (), name.c_str ());
1074 if (rc) goto done;
1075 }
1076
1077 // If the caller asks us, add this file or directory to the arguments.
1078 if (add_arg)
1079 rc = add_cmd_arg (subdir + "/" + rpath.substr (1));
1080
1081 done:
1082 if (rc != 0)
1083 {
1084 const char* e = strerror (errno);
1085 clog << "ERROR: unable to add "
1086 << rpath
1087 << " to temp directory as "
1088 << name << ": " << e
1089 << endl;
1090 }
1091 else
1092 {
1093 files_seen = true;
1094 }
1095 return rc;
1096 }
1097
1098 int
1099 http_client_backend::package_request ()
1100 {
1101 int rc = 0;
1102
1103 http->add_module ("kernel-" + s.kernel_release);
1104 http->get_kernel_buildid ();
1105
1106 for (set<std::string>::const_iterator i = s.unwindsym_modules.begin();
1107 i != s.unwindsym_modules.end();
1108 ++i)
1109 {
1110 string module = (*i);
1111 if (module != "kernel")
1112 {
1113 string rpmname = http->get_rpmname (module);
1114 if (! rpmname.empty())
1115 {
1116 http->get_buildid (module);
1117 http->add_module (rpmname);
1118 }
1119 else if (module[0] == '/')
1120 {
1121 include_file_or_directory ("files", module, false);
1122 }
1123 }
1124 }
1125
1126 // Package up the temporary directory into a zip file, if needed.
1127 if (files_seen)
1128 {
1129 string client_zipfile = client_tmpdir + ".zip";
1130 string cmd = "cd " + cmdstr_quoted(client_tmpdir) + " && zip -qr "
1131 + cmdstr_quoted(client_zipfile) + " *";
1132 vector<string> sh_cmd { "sh", "-c", cmd };
1133 rc = stap_system (s.verbose, sh_cmd);
1134
1135 if (rc == 0)
1136 http->add_file(client_zipfile);
1137 }
1138 return rc;
1139 }
1140
1141
1142 int
1143 http_client_backend::find_and_connect_to_server ()
1144 {
1145 const string cert_db = local_client_cert_db_path ();
1146 const string nick = server_cert_nickname ();
1147
1148 if (s.verbose >= 2)
1149 clog << "connecting to server using cert db " + cert_db << endl;
1150
1151 vector<compile_server_info> specified_servers;
1152 nss_get_specified_server_info (s, specified_servers);
1153
1154 if (! pr_contains (s.privilege, pr_stapdev))
1155 if (! http->check_trust (http->signer_trust, specified_servers))
1156 return 1;
1157
1158 if (! http->check_trust (http->ssl_trust, specified_servers))
1159 return 1;
1160
1161 for (vector<compile_server_info>::iterator i = specified_servers.begin ();
1162 i != specified_servers.end ();
1163 ++i)
1164 {
1165 // Try to connect to the server. We'll try to grab the base
1166 // directory of the server just to see if we can make a
1167 // connection.
1168 __label__ TRY_NEXT_SERVER;
1169 string pem_cert;
1170 bool add_cert = false;
1171 string url = "https://" + i->host_specification();
1172 http->host = i->unresolved_host_name;
1173
1174 int download_tries;
1175 if (get_pem_cert(cert_db, nick, http->host, pem_cert) == true)
1176 download_tries = 2; // 1st try cert db 2nd download cert from server
1177 else
1178 download_tries = 1; // download cert from server
1179
1180 while (download_tries)
1181 {
1182 if (download_tries == 1)
1183 {
1184 pem_cert = "";
1185 if (http->download_pem_cert (url, pem_cert) == false)
1186 goto TRY_NEXT_SERVER;
1187 else
1188 add_cert = true;
1189 }
1190 string pem_tmp = client_tmpdir + "/pemXXXXXX";
1191 int fd = mkstemp ((char*)pem_tmp.c_str());
1192 close(fd);
1193 std::ofstream pem_out(pem_tmp);
1194 pem_out << pem_cert;
1195 pem_out.close();
1196 http->pem_cert_file = pem_tmp;
1197 // Similar to CURLOPT_VERIFYHOST: compare source alternate names to hostname
1198 if (have_san_match (url, pem_cert) == false)
1199 goto TRY_NEXT_SERVER;
1200
1201 // curl invocation for a download must be the same as the preceding post
1202 if (http->download (url + "/", http->json_type, true, false) == true)
1203 break;
1204 remove_file_or_dir (pem_tmp.c_str());
1205 download_tries -= 1;
1206 }
1207
1208 // FIXME: The server returns its version number. We might
1209 // need to check it for compatibility.
1210
1211 // Send our build request.
1212 if (http->post (url + "/builds", request_parameters))
1213 {
1214 s.winning_server = url;
1215 http->host = url;
1216 if (add_cert)
1217 http->add_server_cert_to_client (client_tmpdir, db_nssinitcontext);
1218 return 0;
1219 }
1220 TRY_NEXT_SERVER:;
1221 }
1222
1223 return 1;
1224 }
1225
1226 int
1227 http_client_backend::unpack_response ()
1228 {
1229 std::string build_uri;
1230 std::map<std::string, std::string>::iterator it_loc;
1231 it_loc = http->header_values.find("Location");
1232 if (it_loc == http->header_values.end())
1233 {
1234 clog << "Cannot get location from server" << endl;
1235 return 1;
1236 }
1237 build_uri = http->host + http->header_values["Location"];
1238
1239 if (s.verbose >= 2)
1240 clog << "Initial response code: " << http->get_response_code() << endl;
1241 while (true)
1242 {
1243 auto it = http->header_values.find("Retry-After");
1244 if (it == http->header_values.end())
1245 {
1246 clog << "No retry-after?" << endl;
1247 break;
1248 }
1249 int retry = std::stoi(http->header_values["Retry-After"], nullptr, 10);
1250 if (s.verbose >= 2)
1251 clog << "Waiting " << retry << " seconds" << endl;
1252 sleep (retry);
1253 if (http->download (http->host + http->header_values["Location"],
1254 http->json_type, true, false))
1255 {
1256 // We need to wait until we get a 303 (See Other)
1257 long response_code = http->get_response_code();
1258 if (s.verbose >= 2)
1259 clog << "Response code: " << response_code << endl;
1260 if (response_code == 200)
1261 continue;
1262 else if (response_code == 303)
1263 break;
1264 else
1265 {
1266 clog << "Received a unhandled response code "
1267 << response_code << endl;
1268 return 1;
1269 }
1270 }
1271 }
1272
1273 // If we're here, we got a '303' (See Other). Read the "other"
1274 // location, which should contain our results.
1275 if (! http->download (http->host + http->header_values["Location"],
1276 http->json_type, true, false))
1277 {
1278 clog << "Couldn't read result information" << endl;
1279 return 1;
1280 }
1281
1282 // Get the server version number.
1283 json_object *ver_obj;
1284 json_bool jfound = json_object_object_get_ex (http->root, "version",
1285 &ver_obj);
1286 if (jfound)
1287 {
1288 server_version = json_object_get_string(ver_obj);
1289 }
1290 else
1291 {
1292 clog << "Couldn't find 'version' in JSON results data" << endl;
1293 return 1;
1294 }
1295
1296 // Get the return code information.
1297 json_object *rc_obj;
1298 jfound = json_object_object_get_ex (http->root, "rc", &rc_obj);
1299 if (jfound)
1300 {
1301 int rc = json_object_get_int(rc_obj);
1302 write_to_file(s.tmpdir + "/rc", rc);
1303 }
1304 else
1305 {
1306 clog << "Couldn't find 'rc' in JSON results data" << endl;
1307 return 1;
1308 }
1309
1310 // Download each item in the optional 'files' array. This is
1311 // optional since not all stap invocations produce an output file
1312 // (like a module).
1313 json_object *files;
1314 json_object_object_get_ex (http->root, "files", &files);
1315 if (files)
1316 {
1317 for (size_t k = 0; k < (size_t)json_object_array_length (files); k++)
1318 {
1319 json_object *files_element = json_object_array_get_idx (files, k);
1320 json_object *loc;
1321 jfound = json_object_object_get_ex (files_element, "location", &loc);
1322 string location = json_object_get_string (loc);
1323 http->download (http->host + location, http->file_type, true, false);
1324 }
1325 }
1326
1327 // Output stdout and stderr.
1328 json_object *loc_obj;
1329 jfound = json_object_object_get_ex (http->root, "stderr_location", &loc_obj);
1330 if (jfound)
1331 {
1332 string loc_str = json_object_get_string (loc_obj);
1333 http->download (http->host + loc_str, http->file_type, true, false);
1334 }
1335 else
1336 {
1337 clog << "Couldn't find 'stderr' in JSON results data" << endl;
1338 return 1;
1339 }
1340
1341 jfound = json_object_object_get_ex (http->root, "stdout_location", &loc_obj);
1342 if (jfound)
1343 {
1344 string loc_str = json_object_get_string (loc_obj);
1345 http->download (http->host + loc_str, http->file_type, true, false);
1346 }
1347 else
1348 {
1349 clog << "Couldn't find 'stdout' in JSON results data" << endl;
1350 return 1;
1351 }
1352
1353 delete http;
1354 return 0;
1355 }
1356
1357
1358 int
1359 http_client_backend::add_protocol_version (const std::string &version)
1360 {
1361 // Add the protocol version (so the server can ensure we're
1362 // compatible).
1363 request_parameters.push_back(make_tuple("version", version));
1364 return 0;
1365 }
1366
1367
1368 int
1369 http_client_backend::add_sysinfo ()
1370 {
1371 request_parameters.push_back(make_tuple("kver", s.kernel_release));
1372 request_parameters.push_back(make_tuple("arch", s.architecture));
1373
1374 vector<string> distro_info;
1375
1376 get_distro_info (distro_info);
1377 if (! distro_info.empty())
1378 {
1379 std::replace(distro_info[0].begin(), distro_info[0].end(), '\n', ' ');
1380 std::replace(distro_info[1].begin(), distro_info[1].end(), '\n', ' ');
1381 request_parameters.push_back(make_tuple("distro_name", distro_info[0]));
1382 request_parameters.push_back(make_tuple("distro_version", distro_info[1]));
1383 }
1384 return 0;
1385 }
1386
1387 int
1388 http_client_backend::add_tmpdir_file (const std::string &)
1389 {
1390 files_seen = true;
1391 return 0;
1392 }
1393
1394 int
1395 http_client_backend::add_cmd_arg (const std::string &arg)
1396 {
1397 request_parameters.push_back(make_tuple("cmd_args", arg));
1398 return 0;
1399 }
1400
1401 void
1402 http_client_backend::add_localization_variable (const std::string &name,
1403 const std::string &value)
1404 {
1405 http->env_vars.push_back(make_tuple(name, value));
1406 return;
1407 }
1408
1409 void
1410 http_client_backend::add_mok_fingerprint (const std::string &)
1411 {
1412 // FIXME: We'll probably just add to the request_parameters here.
1413 return;
1414 }
1415
1416 void
1417 http_client_backend::fill_in_server_info (compile_server_info &info)
1418 {
1419 const string cert_db = local_client_cert_db_path ();
1420 const string nick = server_cert_nickname ();
1421 string host = info.unresolved_host_name;
1422 if (info.port == 0)
1423 // Sync with httpd/main.cxx
1424 info.port = 1234;
1425 string url = "https://" + host + ":" + std::to_string(info.port);
1426 string pem_cert;
1427
1428 if (host.empty())
1429 return;
1430
1431 if (s.verbose >= 2)
1432 {
1433 char hostname[NI_MAXHOST];
1434 memset (&hostname, '\0', NI_MAXHOST);
1435 PR_NetAddrToString(&info.address, hostname, NI_MAXHOST);
1436 clog << "getting server info for " + url + " " + hostname + " using cert db " + cert_db << endl;
1437 }
1438
1439 // Try to get the server certificate and the base
1440 // directory of the server just to see if we can make a
1441 // connection.
1442 int download_tries;
1443 // Obtain server info based on network address instead of host name?
1444 if (file_exists (cert_db) == true && get_pem_cert(cert_db, nick, http->host, pem_cert) == true)
1445 download_tries = 2; // 1st try cert db 2nd download cert from server
1446 else
1447 download_tries = 1; // download cert from server
1448
1449 while (download_tries)
1450 {
1451 if (download_tries == 1)
1452 {
1453 pem_cert = "";
1454 if (http->download_pem_cert (url, pem_cert) == false)
1455 return;
1456 }
1457 string pem_tmp = s.tmpdir + "/pemXXXXXX";
1458 int fd = mkstemp ((char*)pem_tmp.c_str());
1459 close(fd);
1460 std::ofstream pem_out(pem_tmp);
1461 pem_out << pem_cert;
1462 pem_out.close();
1463 http->pem_cert_file = pem_tmp;
1464 // Similar to CURLOPT_VERIFYHOST: compare source alternate names to hostname
1465 if (have_san_match (url, pem_cert) == false)
1466 return;
1467
1468 // curl invocation for a download must be the same as the preceding post
1469 if (http->download (url + "/", http->json_type, false, true) == true)
1470 break;
1471 remove_file_or_dir (pem_tmp.c_str());
1472 download_tries -= 1;
1473 }
1474
1475 json_object *ver_obj;
1476 json_bool jfound;
1477
1478 // Get the server version number.
1479 jfound = json_object_object_get_ex (http->root, "version", &ver_obj);
1480 if (jfound)
1481 info.version = json_object_get_string(ver_obj);
1482
1483 // Get the server arch.
1484 jfound = json_object_object_get_ex (http->root, "arch", &ver_obj);
1485 if (jfound)
1486 info.sysinfo = json_object_get_string(ver_obj);
1487
1488 // Get the server certificate info.
1489 jfound = json_object_object_get_ex (http->root, "cert_info", &ver_obj);
1490 if (jfound)
1491 info.certinfo = json_object_get_string(ver_obj);
1492
1493 // If the download worked, this server is obviously online.
1494 nss_add_online_server_info (s, info);
1495 }
1496
1497 int
1498 http_client_backend::trust_server_info (const compile_server_info &info)
1499 {
1500 const string cert_db = local_client_cert_db_path ();
1501 const string nick = server_cert_nickname ();
1502 string pem_cert;
1503 string host = info.unresolved_host_name;
1504 string url = "https://" + host + ":" + std::to_string(info.port);
1505
1506 if (s.verbose >= 2)
1507 clog << "getting server trust for " + url + " using cert db " + cert_db << endl;
1508
1509 if (http->download_pem_cert (url, pem_cert) == false)
1510 return 1;
1511
1512 string pem_tmp = s.tmpdir + "/pemXXXXXX";
1513 int fd = mkstemp ((char*)pem_tmp.c_str());
1514 close(fd);
1515 std::ofstream pem_out(pem_tmp);
1516 pem_out << pem_cert;
1517 pem_out.close();
1518 http->pem_cert_file = pem_tmp;
1519 // Similar to CURLOPT_VERIFYHOST: compare source alternate names to hostname
1520 if (have_san_match (url, pem_cert) == false)
1521 return 1;
1522
1523 if (http->download (url + "/", http->json_type, false, true))
1524 http->add_server_cert_to_client (s.tmpdir, db_no_nssinit);
1525
1526 return 0;
1527 }
1528
1529 #endif /* HAVE_HTTP_SUPPORT */
This page took 0.105915 seconds and 5 git commands to generate.