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