917996af0a43cb2dfc5ebe2458fbc04eb777be77
[kai/samba-autobuild/.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                          const char *path,
40                          uid_t *uid)
41 {
42         ssize_t size;
43         uint8_t uid_buf[4];
44         size = SMB_VFS_NEXT_GETXATTR(handle, path, FAKE_UID, uid_buf, sizeof(uid_buf));
45         if (size == -1 && errno == ENOATTR) {
46                 return 0;
47         }
48         if (size != 4) {
49                 return -1;
50         }
51         *uid = IVAL(uid_buf, 0);
52         return 0;
53 }
54
55 static int fake_acls_gid(vfs_handle_struct *handle,
56                          const char *path,
57                          uid_t *gid)
58 {
59         ssize_t size;
60         uint8_t gid_buf[4];
61
62         size = SMB_VFS_NEXT_GETXATTR(handle, path, FAKE_GID, gid_buf, sizeof(gid_buf));
63         if (size == -1 && errno == ENOATTR) {
64                 return 0;
65         }
66         if (size != 4) {
67                 return -1;
68         }
69         *gid = IVAL(gid_buf, 0);
70         return 0;
71 }
72
73 static int fake_acls_fuid(vfs_handle_struct *handle,
74                            files_struct *fsp,
75                            uid_t *uid)
76 {
77         ssize_t size;
78         uint8_t uid_buf[4];
79
80         size = SMB_VFS_NEXT_FGETXATTR(handle, fsp, FAKE_UID, uid_buf, sizeof(uid_buf));
81         if (size == -1 && errno == ENOATTR) {
82                 return 0;
83         }
84         if (size != 4) {
85                 return -1;
86         }
87         *uid = IVAL(uid_buf, 0);
88         return 0;
89 }
90
91 static int fake_acls_fgid(vfs_handle_struct *handle,
92                            files_struct *fsp,
93                           uid_t *gid)
94 {
95         ssize_t size;
96         uint8_t gid_buf[4];
97
98         size = SMB_VFS_NEXT_FGETXATTR(handle, fsp, FAKE_GID, gid_buf, sizeof(gid_buf));
99         if (size == -1 && errno == ENOATTR) {
100                 return 0;
101         }
102         if (size != 4) {
103                 return -1;
104         }
105         *gid = IVAL(gid_buf, 0);
106         return 0;
107 }
108
109 static int fake_acls_stat(vfs_handle_struct *handle,
110                            struct smb_filename *smb_fname)
111 {
112         int ret = -1;
113
114         ret = SMB_VFS_NEXT_STAT(handle, smb_fname);
115         if (ret == 0) {
116                 TALLOC_CTX *frame = talloc_stackframe();
117                 char *path;
118                 struct smb_filename smb_fname_base = {
119                         .base_name = smb_fname->base_name
120                 };
121                 NTSTATUS status;
122                 /*
123                  * As we're calling getxattr directly here
124                  * we need to use only the base_name, not
125                  * the full name containing any stream name.
126                  */
127                 status = get_full_smb_filename(frame, &smb_fname_base, &path);
128                 if (!NT_STATUS_IS_OK(status)) {
129                         errno = map_errno_from_nt_status(status);
130                         TALLOC_FREE(frame);
131                         return -1;
132                 }
133                 
134                 ret = fake_acls_uid(handle, path, &smb_fname->st.st_ex_uid);
135                 if (ret != 0) {
136                         TALLOC_FREE(frame);
137                         return ret;
138                 }
139                 ret = fake_acls_gid(handle, path, &smb_fname->st.st_ex_gid);
140                 if (ret != 0) {
141                         TALLOC_FREE(frame);
142                         return ret;
143                 }
144                 TALLOC_FREE(frame);
145         }
146
147         return ret;
148 }
149
150 static int fake_acls_lstat(vfs_handle_struct *handle,
151                            struct smb_filename *smb_fname)
152 {
153         int ret = -1;
154
155         ret = SMB_VFS_NEXT_LSTAT(handle, smb_fname);
156         if (ret == 0) {
157                 TALLOC_CTX *frame = talloc_stackframe();
158                 char *path;
159                 struct smb_filename smb_fname_base = {
160                         .base_name = smb_fname->base_name
161                 };
162                 NTSTATUS status;
163                 /*
164                  * As we're calling getxattr directly here
165                  * we need to use only the base_name, not
166                  * the full name containing any stream name.
167                  */
168                 status = get_full_smb_filename(frame, &smb_fname_base, &path);
169                 if (!NT_STATUS_IS_OK(status)) {
170                         errno = map_errno_from_nt_status(status);
171                         TALLOC_FREE(frame);
172                         return -1;
173                 }
174
175                 /* This isn't quite right (calling getxattr not
176                  * lgetxattr), but for the test purposes of this
177                  * module (fake NT ACLs from windows clients), it is
178                  * close enough.  We removed the l*xattr functions
179                  * because linux doesn't support using them, but we
180                  * could fake them in xattr_tdb if we really wanted
181                  * to.  We ignore errors because the link might not point anywhere */
182                 fake_acls_uid(handle, path, &smb_fname->st.st_ex_uid);
183                 fake_acls_gid(handle, path, &smb_fname->st.st_ex_gid);
184                 TALLOC_FREE(frame);
185         }
186
187         return ret;
188 }
189
190 static int fake_acls_fstat(vfs_handle_struct *handle, files_struct *fsp, SMB_STRUCT_STAT *sbuf)
191 {
192         int ret = -1;
193
194         ret = SMB_VFS_NEXT_FSTAT(handle, fsp, sbuf);
195         if (ret == 0) {
196                 ret = fake_acls_fuid(handle, fsp, &sbuf->st_ex_uid);
197                 if (ret != 0) {
198                         return ret;
199                 }
200                 ret = fake_acls_fgid(handle, fsp, &sbuf->st_ex_gid);
201                 if (ret != 0) {
202                         return ret;
203                 }
204         }
205         return ret;
206 }
207
208 static SMB_ACL_T fake_acls_blob2acl(DATA_BLOB *blob, TALLOC_CTX *mem_ctx)
209 {
210         enum ndr_err_code ndr_err;
211         struct smb_acl_t *acl = talloc(mem_ctx, struct smb_acl_t);
212         if (!acl) {
213                 errno = ENOMEM;
214                 return NULL;
215         }
216
217         ndr_err = ndr_pull_struct_blob(blob, acl, acl, 
218                 (ndr_pull_flags_fn_t)ndr_pull_smb_acl_t);
219
220         if (!NDR_ERR_CODE_IS_SUCCESS(ndr_err)) {
221                 DEBUG(0, ("ndr_pull_acl_t failed: %s\n",
222                           ndr_errstr(ndr_err)));
223                 TALLOC_FREE(acl);
224                 return NULL;
225         }
226         return acl;
227 }
228
229 static DATA_BLOB fake_acls_acl2blob(TALLOC_CTX *mem_ctx, SMB_ACL_T acl)
230 {
231         enum ndr_err_code ndr_err;
232         DATA_BLOB blob;
233         ndr_err = ndr_push_struct_blob(&blob, mem_ctx, acl, 
234                 (ndr_push_flags_fn_t)ndr_push_smb_acl_t);
235
236         if (!NDR_ERR_CODE_IS_SUCCESS(ndr_err)) {
237                 DEBUG(0, ("ndr_push_acl_t failed: %s\n",
238                           ndr_errstr(ndr_err)));
239                 return data_blob_null;
240         }
241         return blob;
242 }
243
244 static SMB_ACL_T fake_acls_sys_acl_get_file(struct vfs_handle_struct *handle,
245                                 const struct smb_filename *smb_fname,
246                                 SMB_ACL_TYPE_T type,
247                                 TALLOC_CTX *mem_ctx)
248 {
249         DATA_BLOB blob = data_blob_null;
250         ssize_t length;
251         const char *name = NULL;
252         struct smb_acl_t *acl = NULL;
253         const char *path = smb_fname->base_name;
254         TALLOC_CTX *frame = talloc_stackframe();
255         switch (type) {
256         case SMB_ACL_TYPE_ACCESS:
257                 name = FAKE_ACL_ACCESS_XATTR;
258                 break;
259         case SMB_ACL_TYPE_DEFAULT:
260                 name = FAKE_ACL_DEFAULT_XATTR;
261                 break;
262         }
263
264         do {
265                 blob.length += 1000;
266                 blob.data = talloc_realloc(frame, blob.data, uint8_t, blob.length);
267                 if (!blob.data) {
268                         errno = ENOMEM;
269                         TALLOC_FREE(frame);
270                         return NULL;
271                 }
272                 length = SMB_VFS_NEXT_GETXATTR(handle, path, name, blob.data, blob.length);
273                 blob.length = length;
274         } while (length == -1 && errno == ERANGE);
275         if (length == -1 && errno == ENOATTR) {
276                 TALLOC_FREE(frame);
277                 return NULL;
278         }
279         if (length != -1) {
280                 acl = fake_acls_blob2acl(&blob, mem_ctx);
281         }
282         TALLOC_FREE(frame);
283         return acl;
284 }
285
286 static SMB_ACL_T fake_acls_sys_acl_get_fd(struct vfs_handle_struct *handle,
287                                           files_struct *fsp,
288                                           TALLOC_CTX *mem_ctx)
289 {
290         DATA_BLOB blob = data_blob_null;
291         ssize_t length;
292         const char *name = FAKE_ACL_ACCESS_XATTR;
293         struct smb_acl_t *acl = NULL;
294         TALLOC_CTX *frame = talloc_stackframe();
295                 
296         do {
297                 blob.length += 1000;
298                 blob.data = talloc_realloc(frame, blob.data, uint8_t, blob.length);
299                 if (!blob.data) {
300                         errno = ENOMEM;
301                         TALLOC_FREE(frame);
302                         return NULL;
303                 }
304                 length = SMB_VFS_NEXT_FGETXATTR(handle, fsp, name, blob.data, blob.length);
305                 blob.length = length;
306         } while (length == -1 && errno == ERANGE);
307         if (length == -1 && errno == ENOATTR) {
308                 TALLOC_FREE(frame);
309                 return NULL;
310         }
311         if (length != -1) {
312                 acl = fake_acls_blob2acl(&blob, mem_ctx);
313         }
314         TALLOC_FREE(frame);
315         return acl;
316 }
317
318
319 static int fake_acls_sys_acl_set_file(vfs_handle_struct *handle,
320                         const struct smb_filename *smb_fname,
321                         SMB_ACL_TYPE_T acltype,
322                         SMB_ACL_T theacl)
323 {
324         int ret;
325         const char *name = NULL;
326         TALLOC_CTX *frame = talloc_stackframe();
327         DATA_BLOB blob = fake_acls_acl2blob(frame, theacl);
328         if (!blob.data) {
329                 DEBUG(0, ("Failed to convert ACL to linear blob for xattr storage\n"));
330                 TALLOC_FREE(frame);
331                 errno = EINVAL;
332                 return -1;
333         }
334         switch (acltype) {
335         case SMB_ACL_TYPE_ACCESS:
336                 name = FAKE_ACL_ACCESS_XATTR;
337                 break;
338         case SMB_ACL_TYPE_DEFAULT:
339                 name = FAKE_ACL_DEFAULT_XATTR;
340                 break;
341         }
342         ret = SMB_VFS_NEXT_SETXATTR(handle, smb_fname,
343                         name, blob.data, blob.length, 0);
344         TALLOC_FREE(frame);
345         return ret;
346 }
347
348 static int fake_acls_sys_acl_set_fd(vfs_handle_struct *handle, files_struct *fsp, SMB_ACL_T theacl)
349 {
350         int ret;
351         const char *name = FAKE_ACL_ACCESS_XATTR;
352         TALLOC_CTX *frame = talloc_stackframe();
353         DATA_BLOB blob = fake_acls_acl2blob(frame, theacl);
354         if (!blob.data) {
355                 DEBUG(0, ("Failed to convert ACL to linear blob for xattr storage\n"));
356                 TALLOC_FREE(frame);
357                 errno = EINVAL;
358                 return -1;
359         }
360         ret = SMB_VFS_NEXT_FSETXATTR(handle, fsp, name, blob.data, blob.length, 0);
361         TALLOC_FREE(frame);
362         return ret;
363 }
364
365 static int fake_acls_sys_acl_delete_def_file(vfs_handle_struct *handle,
366                         const struct smb_filename *smb_fname_in)
367 {
368         int ret;
369         const char *name = FAKE_ACL_DEFAULT_XATTR;
370         TALLOC_CTX *frame = talloc_stackframe();
371         struct smb_filename *smb_fname = cp_smb_filename_nostream(talloc_tos(),
372                                                 smb_fname_in);
373
374         if (smb_fname == NULL) {
375                 TALLOC_FREE(frame);
376                 errno = ENOMEM;
377                 return -1;
378         }
379
380         ret = SMB_VFS_NEXT_STAT(handle, smb_fname);
381         if (ret == -1) {
382                 TALLOC_FREE(frame);
383                 return -1;
384         }
385
386         if (!S_ISDIR(smb_fname->st.st_ex_mode)) {
387                 errno = EINVAL;
388                 TALLOC_FREE(frame);
389                 return -1;
390         }
391
392         ret = SMB_VFS_NEXT_REMOVEXATTR(handle, smb_fname, name);
393         if (ret == -1 && errno == ENOATTR) {
394                 ret = 0;
395                 errno = 0;
396         }
397
398         TALLOC_FREE(frame);
399         return ret;
400 }
401
402 static int fake_acls_chown(vfs_handle_struct *handle,
403                         const struct smb_filename *smb_fname,
404                         uid_t uid,
405                         gid_t gid)
406 {
407         int ret;
408         uint8_t id_buf[4];
409         if (uid != -1) {
410                 SIVAL(id_buf, 0, uid);
411                 ret = SMB_VFS_NEXT_SETXATTR(handle,
412                                 smb_fname,
413                                 FAKE_UID,
414                                 id_buf,
415                                 sizeof(id_buf),
416                                 0);
417                 if (ret != 0) {
418                         return ret;
419                 }
420         }
421         if (gid != -1) {
422                 SIVAL(id_buf, 0, gid);
423                 ret = SMB_VFS_NEXT_SETXATTR(handle,
424                                 smb_fname,
425                                 FAKE_GID,
426                                 id_buf,
427                                 sizeof(id_buf),
428                                 0);
429                 if (ret != 0) {
430                         return ret;
431                 }
432         }
433         return 0;
434 }
435
436 static int fake_acls_lchown(vfs_handle_struct *handle,
437                         const struct smb_filename *smb_fname,
438                         uid_t uid,
439                         gid_t gid)
440 {
441         int ret;
442         uint8_t id_buf[4];
443         if (uid != -1) {
444                 /* This isn't quite right (calling setxattr not
445                  * lsetxattr), but for the test purposes of this
446                  * module (fake NT ACLs from windows clients), it is
447                  * close enough.  We removed the l*xattr functions
448                  * because linux doesn't support using them, but we
449                  * could fake them in xattr_tdb if we really wanted
450                  * to.
451                  */
452                 SIVAL(id_buf, 0, uid);
453                 ret = SMB_VFS_NEXT_SETXATTR(handle,
454                                 smb_fname,
455                                 FAKE_UID,
456                                 id_buf,
457                                 sizeof(id_buf),
458                                 0);
459                 if (ret != 0) {
460                         return ret;
461                 }
462         }
463         if (gid != -1) {
464                 SIVAL(id_buf, 0, gid);
465                 ret = SMB_VFS_NEXT_SETXATTR(handle,
466                                 smb_fname,
467                                 FAKE_GID,
468                                 id_buf,
469                                 sizeof(id_buf),
470                                 0);
471                 if (ret != 0) {
472                         return ret;
473                 }
474         }
475         return 0;
476 }
477
478 static int fake_acls_fchown(vfs_handle_struct *handle, files_struct *fsp, uid_t uid, gid_t gid)
479 {
480         int ret;
481         uint8_t id_buf[4];
482         if (uid != -1) {
483                 SIVAL(id_buf, 0, uid);
484                 ret = SMB_VFS_NEXT_FSETXATTR(handle, fsp, FAKE_UID, id_buf, sizeof(id_buf), 0);
485                 if (ret != 0) {
486                         return ret;
487                 }
488         }
489         if (gid != -1) {
490                 SIVAL(id_buf, 0, gid);
491                 ret = SMB_VFS_NEXT_FSETXATTR(handle, fsp, FAKE_GID, id_buf, sizeof(id_buf), 0);
492                 if (ret != 0) {
493                         return ret;
494                 }
495         }
496         return 0;
497 }
498
499
500 static struct vfs_fn_pointers vfs_fake_acls_fns = {
501         .stat_fn = fake_acls_stat,
502         .lstat_fn = fake_acls_lstat,
503         .fstat_fn = fake_acls_fstat,
504         .sys_acl_get_file_fn = fake_acls_sys_acl_get_file,
505         .sys_acl_get_fd_fn = fake_acls_sys_acl_get_fd,
506         .sys_acl_blob_get_file_fn = posix_sys_acl_blob_get_file,
507         .sys_acl_blob_get_fd_fn = posix_sys_acl_blob_get_fd,
508         .sys_acl_set_file_fn = fake_acls_sys_acl_set_file,
509         .sys_acl_set_fd_fn = fake_acls_sys_acl_set_fd,
510         .sys_acl_delete_def_file_fn = fake_acls_sys_acl_delete_def_file,
511         .chown_fn = fake_acls_chown,
512         .lchown_fn = fake_acls_lchown,
513         .fchown_fn = fake_acls_fchown,
514         
515 };
516
517 NTSTATUS vfs_fake_acls_init(TALLOC_CTX *);
518 NTSTATUS vfs_fake_acls_init(TALLOC_CTX *ctx)
519 {
520         return smb_register_vfs(SMB_VFS_INTERFACE_VERSION, "fake_acls",
521                                 &vfs_fake_acls_fns);
522 }