1 /*
2 * Copyright (c) 2023 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 <unistd.h>
17 #include <libgen.h>
18 #include <include/fts.h>
19 #include <linux/limits.h>
20 #include "src/callbacks.h"
21 #include "src/label_internal.h"
22 #include "policycoreutils.h"
23 #include "selinux_error.h"
24 #include "selinux_klog.h"
25 #include "selinux/restorecon.h"
26
27 static pthread_once_t g_fcOnce = PTHREAD_ONCE_INIT;
28 static struct selabel_handle *g_fcHandle = NULL;
29 static const char SYSTEM_FILE_CONTEXTS[] = "/system/etc/selinux/targeted/contexts/file_contexts";
30 static const char VENDOR_FILE_CONTEXTS[] = "/vendor/etc/selinux/targeted/contexts/file_contexts";
31 #define MAX_OPT_NUM 3 // system + vendor + digest
32
33 typedef struct selinux_opt SelinuxOptions;
34
SetFileContextsHandle(void)35 static void SetFileContextsHandle(void)
36 {
37 if (g_fcHandle != NULL) {
38 selinux_log(SELINUX_ERROR, "File_contexts handle already set\n");
39 return;
40 }
41
42 SelinuxOptions options[MAX_OPT_NUM] = {0};
43
44 unsigned int index = 0;
45 if (access(SYSTEM_FILE_CONTEXTS, R_OK) == 0) {
46 SelinuxOptions systemOption = {SELABEL_OPT_PATH, SYSTEM_FILE_CONTEXTS};
47 options[index++] = systemOption;
48 }
49 if (access(VENDOR_FILE_CONTEXTS, R_OK) == 0) {
50 SelinuxOptions vendorOption = {SELABEL_OPT_PATH, VENDOR_FILE_CONTEXTS};
51 options[index++] = vendorOption;
52 }
53
54 // default option of selabel_open
55 SelinuxOptions digestOption = {SELABEL_OPT_DIGEST, (char *)1};
56 options[index++] = digestOption;
57
58 g_fcHandle = selabel_open(SELABEL_CTX_FILE, options, index);
59 if (g_fcHandle == NULL) {
60 selinux_log(SELINUX_ERROR, "File_contexts handle open fail\n");
61 return;
62 }
63
64 selinux_restorecon_set_sehandle(g_fcHandle);
65 }
66
RestoreconCommon(const char * path,unsigned int flag,unsigned int nthreads)67 int RestoreconCommon(const char *path, unsigned int flag, unsigned int nthreads)
68 {
69 __selinux_once(g_fcOnce, SetFileContextsHandle);
70 if (g_fcHandle == NULL) {
71 selinux_log(SELINUX_ERROR, "File_contexts handle is null\n");
72 return -1;
73 }
74 return selinux_restorecon_parallel(path, flag, nthreads);
75 }
76
SelinuxSetCallback(void)77 static void SelinuxSetCallback(void)
78 {
79 SetSelinuxKmsgLevel(SELINUX_KERROR);
80 union selinux_callback cb;
81 cb.func_log = SelinuxKmsg;
82 selinux_set_callback(SELINUX_CB_LOG, cb);
83 }
84
RestoreconSb(const char * path,char * newSecontext)85 static int RestoreconSb(const char *path, char *newSecontext)
86 {
87 char *oldSecontext = NULL;
88 if (lgetfilecon(path, &oldSecontext) < 0) {
89 selinux_log(SELINUX_ERROR, "Get current secontext failed on: %s, errno: %s\n", path, strerror(errno));
90 return -SELINUX_GET_CONTEXT_ERROR;
91 }
92
93 if (strcmp(oldSecontext, newSecontext)) {
94 if (lsetfilecon(path, newSecontext) < 0) {
95 selinux_log(SELINUX_ERROR, "Set selinux context failed on: %s, errno: %s\n", path, strerror(errno));
96 freecon(oldSecontext);
97 return -SELINUX_SET_CONTEXT_ERROR;
98 }
99 }
100 freecon(oldSecontext);
101 return SELINUX_SUCC;
102 }
103
RestoreconRecurseFromParentDir(const char * realPath,char * newSecontext)104 static int RestoreconRecurseFromParentDir(const char *realPath, char *newSecontext)
105 {
106 char *paths[2] = {NULL, NULL};
107 paths[0] = strdup(realPath);
108 if (paths[0] == NULL) {
109 return -SELINUX_PTR_NULL;
110 }
111
112 FTS *fts = fts_open(paths, FTS_PHYSICAL | FTS_NOCHDIR, NULL);
113 if (fts == NULL) {
114 selinux_log(SELINUX_ERROR, "file_open failed on %s: %s\n", paths[0], strerror(errno));
115 free(paths[0]);
116 return -SELINUX_FTS_OPEN_ERROR;
117 }
118
119 FTSENT *ftsent = NULL;
120 int error = 0;
121 while ((ftsent = fts_read(fts)) != NULL) {
122 switch (ftsent->fts_info) {
123 case FTS_DC:
124 selinux_log(SELINUX_ERROR, "Fts ELOOP on %s\n", ftsent->fts_path);
125 (void)fts_close(fts);
126 free(paths[0]);
127 return -SELINUX_FTS_ELOOP;
128 case FTS_DP:
129 continue;
130 case FTS_DNR:
131 selinux_log(SELINUX_ERROR, "Read error on %s, errno: %s\n", ftsent->fts_path, strerror(errno));
132 error = -SELINUX_UNKNOWN_ERROR;
133 fts_set(fts, ftsent, FTS_SKIP);
134 continue;
135 case FTS_ERR:
136 selinux_log(SELINUX_ERROR, "Error on %s, errno: %s\n", ftsent->fts_path, strerror(errno));
137 error = -SELINUX_UNKNOWN_ERROR;
138 fts_set(fts, ftsent, FTS_SKIP);
139 continue;
140 case FTS_NS:
141 selinux_log(SELINUX_ERROR, "stat error on %s, errno: %s\n", ftsent->fts_path, strerror(errno));
142 error = -SELINUX_UNKNOWN_ERROR;
143 fts_set(fts, ftsent, FTS_SKIP);
144 continue;
145 case FTS_D:
146 default:
147 if (RestoreconSb(ftsent->fts_path, newSecontext) != 0) {
148 error = -SELINUX_RESTORECON_ERROR;
149 }
150 break;
151 }
152 }
153 (void)fts_close(fts);
154 free(paths[0]);
155 return error;
156 }
157
Restorecon(const char * path)158 int Restorecon(const char *path)
159 {
160 return RestoreconCommon(path, SELINUX_RESTORECON_REALPATH, 1);
161 }
162
RestoreconRecurse(const char * path)163 int RestoreconRecurse(const char *path)
164 {
165 return RestoreconCommon(path, SELINUX_RESTORECON_REALPATH | SELINUX_RESTORECON_RECURSE, 1);
166 }
167
RestoreconRecurseParallel(const char * path,unsigned int nthreads)168 int RestoreconRecurseParallel(const char *path, unsigned int nthreads)
169 {
170 return RestoreconCommon(path, SELINUX_RESTORECON_REALPATH | SELINUX_RESTORECON_RECURSE, nthreads);
171 }
172
RestoreconRecurseForce(const char * path)173 int RestoreconRecurseForce(const char *path)
174 {
175 return RestoreconCommon(path,
176 SELINUX_RESTORECON_REALPATH | SELINUX_RESTORECON_RECURSE | SELINUX_RESTORECON_IGNORE_DIGEST, 1);
177 }
178
179 /* Restorecon the path recursively, using parent directory's label */
RestoreconFromParentDir(const char * path)180 int RestoreconFromParentDir(const char *path)
181 {
182 static pthread_once_t fcOnce = PTHREAD_ONCE_INIT;
183 __selinux_once(fcOnce, SelinuxSetCallback);
184
185 if (path == NULL) {
186 return -SELINUX_ARG_INVALID;
187 }
188
189 // check selinux state, less than 1 is disabled
190 if (is_selinux_enabled() < 1) {
191 selinux_log(SELINUX_ERROR, "Selinux not enabled\n");
192 return -SELINUX_STAT_INVAILD;
193 }
194
195 char realPath[PATH_MAX + 1] = { 0x00 };
196 char parent[PATH_MAX + 1] = { 0x00 };
197 if (realpath(path, realPath) == NULL || realpath(path, parent) == NULL) {
198 selinux_log(SELINUX_ERROR, "Get real path failed: %s, errno: %s\n", path, strerror(errno));
199 return -SELINUX_PATH_INVAILD;
200 }
201
202 char *parentPath = dirname(parent);
203 char *parentSecontext = NULL;
204 if (lgetfilecon(parentPath, &parentSecontext) < 0) {
205 selinux_log(SELINUX_ERROR, "Get parent dir secontext failed: %s, errno: %s\n", parentPath, strerror(errno));
206 return -SELINUX_GET_CONTEXT_ERROR;
207 }
208 int ret = RestoreconRecurseFromParentDir(realPath, parentSecontext);
209 freecon(parentSecontext);
210 return ret;
211 }