Move some modules to common library
[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
36 #include <dpl/log/log.h>
37 #include <tzplatform_config.h>
38
39 #include <smack-common.h>
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 APP_RULES_TEMPLATE_FILE_PATH = tzplatform_mkpath(TZ_SYS_SMACK, "app-rules-template.smack");
47
48 SmackRules::SmackRules()
49 {
50     if (smack_accesses_new(&m_handle) < 0) {
51         LogError("Failed to create smack_accesses handle");
52         throw std::bad_alloc();
53     }
54 }
55
56 SmackRules::~SmackRules() {
57     smack_accesses_free(m_handle);
58 }
59
60 bool SmackRules::add(const std::string &subject, const std::string &object,
61         const std::string &permissions)
62 {
63     return 0 == smack_accesses_add(m_handle, subject.c_str(), object.c_str(), permissions.c_str());
64 }
65
66 bool SmackRules::clear() const
67 {
68     return 0 == smack_accesses_clear(m_handle);
69 }
70
71 bool SmackRules::apply() const
72 {
73     return 0 == smack_accesses_apply(m_handle);
74 }
75
76 bool SmackRules::loadFromFile(const std::string &path)
77 {
78     int fd;
79     bool ret = true;
80
81     fd = TEMP_FAILURE_RETRY(open(path.c_str(), O_RDONLY));
82     if (fd == -1) {
83         LogError("Failed to open file: " << path);
84         return false;
85     }
86
87     if (smack_accesses_add_from_file(m_handle, fd)) {
88         LogError("Failed to load smack rules from file: " << path);
89         ret = false;
90     }
91
92     if (close(fd) == -1) {
93         // don't change the return code, the descriptor should be closed despite the error.
94         LogWarning("Error while closing the file: " << path << ", error: " << strerror(errno));
95     }
96
97     return ret;
98 }
99
100 bool SmackRules::saveToFile(const std::string &path) const
101 {
102     int fd;
103     bool ret = true;
104
105     fd = TEMP_FAILURE_RETRY(open(path.c_str(), O_CREAT | O_WRONLY | O_TRUNC, 0644));
106     if (fd == -1) {
107         LogError("Failed to create file: " << path);
108         return false;
109     }
110
111     if (smack_accesses_save(m_handle, fd)) {
112         LogError("Failed to save rules to file: " << path);
113         unlink(path.c_str());
114         ret = false;
115     }
116
117     if (close(fd) == -1) {
118         if (errno == EIO) {
119             LogError("I/O Error occured while closing the file: " << path << ", error: " << strerror(errno));
120             unlink(path.c_str());
121             return false;
122         } else {
123             // non critical error
124             // don't change the return code, the descriptor should be closed despite the error.
125             LogWarning("Error while closing the file: " << path << ", error: " << strerror(errno));
126         }
127     }
128
129     return ret;
130 }
131
132
133 bool SmackRules::addFromTemplateFile(const std::string &pkgId)
134 {
135     std::vector<std::string> templateRules;
136     std::string line;
137     std::ifstream templateRulesFile(APP_RULES_TEMPLATE_FILE_PATH);
138
139     if (!templateRulesFile.is_open()) {
140         LogError("Cannot open rules template file: " << APP_RULES_TEMPLATE_FILE_PATH);
141         return false;
142     }
143
144     while (std::getline(templateRulesFile, line)) {
145         templateRules.push_back(line);
146     }
147
148     if (templateRulesFile.bad()) {
149         LogError("Error reading template file: " << APP_RULES_TEMPLATE_FILE_PATH);
150         return false;
151     }
152
153     return addFromTemplate(templateRules, pkgId);
154 }
155
156 bool SmackRules::addFromTemplate(const std::vector<std::string> &templateRules,
157         const std::string &pkgId)
158 {
159     for (auto rule : templateRules) {
160         if (rule.empty())
161             continue;
162
163         std::stringstream stream(rule);
164         std::string subject, object, permissions;
165         stream >> subject >> object >> permissions;
166
167         if (stream.fail() || !stream.eof()) {
168             LogError("Invalid rule template: " << rule);
169             return false;
170         }
171
172         bool subjectIsTemplate = (subject == SMACK_APP_LABEL_TEMPLATE);
173         bool objectIsTemplate = (object == SMACK_APP_LABEL_TEMPLATE);
174
175         if (objectIsTemplate == subjectIsTemplate) {
176             LogError("Invalid rule template. Exactly one app label template expected: " << rule);
177             return false;
178         }
179
180         if (subjectIsTemplate) {
181             if (!generateAppLabel(pkgId, subject)) {
182                 LogError("Failed to generate app label from pkgid: " << pkgId);
183                 return false;
184             }
185         }
186
187         if (objectIsTemplate) {
188             if (!generateAppLabel(pkgId, object)) {
189                 LogError("Failed to generate app label from pkgid: " << pkgId);
190                 return false;
191             }
192         }
193
194         if (!add(subject, object, permissions)) {
195             LogError("Failed to add rule: " << subject << " " << object << " " << permissions);
196             return false;
197         }
198     }
199
200     return true;
201 }
202
203 std::string SmackRules::getPackageRulesFilePath(const std::string &pkgId)
204 {
205     std::string path(tzplatform_mkpath3(TZ_SYS_SMACK, "accesses.d", pkgId.c_str()));
206     return path;
207 }
208
209 bool SmackRules::installPackageRules(const std::string &pkgId)
210 {
211     try {
212          SmackRules smackRules;
213          std::string path = getPackageRulesFilePath(pkgId);
214
215          if (!smackRules.addFromTemplateFile(pkgId)) {
216              LogError("Failed to load smack rules for pkgId " << pkgId);
217              return false;
218          }
219
220          if (smack_smackfs_path() != NULL && !smackRules.apply()) {
221              LogError("Failed to apply application rules to kernel");
222              return false;
223          }
224
225          if (!smackRules.saveToFile(path)) {
226              smackRules.clear();
227              return false;
228          }
229
230          return true;
231      } catch (const std::bad_alloc &e) {
232          LogError("Out of memory while trying to install smack rules for pkgId " << pkgId);
233          return false;
234      }
235 }
236
237 /* FIXME: Remove this function if real pkgId instead of "User" label will be used
238  * in generateAppLabel(). */
239 bool SmackRules::addMissingRulesFix()
240 {
241     DIR *dir;
242     struct dirent *ent;
243     SmackRules rules;
244     std::string path(tzplatform_mkpath(TZ_SYS_SMACK, "accesses.d"));
245
246     dir = opendir(path.c_str());
247     if (dir != NULL) {
248         while ((ent = readdir(dir))) {
249             if (ent->d_type == DT_REG) {
250                 rules.loadFromFile(tzplatform_mkpath3(TZ_SYS_SMACK, "accesses.d/", ent->d_name));
251                 // Do not check error here. If this fails we can't do anything anyway.
252             }
253         }
254         rules.apply();
255     }
256     else
257         return false;
258
259     closedir(dir);
260
261     return true;
262 }
263
264 bool SmackRules::uninstallPackageRules(const std::string &pkgId)
265 {
266     std::string path = getPackageRulesFilePath(pkgId);
267     if (access(path.c_str(), F_OK) == -1) {
268         if (errno == ENOENT) {
269             LogWarning("Smack rules were not installed for pkgId: " << pkgId);
270             return true;
271         }
272
273         LogWarning("Cannot access smack rules path: " << path);
274         return false;
275     }
276
277     try {
278         SmackRules rules;
279         if (rules.loadFromFile(path)) {
280             if (smack_smackfs_path() != NULL && !rules.clear()) {
281                 LogWarning("Failed to clear smack kernel rules for pkgId: " << pkgId);
282                 // don't stop uninstallation
283             }
284         } else {
285             LogWarning("Failed to load rules from file: " << path);
286             // don't stop uninstallation
287         }
288
289         if (unlink(path.c_str()) == -1) {
290             LogError("Failed to remove smack rules file: " << path);
291             return false;
292         }
293
294         // FIXME: Reloading all rules:
295         SmackRules::addMissingRulesFix();
296
297         return true;
298     } catch (const std::bad_alloc &e) {
299         LogError("Out of memory while trying to uninstall smack rules for pkgId: " << pkgId);
300         return false;
301     }
302 }
303
304 } // namespace SecurityManager
305