• 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 "paraperm_checker.h"
17 #include <fcntl.h>
18 #include <fstream>
19 #include <regex>
20 #include <securec.h>
21 #include <selinux_internal.h>
22 #include <sstream>
23 #include <string>
24 #include <sys/socket.h>
25 #include <unistd.h>
26 #include "callbacks.h"
27 #include "selinux_error.h"
28 #include "selinux_klog.h"
29 #include "contexts_trie.h"
30 
31 using namespace Selinux;
32 
33 namespace {
34 static const std::string PARAMETER_CONTEXTS_FILE = "/system/etc/selinux/targeted/contexts/parameter_contexts";
35 static const std::string TYPE_PREFIX = "u:object_r:";
36 static const char *DEFAULT_CONTEXT = "u:object_r:default_param:s0";
37 static pthread_once_t FC_ONCE = PTHREAD_ONCE_INIT;
38 static std::unique_ptr<ParamContextsTrie> g_contextsTrie = nullptr;
39 static ParamContextsList *g_contextsList = nullptr;
40 static const int CONTEXTS_LENGTH_MIN = 16; // sizeof("x u:object_r:x:s0")
41 static const int CONTEXTS_LENGTH_MAX = 1024;
42 } // namespace
43 
44 struct AuditMsg {
45     const ucred *ucred;
46     const char *name;
47 };
48 
SelinuxAuditCallback(void * data,security_class_t cls,char * buf,size_t len)49 static int SelinuxAuditCallback(void *data, security_class_t cls, char *buf, size_t len)
50 {
51     if (data == nullptr || buf == nullptr) {
52         return -1;
53     }
54     auto *msg = reinterpret_cast<AuditMsg *>(data);
55     if (!msg->name || !msg->ucred) {
56         selinux_log(SELINUX_ERROR, "Selinux audit msg invalid argument\n");
57         return -1;
58     }
59     if (snprintf_s(buf, len, len - 1, "parameter=%s pid=%d uid=%d gid=%d", msg->name, msg->ucred->pid, msg->ucred->uid,
60                    msg->ucred->gid) <= 0) {
61         return -1;
62     }
63     return 0;
64 }
65 
SelinuxSetCallback()66 static void SelinuxSetCallback()
67 {
68     union selinux_callback cb;
69     cb.func_log = SelinuKLog;
70     selinux_set_callback(SELINUX_CB_LOG, cb);
71     cb.func_audit = SelinuxAuditCallback;
72     selinux_set_callback(SELINUX_CB_AUDIT, cb);
73 }
74 
ReleaseMem()75 static void ReleaseMem()
76 {
77     DestroyParamList(&g_contextsList);
78     if (g_contextsTrie != nullptr) {
79         g_contextsTrie->Clear();
80         g_contextsTrie = nullptr;
81     }
82 }
83 
CheckParaNameValid(const char * paraName)84 static int CheckParaNameValid(const char *paraName)
85 {
86     if (paraName == nullptr) {
87         return -SELINUX_PTR_NULL;
88     }
89     std::string para = paraName;
90     if (para.empty() || para[0] == '.' || para[para.size() - 1] == '.' || para.find("..") != std::string::npos) {
91         return -SELINUX_ARG_INVALID;
92     }
93     std::regex paraReg("^[a-zA-Z0-9_\\-@:](([\\.]{0,1})[a-zA-Z0-9_\\-@:])*");
94     if (std::regex_match(para, paraReg)) {
95         return SELINUX_SUCC;
96     }
97     return -SELINUX_ARG_INVALID;
98 }
99 
CouldSkip(const std::string & line)100 static bool CouldSkip(const std::string &line)
101 {
102     if (line.size() < CONTEXTS_LENGTH_MIN || line.size() > CONTEXTS_LENGTH_MAX) {
103         return true;
104     }
105     int i = 0;
106     while (isspace(line[i])) {
107         i++;
108     }
109     if (line[i] == '#') {
110         return true;
111     }
112     if (line.find(TYPE_PREFIX) == line.npos) {
113         return true;
114     }
115     return false;
116 }
117 
StartWith(const std::string & dst,const std::string & prefix)118 static bool StartWith(const std::string &dst, const std::string &prefix)
119 {
120     return dst.compare(0, prefix.size(), prefix) == 0;
121 }
122 
DecodeString(const std::string & line)123 static struct ParameterInfo DecodeString(const std::string &line)
124 {
125     std::stringstream input(line);
126     struct ParameterInfo contextBuff = {"", ""};
127     std::string name;
128     if (input >> name) {
129         contextBuff.paraName = name;
130     }
131     std::string context;
132     if (input >> context) {
133         if (StartWith(context, TYPE_PREFIX)) {
134             contextBuff.paraContext = context;
135         }
136     }
137     return contextBuff;
138 }
139 
InsertContextsList(const ParameterInfo & tmpInfo,ParamContextsList ** head)140 static bool InsertContextsList(const ParameterInfo &tmpInfo, ParamContextsList **head)
141 {
142     ParamContextsList *node = (ParamContextsList *)malloc(sizeof(ParamContextsList));
143     if (node == nullptr) {
144         return false;
145     }
146     struct ParameterNode contextBuff = {nullptr, nullptr};
147     contextBuff.paraName = strdup(tmpInfo.paraName.c_str());
148     contextBuff.paraContext = strdup(tmpInfo.paraContext.c_str());
149     node->info = contextBuff;
150     node->next = nullptr;
151     (*head)->next = node;
152     *head = (*head)->next;
153     return true;
154 }
155 
ParameterContextsLoad()156 static bool ParameterContextsLoad()
157 {
158     ReleaseMem();
159     std::ifstream contextsFile(PARAMETER_CONTEXTS_FILE);
160     if (!contextsFile) {
161         selinux_log(SELINUX_ERROR, "Load parameter_contexts fail, no such file: %s\n", PARAMETER_CONTEXTS_FILE.c_str());
162         return false;
163     }
164     g_contextsTrie = std::make_unique<ParamContextsTrie>();
165     g_contextsList = (ParamContextsList *)malloc(sizeof(ParamContextsList));
166     if (g_contextsList == nullptr) {
167         selinux_log(SELINUX_ERROR, "malloc param info list head fail\n");
168         return false;
169     }
170     ParamContextsList *head = g_contextsList;
171     int lineNum = 0;
172     std::string line;
173     while (getline(contextsFile, line)) {
174         lineNum++;
175         if (CouldSkip(line))
176             continue;
177         struct ParameterInfo tmpInfo = DecodeString(line);
178         if (tmpInfo.paraContext.empty() || tmpInfo.paraName.empty()) {
179             selinux_log(SELINUX_ERROR, "parameter_contexts read fail in line %d\n", lineNum);
180             continue;
181         }
182         if (!g_contextsTrie->Insert(tmpInfo.paraName, tmpInfo.paraContext)) {
183             selinux_log(SELINUX_ERROR, "insert contexts trie node fail\n");
184             contextsFile.close();
185             ReleaseMem();
186             return false;
187         }
188         if (!InsertContextsList(tmpInfo, &head)) {
189             selinux_log(SELINUX_ERROR, "malloc param info list node fail\n");
190             contextsFile.close();
191             ReleaseMem();
192             return false;
193         }
194     }
195     head = g_contextsList->next;
196     free(g_contextsList);
197     g_contextsList = head;
198     selinux_log(SELINUX_INFO, "Load parameter_contexts succes: %s\n", PARAMETER_CONTEXTS_FILE.c_str());
199     contextsFile.close();
200     return true;
201 }
202 
CheckPerm(const std::string & paraName,const char * srcContext,const char * destContext,const ucred & uc)203 static int CheckPerm(const std::string &paraName, const char *srcContext, const char *destContext, const ucred &uc)
204 {
205     if (srcContext == nullptr || destContext == nullptr) {
206         selinux_log(SELINUX_INFO, "context empty!\n");
207         return -SELINUX_PTR_NULL;
208     }
209     selinux_log(SELINUX_INFO, "srcContext[%s] is setting param[%s] destContext[%s]\n", srcContext, paraName.c_str(),
210                 destContext);
211     AuditMsg msg;
212     msg.name = paraName.c_str();
213     msg.ucred = &uc;
214     int res = selinux_check_access(srcContext, destContext, "parameter_service", "set", &msg);
215     return res == 0 ? SELINUX_SUCC : -SELINUX_PERMISSION_DENY;
216 }
217 
SetSelinuxLogCallback()218 void SetSelinuxLogCallback()
219 {
220     __selinux_once(FC_ONCE, SelinuxSetCallback);
221     return;
222 }
223 
DestroyParamList(ParamContextsList ** list)224 void DestroyParamList(ParamContextsList **list)
225 {
226     if (list == nullptr) {
227         return;
228     }
229     ParamContextsList *tmpNode;
230     ParamContextsList *listHead = *list;
231     while (listHead != nullptr) {
232         tmpNode = listHead->next;
233         free(listHead->info.paraName);
234         listHead->info.paraName = nullptr;
235         free(listHead->info.paraContext);
236         listHead->info.paraContext = nullptr;
237         free(listHead);
238         listHead = tmpNode;
239     }
240     *list = nullptr;
241     return;
242 }
243 
GetParamList()244 ParamContextsList *GetParamList()
245 {
246     if (g_contextsList == nullptr) {
247         if (!ParameterContextsLoad()) {
248             return nullptr;
249         }
250     }
251     return g_contextsList;
252 }
253 
GetParamLabel(const char * paraName,char ** context)254 int GetParamLabel(const char *paraName, char **context)
255 {
256     if (paraName == nullptr || context == nullptr) {
257         return -SELINUX_PTR_NULL;
258     }
259 
260     int ret = CheckParaNameValid(paraName);
261     if (ret != 0) {
262         selinux_log(SELINUX_ERROR, "paraName invalid!\n");
263         return ret;
264     }
265 
266     if (g_contextsTrie == nullptr) {
267         if (!ParameterContextsLoad()) {
268             return -SELINUX_CONTEXTS_FILE_LOAD_ERROR;
269         }
270     }
271 
272     if (!g_contextsTrie->Search(std::string(paraName), context)) {
273         return -SELINUX_KEY_NOT_FOUND;
274     }
275     selinux_log(SELINUX_INFO, "find context: %s\n", *context);
276     return SELINUX_SUCC;
277 }
278 
ReadParamCheck(const char * paraName)279 int ReadParamCheck(const char *paraName)
280 {
281     int ret = CheckParaNameValid(paraName);
282     if (ret != 0) {
283         selinux_log(SELINUX_ERROR, "paraName invalid!\n");
284         return ret;
285     }
286 
287     char *srcContext = nullptr;
288     int rc = getcon(&srcContext);
289     if (rc < 0) {
290         selinux_log(SELINUX_ERROR, "getcon failed!\n");
291         return -SELINUX_GET_CONTEXT_ERROR;
292     }
293 
294     AuditMsg msg;
295     msg.name = paraName;
296     ucred uc = {.pid = getpid(), .uid = getuid(), .gid = getgid()};
297     msg.ucred = &uc;
298     char *destContext = nullptr;
299     if (GetParamLabel(paraName, &destContext) != 0) {
300         destContext = strdup(DEFAULT_CONTEXT);
301     }
302     if (srcContext == nullptr || destContext == nullptr) {
303         freecon(srcContext);
304         return -SELINUX_PTR_NULL;
305     }
306     selinux_log(SELINUX_INFO, "srcContext[%s] is reading param[%s] destContext[%s]\n", srcContext, paraName,
307                 destContext);
308     int res = selinux_check_access(srcContext, destContext, "file", "read", &msg);
309     freecon(srcContext);
310     free(destContext);
311     return res == 0 ? SELINUX_SUCC : -SELINUX_PERMISSION_DENY;
312 }
313 
SetParamCheck(const char * paraName,struct ucred * uc)314 int SetParamCheck(const char *paraName, struct ucred *uc)
315 {
316     if (paraName == nullptr || uc == nullptr) {
317         return -SELINUX_PTR_NULL;
318     }
319 
320     int ret = CheckParaNameValid(paraName);
321     if (ret != 0) {
322         selinux_log(SELINUX_ERROR, "paraName invalid!\n");
323         return ret;
324     }
325 
326     char *srcContext = nullptr;
327 
328     int rc = getpidcon(uc->pid, &srcContext);
329     if (rc < 0) {
330         selinux_log(SELINUX_ERROR, "getpidcon failed!\n");
331         return -SELINUX_GET_CONTEXT_ERROR;
332     }
333     char *destContext = nullptr;
334     if (GetParamLabel(paraName, &destContext) != 0) {
335         destContext = strdup(DEFAULT_CONTEXT);
336     }
337     int res = CheckPerm(std::string(paraName), srcContext, destContext, *uc);
338     freecon(srcContext);
339     free(destContext);
340     return res;
341 }
342