1 /*
2  * Copyright (C) 2017 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 "persistent_properties.h"
18 
19 #include <dirent.h>
20 #include <fcntl.h>
21 #include <sys/stat.h>
22 #include <sys/system_properties.h>
23 #include <sys/types.h>
24 
25 #include <memory>
26 
27 #include <android-base/file.h>
28 #include <android-base/logging.h>
29 #include <android-base/strings.h>
30 #include <android-base/unique_fd.h>
31 
32 #include "util.h"
33 
34 using android::base::Dirname;
35 using android::base::ReadFdToString;
36 using android::base::StartsWith;
37 using android::base::unique_fd;
38 using android::base::WriteStringToFd;
39 
40 namespace android {
41 namespace init {
42 
43 std::string persistent_property_filename = "/data/property/persistent_properties";
44 
45 namespace {
46 
47 constexpr const char kLegacyPersistentPropertyDir[] = "/data/property";
48 
AddPersistentProperty(const std::string & name,const std::string & value,PersistentProperties * persistent_properties)49 void AddPersistentProperty(const std::string& name, const std::string& value,
50                            PersistentProperties* persistent_properties) {
51     auto persistent_property_record = persistent_properties->add_properties();
52     persistent_property_record->set_name(name);
53     persistent_property_record->set_value(value);
54 }
55 
LoadLegacyPersistentProperties()56 Result<PersistentProperties> LoadLegacyPersistentProperties() {
57     std::unique_ptr<DIR, decltype(&closedir)> dir(opendir(kLegacyPersistentPropertyDir), closedir);
58     if (!dir) {
59         return ErrnoError() << "Unable to open persistent property directory \""
60                             << kLegacyPersistentPropertyDir << "\"";
61     }
62 
63     PersistentProperties persistent_properties;
64     dirent* entry;
65     while ((entry = readdir(dir.get())) != nullptr) {
66         if (!StartsWith(entry->d_name, "persist.")) {
67             continue;
68         }
69         if (entry->d_type != DT_REG) {
70             continue;
71         }
72 
73         unique_fd fd(openat(dirfd(dir.get()), entry->d_name, O_RDONLY | O_NOFOLLOW | O_CLOEXEC));
74         if (fd == -1) {
75             PLOG(ERROR) << "Unable to open persistent property file \"" << entry->d_name << "\"";
76             continue;
77         }
78 
79         struct stat sb;
80         if (fstat(fd, &sb) == -1) {
81             PLOG(ERROR) << "fstat on property file \"" << entry->d_name << "\" failed";
82             continue;
83         }
84 
85         // File must not be accessible to others, be owned by root/root, and
86         // not be a hard link to any other file.
87         if (((sb.st_mode & (S_IRWXG | S_IRWXO)) != 0) || sb.st_uid != 0 || sb.st_gid != 0 ||
88             sb.st_nlink != 1) {
89             PLOG(ERROR) << "skipping insecure property file " << entry->d_name
90                         << " (uid=" << sb.st_uid << " gid=" << sb.st_gid << " nlink=" << sb.st_nlink
91                         << " mode=" << std::oct << sb.st_mode << ")";
92             continue;
93         }
94 
95         std::string value;
96         if (ReadFdToString(fd, &value)) {
97             AddPersistentProperty(entry->d_name, value, &persistent_properties);
98         } else {
99             PLOG(ERROR) << "Unable to read persistent property file " << entry->d_name;
100         }
101     }
102     return persistent_properties;
103 }
104 
RemoveLegacyPersistentPropertyFiles()105 void RemoveLegacyPersistentPropertyFiles() {
106     std::unique_ptr<DIR, decltype(&closedir)> dir(opendir(kLegacyPersistentPropertyDir), closedir);
107     if (!dir) {
108         PLOG(ERROR) << "Unable to open persistent property directory \""
109                     << kLegacyPersistentPropertyDir << "\"";
110         return;
111     }
112 
113     dirent* entry;
114     while ((entry = readdir(dir.get())) != nullptr) {
115         if (!StartsWith(entry->d_name, "persist.")) {
116             continue;
117         }
118         if (entry->d_type != DT_REG) {
119             continue;
120         }
121         unlinkat(dirfd(dir.get()), entry->d_name, 0);
122     }
123 }
124 
LoadPersistentPropertiesFromMemory()125 PersistentProperties LoadPersistentPropertiesFromMemory() {
126     PersistentProperties persistent_properties;
127     __system_property_foreach(
128         [](const prop_info* pi, void* cookie) {
129             __system_property_read_callback(
130                 pi,
131                 [](void* cookie, const char* name, const char* value, unsigned serial) {
132                     if (StartsWith(name, "persist.")) {
133                         auto properties = reinterpret_cast<PersistentProperties*>(cookie);
134                         AddPersistentProperty(name, value, properties);
135                     }
136                 },
137                 cookie);
138         },
139         &persistent_properties);
140     return persistent_properties;
141 }
142 
ReadPersistentPropertyFile()143 Result<std::string> ReadPersistentPropertyFile() {
144     const std::string temp_filename = persistent_property_filename + ".tmp";
145     if (access(temp_filename.c_str(), F_OK) == 0) {
146         LOG(INFO)
147             << "Found temporary property file while attempting to persistent system properties"
148                " a previous persistent property write may have failed";
149         unlink(temp_filename.c_str());
150     }
151     auto file_contents = ReadFile(persistent_property_filename);
152     if (!file_contents.ok()) {
153         return Error() << "Unable to read persistent property file: " << file_contents.error();
154     }
155     return *file_contents;
156 }
157 
158 }  // namespace
159 
LoadPersistentPropertyFile()160 Result<PersistentProperties> LoadPersistentPropertyFile() {
161     auto file_contents = ReadPersistentPropertyFile();
162     if (!file_contents.ok()) return file_contents.error();
163 
164     PersistentProperties persistent_properties;
165     if (persistent_properties.ParseFromString(*file_contents)) return persistent_properties;
166 
167     // If the file cannot be parsed in either format, then we don't have any recovery
168     // mechanisms, so we delete it to allow for future writes to take place successfully.
169     unlink(persistent_property_filename.c_str());
170     return Error() << "Unable to parse persistent property file: Could not parse protobuf";
171 }
172 
WritePersistentPropertyFile(const PersistentProperties & persistent_properties)173 Result<void> WritePersistentPropertyFile(const PersistentProperties& persistent_properties) {
174     const std::string temp_filename = persistent_property_filename + ".tmp";
175     unique_fd fd(TEMP_FAILURE_RETRY(
176         open(temp_filename.c_str(), O_WRONLY | O_CREAT | O_NOFOLLOW | O_TRUNC | O_CLOEXEC, 0600)));
177     if (fd == -1) {
178         return ErrnoError() << "Could not open temporary properties file";
179     }
180     std::string serialized_string;
181     if (!persistent_properties.SerializeToString(&serialized_string)) {
182         return Error() << "Unable to serialize properties";
183     }
184     if (!WriteStringToFd(serialized_string, fd)) {
185         return ErrnoError() << "Unable to write file contents";
186     }
187     fsync(fd);
188     fd.reset();
189 
190     if (rename(temp_filename.c_str(), persistent_property_filename.c_str())) {
191         int saved_errno = errno;
192         unlink(temp_filename.c_str());
193         return Error(saved_errno) << "Unable to rename persistent property file";
194     }
195 
196     // rename() is atomic with regards to the kernel's filesystem buffers, but the parent
197     // directories must be fsync()'ed otherwise, the rename is not necessarily written to storage.
198     // Note in this case, that the source and destination directories are the same, so only one
199     // fsync() is required.
200     auto dir = Dirname(persistent_property_filename);
201     auto dir_fd = unique_fd{open(dir.c_str(), O_DIRECTORY | O_RDONLY | O_CLOEXEC)};
202     if (dir_fd < 0) {
203         return ErrnoError() << "Unable to open persistent properties directory for fsync()";
204     }
205     fsync(dir_fd);
206 
207     return {};
208 }
209 
210 // Persistent properties are not written often, so we rather not keep any data in memory and read
211 // then rewrite the persistent property file for each update.
WritePersistentProperty(const std::string & name,const std::string & value)212 void WritePersistentProperty(const std::string& name, const std::string& value) {
213     auto persistent_properties = LoadPersistentPropertyFile();
214 
215     if (!persistent_properties.ok()) {
216         LOG(ERROR) << "Recovering persistent properties from memory: "
217                    << persistent_properties.error();
218         persistent_properties = LoadPersistentPropertiesFromMemory();
219     }
220     auto it = std::find_if(persistent_properties->mutable_properties()->begin(),
221                            persistent_properties->mutable_properties()->end(),
222                            [&name](const auto& record) { return record.name() == name; });
223     if (it != persistent_properties->mutable_properties()->end()) {
224         it->set_name(name);
225         it->set_value(value);
226     } else {
227         AddPersistentProperty(name, value, &persistent_properties.value());
228     }
229 
230     if (auto result = WritePersistentPropertyFile(*persistent_properties); !result.ok()) {
231         LOG(ERROR) << "Could not store persistent property: " << result.error();
232     }
233 }
234 
LoadPersistentProperties()235 PersistentProperties LoadPersistentProperties() {
236     auto persistent_properties = LoadPersistentPropertyFile();
237 
238     if (!persistent_properties.ok()) {
239         LOG(ERROR) << "Could not load single persistent property file, trying legacy directory: "
240                    << persistent_properties.error();
241         persistent_properties = LoadLegacyPersistentProperties();
242         if (!persistent_properties.ok()) {
243             LOG(ERROR) << "Unable to load legacy persistent properties: "
244                        << persistent_properties.error();
245             return {};
246         }
247         if (auto result = WritePersistentPropertyFile(*persistent_properties); result.ok()) {
248             RemoveLegacyPersistentPropertyFiles();
249         } else {
250             LOG(ERROR) << "Unable to write single persistent property file: " << result.error();
251             // Fall through so that we still set the properties that we've read.
252         }
253     }
254 
255     return *persistent_properties;
256 }
257 
258 }  // namespace init
259 }  // namespace android
260