]> git.sur5r.net Git - bacula/bacula/blob - bacula/src/filed/acl.c
kes Generally clean up the manual tape loading code. The main
[bacula/bacula] / bacula / src / filed / acl.c
1 /*
2    Bacula® - The Network Backup Solution
3
4    Copyright (C) 2004-2008 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)     (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 defined(HAVE_IRIX_OS)
180       /* 
181        * From observation, IRIX's acl_get_file() seems to return a
182        * non-NULL acl with a count field of -1 when a file has no ACL
183        * defined, while IRIX's acl_to_text() returns NULL when presented
184        * with such an ACL. 
185        *
186        * Checking the count in the acl structure before calling
187        * acl_to_text() lets us avoid error messages about files
188        * with no ACLs, without modifying the flow of the code used for 
189        * other operating systems, and it saves making some calls
190        * to acl_to_text() besides.
191        */
192       if (acl->acl_cnt <= 0) {
193         acl_free(acl);
194          return 0;
195       }
196 #endif
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       berrno be;
204       Jmsg2(jcr, M_ERROR, 0, _("acl_to_text error on file \"%s\": ERR=%s\n"),
205          jcr->last_fname, be.bstrerror());
206       Dmsg3(100, "acl_to_text error acl=%s file=%s ERR=%s\n",  
207          jcr->acl_text, jcr->last_fname, be.bstrerror());
208       acl_free(acl);
209 #ifndef HAVE_OSF1_OS          /* BACL_ENOTSUP not defined for OSF1 */
210    } else if (errno == BACL_ENOTSUP) {
211       /* Not supported, just pretend there is nothing to see */
212       return pm_strcpy(jcr->acl_text, "");
213 #endif
214    }
215    /***** Do we really want to silently ignore errors from acl_get_file
216      and acl_to_text?  *****/
217    return 0;
218 }
219
220 int bacl_set(JCR *jcr, int acltype)
221 {
222    acl_t acl;
223    acl_type_t ostype;
224
225    ostype = (acltype & BACL_TYPE_DEFAULT) ? ACL_TYPE_DEFAULT : ACL_TYPE_ACCESS;
226
227    /* If we get empty default ACLs, clear ACLs now */
228    if (ostype == ACL_TYPE_DEFAULT && strlen(jcr->acl_text) == 0) {
229       if (acl_delete_def_file(jcr->last_fname) == 0) {
230          return 0;
231       }
232       berrno be;
233       Jmsg2(jcr, M_ERROR, 0, _("acl_delete_def_file error on file \"%s\": ERR=%s\n"),
234          jcr->last_fname, be.bstrerror());
235       return -1;
236    }
237
238    acl = acl_from_text(jcr->acl_text);
239    if (acl == NULL) {
240       berrno be;
241       Jmsg2(jcr, M_ERROR, 0, _("acl_from_text error on file \"%s\": ERR=%s\n"),
242          jcr->last_fname, be.bstrerror());
243       Dmsg3(100, "acl_from_text error acl=%s file=%s ERR=%s\n",  
244          jcr->acl_text, jcr->last_fname, be.bstrerror());
245       return -1;
246    }
247
248    /*
249     * FreeBSD always fails acl_valid() - at least on valid input...
250     * As it does the right thing, given valid input, just ignore acl_valid().
251     */
252 #ifndef HAVE_FREEBSD_OS
253    if (acl_valid(acl) != 0) {
254       berrno be;
255       Jmsg2(jcr, M_ERROR, 0, _("ac_valid error on file \"%s\": ERR=%s\n"),
256          jcr->last_fname, be.bstrerror());
257       Dmsg3(100, "acl_valid error acl=%s file=%s ERR=%s\n",  
258          jcr->acl_text, jcr->last_fname, be.bstrerror());
259       acl_free(acl);
260       return -1;
261    }
262 #endif
263
264    /*
265     * Restore the ACLs, but don't complain about links which really should
266     * not have attributes, and the file it is linked to may not yet be restored.
267     */
268    if (acl_set_file(jcr->last_fname, ostype, acl) != 0 && jcr->last_type != FT_LNK) {
269       berrno be;
270       Jmsg2(jcr, M_ERROR, 0, _("acl_set_file error on file \"%s\": ERR=%s\n"),
271          jcr->last_fname, be.bstrerror());
272       Dmsg3(100, "acl_set_file error acl=%s file=%s ERR=%s\n",  
273          jcr->acl_text, jcr->last_fname, be.bstrerror());
274       acl_free(acl);
275       return -1;
276    }
277    acl_free(acl);
278    return 0;
279 }
280
281 #elif defined(HAVE_HPUX_OS)
282 #include <sys/acl.h>
283 #include <acllib.h>
284
285 int bacl_get(JCR *jcr, int acltype)
286 {
287    int n, len;
288    struct acl_entry acls[NACLENTRIES];
289    char *acl_text;
290
291    if ((n = getacl(jcr->last_fname, 0, acls)) <= 0) {
292       if (errno == BACL_ENOTSUP) {
293          return pm_strcpy(jcr->acl_text, "");
294       }
295       return -1;
296    }
297    if ((n = getacl(jcr->last_fname, n, acls)) > 0) {
298       if ((acl_text = acltostr(n, acls, FORM_SHORT)) != NULL) {
299          len = pm_strcpy(jcr->acl_text, acl_text);
300          actuallyfree(acl_text);
301          return len;
302       }
303       berrno be;
304       Jmsg2(jcr, M_ERROR, 0, _("acltostr error on file \"%s\": ERR=%s\n"),
305          jcr->last_fname, be.bstrerror());
306       Dmsg3(100, "acltostr error acl=%s file=%s ERR=%s\n",  
307          jcr->acl_text, jcr->last_fname, be.bstrerror());
308       return -1;
309    }
310    return -1;
311 }
312
313 int bacl_set(JCR *jcr, int acltype)
314 {
315    int n, stat;
316    struct acl_entry acls[NACLENTRIES];
317
318    n = strtoacl(jcr->acl_text, 0, NACLENTRIES, acls, ACL_FILEOWNER, ACL_FILEGROUP);
319    if (n <= 0) {
320       berrno be;
321       Jmsg2(jcr, M_ERROR, 0, _("strtoacl error on file \"%s\": ERR=%s\n"),
322          jcr->last_fname, be.bstrerror());
323       Dmsg3(100, "strtoacl error acl=%s file=%s ERR=%s\n",  
324          jcr->acl_text, jcr->last_fname, be.bstrerror());
325       return -1;
326    }
327    if (strtoacl(jcr->acl_text, n, NACLENTRIES, acls, ACL_FILEOWNER, ACL_FILEGROUP) != n) {
328       berrno be;
329       Jmsg2(jcr, M_ERROR, 0, _("strtoacl error on file \"%s\": ERR=%s\n"),
330          jcr->last_fname, be.bstrerror());
331       Dmsg3(100, "strtoacl error acl=%s file=%s ERR=%s\n",  
332          jcr->acl_text, jcr->last_fname, be.bstrerror());
333       return -1;
334    }
335    /*
336     * Restore the ACLs, but don't complain about links which really should
337     * not have attributes, and the file it is linked to may not yet be restored.
338     */
339    if (setacl(jcr->last_fname, n, acls) != 0 && jcr->last_type != FT_LNK) {
340       berrno be;
341       Jmsg2(jcr, M_ERROR, 0, _("setacl error on file \"%s\": ERR=%s\n"),
342          jcr->last_fname, be.bstrerror());
343       Dmsg3(100, "setacl error acl=%s file=%s ERR=%s\n",  
344          jcr->acl_text, jcr->last_fname, be.bstrerror());
345       return -1;
346    }
347    return 0;
348 }
349
350 #elif defined(HAVE_SUN_OS)
351 #include <sys/acl.h>
352
353 int bacl_get(JCR *jcr, int acltype)
354 {
355    int n, len;
356    aclent_t *acls;
357    char *acl_text;
358
359    n = acl(jcr->last_fname, GETACLCNT, 0, NULL);
360    if (n < MIN_ACL_ENTRIES) {
361       return -1;
362    } else if (n == MIN_ACL_ENTRIES) {
363       /* The ACLs simply reflect the (already known) standard permissions */
364       return pm_strcpy(jcr->acl_text, "");
365    }
366    if ((acls = (aclent_t *)malloc(n * sizeof(aclent_t))) == NULL) {
367       return -1;
368    }
369    if (acl(jcr->last_fname, GETACL, n, acls) == n) {
370       if ((acl_text = acltotext(acls, n)) != NULL) {
371          len = pm_strcpy(jcr->acl_text, acl_text);
372          actuallyfree(acl_text);
373          free(acls);
374          return len;
375       }
376       berrno be;
377       Jmsg2(jcr, M_ERROR, 0, _("acltotext error on file \"%s\": ERR=%s\n"),
378          jcr->last_fname, be.bstrerror());
379       Dmsg3(100, "acltotext error acl=%s file=%s ERR=%s\n",  
380          jcr->acl_text, jcr->last_fname, be.bstrerror());
381    }
382    free(acls);
383    return -1;
384 }
385
386 int bacl_set(JCR *jcr, int acltype)
387 {
388    int n;
389    aclent_t *acls;
390
391    acls = aclfromtext(jcr->acl_text, &n);
392    if (!acls) {
393       berrno be;
394       Jmsg2(jcr, M_ERROR, 0, _("aclfromtext error on file \"%s\": ERR=%s\n"),
395          jcr->last_fname, be.bstrerror());
396       Dmsg3(100, "aclfromtext error acl=%s file=%s ERR=%s\n",  
397          jcr->acl_text, jcr->last_fname, be.bstrerror());
398       return -1;
399    }
400    /*
401     * Restore the ACLs, but don't complain about links which really should
402     * not have attributes, and the file it is linked to may not yet be restored.
403     */
404    if (acl(jcr->last_fname, SETACL, n, acls) == -1 && jcr->last_type != FT_LNK) {
405       berrno be;
406       Jmsg2(jcr, M_ERROR, 0, _("acl(SETACL) error on file \"%s\": ERR=%s\n"),
407          jcr->last_fname, be.bstrerror());
408       Dmsg3(100, "acl(SETACL) error acl=%s file=%s ERR=%s\n",  
409          jcr->acl_text, jcr->last_fname, be.bstrerror());
410       actuallyfree(acls);
411       return -1;
412    }
413    actuallyfree(acls);
414    return 0;
415 }
416
417 #endif
418
419
420 #ifdef TEST_PROGRAM
421
422 /*
423  * Test program setup 
424  *
425  * Compile and set up with eg. with eg.
426  *
427  *    $ cc -DTEST_PROGRAM -DHAVE_SUN_OS -lsec -o acl acl.c
428  *    $ ln -s acl aclcp
429  *
430  * You can then list ACLs with acl and copy them with aclcp.
431  *
432  * For a list of compiler flags, see the list preceding the big #if below.
433  */
434 #include <errno.h>
435 #include <stdio.h>
436 #include <stdlib.h>
437 #include <string.h>
438 #include <sys/stat.h>
439 #include "acl.h"
440
441 #define BACLLEN 65535
442 #define pm_strcpy(d,s)     (strncpy(d, s, BACLLEN - 1) == NULL ? -1 : (int)strlen(d))
443 #define Dmsg0(n,s)         fprintf(stderr, s)
444 #define Dmsg1(n,s,a1)      fprintf(stderr, s, a1)
445 #define Dmsg2(n,s,a1,a2)   fprintf(stderr, s, a1, a2)
446
447 int aclls(char *fname);
448 int aclcp(char *src, char *dst);
449
450 struct JCRstruct {
451    char *last_fname;
452    char acl_text[BACLLEN];
453 };
454 typedef struct JCRstruct JCR;
455 JCR jcr;
456
457 int main(int argc, char **argv)
458 {
459    char *prgname;
460    int status = 0;
461
462    if (argc < 1) {
463       Dmsg0(200, "Cannot determine my own name\n");
464       return EXIT_FAILURE;
465    }
466
467    prgname = last_path_separator(argv[0]);
468    if (prgname == NULL || *++prgname == '\0') {
469       prgname = argv[0];
470    }
471    --argc;
472    ++argv;
473
474    /* aclcp "copies" ACLs - set ACLs on destination equal to ACLs on source */
475    if (strcmp(prgname, "aclcp") == 0) {
476       int verbose = 0;
477       if (strcmp(*argv, "-v") == 0) {
478          ++verbose;
479          --argc;
480          ++argv;
481       }
482       if (argc != 2) {
483          Dmsg2(200, "%s: wrong number of arguments\n"
484                "usage:\t%s [-v] source destination\n"
485                "\tCopies ACLs from source to destination.\n"
486                "\tSpecify -v to show ACLs after copy for verification.\n",
487                prgname, prgname);
488          return EXIT_FAILURE;
489       }
490       if (strcmp(argv[0], argv[1]) == 0) {
491          Dmsg2(200, "%s: identical source and destination.\n"
492                "usage:\t%s [-v] source destination\n"
493                "\tCopies ACLs from source to destination.\n"
494                "\tSpecify -v to show ACLs after copy for verification.\n",
495                prgname, prgname);
496          return EXIT_FAILURE;
497       }
498       if (verbose) {
499          aclls(argv[0]);
500       }
501       status = aclcp(argv[0], argv[1]);
502       if (verbose && status == 0) {
503          aclls(argv[1]);
504       }
505       return status;
506    }
507
508    /* Default: just list ACLs */
509    if (argc < 1) {
510       Dmsg2(200, "%s: missing arguments\n"
511             "usage:\t%s file ...\n"
512             "\tLists ACLs of specified files or directories.\n",
513             prgname, prgname);
514       return EXIT_FAILURE;
515    }
516    while (argc--) {
517       if (!aclls(*argv++)) {
518          status = EXIT_FAILURE;
519       }
520    }
521
522    return status;
523 }
524
525 /**** Test program *****/
526 int aclcp(char *src, char *dst)
527 {
528    struct stat st;
529
530    if (lstat(dst, &st) != 0) {
531       Dmsg0(200, "aclcp: destination does not exist\n");
532       return EXIT_FAILURE;
533    }
534    if (S_ISLNK(st.st_mode)) {
535       Dmsg0(200, "aclcp: cannot set ACL on symlinks\n");
536       return EXIT_FAILURE;
537    }
538    if (lstat(src, &st) != 0) {
539       Dmsg0(200, "aclcp: source does not exist\n");
540       return EXIT_FAILURE;
541    }
542    if (S_ISLNK(st.st_mode)) {
543       Dmsg0(200, "aclcp: will not read ACL from symlinks\n");
544       return EXIT_FAILURE;
545    }
546
547    jcr.last_fname = src;
548    if (bacl_get(&jcr, BACL_TYPE_ACCESS) < 0) {
549       Dmsg1(200, "aclcp: could not read ACLs for %s\n", jcr.last_fname);
550       return EXIT_FAILURE;
551    } else {
552       jcr.last_fname = dst;
553       if (bacl_set(&jcr, BACL_TYPE_ACCESS) < 0) {
554          Dmsg1(200, "aclcp: could not set ACLs on %s\n", jcr.last_fname);
555          return EXIT_FAILURE;
556       }
557    }
558
559    if (S_ISDIR(st.st_mode) && (BACL_CAP & BACL_CAP_DEFAULTS_DIR)) {
560       jcr.last_fname = src;
561       if (bacl_get(&jcr, BACL_TYPE_DEFAULT) < 0) {
562          Dmsg1(200, "aclcp: could not read default ACLs for %s\n", jcr.last_fname);
563          return EXIT_FAILURE;
564       } else {
565          jcr.last_fname = dst;
566          if (bacl_set(&jcr, BACL_TYPE_DEFAULT) < 0) {
567             Dmsg1(200, "aclcp: could not set default ACLs on %s\n", jcr.last_fname);
568             return EXIT_FAILURE;
569          }
570       }
571    }
572
573    return 0;
574 }
575
576 /**** Test program *****/
577 int aclls(char *fname)
578 {
579    struct stat st;
580    int len;
581
582    if (lstat(fname, &st) != 0) {
583       Dmsg0(200, "acl: source does not exist\n");
584       return EXIT_FAILURE;
585    }
586    if (S_ISLNK(st.st_mode)) {
587       Dmsg0(200, "acl: will not read ACL from symlinks\n");
588       return EXIT_FAILURE;
589    }
590
591    jcr.last_fname = fname;
592
593    len = bacl_get(&jcr, BACL_TYPE_ACCESS);
594    if (len < 0) {
595       Dmsg1(200, "acl: could not read ACLs for %s\n", jcr.last_fname);
596       return EXIT_FAILURE;
597    } else if (len == 0) {
598       printf("#file: %s [standard permissions - or unsupported]\n\n", jcr.last_fname);
599    } else {
600       printf("#file: %s\n%s\n", jcr.last_fname, jcr.acl_text);
601    }
602
603    if (S_ISDIR(st.st_mode) && (BACL_CAP & BACL_CAP_DEFAULTS_DIR)) {
604       len = bacl_get(&jcr, BACL_TYPE_DEFAULT);
605       if (len < 0) {
606          Dmsg1(200, "acl: could not read default ACLs for %s\n", jcr.last_fname);
607          return EXIT_FAILURE;
608       } else if (len == 0) {
609          printf("#file: %s [default, none - or unsupported]\n\n", jcr.last_fname);
610       } else {
611          printf("#file: %s [default]\n%s\n", jcr.last_fname, jcr.acl_text);
612       }
613    }
614
615    return 0;
616 }
617 #endif