2 * Functions to handle ACL for bacula.
4 * We handle two different typers of ACLs: access and default ACLS.
5 * Default ACLs only apply to directories.
7 * On some systems (eg. linux and FreeBSD) we must obtain the two ACLs
8 * independently, while others (eg. Solaris) provide both in one call.
10 * As for the streams to use, we have two choices:
12 * 1. Build a generic framework.
13 * With two different types of ACLs, supported differently, we
14 * probably end up encoding and decoding everything ourselves.
16 * 2. Take the easy way out.
17 * Just handle each platform individually, assuming that backups
18 * and restores are done on the same (kind of) client.
20 * Currently we take the easy way out. We use two kinds of streams, one
21 * for access ACLs and one for default ACLs. If an OS mixes the two, we
22 * send the mix in the access ACL stream.
24 * Looking at more man pages, supporting a framework seems really hard
25 * if we want to support HP-UX. Deity knows what AIX is up to.
27 * Written by Preben 'Peppe' Guldberg, December MMIV
32 Copyright (C) 2004-2006 Kern Sibbald
34 This program is free software; you can redistribute it and/or
35 modify it under the terms of the GNU General Public License
36 version 2 as amended with additional clauses defined in the
37 file LICENSE in the main source directory.
39 This program is distributed in the hope that it will be useful,
40 but WITHOUT ANY WARRANTY; without even the implied warranty of
41 MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
42 the file LICENSE for additional details.
54 * List of supported OSs.
55 * Not sure if all the HAVE_XYZ_OS are correct for autoconf.
56 * The ones that says man page, are coded according to man pages only.
58 #if !defined(HAVE_ACL) /* ACL support is required, of course */ \
59 || !( defined(HAVE_AIX_OS) /* man page -- may need flags */ \
60 || defined(HAVE_FREEBSD_OS) /* tested -- compile wihtout flags */ \
61 || defined(HAVE_DARWIN_OS) /* tested -- compile wihtout flags */ \
62 || defined(HAVE_IRIX_OS) /* man page -- compile without flags */ \
63 || defined(HAVE_OSF1_OS) /* man page -- may need -lpacl */ \
64 || defined(HAVE_LINUX_OS) /* tested -- compile with -lacl */ \
65 || defined(HAVE_HPUX_OS) /* man page -- may need flags */ \
66 || defined(HAVE_SUN_OS) /* tested -- compile with -lsec */ \
70 * For now we abandon this test and only test for Linux:
71 * 1. This is backwards compatible.
72 * 2. If we allow any of the other now, we may have to provide conversion
73 * routines if we ever want to distinguish them. Or just do our best
74 * with what we have and give all ACL streams a new number/type.
77 #if !defined(HAVE_ACL) \
78 || !( defined(HAVE_LINUX_OS) \
79 || defined(HAVE_FREEBSD_OS) \
80 || defined(HAVE_DARWIN_OS) \
81 || defined(HAVE_IRIX_OS) \
82 || defined(HAVE_OSF1_OS) \
83 || defined(HAVE_SUN_OS) \
86 /* bacl_get() returns the lenght of the string, or -1 on error. */
87 int bacl_get(JCR *jcr, int acltype)
89 Jmsg(jcr, M_FATAL, 0, _("ACL support not configured for your machine.\n"));
93 int bacl_set(JCR *jcr, int acltype)
95 Jmsg(jcr, M_FATAL, 0, _("ACL support not configured for your machine.\n"));
99 #elif defined(HAVE_AIX_OS)
101 #include <sys/access.h>
103 int bacl_get(JCR *jcr, int acltype)
108 if ((acl_text = acl_get(jcr->last_fname)) != NULL) {
109 len = pm_strcpy(jcr->acl_text, acl_text);
110 actuallyfree(acl_text);
116 int bacl_set(JCR *jcr, int acltype)
118 if (acl_put(jcr->last_fname, jcr->acl_text, 0) != 0) {
124 #elif defined(HAVE_FREEBSD_OS) \
125 || defined(HAVE_DARWIN_OS) \
126 || defined(HAVE_IRIX_OS) \
127 || defined(HAVE_OSF1_OS) \
128 || defined(HAVE_LINUX_OS)
130 #include <sys/types.h>
133 /* On IRIX we can get shortened ACLs */
134 #if defined(HAVE_IRIX_OS) && defined(BACL_WANT_SHORT_ACLS)
135 #define acl_to_text(acl,len) acl_to_short_text((acl), (len))
138 /* In Linux we can get numeric and/or shorted ACLs */
139 #if defined(HAVE_LINUX_OS)
140 #if defined(BACL_WANT_SHORT_ACLS) && defined(BACL_WANT_NUMERIC_IDS)
141 #define BACL_ALTERNATE_TEXT (TEXT_ABBREVIATE|TEXT_NUMERIC_IDS)
142 #elif defined(BACL_WANT_SHORT_ACLS)
143 #define BACL_ALTERNATE_TEXT TEXT_ABBREVIATE
144 #elif defined(BACL_WANT_NUMERIC_IDS)
145 #define BACL_ALTERNATE_TEXT TEXT_NUMERIC_IDS
147 #ifdef BACL_ALTERNATE_TEXT
148 #include <acl/libacl.h>
149 #define acl_to_text(acl,len) ((len), acl_to_any_text((acl), NULL, ',', BACL_ALTERNATE_TEXT))
153 int bacl_get(JCR *jcr, int acltype)
160 ostype = (acltype & BACL_TYPE_DEFAULT) ? ACL_TYPE_DEFAULT : ACL_TYPE_ACCESS;
162 acl = acl_get_file(jcr->last_fname, ostype);
164 if ((acl_text = acl_to_text(acl, NULL)) != NULL) {
165 len = pm_strcpy(jcr->acl_text, acl_text);
171 Jmsg2(jcr, M_ERROR, 0, _("acl_to_text error on file \"%s\": ERR=%s\n"),
172 jcr->last_fname, be.strerror());
173 Dmsg3(100, "acl_to_text error acl=%s file=%s ERR=%s\n",
174 jcr->acl_text, jcr->last_fname, be.strerror());
176 #ifndef HAVE_OSF1_OS /* BACL_ENOTSUP not defined for OSF1 */
177 } else if (errno == BACL_ENOTSUP) {
178 /* Not supported, just pretend there is nothing to see */
179 return pm_strcpy(jcr->acl_text, "");
182 /***** Do we really want to silently ignore errors from acl_get_file
183 and acl_to_text? *****/
187 int bacl_set(JCR *jcr, int acltype)
192 ostype = (acltype & BACL_TYPE_DEFAULT) ? ACL_TYPE_DEFAULT : ACL_TYPE_ACCESS;
194 /* If we get empty default ACLs, clear ACLs now */
195 if (ostype == ACL_TYPE_DEFAULT && strlen(jcr->acl_text) == 0) {
196 if (acl_delete_def_file(jcr->last_fname) == 0) {
200 Jmsg2(jcr, M_ERROR, 0, _("acl_delete_def_file error on file \"%s\": ERR=%s\n"),
201 jcr->last_fname, be.strerror());
205 acl = acl_from_text(jcr->acl_text);
208 Jmsg2(jcr, M_ERROR, 0, _("acl_from_text error on file \"%s\": ERR=%s\n"),
209 jcr->last_fname, be.strerror());
210 Dmsg3(100, "acl_from_text error acl=%s file=%s ERR=%s\n",
211 jcr->acl_text, jcr->last_fname, be.strerror());
216 * FreeBSD always fails acl_valid() - at least on valid input...
217 * As it does the right thing, given valid input, just ignore acl_valid().
219 #ifndef HAVE_FREEBSD_OS
220 if (acl_valid(acl) != 0) {
222 Jmsg2(jcr, M_ERROR, 0, _("ac_valid error on file \"%s\": ERR=%s\n"),
223 jcr->last_fname, be.strerror());
224 Dmsg3(100, "acl_valid error acl=%s file=%s ERR=%s\n",
225 jcr->acl_text, jcr->last_fname, be.strerror());
232 * Restore the ACLs, but don't complain about links which really should
233 * not have attributes, and the file it is linked to may not yet be restored.
235 if (acl_set_file(jcr->last_fname, ostype, acl) != 0 && jcr->last_type != FT_LNK) {
237 Jmsg2(jcr, M_ERROR, 0, _("acl_set_file error on file \"%s\": ERR=%s\n"),
238 jcr->last_fname, be.strerror());
239 Dmsg3(100, "acl_set_file error acl=%s file=%s ERR=%s\n",
240 jcr->acl_text, jcr->last_fname, be.strerror());
248 #elif defined(HAVE_HPUX_OS)
252 int bacl_get(JCR *jcr, int acltype)
255 struct acl_entry acls[NACLENTRIES];
258 if ((n = getacl(jcr->last_fname, 0, acls)) <= 0) {
259 if (errno == BACL_ENOTSUP) {
260 return pm_strcpy(jcr->acl_text, "");
264 if ((n = getacl(jcr->last_fname, n, acls)) > 0) {
265 if ((acl_text = acltostr(n, acls, FORM_SHORT)) != NULL) {
266 len = pm_strcpy(jcr->acl_text, acl_text);
267 actuallyfree(acl_text);
271 Jmsg2(jcr, M_ERROR, 0, _("acltostr error on file \"%s\": ERR=%s\n"),
272 jcr->last_fname, be.strerror());
273 Dmsg3(100, "acltostr error acl=%s file=%s ERR=%s\n",
274 jcr->acl_text, jcr->last_fname, be.strerror());
280 int bacl_set(JCR *jcr, int acltype)
283 struct acl_entry acls[NACLENTRIES];
285 n = strtoacl(jcr->acl_text, 0, NACLENTRIES, acls, ACL_FILEOWNER, ACL_FILEGROUP);
288 Jmsg2(jcr, M_ERROR, 0, _("strtoacl error on file \"%s\": ERR=%s\n"),
289 jcr->last_fname, be.strerror());
290 Dmsg3(100, "strtoacl error acl=%s file=%s ERR=%s\n",
291 jcr->acl_text, jcr->last_fname, be.strerror());
294 if (strtoacl(jcr->acl_text, n, NACLENTRIES, acls, ACL_FILEOWNER, ACL_FILEGROUP) != n) {
296 Jmsg2(jcr, M_ERROR, 0, _("strtoacl error on file \"%s\": ERR=%s\n"),
297 jcr->last_fname, be.strerror());
298 Dmsg3(100, "strtoacl error acl=%s file=%s ERR=%s\n",
299 jcr->acl_text, jcr->last_fname, be.strerror());
303 * Restore the ACLs, but don't complain about links which really should
304 * not have attributes, and the file it is linked to may not yet be restored.
306 if (setacl(jcr->last_fname, n, acls) != 0 && jcr->last_type != FT_LNK) {
308 Jmsg2(jcr, M_ERROR, 0, _("setacl error on file \"%s\": ERR=%s\n"),
309 jcr->last_fname, be.strerror());
310 Dmsg3(100, "setacl error acl=%s file=%s ERR=%s\n",
311 jcr->acl_text, jcr->last_fname, be.strerror());
317 #elif defined(HAVE_SUN_OS)
320 int bacl_get(JCR *jcr, int acltype)
326 n = acl(jcr->last_fname, GETACLCNT, 0, NULL);
327 if (n < MIN_ACL_ENTRIES) {
329 } else if (n == MIN_ACL_ENTRIES) {
330 /* The ACLs simply reflect the (already known) standard permissions */
331 return pm_strcpy(jcr->acl_text, "");
333 if ((acls = (aclent_t *)malloc(n * sizeof(aclent_t))) == NULL) {
336 if (acl(jcr->last_fname, GETACL, n, acls) == n) {
337 if ((acl_text = acltotext(acls, n)) != NULL) {
338 len = pm_strcpy(jcr->acl_text, acl_text);
339 actuallyfree(acl_text);
344 Jmsg2(jcr, M_ERROR, 0, _("acltotext error on file \"%s\": ERR=%s\n"),
345 jcr->last_fname, be.strerror());
346 Dmsg3(100, "acltotext error acl=%s file=%s ERR=%s\n",
347 jcr->acl_text, jcr->last_fname, be.strerror());
353 int bacl_set(JCR *jcr, int acltype)
358 acls = aclfromtext(jcr->acl_text, &n);
361 Jmsg2(jcr, M_ERROR, 0, _("aclfromtext error on file \"%s\": ERR=%s\n"),
362 jcr->last_fname, be.strerror());
363 Dmsg3(100, "aclfromtext error acl=%s file=%s ERR=%s\n",
364 jcr->acl_text, jcr->last_fname, be.strerror());
368 * Restore the ACLs, but don't complain about links which really should
369 * not have attributes, and the file it is linked to may not yet be restored.
371 if (acl(jcr->last_fname, SETACL, n, acls) == -1 && jcr->last_type != FT_LNK) {
373 Jmsg2(jcr, M_ERROR, 0, _("acl(SETACL) error on file \"%s\": ERR=%s\n"),
374 jcr->last_fname, be.strerror());
375 Dmsg3(100, "acl(SETACL) error acl=%s file=%s ERR=%s\n",
376 jcr->acl_text, jcr->last_fname, be.strerror());
392 * Compile and set up with eg. with eg.
394 * $ cc -DTEST_PROGRAM -DHAVE_SUN_OS -lsec -o acl acl.c
397 * You can then list ACLs with acl and copy them with aclcp.
399 * For a list of compiler flags, see the list preceding the big #if below.
405 #include <sys/stat.h>
408 #define BACLLEN 65535
409 #define pm_strcpy(d,s) (strncpy(d, s, BACLLEN - 1) == NULL ? -1 : (int)strlen(d))
410 #define Dmsg0(n,s) fprintf(stderr, s)
411 #define Dmsg1(n,s,a1) fprintf(stderr, s, a1)
412 #define Dmsg2(n,s,a1,a2) fprintf(stderr, s, a1, a2)
414 int aclls(char *fname);
415 int aclcp(char *src, char *dst);
419 char acl_text[BACLLEN];
421 typedef struct JCRstruct JCR;
424 int main(int argc, char **argv)
430 Dmsg0(200, "Cannot determine my own name\n");
434 prgname = last_path_separator(argv[0]);
435 if (prgname == NULL || *++prgname == '\0') {
441 /* aclcp "copies" ACLs - set ACLs on destination equal to ACLs on source */
442 if (strcmp(prgname, "aclcp") == 0) {
444 if (strcmp(*argv, "-v") == 0) {
450 Dmsg2(200, "%s: wrong number of arguments\n"
451 "usage:\t%s [-v] source destination\n"
452 "\tCopies ACLs from source to destination.\n"
453 "\tSpecify -v to show ACLs after copy for verification.\n",
457 if (strcmp(argv[0], argv[1]) == 0) {
458 Dmsg2(200, "%s: identical source and destination.\n"
459 "usage:\t%s [-v] source destination\n"
460 "\tCopies ACLs from source to destination.\n"
461 "\tSpecify -v to show ACLs after copy for verification.\n",
468 status = aclcp(argv[0], argv[1]);
469 if (verbose && status == 0) {
475 /* Default: just list ACLs */
477 Dmsg2(200, "%s: missing arguments\n"
478 "usage:\t%s file ...\n"
479 "\tLists ACLs of specified files or directories.\n",
484 if (!aclls(*argv++)) {
485 status = EXIT_FAILURE;
492 /**** Test program *****/
493 int aclcp(char *src, char *dst)
497 if (lstat(dst, &st) != 0) {
498 Dmsg0(200, "aclcp: destination does not exist\n");
501 if (S_ISLNK(st.st_mode)) {
502 Dmsg0(200, "aclcp: cannot set ACL on symlinks\n");
505 if (lstat(src, &st) != 0) {
506 Dmsg0(200, "aclcp: source does not exist\n");
509 if (S_ISLNK(st.st_mode)) {
510 Dmsg0(200, "aclcp: will not read ACL from symlinks\n");
514 jcr.last_fname = src;
515 if (bacl_get(&jcr, BACL_TYPE_ACCESS) < 0) {
516 Dmsg1(200, "aclcp: could not read ACLs for %s\n", jcr.last_fname);
519 jcr.last_fname = dst;
520 if (bacl_set(&jcr, BACL_TYPE_ACCESS) < 0) {
521 Dmsg1(200, "aclcp: could not set ACLs on %s\n", jcr.last_fname);
526 if (S_ISDIR(st.st_mode) && (BACL_CAP & BACL_CAP_DEFAULTS_DIR)) {
527 jcr.last_fname = src;
528 if (bacl_get(&jcr, BACL_TYPE_DEFAULT) < 0) {
529 Dmsg1(200, "aclcp: could not read default ACLs for %s\n", jcr.last_fname);
532 jcr.last_fname = dst;
533 if (bacl_set(&jcr, BACL_TYPE_DEFAULT) < 0) {
534 Dmsg1(200, "aclcp: could not set default ACLs on %s\n", jcr.last_fname);
543 /**** Test program *****/
544 int aclls(char *fname)
549 if (lstat(fname, &st) != 0) {
550 Dmsg0(200, "acl: source does not exist\n");
553 if (S_ISLNK(st.st_mode)) {
554 Dmsg0(200, "acl: will not read ACL from symlinks\n");
558 jcr.last_fname = fname;
560 len = bacl_get(&jcr, BACL_TYPE_ACCESS);
562 Dmsg1(200, "acl: could not read ACLs for %s\n", jcr.last_fname);
564 } else if (len == 0) {
565 printf("#file: %s [standard permissions - or unsupported]\n\n", jcr.last_fname);
567 printf("#file: %s\n%s\n", jcr.last_fname, jcr.acl_text);
570 if (S_ISDIR(st.st_mode) && (BACL_CAP & BACL_CAP_DEFAULTS_DIR)) {
571 len = bacl_get(&jcr, BACL_TYPE_DEFAULT);
573 Dmsg1(200, "acl: could not read default ACLs for %s\n", jcr.last_fname);
575 } else if (len == 0) {
576 printf("#file: %s [default, none - or unsupported]\n\n", jcr.last_fname);
578 printf("#file: %s [default]\n%s\n", jcr.last_fname, jcr.acl_text);