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-2005 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.
50 /* So we can free system allocated memory */
53 #define malloc &* dont use malloc in this routine
59 * Compile and set up with eg. with eg.
61 * $ cc -DTEST_PROGRAM -DHAVE_SUN_OS -lsec -o acl acl.c
64 * You can then list ACLs with acl and copy them with aclcp.
66 * For a list of compiler flags, see the list preceding the big #if below.
76 #define pm_strcpy(d,s) (strncpy(d, s, BACLLEN - 1) == NULL ? -1 : (int)strlen(d))
77 #define Dmsg0(n,s) fprintf(stderr, s)
78 #define Dmsg1(n,s,a1) fprintf(stderr, s, a1)
79 #define Dmsg2(n,s,a1,a2) fprintf(stderr, s, a1, a2)
81 int aclls(char *fname);
82 int aclcp(char *src, char *dst);
86 char acl_text[BACLLEN];
88 typedef struct JCRstruct JCR;
93 * List of supported OSs.
94 * Not sure if all the HAVE_XYZ_OS are correct for autoconf.
95 * The ones that says man page, are coded according to man pages only.
97 #if !defined(HAVE_ACL) /* ACL support is required, of course */ \
98 || !( defined(HAVE_AIX_OS) /* man page -- may need flags */ \
99 || defined(HAVE_FREEBSD_OS) /* tested -- compile wihtout flags */ \
100 || defined(HAVE_DARWIN_OS) /* tested -- compile wihtout flags */ \
101 || defined(HAVE_IRIX_OS) /* man page -- compile without flags */ \
102 || defined(HAVE_OSF1_OS) /* man page -- may need -lpacl */ \
103 || defined(HAVE_LINUX_OS) /* tested -- compile with -lacl */ \
104 || defined(HAVE_HPUX_OS) /* man page -- may need flags */ \
105 || defined(HAVE_SUN_OS) /* tested -- compile with -lsec */ \
109 * For now we abandon this test and only test for Linux:
110 * 1. This is backwards compatible.
111 * 2. If we allow any of the other now, we may have to provide conversion
112 * routines if we ever want to distinguish them. Or just do our best
113 * with what we have and give all ACL streams a new number/type.
116 #if !defined(HAVE_ACL) \
117 || !( defined(HAVE_LINUX_OS) \
118 || defined(HAVE_FREEBSD_OS) \
119 || defined(HAVE_DARWIN_OS) \
122 /* bacl_get() returns the lenght of the string, or -1 on error. */
123 int bacl_get(JCR *jcr, int acltype)
128 int bacl_set(JCR *jcr, int acltype)
133 #elif defined(HAVE_AIX_OS)
135 #include <sys/access.h>
137 int bacl_get(JCR *jcr, int acltype)
142 if ((acl_text = acl_get(jcr->last_fname)) != NULL) {
143 len = pm_strcpy(jcr->acl_text, acl_text);
150 int bacl_set(JCR *jcr, int acltype)
152 if (acl_put(jcr->last_fname, jcr->acl_text, 0) != 0) {
158 #elif defined(HAVE_FREEBSD_OS) \
159 || defined(HAVE_DARWIN_OS) \
160 || defined(HAVE_IRIX_OS) \
161 || defined(HAVE_OSF1_OS) \
162 || defined(HAVE_LINUX_OS)
164 #include <sys/types.h>
167 /* On IRIX we can get shortened ACLs */
168 #if defined(HAVE_IRIX_OS) && defined(BACL_WANT_SHORT_ACLS)
169 #define acl_to_text(acl,len) acl_to_short_text((acl), (len))
172 /* In Linux we can get numeric and/or shorted ACLs */
173 #if defined(HAVE_LINUX_OS)
174 #if defined(BACL_WANT_SHORT_ACLS) && defined(BACL_WANT_NUMERIC_IDS)
175 #define BACL_ALTERNATE_TEXT (TEXT_ABBREVIATE|TEXT_NUMERIC_IDS)
176 #elif defined(BACL_WANT_SHORT_ACLS)
177 #define BACL_ALTERNATE_TEXT TEXT_ABBREVIATE
178 #elif defined(BACL_WANT_NUMERIC_IDS)
179 #define BACL_ALTERNATE_TEXT TEXT_NUMERIC_IDS
181 #ifdef BACL_ALTERNATE_TEXT
182 #include <acl/libacl.h>
183 #define acl_to_text(acl,len) ((len), acl_to_any_text((acl), NULL, ',', BACL_ALTERNATE_TEXT))
187 int bacl_get(JCR *jcr, int acltype)
194 ostype = (acltype & BACL_TYPE_DEFAULT) ? ACL_TYPE_DEFAULT : ACL_TYPE_ACCESS;
196 acl = acl_get_file(jcr->last_fname, ostype);
198 if ((acl_text = acl_to_text(acl, NULL)) != NULL) {
199 len = pm_strcpy(jcr->acl_text, acl_text);
205 #ifndef HAVE_OSF1_OS /* BACL_ENOTSUP not defined for OSF1 */
206 } else if (errno == BACL_ENOTSUP) {
207 /* Not supported, just pretend there is nothing to see */
208 return pm_strcpy(jcr->acl_text, "");
211 /***** Do we really want to silently ignore errors from acl_get_file
212 and acl_to_text? *****/
216 int bacl_set(JCR *jcr, int acltype)
221 ostype = (acltype & BACL_TYPE_DEFAULT) ? ACL_TYPE_DEFAULT : ACL_TYPE_ACCESS;
223 /* If we get empty default ACLs, clear ACLs now */
224 if (ostype == ACL_TYPE_DEFAULT && strlen(jcr->acl_text) == 0) {
225 if (acl_delete_def_file(jcr->last_fname) == 0) {
231 acl = acl_from_text(jcr->acl_text);
237 * FreeBSD always fails acl_valid() - at least on valid input...
238 * As it does the right thing, given valid input, just ignore acl_valid().
240 #ifndef HAVE_FREEBSD_OS
241 if (acl_valid(acl) != 0) {
247 if (acl_set_file(jcr->last_fname, ostype, acl) != 0) {
255 #elif defined(HAVE_HPUX_OS)
259 int bacl_get(JCR *jcr, int acltype)
262 struct acl_entry acls[NACLENTRIES];
265 if ((n = getacl(jcr->last_fname, 0, acls)) <= 0) {
266 if (errno == BACL_ENOTSUP) {
267 return pm_strcpy(jcr->acl_text, "");
271 if ((n = getacl(jcr->last_fname, n, acls)) > 0) {
272 if ((acl_text = acltostr(n, acls, FORM_SHORT)) != NULL) {
273 len = pm_strcpy(jcr->acl_text, acl_text);
281 int bacl_set(JCR *jcr, int acltype)
284 struct acl_entry acls[NACLENTRIES];
286 n = strtoacl(jcr->acl_text, 0, NACLENTRIES, acls, ACL_FILEOWNER, ACL_FILEGROUP);
290 if (strtoacl(jcr->acl_text, n, NACLENTRIES, acls, ACL_FILEOWNER, ACL_FILEGROUP) != n) {
293 if (setacl(jcr->last_fname, n, acls) != 0) {
299 #elif defined(HAVE_SUN_OS)
302 int bacl_get(JCR *jcr, int acltype)
308 n = acl(jcr->last_fname, GETACLCNT, 0, NULL);
309 if (n < MIN_ACL_ENTRIES) {
311 } else if (n == MIN_ACL_ENTRIES) {
312 /* The ACLs simply reflect the (already known) standard permissions */
313 return pm_strcpy(jcr->acl_text, "");
315 if ((acls = (aclent_t *)malloc(n * sizeof(aclent_t))) == NULL) {
318 if (acl(jcr->last_fname, GETACL, n, acls) == n) {
319 if ((acl_text = acltotext(acls, n)) != NULL) {
320 len = pm_strcpy(jcr->acl_text, acl_text);
330 int bacl_set(JCR *jcr, int acltype)
335 acls = aclfromtext(jcr->acl_text, &n);
339 if (acl(jcr->last_fname, SETACL, n, acls) != 0) {
351 int main(int argc, char **argv)
357 Dmsg0(200, "Cannot determine my own name\n");
361 prgname = strrchr(argv[0], '/');
362 if (prgname == NULL || *++prgname == '\0') {
368 /* aclcp "copies" ACLs - set ACLs on destination equal to ACLs on source */
369 if (strcmp(prgname, "aclcp") == 0) {
371 if (strcmp(*argv, "-v") == 0) {
377 Dmsg2(200, "%s: wrong number of arguments\n"
378 "usage:\t%s [-v] source destination\n"
379 "\tCopies ACLs from source to destination.\n"
380 "\tSpecify -v to show ACLs after copy for verification.\n",
384 if (strcmp(argv[0], argv[1]) == 0) {
385 Dmsg2(200, "%s: identical source and destination.\n"
386 "usage:\t%s [-v] source destination\n"
387 "\tCopies ACLs from source to destination.\n"
388 "\tSpecify -v to show ACLs after copy for verification.\n",
395 status = aclcp(argv[0], argv[1]);
396 if (verbose && status == 0) {
402 /* Default: just list ACLs */
404 Dmsg2(200, "%s: missing arguments\n"
405 "usage:\t%s file ...\n"
406 "\tLists ACLs of specified files or directories.\n",
411 if (!aclls(*argv++)) {
412 status = EXIT_FAILURE;
419 /**** Test program *****/
420 int aclcp(char *src, char *dst)
424 if (lstat(dst, &st) != 0) {
425 Dmsg0(200, "aclcp: destination does not exist\n");
428 if (S_ISLNK(st.st_mode)) {
429 Dmsg0(200, "aclcp: cannot set ACL on symlinks\n");
432 if (lstat(src, &st) != 0) {
433 Dmsg0(200, "aclcp: source does not exist\n");
436 if (S_ISLNK(st.st_mode)) {
437 Dmsg0(200, "aclcp: will not read ACL from symlinks\n");
441 jcr.last_fname = src;
442 if (bacl_get(&jcr, BACL_TYPE_ACCESS) < 0) {
443 Dmsg1(200, "aclcp: could not read ACLs for %s\n", jcr.last_fname);
446 jcr.last_fname = dst;
447 if (bacl_set(&jcr, BACL_TYPE_ACCESS) < 0) {
448 Dmsg1(200, "aclcp: could not set ACLs on %s\n", jcr.last_fname);
453 if (S_ISDIR(st.st_mode) && (BACL_CAP & BACL_CAP_DEFAULTS_DIR)) {
454 jcr.last_fname = src;
455 if (bacl_get(&jcr, BACL_TYPE_DEFAULT) < 0) {
456 Dmsg1(200, "aclcp: could not read default ACLs for %s\n", jcr.last_fname);
459 jcr.last_fname = dst;
460 if (bacl_set(&jcr, BACL_TYPE_DEFAULT) < 0) {
461 Dmsg1(200, "aclcp: could not set default ACLs on %s\n", jcr.last_fname);
470 /**** Test program *****/
471 int aclls(char *fname)
476 if (lstat(fname, &st) != 0) {
477 Dmsg0(200, "acl: source does not exist\n");
480 if (S_ISLNK(st.st_mode)) {
481 Dmsg0(200, "acl: will not read ACL from symlinks\n");
485 jcr.last_fname = fname;
487 len = bacl_get(&jcr, BACL_TYPE_ACCESS);
489 Dmsg1(200, "acl: could not read ACLs for %s\n", jcr.last_fname);
491 } else if (len == 0) {
492 printf("#file: %s [standard permissions - or unsupported]\n\n", jcr.last_fname);
494 printf("#file: %s\n%s\n", jcr.last_fname, jcr.acl_text);
497 if (S_ISDIR(st.st_mode) && (BACL_CAP & BACL_CAP_DEFAULTS_DIR)) {
498 len = bacl_get(&jcr, BACL_TYPE_DEFAULT);
500 Dmsg1(200, "acl: could not read default ACLs for %s\n", jcr.last_fname);
502 } else if (len == 0) {
503 printf("#file: %s [default, none - or unsupported]\n\n", jcr.last_fname);
505 printf("#file: %s [default]\n%s\n", jcr.last_fname, jcr.acl_text);