1 /* @(#)xattr.c	1.24 20/10/20 Copyright 2003-2020 J. Schilling */
2 #include <schily/mconfig.h>
3 #ifndef lint
4 static	UConst char sccsid[] =
5 	"@(#)xattr.c	1.24 20/10/20 Copyright 2003-2020 J. Schilling";
6 #endif
7 /*
8  *	Handle Extended File Attributes on Linux
9  *
10  *	Copyright (c) 2003-2020 J. Schilling
11  *	Thanks to Anreas Gr�nbacher <agruen@suse.de> for the
12  *	first implemenation.
13  */
14 /*
15  * The contents of this file are subject to the terms of the
16  * Common Development and Distribution License, Version 1.0 only
17  * (the "License").  You may not use this file except in compliance
18  * with the License.
19  *
20  * See the file CDDL.Schily.txt in this distribution for details.
21  * A copy of the CDDL is also available via the Internet at
22  * http://www.opensource.org/licenses/cddl1.txt
23  *
24  * When distributing Covered Code, include this CDDL HEADER in each
25  * file and include the License file CDDL.Schily.txt from this distribution.
26  */
27 
28 #include <schily/stdio.h>
29 #include <schily/stdlib.h>
30 #include <schily/errno.h>
31 #include <schily/string.h>
32 #if defined(HAVE_SYS_XATTR_H)
33 #include <sys/xattr.h>
34 #else
35 #if defined(HAVE_ATTR_XATTR_H)
36 #include <attr/xattr.h>
37 #endif	/* HAVE_ATTR_XATTR_H */
38 #endif	/* HAVE_SYS_XATTR_H */
39 #include "star.h"
40 #include <schily/standard.h>
41 #include <schily/unistd.h>
42 #include <schily/fcntl.h>	/* For open() with hop_dirs() */
43 #define	GT_COMERR		/* #define comerr gtcomerr */
44 #define	GT_ERROR		/* #define error gterror   */
45 #include <schily/schily.h>
46 #include "starsubs.h"
47 #include "checkerr.h"
48 
49 #if defined(USE_XATTR) && \
50 	(defined(HAVE_LISTXATTR) || defined(HAVE_LLISTXATTR)) && \
51 	(defined(HAVE_GETXATTR) || defined(HAVE_LGETXATTR))
52 
53 LOCAL	ssize_t	llgetxattr	__PR((char *path, const char *name,
54 					void *value, size_t size));
55 LOCAL	int	llsetxattr	__PR((char *path, const char *name,
56 					const void *value, size_t size,
57 					int flags));
58 LOCAL	ssize_t	lllistxattr	__PR((char *path, char *list,
59 					size_t size));
60 #endif	/* USE_XATTR */
61 
62 #if defined(USE_XATTR) && defined(HAVE_LISTXATTR) && defined(HAVE_GETXATTR)
63 /*
64  * Use a global list of extended attributes -- FINFO structs have no
65  * constructors and destructors.
66  */
67 /*
68  * It bad to see new global variables while we are working on a star library.
69  */
70 LOCAL star_xattr_t	*static_xattr;
71 #endif	/* USE_XATTR && HAVE_LISTXATTR && HAVE_GETXATTR */
72 
73 #ifdef	USE_SELINUX
74 extern	BOOL		selinux_enabled;
75 #endif
76 
77 #ifdef	XATTR_NOFOLLOW		/* Max OS X has incompatible prototypes */
78 
79 /*
80  * This works since we currently only use lgetxattr()/lsetxattr()/llistxattr()
81  * but not getxattr()/setxattr()/listxattr(). If we ever need the latter,
82  * we would need to use a different macro names to be able to abstract from
83  * the differences between Linux and Mac OS X.
84  */
85 #define	lgetxattr(p, n, v, s)	getxattr(p, n, v, s, 0, XATTR_NOFOLLOW)
86 #define	lsetxattr(p, n, v, s, f) setxattr(p, n, v, s, 0, (f)|XATTR_NOFOLLOW)
87 #define	llistxattr(p, nb, s)	listxattr(p, nb, s, XATTR_NOFOLLOW)
88 
89 #else	/* !XATTR_NOFOLLOW */
90 /*
91  * This is for Linux
92  */
93 #if defined(HAVE_GETXATTR) && !defined(HAVE_LGETXATTR)
94 #define	lgetxattr	getxattr
95 #endif
96 #if defined(HAVE_SETXATTR) && !defined(HAVE_LSETXATTR)
97 #define	lsetxattr	setxattr
98 #endif
99 #if defined(HAVE_LISTXATTR) && !defined(HAVE_LLISTXATTR)
100 #define	llistxattr	listxattr
101 #endif
102 #endif	/* !XATTR_NOFOLLOW */
103 
104 EXPORT void
opt_xattr()105 opt_xattr()
106 {
107 #if defined(USE_XATTR) && \
108 	(defined(HAVE_LISTXATTR) || defined(HAVE_LLISTXATTR)) && \
109 	(defined(HAVE_GETXATTR) || defined(HAVE_LGETXATTR))
110 #if defined(HAVE_SETXATTR) || defined(HAVE_LSETXATTR)
111 	printf(" Linux-xattr");
112 #endif
113 #endif
114 }
115 
116 EXPORT void
opt_selinux()117 opt_selinux()
118 {
119 #ifdef	USE_SELINUX
120 	printf(" SELinux");
121 #endif
122 }
123 
124 /* ARGSUSED */
125 EXPORT BOOL
get_xattr(info)126 get_xattr(info)
127 	register FINFO	*info;
128 {
129 #if defined(USE_XATTR) && \
130 	(defined(HAVE_LISTXATTR) || defined(HAVE_LLISTXATTR)) && \
131 	(defined(HAVE_GETXATTR) || defined(HAVE_LGETXATTR))
132 	ssize_t	list_len;
133 	size_t	size;
134 	char	*alist;
135 	char	*lp;
136 	int	count;
137 	int	i;
138 
139 	free_xattr(&static_xattr);
140 	info->f_xflags &= ~XF_XATTR;
141 	info->f_xattr = NULL;
142 
143 	list_len = lllistxattr(info->f_sname, NULL, 0);
144 	if (list_len < 0) {
145 		if (!errhidden(E_GETXATTR, info->f_name)) {
146 			if (!errwarnonly(E_GETXATTR, info->f_name))
147 				xstats.s_getxattr++;
148 			errmsg("Cannot listxattr for '%s'.\n", info->f_name);
149 			(void) errabort(E_GETXATTR, info->f_name, TRUE);
150 		}
151 		return (FALSE);
152 	} else if (list_len == 0) {
153 		return (FALSE);
154 	}
155 	alist = ___malloc(list_len+2, "extended attribute");
156 	list_len = lllistxattr(info->f_sname, alist, list_len);
157 	if (list_len < 0) {
158 		if (!errhidden(E_GETXATTR, info->f_name)) {
159 			if (!errwarnonly(E_GETXATTR, info->f_name))
160 				xstats.s_getxattr++;
161 			errmsg("Cannot listxattr for '%s'.\n", info->f_name);
162 			(void) errabort(E_GETXATTR, info->f_name, TRUE);
163 		}
164 		goto fail;
165 	}
166 	alist[list_len] = alist[list_len+1] = '\0';
167 
168 	/*
169 	 * Count the number of attributes
170 	 */
171 	for (lp = alist, count = 0; lp - alist < list_len;
172 						lp = strchr(lp, '\0')+1) {
173 		if (*lp == '\0' ||
174 		    strncmp(lp, "system.", 7) == 0 ||
175 		    strncmp(lp, "xfsroot.", 8) == 0)
176 			continue;
177 		count++;
178 	}
179 
180 	if (count == 0)
181 		goto fail;  /* not really a failure, but... */
182 
183 	size = (count+1) * sizeof (star_xattr_t);
184 	static_xattr = ___malloc(size, "extended attribute");
185 	fillbytes(static_xattr, size, '\0');
186 
187 	for (lp = alist, i = 0; lp - alist < list_len;
188 						lp = strchr(lp, '\0')+1) {
189 		ssize_t	len;
190 
191 		if (*lp == '\0' ||
192 		    strncmp(lp, "system.", 7) == 0 ||
193 		    strncmp(lp, "xfsroot.", 8) == 0)
194 			continue;
195 
196 		static_xattr[i].name = ___malloc(strlen(lp)+1,
197 						"extended attribute");
198 		static_xattr[i].value = NULL;
199 		strcpy(static_xattr[i].name, lp);
200 
201 		len = llgetxattr(info->f_sname, lp, NULL, 0);
202 		if (len < 0) {
203 			if (!errhidden(E_GETXATTR, info->f_name)) {
204 				if (!errwarnonly(E_GETXATTR, info->f_name))
205 					xstats.s_getxattr++;
206 				errmsg("Cannot getxattr for '%s'.\n",
207 							info->f_name);
208 				(void) errabort(E_GETXATTR, info->f_name,
209 								TRUE);
210 			}
211 			goto fail2;
212 		}
213 		static_xattr[i].value_len = len;
214 		static_xattr[i].value = ___malloc(len, "extended attribute");
215 		len = llgetxattr(info->f_sname, lp, static_xattr[i].value, len);
216 		if (len < 0) {
217 			if (!errhidden(E_GETXATTR, info->f_name)) {
218 				if (!errwarnonly(E_GETXATTR, info->f_name))
219 					xstats.s_getxattr++;
220 				errmsg("Cannot getxattr for '%s'.\n",
221 							info->f_name);
222 				(void) errabort(E_GETXATTR, info->f_name,
223 								TRUE);
224 			}
225 			goto fail2;
226 		}
227 		i++;
228 	}
229 
230 	free(alist);
231 	info->f_xflags |= XF_XATTR;
232 	info->f_xattr = static_xattr;
233 	return (TRUE);
234 
235 fail2:
236 	for (; i >= 0; i--) {
237 		free(static_xattr[i].name);
238 		if (static_xattr[i].value != NULL)
239 			free(static_xattr[i].value);
240 	}
241 	free(static_xattr);
242 	static_xattr = NULL;
243 fail:
244 	free(alist);
245 	return (FALSE);
246 #else  /* USE_XATTR */
247 	return (TRUE);
248 #endif  /* USE_XATTR */
249 }
250 
251 /* ARGSUSED */
252 EXPORT BOOL
set_xattr(info)253 set_xattr(info)
254 	register FINFO	*info;
255 {
256 #if defined(USE_XATTR) && \
257 	(defined(HAVE_SETXATTR) || defined(HAVE_LSETXATTR))
258 	star_xattr_t	*xap;
259 
260 	if (info->f_xattr == NULL || (info->f_xflags & XF_XATTR) == 0)
261 		return (TRUE);
262 
263 	for (xap = info->f_xattr; xap->name != NULL; xap++) {
264 #ifdef	USE_SELINUX
265 		if (selinux_enabled &&
266 		    (strcmp(xap->name, "security.selinux") == 0))
267 			continue;
268 #endif
269 		if (llsetxattr(info->f_name, xap->name, xap->value,
270 		    xap->value_len, 0) != 0) {
271 			if (!errhidden(E_SETXATTR, info->f_name)) {
272 				if (!errwarnonly(E_SETXATTR, info->f_name))
273 					xstats.s_setxattr++;
274 				errmsg("Cannot setxattr for '%s'.\n",
275 							info->f_name);
276 				(void) errabort(E_SETXATTR, info->f_name,
277 								TRUE);
278 			}
279 			return (FALSE);
280 		}
281 	}
282 #endif  /* USE_XATTR */
283 	return (TRUE);
284 }
285 
286 #ifdef	USE_SELINUX
287 EXPORT BOOL
setselinux(info)288 setselinux(info)
289 	register FINFO	*info;
290 {
291 	star_xattr_t	*xap;
292 
293 	if (info->f_xattr == NULL || (info->f_xflags & XF_XATTR) == 0) {
294 		if (setfscreatecon(NULL) < 0)
295 			goto err;
296 		return (TRUE);
297 	}
298 
299 	for (xap = info->f_xattr; xap->name != NULL; xap++) {
300 		if (strcmp(xap->name, "security.selinux") == 0) {
301 			if (setfscreatecon(xap->value) < 0)
302 				goto err;
303 			return (TRUE);
304 		}
305 	}
306 	/*
307 	 * There was no "security.selinux" label, so we need to clear
308 	 * the context.
309 	 */
310 	if (setfscreatecon(NULL) < 0)
311 		goto err;
312 	return (TRUE);
313 err:
314 	errmsg("Cannot setup security context for '%s'.\n", info->f_name);
315 	return (FALSE);
316 }
317 #endif	/* USE_SELINUX */
318 
319 EXPORT void
free_xattr(xattr)320 free_xattr(xattr)
321 	star_xattr_t	**xattr;
322 {
323 #ifdef USE_XATTR
324 	star_xattr_t	*xap;
325 
326 	if (*xattr == NULL)
327 		return;
328 
329 	for (xap = *xattr; xap->name != NULL; xap++) {
330 		free(xap->name);
331 		free(xap->value);
332 	}
333 	free(*xattr);
334 	*xattr = NULL;
335 
336 #endif  /* USE_XATTR */
337 }
338 
339 #if defined(USE_XATTR) && \
340 	(defined(HAVE_LISTXATTR) || defined(HAVE_LLISTXATTR)) && \
341 	(defined(HAVE_GETXATTR) || defined(HAVE_LGETXATTR))
342 
343 LOCAL ssize_t
llgetxattr(path,name,value,size)344 llgetxattr(path, name, value, size)
345 	char		*path;
346 	const char	*name;
347 	void		*value;
348 	size_t		size;
349 {
350 #ifdef	HAVE_FCHDIR
351 	char	*p;
352 	int	fd;
353 	int	fdh;
354 	int	err = 0;
355 #endif
356 	ssize_t	ret;
357 
358 	if ((ret = lgetxattr(path, name, value, size)) < 0 &&
359 	    geterrno() != ENAMETOOLONG) {
360 		return (ret);
361 	}
362 
363 #ifdef	HAVE_FCHDIR
364 	if (ret >= 0)
365 		return (ret);
366 
367 	fd = hop_dirs(path, &p);
368 	if (fd >= 0) {
369 		fdh = open(".", O_SEARCH|O_DIRECTORY|O_NDELAY);
370 		if (fdh >= 0) {
371 			(void) fchdir(fd);
372 			ret = lgetxattr(p, name, value, size);
373 			err = geterrno();
374 			(void) fchdir(fdh);
375 			close(fdh);
376 		}
377 		close(fd);
378 	}
379 	close(fd);
380 	if (err)
381 		seterrno(err);
382 #endif
383 	return (ret);
384 }
385 
386 
387 LOCAL int
llsetxattr(path,name,value,size,flags)388 llsetxattr(path, name, value, size, flags)
389 	char		*path;
390 	const char	*name;
391 	const void	*value;
392 	size_t		size;
393 	int		flags;
394 {
395 #ifdef	HAVE_FCHDIR
396 	char	*p;
397 	int	fd;
398 	int	fdh;
399 	int	err = 0;
400 #endif
401 	int	ret;
402 
403 	if ((ret = lsetxattr(path, name, value, size, flags)) < 0 &&
404 	    geterrno() != ENAMETOOLONG) {
405 		return (ret);
406 	}
407 
408 #ifdef	HAVE_FCHDIR
409 	if (ret >= 0)
410 		return (ret);
411 
412 	fd = hop_dirs(path, &p);
413 	if (fd >= 0) {
414 		fdh = open(".", O_SEARCH|O_DIRECTORY|O_NDELAY);
415 		if (fdh >= 0) {
416 			(void) fchdir(fd);
417 			ret = lsetxattr(p, name, value, size, flags);
418 			err = geterrno();
419 			(void) fchdir(fdh);
420 			close(fdh);
421 		}
422 		close(fd);
423 	}
424 	close(fd);
425 	if (err)
426 		seterrno(err);
427 #endif
428 	return (ret);
429 }
430 
431 LOCAL ssize_t
lllistxattr(path,list,size)432 lllistxattr(path, list, size)
433 	char		*path;
434 	char		*list;
435 	size_t		size;
436 {
437 #ifdef	HAVE_FCHDIR
438 	char	*p;
439 	int	fd;
440 	int	fdh;
441 	int	err = 0;
442 #endif
443 	ssize_t	ret;
444 
445 	if ((ret = llistxattr(path, list, size)) < 0 &&
446 	    geterrno() != ENAMETOOLONG) {
447 		return (ret);
448 	}
449 
450 #ifdef	HAVE_FCHDIR
451 	if (ret >= 0)
452 		return (ret);
453 
454 	fd = hop_dirs(path, &p);
455 	if (fd >= 0) {
456 		fdh = open(".", O_SEARCH|O_DIRECTORY|O_NDELAY);
457 		if (fdh >= 0) {
458 			(void) fchdir(fd);
459 			ret = llistxattr(p, list, size);
460 			err = geterrno();
461 			(void) fchdir(fdh);
462 			close(fdh);
463 		}
464 		close(fd);
465 	}
466 	close(fd);
467 	if (err)
468 		seterrno(err);
469 #endif
470 	return (ret);
471 }
472 #endif	/* USE_XATTR */
473