vfs_fake_acls: deny give-ownership
[samba.git] / source3 / modules / vfs_fake_acls.c
1 /* 
2  * Fake ACLs VFS module.  Implements passthrough operation of all VFS
3  * calls to disk functions, except for file ownership and ACLs, which
4  * are stored in xattrs.
5  *
6  * Copyright (C) Tim Potter, 1999-2000
7  * Copyright (C) Alexander Bokovoy, 2002
8  * Copyright (C) Andrew Bartlett, 2002,2012
9  *
10  * This program is free software; you can redistribute it and/or modify
11  * it under the terms of the GNU General Public License as published by
12  * the Free Software Foundation; either version 3 of the License, or
13  * (at your option) any later version.
14  *  
15  * This program is distributed in the hope that it will be useful,
16  * but WITHOUT ANY WARRANTY; without even the implied warranty of
17  * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
18  * GNU General Public License for more details.
19  *  
20  * You should have received a copy of the GNU General Public License
21  * along with this program; if not, see <http://www.gnu.org/licenses/>.
22  */
23
24 #include "includes.h"
25 #include "smbd/smbd.h"
26 #include "system/filesys.h"
27 #include "auth.h"
28 #include "librpc/gen_ndr/ndr_smb_acl.h"
29
30 #undef DBGC_CLASS
31 #define DBGC_CLASS DBGC_VFS
32
33 #define FAKE_UID "system.fake_uid"
34 #define FAKE_GID "system.fake_gid"
35 #define FAKE_ACL_ACCESS_XATTR "system.fake_access_acl"
36 #define FAKE_ACL_DEFAULT_XATTR "system.fake_default_acl"
37
38 static int fake_acls_uid(vfs_handle_struct *handle,
39                          struct smb_filename *smb_fname,
40                          uid_t *uid)
41 {
42         ssize_t size;
43         uint8_t uid_buf[4];
44         size = SMB_VFS_NEXT_GETXATTR(handle, smb_fname,
45                         FAKE_UID, uid_buf, sizeof(uid_buf));
46         if (size == -1 && errno == ENOATTR) {
47                 return 0;
48         }
49         if (size != 4) {
50                 return -1;
51         }
52         *uid = IVAL(uid_buf, 0);
53         return 0;
54 }
55
56 static int fake_acls_gid(vfs_handle_struct *handle,
57                          struct smb_filename *smb_fname,
58                          uid_t *gid)
59 {
60         ssize_t size;
61         uint8_t gid_buf[4];
62
63         size = SMB_VFS_NEXT_GETXATTR(handle, smb_fname,
64                         FAKE_GID, gid_buf, sizeof(gid_buf));
65         if (size == -1 && errno == ENOATTR) {
66                 return 0;
67         }
68         if (size != 4) {
69                 return -1;
70         }
71         *gid = IVAL(gid_buf, 0);
72         return 0;
73 }
74
75 static int fake_acls_fuid(vfs_handle_struct *handle,
76                            files_struct *fsp,
77                            uid_t *uid)
78 {
79         ssize_t size;
80         uint8_t uid_buf[4];
81
82         size = SMB_VFS_NEXT_FGETXATTR(handle, fsp, FAKE_UID, uid_buf, sizeof(uid_buf));
83         if (size == -1 && errno == ENOATTR) {
84                 return 0;
85         }
86         if (size != 4) {
87                 return -1;
88         }
89         *uid = IVAL(uid_buf, 0);
90         return 0;
91 }
92
93 static int fake_acls_fgid(vfs_handle_struct *handle,
94                            files_struct *fsp,
95                           uid_t *gid)
96 {
97         ssize_t size;
98         uint8_t gid_buf[4];
99
100         size = SMB_VFS_NEXT_FGETXATTR(handle, fsp, FAKE_GID, gid_buf, sizeof(gid_buf));
101         if (size == -1 && errno == ENOATTR) {
102                 return 0;
103         }
104         if (size != 4) {
105                 return -1;
106         }
107         *gid = IVAL(gid_buf, 0);
108         return 0;
109 }
110
111 static int fake_acls_stat(vfs_handle_struct *handle,
112                            struct smb_filename *smb_fname)
113 {
114         int ret = -1;
115
116         ret = SMB_VFS_NEXT_STAT(handle, smb_fname);
117         if (ret == 0) {
118                 TALLOC_CTX *frame = talloc_stackframe();
119                 char *path;
120                 struct smb_filename smb_fname_base = {
121                         .base_name = smb_fname->base_name
122                 };
123                 NTSTATUS status;
124                 /*
125                  * As we're calling getxattr directly here
126                  * we need to use only the base_name, not
127                  * the full name containing any stream name.
128                  */
129                 status = get_full_smb_filename(frame, &smb_fname_base, &path);
130                 if (!NT_STATUS_IS_OK(status)) {
131                         errno = map_errno_from_nt_status(status);
132                         TALLOC_FREE(frame);
133                         return -1;
134                 }
135                 
136                 ret = fake_acls_uid(handle, &smb_fname_base,
137                                         &smb_fname->st.st_ex_uid);
138                 if (ret != 0) {
139                         TALLOC_FREE(frame);
140                         return ret;
141                 }
142                 ret = fake_acls_gid(handle, &smb_fname_base,
143                                         &smb_fname->st.st_ex_gid);
144                 if (ret != 0) {
145                         TALLOC_FREE(frame);
146                         return ret;
147                 }
148                 TALLOC_FREE(frame);
149         }
150
151         return ret;
152 }
153
154 static int fake_acls_lstat(vfs_handle_struct *handle,
155                            struct smb_filename *smb_fname)
156 {
157         int ret = -1;
158
159         ret = SMB_VFS_NEXT_LSTAT(handle, smb_fname);
160         if (ret == 0) {
161                 TALLOC_CTX *frame = talloc_stackframe();
162                 char *path;
163                 struct smb_filename smb_fname_base = {
164                         .base_name = smb_fname->base_name
165                 };
166                 NTSTATUS status;
167                 /*
168                  * As we're calling getxattr directly here
169                  * we need to use only the base_name, not
170                  * the full name containing any stream name.
171                  */
172                 status = get_full_smb_filename(frame, &smb_fname_base, &path);
173                 if (!NT_STATUS_IS_OK(status)) {
174                         errno = map_errno_from_nt_status(status);
175                         TALLOC_FREE(frame);
176                         return -1;
177                 }
178
179                 /* This isn't quite right (calling getxattr not
180                  * lgetxattr), but for the test purposes of this
181                  * module (fake NT ACLs from windows clients), it is
182                  * close enough.  We removed the l*xattr functions
183                  * because linux doesn't support using them, but we
184                  * could fake them in xattr_tdb if we really wanted
185                  * to.  We ignore errors because the link might not point anywhere */
186                 fake_acls_uid(handle, &smb_fname_base,
187                         &smb_fname->st.st_ex_uid);
188                 fake_acls_gid(handle, &smb_fname_base,
189                         &smb_fname->st.st_ex_gid);
190                 TALLOC_FREE(frame);
191         }
192
193         return ret;
194 }
195
196 static int fake_acls_fstat(vfs_handle_struct *handle, files_struct *fsp, SMB_STRUCT_STAT *sbuf)
197 {
198         int ret = -1;
199
200         ret = SMB_VFS_NEXT_FSTAT(handle, fsp, sbuf);
201         if (ret == 0) {
202                 ret = fake_acls_fuid(handle, fsp, &sbuf->st_ex_uid);
203                 if (ret != 0) {
204                         return ret;
205                 }
206                 ret = fake_acls_fgid(handle, fsp, &sbuf->st_ex_gid);
207                 if (ret != 0) {
208                         return ret;
209                 }
210         }
211         return ret;
212 }
213
214 static SMB_ACL_T fake_acls_blob2acl(DATA_BLOB *blob, TALLOC_CTX *mem_ctx)
215 {
216         enum ndr_err_code ndr_err;
217         struct smb_acl_t *acl = talloc(mem_ctx, struct smb_acl_t);
218         if (!acl) {
219                 errno = ENOMEM;
220                 return NULL;
221         }
222
223         ndr_err = ndr_pull_struct_blob(blob, acl, acl, 
224                 (ndr_pull_flags_fn_t)ndr_pull_smb_acl_t);
225
226         if (!NDR_ERR_CODE_IS_SUCCESS(ndr_err)) {
227                 DEBUG(0, ("ndr_pull_acl_t failed: %s\n",
228                           ndr_errstr(ndr_err)));
229                 TALLOC_FREE(acl);
230                 return NULL;
231         }
232         return acl;
233 }
234
235 static DATA_BLOB fake_acls_acl2blob(TALLOC_CTX *mem_ctx, SMB_ACL_T acl)
236 {
237         enum ndr_err_code ndr_err;
238         DATA_BLOB blob;
239         ndr_err = ndr_push_struct_blob(&blob, mem_ctx, acl, 
240                 (ndr_push_flags_fn_t)ndr_push_smb_acl_t);
241
242         if (!NDR_ERR_CODE_IS_SUCCESS(ndr_err)) {
243                 DEBUG(0, ("ndr_push_acl_t failed: %s\n",
244                           ndr_errstr(ndr_err)));
245                 return data_blob_null;
246         }
247         return blob;
248 }
249
250 static SMB_ACL_T fake_acls_sys_acl_get_file(struct vfs_handle_struct *handle,
251                                 const struct smb_filename *smb_fname,
252                                 SMB_ACL_TYPE_T type,
253                                 TALLOC_CTX *mem_ctx)
254 {
255         DATA_BLOB blob = data_blob_null;
256         ssize_t length;
257         const char *name = NULL;
258         struct smb_acl_t *acl = NULL;
259         TALLOC_CTX *frame = talloc_stackframe();
260         switch (type) {
261         case SMB_ACL_TYPE_ACCESS:
262                 name = FAKE_ACL_ACCESS_XATTR;
263                 break;
264         case SMB_ACL_TYPE_DEFAULT:
265                 name = FAKE_ACL_DEFAULT_XATTR;
266                 break;
267         }
268
269         do {
270                 blob.length += 1000;
271                 blob.data = talloc_realloc(frame, blob.data, uint8_t, blob.length);
272                 if (!blob.data) {
273                         errno = ENOMEM;
274                         TALLOC_FREE(frame);
275                         return NULL;
276                 }
277                 length = SMB_VFS_NEXT_GETXATTR(handle, smb_fname,
278                                 name, blob.data, blob.length);
279                 blob.length = length;
280         } while (length == -1 && errno == ERANGE);
281         if (length == -1 && errno == ENOATTR) {
282                 TALLOC_FREE(frame);
283                 return NULL;
284         }
285         if (length != -1) {
286                 acl = fake_acls_blob2acl(&blob, mem_ctx);
287         }
288         TALLOC_FREE(frame);
289         return acl;
290 }
291
292 static SMB_ACL_T fake_acls_sys_acl_get_fd(struct vfs_handle_struct *handle,
293                                           files_struct *fsp,
294                                           TALLOC_CTX *mem_ctx)
295 {
296         DATA_BLOB blob = data_blob_null;
297         ssize_t length;
298         const char *name = FAKE_ACL_ACCESS_XATTR;
299         struct smb_acl_t *acl = NULL;
300         TALLOC_CTX *frame = talloc_stackframe();
301                 
302         do {
303                 blob.length += 1000;
304                 blob.data = talloc_realloc(frame, blob.data, uint8_t, blob.length);
305                 if (!blob.data) {
306                         errno = ENOMEM;
307                         TALLOC_FREE(frame);
308                         return NULL;
309                 }
310                 length = SMB_VFS_NEXT_FGETXATTR(handle, fsp, name, blob.data, blob.length);
311                 blob.length = length;
312         } while (length == -1 && errno == ERANGE);
313         if (length == -1 && errno == ENOATTR) {
314                 TALLOC_FREE(frame);
315                 return NULL;
316         }
317         if (length != -1) {
318                 acl = fake_acls_blob2acl(&blob, mem_ctx);
319         }
320         TALLOC_FREE(frame);
321         return acl;
322 }
323
324
325 static int fake_acls_sys_acl_set_file(vfs_handle_struct *handle,
326                         const struct smb_filename *smb_fname,
327                         SMB_ACL_TYPE_T acltype,
328                         SMB_ACL_T theacl)
329 {
330         int ret;
331         const char *name = NULL;
332         TALLOC_CTX *frame = talloc_stackframe();
333         DATA_BLOB blob = fake_acls_acl2blob(frame, theacl);
334         if (!blob.data) {
335                 DEBUG(0, ("Failed to convert ACL to linear blob for xattr storage\n"));
336                 TALLOC_FREE(frame);
337                 errno = EINVAL;
338                 return -1;
339         }
340         switch (acltype) {
341         case SMB_ACL_TYPE_ACCESS:
342                 name = FAKE_ACL_ACCESS_XATTR;
343                 break;
344         case SMB_ACL_TYPE_DEFAULT:
345                 name = FAKE_ACL_DEFAULT_XATTR;
346                 break;
347         }
348         ret = SMB_VFS_NEXT_SETXATTR(handle, smb_fname,
349                         name, blob.data, blob.length, 0);
350         TALLOC_FREE(frame);
351         return ret;
352 }
353
354 static int fake_acls_sys_acl_set_fd(vfs_handle_struct *handle, files_struct *fsp, SMB_ACL_T theacl)
355 {
356         int ret;
357         const char *name = FAKE_ACL_ACCESS_XATTR;
358         TALLOC_CTX *frame = talloc_stackframe();
359         DATA_BLOB blob = fake_acls_acl2blob(frame, theacl);
360         if (!blob.data) {
361                 DEBUG(0, ("Failed to convert ACL to linear blob for xattr storage\n"));
362                 TALLOC_FREE(frame);
363                 errno = EINVAL;
364                 return -1;
365         }
366         ret = SMB_VFS_NEXT_FSETXATTR(handle, fsp, name, blob.data, blob.length, 0);
367         TALLOC_FREE(frame);
368         return ret;
369 }
370
371 static int fake_acls_sys_acl_delete_def_file(vfs_handle_struct *handle,
372                         const struct smb_filename *smb_fname_in)
373 {
374         int ret;
375         const char *name = FAKE_ACL_DEFAULT_XATTR;
376         TALLOC_CTX *frame = talloc_stackframe();
377         struct smb_filename *smb_fname = cp_smb_filename_nostream(talloc_tos(),
378                                                 smb_fname_in);
379
380         if (smb_fname == NULL) {
381                 TALLOC_FREE(frame);
382                 errno = ENOMEM;
383                 return -1;
384         }
385
386         ret = SMB_VFS_NEXT_STAT(handle, smb_fname);
387         if (ret == -1) {
388                 TALLOC_FREE(frame);
389                 return -1;
390         }
391
392         if (!S_ISDIR(smb_fname->st.st_ex_mode)) {
393                 errno = EINVAL;
394                 TALLOC_FREE(frame);
395                 return -1;
396         }
397
398         ret = SMB_VFS_NEXT_REMOVEXATTR(handle, smb_fname, name);
399         if (ret == -1 && errno == ENOATTR) {
400                 ret = 0;
401                 errno = 0;
402         }
403
404         TALLOC_FREE(frame);
405         return ret;
406 }
407
408 static int fake_acls_chown(vfs_handle_struct *handle,
409                         const struct smb_filename *smb_fname,
410                         uid_t uid,
411                         gid_t gid)
412 {
413         int ret;
414         uint8_t id_buf[4];
415         if (uid != -1) {
416                 uid_t current_uid = get_current_uid(handle->conn);
417
418                 if (current_uid != 0 && current_uid != uid) {
419                         return EACCES;
420                 }
421
422                 SIVAL(id_buf, 0, uid);
423                 ret = SMB_VFS_NEXT_SETXATTR(handle,
424                                 smb_fname,
425                                 FAKE_UID,
426                                 id_buf,
427                                 sizeof(id_buf),
428                                 0);
429                 if (ret != 0) {
430                         return ret;
431                 }
432         }
433         if (gid != -1) {
434                 SIVAL(id_buf, 0, gid);
435                 ret = SMB_VFS_NEXT_SETXATTR(handle,
436                                 smb_fname,
437                                 FAKE_GID,
438                                 id_buf,
439                                 sizeof(id_buf),
440                                 0);
441                 if (ret != 0) {
442                         return ret;
443                 }
444         }
445         return 0;
446 }
447
448 static int fake_acls_lchown(vfs_handle_struct *handle,
449                         const struct smb_filename *smb_fname,
450                         uid_t uid,
451                         gid_t gid)
452 {
453         int ret;
454         uint8_t id_buf[4];
455         if (uid != -1) {
456                 uid_t current_uid = get_current_uid(handle->conn);
457
458                 if (current_uid != 0 && current_uid != uid) {
459                         return EACCES;
460                 }
461
462                 /* This isn't quite right (calling setxattr not
463                  * lsetxattr), but for the test purposes of this
464                  * module (fake NT ACLs from windows clients), it is
465                  * close enough.  We removed the l*xattr functions
466                  * because linux doesn't support using them, but we
467                  * could fake them in xattr_tdb if we really wanted
468                  * to.
469                  */
470                 SIVAL(id_buf, 0, uid);
471                 ret = SMB_VFS_NEXT_SETXATTR(handle,
472                                 smb_fname,
473                                 FAKE_UID,
474                                 id_buf,
475                                 sizeof(id_buf),
476                                 0);
477                 if (ret != 0) {
478                         return ret;
479                 }
480         }
481         if (gid != -1) {
482                 SIVAL(id_buf, 0, gid);
483                 ret = SMB_VFS_NEXT_SETXATTR(handle,
484                                 smb_fname,
485                                 FAKE_GID,
486                                 id_buf,
487                                 sizeof(id_buf),
488                                 0);
489                 if (ret != 0) {
490                         return ret;
491                 }
492         }
493         return 0;
494 }
495
496 static int fake_acls_fchown(vfs_handle_struct *handle, files_struct *fsp, uid_t uid, gid_t gid)
497 {
498         int ret;
499         uint8_t id_buf[4];
500         if (uid != -1) {
501                 uid_t current_uid = get_current_uid(handle->conn);
502
503                 if (current_uid != 0 && current_uid != uid) {
504                         return EACCES;
505                 }
506
507                 SIVAL(id_buf, 0, uid);
508                 ret = SMB_VFS_NEXT_FSETXATTR(handle, fsp, FAKE_UID, id_buf, sizeof(id_buf), 0);
509                 if (ret != 0) {
510                         return ret;
511                 }
512         }
513         if (gid != -1) {
514                 SIVAL(id_buf, 0, gid);
515                 ret = SMB_VFS_NEXT_FSETXATTR(handle, fsp, FAKE_GID, id_buf, sizeof(id_buf), 0);
516                 if (ret != 0) {
517                         return ret;
518                 }
519         }
520         return 0;
521 }
522
523
524 static struct vfs_fn_pointers vfs_fake_acls_fns = {
525         .stat_fn = fake_acls_stat,
526         .lstat_fn = fake_acls_lstat,
527         .fstat_fn = fake_acls_fstat,
528         .sys_acl_get_file_fn = fake_acls_sys_acl_get_file,
529         .sys_acl_get_fd_fn = fake_acls_sys_acl_get_fd,
530         .sys_acl_blob_get_file_fn = posix_sys_acl_blob_get_file,
531         .sys_acl_blob_get_fd_fn = posix_sys_acl_blob_get_fd,
532         .sys_acl_set_file_fn = fake_acls_sys_acl_set_file,
533         .sys_acl_set_fd_fn = fake_acls_sys_acl_set_fd,
534         .sys_acl_delete_def_file_fn = fake_acls_sys_acl_delete_def_file,
535         .chown_fn = fake_acls_chown,
536         .lchown_fn = fake_acls_lchown,
537         .fchown_fn = fake_acls_fchown,
538         
539 };
540
541 NTSTATUS vfs_fake_acls_init(TALLOC_CTX *);
542 NTSTATUS vfs_fake_acls_init(TALLOC_CTX *ctx)
543 {
544         return smb_register_vfs(SMB_VFS_INTERFACE_VERSION, "fake_acls",
545                                 &vfs_fake_acls_fns);
546 }