vfs_posixacl: support pathref fd's in posixacl_sys_acl_set_fd()
[samba.git] / source3 / modules / vfs_posixacl.c
1 /*
2    Unix SMB/Netbios implementation.
3    VFS module to get and set posix acls
4    Copyright (C) Volker Lendecke 2006
5
6    This program is free software; you can redistribute it and/or modify
7    it under the terms of the GNU General Public License as published by
8    the Free Software Foundation; either version 3 of the License, or
9    (at your option) any later version.
10
11    This program is distributed in the hope that it will be useful,
12    but WITHOUT ANY WARRANTY; without even the implied warranty of
13    MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
14    GNU General Public License for more details.
15
16    You should have received a copy of the GNU General Public License
17    along with this program.  If not, see <http://www.gnu.org/licenses/>.
18 */
19
20 #include "includes.h"
21 #include "system/filesys.h"
22 #include "smbd/smbd.h"
23 #include "modules/vfs_posixacl.h"
24
25 /* prototypes for static functions first - for clarity */
26
27 static bool smb_ace_to_internal(acl_entry_t posix_ace,
28                                 struct smb_acl_entry *ace);
29 static struct smb_acl_t *smb_acl_to_internal(acl_t acl, TALLOC_CTX *mem_ctx);
30 static int smb_acl_set_mode(acl_entry_t entry, SMB_ACL_PERM_T perm);
31 static acl_t smb_acl_to_posix(const struct smb_acl_t *acl);
32
33
34 /* public functions - the api */
35
36 SMB_ACL_T posixacl_sys_acl_get_file(vfs_handle_struct *handle,
37                                 const struct smb_filename *smb_fname,
38                                 SMB_ACL_TYPE_T type,
39                                 TALLOC_CTX *mem_ctx)
40 {
41         struct smb_acl_t *result;
42         acl_type_t acl_type;
43         acl_t acl;
44
45         switch(type) {
46         case SMB_ACL_TYPE_ACCESS:
47                 acl_type = ACL_TYPE_ACCESS;
48                 break;
49         case SMB_ACL_TYPE_DEFAULT:
50                 acl_type = ACL_TYPE_DEFAULT;
51                 break;
52         default:
53                 errno = EINVAL;
54                 return NULL;
55         }
56
57         acl = acl_get_file(smb_fname->base_name, acl_type);
58
59         if (acl == NULL) {
60                 return NULL;
61         }
62
63         result = smb_acl_to_internal(acl, mem_ctx);
64         acl_free(acl);
65         return result;
66 }
67
68 SMB_ACL_T posixacl_sys_acl_get_fd(vfs_handle_struct *handle,
69                                   files_struct *fsp, TALLOC_CTX *mem_ctx)
70 {
71         struct smb_acl_t *result;
72         acl_t acl = NULL;
73
74         if (!fsp->fsp_flags.is_pathref) {
75                 acl = acl_get_fd(fsp_get_io_fd(fsp));
76         } else if (fsp->fsp_flags.have_proc_fds) {
77                 int fd = fsp_get_pathref_fd(fsp);
78                 const char *proc_fd_path = NULL;
79                 char buf[PATH_MAX];
80
81                 proc_fd_path = sys_proc_fd_path(fd, buf, sizeof(buf));
82                 if (proc_fd_path == NULL) {
83                         return NULL;
84                 }
85
86                 acl = acl_get_file(proc_fd_path, ACL_TYPE_ACCESS);
87         } else {
88                 /*
89                  * This is no longer a handle based call.
90                  */
91                 acl = acl_get_file(fsp->fsp_name->base_name, ACL_TYPE_ACCESS);
92         }
93         if (acl == NULL) {
94                 return NULL;
95         }
96
97         result = smb_acl_to_internal(acl, mem_ctx);
98         acl_free(acl);
99         return result;
100 }
101
102 int posixacl_sys_acl_set_file(vfs_handle_struct *handle,
103                               const struct smb_filename *smb_fname,
104                               SMB_ACL_TYPE_T type,
105                               SMB_ACL_T theacl)
106 {
107         int res;
108         acl_type_t acl_type;
109         acl_t acl;
110
111         DEBUG(10, ("Calling acl_set_file: %s, %d\n",
112                         smb_fname->base_name,
113                         type));
114
115         switch(type) {
116         case SMB_ACL_TYPE_ACCESS:
117                 acl_type = ACL_TYPE_ACCESS;
118                 break;
119         case SMB_ACL_TYPE_DEFAULT:
120                 acl_type = ACL_TYPE_DEFAULT;
121                 break;
122         default:
123                 errno = EINVAL;
124                 return -1;
125         }
126
127         if ((acl = smb_acl_to_posix(theacl)) == NULL) {
128                 return -1;
129         }
130         res = acl_set_file(smb_fname->base_name, acl_type, acl);
131         if (res != 0) {
132                 DEBUG(10, ("acl_set_file failed: %s\n", strerror(errno)));
133         }
134         acl_free(acl);
135         return res;
136 }
137
138 int posixacl_sys_acl_set_fd(vfs_handle_struct *handle,
139                             files_struct *fsp,
140                             SMB_ACL_T theacl)
141 {
142         int res;
143         acl_t acl = smb_acl_to_posix(theacl);
144         int fd = fsp_get_pathref_fd(fsp);
145
146         if (acl == NULL) {
147                 return -1;
148         }
149
150         if (!fsp->fsp_flags.is_pathref) {
151                 res = acl_set_fd(fd, acl);
152         } else if (fsp->fsp_flags.have_proc_fds) {
153                 const char *proc_fd_path = NULL;
154                 char buf[PATH_MAX];
155
156                 proc_fd_path = sys_proc_fd_path(fd, buf, sizeof(buf));
157                 if (proc_fd_path == NULL) {
158                         return -1;
159                 }
160                 res = acl_set_file(proc_fd_path, ACL_TYPE_ACCESS, acl);
161         } else {
162                 /*
163                  * This is no longer a handle based call.
164                  */
165                 res = acl_set_file(fsp->fsp_name->base_name,
166                                    ACL_TYPE_ACCESS,
167                                    acl);
168         }
169
170         acl_free(acl);
171         return res;
172 }
173
174 int posixacl_sys_acl_delete_def_file(vfs_handle_struct *handle,
175                                 const struct smb_filename *smb_fname)
176 {
177         return acl_delete_def_file(smb_fname->base_name);
178 }
179
180
181 /* private functions */
182
183 static bool smb_ace_to_internal(acl_entry_t posix_ace,
184                                 struct smb_acl_entry *ace)
185 {
186         acl_tag_t tag;
187         acl_permset_t permset;
188
189         if (acl_get_tag_type(posix_ace, &tag) != 0) {
190                 DEBUG(0, ("smb_acl_get_tag_type failed\n"));
191                 return False;
192         }
193
194         switch(tag) {
195         case ACL_USER:
196                 ace->a_type = SMB_ACL_USER;
197                 break;
198         case ACL_USER_OBJ:
199                 ace->a_type = SMB_ACL_USER_OBJ;
200                 break;
201         case ACL_GROUP:
202                 ace->a_type = SMB_ACL_GROUP;
203                 break;
204         case ACL_GROUP_OBJ:
205                 ace->a_type = SMB_ACL_GROUP_OBJ;
206                 break;
207         case ACL_OTHER:
208                 ace->a_type = SMB_ACL_OTHER;
209                 break;
210         case ACL_MASK:
211                 ace->a_type = SMB_ACL_MASK;
212                 break;
213 #ifdef HAVE_ACL_EVERYONE
214         case ACL_EVERYONE:
215                 DEBUG(1, ("ACL tag type ACL_EVERYONE. FreeBSD with ZFS? Use 'vfs objects = zfsacl'\n"));
216                 return false;
217 #endif
218         default:
219                 DEBUG(0, ("unknown tag type %d\n", (unsigned int)tag));
220                 return False;
221         }
222         switch(ace->a_type) {
223         case SMB_ACL_USER: {
224                 uid_t *puid = (uid_t *)acl_get_qualifier(posix_ace);
225                 if (puid == NULL) {
226                         DEBUG(0, ("smb_acl_get_qualifier failed\n"));
227                         return False;
228                 }
229                 ace->info.user.uid = *puid;
230                 acl_free(puid);
231                 break;
232         }
233
234         case SMB_ACL_GROUP: {
235                 gid_t *pgid = (uid_t *)acl_get_qualifier(posix_ace);
236                 if (pgid == NULL) {
237                         DEBUG(0, ("smb_acl_get_qualifier failed\n"));
238                         return False;
239                 }
240                 ace->info.group.gid = *pgid;
241                 acl_free(pgid);
242                 break;
243         }
244         default:
245                 break;
246         }
247         if (acl_get_permset(posix_ace, &permset) != 0) {
248                 DEBUG(0, ("smb_acl_get_mode failed\n"));
249                 return False;
250         }
251         ace->a_perm = 0;
252 #ifdef HAVE_ACL_GET_PERM_NP
253         ace->a_perm |= (acl_get_perm_np(permset, ACL_READ) ? SMB_ACL_READ : 0);
254         ace->a_perm |= (acl_get_perm_np(permset, ACL_WRITE) ? SMB_ACL_WRITE : 0);
255         ace->a_perm |= (acl_get_perm_np(permset, ACL_EXECUTE) ? SMB_ACL_EXECUTE : 0);
256 #else
257         ace->a_perm |= (acl_get_perm(permset, ACL_READ) ? SMB_ACL_READ : 0);
258         ace->a_perm |= (acl_get_perm(permset, ACL_WRITE) ? SMB_ACL_WRITE : 0);
259         ace->a_perm |= (acl_get_perm(permset, ACL_EXECUTE) ? SMB_ACL_EXECUTE : 0);
260 #endif
261         return True;
262 }
263
264 static struct smb_acl_t *smb_acl_to_internal(acl_t acl, TALLOC_CTX *mem_ctx)
265 {
266         struct smb_acl_t *result = sys_acl_init(mem_ctx);
267         int entry_id = ACL_FIRST_ENTRY;
268         acl_entry_t e;
269         if (result == NULL) {
270                 return NULL;
271         }
272         while (acl_get_entry(acl, entry_id, &e) == 1) {
273
274                 entry_id = ACL_NEXT_ENTRY;
275
276                 result->acl = talloc_realloc(result, result->acl,
277                                              struct smb_acl_entry, result->count+1);
278                 if (result->acl == NULL) {
279                         TALLOC_FREE(result);
280                         DEBUG(0, ("talloc_realloc failed\n"));
281                         errno = ENOMEM;
282                         return NULL;
283                 }
284
285                 if (!smb_ace_to_internal(e, &result->acl[result->count])) {
286                         TALLOC_FREE(result);
287                         return NULL;
288                 }
289
290                 result->count += 1;
291         }
292         return result;
293 }
294
295 static int smb_acl_set_mode(acl_entry_t entry, SMB_ACL_PERM_T perm)
296 {
297         int ret;
298         acl_permset_t permset;
299
300         if ((ret = acl_get_permset(entry, &permset)) != 0) {
301                 return ret;
302         }
303         if ((ret = acl_clear_perms(permset)) != 0) {
304                 return ret;
305         }
306         if ((perm & SMB_ACL_READ) &&
307             ((ret = acl_add_perm(permset, ACL_READ)) != 0)) {
308                 return ret;
309         }
310         if ((perm & SMB_ACL_WRITE) &&
311             ((ret = acl_add_perm(permset, ACL_WRITE)) != 0)) {
312                 return ret;
313         }
314         if ((perm & SMB_ACL_EXECUTE) &&
315             ((ret = acl_add_perm(permset, ACL_EXECUTE)) != 0)) {
316                 return ret;
317         }
318
319         return 0;
320 }
321
322 static acl_t smb_acl_to_posix(const struct smb_acl_t *acl)
323 {
324         acl_t result;
325         int i;
326
327         result = acl_init(acl->count);
328         if (result == NULL) {
329                 DEBUG(10, ("acl_init failed\n"));
330                 return NULL;
331         }
332
333         for (i=0; i<acl->count; i++) {
334                 const struct smb_acl_entry *entry = &acl->acl[i];
335                 acl_entry_t e;
336                 acl_tag_t tag;
337
338                 if (acl_create_entry(&result, &e) != 0) {
339                         DEBUG(1, ("acl_create_entry failed: %s\n",
340                                   strerror(errno)));
341                         goto fail;
342                 }
343
344                 switch (entry->a_type) {
345                 case SMB_ACL_USER:
346                         tag = ACL_USER;
347                         break;
348                 case SMB_ACL_USER_OBJ:
349                         tag = ACL_USER_OBJ;
350                         break;
351                 case SMB_ACL_GROUP:
352                         tag = ACL_GROUP;
353                         break;
354                 case SMB_ACL_GROUP_OBJ:
355                         tag = ACL_GROUP_OBJ;
356                         break;
357                 case SMB_ACL_OTHER:
358                         tag = ACL_OTHER;
359                         break;
360                 case SMB_ACL_MASK:
361                         tag = ACL_MASK;
362                         break;
363                 default:
364                         DEBUG(1, ("Unknown tag value %d\n", entry->a_type));
365                         goto fail;
366                 }
367
368                 if (acl_set_tag_type(e, tag) != 0) {
369                         DEBUG(10, ("acl_set_tag_type(%d) failed: %s\n",
370                                    tag, strerror(errno)));
371                         goto fail;
372                 }
373
374                 switch (entry->a_type) {
375                 case SMB_ACL_USER:
376                         if (acl_set_qualifier(e, &entry->info.user.uid) != 0) {
377                                 DEBUG(1, ("acl_set_qualifiier failed: %s\n",
378                                           strerror(errno)));
379                                 goto fail;
380                         }
381                         break;
382                 case SMB_ACL_GROUP:
383                         if (acl_set_qualifier(e, &entry->info.group.gid) != 0) {
384                                 DEBUG(1, ("acl_set_qualifiier failed: %s\n",
385                                           strerror(errno)));
386                                 goto fail;
387                         }
388                         break;
389                 default:        /* Shut up, compiler! :-) */
390                         break;
391                 }
392
393                 if (smb_acl_set_mode(e, entry->a_perm) != 0) {
394                         goto fail;
395                 }
396         }
397
398         if (acl_valid(result) != 0) {
399                 char *acl_string = sys_acl_to_text(acl, NULL);
400                 DEBUG(0, ("smb_acl_to_posix: ACL %s is invalid for set (%s)\n",
401                           acl_string, strerror(errno)));
402                 SAFE_FREE(acl_string);
403                 goto fail;
404         }
405
406         return result;
407
408  fail:
409         if (result != NULL) {
410                 acl_free(result);
411         }
412         return NULL;
413 }
414
415 /* VFS operations structure */
416
417 static struct vfs_fn_pointers posixacl_fns = {
418         .sys_acl_get_file_fn = posixacl_sys_acl_get_file,
419         .sys_acl_get_fd_fn = posixacl_sys_acl_get_fd,
420         .sys_acl_blob_get_file_fn = posix_sys_acl_blob_get_file,
421         .sys_acl_blob_get_fd_fn = posix_sys_acl_blob_get_fd,
422         .sys_acl_set_file_fn = posixacl_sys_acl_set_file,
423         .sys_acl_set_fd_fn = posixacl_sys_acl_set_fd,
424         .sys_acl_delete_def_file_fn = posixacl_sys_acl_delete_def_file,
425 };
426
427 static_decl_vfs;
428 NTSTATUS vfs_posixacl_init(TALLOC_CTX *ctx)
429 {
430         return smb_register_vfs(SMB_VFS_INTERFACE_VERSION, "posixacl",
431                                 &posixacl_fns);
432 }