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