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_solaris.h"
31 
32 #if defined(HAVE_SUN_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_SOLARIS_POSIX,
42    STREAM_XACL_SOLARIS_NFS4,
43    0
44 };
45 
46 static const int os_default_acl_streams[] = {
47    0
48 };
49 
50 /*
51  * OS specific constructor
52  */
BACL_Solaris()53 BACL_Solaris::BACL_Solaris(){
54 
55    set_acl_streams(os_acl_streams, os_default_acl_streams);
56    cache = NULL;
57 };
58 
59 /*
60  * OS specific destructor
61  */
~BACL_Solaris()62 BACL_Solaris::~BACL_Solaris(){};
63 
64 /*
65  * Checks if ACL's are available for a specified file
66  *
67  * in:
68  *    jcr - Job Control Record
69  *    name - specifies the system variable to be queried
70  * out:
71  *    bRC_BACL_ok - check successful, lets setup bacltype variable
72  *    bRC_BACL_error -  in case of error
73  *    bRC_BACL_skip - you should skip all other routine
74  */
check_bacltype(JCR * jcr,int name)75 bRC_BACL BACL_Solaris::check_bacltype (JCR *jcr, int name){
76 
77    int rc = 0;
78 
79    rc = pathconf(jcr->last_fname, name);
80    switch (rc){
81       case -1: {
82          /* some error check why */
83          berrno be;
84          if (errno == ENOENT){
85             /* file does not exist skip it */
86             return bRC_BACL_skip;
87          } else {
88             Mmsg2(jcr->errmsg, _("pathconf error on file \"%s\": ERR=%s\n"), jcr->last_fname, be.bstrerror());
89             Dmsg2(100, "pathconf error file=%s ERR=%s\n", jcr->last_fname, be.bstrerror());
90             return bRC_BACL_error;
91          }
92       }
93       case 0:
94          /* No support for ACLs */
95          clear_flag(BACL_FLAG_NATIVE);
96          set_content(NULL);
97          return bRC_BACL_skip;
98       default:
99          break;
100    }
101    return bRC_BACL_ok;
102 };
103 
104 /*
105  * Perform OS specific ACL backup
106  *
107  * in/out - check API at bacl.h
108  */
os_backup_acl(JCR * jcr,FF_PKT * ff_pkt)109 bRC_BACL BACL_Solaris::os_backup_acl (JCR *jcr, FF_PKT *ff_pkt){
110 
111    bRC_BACL rc;
112    int stream;
113 
114    /*
115     * See if filesystem supports acls.
116     */
117    rc = check_bacltype(jcr, _PC_ACL_ENABLED);
118    switch (rc){
119       case bRC_BACL_ok:
120          break;
121       case bRC_BACL_skip:
122          return bRC_BACL_ok;
123       default:
124          /* errors */
125          return rc;
126    }
127 
128    rc = os_get_acl(jcr, &stream);
129    switch (rc){
130       case bRC_BACL_ok:
131          if (get_content_len() > 0){
132             if (send_acl_stream(jcr, stream) == bRC_BACL_fatal){
133                return bRC_BACL_fatal;
134             }
135          }
136          break;
137       default:
138          return rc;
139    }
140 
141    return bRC_BACL_ok;
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_Solaris::os_restore_acl (JCR *jcr, int stream, char *content, uint32_t length){
150 
151    int aclrc = 0;
152 
153    switch (stream){
154       case STREAM_UNIX_ACCESS_ACL:
155       case STREAM_XACL_SOLARIS_POSIX:
156       case STREAM_XACL_SOLARIS_NFS4:
157          aclrc = pathconf(jcr->last_fname, _PC_ACL_ENABLED);
158          break;
159       default:
160          return bRC_BACL_error;
161    }
162 
163    switch (aclrc){
164       case -1: {
165          berrno be;
166 
167          switch (errno){
168             case ENOENT:
169                return bRC_BACL_ok;
170             default:
171                Mmsg2(jcr->errmsg, _("pathconf error on file \"%s\": ERR=%s\n"), jcr->last_fname, be.bstrerror());
172                Dmsg3(100, "pathconf error acl=%s file=%s ERR=%s\n", content, jcr->last_fname, be.bstrerror());
173                return bRC_BACL_error;
174          }
175       }
176       case 0:
177          clear_flag(BACL_FLAG_NATIVE);
178          Mmsg(jcr->errmsg, _("Trying to restore acl on file \"%s\" on filesystem without acl support\n"), jcr->last_fname);
179          return bRC_BACL_error;
180       default:
181          break;
182    }
183 
184    Dmsg2(400, "restore acl stream %i on file: %s\n", stream, jcr->last_fname);
185    switch (stream){
186       case STREAM_XACL_SOLARIS_POSIX:
187          if ((aclrc & (_ACL_ACLENT_ENABLED | _ACL_ACE_ENABLED)) == 0){
188             Mmsg(jcr->errmsg, _("Trying to restore POSIX acl on file \"%s\" on filesystem without aclent acl support\n"), jcr->last_fname);
189             return bRC_BACL_error;
190          }
191          break;
192       case STREAM_XACL_SOLARIS_NFS4:
193          if ((aclrc & _ACL_ACE_ENABLED) == 0){
194             Mmsg(jcr->errmsg, _("Trying to restore NFSv4 acl on file \"%s\" on filesystem without ace acl support\n"), jcr->last_fname);
195             return bRC_BACL_error;
196          }
197          break;
198       default:
199          break;
200    }
201 
202    return os_set_acl(jcr, stream, content, length);
203 };
204 
205 /*
206  * Low level OS specific runtime to get ACL data from file. The ACL data is set in internal content buffer
207  *
208  * in/out - check API at bacl.h
209  */
os_get_acl(JCR * jcr,int * stream)210 bRC_BACL BACL_Solaris::os_get_acl(JCR *jcr, int *stream){
211 
212    int flags;
213    acl_t *aclp;
214    char *acl_text;
215    bRC_BACL rc = bRC_BACL_ok;
216 
217    if (!stream){
218       return bRC_BACL_fatal;
219    }
220 
221    if (acl_get(jcr->last_fname, ACL_NO_TRIVIAL, &aclp) != 0){
222       /* we've got some error */
223       berrno be;
224       switch (errno){
225       case ENOENT:
226          /* file does not exist */
227          return bRC_BACL_ok;
228       default:
229          Mmsg2(jcr->errmsg, _("acl_get error on file \"%s\": ERR=%s\n"), jcr->last_fname, acl_strerror(errno));
230          Dmsg2(100, "acl_get error file=%s ERR=%s\n", jcr->last_fname, acl_strerror(errno));
231          return bRC_BACL_error;
232       }
233    }
234 
235    if (!aclp){
236       /*
237        * The ACLs simply reflect the (already known) standard permissions
238        * So we don't send an ACL stream to the SD.
239        */
240       set_content(NULL);
241       return bRC_BACL_ok;
242    }
243 
244 #if defined(ACL_SID_FMT)
245    /* new format flag added in newer Solaris versions */
246    flags = ACL_APPEND_ID | ACL_COMPACT_FMT | ACL_SID_FMT;
247 #else
248    flags = ACL_APPEND_ID | ACL_COMPACT_FMT;
249 #endif /* ACL_SID_FMT */
250 
251    if ((acl_text = acl_totext(aclp, flags)) != NULL){
252       set_content(acl_text);
253       switch (acl_type(aclp)){
254          case ACLENT_T:
255             *stream = STREAM_XACL_SOLARIS_POSIX;
256             Dmsg1(500, "found acl SOLARIS_POSIX: %s\n", acl_text);
257             break;
258          case ACE_T:
259             *stream = STREAM_XACL_SOLARIS_NFS4;
260             Dmsg1(500, "found acl SOLARIS_NFS4: %s\n", acl_text);
261             break;
262          default:
263             rc = bRC_BACL_error;
264             break;
265       }
266       actuallyfree(acl_text);
267       acl_free(aclp);
268    }
269    return rc;
270 };
271 
272 /*
273  * Low level OS specific runtime to set ACL data on file
274  *
275  * in/out - check API at bacl.h
276  */
os_set_acl(JCR * jcr,int stream,char * content,uint32_t length)277 bRC_BACL BACL_Solaris::os_set_acl(JCR *jcr, int stream, char *content, uint32_t length){
278 
279    int rc;
280    acl_t *aclp;
281 
282    if ((rc = acl_fromtext(content, &aclp)) != 0){
283       Mmsg2(jcr->errmsg, _("acl_fromtext error on file \"%s\": ERR=%s\n"), jcr->last_fname, acl_strerror(rc));
284       Dmsg3(100, "acl_fromtext error acl=%s file=%s ERR=%s\n", content, jcr->last_fname, acl_strerror(rc));
285       return bRC_BACL_error;
286    }
287 
288    switch (stream){
289       case STREAM_XACL_SOLARIS_POSIX:
290          if (acl_type(aclp) != ACLENT_T){
291             Mmsg(jcr->errmsg, _("wrong encoding of acl type in acl stream on file \"%s\"\n"), jcr->last_fname);
292             return bRC_BACL_error;
293          }
294          break;
295       case STREAM_XACL_SOLARIS_NFS4:
296          if (acl_type(aclp) != ACE_T){
297             Mmsg(jcr->errmsg, _("wrong encoding of acl type in acl stream on file \"%s\"\n"), jcr->last_fname);
298             return bRC_BACL_error;
299          }
300          break;
301       default:
302          break;
303    }
304 
305    if ((rc = acl_set(jcr->last_fname, aclp)) == -1 && jcr->last_type != FT_LNK){
306       switch (errno){
307          case ENOENT:
308             acl_free(aclp);
309             return bRC_BACL_ok;
310          default:
311             Mmsg2(jcr->errmsg, _("acl_set error on file \"%s\": ERR=%s\n"), jcr->last_fname, acl_strerror(rc));
312             Dmsg3(100, "acl_set error acl=%s file=%s ERR=%s\n", content, jcr->last_fname, acl_strerror(rc));
313             acl_free(aclp);
314             return bRC_BACL_error;
315       }
316    }
317 
318    acl_free(aclp);
319    return bRC_BACL_ok;
320 };
321 
322 #endif /* HAVE_ACL */
323 
324 #endif /* HAVE_SUN_OS */
325