Home | History | Annotate | Download | only in shill
      1 //
      2 // Copyright (C) 2013 The Android Open Source Project
      3 //
      4 // Licensed under the Apache License, Version 2.0 (the "License");
      5 // you may not use this file except in compliance with the License.
      6 // You may obtain a copy of the License at
      7 //
      8 //      http://www.apache.org/licenses/LICENSE-2.0
      9 //
     10 // Unless required by applicable law or agreed to in writing, software
     11 // distributed under the License is distributed on an "AS IS" BASIS,
     12 // WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
     13 // See the License for the specific language governing permissions and
     14 // limitations under the License.
     15 //
     16 
     17 #include "shill/certificate_file.h"
     18 
     19 #include <sys/stat.h>
     20 
     21 #include <string>
     22 #include <vector>
     23 
     24 #include <base/files/file_util.h>
     25 #include <base/strings/string_split.h>
     26 #include <base/strings/string_util.h>
     27 #include <base/strings/stringprintf.h>
     28 
     29 #include "shill/logging.h"
     30 
     31 using base::FilePath;
     32 using base::SplitString;
     33 using base::StringPrintf;
     34 using std::string;
     35 using std::vector;
     36 
     37 namespace shill {
     38 
     39 namespace Logging {
     40 static auto kModuleLogScope = ScopeLogger::kCrypto;
     41 static string ObjectID(CertificateFile* c) { return "(certificate_file)"; }
     42 }
     43 
     44 const char CertificateFile::kDefaultRootDirectory[] =
     45     RUNDIR "/certificate_export";
     46 const char CertificateFile::kPEMHeader[] = "-----BEGIN CERTIFICATE-----";
     47 const char CertificateFile::kPEMFooter[] = "-----END CERTIFICATE-----";
     48 
     49 CertificateFile::CertificateFile()
     50     : root_directory_(FilePath(kDefaultRootDirectory)) {
     51   SLOG(this, 2) << __func__;
     52 }
     53 
     54 CertificateFile::~CertificateFile() {
     55   SLOG(this, 2) << __func__;
     56   if (!output_file_.empty()) {
     57     base::DeleteFile(output_file_, false);
     58   }
     59 }
     60 
     61 FilePath CertificateFile::CreatePEMFromStrings(
     62     const vector<string>& pem_contents) {
     63   vector<string> pem_output;
     64   for (const auto& content : pem_contents) {
     65     string hex_data = ExtractHexData(content);
     66     if (hex_data.empty()) {
     67       return FilePath();
     68     }
     69     pem_output.push_back(StringPrintf(
     70       "%s\n%s%s\n", kPEMHeader, hex_data.c_str(), kPEMFooter));
     71   }
     72   return WriteFile(base::JoinString(pem_output, ""));
     73 }
     74 
     75 // static
     76 string CertificateFile::ExtractHexData(const std::string& pem_data) {
     77   bool found_header = false;
     78   bool found_footer = false;
     79   vector<string> input_lines = SplitString(
     80       pem_data, "\n", base::TRIM_WHITESPACE, base::SPLIT_WANT_ALL);
     81   vector<string> output_lines;
     82   for (vector<string>::const_iterator it = input_lines.begin();
     83        it != input_lines.end(); ++it) {
     84     string line;
     85     base::TrimWhitespaceASCII(*it, base::TRIM_ALL, &line);
     86     if (base::StartsWith(line, kPEMHeader,
     87                          base::CompareCase::INSENSITIVE_ASCII)) {
     88       if (found_header) {
     89         LOG(ERROR) << "Found two PEM headers in a row.";
     90         return string();
     91       } else {
     92         found_header = true;
     93         output_lines.clear();
     94       }
     95     } else if (base::StartsWith(line, kPEMFooter,
     96                                 base::CompareCase::INSENSITIVE_ASCII)) {
     97       if (!found_header) {
     98         LOG(ERROR) << "Found a PEM footer before header.";
     99         return string();
    100       } else {
    101         found_footer = true;
    102         break;
    103       }
    104     } else if (!line.empty()) {
    105       output_lines.push_back(line);
    106     }
    107   }
    108   if (found_header && !found_footer) {
    109     LOG(ERROR) << "Found PEM header but no footer.";
    110     return string();
    111   }
    112   DCHECK_EQ(found_header, found_footer);
    113   output_lines.push_back("");
    114   return base::JoinString(output_lines, "\n");
    115 }
    116 
    117 FilePath CertificateFile::WriteFile(const string& output_data) {
    118   if (!base::DirectoryExists(root_directory_)) {
    119     if (!base::CreateDirectory(root_directory_)) {
    120       LOG(ERROR) << "Unable to create parent directory  "
    121                  << root_directory_.value();
    122       return FilePath();
    123     }
    124     if (chmod(root_directory_.value().c_str(),
    125               S_IRWXU | S_IXGRP | S_IRGRP | S_IXOTH | S_IROTH)) {
    126       LOG(ERROR) << "Failed to set permissions on "
    127                  << root_directory_.value();
    128       base::DeleteFile(root_directory_, true);
    129       return FilePath();
    130     }
    131   }
    132   if (!output_file_.empty()) {
    133     base::DeleteFile(output_file_, false);
    134     output_file_ = FilePath();
    135   }
    136 
    137   FilePath output_file;
    138   if (!base::CreateTemporaryFileInDir(root_directory_, &output_file)) {
    139     LOG(ERROR) << "Unable to create output file.";
    140     return FilePath();
    141   }
    142 
    143   size_t written =
    144       base::WriteFile(output_file, output_data.c_str(), output_data.length());
    145   if (written != output_data.length()) {
    146     LOG(ERROR) << "Unable to write to output file.";
    147     return FilePath();
    148   }
    149 
    150   if (chmod(output_file.value().c_str(),
    151             S_IRUSR | S_IWUSR | S_IRGRP | S_IROTH)) {
    152     LOG(ERROR) << "Failed to set permissions on " << output_file.value();
    153     base::DeleteFile(output_file, false);
    154     return FilePath();
    155   }
    156   output_file_ = output_file;
    157   return output_file_;
    158 }
    159 
    160 }  // namespace shill
    161