221cf4d125bf0641c8aadc4ad82e55e64f5d7826
[platform/core/security/security-manager.git] / src / common / smack-rules.cpp
1 /*
2  *  Copyright (c) 2014 Samsung Electronics Co., Ltd All Rights Reserved
3  *
4  *  Contact: Rafal Krypa <r.krypa@samsung.com>
5  *
6  *  Licensed under the Apache License, Version 2.0 (the "License");
7  *  you may not use this file except in compliance with the License.
8  *  You may obtain a copy of the License at
9  *
10  *      http://www.apache.org/licenses/LICENSE-2.0
11  *
12  *  Unless required by applicable law or agreed to in writing, software
13  *  distributed under the License is distributed on an "AS IS" BASIS,
14  *  WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
15  *  See the License for the specific language governing permissions and
16  *  limitations under the License
17  */
18 /**
19  * @file        smack-rules.cpp
20  * @author      Jacek Bukarewicz <j.bukarewicz@samsung.com>
21  * @version     1.0
22  * @brief       Implementation of a class managing smack rules
23  *
24  */
25
26 #include <unistd.h>
27 #include <sys/types.h>
28 #include <dirent.h>
29 #include <sys/stat.h>
30 #include <sys/smack.h>
31 #include <fcntl.h>
32 #include <fstream>
33 #include <cstring>
34 #include <sstream>
35 #include <memory>
36
37 #include <dpl/log/log.h>
38 #include <tzplatform_config.h>
39
40 #include "smack-labels.h"
41 #include "smack-rules.h"
42
43 namespace SecurityManager {
44
45 const char *const SMACK_APP_LABEL_TEMPLATE     = "~APP~";
46 const char *const SMACK_PKG_LABEL_TEMPLATE     = "~PKG~";
47 const char *const APP_RULES_TEMPLATE_FILE_PATH = tzplatform_mkpath4(TZ_SYS_SHARE, "security-manager", "policy", "app-rules-template.smack");
48 const char *const SMACK_APP_IN_PACKAGE_PERMS   = "rwxat";
49
50 SmackRules::SmackRules()
51 {
52     if (smack_accesses_new(&m_handle) < 0) {
53         LogError("Failed to create smack_accesses handle");
54         throw std::bad_alloc();
55     }
56 }
57
58 SmackRules::~SmackRules() {
59     smack_accesses_free(m_handle);
60 }
61
62 void SmackRules::add(const std::string &subject, const std::string &object,
63         const std::string &permissions)
64 {
65     if (smack_accesses_add(m_handle, subject.c_str(), object.c_str(), permissions.c_str()))
66         ThrowMsg(SmackException::LibsmackError, "smack_accesses_add");
67 }
68
69 void SmackRules::addModify(const std::string &subject, const std::string &object,
70         const std::string &allowPermissions, const std::string &denyPermissions)
71 {
72     if (smack_accesses_add_modify(m_handle, subject.c_str(), object.c_str(), allowPermissions.c_str(), denyPermissions.c_str()))
73         ThrowMsg(SmackException::LibsmackError, "smack_accesses_add_modify");
74 }
75
76 void SmackRules::clear() const
77 {
78     if (smack_accesses_clear(m_handle))
79         ThrowMsg(SmackException::LibsmackError, "smack_accesses_clear");
80 }
81
82 void SmackRules::apply() const
83 {
84     if (smack_accesses_apply(m_handle))
85         ThrowMsg(SmackException::LibsmackError, "smack_accesses_apply");
86
87 }
88
89 void SmackRules::loadFromFile(const std::string &path)
90 {
91     int fd;
92
93     fd = TEMP_FAILURE_RETRY(open(path.c_str(), O_RDONLY));
94     if (fd == -1) {
95         LogError("Failed to open file: " << path);
96         ThrowMsg(SmackException::FileError, "Failed to open file: " << path);
97     }
98
99     if (smack_accesses_add_from_file(m_handle, fd)) {
100         LogError("Failed to load smack rules from file: " << path);
101         ThrowMsg(SmackException::LibsmackError, "Failed to load smack rules from file: " << path);
102     }
103
104     if (close(fd) == -1) {
105         // don't change the return code, the descriptor should be closed despite the error.
106         LogWarning("Error while closing the file: " << path << ", error: " << strerror(errno));
107     }
108 }
109
110 void SmackRules::saveToFile(const std::string &path) const
111 {
112     int fd;
113
114     fd = TEMP_FAILURE_RETRY(open(path.c_str(), O_CREAT | O_WRONLY | O_TRUNC, 0644));
115     if (fd == -1) {
116         LogError("Failed to create file: " << path);
117         ThrowMsg(SmackException::FileError, "Failed to create file: " << path);
118     }
119
120     if (smack_accesses_save(m_handle, fd)) {
121         LogError("Failed to save rules to file: " << path);
122         unlink(path.c_str());
123         ThrowMsg(SmackException::LibsmackError, "Failed to save rules to file: " << path);
124     }
125
126     if (close(fd) == -1) {
127         if (errno == EIO) {
128             LogError("I/O Error occured while closing the file: " << path << ", error: " << strerror(errno));
129             unlink(path.c_str());
130             ThrowMsg(SmackException::FileError, "I/O Error occured while closing the file: " << path << ", error: " << strerror(errno));
131         } else {
132             // non critical error
133             // don't change the return code, the descriptor should be closed despite the error.
134             LogWarning("Error while closing the file: " << path << ", error: " << strerror(errno));
135         }
136     }
137 }
138
139
140 void SmackRules::addFromTemplateFile(const std::string &appId,
141         const std::string &pkgId)
142 {
143     std::vector<std::string> templateRules;
144     std::string line;
145     std::ifstream templateRulesFile(APP_RULES_TEMPLATE_FILE_PATH);
146
147     if (!templateRulesFile.is_open()) {
148         LogError("Cannot open rules template file: " << APP_RULES_TEMPLATE_FILE_PATH);
149         ThrowMsg(SmackException::FileError, "Cannot open rules template file: " << APP_RULES_TEMPLATE_FILE_PATH);
150     }
151
152     while (std::getline(templateRulesFile, line)) {
153         templateRules.push_back(line);
154     }
155
156     if (templateRulesFile.bad()) {
157         LogError("Error reading template file: " << APP_RULES_TEMPLATE_FILE_PATH);
158         ThrowMsg(SmackException::FileError, "Error reading template file: " << APP_RULES_TEMPLATE_FILE_PATH);
159     }
160
161     addFromTemplate(templateRules, appId, pkgId);
162 }
163
164 void SmackRules::addFromTemplate(const std::vector<std::string> &templateRules,
165         const std::string &appId, const std::string &pkgId)
166 {
167     for (auto rule : templateRules) {
168         if (rule.empty())
169             continue;
170
171         std::stringstream stream(rule);
172         std::string subject, object, permissions;
173         stream >> subject >> object >> permissions;
174
175         if (stream.fail() || !stream.eof()) {
176             LogError("Invalid rule template: " << rule);
177             ThrowMsg(SmackException::FileError, "Invalid rule template: " << rule);
178         }
179
180         if (subject == SMACK_APP_LABEL_TEMPLATE)
181             subject = SmackLabels::generateAppLabel(appId);
182
183         if (subject == SMACK_PKG_LABEL_TEMPLATE)
184              subject = SmackLabels::generatePkgLabel(pkgId);
185
186         if (object == SMACK_APP_LABEL_TEMPLATE)
187             object = SmackLabels::generateAppLabel(appId);
188
189         if (object == SMACK_PKG_LABEL_TEMPLATE)
190             object = SmackLabels::generatePkgLabel(pkgId);
191
192         add(subject, object, permissions);
193     }
194 }
195
196 void SmackRules::generatePackageCrossDeps(const std::vector<std::string> &pkgContents)
197 {
198     LogDebug ("Generating cross-package rules");
199
200     std::string subjectLabel, objectLabel;
201     std::string appsInPackagePerms = SMACK_APP_IN_PACKAGE_PERMS;
202
203     for (const auto &subject : pkgContents) {
204         for (const auto &object : pkgContents) {
205             if (object == subject)
206                 continue;
207
208             subjectLabel = SmackLabels::generateAppLabel(subject);
209             objectLabel = SmackLabels::generateAppLabel(object);
210             LogDebug ("Trying to add rule subject: " << subjectLabel << " object: " << objectLabel << " perms: " << appsInPackagePerms);
211             add(subjectLabel, objectLabel, appsInPackagePerms);
212         }
213     }
214 }
215
216 std::string SmackRules::getPackageRulesFilePath(const std::string &pkgId)
217 {
218     std::string path(tzplatform_mkpath3(TZ_SYS_SMACK, "accesses.d", ("pkg_" + pkgId).c_str()));
219     return path;
220 }
221
222 std::string SmackRules::getApplicationRulesFilePath(const std::string &appId)
223 {
224     std::string path(tzplatform_mkpath3(TZ_SYS_SMACK, "accesses.d", ("app_" +  appId).c_str()));
225     return path;
226 }
227
228 void SmackRules::installApplicationRules(const std::string &appId, const std::string &pkgId,
229         const std::vector<std::string> &pkgContents)
230 {
231     SmackRules smackRules;
232     std::string appPath = getApplicationRulesFilePath(appId);
233
234     smackRules.addFromTemplateFile(appId, pkgId);
235
236     if (smack_smackfs_path() != NULL)
237         smackRules.apply();
238
239     smackRules.saveToFile(appPath);
240     updatePackageRules(pkgId, pkgContents);
241 }
242
243 void SmackRules::updatePackageRules(const std::string &pkgId, const std::vector<std::string> &pkgContents)
244 {
245     SmackRules smackRules;
246     std::string pkgPath = getPackageRulesFilePath(pkgId);
247
248     smackRules.generatePackageCrossDeps(pkgContents);
249
250     if (smack_smackfs_path() != NULL)
251         smackRules.apply();
252
253     smackRules.saveToFile(pkgPath);
254 }
255
256 /* FIXME: Remove this function if real pkgId instead of "User" label will be used
257  * in generateAppLabel(). */
258 void SmackRules::addMissingRulesFix()
259 {
260     struct dirent *ent;
261
262     std::string path(tzplatform_mkpath(TZ_SYS_SMACK, "accesses.d"));
263     std::unique_ptr<DIR, std::function<int(DIR*)>> dir(opendir(path.c_str()), closedir);
264     if (!dir)
265         ThrowMsg(SmackException::FileError, "opendir");
266
267     while ((ent = readdir(dir.get()))) {
268         SmackRules rules;
269         if (ent->d_type == DT_REG) {
270             rules.loadFromFile(tzplatform_mkpath3(TZ_SYS_SMACK, "accesses.d/", ent->d_name));
271             // Do not check error here. If this fails we can't do anything anyway.
272         }
273         rules.apply();
274     }
275 }
276
277 void SmackRules::uninstallPackageRules(const std::string &pkgId)
278 {
279     uninstallRules(getPackageRulesFilePath(pkgId));
280 }
281
282 void SmackRules::uninstallApplicationRules(const std::string &appId,
283         const std::string &pkgId, std::vector<std::string> pkgContents)
284 {
285     uninstallRules(getApplicationRulesFilePath(appId));
286     updatePackageRules(pkgId, pkgContents);
287
288     // FIXME: Reloading all rules:
289     SmackRules::addMissingRulesFix();
290 }
291
292 void SmackRules::uninstallRules(const std::string &path)
293 {
294     if (access(path.c_str(), F_OK) == -1) {
295         if (errno == ENOENT) {
296             LogWarning("Smack rules not found in file: " << path);
297             return;
298         }
299
300         LogWarning("Cannot access smack rules path: " << path);
301         ThrowMsg(SmackException::FileError, "Cannot access smack rules path: " << path);
302     }
303
304     try {
305         SmackRules rules;
306         rules.loadFromFile(path);
307         if (smack_smackfs_path())
308             rules.clear();
309     } catch (const SmackException::Base &e) {
310         LogWarning("Failed to clear smack kernel rules from file: " << path);
311         // don't stop uninstallation
312     }
313
314     if (unlink(path.c_str()) == -1) {
315         LogWarning("Failed to remove smack rules file: " << path);
316         ThrowMsg(SmackException::FileError, "Failed to remove smack rules file: " << path);
317     }
318 }
319
320 } // namespace SecurityManager