1 /*
2 * Copyright (c) 2022 Huawei Device Co., Ltd.
3 * Licensed under the Apache License, Version 2.0 (the "License");
4 * you may not use this file except in compliance with the License.
5 * You may obtain a copy of the License at
6 *
7 * http://www.apache.org/licenses/LICENSE-2.0
8 *
9 * Unless required by applicable law or agreed to in writing, software
10 * distributed under the License is distributed on an "AS IS" BASIS,
11 * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
12 * See the License for the specific language governing permissions and
13 * limitations under the License.
14 */
15
16 #include "hap_restorecon.h"
17
18 #include <cctype>
19 #include <cerrno>
20 #include <climits>
21 #include <clocale>
22 #include <cstdlib>
23 #include <fstream>
24 #include <istream>
25 #include <regex>
26 #include <sstream>
27 #include <streambuf>
28 #include <string>
29 #include <sys/stat.h>
30 #include <type_traits>
31 #include <unordered_map>
32 #include <utility>
33 #include <vector>
34
35 #include <include/fts.h>
36 #include <pthread.h>
37 #include "selinux/context.h"
38 #include "selinux/selinux.h"
39
40 #include "callbacks.h"
41 #include <selinux_internal.h>
42 #include "selinux_error.h"
43 #include "selinux_log.h"
44
45 using namespace selinux;
46
47 namespace {
48 static const std::string SEHAP_CONTEXTS_FILE = "/system/etc/selinux/targeted/contexts/sehap_contexts";
49 static const std::string APL_PREFIX = "apl=";
50 static const std::string NAME_PREFIX = "name=";
51 static const std::string DOMAIN_PREFIX = "domain=";
52 static const std::string TYPE_PREFIX = "type=";
53 static const char *DEFAULT_CONTEXT = "u:object_r:unlabeled:s0";
54 static const int CONTEXTS_LENGTH_MIN = 20; // sizeof("apl=x domain= type=")
55 static const int CONTEXTS_LENGTH_MAX = 1024;
56 static pthread_once_t FC_ONCE = PTHREAD_ONCE_INIT;
57 static std::unordered_map<std::string, struct SehapInfo> sehapContextsBuff;
58 } // namespace
59
SelinuxSetCallback()60 static void SelinuxSetCallback()
61 {
62 SetSelinuxHilogLevel(SELINUX_HILOG_ERROR);
63 union selinux_callback cb;
64 cb.func_log = SelinuxHilog;
65 selinux_set_callback(SELINUX_CB_LOG, cb);
66 }
67
CouldSkip(const std::string & line)68 static bool CouldSkip(const std::string &line)
69 {
70 if (line.size() <= CONTEXTS_LENGTH_MIN || line.size() > CONTEXTS_LENGTH_MAX) {
71 return true;
72 }
73 int i = 0;
74 while (isspace(line[i])) {
75 i++;
76 }
77 if (line[i] == '#') {
78 return true;
79 }
80 if (line.find(APL_PREFIX) == line.npos) {
81 return true;
82 }
83 return false;
84 }
85
DecodeString(std::string & line)86 static struct SehapInfo DecodeString(std::string &line)
87 {
88 std::stringstream input(line);
89 std::string tmp;
90 struct SehapInfo contextBuff;
91 bool aplVisit = false;
92 bool nameVisit = false;
93 bool domainVisit = false;
94 bool typeVisit = false;
95
96 while (input >> tmp) {
97 if (!aplVisit && (tmp.find(APL_PREFIX) != tmp.npos)) {
98 contextBuff.apl = tmp.substr(tmp.find(APL_PREFIX) + APL_PREFIX.size());
99 aplVisit = true;
100 } else if (!nameVisit && (tmp.find(NAME_PREFIX) != tmp.npos)) {
101 contextBuff.name = tmp.substr(tmp.find(NAME_PREFIX) + NAME_PREFIX.size());
102 nameVisit = true;
103 } else if (!domainVisit && (tmp.find(DOMAIN_PREFIX) != tmp.npos)) {
104 contextBuff.domain = tmp.substr(tmp.find(DOMAIN_PREFIX) + DOMAIN_PREFIX.size());
105 domainVisit = true;
106 } else if (!typeVisit && (tmp.find(TYPE_PREFIX) != tmp.npos)) {
107 contextBuff.type = tmp.substr(tmp.find(TYPE_PREFIX) + TYPE_PREFIX.size());
108 typeVisit = true;
109 }
110 }
111
112 return contextBuff;
113 }
114
CheckPath(const std::string & path)115 static bool CheckPath(const std::string &path)
116 {
117 std::regex pathPrefix1("^/data/app/el[1-4]/[0-9]+/(base|database)/.*");
118 std::regex pathPrefix2("^/data/accounts/account_0/appdata/.*");
119 if (std::regex_match(path, pathPrefix1) || std::regex_match(path, pathPrefix2)) {
120 return true;
121 }
122 return false;
123 }
124
CheckApl(const std::string & apl)125 static bool CheckApl(const std::string &apl)
126 {
127 if (apl == "system_core" || apl == "system_basic" || apl == "normal") {
128 return true;
129 }
130 return false;
131 }
132
HapContextsClear()133 static void HapContextsClear()
134 {
135 if (!sehapContextsBuff.empty()) {
136 sehapContextsBuff.clear();
137 }
138 }
139
HapContextsLoad()140 static bool HapContextsLoad()
141 {
142 // load sehap_contexts file
143 std::ifstream contextsFile(SEHAP_CONTEXTS_FILE);
144 if (contextsFile) {
145 HapContextsClear();
146 int lineNum = 0;
147 std::string line;
148 while (getline(contextsFile, line)) {
149 lineNum++;
150 if (CouldSkip(line)) {
151 continue;
152 }
153 struct SehapInfo tmpInfo = DecodeString(line);
154 if (!tmpInfo.apl.empty()) {
155 sehapContextsBuff.emplace(tmpInfo.apl + tmpInfo.name, tmpInfo);
156 } else {
157 selinux_log(SELINUX_INFO, "hap_contexts read fail in line %d\n", lineNum);
158 }
159 }
160 } else {
161 selinux_log(SELINUX_ERROR, "Load hap_contexts fail, no such file: %s\n", SEHAP_CONTEXTS_FILE.c_str());
162 return false;
163 }
164 selinux_log(SELINUX_INFO, "Load hap_contexts succes: %s\n", SEHAP_CONTEXTS_FILE.c_str());
165 contextsFile.close();
166 return true;
167 }
168
HapContext()169 HapContext::HapContext()
170 {
171 __selinux_once(FC_ONCE, SelinuxSetCallback);
172 }
173
~HapContext()174 HapContext::~HapContext() {}
175
TypeSet(std::unordered_map<std::string,SehapInfo>::iterator & iter,bool isDomain,context_t con)176 int HapContext::TypeSet(std::unordered_map<std::string, SehapInfo>::iterator &iter, bool isDomain, context_t con)
177 {
178 std::string type = "";
179 if (isDomain) {
180 type = iter->second.domain;
181 } else {
182 type = iter->second.type;
183 }
184 if (type.size() == 0) {
185 selinux_log(SELINUX_ERROR, "type is empty in contexts file");
186 return -SELINUX_ARG_INVALID;
187 }
188 if (context_type_set(con, type.c_str())) {
189 selinux_log(SELINUX_ERROR, "%s %s\n", GetErrStr(SELINUX_SET_CONTEXT_TYPE_ERROR), type.c_str());
190 return -SELINUX_SET_CONTEXT_TYPE_ERROR;
191 }
192 return SELINUX_SUCC;
193 }
194
HapContextsLookup(bool isDomain,const std::string & apl,const std::string & packageName,context_t con)195 int HapContext::HapContextsLookup(bool isDomain, const std::string &apl, const std::string &packageName, context_t con)
196 {
197 if (sehapContextsBuff.empty()) {
198 if (!HapContextsLoad()) {
199 return -SELINUX_CONTEXTS_FILE_LOAD_ERROR;
200 }
201 }
202
203 auto iter = sehapContextsBuff.find(std::string(apl) + std::string(packageName));
204 if (iter != sehapContextsBuff.end() && apl != "normal") {
205 return TypeSet(iter, isDomain, con);
206 } else {
207 iter = sehapContextsBuff.find(std::string(apl));
208 if (iter != sehapContextsBuff.end()) {
209 return TypeSet(iter, isDomain, con);
210 }
211 }
212 return -SELINUX_KEY_NOT_FOUND;
213 }
214
HapLabelLookup(const std::string & apl,const std::string & packageName,char ** secontextPtr)215 int HapContext::HapLabelLookup(const std::string &apl, const std::string &packageName, char **secontextPtr)
216 {
217 if (apl.empty()) {
218 return -SELINUX_ARG_INVALID;
219 }
220 *secontextPtr = strdup(DEFAULT_CONTEXT);
221 char *secontext = *secontextPtr;
222 context_t con = context_new(secontext);
223 if (con == nullptr) {
224 return -SELINUX_PTR_NULL;
225 }
226
227 int res = HapContextsLookup(false, apl, packageName, con);
228 if (res < 0) {
229 context_free(con);
230 return res;
231 }
232
233 secontext = context_str(con);
234 if (secontext == nullptr) {
235 context_free(con);
236 return -SELINUX_PTR_NULL;
237 }
238
239 // if new contexts is same as old
240 if (!strcmp(secontext, *secontextPtr)) {
241 context_free(con);
242 return SELINUX_SUCC;
243 }
244
245 // check whether the context is valid
246 if (security_check_context(secontext) < 0) {
247 context_free(con);
248 selinux_log(SELINUX_ERROR, "context: %s, %s\n", secontext, GetErrStr(SELINUX_CHECK_CONTEXT_ERROR));
249 return -SELINUX_CHECK_CONTEXT_ERROR;
250 }
251
252 freecon(*secontextPtr);
253 *secontextPtr = strdup(secontext);
254 if (!(*secontextPtr)) {
255 context_free(con);
256 return -SELINUX_PTR_NULL;
257 }
258
259 context_free(con);
260 return SELINUX_SUCC;
261 }
262
RestoreconSb(const std::string & pathname,const struct stat * sb,const std::string & apl,const std::string & packageName)263 int HapContext::RestoreconSb(const std::string &pathname, const struct stat *sb, const std::string &apl,
264 const std::string &packageName)
265 {
266 char *secontext = nullptr;
267 char *oldSecontext = nullptr;
268
269 if (lgetfilecon(pathname.c_str(), &oldSecontext) < 0) {
270 freecon(secontext);
271 freecon(oldSecontext);
272 return -SELINUX_GET_CONTEXT_ERROR;
273 }
274
275 int res = HapLabelLookup(apl, packageName, &secontext);
276 if (res < 0) {
277 freecon(secontext);
278 freecon(oldSecontext);
279 return res;
280 }
281
282 if (strcmp(oldSecontext, secontext)) {
283 if (lsetfilecon(pathname.c_str(), secontext) < 0) {
284 freecon(secontext);
285 freecon(oldSecontext);
286 return -SELINUX_SET_CONTEXT_ERROR;
287 }
288 }
289
290 freecon(secontext);
291 freecon(oldSecontext);
292 return SELINUX_SUCC;
293 }
294
HapFileRestorecon(std::vector<std::string> & pathNameOrig,const std::string & apl,const std::string & packageName,unsigned int flags)295 int HapContext::HapFileRestorecon(std::vector<std::string> &pathNameOrig, const std::string &apl,
296 const std::string &packageName, unsigned int flags)
297 {
298 if (apl.empty() || pathNameOrig.empty() || !CheckApl(apl)) {
299 return -SELINUX_ARG_INVALID;
300 }
301 bool failFlag = false;
302 for (auto pathname : pathNameOrig) {
303 int res = HapFileRestorecon(pathname.c_str(), apl, packageName, flags);
304 if (res != SELINUX_SUCC) {
305 failFlag = true;
306 selinux_log(SELINUX_ERROR, "HapFileRestorecon fail for path: %s, errorNo: %d", pathname.c_str(), res);
307 }
308 }
309 return failFlag ? -SELINUX_RESTORECON_ERROR : SELINUX_SUCC;
310 }
311
HapFileRestorecon(const std::string & pathNameOrig,const std::string & apl,const std::string & packageName,unsigned int flags)312 int HapContext::HapFileRestorecon(const std::string &pathNameOrig, const std::string &apl,
313 const std::string &packageName, unsigned int flags)
314 {
315 if (apl.empty() || pathNameOrig.empty() || !CheckApl(apl)) {
316 return -SELINUX_ARG_INVALID;
317 }
318 if (is_selinux_enabled() < 1) {
319 selinux_log(SELINUX_INFO, "Selinux not enbaled");
320 return SELINUX_SUCC;
321 }
322
323 struct stat sb;
324 char realPath[PATH_MAX];
325 if (realpath(pathNameOrig.c_str(), realPath) == nullptr) {
326 return -SELINUX_PATH_INVAILD;
327 }
328
329 if (!CheckPath(std::string(realPath))) {
330 return -SELINUX_PATH_INVAILD;
331 }
332
333 bool recurse = (flags & SELINUX_HAP_RESTORECON_RECURSE) ? true : false;
334 if (!recurse) {
335 if (lstat(realPath, &sb) < 0) {
336 return -SELINUX_STAT_INVAILD;
337 }
338
339 int res = RestoreconSb(realPath, &sb, apl, packageName);
340 if (res < 0) {
341 selinux_log(SELINUX_ERROR, "RestoreconSb failed");
342 }
343 return res;
344 }
345
346 char *paths[2] = {NULL, NULL};
347 paths[0] = static_cast<char *>(realPath);
348 int ftsFlags = FTS_PHYSICAL | FTS_NOCHDIR;
349 FTS *fts = fts_open(paths, ftsFlags, NULL);
350 if (fts == nullptr) {
351 selinux_log(SELINUX_ERROR, "%s on %s: %s\n", GetErrStr(SELINUX_FTS_OPEN_ERROR), paths[0], strerror(errno));
352 return -SELINUX_FTS_OPEN_ERROR;
353 }
354
355 FTSENT *ftsent = nullptr;
356 int error = 0;
357 while ((ftsent = fts_read(fts)) != NULL) {
358 switch (ftsent->fts_info) {
359 case FTS_DC:
360 selinux_log(SELINUX_ERROR, "%s on %s\n", GetErrStr(SELINUX_FTS_ELOOP), ftsent->fts_path);
361 (void)fts_close(fts);
362 return -SELINUX_FTS_ELOOP;
363 case FTS_DP:
364 continue;
365 case FTS_DNR:
366 selinux_log(SELINUX_ERROR, "Read error on %s, errorno: %s\n", ftsent->fts_path, strerror(errno));
367 fts_set(fts, ftsent, FTS_SKIP);
368 continue;
369 case FTS_ERR:
370 selinux_log(SELINUX_ERROR, "Error on %s, errorno: %s\n", ftsent->fts_path, strerror(errno));
371 fts_set(fts, ftsent, FTS_SKIP);
372 continue;
373 case FTS_NS:
374 selinux_log(SELINUX_ERROR, "stat error on %s, errorno: %s\n", ftsent->fts_path, strerror(errno));
375 fts_set(fts, ftsent, FTS_SKIP);
376 continue;
377 case FTS_D:
378 default:
379 error += RestoreconSb(ftsent->fts_path, ftsent->fts_statp, apl, packageName);
380 break;
381 }
382 }
383 (void)fts_close(fts);
384 return error;
385 }
386
HapDomainSetcontext(const std::string & apl,const std::string & packageName)387 int HapContext::HapDomainSetcontext(const std::string &apl, const std::string &packageName)
388 {
389 if (apl.empty() || !CheckApl(apl)) {
390 return -SELINUX_ARG_INVALID;
391 }
392
393 if (is_selinux_enabled() < 1) {
394 selinux_log(SELINUX_INFO, "Selinux not enbaled");
395 return SELINUX_SUCC;
396 }
397
398 char *typeContext = nullptr;
399 if (getcon(&typeContext)) {
400 return -SELINUX_GET_CONTEXT_ERROR;
401 }
402
403 context_t con = nullptr;
404 con = context_new(typeContext);
405 if (con == nullptr) {
406 return -SELINUX_PTR_NULL;
407 }
408 char *oldTypeContext = typeContext;
409
410 int res = HapContextsLookup(true, apl, packageName, con);
411 if (res < 0) {
412 freecon(oldTypeContext);
413 context_free(con);
414 return res;
415 }
416
417 typeContext = context_str(con);
418 if (typeContext == nullptr) {
419 freecon(oldTypeContext);
420 context_free(con);
421 return -SELINUX_PTR_NULL;
422 }
423
424 selinux_log(SELINUX_INFO, "Hap type for %s is changing from %s to %s\n", packageName.c_str(), oldTypeContext,
425 typeContext);
426
427 if (security_check_context(typeContext) < 0) {
428 freecon(oldTypeContext);
429 context_free(con);
430 selinux_log(SELINUX_ERROR, "context: %s, %s\n", typeContext, GetErrStr(SELINUX_CHECK_CONTEXT_ERROR));
431 return -SELINUX_CHECK_CONTEXT_ERROR;
432 }
433
434 if (strcmp(typeContext, oldTypeContext)) {
435 if (setcon(typeContext) < 0) {
436 freecon(oldTypeContext);
437 context_free(con);
438 return -SELINUX_SET_CONTEXT_ERROR;
439 }
440 }
441 selinux_log(SELINUX_INFO, "Hap setcon finish for %s\n", packageName.c_str());
442
443 freecon(oldTypeContext);
444 context_free(con);
445 return SELINUX_SUCC;
446 }
447