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;
ObjectID(CertificateFile * c)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 
CertificateFile()49 CertificateFile::CertificateFile()
50     : root_directory_(FilePath(kDefaultRootDirectory)) {
51   SLOG(this, 2) << __func__;
52 }
53 
~CertificateFile()54 CertificateFile::~CertificateFile() {
55   SLOG(this, 2) << __func__;
56   if (!output_file_.empty()) {
57     base::DeleteFile(output_file_, false);
58   }
59 }
60 
CreatePEMFromStrings(const vector<string> & pem_contents)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
ExtractHexData(const std::string & pem_data)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 
WriteFile(const string & output_data)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