]> git.sur5r.net Git - bacula/bacula/blob - bacula/src/filed/acl.c
Convert to pure GPL v2 license.
[bacula/bacula] / bacula / src / filed / acl.c
1 /*
2    Bacula® - The Network Backup Solution
3
4    Copyright (C) 2004-2007 Free Software Foundation Europe e.V.
5
6    The main author of Bacula is Kern Sibbald, with contributions from
7    many others, a complete list can be found in the file AUTHORS.
8    This program is Free Software; you can redistribute it and/or
9    modify it under the terms of version two of the GNU General Public
10    License as published by the Free Software Foundation and included
11    in the file LICENSE.
12
13    This program is distributed in the hope that it will be useful, but
14    WITHOUT ANY WARRANTY; without even the implied warranty of
15    MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
16    General Public License for more details.
17
18    You should have received a copy of the GNU General Public License
19    along with this program; if not, write to the Free Software
20    Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA
21    02110-1301, USA.
22
23    Bacula® is a registered trademark of John Walker.
24    The licensor of Bacula is the Free Software Foundation Europe
25    (FSFE), Fiduciary Program, Sumatrastrasse 25, 8006 Zürich,
26    Switzerland, email:ftf@fsfeurope.org.
27 */
28 /*
29  * Functions to handle ACL for bacula.
30  *
31  * We handle two different typers of ACLs: access and default ACLS.
32  * Default ACLs only apply to directories.
33  *
34  * On some systems (eg. linux and FreeBSD) we must obtain the two ACLs
35  * independently, while others (eg. Solaris) provide both in one call.
36  *
37  * As for the streams to use, we have two choices:
38  *
39  * 1. Build a generic framework.
40  *    With two different types of ACLs, supported differently, we
41  *    probably end up encoding and decoding everything ourselves.
42  *
43  * 2. Take the easy way out.
44  *    Just handle each platform individually, assuming that backups
45  *    and restores are done on the same (kind of) client.
46  *
47  * Currently we take the easy way out. We use two kinds of streams, one
48  * for access ACLs and one for default ACLs. If an OS mixes the two, we
49  * send the mix in the access ACL stream.
50  *
51  * Looking at more man pages, supporting a framework seems really hard
52  * if we want to support HP-UX. Deity knows what AIX is up to.
53  *
54  *   Written by Preben 'Peppe' Guldberg, December MMIV
55  *
56  *   Version $Id$
57  */
58
59
60 #ifndef TEST_PROGRAM
61
62 #include "bacula.h"
63 #include "filed.h"
64
65 #endif
66
67 /*
68  * List of supported OSs.
69  * Not sure if all the HAVE_XYZ_OS are correct for autoconf.
70  * The ones that says man page, are coded according to man pages only.
71  */
72 #if !defined(HAVE_ACL)              /* ACL support is required, of course */ \
73    || !( defined(HAVE_AIX_OS)       /* man page -- may need flags         */ \
74       || defined(HAVE_FREEBSD_OS)   /* tested   -- compile without flags  */ \
75       || defined(HAVE_DARWIN_OS)    /* tested   -- compile without flags  */ \
76       || defined(HAVE_IRIX_OS)      /* man page -- compile without flags  */ \
77       || defined(HAVE_OSF1_OS)      /* man page -- may need -lpacl        */ \
78       || defined(HAVE_LINUX_OS)     /* tested   -- compile with -lacl     */ \
79       || defined(HAVE_HPUX_OS)      /* man page -- may need flags         */ \
80       || defined(HAVE_SUN_OS)       /* tested   -- compile with -lsec     */ \
81        )
82 /*
83  * ***FIXME***
84  * For now we abandon this test and only test for Linux:
85  * 1. This is backwards compatible.
86  * 2. If we allow any of the other now, we may have to provide conversion
87  *    routines if we ever want to distinguish them. Or just do our best
88  *    with what we have and give all ACL streams a new number/type.
89  */
90 #endif
91
92 #if !defined(HAVE_ACL) \
93    || !( defined(HAVE_LINUX_OS) \
94       || defined(HAVE_FREEBSD_OS) \
95       || defined(HAVE_DARWIN_OS) \
96       || defined(HAVE_IRIX_OS) \
97       || defined(HAVE_OSF1_OS) \
98       || defined(HAVE_SUN_OS) \
99        )
100
101 /* bacl_get() returns the lenght of the string, or -1 on error. */
102 int bacl_get(JCR *jcr, int acltype)
103 {
104    Jmsg(jcr, M_FATAL, 0, _("ACL support not configured for your machine.\n"));
105    return -1;
106 }
107
108 int bacl_set(JCR *jcr, int acltype)
109 {
110    Jmsg(jcr, M_FATAL, 0, _("ACL support not configured for your machine.\n"));
111    return -1;
112 }
113
114 #elif defined(HAVE_AIX_OS)
115
116 #include <sys/access.h>
117
118 int bacl_get(JCR *jcr, int acltype)
119 {
120    char *acl_text;
121    int len;
122
123    if ((acl_text = acl_get(jcr->last_fname)) != NULL) {
124       len = pm_strcpy(jcr->acl_text, acl_text);
125       actuallyfree(acl_text);
126       return len;
127    }
128    return -1;
129 }
130
131 int bacl_set(JCR *jcr, int acltype)
132 {
133    if (acl_put(jcr->last_fname, jcr->acl_text, 0) != 0) {
134       return -1;
135    }
136    return 0;
137 }
138
139 #elif defined(HAVE_FREEBSD_OS) \
140    || defined(HAVE_DARWIN_OS) \
141    || defined(HAVE_IRIX_OS) \
142    || defined(HAVE_OSF1_OS) \
143    || defined(HAVE_LINUX_OS)
144
145 #include <sys/types.h>
146 #include <sys/acl.h>
147
148 /* On IRIX we can get shortened ACLs */
149 #if defined(HAVE_IRIX_OS) && defined(BACL_WANT_SHORT_ACLS)
150 #define acl_to_text(acl,len)     acl_to_short_text((acl), (len))
151 #endif
152
153 /* In Linux we can get numeric and/or shorted ACLs */
154 #if defined(HAVE_LINUX_OS)
155 #if defined(BACL_WANT_SHORT_ACLS) && defined(BACL_WANT_NUMERIC_IDS)
156 #define BACL_ALTERNATE_TEXT            (TEXT_ABBREVIATE|TEXT_NUMERIC_IDS)
157 #elif defined(BACL_WANT_SHORT_ACLS)
158 #define BACL_ALTERNATE_TEXT            TEXT_ABBREVIATE
159 #elif defined(BACL_WANT_NUMERIC_IDS)
160 #define BACL_ALTERNATE_TEXT            TEXT_NUMERIC_IDS
161 #endif
162 #ifdef BACL_ALTERNATE_TEXT
163 #include <acl/libacl.h>
164 #define acl_to_text(acl,len)     ((len), acl_to_any_text((acl), NULL, ',', BACL_ALTERNATE_TEXT))
165 #endif
166 #endif
167
168 int bacl_get(JCR *jcr, int acltype)
169 {
170    acl_t acl;
171    int len;
172    acl_type_t ostype;
173    char *acl_text;
174
175    ostype = (acltype & BACL_TYPE_DEFAULT) ? ACL_TYPE_DEFAULT : ACL_TYPE_ACCESS;
176
177    acl = acl_get_file(jcr->last_fname, ostype);
178    if (acl) {
179       if ((acl_text = acl_to_text(acl, NULL)) != NULL) {
180          len = pm_strcpy(jcr->acl_text, acl_text);
181          acl_free(acl);
182          acl_free(acl_text);
183          return len;
184       }
185       berrno be;
186       Jmsg2(jcr, M_ERROR, 0, _("acl_to_text error on file \"%s\": ERR=%s\n"),
187          jcr->last_fname, be.bstrerror());
188       Dmsg3(100, "acl_to_text error acl=%s file=%s ERR=%s\n",  
189          jcr->acl_text, jcr->last_fname, be.bstrerror());
190       acl_free(acl);
191 #ifndef HAVE_OSF1_OS          /* BACL_ENOTSUP not defined for OSF1 */
192    } else if (errno == BACL_ENOTSUP) {
193       /* Not supported, just pretend there is nothing to see */
194       return pm_strcpy(jcr->acl_text, "");
195 #endif
196    }
197    /***** Do we really want to silently ignore errors from acl_get_file
198      and acl_to_text?  *****/
199    return 0;
200 }
201
202 int bacl_set(JCR *jcr, int acltype)
203 {
204    acl_t acl;
205    acl_type_t ostype;
206
207    ostype = (acltype & BACL_TYPE_DEFAULT) ? ACL_TYPE_DEFAULT : ACL_TYPE_ACCESS;
208
209    /* If we get empty default ACLs, clear ACLs now */
210    if (ostype == ACL_TYPE_DEFAULT && strlen(jcr->acl_text) == 0) {
211       if (acl_delete_def_file(jcr->last_fname) == 0) {
212          return 0;
213       }
214       berrno be;
215       Jmsg2(jcr, M_ERROR, 0, _("acl_delete_def_file error on file \"%s\": ERR=%s\n"),
216          jcr->last_fname, be.bstrerror());
217       return -1;
218    }
219
220    acl = acl_from_text(jcr->acl_text);
221    if (acl == NULL) {
222       berrno be;
223       Jmsg2(jcr, M_ERROR, 0, _("acl_from_text error on file \"%s\": ERR=%s\n"),
224          jcr->last_fname, be.bstrerror());
225       Dmsg3(100, "acl_from_text error acl=%s file=%s ERR=%s\n",  
226          jcr->acl_text, jcr->last_fname, be.bstrerror());
227       return -1;
228    }
229
230    /*
231     * FreeBSD always fails acl_valid() - at least on valid input...
232     * As it does the right thing, given valid input, just ignore acl_valid().
233     */
234 #ifndef HAVE_FREEBSD_OS
235    if (acl_valid(acl) != 0) {
236       berrno be;
237       Jmsg2(jcr, M_ERROR, 0, _("ac_valid error on file \"%s\": ERR=%s\n"),
238          jcr->last_fname, be.bstrerror());
239       Dmsg3(100, "acl_valid error acl=%s file=%s ERR=%s\n",  
240          jcr->acl_text, jcr->last_fname, be.bstrerror());
241       acl_free(acl);
242       return -1;
243    }
244 #endif
245
246    /*
247     * Restore the ACLs, but don't complain about links which really should
248     * not have attributes, and the file it is linked to may not yet be restored.
249     */
250    if (acl_set_file(jcr->last_fname, ostype, acl) != 0 && jcr->last_type != FT_LNK) {
251       berrno be;
252       Jmsg2(jcr, M_ERROR, 0, _("acl_set_file error on file \"%s\": ERR=%s\n"),
253          jcr->last_fname, be.bstrerror());
254       Dmsg3(100, "acl_set_file error acl=%s file=%s ERR=%s\n",  
255          jcr->acl_text, jcr->last_fname, be.bstrerror());
256       acl_free(acl);
257       return -1;
258    }
259    acl_free(acl);
260    return 0;
261 }
262
263 #elif defined(HAVE_HPUX_OS)
264 #include <sys/acl.h>
265 #include <acllib.h>
266
267 int bacl_get(JCR *jcr, int acltype)
268 {
269    int n, len;
270    struct acl_entry acls[NACLENTRIES];
271    char *acl_text;
272
273    if ((n = getacl(jcr->last_fname, 0, acls)) <= 0) {
274       if (errno == BACL_ENOTSUP) {
275          return pm_strcpy(jcr->acl_text, "");
276       }
277       return -1;
278    }
279    if ((n = getacl(jcr->last_fname, n, acls)) > 0) {
280       if ((acl_text = acltostr(n, acls, FORM_SHORT)) != NULL) {
281          len = pm_strcpy(jcr->acl_text, acl_text);
282          actuallyfree(acl_text);
283          return len;
284       }
285       berrno be;
286       Jmsg2(jcr, M_ERROR, 0, _("acltostr error on file \"%s\": ERR=%s\n"),
287          jcr->last_fname, be.bstrerror());
288       Dmsg3(100, "acltostr error acl=%s file=%s ERR=%s\n",  
289          jcr->acl_text, jcr->last_fname, be.bstrerror());
290       return -1;
291    }
292    return -1;
293 }
294
295 int bacl_set(JCR *jcr, int acltype)
296 {
297    int n, stat;
298    struct acl_entry acls[NACLENTRIES];
299
300    n = strtoacl(jcr->acl_text, 0, NACLENTRIES, acls, ACL_FILEOWNER, ACL_FILEGROUP);
301    if (n <= 0) {
302       berrno be;
303       Jmsg2(jcr, M_ERROR, 0, _("strtoacl error on file \"%s\": ERR=%s\n"),
304          jcr->last_fname, be.bstrerror());
305       Dmsg3(100, "strtoacl error acl=%s file=%s ERR=%s\n",  
306          jcr->acl_text, jcr->last_fname, be.bstrerror());
307       return -1;
308    }
309    if (strtoacl(jcr->acl_text, n, NACLENTRIES, acls, ACL_FILEOWNER, ACL_FILEGROUP) != n) {
310       berrno be;
311       Jmsg2(jcr, M_ERROR, 0, _("strtoacl error on file \"%s\": ERR=%s\n"),
312          jcr->last_fname, be.bstrerror());
313       Dmsg3(100, "strtoacl error acl=%s file=%s ERR=%s\n",  
314          jcr->acl_text, jcr->last_fname, be.bstrerror());
315       return -1;
316    }
317    /*
318     * Restore the ACLs, but don't complain about links which really should
319     * not have attributes, and the file it is linked to may not yet be restored.
320     */
321    if (setacl(jcr->last_fname, n, acls) != 0 && jcr->last_type != FT_LNK) {
322       berrno be;
323       Jmsg2(jcr, M_ERROR, 0, _("setacl error on file \"%s\": ERR=%s\n"),
324          jcr->last_fname, be.bstrerror());
325       Dmsg3(100, "setacl error acl=%s file=%s ERR=%s\n",  
326          jcr->acl_text, jcr->last_fname, be.bstrerror());
327       return -1;
328    }
329    return 0;
330 }
331
332 #elif defined(HAVE_SUN_OS)
333 #include <sys/acl.h>
334
335 int bacl_get(JCR *jcr, int acltype)
336 {
337    int n, len;
338    aclent_t *acls;
339    char *acl_text;
340
341    n = acl(jcr->last_fname, GETACLCNT, 0, NULL);
342    if (n < MIN_ACL_ENTRIES) {
343       return -1;
344    } else if (n == MIN_ACL_ENTRIES) {
345       /* The ACLs simply reflect the (already known) standard permissions */
346       return pm_strcpy(jcr->acl_text, "");
347    }
348    if ((acls = (aclent_t *)malloc(n * sizeof(aclent_t))) == NULL) {
349       return -1;
350    }
351    if (acl(jcr->last_fname, GETACL, n, acls) == n) {
352       if ((acl_text = acltotext(acls, n)) != NULL) {
353          len = pm_strcpy(jcr->acl_text, acl_text);
354          actuallyfree(acl_text);
355          free(acls);
356          return len;
357       }
358       berrno be;
359       Jmsg2(jcr, M_ERROR, 0, _("acltotext error on file \"%s\": ERR=%s\n"),
360          jcr->last_fname, be.bstrerror());
361       Dmsg3(100, "acltotext error acl=%s file=%s ERR=%s\n",  
362          jcr->acl_text, jcr->last_fname, be.bstrerror());
363    }
364    free(acls);
365    return -1;
366 }
367
368 int bacl_set(JCR *jcr, int acltype)
369 {
370    int n;
371    aclent_t *acls;
372
373    acls = aclfromtext(jcr->acl_text, &n);
374    if (!acls) {
375       berrno be;
376       Jmsg2(jcr, M_ERROR, 0, _("aclfromtext error on file \"%s\": ERR=%s\n"),
377          jcr->last_fname, be.bstrerror());
378       Dmsg3(100, "aclfromtext error acl=%s file=%s ERR=%s\n",  
379          jcr->acl_text, jcr->last_fname, be.bstrerror());
380       return -1;
381    }
382    /*
383     * Restore the ACLs, but don't complain about links which really should
384     * not have attributes, and the file it is linked to may not yet be restored.
385     */
386    if (acl(jcr->last_fname, SETACL, n, acls) == -1 && jcr->last_type != FT_LNK) {
387       berrno be;
388       Jmsg2(jcr, M_ERROR, 0, _("acl(SETACL) error on file \"%s\": ERR=%s\n"),
389          jcr->last_fname, be.bstrerror());
390       Dmsg3(100, "acl(SETACL) error acl=%s file=%s ERR=%s\n",  
391          jcr->acl_text, jcr->last_fname, be.bstrerror());
392       actuallyfree(acls);
393       return -1;
394    }
395    actuallyfree(acls);
396    return 0;
397 }
398
399 #endif
400
401
402 #ifdef TEST_PROGRAM
403
404 /*
405  * Test program setup 
406  *
407  * Compile and set up with eg. with eg.
408  *
409  *    $ cc -DTEST_PROGRAM -DHAVE_SUN_OS -lsec -o acl acl.c
410  *    $ ln -s acl aclcp
411  *
412  * You can then list ACLs with acl and copy them with aclcp.
413  *
414  * For a list of compiler flags, see the list preceding the big #if below.
415  */
416 #include <errno.h>
417 #include <stdio.h>
418 #include <stdlib.h>
419 #include <string.h>
420 #include <sys/stat.h>
421 #include "acl.h"
422
423 #define BACLLEN 65535
424 #define pm_strcpy(d,s)     (strncpy(d, s, BACLLEN - 1) == NULL ? -1 : (int)strlen(d))
425 #define Dmsg0(n,s)         fprintf(stderr, s)
426 #define Dmsg1(n,s,a1)      fprintf(stderr, s, a1)
427 #define Dmsg2(n,s,a1,a2)   fprintf(stderr, s, a1, a2)
428
429 int aclls(char *fname);
430 int aclcp(char *src, char *dst);
431
432 struct JCRstruct {
433    char *last_fname;
434    char acl_text[BACLLEN];
435 };
436 typedef struct JCRstruct JCR;
437 JCR jcr;
438
439 int main(int argc, char **argv)
440 {
441    char *prgname;
442    int status = 0;
443
444    if (argc < 1) {
445       Dmsg0(200, "Cannot determine my own name\n");
446       return EXIT_FAILURE;
447    }
448
449    prgname = last_path_separator(argv[0]);
450    if (prgname == NULL || *++prgname == '\0') {
451       prgname = argv[0];
452    }
453    --argc;
454    ++argv;
455
456    /* aclcp "copies" ACLs - set ACLs on destination equal to ACLs on source */
457    if (strcmp(prgname, "aclcp") == 0) {
458       int verbose = 0;
459       if (strcmp(*argv, "-v") == 0) {
460          ++verbose;
461          --argc;
462          ++argv;
463       }
464       if (argc != 2) {
465          Dmsg2(200, "%s: wrong number of arguments\n"
466                "usage:\t%s [-v] source destination\n"
467                "\tCopies ACLs from source to destination.\n"
468                "\tSpecify -v to show ACLs after copy for verification.\n",
469                prgname, prgname);
470          return EXIT_FAILURE;
471       }
472       if (strcmp(argv[0], argv[1]) == 0) {
473          Dmsg2(200, "%s: identical source and destination.\n"
474                "usage:\t%s [-v] source destination\n"
475                "\tCopies ACLs from source to destination.\n"
476                "\tSpecify -v to show ACLs after copy for verification.\n",
477                prgname, prgname);
478          return EXIT_FAILURE;
479       }
480       if (verbose) {
481          aclls(argv[0]);
482       }
483       status = aclcp(argv[0], argv[1]);
484       if (verbose && status == 0) {
485          aclls(argv[1]);
486       }
487       return status;
488    }
489
490    /* Default: just list ACLs */
491    if (argc < 1) {
492       Dmsg2(200, "%s: missing arguments\n"
493             "usage:\t%s file ...\n"
494             "\tLists ACLs of specified files or directories.\n",
495             prgname, prgname);
496       return EXIT_FAILURE;
497    }
498    while (argc--) {
499       if (!aclls(*argv++)) {
500          status = EXIT_FAILURE;
501       }
502    }
503
504    return status;
505 }
506
507 /**** Test program *****/
508 int aclcp(char *src, char *dst)
509 {
510    struct stat st;
511
512    if (lstat(dst, &st) != 0) {
513       Dmsg0(200, "aclcp: destination does not exist\n");
514       return EXIT_FAILURE;
515    }
516    if (S_ISLNK(st.st_mode)) {
517       Dmsg0(200, "aclcp: cannot set ACL on symlinks\n");
518       return EXIT_FAILURE;
519    }
520    if (lstat(src, &st) != 0) {
521       Dmsg0(200, "aclcp: source does not exist\n");
522       return EXIT_FAILURE;
523    }
524    if (S_ISLNK(st.st_mode)) {
525       Dmsg0(200, "aclcp: will not read ACL from symlinks\n");
526       return EXIT_FAILURE;
527    }
528
529    jcr.last_fname = src;
530    if (bacl_get(&jcr, BACL_TYPE_ACCESS) < 0) {
531       Dmsg1(200, "aclcp: could not read ACLs for %s\n", jcr.last_fname);
532       return EXIT_FAILURE;
533    } else {
534       jcr.last_fname = dst;
535       if (bacl_set(&jcr, BACL_TYPE_ACCESS) < 0) {
536          Dmsg1(200, "aclcp: could not set ACLs on %s\n", jcr.last_fname);
537          return EXIT_FAILURE;
538       }
539    }
540
541    if (S_ISDIR(st.st_mode) && (BACL_CAP & BACL_CAP_DEFAULTS_DIR)) {
542       jcr.last_fname = src;
543       if (bacl_get(&jcr, BACL_TYPE_DEFAULT) < 0) {
544          Dmsg1(200, "aclcp: could not read default ACLs for %s\n", jcr.last_fname);
545          return EXIT_FAILURE;
546       } else {
547          jcr.last_fname = dst;
548          if (bacl_set(&jcr, BACL_TYPE_DEFAULT) < 0) {
549             Dmsg1(200, "aclcp: could not set default ACLs on %s\n", jcr.last_fname);
550             return EXIT_FAILURE;
551          }
552       }
553    }
554
555    return 0;
556 }
557
558 /**** Test program *****/
559 int aclls(char *fname)
560 {
561    struct stat st;
562    int len;
563
564    if (lstat(fname, &st) != 0) {
565       Dmsg0(200, "acl: source does not exist\n");
566       return EXIT_FAILURE;
567    }
568    if (S_ISLNK(st.st_mode)) {
569       Dmsg0(200, "acl: will not read ACL from symlinks\n");
570       return EXIT_FAILURE;
571    }
572
573    jcr.last_fname = fname;
574
575    len = bacl_get(&jcr, BACL_TYPE_ACCESS);
576    if (len < 0) {
577       Dmsg1(200, "acl: could not read ACLs for %s\n", jcr.last_fname);
578       return EXIT_FAILURE;
579    } else if (len == 0) {
580       printf("#file: %s [standard permissions - or unsupported]\n\n", jcr.last_fname);
581    } else {
582       printf("#file: %s\n%s\n", jcr.last_fname, jcr.acl_text);
583    }
584
585    if (S_ISDIR(st.st_mode) && (BACL_CAP & BACL_CAP_DEFAULTS_DIR)) {
586       len = bacl_get(&jcr, BACL_TYPE_DEFAULT);
587       if (len < 0) {
588          Dmsg1(200, "acl: could not read default ACLs for %s\n", jcr.last_fname);
589          return EXIT_FAILURE;
590       } else if (len == 0) {
591          printf("#file: %s [default, none - or unsupported]\n\n", jcr.last_fname);
592       } else {
593          printf("#file: %s [default]\n%s\n", jcr.last_fname, jcr.acl_text);
594       }
595    }
596
597    return 0;
598 }
599 #endif