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