]> git.sur5r.net Git - bacula/bacula/blob - bacula/src/filed/acl.c
Correct compile errors in 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       berrno be;
223       Jmsg2(jcr, M_ERROR, 0, _("acl_set_file error on file \"%s\": ERR=%s\n"),
224          jcr->last_fname, be.strerror());
225       Dmsg3(100, "acl_set_file error acl=%s file=%s ERR=%s\n",  
226          jcr->acl_text, jcr->last_fname, be.strerror());
227       acl_free(acl);
228       return -1;
229    }
230    acl_free(acl);
231    return 0;
232 }
233
234 #elif defined(HAVE_HPUX_OS)
235 #include <sys/acl.h>
236 #include <acllib.h>
237
238 int bacl_get(JCR *jcr, int acltype)
239 {
240    int n, len;
241    struct acl_entry acls[NACLENTRIES];
242    char *acl_text;
243
244    if ((n = getacl(jcr->last_fname, 0, acls)) <= 0) {
245       if (errno == BACL_ENOTSUP) {
246          return pm_strcpy(jcr->acl_text, "");
247       }
248       return -1;
249    }
250    if ((n = getacl(jcr->last_fname, n, acls)) > 0) {
251       if ((acl_text = acltostr(n, acls, FORM_SHORT)) != NULL) {
252          len = pm_strcpy(jcr->acl_text, acl_text);
253          actuallyfree(acl_text);
254          return len;
255       }
256    }
257    return -1;
258 }
259
260 int bacl_set(JCR *jcr, int acltype)
261 {
262    int n, stat;
263    struct acl_entry acls[NACLENTRIES];
264
265    n = strtoacl(jcr->acl_text, 0, NACLENTRIES, acls, ACL_FILEOWNER, ACL_FILEGROUP);
266    if (n <= 0) {
267       return -1;
268    }
269    if (strtoacl(jcr->acl_text, n, NACLENTRIES, acls, ACL_FILEOWNER, ACL_FILEGROUP) != n) {
270       return -1;
271    }
272    if (setacl(jcr->last_fname, n, acls) != 0) {
273       return -1;
274    }
275    return 0;
276 }
277
278 #elif defined(HAVE_SUN_OS)
279 #include <sys/acl.h>
280
281 int bacl_get(JCR *jcr, int acltype)
282 {
283    int n, len;
284    aclent_t *acls;
285    char *acl_text;
286
287    n = acl(jcr->last_fname, GETACLCNT, 0, NULL);
288    if (n < MIN_ACL_ENTRIES) {
289       return -1;
290    } else if (n == MIN_ACL_ENTRIES) {
291       /* The ACLs simply reflect the (already known) standard permissions */
292       return pm_strcpy(jcr->acl_text, "");
293    }
294    if ((acls = (aclent_t *)malloc(n * sizeof(aclent_t))) == NULL) {
295       return -1;
296    }
297    if (acl(jcr->last_fname, GETACL, n, acls) == n) {
298       if ((acl_text = acltotext(acls, n)) != NULL) {
299          len = pm_strcpy(jcr->acl_text, acl_text);
300          actuallyfree(acl_text);
301          free(acls);
302          return len;
303       }
304    }
305    free(acls);
306    return -1;
307 }
308
309 int bacl_set(JCR *jcr, int acltype)
310 {
311    int n;
312    aclent_t *acls;
313
314    acls = aclfromtext(jcr->acl_text, &n);
315    if (!acls) {
316       return -1;
317    }
318    if (acl(jcr->last_fname, SETACL, n, acls) == -1) {
319       actuallyfree(acls);
320       return -1;
321    }
322    actuallyfree(acls);
323    return 0;
324 }
325
326 #endif
327
328
329 #ifdef TEST_PROGRAM
330
331 /*
332  * Test program setup 
333  *
334  * Compile and set up with eg. with eg.
335  *
336  *    $ cc -DTEST_PROGRAM -DHAVE_SUN_OS -lsec -o acl acl.c
337  *    $ ln -s acl aclcp
338  *
339  * You can then list ACLs with acl and copy them with aclcp.
340  *
341  * For a list of compiler flags, see the list preceding the big #if below.
342  */
343 #include <errno.h>
344 #include <stdio.h>
345 #include <stdlib.h>
346 #include <string.h>
347 #include <sys/stat.h>
348 #include "acl.h"
349
350 #define BACLLEN 65535
351 #define pm_strcpy(d,s)     (strncpy(d, s, BACLLEN - 1) == NULL ? -1 : (int)strlen(d))
352 #define Dmsg0(n,s)         fprintf(stderr, s)
353 #define Dmsg1(n,s,a1)      fprintf(stderr, s, a1)
354 #define Dmsg2(n,s,a1,a2)   fprintf(stderr, s, a1, a2)
355
356 int aclls(char *fname);
357 int aclcp(char *src, char *dst);
358
359 struct JCRstruct {
360    char *last_fname;
361    char acl_text[BACLLEN];
362 };
363 typedef struct JCRstruct JCR;
364 JCR jcr;
365
366 int main(int argc, char **argv)
367 {
368    char *prgname;
369    int status = 0;
370
371    if (argc < 1) {
372       Dmsg0(200, "Cannot determine my own name\n");
373       return EXIT_FAILURE;
374    }
375
376    prgname = strrchr(argv[0], '/');
377    if (prgname == NULL || *++prgname == '\0') {
378       prgname = argv[0];
379    }
380    --argc;
381    ++argv;
382
383    /* aclcp "copies" ACLs - set ACLs on destination equal to ACLs on source */
384    if (strcmp(prgname, "aclcp") == 0) {
385       int verbose = 0;
386       if (strcmp(*argv, "-v") == 0) {
387          ++verbose;
388          --argc;
389          ++argv;
390       }
391       if (argc != 2) {
392          Dmsg2(200, "%s: wrong number of arguments\n"
393                "usage:\t%s [-v] source destination\n"
394                "\tCopies ACLs from source to destination.\n"
395                "\tSpecify -v to show ACLs after copy for verification.\n",
396                prgname, prgname);
397          return EXIT_FAILURE;
398       }
399       if (strcmp(argv[0], argv[1]) == 0) {
400          Dmsg2(200, "%s: identical source and destination.\n"
401                "usage:\t%s [-v] source destination\n"
402                "\tCopies ACLs from source to destination.\n"
403                "\tSpecify -v to show ACLs after copy for verification.\n",
404                prgname, prgname);
405          return EXIT_FAILURE;
406       }
407       if (verbose) {
408          aclls(argv[0]);
409       }
410       status = aclcp(argv[0], argv[1]);
411       if (verbose && status == 0) {
412          aclls(argv[1]);
413       }
414       return status;
415    }
416
417    /* Default: just list ACLs */
418    if (argc < 1) {
419       Dmsg2(200, "%s: missing arguments\n"
420             "usage:\t%s file ...\n"
421             "\tLists ACLs of specified files or directories.\n",
422             prgname, prgname);
423       return EXIT_FAILURE;
424    }
425    while (argc--) {
426       if (!aclls(*argv++)) {
427          status = EXIT_FAILURE;
428       }
429    }
430
431    return status;
432 }
433
434 /**** Test program *****/
435 int aclcp(char *src, char *dst)
436 {
437    struct stat st;
438
439    if (lstat(dst, &st) != 0) {
440       Dmsg0(200, "aclcp: destination does not exist\n");
441       return EXIT_FAILURE;
442    }
443    if (S_ISLNK(st.st_mode)) {
444       Dmsg0(200, "aclcp: cannot set ACL on symlinks\n");
445       return EXIT_FAILURE;
446    }
447    if (lstat(src, &st) != 0) {
448       Dmsg0(200, "aclcp: source does not exist\n");
449       return EXIT_FAILURE;
450    }
451    if (S_ISLNK(st.st_mode)) {
452       Dmsg0(200, "aclcp: will not read ACL from symlinks\n");
453       return EXIT_FAILURE;
454    }
455
456    jcr.last_fname = src;
457    if (bacl_get(&jcr, BACL_TYPE_ACCESS) < 0) {
458       Dmsg1(200, "aclcp: could not read ACLs for %s\n", jcr.last_fname);
459       return EXIT_FAILURE;
460    } else {
461       jcr.last_fname = dst;
462       if (bacl_set(&jcr, BACL_TYPE_ACCESS) < 0) {
463          Dmsg1(200, "aclcp: could not set ACLs on %s\n", jcr.last_fname);
464          return EXIT_FAILURE;
465       }
466    }
467
468    if (S_ISDIR(st.st_mode) && (BACL_CAP & BACL_CAP_DEFAULTS_DIR)) {
469       jcr.last_fname = src;
470       if (bacl_get(&jcr, BACL_TYPE_DEFAULT) < 0) {
471          Dmsg1(200, "aclcp: could not read default ACLs for %s\n", jcr.last_fname);
472          return EXIT_FAILURE;
473       } else {
474          jcr.last_fname = dst;
475          if (bacl_set(&jcr, BACL_TYPE_DEFAULT) < 0) {
476             Dmsg1(200, "aclcp: could not set default ACLs on %s\n", jcr.last_fname);
477             return EXIT_FAILURE;
478          }
479       }
480    }
481
482    return 0;
483 }
484
485 /**** Test program *****/
486 int aclls(char *fname)
487 {
488    struct stat st;
489    int len;
490
491    if (lstat(fname, &st) != 0) {
492       Dmsg0(200, "acl: source does not exist\n");
493       return EXIT_FAILURE;
494    }
495    if (S_ISLNK(st.st_mode)) {
496       Dmsg0(200, "acl: will not read ACL from symlinks\n");
497       return EXIT_FAILURE;
498    }
499
500    jcr.last_fname = fname;
501
502    len = bacl_get(&jcr, BACL_TYPE_ACCESS);
503    if (len < 0) {
504       Dmsg1(200, "acl: could not read ACLs for %s\n", jcr.last_fname);
505       return EXIT_FAILURE;
506    } else if (len == 0) {
507       printf("#file: %s [standard permissions - or unsupported]\n\n", jcr.last_fname);
508    } else {
509       printf("#file: %s\n%s\n", jcr.last_fname, jcr.acl_text);
510    }
511
512    if (S_ISDIR(st.st_mode) && (BACL_CAP & BACL_CAP_DEFAULTS_DIR)) {
513       len = bacl_get(&jcr, BACL_TYPE_DEFAULT);
514       if (len < 0) {
515          Dmsg1(200, "acl: could not read default ACLs for %s\n", jcr.last_fname);
516          return EXIT_FAILURE;
517       } else if (len == 0) {
518          printf("#file: %s [default, none - or unsupported]\n\n", jcr.last_fname);
519       } else {
520          printf("#file: %s [default]\n%s\n", jcr.last_fname, jcr.acl_text);
521       }
522    }
523
524    return 0;
525 }
526 #endif