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_IRIX_OS) /* man page -- compile without flags */ \
101 || defined(HAVE_OSF1_OS) /* man page -- may need -lpacl */ \
102 || defined(HAVE_LINUX_OS) /* tested -- compile with -lacl */ \
103 || defined(HAVE_HPUX_OS) /* man page -- may need flags */ \
104 || defined(HAVE_SUN_OS) /* tested -- compile with -lsec */ \
108 * For now we abandon this test and only test for Linux:
109 * 1. This is backwards compatible.
110 * 2. If we allow any of the other now, we may have to provide conversion
111 * routines if we ever want to distinguish them. Or just do our best
112 * with what we have and give all ACL streams a new number/type.
115 #if !defined(HAVE_ACL) || !defined(HAVE_LINUX_OS)
117 /* bacl_get() returns the lenght of the string, or -1 on error. */
118 int bacl_get(JCR *jcr, int acltype)
123 int bacl_set(JCR *jcr, int acltype)
128 #elif defined(HAVE_AIX_OS)
130 #include <sys/access.h>
132 int bacl_get(JCR *jcr, int acltype)
137 if ((acl_text = acl_get(jcr->last_fname)) != NULL) {
138 len = pm_strcpy(jcr->acl_text, acl_text);
145 int bacl_set(JCR *jcr, int acltype)
147 if (acl_put(jcr->last_fname, jcr->acl_text, 0) != 0) {
153 #elif defined(HAVE_FREEBSD_OS) \
154 || defined(HAVE_IRIX_OS) \
155 || defined(HAVE_OSF1_OS) \
156 || defined(HAVE_LINUX_OS)
158 #include <sys/types.h>
161 /* On IRIX we can get shortened ACLs */
162 #if defined(HAVE_IRIX_OS) && defined(BACL_WANT_SHORT_ACLS)
163 #define acl_to_text(acl,len) acl_to_short_text((acl), (len))
166 /* In Linux we can get numeric and/or shorted ACLs */
167 #if defined(HAVE_LINUX_OS)
168 #if defined(BACL_WANT_SHORT_ACLS) && defined(BACL_WANT_NUMERIC_IDS)
169 #define BACL_ALTERNATE_TEXT (TEXT_ABBREVIATE|TEXT_NUMERIC_IDS)
170 #elif defined(BACL_WANT_SHORT_ACLS)
171 #define BACL_ALTERNATE_TEXT TEXT_ABBREVIATE
172 #elif defined(BACL_WANT_NUMERIC_IDS)
173 #define BACL_ALTERNATE_TEXT TEXT_NUMERIC_IDS
175 #ifdef BACL_ALTERNATE_TEXT
176 #include <acl/libacl.h>
177 #define acl_to_text(acl,len) ((len), acl_to_any_text((acl), NULL, ',', BACL_ALTERNATE_TEXT))
181 int bacl_get(JCR *jcr, int acltype)
187 ostype = (acltype & BACL_TYPE_DEFAULT) ? ACL_TYPE_DEFAULT : ACL_TYPE_ACCESS;
189 acl = acl_get_file(jcr->last_fname, ostype);
191 if ((acl_text = acl_to_text(acl, NULL)) != NULL) {
192 len = pm_strcpy(jcr->acl_text, acl_text);
198 #ifndef HAVE_OSF1_OS /* BACL_ENOTSUP not defined for OSF1 */
199 } else if (errno == BACL_ENOTSUP) {
200 /* Not supported, just pretend there is nothing to see */
201 return pm_strcpy(jcr->acl_text, "");
204 /***** Do we really want to silently ignore errors from acl_get_file
205 and acl_to_text? *****/
209 int bacl_set(JCR *jcr, int acltype)
214 ostype = (acltype & BACL_TYPE_DEFAULT) ? ACL_TYPE_DEFAULT : ACL_TYPE_ACCESS;
216 /* If we get empty default ACLs, clear ACLs now */
217 if (ostype == ACL_TYPE_DEFAULT && strlen(jcr->acl_text) == 0) {
218 if (acl_delete_def_file(jcr->last_fname) == 0) {
224 acl = acl_from_text(jcr->acl_text);
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) {
240 if (acl_set_file(jcr->last_fname, ostype, acl) != 0) {
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);
274 int bacl_set(JCR *jcr, int acltype)
277 struct acl_entry acls[NACLENTRIES];
279 n = strtoacl(jcr->acl_text, 0, NACLENTRIES, acls, ACL_FILEOWNER, ACL_FILEGROUP);
283 if (strtoacl(jcr->acl_text, n, NACLENTRIES, acls, ACL_FILEOWNER, ACL_FILEGROUP) != n) {
286 if (setacl(jcr->last_fname, n, acls) != 0) {
292 #elif defined(HAVE_SUN_OS)
295 int bacl_get(JCR *jcr, int acltype)
301 n = acl(jcr->last_fname, GETACLCNT, 0, NULL);
302 if (n < MIN_ACL_ENTRIES) {
304 } else if (n == MIN_ACL_ENTRIES) {
305 /* The ACLs simply reflect the (already known) standard permissions */
306 return pm_strcpy(jcr->acl_text, "");
308 if ((acls = (aclent_t *)malloc(n * sizeof(aclent_t))) == NULL) {
311 if (acl(jcr->last_fname, GETACL, n, acls) == n) {
312 if ((acl_text = acltotext(acls, n)) != NULL) {
313 len = pm_strcpy(jcr->acl_text, acl_text);
323 int bacl_set(JCR *jcr, int acltype)
328 acls = aclfromtext(jcr->acl_text, &n);
332 if (acl(jcr->last_fname, SETACL, n, acls) != 0) {
344 int main(int argc, char **argv)
350 Dmsg0(200, "Cannot determine my own name\n");
354 prgname = strrchr(argv[0], '/');
355 if (prgname == NULL || *++prgname == '\0') {
361 /* aclcp "copies" ACLs - set ACLs on destination equal to ACLs on source */
362 if (strcmp(prgname, "aclcp") == 0) {
364 if (strcmp(*argv, "-v") == 0) {
370 Dmsg2(200, "%s: wrong number of arguments\n"
371 "usage:\t%s [-v] source destination\n"
372 "\tCopies ACLs from source to destination.\n"
373 "\tSpecify -v to show ACLs after copy for verification.\n",
377 if (strcmp(argv[0], argv[1]) == 0) {
378 Dmsg2(200, "%s: identical source and destination.\n"
379 "usage:\t%s [-v] source destination\n"
380 "\tCopies ACLs from source to destination.\n"
381 "\tSpecify -v to show ACLs after copy for verification.\n",
388 status = aclcp(argv[0], argv[1]);
389 if (verbose && status == 0) {
395 /* Default: just list ACLs */
397 Dmsg2(200, "%s: missing arguments\n"
398 "usage:\t%s file ...\n"
399 "\tLists ACLs of specified files or directories.\n",
404 if (!aclls(*argv++)) {
405 status = EXIT_FAILURE;
412 /**** Test program *****/
413 int aclcp(char *src, char *dst)
417 if (lstat(dst, &st) != 0) {
418 Dmsg0(200, "aclcp: destination does not exist\n");
421 if (S_ISLNK(st.st_mode)) {
422 Dmsg0(200, "aclcp: cannot set ACL on symlinks\n");
425 if (lstat(src, &st) != 0) {
426 Dmsg0(200, "aclcp: source does not exist\n");
429 if (S_ISLNK(st.st_mode)) {
430 Dmsg0(200, "aclcp: will not read ACL from symlinks\n");
434 jcr.last_fname = src;
435 if (bacl_get(&jcr, BACL_TYPE_ACCESS) < 0) {
436 Dmsg1(200, "aclcp: could not read ACLs for %s\n", jcr.last_fname);
439 jcr.last_fname = dst;
440 if (bacl_set(&jcr, BACL_TYPE_ACCESS) < 0) {
441 Dmsg1(200, "aclcp: could not set ACLs on %s\n", jcr.last_fname);
446 if (S_ISDIR(st.st_mode) && (BACL_CAP & BACL_CAP_DEFAULTS_DIR)) {
447 jcr.last_fname = src;
448 if (bacl_get(&jcr, BACL_TYPE_DEFAULT) < 0) {
449 Dmsg1(200, "aclcp: could not read default ACLs for %s\n", jcr.last_fname);
452 jcr.last_fname = dst;
453 if (bacl_set(&jcr, BACL_TYPE_DEFAULT) < 0) {
454 Dmsg1(200, "aclcp: could not set default ACLs on %s\n", jcr.last_fname);
463 /**** Test program *****/
464 int aclls(char *fname)
469 if (lstat(fname, &st) != 0) {
470 Dmsg0(200, "acl: source does not exist\n");
473 if (S_ISLNK(st.st_mode)) {
474 Dmsg0(200, "acl: will not read ACL from symlinks\n");
478 jcr.last_fname = fname;
480 len = bacl_get(&jcr, BACL_TYPE_ACCESS);
482 Dmsg1(200, "acl: could not read ACLs for %s\n", jcr.last_fname);
484 } else if (len == 0) {
485 printf("#file: %s [standard permissions - or unsupported]\n\n", jcr.last_fname);
487 printf("#file: %s\n%s\n", jcr.last_fname, jcr.acl_text);
490 if (S_ISDIR(st.st_mode) && (BACL_CAP & BACL_CAP_DEFAULTS_DIR)) {
491 len = bacl_get(&jcr, BACL_TYPE_DEFAULT);
493 Dmsg1(200, "acl: could not read default ACLs for %s\n", jcr.last_fname);
495 } else if (len == 0) {
496 printf("#file: %s [default, none - or unsupported]\n\n", jcr.last_fname);
498 printf("#file: %s [default]\n%s\n", jcr.last_fname, jcr.acl_text);