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.
55 * Compile and set up with eg. with eg.
57 * $ cc -DTEST_PROGRAM -DHAVE_SUN_OS -lsec -o acl acl.c
60 * You can then list ACLs with acl and copy them with aclcp.
62 * For a list of compiler flags, see the list preceding the big #if below.
72 #define pm_strcpy(d,s) (strncpy(d, s, BACLLEN - 1) == NULL ? -1 : (int)strlen(d))
73 #define Dmsg0(n,s) fprintf(stderr, s)
74 #define Dmsg1(n,s,a1) fprintf(stderr, s, a1)
75 #define Dmsg2(n,s,a1,a2) fprintf(stderr, s, a1, a2)
77 int aclls(char *fname);
78 int aclcp(char *src, char *dst);
82 char acl_text[BACLLEN];
84 typedef struct JCRstruct JCR;
89 * List of supported OSs.
90 * Not sure if all the HAVE_XYZ_OS are correct for autoconf.
91 * The ones that says man page, are coded according to man pages only.
93 #if !defined(HAVE_ACL) /* ACL support is required, of course */ \
94 || !( defined(HAVE_AIX_OS) /* man page -- may need flags */ \
95 || defined(HAVE_FREEBSD_OS) /* tested -- compile wihtout flags */ \
96 || defined(HAVE_DARWIN_OS) /* tested -- compile wihtout flags */ \
97 || defined(HAVE_IRIX_OS) /* man page -- compile without flags */ \
98 || defined(HAVE_OSF1_OS) /* man page -- may need -lpacl */ \
99 || defined(HAVE_LINUX_OS) /* tested -- compile with -lacl */ \
100 || defined(HAVE_HPUX_OS) /* man page -- may need flags */ \
101 || defined(HAVE_SUN_OS) /* tested -- compile with -lsec */ \
105 * For now we abandon this test and only test for Linux:
106 * 1. This is backwards compatible.
107 * 2. If we allow any of the other now, we may have to provide conversion
108 * routines if we ever want to distinguish them. Or just do our best
109 * with what we have and give all ACL streams a new number/type.
112 #if !defined(HAVE_ACL) \
113 || !( defined(HAVE_LINUX_OS) \
114 || defined(HAVE_FREEBSD_OS) \
115 || defined(HAVE_DARWIN_OS) \
116 || defined(HAVE_IRIX_OS) \
117 || defined(HAVE_OSF1_OS) \
118 || defined(HAVE_SUN_OS) \
121 /* bacl_get() returns the lenght of the string, or -1 on error. */
122 int bacl_get(JCR *jcr, int acltype)
127 int bacl_set(JCR *jcr, int acltype)
132 #elif defined(HAVE_AIX_OS)
134 #include <sys/access.h>
136 int bacl_get(JCR *jcr, int acltype)
141 if ((acl_text = acl_get(jcr->last_fname)) != NULL) {
142 len = pm_strcpy(jcr->acl_text, acl_text);
143 actuallyfree(acl_text);
149 int bacl_set(JCR *jcr, int acltype)
151 if (acl_put(jcr->last_fname, jcr->acl_text, 0) != 0) {
157 #elif defined(HAVE_FREEBSD_OS) \
158 || defined(HAVE_DARWIN_OS) \
159 || defined(HAVE_IRIX_OS) \
160 || defined(HAVE_OSF1_OS) \
161 || defined(HAVE_LINUX_OS)
163 #include <sys/types.h>
166 /* On IRIX we can get shortened ACLs */
167 #if defined(HAVE_IRIX_OS) && defined(BACL_WANT_SHORT_ACLS)
168 #define acl_to_text(acl,len) acl_to_short_text((acl), (len))
171 /* In Linux we can get numeric and/or shorted ACLs */
172 #if defined(HAVE_LINUX_OS)
173 #if defined(BACL_WANT_SHORT_ACLS) && defined(BACL_WANT_NUMERIC_IDS)
174 #define BACL_ALTERNATE_TEXT (TEXT_ABBREVIATE|TEXT_NUMERIC_IDS)
175 #elif defined(BACL_WANT_SHORT_ACLS)
176 #define BACL_ALTERNATE_TEXT TEXT_ABBREVIATE
177 #elif defined(BACL_WANT_NUMERIC_IDS)
178 #define BACL_ALTERNATE_TEXT TEXT_NUMERIC_IDS
180 #ifdef BACL_ALTERNATE_TEXT
181 #include <acl/libacl.h>
182 #define acl_to_text(acl,len) ((len), acl_to_any_text((acl), NULL, ',', BACL_ALTERNATE_TEXT))
186 int bacl_get(JCR *jcr, int acltype)
193 ostype = (acltype & BACL_TYPE_DEFAULT) ? ACL_TYPE_DEFAULT : ACL_TYPE_ACCESS;
195 acl = acl_get_file(jcr->last_fname, ostype);
197 if ((acl_text = acl_to_text(acl, NULL)) != NULL) {
198 len = pm_strcpy(jcr->acl_text, acl_text);
204 #ifndef HAVE_OSF1_OS /* BACL_ENOTSUP not defined for OSF1 */
205 } else if (errno == BACL_ENOTSUP) {
206 /* Not supported, just pretend there is nothing to see */
207 return pm_strcpy(jcr->acl_text, "");
210 /***** Do we really want to silently ignore errors from acl_get_file
211 and acl_to_text? *****/
215 int bacl_set(JCR *jcr, int acltype)
220 ostype = (acltype & BACL_TYPE_DEFAULT) ? ACL_TYPE_DEFAULT : ACL_TYPE_ACCESS;
222 /* If we get empty default ACLs, clear ACLs now */
223 if (ostype == ACL_TYPE_DEFAULT && strlen(jcr->acl_text) == 0) {
224 if (acl_delete_def_file(jcr->last_fname) == 0) {
230 acl = acl_from_text(jcr->acl_text);
236 * FreeBSD always fails acl_valid() - at least on valid input...
237 * As it does the right thing, given valid input, just ignore acl_valid().
239 #ifndef HAVE_FREEBSD_OS
240 if (acl_valid(acl) != 0) {
246 if (acl_set_file(jcr->last_fname, ostype, acl) != 0) {
254 #elif defined(HAVE_HPUX_OS)
258 int bacl_get(JCR *jcr, int acltype)
261 struct acl_entry acls[NACLENTRIES];
264 if ((n = getacl(jcr->last_fname, 0, acls)) <= 0) {
265 if (errno == BACL_ENOTSUP) {
266 return pm_strcpy(jcr->acl_text, "");
270 if ((n = getacl(jcr->last_fname, n, acls)) > 0) {
271 if ((acl_text = acltostr(n, acls, FORM_SHORT)) != NULL) {
272 len = pm_strcpy(jcr->acl_text, acl_text);
273 actuallyfree(acl_text);
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);
289 if (strtoacl(jcr->acl_text, n, NACLENTRIES, acls, ACL_FILEOWNER, ACL_FILEGROUP) != n) {
292 if (setacl(jcr->last_fname, n, acls) != 0) {
298 #elif defined(HAVE_SUN_OS)
301 int bacl_get(JCR *jcr, int acltype)
307 n = acl(jcr->last_fname, GETACLCNT, 0, NULL);
308 if (n < MIN_ACL_ENTRIES) {
310 } else if (n == MIN_ACL_ENTRIES) {
311 /* The ACLs simply reflect the (already known) standard permissions */
312 return pm_strcpy(jcr->acl_text, "");
314 if ((acls = (aclent_t *)malloc(n * sizeof(aclent_t))) == NULL) {
317 if (acl(jcr->last_fname, GETACL, n, acls) == n) {
318 if ((acl_text = acltotext(acls, n)) != NULL) {
319 len = pm_strcpy(jcr->acl_text, acl_text);
320 actuallyfree(acl_text);
329 int bacl_set(JCR *jcr, int acltype)
334 acls = aclfromtext(jcr->acl_text, &n);
338 if (acl(jcr->last_fname, SETACL, n, acls) == -1) {
350 int main(int argc, char **argv)
356 Dmsg0(200, "Cannot determine my own name\n");
360 prgname = strrchr(argv[0], '/');
361 if (prgname == NULL || *++prgname == '\0') {
367 /* aclcp "copies" ACLs - set ACLs on destination equal to ACLs on source */
368 if (strcmp(prgname, "aclcp") == 0) {
370 if (strcmp(*argv, "-v") == 0) {
376 Dmsg2(200, "%s: wrong number of arguments\n"
377 "usage:\t%s [-v] source destination\n"
378 "\tCopies ACLs from source to destination.\n"
379 "\tSpecify -v to show ACLs after copy for verification.\n",
383 if (strcmp(argv[0], argv[1]) == 0) {
384 Dmsg2(200, "%s: identical source and destination.\n"
385 "usage:\t%s [-v] source destination\n"
386 "\tCopies ACLs from source to destination.\n"
387 "\tSpecify -v to show ACLs after copy for verification.\n",
394 status = aclcp(argv[0], argv[1]);
395 if (verbose && status == 0) {
401 /* Default: just list ACLs */
403 Dmsg2(200, "%s: missing arguments\n"
404 "usage:\t%s file ...\n"
405 "\tLists ACLs of specified files or directories.\n",
410 if (!aclls(*argv++)) {
411 status = EXIT_FAILURE;
418 /**** Test program *****/
419 int aclcp(char *src, char *dst)
423 if (lstat(dst, &st) != 0) {
424 Dmsg0(200, "aclcp: destination does not exist\n");
427 if (S_ISLNK(st.st_mode)) {
428 Dmsg0(200, "aclcp: cannot set ACL on symlinks\n");
431 if (lstat(src, &st) != 0) {
432 Dmsg0(200, "aclcp: source does not exist\n");
435 if (S_ISLNK(st.st_mode)) {
436 Dmsg0(200, "aclcp: will not read ACL from symlinks\n");
440 jcr.last_fname = src;
441 if (bacl_get(&jcr, BACL_TYPE_ACCESS) < 0) {
442 Dmsg1(200, "aclcp: could not read ACLs for %s\n", jcr.last_fname);
445 jcr.last_fname = dst;
446 if (bacl_set(&jcr, BACL_TYPE_ACCESS) < 0) {
447 Dmsg1(200, "aclcp: could not set ACLs on %s\n", jcr.last_fname);
452 if (S_ISDIR(st.st_mode) && (BACL_CAP & BACL_CAP_DEFAULTS_DIR)) {
453 jcr.last_fname = src;
454 if (bacl_get(&jcr, BACL_TYPE_DEFAULT) < 0) {
455 Dmsg1(200, "aclcp: could not read default ACLs for %s\n", jcr.last_fname);
458 jcr.last_fname = dst;
459 if (bacl_set(&jcr, BACL_TYPE_DEFAULT) < 0) {
460 Dmsg1(200, "aclcp: could not set default ACLs on %s\n", jcr.last_fname);
469 /**** Test program *****/
470 int aclls(char *fname)
475 if (lstat(fname, &st) != 0) {
476 Dmsg0(200, "acl: source does not exist\n");
479 if (S_ISLNK(st.st_mode)) {
480 Dmsg0(200, "acl: will not read ACL from symlinks\n");
484 jcr.last_fname = fname;
486 len = bacl_get(&jcr, BACL_TYPE_ACCESS);
488 Dmsg1(200, "acl: could not read ACLs for %s\n", jcr.last_fname);
490 } else if (len == 0) {
491 printf("#file: %s [standard permissions - or unsupported]\n\n", jcr.last_fname);
493 printf("#file: %s\n%s\n", jcr.last_fname, jcr.acl_text);
496 if (S_ISDIR(st.st_mode) && (BACL_CAP & BACL_CAP_DEFAULTS_DIR)) {
497 len = bacl_get(&jcr, BACL_TYPE_DEFAULT);
499 Dmsg1(200, "acl: could not read default ACLs for %s\n", jcr.last_fname);
501 } else if (len == 0) {
502 printf("#file: %s [default, none - or unsupported]\n\n", jcr.last_fname);
504 printf("#file: %s [default]\n%s\n", jcr.last_fname, jcr.acl_text);