]> git.sur5r.net Git - bacula/bacula/blob - bacula/src/filed/acl.c
ebl update
[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    Bacula® - The Network Backup Solution
33
34    Copyright (C) 2004-2006 Free Software Foundation Europe e.V.
35
36    The main author of Bacula is Kern Sibbald, with contributions from
37    many others, a complete list can be found in the file AUTHORS.
38    This program is Free Software; you can redistribute it and/or
39    modify it under the terms of version two of the GNU General Public
40    License as published by the Free Software Foundation plus additions
41    that are listed in the file LICENSE.
42
43    This program is distributed in the hope that it will be useful, but
44    WITHOUT ANY WARRANTY; without even the implied warranty of
45    MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
46    General Public License for more details.
47
48    You should have received a copy of the GNU General Public License
49    along with this program; if not, write to the Free Software
50    Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA
51    02110-1301, USA.
52
53    Bacula® is a registered trademark of John Walker.
54    The licensor of Bacula is the Free Software Foundation Europe
55    (FSFE), Fiduciary Program, Sumatrastrasse 25, 8006 Zürich,
56    Switzerland, email:ftf@fsfeurope.org.
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 wihtout flags  */ \
75       || defined(HAVE_DARWIN_OS)    /* tested   -- compile wihtout 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 #if !defined(HAVE_ACL) \
92    || !( defined(HAVE_LINUX_OS) \
93       || defined(HAVE_FREEBSD_OS) \
94       || defined(HAVE_DARWIN_OS) \
95       || defined(HAVE_IRIX_OS) \
96       || defined(HAVE_OSF1_OS) \
97       || defined(HAVE_SUN_OS) \
98        )
99
100 /* bacl_get() returns the lenght of the string, or -1 on error. */
101 int bacl_get(JCR *jcr, int acltype)
102 {
103    Jmsg(jcr, M_FATAL, 0, _("ACL support not configured for your machine.\n"));
104    return -1;
105 }
106
107 int bacl_set(JCR *jcr, int acltype)
108 {
109    Jmsg(jcr, M_FATAL, 0, _("ACL support not configured for your machine.\n"));
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       actuallyfree(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_DARWIN_OS) \
140    || defined(HAVE_IRIX_OS) \
141    || defined(HAVE_OSF1_OS) \
142    || defined(HAVE_LINUX_OS)
143
144 #include <sys/types.h>
145 #include <sys/acl.h>
146
147 /* On IRIX we can get shortened ACLs */
148 #if defined(HAVE_IRIX_OS) && defined(BACL_WANT_SHORT_ACLS)
149 #define acl_to_text(acl,len)     acl_to_short_text((acl), (len))
150 #endif
151
152 /* In Linux we can get numeric and/or shorted ACLs */
153 #if defined(HAVE_LINUX_OS)
154 #if defined(BACL_WANT_SHORT_ACLS) && defined(BACL_WANT_NUMERIC_IDS)
155 #define BACL_ALTERNATE_TEXT            (TEXT_ABBREVIATE|TEXT_NUMERIC_IDS)
156 #elif defined(BACL_WANT_SHORT_ACLS)
157 #define BACL_ALTERNATE_TEXT            TEXT_ABBREVIATE
158 #elif defined(BACL_WANT_NUMERIC_IDS)
159 #define BACL_ALTERNATE_TEXT            TEXT_NUMERIC_IDS
160 #endif
161 #ifdef BACL_ALTERNATE_TEXT
162 #include <acl/libacl.h>
163 #define acl_to_text(acl,len)     ((len), acl_to_any_text((acl), NULL, ',', BACL_ALTERNATE_TEXT))
164 #endif
165 #endif
166
167 int bacl_get(JCR *jcr, int acltype)
168 {
169    acl_t acl;
170    int len;
171    acl_type_t ostype;
172    char *acl_text;
173
174    ostype = (acltype & BACL_TYPE_DEFAULT) ? ACL_TYPE_DEFAULT : ACL_TYPE_ACCESS;
175
176    acl = acl_get_file(jcr->last_fname, ostype);
177    if (acl) {
178       if ((acl_text = acl_to_text(acl, NULL)) != NULL) {
179          len = pm_strcpy(jcr->acl_text, acl_text);
180          acl_free(acl);
181          acl_free(acl_text);
182          return len;
183       }
184       berrno be;
185       Jmsg2(jcr, M_ERROR, 0, _("acl_to_text error on file \"%s\": ERR=%s\n"),
186          jcr->last_fname, be.strerror());
187       Dmsg3(100, "acl_to_text error acl=%s file=%s ERR=%s\n",  
188          jcr->acl_text, jcr->last_fname, be.strerror());
189       acl_free(acl);
190 #ifndef HAVE_OSF1_OS          /* BACL_ENOTSUP not defined for OSF1 */
191    } else if (errno == BACL_ENOTSUP) {
192       /* Not supported, just pretend there is nothing to see */
193       return pm_strcpy(jcr->acl_text, "");
194 #endif
195    }
196    /***** Do we really want to silently ignore errors from acl_get_file
197      and acl_to_text?  *****/
198    return 0;
199 }
200
201 int bacl_set(JCR *jcr, int acltype)
202 {
203    acl_t acl;
204    acl_type_t ostype;
205
206    ostype = (acltype & BACL_TYPE_DEFAULT) ? ACL_TYPE_DEFAULT : ACL_TYPE_ACCESS;
207
208    /* If we get empty default ACLs, clear ACLs now */
209    if (ostype == ACL_TYPE_DEFAULT && strlen(jcr->acl_text) == 0) {
210       if (acl_delete_def_file(jcr->last_fname) == 0) {
211          return 0;
212       }
213       berrno be;
214       Jmsg2(jcr, M_ERROR, 0, _("acl_delete_def_file error on file \"%s\": ERR=%s\n"),
215          jcr->last_fname, be.strerror());
216       return -1;
217    }
218
219    acl = acl_from_text(jcr->acl_text);
220    if (acl == NULL) {
221       berrno be;
222       Jmsg2(jcr, M_ERROR, 0, _("acl_from_text error on file \"%s\": ERR=%s\n"),
223          jcr->last_fname, be.strerror());
224       Dmsg3(100, "acl_from_text error acl=%s file=%s ERR=%s\n",  
225          jcr->acl_text, jcr->last_fname, be.strerror());
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       berrno be;
236       Jmsg2(jcr, M_ERROR, 0, _("ac_valid error on file \"%s\": ERR=%s\n"),
237          jcr->last_fname, be.strerror());
238       Dmsg3(100, "acl_valid error acl=%s file=%s ERR=%s\n",  
239          jcr->acl_text, jcr->last_fname, be.strerror());
240       acl_free(acl);
241       return -1;
242    }
243 #endif
244
245    /*
246     * Restore the ACLs, but don't complain about links which really should
247     * not have attributes, and the file it is linked to may not yet be restored.
248     */
249    if (acl_set_file(jcr->last_fname, ostype, acl) != 0 && jcr->last_type != FT_LNK) {
250       berrno be;
251       Jmsg2(jcr, M_ERROR, 0, _("acl_set_file error on file \"%s\": ERR=%s\n"),
252          jcr->last_fname, be.strerror());
253       Dmsg3(100, "acl_set_file error acl=%s file=%s ERR=%s\n",  
254          jcr->acl_text, jcr->last_fname, be.strerror());
255       acl_free(acl);
256       return -1;
257    }
258    acl_free(acl);
259    return 0;
260 }
261
262 #elif defined(HAVE_HPUX_OS)
263 #include <sys/acl.h>
264 #include <acllib.h>
265
266 int bacl_get(JCR *jcr, int acltype)
267 {
268    int n, len;
269    struct acl_entry acls[NACLENTRIES];
270    char *acl_text;
271
272    if ((n = getacl(jcr->last_fname, 0, acls)) <= 0) {
273       if (errno == BACL_ENOTSUP) {
274          return pm_strcpy(jcr->acl_text, "");
275       }
276       return -1;
277    }
278    if ((n = getacl(jcr->last_fname, n, acls)) > 0) {
279       if ((acl_text = acltostr(n, acls, FORM_SHORT)) != NULL) {
280          len = pm_strcpy(jcr->acl_text, acl_text);
281          actuallyfree(acl_text);
282          return len;
283       }
284       berrno be;
285       Jmsg2(jcr, M_ERROR, 0, _("acltostr error on file \"%s\": ERR=%s\n"),
286          jcr->last_fname, be.strerror());
287       Dmsg3(100, "acltostr error acl=%s file=%s ERR=%s\n",  
288          jcr->acl_text, jcr->last_fname, be.strerror());
289       return -1;
290    }
291    return -1;
292 }
293
294 int bacl_set(JCR *jcr, int acltype)
295 {
296    int n, stat;
297    struct acl_entry acls[NACLENTRIES];
298
299    n = strtoacl(jcr->acl_text, 0, NACLENTRIES, acls, ACL_FILEOWNER, ACL_FILEGROUP);
300    if (n <= 0) {
301       berrno be;
302       Jmsg2(jcr, M_ERROR, 0, _("strtoacl error on file \"%s\": ERR=%s\n"),
303          jcr->last_fname, be.strerror());
304       Dmsg3(100, "strtoacl error acl=%s file=%s ERR=%s\n",  
305          jcr->acl_text, jcr->last_fname, be.strerror());
306       return -1;
307    }
308    if (strtoacl(jcr->acl_text, n, NACLENTRIES, acls, ACL_FILEOWNER, ACL_FILEGROUP) != n) {
309       berrno be;
310       Jmsg2(jcr, M_ERROR, 0, _("strtoacl error on file \"%s\": ERR=%s\n"),
311          jcr->last_fname, be.strerror());
312       Dmsg3(100, "strtoacl error acl=%s file=%s ERR=%s\n",  
313          jcr->acl_text, jcr->last_fname, be.strerror());
314       return -1;
315    }
316    /*
317     * Restore the ACLs, but don't complain about links which really should
318     * not have attributes, and the file it is linked to may not yet be restored.
319     */
320    if (setacl(jcr->last_fname, n, acls) != 0 && jcr->last_type != FT_LNK) {
321       berrno be;
322       Jmsg2(jcr, M_ERROR, 0, _("setacl error on file \"%s\": ERR=%s\n"),
323          jcr->last_fname, be.strerror());
324       Dmsg3(100, "setacl error acl=%s file=%s ERR=%s\n",  
325          jcr->acl_text, jcr->last_fname, be.strerror());
326       return -1;
327    }
328    return 0;
329 }
330
331 #elif defined(HAVE_SUN_OS)
332 #include <sys/acl.h>
333
334 int bacl_get(JCR *jcr, int acltype)
335 {
336    int n, len;
337    aclent_t *acls;
338    char *acl_text;
339
340    n = acl(jcr->last_fname, GETACLCNT, 0, NULL);
341    if (n < MIN_ACL_ENTRIES) {
342       return -1;
343    } else if (n == MIN_ACL_ENTRIES) {
344       /* The ACLs simply reflect the (already known) standard permissions */
345       return pm_strcpy(jcr->acl_text, "");
346    }
347    if ((acls = (aclent_t *)malloc(n * sizeof(aclent_t))) == NULL) {
348       return -1;
349    }
350    if (acl(jcr->last_fname, GETACL, n, acls) == n) {
351       if ((acl_text = acltotext(acls, n)) != NULL) {
352          len = pm_strcpy(jcr->acl_text, acl_text);
353          actuallyfree(acl_text);
354          free(acls);
355          return len;
356       }
357       berrno be;
358       Jmsg2(jcr, M_ERROR, 0, _("acltotext error on file \"%s\": ERR=%s\n"),
359          jcr->last_fname, be.strerror());
360       Dmsg3(100, "acltotext error acl=%s file=%s ERR=%s\n",  
361          jcr->acl_text, jcr->last_fname, be.strerror());
362    }
363    free(acls);
364    return -1;
365 }
366
367 int bacl_set(JCR *jcr, int acltype)
368 {
369    int n;
370    aclent_t *acls;
371
372    acls = aclfromtext(jcr->acl_text, &n);
373    if (!acls) {
374       berrno be;
375       Jmsg2(jcr, M_ERROR, 0, _("aclfromtext error on file \"%s\": ERR=%s\n"),
376          jcr->last_fname, be.strerror());
377       Dmsg3(100, "aclfromtext error acl=%s file=%s ERR=%s\n",  
378          jcr->acl_text, jcr->last_fname, be.strerror());
379       return -1;
380    }
381    /*
382     * Restore the ACLs, but don't complain about links which really should
383     * not have attributes, and the file it is linked to may not yet be restored.
384     */
385    if (acl(jcr->last_fname, SETACL, n, acls) == -1 && jcr->last_type != FT_LNK) {
386       berrno be;
387       Jmsg2(jcr, M_ERROR, 0, _("acl(SETACL) error on file \"%s\": ERR=%s\n"),
388          jcr->last_fname, be.strerror());
389       Dmsg3(100, "acl(SETACL) error acl=%s file=%s ERR=%s\n",  
390          jcr->acl_text, jcr->last_fname, be.strerror());
391       actuallyfree(acls);
392       return -1;
393    }
394    actuallyfree(acls);
395    return 0;
396 }
397
398 #endif
399
400
401 #ifdef TEST_PROGRAM
402
403 /*
404  * Test program setup 
405  *
406  * Compile and set up with eg. with eg.
407  *
408  *    $ cc -DTEST_PROGRAM -DHAVE_SUN_OS -lsec -o acl acl.c
409  *    $ ln -s acl aclcp
410  *
411  * You can then list ACLs with acl and copy them with aclcp.
412  *
413  * For a list of compiler flags, see the list preceding the big #if below.
414  */
415 #include <errno.h>
416 #include <stdio.h>
417 #include <stdlib.h>
418 #include <string.h>
419 #include <sys/stat.h>
420 #include "acl.h"
421
422 #define BACLLEN 65535
423 #define pm_strcpy(d,s)     (strncpy(d, s, BACLLEN - 1) == NULL ? -1 : (int)strlen(d))
424 #define Dmsg0(n,s)         fprintf(stderr, s)
425 #define Dmsg1(n,s,a1)      fprintf(stderr, s, a1)
426 #define Dmsg2(n,s,a1,a2)   fprintf(stderr, s, a1, a2)
427
428 int aclls(char *fname);
429 int aclcp(char *src, char *dst);
430
431 struct JCRstruct {
432    char *last_fname;
433    char acl_text[BACLLEN];
434 };
435 typedef struct JCRstruct JCR;
436 JCR jcr;
437
438 int main(int argc, char **argv)
439 {
440    char *prgname;
441    int status = 0;
442
443    if (argc < 1) {
444       Dmsg0(200, "Cannot determine my own name\n");
445       return EXIT_FAILURE;
446    }
447
448    prgname = last_path_separator(argv[0]);
449    if (prgname == NULL || *++prgname == '\0') {
450       prgname = argv[0];
451    }
452    --argc;
453    ++argv;
454
455    /* aclcp "copies" ACLs - set ACLs on destination equal to ACLs on source */
456    if (strcmp(prgname, "aclcp") == 0) {
457       int verbose = 0;
458       if (strcmp(*argv, "-v") == 0) {
459          ++verbose;
460          --argc;
461          ++argv;
462       }
463       if (argc != 2) {
464          Dmsg2(200, "%s: wrong number of arguments\n"
465                "usage:\t%s [-v] source destination\n"
466                "\tCopies ACLs from source to destination.\n"
467                "\tSpecify -v to show ACLs after copy for verification.\n",
468                prgname, prgname);
469          return EXIT_FAILURE;
470       }
471       if (strcmp(argv[0], argv[1]) == 0) {
472          Dmsg2(200, "%s: identical source and destination.\n"
473                "usage:\t%s [-v] source destination\n"
474                "\tCopies ACLs from source to destination.\n"
475                "\tSpecify -v to show ACLs after copy for verification.\n",
476                prgname, prgname);
477          return EXIT_FAILURE;
478       }
479       if (verbose) {
480          aclls(argv[0]);
481       }
482       status = aclcp(argv[0], argv[1]);
483       if (verbose && status == 0) {
484          aclls(argv[1]);
485       }
486       return status;
487    }
488
489    /* Default: just list ACLs */
490    if (argc < 1) {
491       Dmsg2(200, "%s: missing arguments\n"
492             "usage:\t%s file ...\n"
493             "\tLists ACLs of specified files or directories.\n",
494             prgname, prgname);
495       return EXIT_FAILURE;
496    }
497    while (argc--) {
498       if (!aclls(*argv++)) {
499          status = EXIT_FAILURE;
500       }
501    }
502
503    return status;
504 }
505
506 /**** Test program *****/
507 int aclcp(char *src, char *dst)
508 {
509    struct stat st;
510
511    if (lstat(dst, &st) != 0) {
512       Dmsg0(200, "aclcp: destination does not exist\n");
513       return EXIT_FAILURE;
514    }
515    if (S_ISLNK(st.st_mode)) {
516       Dmsg0(200, "aclcp: cannot set ACL on symlinks\n");
517       return EXIT_FAILURE;
518    }
519    if (lstat(src, &st) != 0) {
520       Dmsg0(200, "aclcp: source does not exist\n");
521       return EXIT_FAILURE;
522    }
523    if (S_ISLNK(st.st_mode)) {
524       Dmsg0(200, "aclcp: will not read ACL from symlinks\n");
525       return EXIT_FAILURE;
526    }
527
528    jcr.last_fname = src;
529    if (bacl_get(&jcr, BACL_TYPE_ACCESS) < 0) {
530       Dmsg1(200, "aclcp: could not read ACLs for %s\n", jcr.last_fname);
531       return EXIT_FAILURE;
532    } else {
533       jcr.last_fname = dst;
534       if (bacl_set(&jcr, BACL_TYPE_ACCESS) < 0) {
535          Dmsg1(200, "aclcp: could not set ACLs on %s\n", jcr.last_fname);
536          return EXIT_FAILURE;
537       }
538    }
539
540    if (S_ISDIR(st.st_mode) && (BACL_CAP & BACL_CAP_DEFAULTS_DIR)) {
541       jcr.last_fname = src;
542       if (bacl_get(&jcr, BACL_TYPE_DEFAULT) < 0) {
543          Dmsg1(200, "aclcp: could not read default ACLs for %s\n", jcr.last_fname);
544          return EXIT_FAILURE;
545       } else {
546          jcr.last_fname = dst;
547          if (bacl_set(&jcr, BACL_TYPE_DEFAULT) < 0) {
548             Dmsg1(200, "aclcp: could not set default ACLs on %s\n", jcr.last_fname);
549             return EXIT_FAILURE;
550          }
551       }
552    }
553
554    return 0;
555 }
556
557 /**** Test program *****/
558 int aclls(char *fname)
559 {
560    struct stat st;
561    int len;
562
563    if (lstat(fname, &st) != 0) {
564       Dmsg0(200, "acl: source does not exist\n");
565       return EXIT_FAILURE;
566    }
567    if (S_ISLNK(st.st_mode)) {
568       Dmsg0(200, "acl: will not read ACL from symlinks\n");
569       return EXIT_FAILURE;
570    }
571
572    jcr.last_fname = fname;
573
574    len = bacl_get(&jcr, BACL_TYPE_ACCESS);
575    if (len < 0) {
576       Dmsg1(200, "acl: could not read ACLs for %s\n", jcr.last_fname);
577       return EXIT_FAILURE;
578    } else if (len == 0) {
579       printf("#file: %s [standard permissions - or unsupported]\n\n", jcr.last_fname);
580    } else {
581       printf("#file: %s\n%s\n", jcr.last_fname, jcr.acl_text);
582    }
583
584    if (S_ISDIR(st.st_mode) && (BACL_CAP & BACL_CAP_DEFAULTS_DIR)) {
585       len = bacl_get(&jcr, BACL_TYPE_DEFAULT);
586       if (len < 0) {
587          Dmsg1(200, "acl: could not read default ACLs for %s\n", jcr.last_fname);
588          return EXIT_FAILURE;
589       } else if (len == 0) {
590          printf("#file: %s [default, none - or unsupported]\n\n", jcr.last_fname);
591       } else {
592          printf("#file: %s [default]\n%s\n", jcr.last_fname, jcr.acl_text);
593       }
594    }
595
596    return 0;
597 }
598 #endif