1 /*
2    Bacula(R) - The Network Backup Solution
3 
4    Copyright (C) 2000-2020 Kern Sibbald
5 
6    The original author of Bacula is Kern Sibbald, with contributions
7    from many others, a complete list can be found in the file AUTHORS.
8 
9    You may use this file and others of this release according to the
10    license defined in the LICENSE file, which includes the Affero General
11    Public License, v3.0 ("AGPLv3") and some additional permissions and
12    terms pursuant to its AGPLv3 Section 7.
13 
14    This notice must be preserved when any source code is
15    conveyed and/or propagated.
16 
17    Bacula(R) is a registered trademark of Kern Sibbald.
18  */
19 /**
20  * Major refactoring of ACL code written by:
21  *
22  *  Radosław Korzeniewski, MMXVI
23  *  radoslaw@korzeniewski.net, radekk@inteos.pl
24  *  Inteos Sp. z o.o. http://www.inteos.pl/
25  *
26  */
27 
28 #include "bacula.h"
29 #include "filed.h"
30 #include "bacl_osx.h"
31 
32 #if defined(HAVE_DARWIN_OS)
33 
34 /* check if ACL support is enabled */
35 #if defined(HAVE_ACL)
36 
37 /*
38  * Define the supported ACL streams for this OS
39  */
40 static const int os_acl_streams[] = {
41    STREAM_XACL_DARWIN_ACCESS,
42    0
43 };
44 
45 static const int os_default_acl_streams[] = {
46    0
47 };
48 
49 /*
50  * OS specific constructor
51  */
BACL_OSX()52 BACL_OSX::BACL_OSX(){
53 
54    set_acl_streams(os_acl_streams, os_default_acl_streams);
55 };
56 
57 /*
58  * Translates Bacula internal acl representation into
59  * acl type
60  *
61  * in:
62  *    bacltype - internal Bacula acl type (BACL_type)
63  * out:
64  *    acl_type_t - os dependent acl type
65  *    when failed - ACL_TYPE_NONE is returned
66  */
get_acltype(BACL_type bacltype)67 acl_type_t BACL_OSX::get_acltype(BACL_type bacltype){
68 
69    acl_type_t acltype;
70 
71    switch (bacltype){
72       case BACL_TYPE_ACCESS:
73          acltype = ACL_TYPE_ACCESS;
74          break;
75    #ifdef HAVE_ACL_TYPE_EXTENDED
76       case BACL_TYPE_EXTENDED:
77          acltype = ACL_TYPE_EXTENDED;
78          break;
79    #endif
80       default:
81          /*
82           * sanity check for acl's not supported by OS
83           */
84          acltype = (acl_type_t)ACL_TYPE_NONE;
85          break;
86    }
87    return acltype;
88 };
89 
90 /*
91  * Counts a number of acl entries
92  *
93  * in:
94  *    acl - acl object
95  * out:
96  *    int - number of entries in acl object
97  *    when no acl entry available or any error then return zero '0'
98  */
acl_nrentries(acl_t acl)99 int BACL_OSX::acl_nrentries(acl_t acl){
100 
101    int nr = 0;
102    acl_entry_t entry;
103    int rc;
104 
105    rc = acl_get_entry(acl, ACL_FIRST_ENTRY, &entry);
106    while (rc == 0){
107       nr++;
108       rc = acl_get_entry(acl, ACL_NEXT_ENTRY, &entry);
109    }
110 
111    return nr;
112 };
113 
114 /*
115  * Perform OS specific ACL backup
116  *
117  * in/out - check API at bacl.h
118  */
os_backup_acl(JCR * jcr,FF_PKT * ff_pkt)119 bRC_BACL BACL_OSX::os_backup_acl (JCR *jcr, FF_PKT *ff_pkt){
120 
121    /* check input data */
122    if (jcr == NULL || ff_pkt == NULL){
123       return bRC_BACL_inval;
124    }
125 
126 #if defined(HAVE_ACL_TYPE_EXTENDED)
127    /*
128     * Use BACL_TYPE_EXTENDED only when available
129     */
130    Dmsg0(400, "MacOSX Extended ACL computed\n");
131    if (os_get_acl(jcr, BACL_TYPE_EXTENDED) == bRC_BACL_fatal){
132       return bRC_BACL_fatal;
133    }
134 #else
135    Dmsg0(400, "MacOSX standard ACL computed\n");
136    if (os_get_acl(jcr, BACL_TYPE_ACCESS) == bRC_BACL_fatal){
137       return bRC_BACL_fatal;
138    }
139 #endif
140 
141    return send_acl_stream(jcr, STREAM_XACL_DARWIN_ACCESS);
142 };
143 
144 /*
145  * Perform OS specific ACL restore
146  *
147  * in/out - check API at bacl.h
148  */
os_restore_acl(JCR * jcr,int stream,char * content,uint32_t length)149 bRC_BACL BACL_OSX::os_restore_acl (JCR *jcr, int stream, char *content, uint32_t length){
150 
151 #if defined(HAVE_ACL_TYPE_EXTENDED)
152       return os_set_acl(jcr, BACL_TYPE_EXTENDED, content, length);
153 #else
154       return os_set_acl(jcr, BACL_TYPE_ACCESS, content, length);
155 #endif
156 };
157 
158 /*
159  * Low level OS specific runtime to get ACL data from file. The ACL data is set in internal content buffer
160  *
161  * in/out - check API at bacl.h
162  */
os_get_acl(JCR * jcr,BACL_type bacltype)163 bRC_BACL BACL_OSX::os_get_acl(JCR *jcr, BACL_type bacltype){
164 
165    acl_t acl;
166    acl_type_t acltype;
167    char *acltext;
168    bRC_BACL rc = bRC_BACL_ok;
169 
170    /* check input data */
171    if (jcr == NULL){
172       return bRC_BACL_inval;
173    }
174 
175    acltype = get_acltype(bacltype);
176    acl = acl_get_file(jcr->last_fname, acltype);
177 
178    if (acl){
179       Dmsg1(400, "OS_ACL read from file: %s\n",jcr->last_fname);
180       if (acl_nrentries(acl) == 0){
181          goto bail_out;
182       }
183 
184       if ((acltext = acl_to_text(acl, NULL)) != NULL){
185          set_content(acltext);
186          acl_free(acl);
187          acl_free(acltext);
188          return bRC_BACL_ok;
189       }
190 
191       berrno be;
192 
193       Mmsg2(jcr->errmsg, _("acl_to_text error on file \"%s\": ERR=%s\n"), jcr->last_fname, be.bstrerror());
194       Dmsg2(100, "acl_to_text error file=%s ERR=%s\n", jcr->last_fname, be.bstrerror());
195 
196       rc = bRC_BACL_error;
197    } else {
198       berrno be;
199 
200       switch (errno){
201       case EOPNOTSUPP:
202          /* fs does not support acl, skip it */
203          Dmsg0(400, "Wow, ACL is not supported on this filesystem\n");
204          clear_flag(BACL_FLAG_NATIVE);
205          break;
206       case ENOENT:
207          break;
208       default:
209          /* Some real error */
210          Mmsg2(jcr->errmsg, _("acl_get_file error on file \"%s\": ERR=%s\n"), jcr->last_fname, be.bstrerror());
211          Dmsg2(100, "acl_get_file error file=%s ERR=%s\n", jcr->last_fname, be.bstrerror());
212          rc = bRC_BACL_error;
213          break;
214       }
215    }
216 
217 bail_out:
218    if (acl){
219       acl_free(acl);
220    }
221    /*
222     * it is a bit of hardcore to clear a poolmemory with a NULL pointer,
223     * but it is working, hehe :)
224     * you may ask why it is working? it is simple, a pm_strcpy function is handling
225     * a null pointer with a substitiution of empty string.
226     */
227    set_content(NULL);
228    return rc;
229 };
230 
231 /*
232  * Low level OS specific runtime to set ACL data on file
233  *
234  * in/out - check API at bacl.h
235  */
os_set_acl(JCR * jcr,BACL_type bacltype,char * content,uint32_t length)236 bRC_BACL BACL_OSX::os_set_acl(JCR *jcr, BACL_type bacltype, char *content, uint32_t length){
237 
238    acl_t acl;
239    acl_type_t acltype;
240 
241    /* check input data */
242    if (jcr == NULL || content == NULL){
243       return bRC_BACL_inval;
244    }
245 
246    acl = acl_from_text(content);
247    if (acl == NULL){
248       berrno be;
249 
250       Mmsg2(jcr->errmsg, _("acl_from_text error on file \"%s\": ERR=%s\n"), jcr->last_fname, be.bstrerror());
251       Dmsg3(100, "acl_from_text error acl=%s file=%s ERR=%s\n", content, jcr->last_fname, be.bstrerror());
252       return bRC_BACL_error;
253    }
254 
255    acltype = get_acltype(bacltype);
256 
257    /*
258     * Restore the ACLs, but don't complain about links which really should
259     * not have attributes, and the file it is linked to may not yet be restored.
260     * This is only true for the old acl streams as in the new implementation we
261     * don't save acls of symlinks (which cannot have acls anyhow)
262     */
263    if (acl_set_file(jcr->last_fname, acltype, acl) != 0 && jcr->last_type != FT_LNK){
264       berrno be;
265       switch (errno){
266       case ENOENT:
267          acl_free(acl);
268          return bRC_BACL_ok;
269       case EOPNOTSUPP:
270          /*
271           * If the filesystem reports it doesn't support ACLs we clear the
272           * BACL_FLAG_NATIVE flag so we skip ACL restores on all other files
273           * on the same filesystem. The BACL_FLAG_NATIVE flag gets set again
274           * when we change from one filesystem to an other.
275           */
276          clear_flag(BACL_FLAG_NATIVE);
277          Mmsg1(jcr->errmsg, _("acl_set_file error on file \"%s\": filesystem doesn't support ACLs\n"), jcr->last_fname);
278          Dmsg2(100, "acl_set_file error acl=%s file=%s filesystem doesn't support ACLs\n", content, jcr->last_fname);
279          acl_free(acl);
280          return bRC_BACL_error;
281       default:
282          Mmsg2(jcr->errmsg, _("acl_set_file error on file \"%s\": ERR=%s\n"), jcr->last_fname, be.bstrerror());
283          Dmsg3(100, "acl_set_file error acl=%s file=%s ERR=%s\n", content, jcr->last_fname, be.bstrerror());
284          acl_free(acl);
285          return bRC_BACL_error;
286       }
287    }
288    acl_free(acl);
289    return bRC_BACL_ok;
290 };
291 
292 #endif /* HAVE_ACL */
293 
294 #endif /* HAVE_DARWIN_OS */
295