Fix crash in module, get more of the NULL acl test right.
[jra/samba/.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 struct security_descriptor *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(struct security_descriptor *, 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                                         struct security_descriptor **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  Create a default security descriptor for a file in case no inheritance
296  exists. All permissions to the owner and SYSTEM.
297 *********************************************************************/
298
299 static struct security_descriptor *default_file_sd(TALLOC_CTX *mem_ctx,
300                                                 SMB_STRUCT_STAT *psbuf)
301 {
302         struct dom_sid owner_sid, group_sid;
303         size_t sd_size;
304         struct security_ace *pace = NULL;
305         struct security_acl *pacl = NULL;
306
307         uid_to_sid(&owner_sid, psbuf->st_uid);
308         gid_to_sid(&group_sid, psbuf->st_gid);
309
310         pace = TALLOC_ARRAY(mem_ctx, struct security_ace, 2);
311         if (!pace) {
312                 return NULL;
313         }
314
315         init_sec_ace(&pace[0], &owner_sid, SEC_ACE_TYPE_ACCESS_ALLOWED,
316                         SEC_RIGHTS_FILE_ALL, 0);
317         init_sec_ace(&pace[1], &global_sid_System, SEC_ACE_TYPE_ACCESS_ALLOWED,
318                         SEC_RIGHTS_FILE_ALL, 0);
319
320         pacl = make_sec_acl(mem_ctx,
321                                 NT4_ACL_REVISION,
322                                 2,
323                                 pace);
324         if (!pacl) {
325                 return NULL;
326         }
327         return make_sec_desc(mem_ctx,
328                         SECURITY_DESCRIPTOR_REVISION_1,
329                         SEC_DESC_SELF_RELATIVE|SEC_DESC_DACL_PRESENT|
330                                 SEC_DESC_DACL_DEFAULTED,
331                         &owner_sid,
332                         &group_sid,
333                         NULL,
334                         pacl,
335                         &sd_size);
336 }
337
338 /*********************************************************************
339 *********************************************************************/
340
341 static NTSTATUS inherit_new_acl(vfs_handle_struct *handle,
342                                         const char *fname,
343                                         files_struct *fsp,
344                                         bool container)
345 {
346         TALLOC_CTX *ctx = talloc_tos();
347         NTSTATUS status;
348         struct security_descriptor *parent_desc = NULL;
349         struct security_descriptor *psd = NULL;
350         DATA_BLOB blob;
351         size_t size;
352         char *parent_name;
353
354         if (!parent_dirname_talloc(ctx,
355                                 fname,
356                                 &parent_name,
357                                 NULL)) {
358                 return NT_STATUS_NO_MEMORY;
359         }
360
361         DEBUG(10,("inherit_new_acl: check directory %s\n",
362                         parent_name));
363
364         status = get_nt_acl_xattr_internal(handle,
365                                         NULL,
366                                         parent_name,
367                                         DACL_SECURITY_INFORMATION,
368                                         &parent_desc);
369         if (NT_STATUS_IS_OK(status)) {
370                 /* Create an inherited descriptor from the parent. */
371                 status = se_create_child_secdesc(ctx,
372                                 &psd,
373                                 &size,
374                                 parent_desc,
375                                 &handle->conn->server_info->ptok->user_sids[PRIMARY_USER_SID_INDEX],
376                                 &handle->conn->server_info->ptok->user_sids[PRIMARY_GROUP_SID_INDEX],
377                                 container);
378                 if (!NT_STATUS_IS_OK(status)) {
379                         return status;
380                 }
381         } else {
382                 DEBUG(10,("inherit_new_acl: directory %s failed "
383                         "to get acl %s\n",
384                         parent_name,
385                         nt_errstr(status) ));
386         }
387
388         if (!psd || psd->dacl == NULL) {
389                 SMB_STRUCT_STAT sbuf;
390                 int ret;
391
392                 TALLOC_FREE(psd);
393                 if (fsp && !fsp->is_directory && fsp->fh->fd != -1) {
394                         ret = SMB_VFS_FSTAT(fsp, &sbuf);
395                 } else {
396                         ret = SMB_VFS_STAT(handle->conn,fname, &sbuf);
397                 }
398                 if (ret == -1) {
399                         return map_nt_error_from_unix(errno);
400                 }
401                 psd = default_file_sd(ctx, &sbuf);
402                 if (!psd) {
403                         return NT_STATUS_NO_MEMORY;
404                 }
405         }
406
407         status = create_acl_blob(psd, &blob);
408         if (!NT_STATUS_IS_OK(status)) {
409                 return status;
410         }
411         if (fsp) {
412                 return store_acl_blob_fsp(fsp, &blob);
413         } else {
414                 return store_acl_blob_pathname(handle->conn, fname, &blob);
415         }
416 }
417
418 /*********************************************************************
419  Check ACL on open. For new files inherit from parent directory.
420 *********************************************************************/
421
422 static int open_acl_xattr(vfs_handle_struct *handle,
423                                         const char *fname,
424                                         files_struct *fsp,
425                                         int flags,
426                                         mode_t mode)
427 {
428         uint32_t access_granted = 0;
429         struct security_descriptor *pdesc = NULL;
430         bool file_existed = true;
431         NTSTATUS status = get_nt_acl_xattr_internal(handle,
432                                         NULL,
433                                         fname,
434                                         (OWNER_SECURITY_INFORMATION |
435                                          GROUP_SECURITY_INFORMATION |
436                                          DACL_SECURITY_INFORMATION),
437                                         &pdesc);
438         if (NT_STATUS_IS_OK(status)) {
439                 /* See if we can access it. */
440                 status = smb1_file_se_access_check(pdesc,
441                                         handle->conn->server_info->ptok,
442                                         fsp->access_mask,
443                                         &access_granted);
444                 if (!NT_STATUS_IS_OK(status)) {
445                         DEBUG(10,("open_acl_xattr: file %s open "
446                                 "refused with error %s\n",
447                                 fname,
448                                 nt_errstr(status) ));
449                         errno = map_errno_from_nt_status(status);
450                         return -1;
451                 }
452         } else if (NT_STATUS_EQUAL(status,NT_STATUS_OBJECT_NAME_NOT_FOUND)) {
453                 file_existed = false;
454         }
455
456         DEBUG(10,("open_acl_xattr: get_nt_acl_attr_internal for "
457                 "file %s returned %s\n",
458                 fname,
459                 nt_errstr(status) ));
460
461         fsp->fh->fd = SMB_VFS_NEXT_OPEN(handle, fname, fsp, flags, mode);
462
463         if (!file_existed && fsp->fh->fd != -1) {
464                 /* File was created. Inherit from parent directory. */
465                 string_set(&fsp->fsp_name, fname);
466                 inherit_new_acl(handle, fname, fsp, false);
467         }
468
469         return fsp->fh->fd;
470 }
471
472 static int mkdir_acl_xattr(vfs_handle_struct *handle, const char *path, mode_t mode)
473 {
474         int ret = SMB_VFS_NEXT_MKDIR(handle, path, mode);
475
476         if (ret == -1) {
477                 return ret;
478         }
479         /* New directory - inherit from parent. */
480         inherit_new_acl(handle, path, NULL, true);
481         return ret;
482 }
483
484 static NTSTATUS fget_nt_acl_xattr(vfs_handle_struct *handle, files_struct *fsp,
485         uint32 security_info, struct security_descriptor **ppdesc)
486 {
487         NTSTATUS status = get_nt_acl_xattr_internal(handle, fsp,
488                                 NULL, security_info, ppdesc);
489         if (NT_STATUS_IS_OK(status)) {
490                 if (DEBUGLEVEL >= 10) {
491                         DEBUG(10,("fget_nt_acl_xattr: returning xattr sd for file %s\n",
492                                 fsp->fsp_name));
493                         NDR_PRINT_DEBUG(security_descriptor, *ppdesc);
494                 }
495                 return NT_STATUS_OK;
496         }
497         return SMB_VFS_NEXT_FGET_NT_ACL(handle, fsp,
498                         security_info, ppdesc);
499 }
500
501 static NTSTATUS get_nt_acl_xattr(vfs_handle_struct *handle,
502         const char *name, uint32 security_info, struct security_descriptor **ppdesc)
503 {
504         NTSTATUS status = get_nt_acl_xattr_internal(handle, NULL,
505                                 name, security_info, ppdesc);
506         if (NT_STATUS_IS_OK(status)) {
507                 if (DEBUGLEVEL >= 10) {
508                         DEBUG(10,("get_nt_acl_xattr: returning xattr sd for file %s\n",
509                                 name));
510                         NDR_PRINT_DEBUG(security_descriptor, *ppdesc);
511                 }
512                 return NT_STATUS_OK;
513         }
514         return SMB_VFS_NEXT_GET_NT_ACL(handle, name,
515                         security_info, ppdesc);
516 }
517
518 static NTSTATUS fset_nt_acl_xattr(vfs_handle_struct *handle, files_struct *fsp,
519         uint32 security_info_sent, const struct security_descriptor *psd)
520 {
521         NTSTATUS status;
522         DATA_BLOB blob;
523
524         if (DEBUGLEVEL >= 10) {
525                 DEBUG(10,("fset_nt_acl_xattr: incoming sd for file %s\n",
526                         fsp->fsp_name));
527                 NDR_PRINT_DEBUG(security_descriptor,
528                         CONST_DISCARD(struct security_descriptor *,psd));
529         }
530
531         if (!psd->owner_sid && !psd->group_sid && !(psd->type & SEC_DESC_DACL_PRESENT)) {
532                 return NT_STATUS_OK;
533         }
534
535         status = SMB_VFS_NEXT_FSET_NT_ACL(handle, fsp, security_info_sent, psd);
536         if (!NT_STATUS_IS_OK(status)) {
537                 return status;
538         }
539
540         /* Ensure owner and group are set. */
541         if (!psd->owner_sid || !psd->group_sid) {
542                 int ret;
543                 SMB_STRUCT_STAT sbuf;
544                 DOM_SID owner_sid, group_sid;
545                 struct security_descriptor *nc_psd = dup_sec_desc(talloc_tos(), psd);
546
547                 if (!nc_psd) {
548                         return NT_STATUS_OK;
549                 }
550                 if (fsp->is_directory || fsp->fh->fd == -1) {
551                         ret = SMB_VFS_STAT(fsp->conn,fsp->fsp_name, &sbuf);
552                 } else {
553                         ret = SMB_VFS_FSTAT(fsp, &sbuf);
554                 }
555                 if (ret == -1) {
556                         /* Lower level acl set succeeded,
557                          * so still return OK. */
558                         return NT_STATUS_OK;
559                 }
560                 create_file_sids(&sbuf, &owner_sid, &group_sid);
561                 /* This is safe as nc_psd is discarded at fn exit. */
562                 nc_psd->owner_sid = &owner_sid;
563                 nc_psd->group_sid = &group_sid;
564                 security_info_sent |= (OWNER_SECURITY_INFORMATION|GROUP_SECURITY_INFORMATION);
565                 psd = nc_psd;
566         }
567
568         if ((security_info_sent & DACL_SECURITY_INFORMATION) &&
569                         psd->dacl != NULL &&
570                         (psd->type & (SE_DESC_DACL_AUTO_INHERITED|
571                                 SE_DESC_DACL_AUTO_INHERIT_REQ))==
572                                 (SE_DESC_DACL_AUTO_INHERITED|
573                                 SE_DESC_DACL_AUTO_INHERIT_REQ) ) {
574                 struct security_descriptor *new_psd = NULL;
575                 status = append_parent_acl(fsp, psd, &new_psd);
576                 if (!NT_STATUS_IS_OK(status)) {
577                         /* Lower level acl set succeeded,
578                          * so still return OK. */
579                         return NT_STATUS_OK;
580                 }
581                 psd = new_psd;
582         }
583
584         if (DEBUGLEVEL >= 10) {
585                 DEBUG(10,("fset_nt_acl_xattr: storing xattr sd for file %s\n",
586                         fsp->fsp_name));
587                 NDR_PRINT_DEBUG(security_descriptor,
588                         CONST_DISCARD(struct security_descriptor *,psd));
589         }
590         create_acl_blob(psd, &blob);
591         store_acl_blob_fsp(fsp, &blob);
592
593         return NT_STATUS_OK;
594 }
595
596 /* VFS operations structure */
597
598 static vfs_op_tuple skel_op_tuples[] =
599 {
600         {SMB_VFS_OP(mkdir_acl_xattr), SMB_VFS_OP_MKDIR, SMB_VFS_LAYER_TRANSPARENT},
601         {SMB_VFS_OP(open_acl_xattr),  SMB_VFS_OP_OPEN,  SMB_VFS_LAYER_TRANSPARENT},
602
603         /* NT File ACL operations */
604
605         {SMB_VFS_OP(fget_nt_acl_xattr),SMB_VFS_OP_FGET_NT_ACL,SMB_VFS_LAYER_TRANSPARENT},
606         {SMB_VFS_OP(get_nt_acl_xattr), SMB_VFS_OP_GET_NT_ACL, SMB_VFS_LAYER_TRANSPARENT},
607         {SMB_VFS_OP(fset_nt_acl_xattr),SMB_VFS_OP_FSET_NT_ACL,SMB_VFS_LAYER_TRANSPARENT},
608
609         {SMB_VFS_OP(NULL), SMB_VFS_OP_NOOP, SMB_VFS_LAYER_NOOP}
610 };
611
612 NTSTATUS vfs_acl_xattr_init(void)
613 {
614         return smb_register_vfs(SMB_VFS_INTERFACE_VERSION, "acl_xattr", skel_op_tuples);
615 }