1 /* selinux - core functions for maintaining SELinux labeling
2    Copyright (C) 2012-2023 Free Software Foundation, Inc.
3 
4    This program is free software: you can redistribute it and/or modify
5    it under the terms of the GNU General Public License as published by
6    the Free Software Foundation, either version 3 of the License, or
7    (at your option) any later version.
8 
9    This program is distributed in the hope that it will be useful,
10    but WITHOUT ANY WARRANTY; without even the implied warranty of
11    MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
12    GNU General Public License for more details.
13 
14    You should have received a copy of the GNU General Public License
15    along with this program.  If not, see <https://www.gnu.org/licenses/>.  */
16 
17 /* Written by Daniel Walsh <dwalsh@redhat.com> */
18 
19 #include <config.h>
20 #include <selinux/label.h>
21 #include <selinux/context.h>
22 #include <sys/types.h>
23 
24 #include "system.h"
25 #include "canonicalize.h"
26 #include "xfts.h"
27 #include "selinux.h"
28 
29 #if HAVE_SELINUX_LABEL_H
30 
31 # if ! HAVE_MODE_TO_SECURITY_CLASS
32 /*
33   This function has been added to libselinux-2.1.12-5, but is here
34   for support with older versions of SELinux
35 
36   Translates a mode into an Internal SELinux security_class definition.
37   Returns 0 on failure, with errno set to EINVAL.
38 */
39 static security_class_t
mode_to_security_class(mode_t m)40 mode_to_security_class (mode_t m)
41 {
42 
43   if (S_ISREG (m))
44     return string_to_security_class ("file");
45   if (S_ISDIR (m))
46     return string_to_security_class ("dir");
47   if (S_ISCHR (m))
48     return string_to_security_class ("chr_file");
49   if (S_ISBLK (m))
50     return string_to_security_class ("blk_file");
51   if (S_ISFIFO (m))
52     return string_to_security_class ("fifo_file");
53   if (S_ISLNK (m))
54     return string_to_security_class ("lnk_file");
55   if (S_ISSOCK (m))
56     return string_to_security_class ("sock_file");
57 
58   errno = EINVAL;
59   return 0;
60 }
61 # endif
62 
63 /*
64   This function takes a PATH and a MODE and then asks SELinux what the label
65   of the path object would be if the current process label created it.
66   It then returns the label.
67 
68   Returns -1 on failure.  errno will be set appropriately.
69 */
70 
71 static int
computecon(char const * path,mode_t mode,char ** con)72 computecon (char const *path, mode_t mode, char **con)
73 {
74   char *scon = nullptr;
75   char *tcon = nullptr;
76   security_class_t tclass;
77   int rc = -1;
78 
79   char *dir = dir_name (path);
80   if (!dir)
81     goto quit;
82   if (getcon (&scon) < 0)
83     goto quit;
84   if (getfilecon (dir, &tcon) < 0)
85     goto quit;
86   tclass = mode_to_security_class (mode);
87   if (!tclass)
88     goto quit;
89   rc = security_compute_create (scon, tcon, tclass, con);
90 
91  quit:;
92   int err = errno;
93   free (dir);
94   freecon (scon);
95   freecon (tcon);
96   errno = err;
97   return rc;
98 }
99 
100 /*
101   This function takes a handle, path and mode, it calls computecon to get the
102   label of the path object if the current process created it, then it calls
103   selabel_lookup to get the default type for the object.  It substitutes the
104   default type into label.  It tells the SELinux Kernel to label all new file
105   system objects created by the current process with this label.
106 
107   Returns -1 on failure.  errno will be set appropriately.
108 */
109 int
defaultcon(struct selabel_handle * selabel_handle,char const * path,mode_t mode)110 defaultcon (struct selabel_handle *selabel_handle,
111             char const *path, mode_t mode)
112 {
113   int rc = -1;
114   char *scon = nullptr;
115   char *tcon = nullptr;
116   context_t scontext = 0, tcontext = 0;
117   char const *contype;
118   char const *constr;
119   char *newpath = nullptr;
120 
121   if (! IS_ABSOLUTE_FILE_NAME (path))
122     {
123       /* Generate absolute name as required by subsequent selabel_lookup.  */
124       newpath = canonicalize_filename_mode (path, CAN_MISSING);
125       if (! newpath)
126         goto quit;
127       path = newpath;
128     }
129 
130   if (selabel_lookup (selabel_handle, &scon, path, mode) < 0)
131     {
132       /* "No such file or directory" is a confusing error,
133          when processing files, when in fact it was the
134          associated default context that was not found.
135          Therefore map the error to something more appropriate
136          to the context in which we're using selabel_lookup().  */
137       if (errno == ENOENT)
138         errno = ENODATA;
139       goto quit;
140     }
141   if (computecon (path, mode, &tcon) < 0)
142     goto quit;
143   if (!(scontext = context_new (scon)))
144     goto quit;
145   if (!(tcontext = context_new (tcon)))
146     goto quit;
147 
148   if (!(contype = context_type_get (scontext)))
149     goto quit;
150   if (context_type_set (tcontext, contype))
151     goto quit;
152   if (!(constr = context_str (tcontext)))
153     goto quit;
154 
155   rc = setfscreatecon (constr);
156 
157  quit:;
158   int err = errno;
159   context_free (scontext);
160   context_free (tcontext);
161   freecon (scon);
162   freecon (tcon);
163   free (newpath);
164   errno = err;
165   return rc;
166 }
167 
168 /*
169   If SELABEL_HANDLE is null, set PATH's label to the default to the
170   local process.  Otherwise use selabel_lookup to determine the
171   default label, extract the type field and then modify the file
172   system object.  Note only the type field is updated, thus preserving MLS
173   levels and user identity etc. of the PATH.
174 
175   Returns -1 on failure.  errno will be set appropriately.
176 */
177 static int
restorecon_private(struct selabel_handle * selabel_handle,char const * path)178 restorecon_private (struct selabel_handle *selabel_handle, char const *path)
179 {
180   int rc = -1;
181   struct stat sb;
182   char *scon = nullptr;
183   char *tcon = nullptr;
184   context_t scontext = 0, tcontext = 0;
185   char const *contype;
186   char const *constr;
187   int fd;
188 
189   if (!selabel_handle)
190     {
191       if (getfscreatecon (&tcon) < 0)
192         return rc;
193       if (!tcon)
194         {
195           errno = ENODATA;
196           return rc;
197         }
198       rc = lsetfilecon (path, tcon);
199       int err = errno;
200       freecon (tcon);
201       errno = err;
202       return rc;
203     }
204 
205   fd = open (path, O_RDONLY | O_NOFOLLOW);
206   if (fd == -1 && (errno != ELOOP))
207     goto quit;
208 
209   if (fd != -1)
210     {
211       if (fstat (fd, &sb) < 0)
212         goto quit;
213     }
214   else
215     {
216       if (lstat (path, &sb) < 0)
217         goto quit;
218     }
219 
220   if (selabel_lookup (selabel_handle, &scon, path, sb.st_mode) < 0)
221     {
222       /* "No such file or directory" is a confusing error,
223          when processing files, when in fact it was the
224          associated default context that was not found.
225          Therefore map the error to something more appropriate
226          to the context in which we're using selabel_lookup.  */
227       if (errno == ENOENT)
228         errno = ENODATA;
229       goto quit;
230     }
231   if (!(scontext = context_new (scon)))
232     goto quit;
233 
234   if (fd != -1)
235     {
236       if (fgetfilecon (fd, &tcon) < 0)
237         goto quit;
238     }
239   else
240     {
241       if (lgetfilecon (path, &tcon) < 0)
242         goto quit;
243     }
244 
245   if (!(tcontext = context_new (tcon)))
246     goto quit;
247 
248   if (!(contype = context_type_get (scontext)))
249     goto quit;
250   if (context_type_set (tcontext, contype))
251     goto quit;
252   if (!(constr = context_str (tcontext)))
253     goto quit;
254 
255   if (fd != -1)
256     rc = fsetfilecon (fd, constr);
257   else
258     rc = lsetfilecon (path, constr);
259 
260  quit:;
261   int err = errno;
262   if (fd != -1)
263     close (fd);
264   context_free (scontext);
265   context_free (tcontext);
266   freecon (scon);
267   freecon (tcon);
268   errno = err;
269   return rc;
270 }
271 
272 /*
273   This function takes three parameters:
274 
275   SELABEL_HANDLE for selabel_lookup, or null to preserve.
276 
277   PATH of an existing file system object.
278 
279   A RECURSE boolean which if the file system object is a directory, will
280   call restorecon_private on every file system object in the directory.
281 
282   Return false on failure.  errno will be set appropriately.
283 */
284 bool
restorecon(struct selabel_handle * selabel_handle,char const * path,bool recurse)285 restorecon (struct selabel_handle *selabel_handle,
286             char const *path, bool recurse)
287 {
288   char *newpath = nullptr;
289 
290   if (! IS_ABSOLUTE_FILE_NAME (path))
291     {
292       /* Generate absolute name as required by subsequent selabel_lookup.
293          When RECURSE, this also generates absolute names in the
294          fts entries, which may be quicker to process in any case.  */
295       newpath = canonicalize_filename_mode (path, CAN_MISSING);
296       if (! newpath)
297         return false;
298       path = newpath;
299     }
300 
301   if (! recurse)
302     {
303       bool ok = restorecon_private (selabel_handle, path) != -1;
304       int err = errno;
305       free (newpath);
306       errno = err;
307       return ok;
308     }
309 
310   char const *ftspath[2] = { path, nullptr };
311   FTS *fts = xfts_open ((char *const *) ftspath, FTS_PHYSICAL, nullptr);
312 
313   int err = 0;
314   for (FTSENT *ent; (ent = fts_read (fts)); )
315     if (restorecon_private (selabel_handle, fts->fts_path) < 0)
316       err = errno;
317 
318   if (errno != 0)
319     err = errno;
320 
321   if (fts_close (fts) != 0)
322     err = errno;
323 
324   free (newpath);
325   return !err;
326 }
327 #endif
328