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