Don't hardcode path to /etc/smack
[platform/core/security/security-manager.git] / src / server / service / 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 <sys/stat.h>
29 #include <sys/smack.h>
30 #include <fcntl.h>
31 #include <fstream>
32 #include <cstring>
33 #include <sstream>
34
35 #include <dpl/log/log.h>
36 #include <tzplatform_config.h>
37
38 #include "smack-rules.h"
39
40 namespace SecurityManager {
41
42 const char *const SMACK_APP_LABEL_TEMPLATE     = "~APP~";
43 const char *const APP_RULES_TEMPLATE_FILE_PATH = tzplatform_mkpath(TZ_SYS_SMACK, "app-rules-template.smack");
44
45 bool SmackRules::generateAppLabel(const std::string &appPkgId, std::string &label)
46 {
47     (void) appPkgId; //todo use pkgId to generate label
48     label = "User";
49     return true;
50 }
51
52 SmackRules::SmackRules()
53 {
54     if (smack_accesses_new(&m_handle) < 0) {
55         LogError("Failed to create smack_accesses handle");
56         throw std::bad_alloc();
57     }
58 }
59
60 SmackRules::~SmackRules() {
61     smack_accesses_free(m_handle);
62 }
63
64 bool SmackRules::add(const std::string &subject, const std::string &object,
65         const std::string &permissions)
66 {
67     return 0 == smack_accesses_add(m_handle, subject.c_str(), object.c_str(), permissions.c_str());
68 }
69
70 bool SmackRules::clear() const
71 {
72     return 0 == smack_accesses_clear(m_handle);
73 }
74
75 bool SmackRules::apply() const
76 {
77     return 0 == smack_accesses_apply(m_handle);
78 }
79
80 bool SmackRules::loadFromFile(const std::string &path)
81 {
82     int fd;
83     bool ret = true;
84
85     fd = TEMP_FAILURE_RETRY(open(path.c_str(), O_RDONLY));
86     if (fd == -1) {
87         LogError("Failed to open file: %s" << path);
88         return false;
89     }
90
91     if (smack_accesses_add_from_file(m_handle, fd)) {
92         LogError("Failed to load smack rules from file: %s" << path);
93         ret = false;
94     }
95
96     if (close(fd) == -1) {
97         // don't change the return code, the descriptor should be closed despite the error.
98         LogWarning("Error while closing the file: " << path << ", error: " << strerror(errno));
99     }
100
101     return ret;
102 }
103
104 bool SmackRules::saveToFile(const std::string &path) const
105 {
106     int fd;
107     bool ret = true;
108
109     fd = TEMP_FAILURE_RETRY(open(path.c_str(), O_CREAT | O_WRONLY | O_TRUNC, 0644));
110     if (fd == -1) {
111         LogError("Failed to create file: %s" << path);
112         return false;
113     }
114
115     if (smack_accesses_save(m_handle, fd)) {
116         LogError("Failed to save rules to file: %s" << path);
117         unlink(path.c_str());
118         ret = false;
119     }
120
121     if (close(fd) == -1) {
122         if (errno == EIO) {
123             LogError("I/O Error occured while closing the file: " << path << ", error: " << strerror(errno));
124             unlink(path.c_str());
125             return false;
126         } else {
127             // non critical error
128             // don't change the return code, the descriptor should be closed despite the error.
129             LogWarning("Error while closing the file: " << path << ", error: " << strerror(errno));
130         }
131     }
132
133     return ret;
134 }
135
136
137 bool SmackRules::addFromTemplateFile(const std::string &pkgId)
138 {
139     std::vector<std::string> templateRules;
140     std::string line;
141     std::ifstream templateRulesFile(APP_RULES_TEMPLATE_FILE_PATH);
142
143     if (!templateRulesFile.is_open()) {
144         LogError("Cannot open rules template file: " << APP_RULES_TEMPLATE_FILE_PATH);
145         return false;
146     }
147
148     while (std::getline(templateRulesFile, line)) {
149         templateRules.push_back(line);
150     }
151
152     if (templateRulesFile.bad()) {
153         LogError("Error reading template file: " << APP_RULES_TEMPLATE_FILE_PATH);
154         return false;
155     }
156
157     return addFromTemplate(templateRules, pkgId);
158 }
159
160 bool SmackRules::addFromTemplate(const std::vector<std::string> &templateRules,
161         const std::string &pkgId)
162 {
163     for (auto rule : templateRules) {
164         if (rule.empty())
165             continue;
166
167         std::stringstream stream(rule);
168         std::string subject, object, permissions;
169         stream >> subject >> object >> permissions;
170
171         if (stream.fail() || !stream.eof()) {
172             LogError("Invalid rule template: " << rule);
173             return false;
174         }
175
176         bool subjectIsTemplate = (subject == SMACK_APP_LABEL_TEMPLATE);
177         bool objectIsTemplate = (object == SMACK_APP_LABEL_TEMPLATE);
178
179         if (objectIsTemplate == subjectIsTemplate) {
180             LogError("Invalid rule template. Exactly one app label template expected: " << rule);
181             return false;
182         }
183
184         if (subjectIsTemplate) {
185             if (!generateAppLabel(pkgId, subject)) {
186                 LogError("Failed to generate app label from pkgid: " << pkgId);
187                 return false;
188             }
189         }
190
191         if (objectIsTemplate) {
192             if (!generateAppLabel(pkgId, object)) {
193                 LogError("Failed to generate app label from pkgid: " << pkgId);
194                 return false;
195             }
196         }
197
198         if (!add(subject, object, permissions)) {
199             LogError("Failed to add rule: " << subject << " " << object << " " << permissions);
200             return false;
201         }
202     }
203
204     return true;
205 }
206
207 std::string SmackRules::getPackageRulesFilePath(const std::string &pkgId)
208 {
209     std::string path(tzplatform_mkpath3(TZ_SYS_SMACK, "accesses.d", pkgId.c_str()));
210     return path;
211 }
212
213 bool SmackRules::installPackageRules(const std::string &pkgId) {
214     try {
215          SmackRules smackRules;
216          std::string path = getPackageRulesFilePath(pkgId);
217
218          if (!smackRules.addFromTemplateFile(pkgId)) {
219              LogError("Failed to load smack rules for pkgId " << pkgId);
220              return false;
221          }
222
223          if (smack_smackfs_path() != NULL && !smackRules.apply()) {
224              LogError("Failed to apply application rules to kernel");
225              return false;
226          }
227
228          if (!smackRules.saveToFile(path)) {
229              smackRules.clear();
230              return false;
231          }
232
233          return true;
234      } catch (const std::bad_alloc &e) {
235          LogError("Out of memory while trying to install smack rules for pkgId " << pkgId);
236          return false;
237      }
238 }
239
240 bool SmackRules::uninstallPackageRules(const std::string &pkgId) {
241     std::string path = getPackageRulesFilePath(pkgId);
242     if (access(path.c_str(), F_OK) == -1) {
243         if (errno == ENOENT) {
244             LogWarning("Smack rules were not installed for pkgId: " << pkgId);
245             return true;
246         }
247
248         LogWarning("Cannot access smack rules path: " << path);
249         return false;
250     }
251
252     try {
253         SmackRules rules;
254         if (rules.loadFromFile(path)) {
255             if (smack_smackfs_path() != NULL && !rules.clear()) {
256                 LogWarning("Failed to clear smack kernel rules for pkgId: " << pkgId);
257                 // don't stop uninstallation
258             }
259         } else {
260             LogWarning("Failed to load rules from file: " << path);
261             // don't stop uninstallation
262         }
263
264         if (unlink(path.c_str()) == -1) {
265             LogError("Failed to remove smack rules file: " << path);
266             return false;
267         }
268
269         return true;
270     } catch (const std::bad_alloc &e) {
271         LogError("Out of memory while trying to uninstall smack rules for pkgId: " << pkgId);
272         return false;
273     }
274 }
275
276 } // namespace SecurityManager
277