]> git.sur5r.net Git - bacula/bacula/blob - bacula/src/filed/acl.c
Apply 1.38.2 changes
[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    Copyright (C) 2004-2005 Kern Sibbald
33
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.
38
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.
43
44  */
45
46 #ifndef TEST_PROGRAM
47
48 #include "bacula.h"
49 #include "filed.h"
50
51 #else
52 /*
53  * Test program setup 
54  *
55  * Compile and set up with eg. with eg.
56  *
57  *    $ cc -DTEST_PROGRAM -DHAVE_SUN_OS -lsec -o acl acl.c
58  *    $ ln -s acl aclcp
59  *
60  * You can then list ACLs with acl and copy them with aclcp.
61  *
62  * For a list of compiler flags, see the list preceding the big #if below.
63  */
64 #include <errno.h>
65 #include <stdio.h>
66 #include <stdlib.h>
67 #include <string.h>
68 #include <sys/stat.h>
69 #include "acl.h"
70
71 #define BACLLEN 65535
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)
76
77 int aclls(char *fname);
78 int aclcp(char *src, char *dst);
79
80 struct JCRstruct {
81    char *last_fname;
82    char acl_text[BACLLEN];
83 };
84 typedef struct JCRstruct JCR;
85 JCR jcr;
86 #endif
87
88 /*
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.
92  */
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     */ \
102        )
103 /*
104  * ***FIXME***
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.
110  */
111 #endif
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) \
119        )
120
121 /* bacl_get() returns the lenght of the string, or -1 on error. */
122 int bacl_get(JCR *jcr, int acltype)
123 {
124    return -1;
125 }
126
127 int bacl_set(JCR *jcr, int acltype)
128 {
129    return -1;
130 }
131
132 #elif defined(HAVE_AIX_OS)
133
134 #include <sys/access.h>
135
136 int bacl_get(JCR *jcr, int acltype)
137 {
138    char *acl_text;
139    int len;
140
141    if ((acl_text = acl_get(jcr->last_fname)) != NULL) {
142       len = pm_strcpy(jcr->acl_text, acl_text);
143       actuallyfree(acl_text);
144       return len;
145    }
146    return -1;
147 }
148
149 int bacl_set(JCR *jcr, int acltype)
150 {
151    if (acl_put(jcr->last_fname, jcr->acl_text, 0) != 0) {
152       return -1;
153    }
154    return 0;
155 }
156
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)
162
163 #include <sys/types.h>
164 #include <sys/acl.h>
165
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))
169 #endif
170
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
179 #endif
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))
183 #endif
184 #endif
185
186 int bacl_get(JCR *jcr, int acltype)
187 {
188    acl_t acl;
189    int len;
190    acl_type_t ostype;
191    char *acl_text;
192
193    ostype = (acltype & BACL_TYPE_DEFAULT) ? ACL_TYPE_DEFAULT : ACL_TYPE_ACCESS;
194
195    acl = acl_get_file(jcr->last_fname, ostype);
196    if (acl) {
197       if ((acl_text = acl_to_text(acl, NULL)) != NULL) {
198          len = pm_strcpy(jcr->acl_text, acl_text);
199          acl_free(acl);
200          acl_free(acl_text);
201          return len;
202       }
203       acl_free(acl);
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, "");
208 #endif
209    }
210    /***** Do we really want to silently ignore errors from acl_get_file
211      and acl_to_text?  *****/
212    return 0;
213 }
214
215 int bacl_set(JCR *jcr, int acltype)
216 {
217    acl_t acl;
218    acl_type_t ostype;
219
220    ostype = (acltype & BACL_TYPE_DEFAULT) ? ACL_TYPE_DEFAULT : ACL_TYPE_ACCESS;
221
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) {
225          return 0;
226       }
227       return -1;
228    }
229
230    acl = acl_from_text(jcr->acl_text);
231    if (acl == NULL) {
232       return -1;
233    }
234
235    /*
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().
238     */
239 #ifndef HAVE_FREEBSD_OS
240    if (acl_valid(acl) != 0) {
241       acl_free(acl);
242       return -1;
243    }
244 #endif
245
246    if (acl_set_file(jcr->last_fname, ostype, acl) != 0) {
247       acl_free(acl);
248       return -1;
249    }
250    acl_free(acl);
251    return 0;
252 }
253
254 #elif defined(HAVE_HPUX_OS)
255 #include <sys/acl.h>
256 #include <acllib.h>
257
258 int bacl_get(JCR *jcr, int acltype)
259 {
260    int n, len;
261    struct acl_entry acls[NACLENTRIES];
262    char *acl_text;
263
264    if ((n = getacl(jcr->last_fname, 0, acls)) <= 0) {
265       if (errno == BACL_ENOTSUP) {
266          return pm_strcpy(jcr->acl_text, "");
267       }
268       return -1;
269    }
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);
274          return len;
275       }
276    }
277    return -1;
278 }
279
280 int bacl_set(JCR *jcr, int acltype)
281 {
282    int n, stat;
283    struct acl_entry acls[NACLENTRIES];
284
285    n = strtoacl(jcr->acl_text, 0, NACLENTRIES, acls, ACL_FILEOWNER, ACL_FILEGROUP);
286    if (n <= 0) {
287       return -1;
288    }
289    if (strtoacl(jcr->acl_text, n, NACLENTRIES, acls, ACL_FILEOWNER, ACL_FILEGROUP) != n) {
290       return -1;
291    }
292    if (setacl(jcr->last_fname, n, acls) != 0) {
293       return -1;
294    }
295    return 0;
296 }
297
298 #elif defined(HAVE_SUN_OS)
299 #include <sys/acl.h>
300
301 int bacl_get(JCR *jcr, int acltype)
302 {
303    int n, len;
304    aclent_t *acls;
305    char *acl_text;
306
307    n = acl(jcr->last_fname, GETACLCNT, 0, NULL);
308    if (n < MIN_ACL_ENTRIES) {
309       return -1;
310    } else if (n == MIN_ACL_ENTRIES) {
311       /* The ACLs simply reflect the (already known) standard permissions */
312       return pm_strcpy(jcr->acl_text, "");
313    }
314    if ((acls = (aclent_t *)malloc(n * sizeof(aclent_t))) == NULL) {
315       return -1;
316    }
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);
321          free(acls);
322          return len;
323       }
324    }
325    free(acls);
326    return -1;
327 }
328
329 int bacl_set(JCR *jcr, int acltype)
330 {
331    int n;
332    aclent_t *acls;
333
334    acls = aclfromtext(jcr->acl_text, &n);
335    if (!acls) {
336       return -1;
337    }
338    if (acl(jcr->last_fname, SETACL, n, acls) == -1) {
339       actuallyfree(acls);
340       return -1;
341    }
342    actuallyfree(acls);
343    return 0;
344 }
345
346 #endif
347
348
349 #ifdef TEST_PROGRAM
350 int main(int argc, char **argv)
351 {
352    char *prgname;
353    int status = 0;
354
355    if (argc < 1) {
356       Dmsg0(200, "Cannot determine my own name\n");
357       return EXIT_FAILURE;
358    }
359
360    prgname = strrchr(argv[0], '/');
361    if (prgname == NULL || *++prgname == '\0') {
362       prgname = argv[0];
363    }
364    --argc;
365    ++argv;
366
367    /* aclcp "copies" ACLs - set ACLs on destination equal to ACLs on source */
368    if (strcmp(prgname, "aclcp") == 0) {
369       int verbose = 0;
370       if (strcmp(*argv, "-v") == 0) {
371          ++verbose;
372          --argc;
373          ++argv;
374       }
375       if (argc != 2) {
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",
380                prgname, prgname);
381          return EXIT_FAILURE;
382       }
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",
388                prgname, prgname);
389          return EXIT_FAILURE;
390       }
391       if (verbose) {
392          aclls(argv[0]);
393       }
394       status = aclcp(argv[0], argv[1]);
395       if (verbose && status == 0) {
396          aclls(argv[1]);
397       }
398       return status;
399    }
400
401    /* Default: just list ACLs */
402    if (argc < 1) {
403       Dmsg2(200, "%s: missing arguments\n"
404             "usage:\t%s file ...\n"
405             "\tLists ACLs of specified files or directories.\n",
406             prgname, prgname);
407       return EXIT_FAILURE;
408    }
409    while (argc--) {
410       if (!aclls(*argv++)) {
411          status = EXIT_FAILURE;
412       }
413    }
414
415    return status;
416 }
417
418 /**** Test program *****/
419 int aclcp(char *src, char *dst)
420 {
421    struct stat st;
422
423    if (lstat(dst, &st) != 0) {
424       Dmsg0(200, "aclcp: destination does not exist\n");
425       return EXIT_FAILURE;
426    }
427    if (S_ISLNK(st.st_mode)) {
428       Dmsg0(200, "aclcp: cannot set ACL on symlinks\n");
429       return EXIT_FAILURE;
430    }
431    if (lstat(src, &st) != 0) {
432       Dmsg0(200, "aclcp: source does not exist\n");
433       return EXIT_FAILURE;
434    }
435    if (S_ISLNK(st.st_mode)) {
436       Dmsg0(200, "aclcp: will not read ACL from symlinks\n");
437       return EXIT_FAILURE;
438    }
439
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);
443       return EXIT_FAILURE;
444    } else {
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);
448          return EXIT_FAILURE;
449       }
450    }
451
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);
456          return EXIT_FAILURE;
457       } else {
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);
461             return EXIT_FAILURE;
462          }
463       }
464    }
465
466    return 0;
467 }
468
469 /**** Test program *****/
470 int aclls(char *fname)
471 {
472    struct stat st;
473    int len;
474
475    if (lstat(fname, &st) != 0) {
476       Dmsg0(200, "acl: source does not exist\n");
477       return EXIT_FAILURE;
478    }
479    if (S_ISLNK(st.st_mode)) {
480       Dmsg0(200, "acl: will not read ACL from symlinks\n");
481       return EXIT_FAILURE;
482    }
483
484    jcr.last_fname = fname;
485
486    len = bacl_get(&jcr, BACL_TYPE_ACCESS);
487    if (len < 0) {
488       Dmsg1(200, "acl: could not read ACLs for %s\n", jcr.last_fname);
489       return EXIT_FAILURE;
490    } else if (len == 0) {
491       printf("#file: %s [standard permissions - or unsupported]\n\n", jcr.last_fname);
492    } else {
493       printf("#file: %s\n%s\n", jcr.last_fname, jcr.acl_text);
494    }
495
496    if (S_ISDIR(st.st_mode) && (BACL_CAP & BACL_CAP_DEFAULTS_DIR)) {
497       len = bacl_get(&jcr, BACL_TYPE_DEFAULT);
498       if (len < 0) {
499          Dmsg1(200, "acl: could not read default ACLs for %s\n", jcr.last_fname);
500          return EXIT_FAILURE;
501       } else if (len == 0) {
502          printf("#file: %s [default, none - or unsupported]\n\n", jcr.last_fname);
503       } else {
504          printf("#file: %s [default]\n%s\n", jcr.last_fname, jcr.acl_text);
505       }
506    }
507
508    return 0;
509 }
510 #endif