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
36 /* So we can free system allocated memory */
39 #define malloc &* dont use malloc in this routine
45 * Compile and set up with eg. with eg.
47 * $ cc -DTEST_PROGRAM -DHAVE_SUN_OS -lsec -o acl acl.c
50 * You can then list ACLs with acl and copy them with aclcp.
52 * For a list of compiler flags, see the list preceding the big #if below.
61 #define pm_strcpy(d,s) (strncpy(d, s, BACLLEN - 1) == NULL ? -1 : (int)strlen(d))
62 #define Dmsg0(n,s) fprintf(stderr, s)
63 #define Dmsg1(n,s,a1) fprintf(stderr, s, a1)
64 #define Dmsg2(n,s,a1,a2) fprintf(stderr, s, a1, a2)
66 int aclls(char *fname);
67 int aclcp(char *src, char *dst);
71 char acl_text[BACLLEN];
73 typedef struct JCRstruct JCR;
78 * List of supported OSs.
79 * Not sure if all the HAVE_XYZ_OS are correct for autoconf.
80 * The ones that says man page, are coded according to man pages only.
82 #if !defined(HAVE_ACL) /* ACL support is required, of course */ \
83 || !( defined(HAVE_AIX_OS) /* man page -- may need flags */ \
84 || defined(HAVE_FREEBSD_OS) /* tested -- compile wihtout flags */ \
85 || defined(HAVE_IRIX_OS) /* man page -- compile without flags */ \
86 || defined(HAVE_OSF1_OS) /* man page -- may need -lpacl */ \
87 || defined(HAVE_LINUX_OS) /* tested -- compile with -lacl */ \
88 || defined(HAVE_HPUX_OS) /* man page -- may need flags */ \
89 || defined(HAVE_SUN_OS) /* tested -- compile with -lsec */ \
93 * For now we abandon this test and only test for Linux:
94 * 1. This is backwards compatible.
95 * 2. If we allow any of the other now, we may have to provide conversion
96 * routines if we ever want to distinguish them. Or just do our best
97 * with what we have and give all ACL streams a new number/type.
100 #if !defined(HAVE_ACL) || ! defined(HAVE_LINUX_OS)
102 /* bacl_get() returns the lenght of the string, or -1 on error. */
103 int bacl_get(JCR *jcr, int acltype)
108 int bacl_set(JCR *jcr, int acltype)
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);
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_IRIX_OS) \
140 || defined(HAVE_OSF1_OS) \
141 || defined(HAVE_LINUX_OS)
143 #include <sys/types.h>
146 /* On IRIX we can get shortened ACLs */
147 #if defined(HAVE_IRIX_OS) && defined(BACL_WANT_SHORT_ACLS)
148 #define acl_to_text(acl,len) acl_to_short_text((acl), (len))
151 /* In Linux we can get numeric and/or shorted ACLs */
152 #if defined(HAVE_LINUX_OS)
153 #if defined(BACL_WANT_SHORT_ACLS) && defined(BACL_WANT_NUMERIC_IDS)
154 #define BACL_ALTERNATE_TEXT (TEXT_ABBREVIATE|TEXT_NUMERIC_IDS)
155 #elif defined(BACL_WANT_SHORT_ACLS)
156 #define BACL_ALTERNATE_TEXT TEXT_ABBREVIATE
157 #elif defined(BACL_WANT_NUMERIC_IDS)
158 #define BACL_ALTERNATE_TEXT TEXT_NUMERIC_IDS
160 #ifdef BACL_ALTERNATE_TEXT
161 #include <acl/libacl.h>
162 #define acl_to_text(acl,len) ((len), acl_to_any_text((acl), NULL, ',', BACL_ALTERNATE_TEXT))
166 int bacl_get(JCR *jcr, int acltype)
172 ostype = (acltype & BACL_TYPE_DEFAULT) ? ACL_TYPE_DEFAULT : ACL_TYPE_ACCESS;
174 acl = acl_get_file(jcr->last_fname, ostype);
176 if ((acl_text = acl_to_text(acl, NULL)) != NULL) {
177 len = pm_strcpy(jcr->acl_text, 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) {
202 acl = acl_from_text(jcr->acl_text);
208 * FreeBSD always fails acl_valid() - at least on valid input...
209 * As it does the right thing, given valid input, just ignore acl_valid().
211 #ifndef HAVE_FREEBSD_OS
212 if (acl_valid(acl) != 0) {
218 if (acl_set_file(jcr->last_fname, ostype, acl) != 0) {
226 #elif defined(HAVE_HPUX_OS)
230 int bacl_get(JCR *jcr, int acltype)
233 struct acl_entry acls[NACLENTRIES];
236 if ((n = getacl(jcr->last_fname, 0, acls)) <= 0) {
239 if ((n = getacl(jcr->last_fname, n, acls)) > 0) {
240 if ((acl_text = acltostr(n, acls, FORM_SHORT)) != NULL) {
241 len = pm_strcpy(jcr->acl_text, acl_text);
249 int bacl_set(JCR *jcr, int acltype)
252 struct acl_entry acls[NACLENTRIES];
254 n = strtoacl(jcr->acl_text, 0, NACLENTRIES, acls, ACL_FILEOWNER, ACL_FILEGROUP);
258 if (strtoacl(jcr->acl_text, n, NACLENTRIES, acls, ACL_FILEOWNER, ACL_FILEGROUP) != n) {
261 if (setacl(jcr->last_fname, n, acls) != 0) {
267 #elif defined(HAVE_SUN_OS)
270 int bacl_get(JCR *jcr, int acltype)
276 n = acl(jcr->last_fname, GETACLCNT, 0, NULL);
280 if ((acls = (aclent_t *)malloc(n * sizeof(aclent_t))) == NULL) {
283 if (acl(jcr->last_fname, GETACL, n, acls) == n) {
284 if ((acl_text = acltotext(acls, n)) != NULL) {
285 pm_strcpy(jcr->acl_text, acl_text);
295 int bacl_set(JCR *jcr, int acltype)
300 acls = aclfromtext(jcr->acl_text, &n);
304 if (acl(jcr->last_fname, SETACL, n, acls) != 0) {
316 int main(int argc, char **argv)
322 Dmsg0(200, "Cannot determine my own name\n");
326 prgname = strrchr(argv[0], '/');
327 if (prgname == NULL || *++prgname == '\0') {
333 /* aclcp "copies" ACLs - set ACLs on destination equal to ACLs on source */
334 if (strcmp(prgname, "aclcp") == 0) {
336 if (strcmp(*argv, "-v") == 0) {
342 Dmsg2(200, "%s: wrong number of arguments\n"
343 "usage:\t%s [-v] source destination\n"
344 "\tCopies ACLs from source to destination.\n"
345 "\tSpecify -v to show ACLs after copy for verification.\n",
349 if (strcmp(argv[0], argv[1]) == 0) {
350 Dmsg2(200, "%s: identical source and destination.\n"
351 "usage:\t%s [-v] source destination\n"
352 "\tCopies ACLs from source to destination.\n"
353 "\tSpecify -v to show ACLs after copy for verification.\n",
360 status = aclcp(argv[0], argv[1]);
361 if (verbose && status == 0) {
367 /* Default: just list ACLs */
369 Dmsg2(200, "%s: missing arguments\n"
370 "usage:\t%s file ...\n"
371 "\tLists ACLs of specified files or directories.\n",
376 if (!aclls(*argv++)) {
377 status = EXIT_FAILURE;
384 /**** Test program *****/
385 int aclcp(char *src, char *dst)
389 if (lstat(dst, &st) != 0) {
390 Dmsg0(200, "aclcp: destination does not exist\n");
393 if (S_ISLNK(st.st_mode)) {
394 Dmsg0(200, "aclcp: cannot set ACL on symlinks\n");
397 if (lstat(src, &st) != 0) {
398 Dmsg0(200, "aclcp: source does not exist\n");
401 if (S_ISLNK(st.st_mode)) {
402 Dmsg0(200, "aclcp: will not read ACL from symlinks\n");
406 jcr.last_fname = src;
407 if (bacl_get(&jcr, BACL_TYPE_ACCESS) < 0) {
408 Dmsg1(200, "aclcp: could not read ACLs for %s\n", jcr.last_fname);
411 jcr.last_fname = dst;
412 if (bacl_set(&jcr, BACL_TYPE_ACCESS) < 0) {
413 Dmsg1(200, "aclcp: could not set ACLs on %s\n", jcr.last_fname);
418 if (S_ISDIR(st.st_mode) && (BACL_CAP & BACL_CAP_DEFAULTS_DIR)) {
419 jcr.last_fname = src;
420 if (bacl_get(&jcr, BACL_TYPE_DEFAULT) < 0) {
421 Dmsg1(200, "aclcp: could not read default ACLs for %s\n", jcr.last_fname);
424 jcr.last_fname = dst;
425 if (bacl_set(&jcr, BACL_TYPE_DEFAULT) < 0) {
426 Dmsg1(200, "aclcp: could not set default ACLs on %s\n", jcr.last_fname);
435 /**** Test program *****/
436 int aclls(char *fname)
440 if (lstat(fname, &st) != 0) {
441 Dmsg0(200, "acl: source does not exist\n");
444 if (S_ISLNK(st.st_mode)) {
445 Dmsg0(200, "acl: will not read ACL from symlinks\n");
449 jcr.last_fname = fname;
451 if (bacl_get(&jcr, BACL_TYPE_ACCESS) < 0) {
452 Dmsg1(200, "acl: could not read ACLs for %s\n", jcr.last_fname);
455 printf("#file: %s\n%s\n", jcr.last_fname, jcr.acl_text);
457 if (S_ISDIR(st.st_mode) && (BACL_CAP & BACL_CAP_DEFAULTS_DIR)) {
458 if (bacl_get(&jcr, BACL_TYPE_DEFAULT) < 0) {
459 Dmsg1(200, "acl: could not read default ACLs for %s\n", jcr.last_fname);
462 printf("#file: %s [default]\n%s\n", jcr.last_fname, jcr.acl_text);