Change smack labeling to be appId based.
[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-labels.h"
40 #include "smack-rules.h"
41
42 namespace SecurityManager {
43
44 const char *const SMACK_APP_LABEL_TEMPLATE     = "~APP~";
45 const char *const SMACK_PKG_LABEL_TEMPLATE     = "~PKG~";
46 const char *const APP_RULES_TEMPLATE_FILE_PATH = tzplatform_mkpath(TZ_SYS_SMACK, "app-rules-template.smack");
47 const char *const SMACK_APP_IN_PACKAGE_PERMS   = "rwxat";
48
49 SmackRules::SmackRules()
50 {
51     if (smack_accesses_new(&m_handle) < 0) {
52         LogError("Failed to create smack_accesses handle");
53         throw std::bad_alloc();
54     }
55 }
56
57 SmackRules::~SmackRules() {
58     smack_accesses_free(m_handle);
59 }
60
61 bool SmackRules::add(const std::string &subject, const std::string &object,
62         const std::string &permissions)
63 {
64     return 0 == smack_accesses_add(m_handle, subject.c_str(), object.c_str(), permissions.c_str());
65 }
66
67 bool SmackRules::addModify(const std::string &subject, const std::string &object,
68         const std::string &allowPermissions, const std::string &denyPermissions)
69 {
70     return 0 == smack_accesses_add_modify(m_handle, subject.c_str(), object.c_str(), allowPermissions.c_str(), denyPermissions.c_str());
71 }
72
73 bool SmackRules::clear() const
74 {
75     return 0 == smack_accesses_clear(m_handle);
76 }
77
78 bool SmackRules::apply() const
79 {
80     return 0 == smack_accesses_apply(m_handle);
81 }
82
83 bool SmackRules::loadFromFile(const std::string &path)
84 {
85     int fd;
86     bool ret = true;
87
88     fd = TEMP_FAILURE_RETRY(open(path.c_str(), O_RDONLY));
89     if (fd == -1) {
90         LogError("Failed to open file: " << path);
91         return false;
92     }
93
94     if (smack_accesses_add_from_file(m_handle, fd)) {
95         LogError("Failed to load smack rules from file: " << path);
96         ret = false;
97     }
98
99     if (close(fd) == -1) {
100         // don't change the return code, the descriptor should be closed despite the error.
101         LogWarning("Error while closing the file: " << path << ", error: " << strerror(errno));
102     }
103
104     return ret;
105 }
106
107 bool SmackRules::saveToFile(const std::string &path) const
108 {
109     int fd;
110     bool ret = true;
111
112     fd = TEMP_FAILURE_RETRY(open(path.c_str(), O_CREAT | O_WRONLY | O_TRUNC, 0644));
113     if (fd == -1) {
114         LogError("Failed to create file: " << path);
115         return false;
116     }
117
118     if (smack_accesses_save(m_handle, fd)) {
119         LogError("Failed to save rules to file: " << path);
120         unlink(path.c_str());
121         ret = false;
122     }
123
124     if (close(fd) == -1) {
125         if (errno == EIO) {
126             LogError("I/O Error occured while closing the file: " << path << ", error: " << strerror(errno));
127             unlink(path.c_str());
128             return false;
129         } else {
130             // non critical error
131             // don't change the return code, the descriptor should be closed despite the error.
132             LogWarning("Error while closing the file: " << path << ", error: " << strerror(errno));
133         }
134     }
135
136     return ret;
137 }
138
139
140 bool 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         return false;
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         return false;
159     }
160
161     return addFromTemplate(templateRules, appId, pkgId);
162 }
163
164 bool 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             return false;
178         }
179
180         if (subject == SMACK_APP_LABEL_TEMPLATE) {
181             if (!generateAppLabel(appId, subject)) {
182                 LogError("Failed to generate app label from appId: " << appId);
183                 return false;
184             }
185         }
186
187         if (subject == SMACK_PKG_LABEL_TEMPLATE) {
188             if (!generatePkgLabel(pkgId, object)) {
189                 LogError("Failed to generate pkg label from pkgid: " << pkgId);
190                 return false;
191             }
192         }
193
194         if (object == SMACK_APP_LABEL_TEMPLATE) {
195             if (!generateAppLabel(appId, object)) {
196                 LogError("Failed to generate app label from appId: " << appId);
197                 return false;
198             }
199         }
200
201         if (object == SMACK_PKG_LABEL_TEMPLATE) {
202             if (!generatePkgLabel(pkgId, object)) {
203                 LogError("Failed to generate pkg label from pkgId: " << pkgId);
204                 return false;
205             }
206         }
207
208         if (!add(subject, object, permissions)) {
209             LogError("Failed to add rule: " << subject << " " << object << " " << permissions);
210             return false;
211         }
212     }
213
214     return true;
215 }
216
217 bool SmackRules::generatePackageCrossDeps(const std::vector<std::string> &pkgContents)
218 {
219     LogDebug ("Generating cross-package rules");
220
221     std::string subjectLabel, objectLabel;
222     std::string appsInPackagePerms = SMACK_APP_IN_PACKAGE_PERMS;
223
224     for (const auto &subject : pkgContents) {
225         for (const auto &object : pkgContents) {
226             if (object == subject)
227                 continue;
228
229             if (generateAppLabel(subject, subjectLabel) && generateAppLabel(object, objectLabel)) {
230                 LogDebug ("Trying to add rule subject: " << subjectLabel << " object: " << objectLabel
231                             << " perms: " << appsInPackagePerms);
232                 if (!add (subjectLabel, objectLabel, appsInPackagePerms)) {
233                     LogError ("Can't add in-package rule for subject: "
234                                 << subject << " and object: " << object);
235                     return false;
236                 }
237             }
238             else {
239                 LogError ("Failed to created smack labels for subject: "
240                             << subject << " and object: " << object);
241                 return false;
242             }
243         }
244     }
245
246     return true;
247 }
248
249 std::string SmackRules::getPackageRulesFilePath(const std::string &pkgId)
250 {
251     std::string path(tzplatform_mkpath3(TZ_SYS_SMACK, "accesses.d", ("pkg_" + pkgId).c_str()));
252     return path;
253 }
254
255 std::string SmackRules::getApplicationRulesFilePath(const std::string &appId)
256 {
257     std::string path(tzplatform_mkpath3(TZ_SYS_SMACK, "accesses.d", ("app_" +  appId).c_str()));
258     return path;
259 }
260
261 bool SmackRules::installApplicationRules(const std::string &appId, const std::string &pkgId,
262         const std::vector<std::string> &pkgContents) {
263     try {
264         SmackRules smackRules;
265         std::string appPath = getApplicationRulesFilePath(appId);
266
267         if (!smackRules.addFromTemplateFile(appId, pkgId)) {
268             LogError("Failed to load smack rules for appId: " << appId << " with pkgId: " << pkgId);
269             return false;
270         }
271
272         if (smack_smackfs_path() != NULL && !smackRules.apply()) {
273             LogError("Failed to apply application rules to kernel [app]");
274             return false;
275         }
276
277         if (!smackRules.saveToFile(appPath)) {
278             smackRules.clear();
279             return false;
280         }
281
282         if (!updatePackageRules(pkgId, pkgContents))
283         {
284             return false;
285         }
286
287         return true;
288     } catch (const std::bad_alloc &e) {
289         LogError("Out of memory while trying to install smack rules for appId: "
290                 << appId << "in pkgId: " << pkgId);
291         return false;
292     }
293 }
294
295 bool SmackRules::updatePackageRules(const std::string &pkgId, const std::vector<std::string> &pkgContents)
296 {
297     try {
298         SmackRules smackRules;
299         std::string pkgPath = getPackageRulesFilePath(pkgId);
300
301         if (!smackRules.generatePackageCrossDeps(pkgContents))
302         {
303             LogError("Failed to create application in-package cross dependencies");
304             return false;
305         }
306
307         if (smack_smackfs_path() != NULL && !smackRules.apply()) {
308              LogError("Failed to apply application rules to kernel [pkg]");
309              return false;
310          }
311
312          if (!smackRules.saveToFile(pkgPath)) {
313              smackRules.clear();
314              return false;
315          }
316
317          return true;
318     } catch (const std::bad_alloc &e) {
319         LogError("Out of memory while trying to install smack rules for pkgId: " << pkgId);
320         return false;
321     }
322 }
323
324 /* FIXME: Remove this function if real pkgId instead of "User" label will be used
325  * in generateAppLabel(). */
326 bool SmackRules::addMissingRulesFix()
327 {
328     DIR *dir;
329     struct dirent *ent;
330     SmackRules rules;
331     std::string path(tzplatform_mkpath(TZ_SYS_SMACK, "accesses.d"));
332
333     dir = opendir(path.c_str());
334     if (dir != NULL) {
335         while ((ent = readdir(dir))) {
336             if (ent->d_type == DT_REG) {
337                 rules.loadFromFile(tzplatform_mkpath3(TZ_SYS_SMACK, "accesses.d/", ent->d_name));
338                 // Do not check error here. If this fails we can't do anything anyway.
339             }
340         }
341         rules.apply();
342     }
343     else
344         return false;
345
346     closedir(dir);
347
348     return true;
349 }
350
351 bool SmackRules::uninstallPackageRules(const std::string &pkgId)
352 {
353     if (!uninstallRules(getPackageRulesFilePath(pkgId)))
354     {
355         LogError("Failed to uninstall application rules for pkgId: " << pkgId);
356         return false;
357     }
358
359     return true;
360 }
361
362 bool SmackRules::uninstallApplicationRules(const std::string &appId,
363         const std::string &pkgId, std::vector<std::string> pkgContents)
364 {
365     if (!uninstallRules (getApplicationRulesFilePath(appId)))
366     {
367         LogError("Failed to uninstall application rules for appId: " << appId);
368         return false;
369     }
370
371     if (!updatePackageRules(pkgId, pkgContents))
372     {
373         LogError("failed to update package rules for appId: " << appId
374                 << " pkgId: " << pkgId);
375         return false;
376     }
377
378     // FIXME: Reloading all rules:
379     SmackRules::addMissingRulesFix();
380
381     return true;
382 }
383
384 bool SmackRules::uninstallRules(const std::string &path)
385 {
386     if (access(path.c_str(), F_OK) == -1) {
387         if (errno == ENOENT) {
388             LogWarning("Smack rules not found in file: " << path);
389             return true;
390         }
391
392         LogWarning("Cannot access smack rules path: " << path);
393         return false;
394     }
395
396     try {
397         SmackRules rules;
398         if (rules.loadFromFile(path)) {
399             if (smack_smackfs_path() != NULL && !rules.clear()) {
400                 LogWarning("Failed to clear smack kernel rules from file: " << path);
401                 // don't stop uninstallation
402             }
403         } else {
404             LogWarning("Failed to load rules from file: " << path);
405             // don't stop uninstallation
406         }
407
408         if (unlink(path.c_str()) == -1) {
409             LogError("Failed to remove smack rules file: " << path);
410             return false;
411         }
412     } catch (const std::bad_alloc &e) {
413         LogError("Out of memory while trying to uninstall smack rules from path: " << path);
414         return false;
415     }
416     return true;
417 }
418
419 } // namespace SecurityManager
420