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 "seccomp_policy.h"
17 #include "plugin_adapter.h"
18 #include "securec.h"
19 #include "config_policy_utils.h"
20 
21 #ifdef WITH_SECCOMP_DEBUG
22 #include "init_utils.h"
23 #include "init_param.h"
24 #endif
25 
26 #include <dlfcn.h>
27 #include <sys/syscall.h>
28 #include <unistd.h>
29 #include <ctype.h>
30 #include <errno.h>
31 #include <assert.h>
32 #include <linux/audit.h>
33 #include <linux/seccomp.h>
34 #include <linux/filter.h>
35 #include <limits.h>
36 
37 #ifndef SECCOMP_SET_MODE_FILTER
38 #define SECCOMP_SET_MODE_FILTER  (1)
39 #endif
40 
41 #ifdef __aarch64__
42 #define FILTER_LIB_PATH_FORMAT "lib64/seccomp/lib%s_filter.z.so"
43 #define FILTER_LIB_PATH_PART "lib64/seccomp/lib"
44 #else
45 #define FILTER_LIB_PATH_FORMAT "lib/seccomp/lib%s_filter.z.so"
46 #define FILTER_LIB_PATH_PART "lib/seccomp/lib"
47 #endif
48 #define FILTER_NAME_FORMAT "g_%sSeccompFilter"
49 #define FILTER_SIZE_STRING "Size"
50 
51 typedef enum {
52     SECCOMP_SUCCESS,
53     INPUT_ERROR,
54     RETURN_NULL,
55     RETURN_ERROR,
56     RETURN_LENGTH_CHECK
57 } SeccompErrorCode;
58 
IsSupportFilterFlag(unsigned int filterFlag)59 static bool IsSupportFilterFlag(unsigned int filterFlag)
60 {
61     errno = 0;
62     long ret = syscall(__NR_seccomp, SECCOMP_SET_MODE_FILTER, filterFlag, NULL);
63     if (ret != -1 || errno != EFAULT) {
64         PLUGIN_LOGE("not support  seccomp flag %u", filterFlag);
65         return false;
66     }
67 
68     return true;
69 }
70 
InstallSeccompPolicy(const struct sock_filter * filter,size_t filterSize,unsigned int filterFlag)71 static bool InstallSeccompPolicy(const struct sock_filter* filter, size_t filterSize, unsigned int filterFlag)
72 {
73     if (filter == NULL) {
74         return false;
75     }
76 
77     unsigned int flag = 0;
78     struct sock_fprog prog = {
79         (unsigned short)filterSize,
80         (struct sock_filter*)filter
81     };
82 
83     if (IsSupportFilterFlag(SECCOMP_FILTER_FLAG_TSYNC) && (filterFlag & SECCOMP_FILTER_FLAG_TSYNC)) {
84         flag |= SECCOMP_FILTER_FLAG_TSYNC;
85     }
86 
87     if (IsSupportFilterFlag(SECCOMP_FILTER_FLAG_LOG) && (filterFlag & SECCOMP_FILTER_FLAG_LOG)) {
88         flag |= SECCOMP_FILTER_FLAG_LOG;
89     }
90 
91     if (syscall(__NR_seccomp, SECCOMP_SET_MODE_FILTER, flag, &prog) != 0) {
92         PLUGIN_LOGE("SetSeccompFilter failed");
93         return false;
94     }
95 
96     return true;
97 }
98 
GetFilterFileByName(const char * filterName,char * filterLibRealPath,unsigned int pathSize)99 static bool GetFilterFileByName(const char *filterName, char *filterLibRealPath, unsigned int pathSize)
100 {
101     size_t maxFilterNameLen = PATH_MAX - strlen(FILTER_LIB_PATH_FORMAT) + strlen("%s") - 1;
102     if (filterName == NULL || strlen(filterName) > maxFilterNameLen) {
103         return false;
104     }
105 
106     bool flag = false;
107     char filterLibPath[PATH_MAX] = {0};
108 
109     int rc = snprintf_s(filterLibPath, sizeof(filterLibPath), \
110                             strlen(filterName) + strlen(FILTER_LIB_PATH_FORMAT) - strlen("%s"), \
111                             FILTER_LIB_PATH_FORMAT, filterName);
112     if (rc == -1) {
113         return false;
114     }
115 
116     int seccompPathNum = 0;
117     CfgFiles *files = GetCfgFiles(filterLibPath);
118     for (int i = MAX_CFG_POLICY_DIRS_CNT - 1; files && i >= 0; i--) {
119         if (files->paths[i]) {
120             seccompPathNum++;
121         }
122     }
123 
124     // allow only one path to a seccomp shared library to avoid shared library replaced
125     if (seccompPathNum == 1 && files && files->paths[0]) {
126         if (memcpy_s(filterLibRealPath, pathSize, files->paths[0], strlen(files->paths[0]) + 1) == EOK) {
127             flag = true;
128         }
129     }
130     FreeCfgFiles(files);
131 
132     return flag;
133 }
134 
GetSeccompPolicy(const char * filterName,int ** handler,const char * filterLibRealPath,struct sock_fprog * prog)135 static int GetSeccompPolicy(const char *filterName, int **handler,
136                             const char *filterLibRealPath, struct sock_fprog *prog)
137 {
138     if (filterName == NULL || filterLibRealPath == NULL || handler == NULL || prog == NULL) {
139         return INPUT_ERROR;
140     }
141 
142     if (strstr(filterLibRealPath, FILTER_LIB_PATH_PART) == NULL) {
143         return INPUT_ERROR;
144     }
145 
146     char filterVaribleName[PATH_MAX] = {0};
147     struct sock_filter *filter = NULL;
148     size_t *filterSize = NULL;
149     void *policyHanlder = NULL;
150     int ret = SECCOMP_SUCCESS;
151     do {
152         int rc = snprintf_s(filterVaribleName, sizeof(filterVaribleName), \
153                     strlen(filterName) + strlen(FILTER_NAME_FORMAT) - strlen("%s"), FILTER_NAME_FORMAT, filterName);
154         if (rc == -1) {
155             return RETURN_ERROR;
156         }
157         char realPath[PATH_MAX] = { 0 };
158         realpath(filterLibRealPath, realPath);
159         policyHanlder = dlopen(realPath, RTLD_LAZY);
160         PLUGIN_CHECK(policyHanlder != NULL, return RETURN_ERROR, "dlopen error policyHanlder:NULL");
161 
162         filter = (struct sock_filter *)dlsym(policyHanlder, filterVaribleName);
163         if (filter == NULL) {
164             ret = RETURN_NULL;
165             break;
166         }
167 
168         size_t filterVaribleNameLen = strlen(filterVaribleName) + strlen(FILTER_SIZE_STRING) + 1;
169         if (filterVaribleNameLen > sizeof(filterVaribleName)) {
170             ret = RETURN_LENGTH_CHECK;
171             break;
172         }
173         rc = strcat_s(filterVaribleName, filterVaribleNameLen, FILTER_SIZE_STRING);
174         if (rc != 0) {
175             ret = RETURN_ERROR;
176             break;
177         }
178 
179         filterSize = (size_t *)dlsym(policyHanlder, filterVaribleName);
180         if (filterSize == NULL) {
181             ret = RETURN_NULL;
182             break;
183         }
184     } while (0);
185 
186     *handler = (int *)policyHanlder;
187     prog->filter = filter;
188     if (filterSize != NULL) {
189         prog->len = (unsigned short)(*filterSize);
190     }
191 
192     return ret;
193 }
194 
195 
IsEnableSeccomp(void)196 bool IsEnableSeccomp(void)
197 {
198     bool isEnableSeccompFlag = true;
199 #ifdef WITH_SECCOMP_DEBUG
200     char value[MAX_BUFFER_LEN] = {0};
201     unsigned int len = MAX_BUFFER_LEN;
202     if (SystemReadParam("persist.init.debug.seccomp.enable", value, &len) == 0) {
203         if (strncmp(value, "0", len) == 0) {
204             isEnableSeccompFlag = false;
205         }
206     }
207 #endif
208     return isEnableSeccompFlag;
209 }
210 
SetSeccompPolicyWithName(SeccompFilterType type,const char * filterName)211 bool SetSeccompPolicyWithName(SeccompFilterType type, const char *filterName)
212 {
213     if (filterName == NULL) {
214         return false;
215     }
216 
217 #ifdef WITH_SECCOMP_DEBUG
218     if (!IsEnableSeccomp()) {
219         return true;
220     }
221 #endif
222 
223     void *handler = NULL;
224     char filterLibRealPath[PATH_MAX] = {0};
225     struct sock_fprog prog;
226     bool ret = false;
227     const char *filterNamePtr = filterName;
228 
229     bool flag = GetFilterFileByName(filterNamePtr, filterLibRealPath, sizeof(filterLibRealPath));
230     if (!flag) {
231         if (type == SYSTEM_SA) {
232             filterNamePtr = SYSTEM_NAME;
233             flag = GetFilterFileByName(filterNamePtr, filterLibRealPath, sizeof(filterLibRealPath));
234             PLUGIN_CHECK(flag == true, return ret, "get filter name failed");
235         } else if (type == SYSTEM_OTHERS) {
236             return true;
237         } else {
238             PLUGIN_LOGE("get filter name failed");
239             return ret;
240         }
241     }
242 
243     int retCode = GetSeccompPolicy(filterNamePtr, (int **)&handler, filterLibRealPath, &prog);
244     if (retCode == SECCOMP_SUCCESS) {
245         ret = InstallSeccompPolicy(prog.filter, prog.len, SECCOMP_FILTER_FLAG_LOG);
246     } else {
247         PLUGIN_LOGE("get seccomp policy failed return is %d and path is %s", retCode, filterLibRealPath);
248     }
249 #ifndef COVERAGE_TEST
250     if (handler != NULL) {
251         dlclose(handler);
252     }
253 #endif
254     return ret;
255 }
256