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