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