| OLD | NEW |
| (Empty) | |
| 1 // Copyright 2015 The Chromium Authors. All rights reserved. |
| 2 // Use of this source code is governed by a BSD-style license that can be |
| 3 // found in the LICENSE file. |
| 4 |
| 5 #include "net/tools/ct_mapper/write_results.h" |
| 6 |
| 7 #include <cstdio> |
| 8 #include <iostream> |
| 9 #include <map> |
| 10 #include <memory> |
| 11 #include <string> |
| 12 |
| 13 #include "base/base64.h" |
| 14 #include "base/command_line.h" |
| 15 #include "base/files/file_util.h" |
| 16 #include "base/format_macros.h" |
| 17 #include "base/json/json_writer.h" |
| 18 #include "base/logging.h" |
| 19 #include "base/memory/ptr_util.h" |
| 20 #include "base/strings/string_number_conversions.h" |
| 21 #include "base/strings/string_util.h" |
| 22 #include "base/strings/stringprintf.h" |
| 23 #include "base/values.h" |
| 24 #include "net/der/input.h" |
| 25 #include "net/tools/ct_mapper/entry.h" |
| 26 #include "net/tools/ct_mapper/metrics.h" |
| 27 |
| 28 namespace net { |
| 29 namespace { |
| 30 |
| 31 // Wraps the lines of text to fit in |width| characters (not including the |
| 32 // newline) |
| 33 std::string FormatFixedColumn(const std::string& text, size_t width) { |
| 34 std::string result; |
| 35 |
| 36 size_t pos = 0; |
| 37 while (pos < text.size()) { |
| 38 size_t remaining = text.size() - pos; |
| 39 size_t chunk_len = remaining < width ? remaining : width; |
| 40 result += text.substr(pos, chunk_len) + "\n"; |
| 41 pos += chunk_len; |
| 42 } |
| 43 |
| 44 return result; |
| 45 } |
| 46 |
| 47 void WriteStringToFile(const std::string& data, const base::FilePath& path) { |
| 48 CHECK_EQ(static_cast<int>(data.size()), |
| 49 base::WriteFile(path, data.data(), data.size())); |
| 50 } |
| 51 |
| 52 std::string ExecCommand(const std::string& cmd) { |
| 53 FILE* pipe = popen(cmd.c_str(), "r"); |
| 54 |
| 55 if (!pipe) |
| 56 return "ERROR"; |
| 57 |
| 58 std::string result = ""; |
| 59 char buffer[256]; |
| 60 while (!feof(pipe)) { |
| 61 if (fgets(buffer, sizeof(buffer), pipe) != NULL) |
| 62 result += buffer; |
| 63 } |
| 64 |
| 65 pclose(pipe); |
| 66 return result; |
| 67 } |
| 68 |
| 69 std::string ExecOpenSslCmd(const std::string& cmd, const der::Input& input) { |
| 70 base::FilePath tmp_input_path; |
| 71 CHECK(base::CreateTemporaryFile(&tmp_input_path)); |
| 72 WriteStringToFile(input.AsString(), tmp_input_path); |
| 73 |
| 74 std::string result = ExecCommand(base::StringPrintf( |
| 75 "%s -in \"%s\"", cmd.c_str(), tmp_input_path.value().c_str())); |
| 76 |
| 77 CHECK(base::DeleteFile(tmp_input_path, false)); |
| 78 |
| 79 return result; |
| 80 } |
| 81 |
| 82 // Writes |cert| in PEM format. Which is to say something like: |
| 83 // |
| 84 // -----BEGIN CERTIFICATE----- |
| 85 // gobbeldy gook in here here... |
| 86 // -----END CERTIFICATE----- |
| 87 std::string FormatCertificateAsPem(const net::der::Input& cert) { |
| 88 std::string result; |
| 89 result += "-----BEGIN CERTIFICATE-----\n"; |
| 90 const char* x = reinterpret_cast<const char*>(cert.UnsafeData()); |
| 91 std::string base64_encoded; |
| 92 base::Base64Encode(base::StringPiece(x, cert.Length()), &base64_encoded); |
| 93 result += FormatFixedColumn(base64_encoded, 75); |
| 94 result += "-----END CERTIFICATE-----\n"; |
| 95 return result; |
| 96 } |
| 97 |
| 98 std::string FormatSampleAsPem(const Sample& sample) { |
| 99 // Make a single vector with all the certificates, for simplicity. |
| 100 const std::vector<der::Input>& chain = sample.certs; |
| 101 |
| 102 std::string data; |
| 103 |
| 104 for (size_t i = 0; i < chain.size(); ++i) { |
| 105 const auto& cert = chain[i]; |
| 106 data += "\n\n"; |
| 107 data += |
| 108 base::StringPrintf("========= Cert%d =========\n", static_cast<int>(i)); |
| 109 data += ExecOpenSslCmd("openssl x509 -noout -text -inform DER", cert); |
| 110 } |
| 111 |
| 112 data += "\n"; |
| 113 |
| 114 data += "=====================================\n"; |
| 115 data += "Parsed data\n"; |
| 116 data += "=====================================\n"; |
| 117 |
| 118 for (size_t i = 0; i < chain.size(); ++i) { |
| 119 const auto& cert = chain[i]; |
| 120 data += "\n\n"; |
| 121 data += |
| 122 base::StringPrintf("========= Cert%d =========\n", static_cast<int>(i)); |
| 123 data += ExecOpenSslCmd("openssl asn1parse -i -inform DER", cert); |
| 124 } |
| 125 |
| 126 data += "\n"; |
| 127 data += "=====================================\n"; |
| 128 data += "PEM data\n"; |
| 129 data += "=====================================\n"; |
| 130 |
| 131 for (size_t i = 0; i < chain.size(); ++i) { |
| 132 const auto& cert = chain[i]; |
| 133 data += "\n\n"; |
| 134 data += |
| 135 base::StringPrintf("========= Cert%d =========\n", static_cast<int>(i)); |
| 136 data += FormatCertificateAsPem(cert); |
| 137 } |
| 138 |
| 139 return data; |
| 140 } |
| 141 |
| 142 base::FilePath GetSrcDir() { |
| 143 // Assume that the current program is running in a subdirectory of "src". Keep |
| 144 // descending until we find the src directory... |
| 145 |
| 146 auto src_path = base::MakeAbsoluteFilePath( |
| 147 base::CommandLine::ForCurrentProcess()->GetProgram()); |
| 148 |
| 149 while (src_path != src_path.DirName()) { |
| 150 if (src_path.BaseName().value() == "src") { |
| 151 return src_path; |
| 152 } |
| 153 src_path = src_path.DirName(); |
| 154 } |
| 155 |
| 156 return base::FilePath(); |
| 157 } |
| 158 |
| 159 void CopyStaticFile(const std::string& src_relative_path, |
| 160 const base::FilePath& dest_dir) { |
| 161 auto path = GetSrcDir().AppendASCII(src_relative_path); |
| 162 CHECK(base::CopyFile(path, dest_dir.Append(path.BaseName()))); |
| 163 } |
| 164 |
| 165 std::unique_ptr<base::ListValue> CreateListForSamples( |
| 166 const std::vector<Sample>& samples, |
| 167 size_t metrics_i, |
| 168 size_t bucket_i, |
| 169 const base::FilePath& dir_path) { |
| 170 std::unique_ptr<base::ListValue> samples_list = |
| 171 base::MakeUnique<base::ListValue>(); |
| 172 |
| 173 if (!samples.empty()) { |
| 174 std::string samples_dir_name = |
| 175 base::SizeTToString(metrics_i) + "-" + base::SizeTToString(bucket_i); |
| 176 |
| 177 // Create a directory to hold all the samples for this bucket |
| 178 base::FilePath samples_dir = dir_path.AppendASCII(samples_dir_name); |
| 179 CHECK(base::CreateDirectory(samples_dir)); |
| 180 |
| 181 // Write all the samples into the bucket's directory. |
| 182 for (size_t samples_i = 0; samples_i < samples.size(); ++samples_i) { |
| 183 const auto& sample = samples[samples_i]; |
| 184 std::string data = FormatSampleAsPem(sample) + "\n\n"; |
| 185 std::string samples_relpath = |
| 186 samples_dir_name + "/" + base::SizeTToString(samples_i); |
| 187 |
| 188 if (sample.certs.size() == 1) |
| 189 samples_relpath += ".cert"; |
| 190 else |
| 191 samples_relpath += ".chain"; |
| 192 |
| 193 samples_relpath += ".txt"; |
| 194 |
| 195 WriteStringToFile(data, dir_path.AppendASCII(samples_relpath)); |
| 196 samples_list->AppendString(samples_relpath); |
| 197 } |
| 198 } |
| 199 |
| 200 return samples_list; |
| 201 } |
| 202 |
| 203 std::unique_ptr<base::DictionaryValue> CreateDictForBucket( |
| 204 const BucketValue& bucket, |
| 205 const std::string& bucket_name, |
| 206 size_t metrics_i, |
| 207 size_t bucket_i, |
| 208 const base::FilePath& dir_path) { |
| 209 std::unique_ptr<base::DictionaryValue> bucket_dict = |
| 210 base::MakeUnique<base::DictionaryValue>(); |
| 211 |
| 212 bucket_dict->SetString("name", bucket_name); |
| 213 bucket_dict->SetDouble("total", bucket.total); |
| 214 |
| 215 bucket_dict->Set("samples", CreateListForSamples(bucket.samples, metrics_i, |
| 216 bucket_i, dir_path)); |
| 217 |
| 218 return bucket_dict; |
| 219 } |
| 220 |
| 221 std::unique_ptr<base::ListValue> CreateListForBuckets( |
| 222 const MetricsItem::BucketMap& bucket_map, |
| 223 size_t metrics_i, |
| 224 const base::FilePath& dir_path) { |
| 225 std::unique_ptr<base::ListValue> buckets_value = |
| 226 base::MakeUnique<base::ListValue>(); |
| 227 |
| 228 size_t bucket_i = 0; |
| 229 for (const auto& bucket_it : bucket_map) { |
| 230 const auto& bucket = bucket_it.second; |
| 231 buckets_value->Append(CreateDictForBucket(bucket, bucket_it.first, |
| 232 metrics_i, bucket_i, dir_path)); |
| 233 bucket_i++; |
| 234 } |
| 235 |
| 236 return buckets_value; |
| 237 } |
| 238 |
| 239 std::unique_ptr<base::DictionaryValue> CreateDictForMetricsItem( |
| 240 const std::string& name, |
| 241 const MetricsItem& metrics_item, |
| 242 size_t metrics_i, |
| 243 const base::FilePath& dir_path) { |
| 244 std::unique_ptr<base::DictionaryValue> metrics_dict = |
| 245 base::MakeUnique<base::DictionaryValue>(); |
| 246 |
| 247 metrics_dict->SetString("name", name); |
| 248 metrics_dict->SetDouble("total", metrics_item.total()); |
| 249 metrics_dict->Set("buckets", CreateListForBuckets(metrics_item.buckets(), |
| 250 metrics_i, dir_path)); |
| 251 return metrics_dict; |
| 252 } |
| 253 |
| 254 std::unique_ptr<base::ListValue> CreateListForMetrics( |
| 255 const Metrics& metrics, |
| 256 const base::FilePath& dir_path) { |
| 257 std::unique_ptr<base::ListValue> metrics_array_value = |
| 258 base::MakeUnique<base::ListValue>(); |
| 259 |
| 260 size_t metrics_i = 0; |
| 261 for (const auto& metrics_item_it : metrics.items()) { |
| 262 const std::string& metrics_item_name = metrics_item_it.first; |
| 263 const MetricsItem& metrics_item = metrics_item_it.second; |
| 264 |
| 265 metrics_array_value->Append(CreateDictForMetricsItem( |
| 266 metrics_item_name, metrics_item, metrics_i, dir_path)); |
| 267 |
| 268 metrics_i++; |
| 269 } |
| 270 |
| 271 return metrics_array_value; |
| 272 } |
| 273 |
| 274 } // namespace |
| 275 |
| 276 void WriteResultsToDirectory(const Metrics& metrics, |
| 277 const base::FilePath& dir_path) { |
| 278 CopyStaticFile("net/tools/ct_mapper/index.html", dir_path); |
| 279 CopyStaticFile("net/tools/ct_mapper/main.js", dir_path); |
| 280 |
| 281 base::DictionaryValue results_dict; |
| 282 |
| 283 results_dict.Set("metrics", CreateListForMetrics(metrics, dir_path)); |
| 284 |
| 285 // JSON-ify the overview information. |
| 286 std::string json; |
| 287 CHECK(base::JSONWriter::WriteWithOptions( |
| 288 results_dict, |
| 289 base::JSONWriter::OPTIONS_PRETTY_PRINT | |
| 290 base::JSONWriter::OPTIONS_OMIT_DOUBLE_TYPE_PRESERVATION, |
| 291 &json)); |
| 292 |
| 293 // Use a .js file rather than a pure data file (since for data file would need |
| 294 // to XHR it in... and Chrome doesn't let you XHR on file://. LAME. |
| 295 WriteStringToFile("var g_data = " + json + ";", |
| 296 dir_path.AppendASCII("data.js")); |
| 297 } |
| 298 |
| 299 } // namespace net |
| OLD | NEW |