Merge branch 'master' of git://git.samba.org/samba
[kai/samba-autobuild/.git] / source3 / modules / vfs_acl_xattr.c
1 /*
2  * Store Windows ACLs in xattrs.
3  *
4  * Copyright (C) Volker Lendecke, 2008
5  * Copyright (C) Jeremy Allison, 2008
6  *
7  * This program is free software; you can redistribute it and/or modify
8  * it under the terms of the GNU General Public License as published by
9  * the Free Software Foundation; either version 3 of the License, or
10  * (at your option) any later version.
11  *
12  * This program is distributed in the hope that it will be useful,
13  * but WITHOUT ANY WARRANTY; without even the implied warranty of
14  * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
15  * GNU General Public License for more details.
16  *
17  * You should have received a copy of the GNU General Public License
18  * along with this program; if not, see <http://www.gnu.org/licenses/>.
19  */
20
21 /* NOTE: This is an experimental module, not yet finished. JRA. */
22
23 #include "includes.h"
24 #include "librpc/gen_ndr/xattr.h"
25 #include "librpc/gen_ndr/ndr_xattr.h"
26
27 #undef DBGC_CLASS
28 #define DBGC_CLASS DBGC_VFS
29
30 static NTSTATUS parse_acl_blob(const DATA_BLOB *pblob,
31                                 const struct timespec cts,
32                                 uint32 security_info,
33                                 struct security_descriptor **ppdesc)
34 {
35         TALLOC_CTX *ctx = talloc_tos();
36         struct xattr_NTACL xacl;
37         enum ndr_err_code ndr_err;
38         size_t sd_size;
39
40         ndr_err = ndr_pull_struct_blob(pblob, ctx, NULL, &xacl,
41                         (ndr_pull_flags_fn_t)ndr_pull_xattr_NTACL);
42
43         if (!NDR_ERR_CODE_IS_SUCCESS(ndr_err)) {
44                 DEBUG(5, ("parse_acl_blob: ndr_pull_xattr_NTACL failed: %s\n",
45                         ndr_errstr(ndr_err)));
46                 return ndr_map_error2ntstatus(ndr_err);;
47         }
48
49         if (xacl.version != 2) {
50                 return NT_STATUS_REVISION_MISMATCH;
51         }
52
53 #if 0
54         {
55                 struct timespec ts;
56                 /* Arg. This doesn't work. Too many activities
57                  * change the ctime. May have to roll back to
58                  * version 1.
59                  */
60                 /*
61                  * Check that the ctime timestamp is ealier
62                  * than the stored timestamp.
63                  */
64
65                 ts = nt_time_to_unix_timespec(&xacl.info.sd_ts->last_changed);
66
67                 if (timespec_compare(&cts, &ts) > 0) {
68                         DEBUG(5, ("parse_acl_blob: stored ACL out of date "
69                                 "(%s > %s.\n",
70                                 timestring(ctx, cts.tv_sec),
71                                 timestring(ctx, ts.tv_sec)));
72                         return NT_STATUS_EA_CORRUPT_ERROR;
73                 }
74         }
75 #endif
76
77         *ppdesc = make_sec_desc(ctx, SEC_DESC_REVISION, SEC_DESC_SELF_RELATIVE,
78                         (security_info & OWNER_SECURITY_INFORMATION)
79                         ? xacl.info.sd_ts->sd->owner_sid : NULL,
80                         (security_info & GROUP_SECURITY_INFORMATION)
81                         ? xacl.info.sd_ts->sd->group_sid : NULL,
82                         (security_info & SACL_SECURITY_INFORMATION)
83                         ? xacl.info.sd_ts->sd->sacl : NULL,
84                         (security_info & DACL_SECURITY_INFORMATION)
85                         ? xacl.info.sd_ts->sd->dacl : NULL,
86                         &sd_size);
87
88         TALLOC_FREE(xacl.info.sd);
89
90         return (*ppdesc != NULL) ? NT_STATUS_OK : NT_STATUS_NO_MEMORY;
91 }
92
93 static NTSTATUS get_acl_blob(TALLOC_CTX *ctx,
94                         vfs_handle_struct *handle,
95                         files_struct *fsp,
96                         const char *name,
97                         DATA_BLOB *pblob)
98 {
99         size_t size = 1024;
100         uint8_t *val = NULL;
101         uint8_t *tmp;
102         ssize_t sizeret;
103         int saved_errno = 0;
104
105         ZERO_STRUCTP(pblob);
106
107   again:
108
109         tmp = TALLOC_REALLOC_ARRAY(ctx, val, uint8_t, size);
110         if (tmp == NULL) {
111                 TALLOC_FREE(val);
112                 return NT_STATUS_NO_MEMORY;
113         }
114         val = tmp;
115
116         become_root();
117         if (fsp && fsp->fh->fd != -1) {
118                 sizeret = SMB_VFS_FGETXATTR(fsp, XATTR_NTACL_NAME, val, size);
119         } else {
120                 sizeret = SMB_VFS_GETXATTR(handle->conn, name,
121                                         XATTR_NTACL_NAME, val, size);
122         }
123         if (sizeret == -1) {
124                 saved_errno = errno;
125         }
126         unbecome_root();
127
128         /* Max ACL size is 65536 bytes. */
129         if (sizeret == -1) {
130                 errno = saved_errno;
131                 if ((errno == ERANGE) && (size != 65536)) {
132                         /* Too small, try again. */
133                         size = 65536;
134                         goto again;
135                 }
136
137                 /* Real error - exit here. */
138                 TALLOC_FREE(val);
139                 return map_nt_error_from_unix(errno);
140         }
141
142         pblob->data = val;
143         pblob->length = sizeret;
144         return NT_STATUS_OK;
145 }
146
147 static NTSTATUS create_acl_blob(const SEC_DESC *psd, DATA_BLOB *pblob)
148 {
149         struct xattr_NTACL xacl;
150         struct security_descriptor_timestamp sd_ts;
151         enum ndr_err_code ndr_err;
152         TALLOC_CTX *ctx = talloc_tos();
153         struct timespec curr = timespec_current();
154
155         ZERO_STRUCT(xacl);
156         ZERO_STRUCT(sd_ts);
157
158         /* Horrid hack as setting an xattr changes the ctime
159          * on Linux. This gives a race of 1 second during
160          * which we would not see a POSIX ACL set.
161          */
162         curr.tv_sec += 1;
163
164         xacl.version = 2;
165         xacl.info.sd_ts = &sd_ts;
166         xacl.info.sd_ts->sd = CONST_DISCARD(SEC_DESC *, psd);
167         unix_timespec_to_nt_time(&xacl.info.sd_ts->last_changed, curr);
168
169         DEBUG(10, ("create_acl_blob: timestamp stored as %s\n",
170                 timestring(ctx, curr.tv_sec) ));
171
172         ndr_err = ndr_push_struct_blob(
173                         pblob, ctx, NULL, &xacl,
174                         (ndr_push_flags_fn_t)ndr_push_xattr_NTACL);
175
176         if (!NDR_ERR_CODE_IS_SUCCESS(ndr_err)) {
177                 DEBUG(5, ("create_acl_blob: ndr_push_xattr_NTACL failed: %s\n",
178                         ndr_errstr(ndr_err)));
179                 return ndr_map_error2ntstatus(ndr_err);;
180         }
181
182         return NT_STATUS_OK;
183 }
184
185 static NTSTATUS store_acl_blob_fsp(files_struct *fsp,
186                                 DATA_BLOB *pblob)
187 {
188         int ret;
189         int saved_errno = 0;
190
191         DEBUG(10,("store_acl_blob_fsp: storing blob length %u on file %s\n",
192                         (unsigned int)pblob->length, fsp->fsp_name));
193
194         become_root();
195         if (fsp->fh->fd != -1) {
196                 ret = SMB_VFS_FSETXATTR(fsp, XATTR_NTACL_NAME,
197                         pblob->data, pblob->length, 0);
198         } else {
199                 ret = SMB_VFS_SETXATTR(fsp->conn, fsp->fsp_name,
200                                 XATTR_NTACL_NAME,
201                                 pblob->data, pblob->length, 0);
202         }
203         if (ret) {
204                 saved_errno = errno;
205         }
206         unbecome_root();
207         if (ret) {
208                 errno = saved_errno;
209                 DEBUG(5, ("store_acl_blob_fsp: setting attr failed for file %s"
210                         "with error %s\n",
211                         fsp->fsp_name,
212                         strerror(errno) ));
213                 return map_nt_error_from_unix(errno);
214         }
215         return NT_STATUS_OK;
216 }
217
218 static NTSTATUS store_acl_blob_pathname(connection_struct *conn,
219                                         const char *fname,
220                                         DATA_BLOB *pblob)
221 {
222         int ret;
223         int saved_errno = 0;
224
225         DEBUG(10,("store_acl_blob_pathname: storing blob "
226                         "length %u on file %s\n",
227                         (unsigned int)pblob->length, fname));
228
229         become_root();
230         ret = SMB_VFS_SETXATTR(conn, fname,
231                                 XATTR_NTACL_NAME,
232                                 pblob->data, pblob->length, 0);
233         if (ret) {
234                 saved_errno = errno;
235         }
236         unbecome_root();
237         if (ret) {
238                 errno = saved_errno;
239                 DEBUG(5, ("store_acl_blob_pathname: setting attr failed "
240                         "for file %s with error %s\n",
241                         fname,
242                         strerror(errno) ));
243                 return map_nt_error_from_unix(errno);
244         }
245         return NT_STATUS_OK;
246 }
247
248
249 static NTSTATUS get_nt_acl_xattr_internal(vfs_handle_struct *handle,
250                                         files_struct *fsp,
251                                         const char *name,
252                                         uint32 security_info,
253                                         SEC_DESC **ppdesc)
254 {
255         TALLOC_CTX *ctx = talloc_tos();
256         DATA_BLOB blob;
257         SMB_STRUCT_STAT sbuf;
258         NTSTATUS status;
259
260         if (fsp && name == NULL) {
261                 name = fsp->fsp_name;
262         }
263
264         DEBUG(10, ("get_nt_acl_xattr_internal: name=%s\n", name));
265
266         status = get_acl_blob(ctx, handle, fsp, name, &blob);
267         if (!NT_STATUS_IS_OK(status)) {
268                 DEBUG(10, ("get_acl_blob returned %s\n", nt_errstr(status)));
269                 return status;
270         }
271
272         if (fsp && fsp->fh->fd != -1) {
273                 if (SMB_VFS_FSTAT(fsp, &sbuf) == -1) {
274                         return map_nt_error_from_unix(errno);
275                 }
276         } else {
277                 if (SMB_VFS_STAT(handle->conn, name, &sbuf) == -1) {
278                         return map_nt_error_from_unix(errno);
279                 }
280         }
281
282         status = parse_acl_blob(&blob, get_ctimespec(&sbuf),
283                         security_info, ppdesc);
284         if (!NT_STATUS_IS_OK(status)) {
285                 DEBUG(10, ("parse_acl_blob returned %s\n",
286                                 nt_errstr(status)));
287                 return status;
288         }
289
290         TALLOC_FREE(blob.data);
291         return status;
292 }
293
294 /*********************************************************************
295  * Currently this only works for existing files. Need to work on
296  * inheritance for new files.
297 *********************************************************************/
298
299 static NTSTATUS inherit_new_acl(vfs_handle_struct *handle,
300                                         const char *fname,
301                                         files_struct *fsp,
302                                         bool container)
303 {
304         TALLOC_CTX *ctx = talloc_tos();
305         NTSTATUS status;
306         SEC_DESC *parent_desc = NULL;
307         SEC_DESC *psd = NULL;
308         DATA_BLOB blob;
309         size_t size;
310         char *parent_name;
311
312         if (!parent_dirname_talloc(ctx,
313                                 fname,
314                                 &parent_name,
315                                 NULL)) {
316                 return NT_STATUS_NO_MEMORY;
317         }
318
319         DEBUG(10,("inherit_new_acl: check directory %s\n",
320                         parent_name));
321
322         status = get_nt_acl_xattr_internal(handle,
323                                         NULL,
324                                         parent_name,
325                                         DACL_SECURITY_INFORMATION,
326                                         &parent_desc);
327         if (!NT_STATUS_IS_OK(status)) {
328                 DEBUG(10,("inherit_new_acl: directory %s failed "
329                         "to get acl %s\n",
330                         parent_name,
331                         nt_errstr(status) ));
332                 return status;
333         }
334
335         /* Create an inherited descriptor from the parent. */
336         status = se_create_child_secdesc(ctx,
337                                 &psd,
338                                 &size,
339                                 parent_desc,
340                                 &handle->conn->server_info->ptok->user_sids[PRIMARY_USER_SID_INDEX],
341                                 &handle->conn->server_info->ptok->user_sids[PRIMARY_GROUP_SID_INDEX],
342                                 container);
343         if (!NT_STATUS_IS_OK(status)) {
344                 return status;
345         }
346         status = create_acl_blob(psd, &blob);
347         if (!NT_STATUS_IS_OK(status)) {
348                 return status;
349         }
350         if (fsp) {
351                 return store_acl_blob_fsp(fsp, &blob);
352         } else {
353                 return store_acl_blob_pathname(handle->conn, fname, &blob);
354         }
355 }
356
357 /*********************************************************************
358  Check ACL on open. For new files inherit from parent directory.
359 *********************************************************************/
360
361 static int open_acl_xattr(vfs_handle_struct *handle,
362                                         const char *fname,
363                                         files_struct *fsp,
364                                         int flags,
365                                         mode_t mode)
366 {
367         uint32_t access_granted = 0;
368         SEC_DESC *pdesc = NULL;
369         bool file_existed = true;
370         NTSTATUS status = get_nt_acl_xattr_internal(handle,
371                                         NULL,
372                                         fname,
373                                         (OWNER_SECURITY_INFORMATION |
374                                          GROUP_SECURITY_INFORMATION |
375                                          DACL_SECURITY_INFORMATION),
376                                         &pdesc);
377         if (NT_STATUS_IS_OK(status)) {
378                 /* See if we can access it. */
379                 if (!se_access_check(pdesc,
380                                         handle->conn->server_info->ptok,
381                                         fsp->access_mask,
382                                         &access_granted,
383                                         &status)) {
384                         errno = map_errno_from_nt_status(status);
385                         return -1;
386                 }
387         } else if (NT_STATUS_EQUAL(status,NT_STATUS_OBJECT_NAME_NOT_FOUND)) {
388                 file_existed = false;
389         }
390
391         DEBUG(10,("open_acl_xattr: get_nt_acl_attr_internal for "
392                 "file %s returned %s\n",
393                 fname,
394                 nt_errstr(status) ));
395
396         fsp->fh->fd = SMB_VFS_NEXT_OPEN(handle, fname, fsp, flags, mode);
397
398         if (!file_existed && fsp->fh->fd != -1) {
399                 /* File was created. Inherit from parent directory. */
400                 string_set(&fsp->fsp_name, fname);
401                 inherit_new_acl(handle, fname, fsp, false);
402         }
403
404         return fsp->fh->fd;
405 }
406
407 static int mkdir_acl_xattr(vfs_handle_struct *handle, const char *path, mode_t mode)
408 {
409         int ret = SMB_VFS_NEXT_MKDIR(handle, path, mode);
410
411         if (ret == -1) {
412                 return ret;
413         }
414         /* New directory - inherit from parent. */
415         inherit_new_acl(handle, path, NULL, true);
416         return ret;
417 }
418
419 static NTSTATUS fget_nt_acl_xattr(vfs_handle_struct *handle, files_struct *fsp,
420         uint32 security_info, SEC_DESC **ppdesc)
421 {
422         NTSTATUS status = get_nt_acl_xattr_internal(handle, fsp,
423                                 NULL, security_info, ppdesc);
424         if (NT_STATUS_IS_OK(status)) {
425                 if (DEBUGLEVEL >= 10) {
426                         DEBUG(10,("fget_nt_acl_xattr: returning xattr sd for file %s\n",
427                                 fsp->fsp_name));
428                         NDR_PRINT_DEBUG(security_descriptor, *ppdesc);
429                 }
430                 return NT_STATUS_OK;
431         }
432         return SMB_VFS_NEXT_FGET_NT_ACL(handle, fsp,
433                         security_info, ppdesc);
434 }
435
436 static NTSTATUS get_nt_acl_xattr(vfs_handle_struct *handle,
437         const char *name, uint32 security_info, SEC_DESC **ppdesc)
438 {
439         NTSTATUS status = get_nt_acl_xattr_internal(handle, NULL,
440                                 name, security_info, ppdesc);
441         if (NT_STATUS_IS_OK(status)) {
442                 if (DEBUGLEVEL >= 10) {
443                         DEBUG(10,("get_nt_acl_xattr: returning xattr sd for file %s\n",
444                                 name));
445                         NDR_PRINT_DEBUG(security_descriptor, *ppdesc);
446                 }
447                 return NT_STATUS_OK;
448         }
449         return SMB_VFS_NEXT_GET_NT_ACL(handle, name,
450                         security_info, ppdesc);
451 }
452
453 static NTSTATUS fset_nt_acl_xattr(vfs_handle_struct *handle, files_struct *fsp,
454         uint32 security_info_sent, const SEC_DESC *psd)
455 {
456         NTSTATUS status;
457         DATA_BLOB blob;
458
459         if (DEBUGLEVEL >= 10) {
460                 DEBUG(10,("fset_nt_acl_xattr: incoming sd for file %s\n",
461                         fsp->fsp_name));
462                 NDR_PRINT_DEBUG(security_descriptor,
463                         CONST_DISCARD(SEC_DESC *,psd));
464         }
465
466         status = SMB_VFS_NEXT_FSET_NT_ACL(handle, fsp, security_info_sent, psd);
467         if (!NT_STATUS_IS_OK(status)) {
468                 return status;
469         }
470
471         /* Ensure owner and group are set. */
472         if (!psd->owner_sid || !psd->group_sid) {
473                 int ret;
474                 SMB_STRUCT_STAT sbuf;
475                 DOM_SID owner_sid, group_sid;
476                 SEC_DESC *nc_psd = dup_sec_desc(talloc_tos(), psd);
477
478                 if (!nc_psd) {
479                         return NT_STATUS_OK;
480                 }
481                 if (fsp->is_directory || fsp->fh->fd == -1) {
482                         ret = SMB_VFS_STAT(fsp->conn,fsp->fsp_name, &sbuf);
483                 } else {
484                         ret = SMB_VFS_FSTAT(fsp, &sbuf);
485                 }
486                 if (ret == -1) {
487                         /* Lower level acl set succeeded,
488                          * so still return OK. */
489                         return NT_STATUS_OK;
490                 }
491                 create_file_sids(&sbuf, &owner_sid, &group_sid);
492                 /* This is safe as nc_psd is discarded at fn exit. */
493                 nc_psd->owner_sid = &owner_sid;
494                 nc_psd->group_sid = &group_sid;
495                 security_info_sent |= (OWNER_SECURITY_INFORMATION|GROUP_SECURITY_INFORMATION);
496                 psd = nc_psd;
497         }
498
499         if ((security_info_sent & DACL_SECURITY_INFORMATION) &&
500                         psd->dacl != NULL &&
501                         (psd->type & (SE_DESC_DACL_AUTO_INHERITED|
502                                 SE_DESC_DACL_AUTO_INHERIT_REQ))==
503                                 (SE_DESC_DACL_AUTO_INHERITED|
504                                 SE_DESC_DACL_AUTO_INHERIT_REQ) ) {
505                 SEC_DESC *new_psd = NULL;
506                 status = append_parent_acl(fsp, psd, &new_psd);
507                 if (!NT_STATUS_IS_OK(status)) {
508                         /* Lower level acl set succeeded,
509                          * so still return OK. */
510                         return NT_STATUS_OK;
511                 }
512                 psd = new_psd;
513         }
514
515         if (DEBUGLEVEL >= 10) {
516                 DEBUG(10,("fset_nt_acl_xattr: storing xattr sd for file %s\n",
517                         fsp->fsp_name));
518                 NDR_PRINT_DEBUG(security_descriptor,
519                         CONST_DISCARD(SEC_DESC *,psd));
520         }
521         create_acl_blob(psd, &blob);
522         store_acl_blob_fsp(fsp, &blob);
523
524         return NT_STATUS_OK;
525 }
526
527 /* VFS operations structure */
528
529 static vfs_op_tuple skel_op_tuples[] =
530 {
531         {SMB_VFS_OP(mkdir_acl_xattr), SMB_VFS_OP_MKDIR, SMB_VFS_LAYER_TRANSPARENT},
532         {SMB_VFS_OP(open_acl_xattr),  SMB_VFS_OP_OPEN,  SMB_VFS_LAYER_TRANSPARENT},
533
534         /* NT File ACL operations */
535
536         {SMB_VFS_OP(fget_nt_acl_xattr),SMB_VFS_OP_FGET_NT_ACL,SMB_VFS_LAYER_TRANSPARENT},
537         {SMB_VFS_OP(get_nt_acl_xattr), SMB_VFS_OP_GET_NT_ACL, SMB_VFS_LAYER_TRANSPARENT},
538         {SMB_VFS_OP(fset_nt_acl_xattr),SMB_VFS_OP_FSET_NT_ACL,SMB_VFS_LAYER_TRANSPARENT},
539
540         {SMB_VFS_OP(NULL), SMB_VFS_OP_NOOP, SMB_VFS_LAYER_NOOP}
541 };
542
543 NTSTATUS vfs_acl_xattr_init(void)
544 {
545         return smb_register_vfs(SMB_VFS_INTERFACE_VERSION, "acl_xattr", skel_op_tuples);
546 }