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 Bacula® - The Network Backup Solution
34 Copyright (C) 2004-2006 Free Software Foundation Europe e.V.
36 The main author of Bacula is Kern Sibbald, with contributions from
37 many others, a complete list can be found in the file AUTHORS.
38 This program is Free Software; you can redistribute it and/or
39 modify it under the terms of version two of the GNU General Public
40 License as published by the Free Software Foundation plus additions
41 that are listed in the file LICENSE.
43 This program is distributed in the hope that it will be useful, but
44 WITHOUT ANY WARRANTY; without even the implied warranty of
45 MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
46 General Public License for more details.
48 You should have received a copy of the GNU General Public License
49 along with this program; if not, write to the Free Software
50 Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA
53 Bacula® is a registered trademark of John Walker.
54 The licensor of Bacula is the Free Software Foundation Europe
55 (FSFE), Fiduciary Program, Sumatrastrasse 25, 8006 Zürich,
56 Switzerland, email:ftf@fsfeurope.org.
68 * List of supported OSs.
69 * Not sure if all the HAVE_XYZ_OS are correct for autoconf.
70 * The ones that says man page, are coded according to man pages only.
72 #if !defined(HAVE_ACL) /* ACL support is required, of course */ \
73 || !( defined(HAVE_AIX_OS) /* man page -- may need flags */ \
74 || defined(HAVE_FREEBSD_OS) /* tested -- compile wihtout flags */ \
75 || defined(HAVE_DARWIN_OS) /* tested -- compile wihtout flags */ \
76 || defined(HAVE_IRIX_OS) /* man page -- compile without flags */ \
77 || defined(HAVE_OSF1_OS) /* man page -- may need -lpacl */ \
78 || defined(HAVE_LINUX_OS) /* tested -- compile with -lacl */ \
79 || defined(HAVE_HPUX_OS) /* man page -- may need flags */ \
80 || defined(HAVE_SUN_OS) /* tested -- compile with -lsec */ \
84 * For now we abandon this test and only test for Linux:
85 * 1. This is backwards compatible.
86 * 2. If we allow any of the other now, we may have to provide conversion
87 * routines if we ever want to distinguish them. Or just do our best
88 * with what we have and give all ACL streams a new number/type.
91 #if !defined(HAVE_ACL) \
92 || !( defined(HAVE_LINUX_OS) \
93 || defined(HAVE_FREEBSD_OS) \
94 || defined(HAVE_DARWIN_OS) \
95 || defined(HAVE_IRIX_OS) \
96 || defined(HAVE_OSF1_OS) \
97 || defined(HAVE_SUN_OS) \
100 /* bacl_get() returns the lenght of the string, or -1 on error. */
101 int bacl_get(JCR *jcr, int acltype)
103 Jmsg(jcr, M_FATAL, 0, _("ACL support not configured for your machine.\n"));
107 int bacl_set(JCR *jcr, int acltype)
109 Jmsg(jcr, M_FATAL, 0, _("ACL support not configured for your machine.\n"));
113 #elif defined(HAVE_AIX_OS)
115 #include <sys/access.h>
117 int bacl_get(JCR *jcr, int acltype)
122 if ((acl_text = acl_get(jcr->last_fname)) != NULL) {
123 len = pm_strcpy(jcr->acl_text, acl_text);
124 actuallyfree(acl_text);
130 int bacl_set(JCR *jcr, int acltype)
132 if (acl_put(jcr->last_fname, jcr->acl_text, 0) != 0) {
138 #elif defined(HAVE_FREEBSD_OS) \
139 || defined(HAVE_DARWIN_OS) \
140 || defined(HAVE_IRIX_OS) \
141 || defined(HAVE_OSF1_OS) \
142 || defined(HAVE_LINUX_OS)
144 #include <sys/types.h>
147 /* On IRIX we can get shortened ACLs */
148 #if defined(HAVE_IRIX_OS) && defined(BACL_WANT_SHORT_ACLS)
149 #define acl_to_text(acl,len) acl_to_short_text((acl), (len))
152 /* In Linux we can get numeric and/or shorted ACLs */
153 #if defined(HAVE_LINUX_OS)
154 #if defined(BACL_WANT_SHORT_ACLS) && defined(BACL_WANT_NUMERIC_IDS)
155 #define BACL_ALTERNATE_TEXT (TEXT_ABBREVIATE|TEXT_NUMERIC_IDS)
156 #elif defined(BACL_WANT_SHORT_ACLS)
157 #define BACL_ALTERNATE_TEXT TEXT_ABBREVIATE
158 #elif defined(BACL_WANT_NUMERIC_IDS)
159 #define BACL_ALTERNATE_TEXT TEXT_NUMERIC_IDS
161 #ifdef BACL_ALTERNATE_TEXT
162 #include <acl/libacl.h>
163 #define acl_to_text(acl,len) ((len), acl_to_any_text((acl), NULL, ',', BACL_ALTERNATE_TEXT))
167 int bacl_get(JCR *jcr, int acltype)
174 ostype = (acltype & BACL_TYPE_DEFAULT) ? ACL_TYPE_DEFAULT : ACL_TYPE_ACCESS;
176 acl = acl_get_file(jcr->last_fname, ostype);
178 if ((acl_text = acl_to_text(acl, NULL)) != NULL) {
179 len = pm_strcpy(jcr->acl_text, acl_text);
185 Jmsg2(jcr, M_ERROR, 0, _("acl_to_text error on file \"%s\": ERR=%s\n"),
186 jcr->last_fname, be.bstrerror());
187 Dmsg3(100, "acl_to_text error acl=%s file=%s ERR=%s\n",
188 jcr->acl_text, jcr->last_fname, be.bstrerror());
190 #ifndef HAVE_OSF1_OS /* BACL_ENOTSUP not defined for OSF1 */
191 } else if (errno == BACL_ENOTSUP) {
192 /* Not supported, just pretend there is nothing to see */
193 return pm_strcpy(jcr->acl_text, "");
196 /***** Do we really want to silently ignore errors from acl_get_file
197 and acl_to_text? *****/
201 int bacl_set(JCR *jcr, int acltype)
206 ostype = (acltype & BACL_TYPE_DEFAULT) ? ACL_TYPE_DEFAULT : ACL_TYPE_ACCESS;
208 /* If we get empty default ACLs, clear ACLs now */
209 if (ostype == ACL_TYPE_DEFAULT && strlen(jcr->acl_text) == 0) {
210 if (acl_delete_def_file(jcr->last_fname) == 0) {
214 Jmsg2(jcr, M_ERROR, 0, _("acl_delete_def_file error on file \"%s\": ERR=%s\n"),
215 jcr->last_fname, be.bstrerror());
219 acl = acl_from_text(jcr->acl_text);
222 Jmsg2(jcr, M_ERROR, 0, _("acl_from_text error on file \"%s\": ERR=%s\n"),
223 jcr->last_fname, be.bstrerror());
224 Dmsg3(100, "acl_from_text error acl=%s file=%s ERR=%s\n",
225 jcr->acl_text, jcr->last_fname, be.bstrerror());
230 * FreeBSD always fails acl_valid() - at least on valid input...
231 * As it does the right thing, given valid input, just ignore acl_valid().
233 #ifndef HAVE_FREEBSD_OS
234 if (acl_valid(acl) != 0) {
236 Jmsg2(jcr, M_ERROR, 0, _("ac_valid error on file \"%s\": ERR=%s\n"),
237 jcr->last_fname, be.bstrerror());
238 Dmsg3(100, "acl_valid error acl=%s file=%s ERR=%s\n",
239 jcr->acl_text, jcr->last_fname, be.bstrerror());
246 * Restore the ACLs, but don't complain about links which really should
247 * not have attributes, and the file it is linked to may not yet be restored.
249 if (acl_set_file(jcr->last_fname, ostype, acl) != 0 && jcr->last_type != FT_LNK) {
251 Jmsg2(jcr, M_ERROR, 0, _("acl_set_file error on file \"%s\": ERR=%s\n"),
252 jcr->last_fname, be.bstrerror());
253 Dmsg3(100, "acl_set_file error acl=%s file=%s ERR=%s\n",
254 jcr->acl_text, jcr->last_fname, be.bstrerror());
262 #elif defined(HAVE_HPUX_OS)
266 int bacl_get(JCR *jcr, int acltype)
269 struct acl_entry acls[NACLENTRIES];
272 if ((n = getacl(jcr->last_fname, 0, acls)) <= 0) {
273 if (errno == BACL_ENOTSUP) {
274 return pm_strcpy(jcr->acl_text, "");
278 if ((n = getacl(jcr->last_fname, n, acls)) > 0) {
279 if ((acl_text = acltostr(n, acls, FORM_SHORT)) != NULL) {
280 len = pm_strcpy(jcr->acl_text, acl_text);
281 actuallyfree(acl_text);
285 Jmsg2(jcr, M_ERROR, 0, _("acltostr error on file \"%s\": ERR=%s\n"),
286 jcr->last_fname, be.bstrerror());
287 Dmsg3(100, "acltostr error acl=%s file=%s ERR=%s\n",
288 jcr->acl_text, jcr->last_fname, be.bstrerror());
294 int bacl_set(JCR *jcr, int acltype)
297 struct acl_entry acls[NACLENTRIES];
299 n = strtoacl(jcr->acl_text, 0, NACLENTRIES, acls, ACL_FILEOWNER, ACL_FILEGROUP);
302 Jmsg2(jcr, M_ERROR, 0, _("strtoacl error on file \"%s\": ERR=%s\n"),
303 jcr->last_fname, be.bstrerror());
304 Dmsg3(100, "strtoacl error acl=%s file=%s ERR=%s\n",
305 jcr->acl_text, jcr->last_fname, be.bstrerror());
308 if (strtoacl(jcr->acl_text, n, NACLENTRIES, acls, ACL_FILEOWNER, ACL_FILEGROUP) != n) {
310 Jmsg2(jcr, M_ERROR, 0, _("strtoacl error on file \"%s\": ERR=%s\n"),
311 jcr->last_fname, be.bstrerror());
312 Dmsg3(100, "strtoacl error acl=%s file=%s ERR=%s\n",
313 jcr->acl_text, jcr->last_fname, be.bstrerror());
317 * Restore the ACLs, but don't complain about links which really should
318 * not have attributes, and the file it is linked to may not yet be restored.
320 if (setacl(jcr->last_fname, n, acls) != 0 && jcr->last_type != FT_LNK) {
322 Jmsg2(jcr, M_ERROR, 0, _("setacl error on file \"%s\": ERR=%s\n"),
323 jcr->last_fname, be.bstrerror());
324 Dmsg3(100, "setacl error acl=%s file=%s ERR=%s\n",
325 jcr->acl_text, jcr->last_fname, be.bstrerror());
331 #elif defined(HAVE_SUN_OS)
334 int bacl_get(JCR *jcr, int acltype)
340 n = acl(jcr->last_fname, GETACLCNT, 0, NULL);
341 if (n < MIN_ACL_ENTRIES) {
343 } else if (n == MIN_ACL_ENTRIES) {
344 /* The ACLs simply reflect the (already known) standard permissions */
345 return pm_strcpy(jcr->acl_text, "");
347 if ((acls = (aclent_t *)malloc(n * sizeof(aclent_t))) == NULL) {
350 if (acl(jcr->last_fname, GETACL, n, acls) == n) {
351 if ((acl_text = acltotext(acls, n)) != NULL) {
352 len = pm_strcpy(jcr->acl_text, acl_text);
353 actuallyfree(acl_text);
358 Jmsg2(jcr, M_ERROR, 0, _("acltotext error on file \"%s\": ERR=%s\n"),
359 jcr->last_fname, be.bstrerror());
360 Dmsg3(100, "acltotext error acl=%s file=%s ERR=%s\n",
361 jcr->acl_text, jcr->last_fname, be.bstrerror());
367 int bacl_set(JCR *jcr, int acltype)
372 acls = aclfromtext(jcr->acl_text, &n);
375 Jmsg2(jcr, M_ERROR, 0, _("aclfromtext error on file \"%s\": ERR=%s\n"),
376 jcr->last_fname, be.bstrerror());
377 Dmsg3(100, "aclfromtext error acl=%s file=%s ERR=%s\n",
378 jcr->acl_text, jcr->last_fname, be.bstrerror());
382 * Restore the ACLs, but don't complain about links which really should
383 * not have attributes, and the file it is linked to may not yet be restored.
385 if (acl(jcr->last_fname, SETACL, n, acls) == -1 && jcr->last_type != FT_LNK) {
387 Jmsg2(jcr, M_ERROR, 0, _("acl(SETACL) error on file \"%s\": ERR=%s\n"),
388 jcr->last_fname, be.bstrerror());
389 Dmsg3(100, "acl(SETACL) error acl=%s file=%s ERR=%s\n",
390 jcr->acl_text, jcr->last_fname, be.bstrerror());
406 * Compile and set up with eg. with eg.
408 * $ cc -DTEST_PROGRAM -DHAVE_SUN_OS -lsec -o acl acl.c
411 * You can then list ACLs with acl and copy them with aclcp.
413 * For a list of compiler flags, see the list preceding the big #if below.
419 #include <sys/stat.h>
422 #define BACLLEN 65535
423 #define pm_strcpy(d,s) (strncpy(d, s, BACLLEN - 1) == NULL ? -1 : (int)strlen(d))
424 #define Dmsg0(n,s) fprintf(stderr, s)
425 #define Dmsg1(n,s,a1) fprintf(stderr, s, a1)
426 #define Dmsg2(n,s,a1,a2) fprintf(stderr, s, a1, a2)
428 int aclls(char *fname);
429 int aclcp(char *src, char *dst);
433 char acl_text[BACLLEN];
435 typedef struct JCRstruct JCR;
438 int main(int argc, char **argv)
444 Dmsg0(200, "Cannot determine my own name\n");
448 prgname = last_path_separator(argv[0]);
449 if (prgname == NULL || *++prgname == '\0') {
455 /* aclcp "copies" ACLs - set ACLs on destination equal to ACLs on source */
456 if (strcmp(prgname, "aclcp") == 0) {
458 if (strcmp(*argv, "-v") == 0) {
464 Dmsg2(200, "%s: wrong number of arguments\n"
465 "usage:\t%s [-v] source destination\n"
466 "\tCopies ACLs from source to destination.\n"
467 "\tSpecify -v to show ACLs after copy for verification.\n",
471 if (strcmp(argv[0], argv[1]) == 0) {
472 Dmsg2(200, "%s: identical source and destination.\n"
473 "usage:\t%s [-v] source destination\n"
474 "\tCopies ACLs from source to destination.\n"
475 "\tSpecify -v to show ACLs after copy for verification.\n",
482 status = aclcp(argv[0], argv[1]);
483 if (verbose && status == 0) {
489 /* Default: just list ACLs */
491 Dmsg2(200, "%s: missing arguments\n"
492 "usage:\t%s file ...\n"
493 "\tLists ACLs of specified files or directories.\n",
498 if (!aclls(*argv++)) {
499 status = EXIT_FAILURE;
506 /**** Test program *****/
507 int aclcp(char *src, char *dst)
511 if (lstat(dst, &st) != 0) {
512 Dmsg0(200, "aclcp: destination does not exist\n");
515 if (S_ISLNK(st.st_mode)) {
516 Dmsg0(200, "aclcp: cannot set ACL on symlinks\n");
519 if (lstat(src, &st) != 0) {
520 Dmsg0(200, "aclcp: source does not exist\n");
523 if (S_ISLNK(st.st_mode)) {
524 Dmsg0(200, "aclcp: will not read ACL from symlinks\n");
528 jcr.last_fname = src;
529 if (bacl_get(&jcr, BACL_TYPE_ACCESS) < 0) {
530 Dmsg1(200, "aclcp: could not read ACLs for %s\n", jcr.last_fname);
533 jcr.last_fname = dst;
534 if (bacl_set(&jcr, BACL_TYPE_ACCESS) < 0) {
535 Dmsg1(200, "aclcp: could not set ACLs on %s\n", jcr.last_fname);
540 if (S_ISDIR(st.st_mode) && (BACL_CAP & BACL_CAP_DEFAULTS_DIR)) {
541 jcr.last_fname = src;
542 if (bacl_get(&jcr, BACL_TYPE_DEFAULT) < 0) {
543 Dmsg1(200, "aclcp: could not read default ACLs for %s\n", jcr.last_fname);
546 jcr.last_fname = dst;
547 if (bacl_set(&jcr, BACL_TYPE_DEFAULT) < 0) {
548 Dmsg1(200, "aclcp: could not set default ACLs on %s\n", jcr.last_fname);
557 /**** Test program *****/
558 int aclls(char *fname)
563 if (lstat(fname, &st) != 0) {
564 Dmsg0(200, "acl: source does not exist\n");
567 if (S_ISLNK(st.st_mode)) {
568 Dmsg0(200, "acl: will not read ACL from symlinks\n");
572 jcr.last_fname = fname;
574 len = bacl_get(&jcr, BACL_TYPE_ACCESS);
576 Dmsg1(200, "acl: could not read ACLs for %s\n", jcr.last_fname);
578 } else if (len == 0) {
579 printf("#file: %s [standard permissions - or unsupported]\n\n", jcr.last_fname);
581 printf("#file: %s\n%s\n", jcr.last_fname, jcr.acl_text);
584 if (S_ISDIR(st.st_mode) && (BACL_CAP & BACL_CAP_DEFAULTS_DIR)) {
585 len = bacl_get(&jcr, BACL_TYPE_DEFAULT);
587 Dmsg1(200, "acl: could not read default ACLs for %s\n", jcr.last_fname);
589 } else if (len == 0) {
590 printf("#file: %s [default, none - or unsupported]\n\n", jcr.last_fname);
592 printf("#file: %s [default]\n%s\n", jcr.last_fname, jcr.acl_text);