]> git.sur5r.net Git - bacula/bacula/blob - bacula/src/filed/acl.c
2e5a85a692f1ecf12c41ac6cfeef420daf1d5fa4
[bacula/bacula] / bacula / src / filed / acl.c
1 /*
2  * Functions to handle ACL for bacula.
3  *
4  * We handle two different typers of ACLs: access and default ACLS.
5  * Default ACLs only apply to directories.
6  *
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.
9  *
10  * As for the streams to use, we have two choices:
11  *
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.
15  *
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.
19  *
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.
23  *
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.
26  *
27  *   Written by Preben 'Peppe' Guldberg, December MMIV
28  *
29  *   Version $Id$
30  */
31
32 #ifndef TEST_PROGRAM
33
34 #include "bacula.h"
35 #include "filed.h"
36 /* So we can free system allocated memory */
37 #undef free
38 #undef malloc
39 #define malloc &* dont use malloc in this routine
40
41 #else
42 /*
43  * Test program setup 
44  *
45  * Compile and set up with eg. with eg.
46  *
47  *    $ cc -DTEST_PROGRAM -DHAVE_SUN_OS -lsec -o acl acl.c
48  *    $ ln -s acl aclcp
49  *
50  * You can then list ACLs with acl and copy them with aclcp.
51  *
52  * For a list of compiler flags, see the list preceding the big #if below.
53  */
54 #include <stdio.h>
55 #include <stdlib.h>
56 #include <string.h>
57 #include <sys/stat.h>
58 #include "acl.h"
59
60 #define BACLLEN 65535
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)
65
66 int aclls(char *fname);
67 int aclcp(char *src, char *dst);
68
69 struct JCRstruct {
70    char *last_fname;
71    char acl_text[BACLLEN];
72 };
73 typedef struct JCRstruct JCR;
74 JCR jcr;
75 #endif
76
77 /*
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.
81  */
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     */ \
90        )
91 /*
92  * ***FIXME***
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.
98  */
99 #endif
100 #if !defined(HAVE_ACL) || ! defined(HAVE_LINUX_OS)
101
102 /* bacl_get() returns the lenght of the string, or -1 on error. */
103 int bacl_get(JCR *jcr, int acltype)
104 {
105    return -1;
106 }
107
108 int bacl_set(JCR *jcr, int acltype)
109 {
110    return -1;
111 }
112
113 #elif defined(HAVE_AIX_OS)
114
115 #include <sys/access.h>
116
117 int bacl_get(JCR *jcr, int acltype)
118 {
119    char *acl_text;
120    int len;
121
122    if ((acl_text = acl_get(jcr->last_fname)) != NULL) {
123       len = pm_strcpy(jcr->acl_text, acl_text);
124       free(acl_text);
125       return len;
126    }
127    return -1;
128 }
129
130 int bacl_set(JCR *jcr, int acltype)
131 {
132    if (acl_put(jcr->last_fname, jcr->acl_text, 0) != 0) {
133       return -1;
134    }
135    return 0;
136 }
137
138 #elif defined(HAVE_FREEBSD_OS) \
139    || defined(HAVE_IRIX_OS) \
140    || defined(HAVE_OSF1_OS) \
141    || defined(HAVE_LINUX_OS)
142
143 #include <sys/types.h>
144 #include <sys/acl.h>
145
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))
149 #endif
150
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
159 #endif
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))
163 #endif
164 #endif
165
166 int bacl_get(JCR *jcr, int acltype)
167 {
168    acl_t acl;
169    int len, ostype;
170    char *acl_text;
171
172    ostype = (acltype & BACL_TYPE_DEFAULT) ? ACL_TYPE_DEFAULT : ACL_TYPE_ACCESS;
173
174    acl = acl_get_file(jcr->last_fname, ostype);
175    if (acl) {
176       if ((acl_text = acl_to_text(acl, NULL)) != NULL) {
177          len = pm_strcpy(jcr->acl_text, acl_text);
178          acl_free(acl_text);
179          return len;
180       }
181    }
182    /***** Do we really want to silently ignore errors from acl_get_file
183      and acl_to_text?  *****/
184    return -1;
185 }
186
187 int bacl_set(JCR *jcr, int acltype)
188 {
189    acl_t acl;
190    int ostype;
191
192    ostype = (acltype & BACL_TYPE_DEFAULT) ? ACL_TYPE_DEFAULT : ACL_TYPE_ACCESS;
193
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) {
197          return 0;
198       }
199       return -1;
200    }
201
202    acl = acl_from_text(jcr->acl_text);
203    if (acl == NULL) {
204       return -1;
205    }
206
207    /*
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().
210     */
211 #ifndef HAVE_FREEBSD_OS
212    if (acl_valid(acl) != 0) {
213       acl_free(acl);
214       return -1;
215    }
216 #endif
217
218    if (acl_set_file(jcr->last_fname, ostype, acl) != 0) {
219       acl_free(acl);
220       return -1;
221    }
222    acl_free(acl);
223    return 0;
224 }
225
226 #elif defined(HAVE_HPUX_OS)
227 #include <sys/acl.h>
228 #include <acllib.h>
229
230 int bacl_get(JCR *jcr, int acltype)
231 {
232    int n, len;
233    struct acl_entry acls[NACLENTRIES];
234    char *acl_text;
235
236    if ((n = getacl(jcr->last_fname, 0, acls)) <= 0) {
237       return -1;
238    }
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);
242          free(acl_text);
243          return len;
244       }
245    }
246    return -1;
247 }
248
249 int bacl_set(JCR *jcr, int acltype)
250 {
251    int n, stat;
252    struct acl_entry acls[NACLENTRIES];
253
254    n = strtoacl(jcr->acl_text, 0, NACLENTRIES, acls, ACL_FILEOWNER, ACL_FILEGROUP);
255    if (n <= 0) {
256       return -1;
257    }
258    if (strtoacl(jcr->acl_text, n, NACLENTRIES, acls, ACL_FILEOWNER, ACL_FILEGROUP) != n) {
259       return -1;
260    }
261    if (setacl(jcr->last_fname, n, acls) != 0) {
262       return -1;
263    }
264    return 0;
265 }
266
267 #elif defined(HAVE_SUN_OS)
268 #include <sys/acl.h>
269
270 int bacl_get(JCR *jcr, int acltype)
271 {
272    int n, len;
273    aclent_t *acls;
274    char *acl_text;
275
276    n = acl(jcr->last_fname, GETACLCNT, 0, NULL);
277    if (n <= 0) {
278       return -1;
279    }
280    if ((acls = (aclent_t *)malloc(n * sizeof(aclent_t))) == NULL) {
281       return -1;
282    }
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);
286          free(acl_text);
287          free(acls);
288          return len;
289       }
290    }
291    free(acls);
292    return -1;
293 }
294
295 int bacl_set(JCR *jcr, int acltype)
296 {
297    int n;
298    aclent_t *acls;
299
300    acls = aclfromtext(jcr->acl_text, &n);
301    if (!acls) {
302       return -1;
303    }
304    if (acl(jcr->last_fname, SETACL, n, acls) != 0) {
305       free(acls);
306       return -1;
307    }
308    free(acls);
309    return 0;
310 }
311
312 #endif
313
314
315 #ifdef TEST_PROGRAM
316 int main(int argc, char **argv)
317 {
318    char *prgname;
319    int status = 0;
320
321    if (argc < 1) {
322       Dmsg0(200, "Cannot determine my own name\n");
323       return EXIT_FAILURE;
324    }
325
326    prgname = strrchr(argv[0], '/');
327    if (prgname == NULL || *++prgname == '\0') {
328       prgname = argv[0];
329    }
330    --argc;
331    ++argv;
332
333    /* aclcp "copies" ACLs - set ACLs on destination equal to ACLs on source */
334    if (strcmp(prgname, "aclcp") == 0) {
335       int verbose = 0;
336       if (strcmp(*argv, "-v") == 0) {
337          ++verbose;
338          --argc;
339          ++argv;
340       }
341       if (argc != 2) {
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",
346                prgname, prgname);
347          return EXIT_FAILURE;
348       }
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",
354                prgname, prgname);
355          return EXIT_FAILURE;
356       }
357       if (verbose) {
358          aclls(argv[0]);
359       }
360       status = aclcp(argv[0], argv[1]);
361       if (verbose && status == 0) {
362          aclls(argv[1]);
363       }
364       return status;
365    }
366
367    /* Default: just list ACLs */
368    if (argc < 1) {
369       Dmsg2(200, "%s: missing arguments\n"
370             "usage:\t%s file ...\n"
371             "\tLists ACLs of specified files or directories.\n",
372             prgname, prgname);
373       return EXIT_FAILURE;
374    }
375    while (argc--) {
376       if (!aclls(*argv++)) {
377          status = EXIT_FAILURE;
378       }
379    }
380
381    return status;
382 }
383
384 /**** Test program *****/
385 int aclcp(char *src, char *dst)
386 {
387    struct stat st;
388
389    if (lstat(dst, &st) != 0) {
390       Dmsg0(200, "aclcp: destination does not exist\n");
391       return EXIT_FAILURE;
392    }
393    if (S_ISLNK(st.st_mode)) {
394       Dmsg0(200, "aclcp: cannot set ACL on symlinks\n");
395       return EXIT_FAILURE;
396    }
397    if (lstat(src, &st) != 0) {
398       Dmsg0(200, "aclcp: source does not exist\n");
399       return EXIT_FAILURE;
400    }
401    if (S_ISLNK(st.st_mode)) {
402       Dmsg0(200, "aclcp: will not read ACL from symlinks\n");
403       return EXIT_FAILURE;
404    }
405
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);
409       return EXIT_FAILURE;
410    } else {
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);
414          return EXIT_FAILURE;
415       }
416    }
417
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);
422          return EXIT_FAILURE;
423       } else {
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);
427             return EXIT_FAILURE;
428          }
429       }
430    }
431
432    return 0;
433 }
434
435 /**** Test program *****/
436 int aclls(char *fname)
437 {
438    struct stat st;
439
440    if (lstat(fname, &st) != 0) {
441       Dmsg0(200, "acl: source does not exist\n");
442       return EXIT_FAILURE;
443    }
444    if (S_ISLNK(st.st_mode)) {
445       Dmsg0(200, "acl: will not read ACL from symlinks\n");
446       return EXIT_FAILURE;
447    }
448
449    jcr.last_fname = fname;
450
451    if (bacl_get(&jcr, BACL_TYPE_ACCESS) < 0) {
452       Dmsg1(200, "acl: could not read ACLs for %s\n", jcr.last_fname);
453       return EXIT_FAILURE;
454    }
455    printf("#file: %s\n%s\n", jcr.last_fname, jcr.acl_text);
456
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);
460          return EXIT_FAILURE;
461       }
462       printf("#file: %s [default]\n%s\n", jcr.last_fname, jcr.acl_text);
463    }
464
465    return 0;
466 }
467 #endif