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
51typedef enum {
52    SECCOMP_SUCCESS,
53    INPUT_ERROR,
54    RETURN_NULL,
55    RETURN_ERROR,
56    RETURN_LENGTH_CHECK
57} SeccompErrorCode;
58
59static 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
71static 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
99static 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
135static 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
196bool 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
211bool 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