• Home
  • Line#
  • Scopes#
  • Navigate#
  • Raw
  • Download
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