1 #include "../burp.h"
2 #include "../alloc.h"
3 #include "../asfd.h"
4 #include "../async.h"
5 #include "../cntr.h"
6 #include "../log.h"
7 #include "../prepend.h"
8 #include "extrameta.h"
9 #include "xattr.h"
10 
11 #ifdef HAVE_XATTR
12 
13 #ifdef HAVE_SYS_XATTR_H
14 #include <sys/xattr.h>
15 #endif
16 #ifdef HAVE_SYS_EXTATTR_H
17 #include <sys/extattr.h>
18 #endif
19 #ifdef HAVE_LIBUTIL_H
20 #include <libutil.h>
21 #endif
22 
23 #ifdef HAVE_DARWIN_OS
24 /*
25  * OSX doesn't have llistxattr, lgetxattr and lsetxattr but has
26  * listxattr, getxattr and setxattr with an extra options argument
27  * which mimics the l variants of the functions when we specify
28  * XATTR_NOFOLLOW as the options value.
29  */
30 #define llistxattr(path, list, size) \
31 listxattr((path), (list), (size), XATTR_NOFOLLOW)
32 #define lgetxattr(path, name, value, size) \
33 getxattr((path), (name), (value), (size), 0, XATTR_NOFOLLOW)
34 #define lsetxattr(path, name, value, size, flags) \
35 setxattr((path), (name), (value), (size), (flags), XATTR_NOFOLLOW)
36 static const char *acl_skiplist[2] = {
37     "com.apple.system.Security",
38     NULL
39 };
40 #elif HAVE_LINUX_OS
41 static const char *acl_skiplist[3] = {
42     "system.posix_acl_access",
43     "system.posix_acl_default",
44     NULL
45 };
46 #elif HAVE_FREEBSD_OS
47 static const char *acl_skiplist[2] = {
48     "system.posix1e.acl_access", NULL
49 };
50 #else
51 static const char *acl_skiplist[1] = {
52 	NULL
53 };
54 #endif
55 
56 // Skip xattr entries that were already saved as ACLs.
in_skiplist(const char * xattr)57 static int in_skiplist(const char *xattr)
58 {
59 	for(int c=0; acl_skiplist[c]; c++)
60 		if(!strcmp(xattr, acl_skiplist[c]))
61 			return 1;
62 	return 0;
63 }
64 
append_to_extrameta(const char * toappend,char metasymbol,char ** xattrtext,size_t * xlen,uint32_t totallen)65 static int append_to_extrameta(const char *toappend, char metasymbol,
66 	char **xattrtext, size_t *xlen, uint32_t totallen)
67 {
68 	char tmp3[10];
69 	size_t newlen=0;
70 	snprintf(tmp3, sizeof(tmp3), "%c%08X", metasymbol, totallen);
71 	newlen=(*xlen)+9+totallen+1;
72 	if(!(*xattrtext=(char *)
73 		realloc_w(*xattrtext, newlen, __func__)))
74 			return -1;
75 	memcpy((*xattrtext)+(*xlen), tmp3, 9);
76 	(*xlen)+=9;
77 	memcpy((*xattrtext)+(*xlen), toappend, totallen);
78 	(*xlen)+=totallen;
79 	(*xattrtext)[*xlen]='\0';
80 	return 0;
81 }
82 
83 #ifndef UTEST
84 static
85 #endif
get_next_xattr_str(struct asfd * asfd,char ** data,size_t * l,struct cntr * cntr,uint32_t * s,const char * path)86 char *get_next_xattr_str(struct asfd *asfd, char **data, size_t *l,
87 	struct cntr *cntr, uint32_t *s, const char *path)
88 {
89 	char *ret=NULL;
90 
91 	if(*l<8)
92 	{
93 		logw(asfd, cntr, "length of xattr '%s' %zd is too short for %s\n",
94 			*data, *l, path);
95 		return NULL;
96 	}
97 
98 	if((sscanf(*data, "%08X", s))!=1)
99 	{
100 		logw(asfd, cntr, "sscanf of xattr '%s' %zd failed for %s\n",
101 			*data, *l, path);
102 		return NULL;
103 	}
104 	*data+=8;
105 	*l-=8;
106 	if(*s>*l)
107 	{
108 		logw(asfd, cntr, "requested length %d of xattr '%s' %zd is too long for %s\n",
109 			*s, *data, *l, path);
110 		return NULL;
111 	}
112 	if(!(ret=(char *)malloc_w((*s)+1, __func__)))
113 		return NULL;
114 	memcpy(ret, *data, *s);
115 	ret[*s]='\0';
116 
117 	*data+=*s;
118 	*l-=*s;
119 
120 	return ret;
121 }
122 
123 #ifdef HAVE_SYS_EXTATTR_H
124 static int namespaces[2] = {
125 	EXTATTR_NAMESPACE_USER,
126 	EXTATTR_NAMESPACE_SYSTEM
127 };
128 
has_xattr(const char * path)129 int has_xattr(const char *path)
130 {
131 	int i=0;
132 	for(i=0; i<(int)(sizeof(namespaces)/sizeof(int)); i++)
133 	{
134 		if(extattr_list_link(path, namespaces[i], NULL, 0)>0)
135 			return 1;
136 	}
137 	return 0;
138 }
139 
140 #define BSD_BUF_SIZE	1024
get_xattr(struct asfd * asfd,const char * path,char ** xattrtext,size_t * xlen,struct cntr * cntr)141 int get_xattr(struct asfd *asfd, const char *path,
142 	char **xattrtext, size_t *xlen, struct cntr *cntr)
143 {
144 	int i=0;
145 	uint32_t maxlen=0xFFFFFFFF/2;
146 
147 	for(i=0; i<(int)(sizeof(namespaces)/sizeof(int)); i++)
148 	{
149 		int j=0;
150 		ssize_t len=0;
151 		int have_acl=0;
152 		char *xattrlist=NULL;
153 		char *cnamespace=NULL;
154 		uint32_t totallen=0;
155 		char *toappend=NULL;
156 		static char z[BSD_BUF_SIZE*2]="";
157 		char cattrname[BSD_BUF_SIZE]="";
158 		if((len=extattr_list_link(path, namespaces[i], NULL, 0))<0)
159 		{
160 			logw(asfd, cntr, "could not extattr_list_link of '%s': %zd\n",
161 				path, len);
162 			return 0; // carry on
163 		}
164 		if(!len) continue;
165 		if(xattrtext && *xattrtext)
166 		{
167 			// Already have some meta text, which means that some
168 			// ACLs were set.
169 			have_acl++;
170 		}
171 		if(!(xattrlist=(char *)calloc_w(1, len+1, __func__)))
172 			return -1;
173 		if((len=extattr_list_link(path, namespaces[i], xattrlist, len))<=0)
174 		{
175 			logw(asfd, cntr, "could not extattr_list_link '%s': %zd\n",
176 				path, len);
177 			free_w(&xattrlist);
178 			return 0; // carry on
179 		}
180 		xattrlist[len]='\0';
181 
182 		if(extattr_namespace_to_string(namespaces[i], &cnamespace))
183 		{
184 			logp("Failed to convert %d into namespace on '%s'\n",
185 				 namespaces[i], path);
186 			free_w(&xattrlist);
187 			return 0; // carry on
188 		}
189 
190 
191 		for(j=0; j<(int)len; j+=xattrlist[j]+1)
192 		{
193 			int cnt=0;
194 			char tmp1[9];
195 			char tmp2[9];
196 			size_t newlen=0;
197 			uint32_t zlen=0;
198 			ssize_t vlen=0;
199 			char *val=NULL;
200 			cnt=xattrlist[j];
201 			if(cnt>((int)sizeof(cattrname)-1))
202 				cnt=((int)sizeof(cattrname)-1);
203 			strncpy(cattrname, xattrlist+(j+1), cnt);
204 			cattrname[cnt]='\0';
205 			snprintf(z, sizeof(z), "%s.%s",
206 				cnamespace, cattrname);
207 
208 			if(have_acl && in_skiplist(z))
209 				continue;
210 			zlen=(uint32_t)strlen(z);
211 			//printf("\ngot: %s (%s)\n", z, path);
212 
213 			if((vlen=extattr_list_link(path, namespaces[i],
214 				xattrlist, len))<0)
215 			{
216 				logw(asfd, cntr, "could not extattr_list_link on %s for %s: %zd\n", path, cnamespace, vlen);
217 				continue;
218 			}
219 			if(vlen)
220 			{
221 				if(!(val=(char *)malloc_w(vlen+1, __func__)))
222 				{
223 					free_w(&xattrlist);
224 					free_w(&toappend);
225 					return -1;
226 				}
227 				if((vlen=extattr_get_link(path, namespaces[i],
228 					cattrname, val, vlen))<0)
229 				{
230 					logw(asfd, cntr, "could not extattr_list_link %s for %s: %zd\n", path, cnamespace, vlen);
231 					free_w(&val);
232 					continue;
233 				}
234 				val[vlen]='\0';
235 
236 				if(vlen>maxlen)
237 				{
238 					logw(asfd, cntr, "xattr value of '%s' too long: %zd\n",
239 						path, vlen);
240 					free_w(&toappend);
241 					free_w(&val);
242 					break;
243 				}
244 			}
245 
246 			snprintf(tmp1, sizeof(tmp1), "%08X", zlen);
247 			snprintf(tmp2, sizeof(tmp2), "%08X", (uint32_t)vlen);
248 			newlen=totallen+8+zlen+8+vlen;
249 			if(!(toappend=(char *)realloc_w(toappend, newlen, __func__)))
250 			{
251 				free_w(&val);
252 				free_w(&xattrlist);
253 				return -1;
254 			}
255 			memcpy(toappend+totallen, tmp1, 8);
256 			totallen+=8;
257 			memcpy(toappend+totallen, z, zlen);
258 			totallen+=zlen;
259 			memcpy(toappend+totallen, tmp2, 8);
260 			totallen+=8;
261 			memcpy(toappend+totallen, val, vlen);
262 			totallen+=vlen;
263 			free_w(&val);
264 
265 			if(totallen>maxlen)
266 			{
267 				logw(asfd, cntr,
268 				  "xattr length of '%s' grew too long: %d\n",
269 				  path, totallen);
270 				free_w(&val);
271 				free_w(&toappend);
272 				free_w(&xattrlist);
273 				return 0; // carry on
274 			}
275 		}
276 
277 		if(toappend)
278 		{
279 			if(append_to_extrameta(toappend, META_XATTR_BSD,
280 				xattrtext, xlen, totallen))
281 			{
282 				free_w(&toappend);
283 				free_w(&xattrlist);
284 				return -1;
285 			}
286 		}
287 		free_w(&toappend);
288 		free_w(&xattrlist);
289 	}
290 
291 	return 0;
292 }
293 
do_set_xattr_bsd(struct asfd * asfd,const char * path,const char * xattrtext,size_t xlen,struct cntr * cntr)294 static int do_set_xattr_bsd(struct asfd *asfd,
295 	const char *path,
296 	const char *xattrtext, size_t xlen, struct cntr *cntr)
297 {
298 	int ret=-1;
299 	size_t l=0;
300 	char *data=NULL;
301 	char *value=NULL;
302 	char *nspace=NULL;
303 
304 	data=(char *)xattrtext;
305 	l=xlen;
306 	while(l>0)
307 	{
308 		ssize_t cnt;
309 		uint32_t vlen=0;
310 		int cnspace=0;
311 		char *name=NULL;
312 
313 		if(!(nspace=get_next_xattr_str(asfd, &data, &l,
314 			cntr, &vlen, path))
315 		  || !(value=get_next_xattr_str(asfd, &data, &l,
316 			cntr, &vlen, path)))
317 				goto end;
318 
319 		// Need to split the name into two parts.
320 		if(!(name=strchr(nspace, '.')))
321 		{
322 			logw(asfd, cntr,
323 			  "could not split %s into namespace and name on %s\n",
324 				nspace, path);
325 			goto end;
326 		}
327 		*name='\0';
328 		name++;
329 
330 		if(extattr_string_to_namespace(nspace, &cnspace))
331 		{
332 			logw(asfd, cntr,
333 				"could not convert %s into namespace on %s\n",
334 				nspace, path);
335 			goto end;
336 		}
337 
338 		//printf("set_link: %d %s %s %s\n", cnspace, nspace, name, value);
339 		if((cnt=extattr_set_link(path,
340 			cnspace, name, value, vlen))!=vlen)
341 		{
342 			logw(asfd, cntr,
343 				"extattr_set_link error on %s %zd!=%d: %s\n",
344 				path, cnt, vlen, strerror(errno));
345 			goto end;
346 		}
347 
348 		free_w(&nspace);
349 		free_w(&value);
350 	}
351 	ret=0;
352 end:
353 	free_w(&nspace);
354 	free_w(&value);
355 	return ret;
356 }
357 
set_xattr(struct asfd * asfd,const char * path,const char * xattrtext,size_t xlen,char metacmd,struct cntr * cntr)358 int set_xattr(struct asfd *asfd, const char *path,
359 	const char *xattrtext,
360 	size_t xlen, char metacmd, struct cntr *cntr)
361 {
362 	switch(metacmd)
363 	{
364 		case META_XATTR_BSD:
365 			return do_set_xattr_bsd(asfd, path,
366 				xattrtext, xlen, cntr);
367 		default:
368 			logp("unknown xattr type: %c\n", metacmd);
369 			logw(asfd, cntr, "unknown xattr type: %c\n", metacmd);
370 			break;
371 	}
372 	return -1;
373 }
374 
375 #elif HAVE_SYS_XATTR_H
376 
has_xattr(const char * path)377 int has_xattr(const char *path)
378 {
379 	if(llistxattr(path, NULL, 0)>0) return 1;
380 	return 0;
381 }
382 
get_toappend(struct asfd * asfd,const char * path,char ** toappend,const char * xattrlist,ssize_t len,uint32_t * totallen,int have_acl,struct cntr * cntr)383 static int get_toappend(struct asfd *asfd, const char *path,
384 	char **toappend, const char *xattrlist,
385 	ssize_t len, uint32_t *totallen,
386 	int have_acl,
387 	struct cntr *cntr)
388 {
389 	char *val=NULL;
390 	const char *z=NULL;
391 	uint32_t maxlen=0xFFFFFFFF/2;
392 
393 	for(z=xattrlist; z-xattrlist < len; z=strchr(z, '\0')+1)
394 	{
395 		char tmp1[9];
396 		char tmp2[9];
397 		ssize_t vlen;
398 		uint32_t zlen=0;
399 		uint32_t newlen=0;
400 
401 		free_w(&val);
402 
403 		if((zlen=(uint32_t)strlen(z))>maxlen)
404 		{
405 			logw(asfd, cntr,
406 				"xattr element of '%s' too long: %d\n",
407 				path, zlen);
408 			goto carryon;
409 		}
410 
411 		if(have_acl && in_skiplist(z))
412 			continue;
413 
414 		if((vlen=lgetxattr(path, z, NULL, 0))<0)
415 		{
416 			logw(asfd, cntr,
417 				"could not lgetxattr on %s for %s: %zd %s\n",
418 				path, z, vlen, strerror(errno));
419 			continue;
420 		}
421 		if(vlen)
422 		{
423 			if(!(val=(char *)malloc_w(vlen+1, __func__)))
424 				goto error;
425 			if((vlen=lgetxattr(path, z, val, vlen))<0)
426 			{
427 				logw(asfd, cntr,
428 				  "could not lgetxattr %s for %s: %zd %s\n",
429 					path, z, vlen, strerror(errno));
430 				continue;
431 			}
432 			val[vlen]='\0';
433 
434 			if(vlen>maxlen)
435 			{
436 				logw(asfd, cntr,
437 					"xattr value of '%s' too long: %zd\n",
438 					path, vlen);
439 				goto carryon;
440 			}
441 		}
442 
443 		snprintf(tmp1, sizeof(tmp1), "%08X", zlen);
444 		snprintf(tmp2, sizeof(tmp2), "%08X", (uint32_t)vlen);
445 		newlen=(*totallen)+8+zlen+8+vlen;
446 		if(!(*toappend=(char *)realloc_w(*toappend, newlen, __func__)))
447 			goto error;
448 		memcpy((*toappend)+(*totallen), tmp1, 8);
449 		*totallen+=8;
450 		memcpy((*toappend)+(*totallen), z, zlen);
451 		*totallen+=zlen;
452 		memcpy((*toappend)+(*totallen), tmp2, 8);
453 		*totallen+=8;
454 		memcpy((*toappend)+(*totallen), val, vlen);
455 		*totallen+=vlen;
456 
457 		if(*totallen>maxlen)
458 		{
459 			logw(asfd, cntr,
460 				"xattr length of '%s' grew too long: %d\n",
461 				path, *totallen);
462 			goto carryon;
463 		}
464 	}
465 
466 	free_w(&val);
467 	return 0;
468 error:
469 	free_w(&val);
470 	free_w(toappend);
471 	return -1;
472 carryon:
473 	free_w(&val);
474 	free_w(toappend);
475 	return 0;
476 }
477 
get_xattr(struct asfd * asfd,const char * path,char ** xattrtext,size_t * xlen,struct cntr * cntr)478 int get_xattr(struct asfd *asfd, const char *path,
479 	char **xattrtext, size_t *xlen, struct cntr *cntr)
480 {
481 	int ret=0;
482 	ssize_t len;
483 	int have_acl=0;
484 	char *toappend=NULL;
485 	char *xattrlist=NULL;
486 	uint32_t totallen=0;
487 
488 	if((len=llistxattr(path, NULL, 0))<0)
489 	{
490 		logw(asfd, cntr, "could not llistxattr '%s': %zd %s\n",
491 			path, len, strerror(errno));
492 		goto end; // Carry on.
493 	}
494 	if(!(xattrlist=(char *)calloc_w(1, len, __func__)))
495 	{
496 		ret=-1;
497 		goto end;
498 	}
499 	if((len=llistxattr(path, xattrlist, len))<0)
500 	{
501 		logw(asfd, cntr, "could not llistxattr '%s': %zd %s\n",
502 			path, len, strerror(errno));
503 		goto end; // Carry on.
504 	}
505 
506 	if(xattrtext && *xattrtext)
507 	{
508 		// Already have some meta text, which means that some
509 		// ACLs were set.
510 		have_acl++;
511 	}
512 
513 	if(get_toappend(asfd, path, &toappend, xattrlist, len, &totallen,
514 		have_acl, cntr))
515 	{
516 		ret=-1;
517 		goto end;
518 	}
519 
520 	if(toappend)
521 		ret=append_to_extrameta(toappend, META_XATTR,
522 			xattrtext, xlen, totallen);
523 end:
524 	free_w(&toappend);
525 	free_w(&xattrlist);
526 	return ret;
527 }
528 
do_set_xattr(struct asfd * asfd,const char * path,const char * xattrtext,size_t xlen,struct cntr * cntr)529 static int do_set_xattr(struct asfd *asfd,
530 	const char *path,
531 	const char *xattrtext, size_t xlen, struct cntr *cntr)
532 {
533 	size_t l=0;
534 	int ret=-1;
535 	char *data=NULL;
536 	char *name=NULL;
537 	char *value=NULL;
538 
539 	data=(char *)xattrtext;
540 	l=xlen;
541 	while(l>0)
542 	{
543 		uint32_t s=0;
544 		free_w(&name);
545 		free_w(&value);
546 
547 		if(!(name=get_next_xattr_str(asfd, &data, &l,
548 			cntr, &s, path))
549 		  || !(value=get_next_xattr_str(asfd, &data, &l,
550 			cntr, &s, path)))
551 				goto end;
552 		if(lsetxattr(path, name, value, s, 0))
553 		{
554 			logw(asfd, cntr, "lsetxattr error on %s: %s\n",
555 				path, strerror(errno));
556 			goto end;
557 		}
558 	}
559 
560 	ret=0;
561 end:
562 	free_w(&name);
563 	free_w(&value);
564 	return ret;
565 }
566 
set_xattr(struct asfd * asfd,const char * path,const char * xattrtext,size_t xlen,char metacmd,struct cntr * cntr)567 int set_xattr(struct asfd *asfd, const char *path,
568 	const char *xattrtext, size_t xlen, char metacmd, struct cntr *cntr)
569 {
570 	switch(metacmd)
571 	{
572 		case META_XATTR:
573 			return do_set_xattr(asfd,
574 				path, xattrtext, xlen, cntr);
575 		default:
576 			logp("unknown xattr type: %c\n", metacmd);
577 			logw(asfd, cntr, "unknown xattr type: %c\n", metacmd);
578 			break;
579 	}
580 	return -1;
581 }
582 #endif
583 
584 #ifdef UTEST
fs_supports_xattr(void)585 int fs_supports_xattr(void)
586 {
587 	FILE *fp;
588 	int ret=-1;
589 	const char *fname="xattr_test_file";
590 	if(!(fp=fopen(fname, "w")))
591 	{
592 		printf("Could not open %s!\n", fname);
593 		return 0;
594 	}
595 	fclose(fp);
596 #ifdef HAVE_SYS_EXTATTR_H
597 	ret=extattr_set_link(fname, EXTATTR_NAMESPACE_USER, "comment", "a", strlen("a"));
598 #elif HAVE_SYS_XATTR_H
599 	ret=lsetxattr(fname, "user.comment", "a", strlen("a"), /*flags*/0);
600 #else
601 	errno=ENOTSUP;
602 #endif
603 	if(ret<0 && errno==ENOTSUP)
604 	{
605 		printf("File system does not support xattrs!\n");
606 		unlink(fname);
607 		return 0;
608 	}
609 	unlink(fname);
610 	return 1;
611 }
612 #endif
613 
614 #endif
615