]> git.sur5r.net Git - bacula/bacula/blob - bacula/src/filed/acl.c
Apply fix from Martin Simmons to clear structure before
[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_DARWIN_OS)    /* tested   -- compile wihtout flags  */ \
101       || defined(HAVE_IRIX_OS)      /* man page -- compile without flags  */ \
102       || defined(HAVE_OSF1_OS)      /* man page -- may need -lpacl        */ \
103       || defined(HAVE_LINUX_OS)     /* tested   -- compile with -lacl     */ \
104       || defined(HAVE_HPUX_OS)      /* man page -- may need flags         */ \
105       || defined(HAVE_SUN_OS)       /* tested   -- compile with -lsec     */ \
106        )
107 /*
108  * ***FIXME***
109  * For now we abandon this test and only test for Linux:
110  * 1. This is backwards compatible.
111  * 2. If we allow any of the other now, we may have to provide conversion
112  *    routines if we ever want to distinguish them. Or just do our best
113  *    with what we have and give all ACL streams a new number/type.
114  */
115 #endif
116 #if !defined(HAVE_ACL) \
117    || !( defined(HAVE_LINUX_OS) \
118       || defined(HAVE_FREEBSD_OS) \
119       || defined(HAVE_DARWIN_OS) \
120        )
121
122 /* bacl_get() returns the lenght of the string, or -1 on error. */
123 int bacl_get(JCR *jcr, int acltype)
124 {
125    return -1;
126 }
127
128 int bacl_set(JCR *jcr, int acltype)
129 {
130    return -1;
131 }
132
133 #elif defined(HAVE_AIX_OS)
134
135 #include <sys/access.h>
136
137 int bacl_get(JCR *jcr, int acltype)
138 {
139    char *acl_text;
140    int len;
141
142    if ((acl_text = acl_get(jcr->last_fname)) != NULL) {
143       len = pm_strcpy(jcr->acl_text, acl_text);
144       free(acl_text);
145       return len;
146    }
147    return -1;
148 }
149
150 int bacl_set(JCR *jcr, int acltype)
151 {
152    if (acl_put(jcr->last_fname, jcr->acl_text, 0) != 0) {
153       return -1;
154    }
155    return 0;
156 }
157
158 #elif defined(HAVE_FREEBSD_OS) \
159    || defined(HAVE_DARWIN_OS) \
160    || defined(HAVE_IRIX_OS) \
161    || defined(HAVE_OSF1_OS) \
162    || defined(HAVE_LINUX_OS)
163
164 #include <sys/types.h>
165 #include <sys/acl.h>
166
167 /* On IRIX we can get shortened ACLs */
168 #if defined(HAVE_IRIX_OS) && defined(BACL_WANT_SHORT_ACLS)
169 #define acl_to_text(acl,len)     acl_to_short_text((acl), (len))
170 #endif
171
172 /* In Linux we can get numeric and/or shorted ACLs */
173 #if defined(HAVE_LINUX_OS)
174 #if defined(BACL_WANT_SHORT_ACLS) && defined(BACL_WANT_NUMERIC_IDS)
175 #define BACL_ALTERNATE_TEXT            (TEXT_ABBREVIATE|TEXT_NUMERIC_IDS)
176 #elif defined(BACL_WANT_SHORT_ACLS)
177 #define BACL_ALTERNATE_TEXT            TEXT_ABBREVIATE
178 #elif defined(BACL_WANT_NUMERIC_IDS)
179 #define BACL_ALTERNATE_TEXT            TEXT_NUMERIC_IDS
180 #endif
181 #ifdef BACL_ALTERNATE_TEXT
182 #include <acl/libacl.h>
183 #define acl_to_text(acl,len)     ((len), acl_to_any_text((acl), NULL, ',', BACL_ALTERNATE_TEXT))
184 #endif
185 #endif
186
187 int bacl_get(JCR *jcr, int acltype)
188 {
189    acl_t acl;
190    int len;
191    acl_type_t ostype;
192    char *acl_text;
193
194    ostype = (acltype & BACL_TYPE_DEFAULT) ? ACL_TYPE_DEFAULT : ACL_TYPE_ACCESS;
195
196    acl = acl_get_file(jcr->last_fname, ostype);
197    if (acl) {
198       if ((acl_text = acl_to_text(acl, NULL)) != NULL) {
199          len = pm_strcpy(jcr->acl_text, acl_text);
200          acl_free(acl);
201          acl_free(acl_text);
202          return len;
203       }
204       acl_free(acl);
205 #ifndef HAVE_OSF1_OS          /* BACL_ENOTSUP not defined for OSF1 */
206    } else if (errno == BACL_ENOTSUP) {
207       /* Not supported, just pretend there is nothing to see */
208       return pm_strcpy(jcr->acl_text, "");
209 #endif
210    }
211    /***** Do we really want to silently ignore errors from acl_get_file
212      and acl_to_text?  *****/
213    return 0;
214 }
215
216 int bacl_set(JCR *jcr, int acltype)
217 {
218    acl_t acl;
219    acl_type_t ostype;
220
221    ostype = (acltype & BACL_TYPE_DEFAULT) ? ACL_TYPE_DEFAULT : ACL_TYPE_ACCESS;
222
223    /* If we get empty default ACLs, clear ACLs now */
224    if (ostype == ACL_TYPE_DEFAULT && strlen(jcr->acl_text) == 0) {
225       if (acl_delete_def_file(jcr->last_fname) == 0) {
226          return 0;
227       }
228       return -1;
229    }
230
231    acl = acl_from_text(jcr->acl_text);
232    if (acl == NULL) {
233       return -1;
234    }
235
236    /*
237     * FreeBSD always fails acl_valid() - at least on valid input...
238     * As it does the right thing, given valid input, just ignore acl_valid().
239     */
240 #ifndef HAVE_FREEBSD_OS
241    if (acl_valid(acl) != 0) {
242       acl_free(acl);
243       return -1;
244    }
245 #endif
246
247    if (acl_set_file(jcr->last_fname, ostype, acl) != 0) {
248       acl_free(acl);
249       return -1;
250    }
251    acl_free(acl);
252    return 0;
253 }
254
255 #elif defined(HAVE_HPUX_OS)
256 #include <sys/acl.h>
257 #include <acllib.h>
258
259 int bacl_get(JCR *jcr, int acltype)
260 {
261    int n, len;
262    struct acl_entry acls[NACLENTRIES];
263    char *acl_text;
264
265    if ((n = getacl(jcr->last_fname, 0, acls)) <= 0) {
266       if (errno == BACL_ENOTSUP) {
267          return pm_strcpy(jcr->acl_text, "");
268       }
269       return -1;
270    }
271    if ((n = getacl(jcr->last_fname, n, acls)) > 0) {
272       if ((acl_text = acltostr(n, acls, FORM_SHORT)) != NULL) {
273          len = pm_strcpy(jcr->acl_text, acl_text);
274          free(acl_text);
275          return len;
276       }
277    }
278    return -1;
279 }
280
281 int bacl_set(JCR *jcr, int acltype)
282 {
283    int n, stat;
284    struct acl_entry acls[NACLENTRIES];
285
286    n = strtoacl(jcr->acl_text, 0, NACLENTRIES, acls, ACL_FILEOWNER, ACL_FILEGROUP);
287    if (n <= 0) {
288       return -1;
289    }
290    if (strtoacl(jcr->acl_text, n, NACLENTRIES, acls, ACL_FILEOWNER, ACL_FILEGROUP) != n) {
291       return -1;
292    }
293    if (setacl(jcr->last_fname, n, acls) != 0) {
294       return -1;
295    }
296    return 0;
297 }
298
299 #elif defined(HAVE_SUN_OS)
300 #include <sys/acl.h>
301
302 int bacl_get(JCR *jcr, int acltype)
303 {
304    int n, len;
305    aclent_t *acls;
306    char *acl_text;
307
308    n = acl(jcr->last_fname, GETACLCNT, 0, NULL);
309    if (n < MIN_ACL_ENTRIES) {
310       return -1;
311    } else if (n == MIN_ACL_ENTRIES) {
312       /* The ACLs simply reflect the (already known) standard permissions */
313       return pm_strcpy(jcr->acl_text, "");
314    }
315    if ((acls = (aclent_t *)malloc(n * sizeof(aclent_t))) == NULL) {
316       return -1;
317    }
318    if (acl(jcr->last_fname, GETACL, n, acls) == n) {
319       if ((acl_text = acltotext(acls, n)) != NULL) {
320          len = pm_strcpy(jcr->acl_text, acl_text);
321          free(acl_text);
322          free(acls);
323          return len;
324       }
325    }
326    free(acls);
327    return -1;
328 }
329
330 int bacl_set(JCR *jcr, int acltype)
331 {
332    int n;
333    aclent_t *acls;
334
335    acls = aclfromtext(jcr->acl_text, &n);
336    if (!acls) {
337       return -1;
338    }
339    if (acl(jcr->last_fname, SETACL, n, acls) != 0) {
340       free(acls);
341       return -1;
342    }
343    free(acls);
344    return 0;
345 }
346
347 #endif
348
349
350 #ifdef TEST_PROGRAM
351 int main(int argc, char **argv)
352 {
353    char *prgname;
354    int status = 0;
355
356    if (argc < 1) {
357       Dmsg0(200, "Cannot determine my own name\n");
358       return EXIT_FAILURE;
359    }
360
361    prgname = strrchr(argv[0], '/');
362    if (prgname == NULL || *++prgname == '\0') {
363       prgname = argv[0];
364    }
365    --argc;
366    ++argv;
367
368    /* aclcp "copies" ACLs - set ACLs on destination equal to ACLs on source */
369    if (strcmp(prgname, "aclcp") == 0) {
370       int verbose = 0;
371       if (strcmp(*argv, "-v") == 0) {
372          ++verbose;
373          --argc;
374          ++argv;
375       }
376       if (argc != 2) {
377          Dmsg2(200, "%s: wrong number of arguments\n"
378                "usage:\t%s [-v] source destination\n"
379                "\tCopies ACLs from source to destination.\n"
380                "\tSpecify -v to show ACLs after copy for verification.\n",
381                prgname, prgname);
382          return EXIT_FAILURE;
383       }
384       if (strcmp(argv[0], argv[1]) == 0) {
385          Dmsg2(200, "%s: identical source and destination.\n"
386                "usage:\t%s [-v] source destination\n"
387                "\tCopies ACLs from source to destination.\n"
388                "\tSpecify -v to show ACLs after copy for verification.\n",
389                prgname, prgname);
390          return EXIT_FAILURE;
391       }
392       if (verbose) {
393          aclls(argv[0]);
394       }
395       status = aclcp(argv[0], argv[1]);
396       if (verbose && status == 0) {
397          aclls(argv[1]);
398       }
399       return status;
400    }
401
402    /* Default: just list ACLs */
403    if (argc < 1) {
404       Dmsg2(200, "%s: missing arguments\n"
405             "usage:\t%s file ...\n"
406             "\tLists ACLs of specified files or directories.\n",
407             prgname, prgname);
408       return EXIT_FAILURE;
409    }
410    while (argc--) {
411       if (!aclls(*argv++)) {
412          status = EXIT_FAILURE;
413       }
414    }
415
416    return status;
417 }
418
419 /**** Test program *****/
420 int aclcp(char *src, char *dst)
421 {
422    struct stat st;
423
424    if (lstat(dst, &st) != 0) {
425       Dmsg0(200, "aclcp: destination does not exist\n");
426       return EXIT_FAILURE;
427    }
428    if (S_ISLNK(st.st_mode)) {
429       Dmsg0(200, "aclcp: cannot set ACL on symlinks\n");
430       return EXIT_FAILURE;
431    }
432    if (lstat(src, &st) != 0) {
433       Dmsg0(200, "aclcp: source does not exist\n");
434       return EXIT_FAILURE;
435    }
436    if (S_ISLNK(st.st_mode)) {
437       Dmsg0(200, "aclcp: will not read ACL from symlinks\n");
438       return EXIT_FAILURE;
439    }
440
441    jcr.last_fname = src;
442    if (bacl_get(&jcr, BACL_TYPE_ACCESS) < 0) {
443       Dmsg1(200, "aclcp: could not read ACLs for %s\n", jcr.last_fname);
444       return EXIT_FAILURE;
445    } else {
446       jcr.last_fname = dst;
447       if (bacl_set(&jcr, BACL_TYPE_ACCESS) < 0) {
448          Dmsg1(200, "aclcp: could not set ACLs on %s\n", jcr.last_fname);
449          return EXIT_FAILURE;
450       }
451    }
452
453    if (S_ISDIR(st.st_mode) && (BACL_CAP & BACL_CAP_DEFAULTS_DIR)) {
454       jcr.last_fname = src;
455       if (bacl_get(&jcr, BACL_TYPE_DEFAULT) < 0) {
456          Dmsg1(200, "aclcp: could not read default ACLs for %s\n", jcr.last_fname);
457          return EXIT_FAILURE;
458       } else {
459          jcr.last_fname = dst;
460          if (bacl_set(&jcr, BACL_TYPE_DEFAULT) < 0) {
461             Dmsg1(200, "aclcp: could not set default ACLs on %s\n", jcr.last_fname);
462             return EXIT_FAILURE;
463          }
464       }
465    }
466
467    return 0;
468 }
469
470 /**** Test program *****/
471 int aclls(char *fname)
472 {
473    struct stat st;
474    int len;
475
476    if (lstat(fname, &st) != 0) {
477       Dmsg0(200, "acl: source does not exist\n");
478       return EXIT_FAILURE;
479    }
480    if (S_ISLNK(st.st_mode)) {
481       Dmsg0(200, "acl: will not read ACL from symlinks\n");
482       return EXIT_FAILURE;
483    }
484
485    jcr.last_fname = fname;
486
487    len = bacl_get(&jcr, BACL_TYPE_ACCESS);
488    if (len < 0) {
489       Dmsg1(200, "acl: could not read ACLs for %s\n", jcr.last_fname);
490       return EXIT_FAILURE;
491    } else if (len == 0) {
492       printf("#file: %s [standard permissions - or unsupported]\n\n", jcr.last_fname);
493    } else {
494       printf("#file: %s\n%s\n", jcr.last_fname, jcr.acl_text);
495    }
496
497    if (S_ISDIR(st.st_mode) && (BACL_CAP & BACL_CAP_DEFAULTS_DIR)) {
498       len = bacl_get(&jcr, BACL_TYPE_DEFAULT);
499       if (len < 0) {
500          Dmsg1(200, "acl: could not read default ACLs for %s\n", jcr.last_fname);
501          return EXIT_FAILURE;
502       } else if (len == 0) {
503          printf("#file: %s [default, none - or unsupported]\n\n", jcr.last_fname);
504       } else {
505          printf("#file: %s [default]\n%s\n", jcr.last_fname, jcr.acl_text);
506       }
507    }
508
509    return 0;
510 }
511 #endif