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 }