1 /*
2 Bacula(R) - The Network Backup Solution
3
4 Copyright (C) 2000-2020 Kern Sibbald
5
6 The original author of Bacula is Kern Sibbald, with contributions
7 from many others, a complete list can be found in the file AUTHORS.
8
9 You may use this file and others of this release according to the
10 license defined in the LICENSE file, which includes the Affero General
11 Public License, v3.0 ("AGPLv3") and some additional permissions and
12 terms pursuant to its AGPLv3 Section 7.
13
14 This notice must be preserved when any source code is
15 conveyed and/or propagated.
16
17 Bacula(R) is a registered trademark of Kern Sibbald.
18 */
19 /**
20 * Major refactoring of ACL code written by:
21 *
22 * Radosław Korzeniewski, MMXVI
23 * radoslaw@korzeniewski.net, radekk@inteos.pl
24 * Inteos Sp. z o.o. http://www.inteos.pl/
25 *
26 */
27
28 #include "bacula.h"
29 #include "filed.h"
30 #include "bacl_freebsd.h"
31
32 #if defined(HAVE_FREEBSD_OS)
33
34 /* check if ACL support is enabled */
35 #if defined(HAVE_ACL)
36
37 /*
38 * Define the supported ACL streams for this OS
39 */
40 static const int os_acl_streams[] = {
41 STREAM_XACL_FREEBSD_ACCESS,
42 STREAM_XACL_FREEBSD_NFS4,
43 0
44 };
45
46 static const int os_default_acl_streams[] = {
47 STREAM_XACL_FREEBSD_DEFAULT,
48 0
49 };
50
51 /*
52 * OS specific constructor
53 */
BACL_FreeBSD()54 BACL_FreeBSD::BACL_FreeBSD(){
55
56 set_acl_streams(os_acl_streams, os_default_acl_streams);
57 };
58
59 /*
60 * Translates Bacula internal acl representation into acl type
61 *
62 * in:
63 * bacltype - internal Bacula acl type (BACL_type)
64 * out:
65 * acl_type_t - os dependent acl type
66 * when failed - ACL_TYPE_NONE is returned
67 */
get_acltype(BACL_type bacltype)68 acl_type_t BACL_FreeBSD::get_acltype(BACL_type bacltype){
69
70 acl_type_t acltype;
71
72 switch (bacltype){
73 #ifdef HAVE_ACL_TYPE_NFS4
74 case BACL_TYPE_NFS4:
75 acltype = ACL_TYPE_NFS4;
76 break;
77 #endif
78 case BACL_TYPE_ACCESS:
79 acltype = ACL_TYPE_ACCESS;
80 break;
81 case BACL_TYPE_DEFAULT:
82 acltype = ACL_TYPE_DEFAULT;
83 break;
84 default:
85 /*
86 * sanity check for acl's not supported by OS
87 */
88 acltype = (acl_type_t)ACL_TYPE_NONE;
89 break;
90 }
91 return acltype;
92 };
93
94 /*
95 * Counts a number of acl entries
96 *
97 * in:
98 * acl - acl object
99 * out:
100 * int - number of entries in acl object
101 * when no acl entry available or any error then return zero '0'
102 */
acl_nrentries(acl_t acl)103 int BACL_FreeBSD::acl_nrentries(acl_t acl){
104
105 int nr = 0;
106 acl_entry_t aclentry;
107 int rc;
108
109 rc = acl_get_entry(acl, ACL_FIRST_ENTRY, &aclentry);
110 while (rc == 1){
111 nr++;
112 rc = acl_get_entry(acl, ACL_NEXT_ENTRY, &aclentry);
113 }
114
115 return nr;
116 };
117
118 /*
119 * Checks if acl is simple.
120 *
121 * acl is simple if it has only the following entries:
122 * "user::",
123 * "group::",
124 * "other::"
125 *
126 * in:
127 * acl - acl object
128 * out:
129 * true - when acl object is simple
130 * false - when acl object is not simple
131 */
acl_issimple(acl_t acl)132 bool BACL_FreeBSD::acl_issimple(acl_t acl){
133
134 acl_entry_t aclentry;
135 acl_tag_t acltag;
136 int rc;
137
138 rc = acl_get_entry(acl, ACL_FIRST_ENTRY, &aclentry);
139 while (rc == 1){
140 if (acl_get_tag_type(aclentry, &acltag) < 0){
141 return true;
142 }
143 /*
144 * Check for ACL_USER_OBJ, ACL_GROUP_OBJ or ACL_OTHER to find out.
145 */
146 if (acltag != ACL_USER_OBJ &&
147 acltag != ACL_GROUP_OBJ &&
148 acltag != ACL_OTHER){
149 return false;
150 }
151 rc = acl_get_entry(acl, ACL_NEXT_ENTRY, &aclentry);
152 }
153 return true;
154 };
155
156 /*
157 * Checks if ACL's are available for a specified file
158 *
159 * in:
160 * jcr - Job Control Record
161 * name - specifies the system variable to be queried
162 * out:
163 * bRC_BACL_ok - check successful, lets setup bacltype variable
164 * bRC_BACL_error - in case of error
165 * bRC_BACL_skip - you should skip all other routine
166 * bRC_BACL_cont - you need to continue your routine
167 */
check_bacltype(JCR * jcr,int name)168 bRC_BACL BACL_FreeBSD::check_bacltype (JCR *jcr, int name){
169
170 int aclrc = 0;
171
172 aclrc = pathconf(jcr->last_fname, name);
173 switch (aclrc){
174 case -1: {
175 /* some error check why */
176 berrno be;
177 if (errno == ENOENT){
178 /* file does not exist skip it */
179 return bRC_BACL_skip;
180 } else {
181 Mmsg2(jcr->errmsg, _("pathconf error on file \"%s\": ERR=%s\n"), jcr->last_fname, be.bstrerror());
182 Dmsg2(100, "pathconf error file=%s ERR=%s\n", jcr->last_fname, be.bstrerror());
183 return bRC_BACL_error;
184 }
185 }
186 case 0:
187 /* continue the routine */
188 return bRC_BACL_cont;
189 default:
190 break;
191 }
192 return bRC_BACL_ok;
193 };
194
195 /*
196 * Perform OS specific ACL backup
197 *
198 * in/out - check API at bacl.h
199 */
os_backup_acl(JCR * jcr,FF_PKT * ff_pkt)200 bRC_BACL BACL_FreeBSD::os_backup_acl (JCR *jcr, FF_PKT *ff_pkt){
201
202 bRC_BACL rc;
203 BACL_type bacltype = BACL_TYPE_NONE;
204
205 #if defined(_PC_ACL_NFS4)
206 /*
207 * Check if filesystem supports NFS4 acls.
208 */
209 rc = check_bacltype(jcr, _PC_ACL_NFS4);
210 switch (rc){
211 case bRC_BACL_ok:
212 bacltype = BACL_TYPE_NFS4;
213 break;
214 case bRC_BACL_skip:
215 return bRC_BACL_ok;
216 case bRC_BACL_cont:
217 break;
218 default:
219 /* errors */
220 return rc;
221 }
222 #endif
223 if (bacltype == BACL_TYPE_NONE){
224 /*
225 * Check if filesystem supports POSIX acls.
226 */
227 rc = check_bacltype(jcr, _PC_ACL_EXTENDED);
228 switch (rc){
229 case bRC_BACL_ok:
230 bacltype = BACL_TYPE_ACCESS;
231 break;
232 case bRC_BACL_skip:
233 return bRC_BACL_ok;
234 case bRC_BACL_cont:
235 break;
236 default:
237 /* errors */
238 return rc;
239 }
240 }
241
242 /* no ACL's available for file, so skip this filesystem */
243 if (bacltype == BACL_TYPE_NONE){
244 clear_flag(BACL_FLAG_NATIVE);
245 /*
246 * it is a bit of hardcore to clear a poolmemory with a NULL pointer,
247 * but it is working, hehe :)
248 * you may ask why it is working? it is simple, a pm_strcpy function is handling
249 * a null pointer with a substitiution of empty string.
250 */
251 set_content(NULL);
252 return bRC_BACL_ok;
253 }
254
255 switch (bacltype){
256 case BACL_TYPE_NFS4:
257 /*
258 * Read NFS4 ACLs
259 */
260 if (os_get_acl(jcr, BACL_TYPE_NFS4) == bRC_BACL_fatal)
261 return bRC_BACL_fatal;
262
263 if (get_content_len() > 0){
264 if (send_acl_stream(jcr, STREAM_XACL_FREEBSD_NFS4) == bRC_BACL_fatal)
265 return bRC_BACL_fatal;
266 }
267 break;
268 case BACL_TYPE_ACCESS:
269 /*
270 * Read access ACLs
271 */
272 if (os_get_acl(jcr, BACL_TYPE_ACCESS) == bRC_BACL_fatal)
273 return bRC_BACL_fatal;
274
275 if (get_content_len() > 0){
276 if (send_acl_stream(jcr, STREAM_XACL_FREEBSD_ACCESS) == bRC_BACL_fatal)
277 return bRC_BACL_fatal;
278 }
279
280 /*
281 * Directories can have default ACLs too
282 */
283 if (ff_pkt->type == FT_DIREND){
284 if (os_get_acl(jcr, BACL_TYPE_DEFAULT) == bRC_BACL_fatal)
285 return bRC_BACL_fatal;
286 if (get_content_len() > 0){
287 if (send_acl_stream(jcr, STREAM_XACL_FREEBSD_DEFAULT) == bRC_BACL_fatal)
288 return bRC_BACL_fatal;
289 }
290 }
291 break;
292 default:
293 break;
294 }
295
296 return bRC_BACL_ok;
297 };
298
299 /*
300 * Perform OS specific ACL restore
301 *
302 * in/out - check API at bacl.h
303 */
os_restore_acl(JCR * jcr,int stream,char * content,uint32_t length)304 bRC_BACL BACL_FreeBSD::os_restore_acl (JCR *jcr, int stream, char *content, uint32_t length){
305
306 int aclrc = 0;
307 const char *acl_type_name;
308
309 switch (stream){
310 case STREAM_UNIX_ACCESS_ACL:
311 case STREAM_XACL_FREEBSD_ACCESS:
312 case STREAM_UNIX_DEFAULT_ACL:
313 case STREAM_XACL_FREEBSD_DEFAULT:
314 aclrc = pathconf(jcr->last_fname, _PC_ACL_EXTENDED);
315 acl_type_name = "POSIX";
316 break;
317 case STREAM_XACL_FREEBSD_NFS4:
318 #if defined(_PC_ACL_NFS4)
319 aclrc = pathconf(jcr->last_fname, _PC_ACL_NFS4);
320 #endif
321 acl_type_name = "NFS4";
322 break;
323 default:
324 acl_type_name = "unknown";
325 break;
326 }
327
328 switch (aclrc){
329 case -1: {
330 berrno be;
331
332 switch (errno){
333 case ENOENT:
334 return bRC_BACL_ok;
335 default:
336 Mmsg2(jcr->errmsg, _("pathconf error on file \"%s\": ERR=%s\n"), jcr->last_fname, be.bstrerror());
337 Dmsg3(100, "pathconf error acl=%s file=%s ERR=%s\n", content, jcr->last_fname, be.bstrerror());
338 return bRC_BACL_error;
339 }
340 }
341 case 0:
342 clear_flag(BACL_FLAG_NATIVE);
343 Mmsg2(jcr->errmsg, _("Trying to restore acl on file \"%s\" on filesystem without %s acl support\n"), jcr->last_fname, acl_type_name);
344 return bRC_BACL_error;
345 default:
346 break;
347 }
348
349 switch (stream){
350 case STREAM_UNIX_ACCESS_ACL:
351 case STREAM_XACL_FREEBSD_ACCESS:
352 return os_set_acl(jcr, BACL_TYPE_ACCESS, content, length);
353 case STREAM_UNIX_DEFAULT_ACL:
354 case STREAM_XACL_FREEBSD_DEFAULT:
355 return os_set_acl(jcr, BACL_TYPE_DEFAULT, content, length);
356 case STREAM_XACL_FREEBSD_NFS4:
357 return os_set_acl(jcr, BACL_TYPE_NFS4, content, length);
358 default:
359 break;
360 }
361 return bRC_BACL_error;
362 };
363
364 /*
365 * Low level OS specific runtime to get ACL data from file.
366 * The ACL data is set in internal content buffer
367 *
368 * in/out - check API at bacl.h
369 */
os_get_acl(JCR * jcr,BACL_type bacltype)370 bRC_BACL BACL_FreeBSD::os_get_acl(JCR *jcr, BACL_type bacltype){
371
372 acl_t acl;
373 acl_type_t acltype;
374 char *acltext;
375 bRC_BACL rc = bRC_BACL_ok;
376
377 acltype = get_acltype(bacltype);
378 acl = acl_get_file(jcr->last_fname, acltype);
379
380 if (acl){
381 Dmsg1(400, "OS_ACL read from file: %s\n",jcr->last_fname);
382 if (acl_nrentries(acl) == 0){
383 goto bail_out;
384 }
385
386 /* check for simple ACL which correspond to standard permissions only */
387 if (bacltype == BACL_TYPE_ACCESS && acl_issimple(acl)){
388 goto bail_out;
389 }
390
391 #if defined(_PC_ACL_NFS4)
392 if (bacltype == BACL_TYPE_NFS4){
393 int trivial;
394 if (acl_is_trivial_np(acl, &trivial) == 0){
395 if (trivial == 1){
396 goto bail_out;
397 }
398 }
399 }
400 #endif
401
402 if ((acltext = acl_to_text(acl, NULL)) != NULL){
403 set_content(acltext);
404 acl_free(acl);
405 acl_free(acltext);
406 return bRC_BACL_ok;
407 }
408
409 berrno be;
410
411 Mmsg2(jcr->errmsg, _("acl_to_text error on file \"%s\": ERR=%s\n"), jcr->last_fname, be.bstrerror());
412 Dmsg2(100, "acl_to_text error file=%s ERR=%s\n", jcr->last_fname, be.bstrerror());
413
414 rc = bRC_BACL_error;
415
416 } else {
417 berrno be;
418
419 switch (errno){
420 case EOPNOTSUPP:
421 /* fs does not support acl, skip it */
422 Dmsg0(400, "Wow, ACL is not supported on this filesystem\n");
423 clear_flag(BACL_FLAG_NATIVE);
424 break;
425 case ENOENT:
426 break;
427 default:
428 /* Some real error */
429 Mmsg2(jcr->errmsg, _("acl_get_file error on file \"%s\": ERR=%s\n"), jcr->last_fname, be.bstrerror());
430 Dmsg2(100, "acl_get_file error file=%s ERR=%s\n", jcr->last_fname, be.bstrerror());
431 rc = bRC_BACL_error;
432 break;
433 }
434 }
435
436 bail_out:
437 if (acl){
438 acl_free(acl);
439 }
440 /*
441 * it is a bit of hardcore to clear a pool memory with a NULL pointer,
442 * but it is working, hehe :)
443 * you may ask why it is working? it is simple, a pm_strcpy function is handling
444 * a null pointer with a substitution of empty string.
445 */
446 set_content(NULL);
447 return rc;
448 };
449
450 /*
451 * Low level OS specific runtime to set ACL data on file
452 *
453 * in/out - check API at bacl.h
454 */
os_set_acl(JCR * jcr,BACL_type bacltype,char * content,uint32_t length)455 bRC_BACL BACL_FreeBSD::os_set_acl(JCR *jcr, BACL_type bacltype, char *content, uint32_t length){
456
457 acl_t acl;
458 acl_type_t acltype;
459
460 acltype = get_acltype(bacltype);
461 if (acltype == ACL_TYPE_DEFAULT && length == 0){
462 /* delete ACl from file when no acl data available for default acl's */
463 if (acl_delete_def_file(jcr->last_fname) == 0){
464 return bRC_BACL_ok;
465 }
466
467 berrno be;
468 switch (errno){
469 case ENOENT:
470 return bRC_BACL_ok;
471 case ENOTSUP:
472 /*
473 * If the filesystem reports it doesn't support acl's we clear the
474 * BACL_FLAG_NATIVE flag so we skip ACL restores on all other files
475 * on the same filesystem. The BACL_FLAG_NATIVE flag gets set again
476 * when we change from one filesystem to an other.
477 */
478 clear_flag(BACL_FLAG_NATIVE);
479 Mmsg(jcr->errmsg, _("acl_delete_def_file error on file \"%s\": filesystem doesn't support ACLs\n"), jcr->last_fname);
480 return bRC_BACL_error;
481 default:
482 Mmsg2(jcr->errmsg, _("acl_delete_def_file error on file \"%s\": ERR=%s\n"), jcr->last_fname, be.bstrerror());
483 return bRC_BACL_error;
484 }
485 }
486
487 acl = acl_from_text(content);
488 if (acl == NULL){
489 berrno be;
490
491 Mmsg2(jcr->errmsg, _("acl_from_text error on file \"%s\": ERR=%s\n"), jcr->last_fname, be.bstrerror());
492 Dmsg3(100, "acl_from_text error acl=%s file=%s ERR=%s\n", content, jcr->last_fname, be.bstrerror());
493 return bRC_BACL_error;
494 }
495
496 /*
497 * Restore the ACLs, but don't complain about links which really should
498 * not have attributes, and the file it is linked to may not yet be restored.
499 * This is only true for the old acl streams as in the new implementation we
500 * don't save acls of symlinks (which cannot have acls anyhow)
501 */
502 if (acl_set_file(jcr->last_fname, acltype, acl) != 0 && jcr->last_type != FT_LNK){
503 berrno be;
504 switch (errno){
505 case ENOENT:
506 acl_free(acl);
507 return bRC_BACL_ok;
508 case ENOTSUP:
509 /*
510 * If the filesystem reports it doesn't support ACLs we clear the
511 * BACL_FLAG_NATIVE flag so we skip ACL restores on all other files
512 * on the same filesystem. The BACL_FLAG_NATIVE flag gets set again
513 * when we change from one filesystem to an other.
514 */
515 clear_flag(BACL_FLAG_NATIVE);
516 Mmsg(jcr->errmsg, _("acl_set_file error on file \"%s\": filesystem doesn't support ACLs\n"), jcr->last_fname);
517 Dmsg2(100, "acl_set_file error acl=%s file=%s filesystem doesn't support ACLs\n", content, jcr->last_fname);
518 acl_free(acl);
519 return bRC_BACL_error;
520 default:
521 Mmsg2(jcr->errmsg, _("acl_set_file error on file \"%s\": ERR=%s\n"), jcr->last_fname, be.bstrerror());
522 Dmsg3(100, "acl_set_file error acl=%s file=%s ERR=%s\n", content, jcr->last_fname, be.bstrerror());
523 acl_free(acl);
524 return bRC_BACL_error;
525 }
526 }
527 acl_free(acl);
528 return bRC_BACL_ok;
529 };
530
531 #endif /* HAVE_ACL */
532
533 #endif /* HAVE_FREEBSD_OS */
534