s3:modules:nfs4_acls: improve fix for bug #8330
[obnox/samba/samba-obnox.git] / source3 / modules / nfs4_acls.c
1 /*
2  * NFS4 ACL handling
3  *
4  * Copyright (C) Jim McDonough, 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 "smbd/smbd.h"
22 #include "nfs4_acls.h"
23 #include "librpc/gen_ndr/ndr_security.h"
24 #include "../libcli/security/dom_sid.h"
25 #include "../libcli/security/security.h"
26 #include "include/dbwrap.h"
27 #include "system/filesys.h"
28 #include "passdb/lookup_sid.h"
29 #include "util_tdb.h"
30 #include "lib/param/loadparm.h"
31
32 #undef DBGC_CLASS
33 #define DBGC_CLASS DBGC_ACLS
34
35 #define SMBACL4_PARAM_TYPE_NAME "nfs4"
36
37 extern const struct generic_mapping file_generic_mapping;
38
39 #define SMB_ACE4_INT_MAGIC 0x76F8A967
40 typedef struct _SMB_ACE4_INT_T
41 {
42         uint32  magic;
43         SMB_ACE4PROP_T  prop;
44         void    *next;
45 } SMB_ACE4_INT_T;
46
47 #define SMB_ACL4_INT_MAGIC 0x29A3E792
48 typedef struct _SMB_ACL4_INT_T
49 {
50         uint32  magic;
51         uint32  naces;
52         SMB_ACE4_INT_T  *first;
53         SMB_ACE4_INT_T  *last;
54 } SMB_ACL4_INT_T;
55
56 /************************************************
57  Split the ACE flag mapping between nfs4 and Windows
58  into two separate functions rather than trying to do
59  it inline. Allows us to carefully control what flags
60  are mapped to what in one place.
61 ************************************************/
62
63 static uint32_t map_nfs4_ace_flags_to_windows_ace_flags(uint32_t nfs4_ace_flags)
64 {
65         uint32_t win_ace_flags = 0;
66
67         /* The nfs4 flags <= 0xf map perfectly. */
68         win_ace_flags = nfs4_ace_flags & (SEC_ACE_FLAG_OBJECT_INHERIT|
69                                       SEC_ACE_FLAG_CONTAINER_INHERIT|
70                                       SEC_ACE_FLAG_NO_PROPAGATE_INHERIT|
71                                       SEC_ACE_FLAG_INHERIT_ONLY);
72
73         /* flags greater than 0xf have diverged :-(. */
74         /* See the nfs4 ace flag definitions here:
75            http://www.ietf.org/rfc/rfc3530.txt.
76            And the Windows ace flag definitions here:
77            librpc/idl/security.idl. */
78         if (nfs4_ace_flags & SMB_ACE4_INHERITED_ACE) {
79                 win_ace_flags |= SEC_ACE_FLAG_INHERITED_ACE;
80         }
81
82         return win_ace_flags;
83 }
84
85 static uint32_t map_windows_ace_flags_to_nfs4_ace_flags(uint32_t win_ace_flags)
86 {
87         uint32_t nfs4_ace_flags = 0;
88
89         /* The windows flags <= 0xf map perfectly. */
90         nfs4_ace_flags = win_ace_flags & (SMB_ACE4_FILE_INHERIT_ACE|
91                                       SMB_ACE4_DIRECTORY_INHERIT_ACE|
92                                       SMB_ACE4_NO_PROPAGATE_INHERIT_ACE|
93                                       SMB_ACE4_INHERIT_ONLY_ACE);
94
95         /* flags greater than 0xf have diverged :-(. */
96         /* See the nfs4 ace flag definitions here:
97            http://www.ietf.org/rfc/rfc3530.txt.
98            And the Windows ace flag definitions here:
99            librpc/idl/security.idl. */
100         if (win_ace_flags & SEC_ACE_FLAG_INHERITED_ACE) {
101                 nfs4_ace_flags |= SMB_ACE4_INHERITED_ACE;
102         }
103
104         return nfs4_ace_flags;
105 }
106
107 static SMB_ACL4_INT_T *get_validated_aclint(SMB4ACL_T *theacl)
108 {
109         SMB_ACL4_INT_T *aclint = (SMB_ACL4_INT_T *)theacl;
110         if (theacl==NULL)
111         {
112                 DEBUG(2, ("acl is NULL\n"));
113                 errno = EINVAL;
114                 return NULL;
115         }
116         if (aclint->magic!=SMB_ACL4_INT_MAGIC)
117         {
118                 DEBUG(2, ("aclint bad magic 0x%x\n", aclint->magic));
119                 errno = EINVAL;
120                 return NULL;
121         }
122         return aclint;
123 }
124
125 static SMB_ACE4_INT_T *get_validated_aceint(SMB4ACE_T *ace)
126 {
127         SMB_ACE4_INT_T *aceint = (SMB_ACE4_INT_T *)ace;
128         if (ace==NULL)
129         {
130                 DEBUG(2, ("ace is NULL\n"));
131                 errno = EINVAL;
132                 return NULL;
133         }
134         if (aceint->magic!=SMB_ACE4_INT_MAGIC)
135         {
136                 DEBUG(2, ("aceint bad magic 0x%x\n", aceint->magic));
137                 errno = EINVAL;
138                 return NULL;
139         }
140         return aceint;
141 }
142
143 SMB4ACL_T *smb_create_smb4acl(void)
144 {
145         TALLOC_CTX *mem_ctx = talloc_tos();
146         SMB_ACL4_INT_T  *theacl = (SMB_ACL4_INT_T *)TALLOC_ZERO_SIZE(mem_ctx, sizeof(SMB_ACL4_INT_T));
147         if (theacl==NULL)
148         {
149                 DEBUG(0, ("TALLOC_SIZE failed\n"));
150                 errno = ENOMEM;
151                 return NULL;
152         }
153         theacl->magic = SMB_ACL4_INT_MAGIC;
154         /* theacl->first, last = NULL not needed */
155         return (SMB4ACL_T *)theacl;
156 }
157
158 SMB4ACE_T *smb_add_ace4(SMB4ACL_T *theacl, SMB_ACE4PROP_T *prop)
159 {
160         SMB_ACL4_INT_T *aclint = get_validated_aclint(theacl);
161         TALLOC_CTX *mem_ctx = talloc_tos();
162         SMB_ACE4_INT_T *ace;
163
164         ace = (SMB_ACE4_INT_T *)TALLOC_ZERO_SIZE(mem_ctx, sizeof(SMB_ACE4_INT_T));
165         if (ace==NULL)
166         {
167                 DEBUG(0, ("TALLOC_SIZE failed\n"));
168                 errno = ENOMEM;
169                 return NULL;
170         }
171         ace->magic = SMB_ACE4_INT_MAGIC;
172         /* ace->next = NULL not needed */
173         memcpy(&ace->prop, prop, sizeof(SMB_ACE4PROP_T));
174
175         if (aclint->first==NULL)
176         {
177                 aclint->first = ace;
178                 aclint->last = ace;
179         } else {
180                 aclint->last->next = (void *)ace;
181                 aclint->last = ace;
182         }
183         aclint->naces++;
184
185         return (SMB4ACE_T *)ace;
186 }
187
188 SMB_ACE4PROP_T *smb_get_ace4(SMB4ACE_T *ace)
189 {
190         SMB_ACE4_INT_T *aceint = get_validated_aceint(ace);
191         if (aceint==NULL)
192                 return NULL;
193
194         return &aceint->prop;
195 }
196
197 SMB4ACE_T *smb_next_ace4(SMB4ACE_T *ace)
198 {
199         SMB_ACE4_INT_T *aceint = get_validated_aceint(ace);
200         if (aceint==NULL)
201                 return NULL;
202
203         return (SMB4ACE_T *)aceint->next;
204 }
205
206 SMB4ACE_T *smb_first_ace4(SMB4ACL_T *theacl)
207 {
208         SMB_ACL4_INT_T *aclint = get_validated_aclint(theacl);
209         if (aclint==NULL)
210                 return NULL;
211
212         return (SMB4ACE_T *)aclint->first;
213 }
214
215 uint32 smb_get_naces(SMB4ACL_T *theacl)
216 {
217         SMB_ACL4_INT_T *aclint = get_validated_aclint(theacl);
218         if (aclint==NULL)
219                 return 0;
220
221         return aclint->naces;
222 }
223
224 static int smbacl4_GetFileOwner(struct connection_struct *conn,
225                                 const char *filename,
226                                 SMB_STRUCT_STAT *psbuf)
227 {
228         memset(psbuf, 0, sizeof(SMB_STRUCT_STAT));
229
230         /* Get the stat struct for the owner info. */
231         if (vfs_stat_smb_fname(conn, filename, psbuf) != 0)
232         {
233                 DEBUG(8, ("vfs_stat_smb_fname failed with error %s\n",
234                         strerror(errno)));
235                 return -1;
236         }
237
238         return 0;
239 }
240
241 static int smbacl4_fGetFileOwner(files_struct *fsp, SMB_STRUCT_STAT *psbuf)
242 {
243         memset(psbuf, 0, sizeof(SMB_STRUCT_STAT));
244
245         if (fsp->fh->fd == -1) {
246                 return smbacl4_GetFileOwner(fsp->conn,
247                                             fsp->fsp_name->base_name, psbuf);
248         }
249         if (SMB_VFS_FSTAT(fsp, psbuf) != 0)
250         {
251                 DEBUG(8, ("SMB_VFS_FSTAT failed with error %s\n",
252                         strerror(errno)));
253                 return -1;
254         }
255
256         return 0;
257 }
258
259 static bool smbacl4_nfs42win(TALLOC_CTX *mem_ctx, SMB4ACL_T *theacl, /* in */
260         struct dom_sid *psid_owner, /* in */
261         struct dom_sid *psid_group, /* in */
262         bool is_directory, /* in */
263         struct security_ace **ppnt_ace_list, /* out */
264         int *pgood_aces /* out */
265 )
266 {
267         SMB_ACL4_INT_T *aclint = (SMB_ACL4_INT_T *)theacl;
268         SMB_ACE4_INT_T *aceint;
269         struct security_ace *nt_ace_list = NULL;
270         int good_aces = 0;
271
272         DEBUG(10, ("smbacl_nfs42win entered\n"));
273
274         aclint = get_validated_aclint(theacl);
275         /* We do not check for naces being 0 or theacl being NULL here because it is done upstream */
276         /* in smb_get_nt_acl_nfs4(). */
277         nt_ace_list = (struct security_ace *)TALLOC_ZERO_SIZE(mem_ctx, aclint->naces * sizeof(struct security_ace));
278         if (nt_ace_list==NULL)
279         {
280                 DEBUG(10, ("talloc error"));
281                 errno = ENOMEM;
282                 return False;
283         }
284
285         for (aceint=aclint->first; aceint!=NULL; aceint=(SMB_ACE4_INT_T *)aceint->next) {
286                 uint32_t mask;
287                 struct dom_sid sid;
288                 SMB_ACE4PROP_T  *ace = &aceint->prop;
289                 uint32_t win_ace_flags;
290
291                 DEBUG(10, ("magic: 0x%x, type: %d, iflags: %x, flags: %x, mask: %x, "
292                         "who: %d\n", aceint->magic, ace->aceType, ace->flags,
293                         ace->aceFlags, ace->aceMask, ace->who.id));
294
295                 SMB_ASSERT(aceint->magic==SMB_ACE4_INT_MAGIC);
296
297                 if (ace->flags & SMB_ACE4_ID_SPECIAL) {
298                         switch (ace->who.special_id) {
299                         case SMB_ACE4_WHO_OWNER:
300                                 sid_copy(&sid, psid_owner);
301                                 break;
302                         case SMB_ACE4_WHO_GROUP:
303                                 sid_copy(&sid, psid_group);
304                                 break;
305                         case SMB_ACE4_WHO_EVERYONE:
306                                 sid_copy(&sid, &global_sid_World);
307                                 break;
308                         default:
309                                 DEBUG(8, ("invalid special who id %d "
310                                         "ignored\n", ace->who.special_id));
311                         }
312                 } else {
313                         if (ace->aceFlags & SMB_ACE4_IDENTIFIER_GROUP) {
314                                 gid_to_sid(&sid, ace->who.gid);
315                         } else {
316                                 uid_to_sid(&sid, ace->who.uid);
317                         }
318                 }
319                 DEBUG(10, ("mapped %d to %s\n", ace->who.id,
320                            sid_string_dbg(&sid)));
321
322                 if (is_directory && (ace->aceMask & SMB_ACE4_ADD_FILE)) {
323                         ace->aceMask |= SMB_ACE4_DELETE_CHILD;
324                 }
325
326                 win_ace_flags = map_nfs4_ace_flags_to_windows_ace_flags(ace->aceFlags);
327                 if (!is_directory && (win_ace_flags & (SEC_ACE_FLAG_OBJECT_INHERIT|SEC_ACE_FLAG_CONTAINER_INHERIT))) {
328                         /*
329                          * GPFS sets inherits dir_inhert and file_inherit flags
330                          * to files, too, which confuses windows, and seems to
331                          * be wrong anyways. ==> Map these bits away for files.
332                          */
333                         DEBUG(10, ("removing inherit flags from nfs4 ace\n"));
334                         win_ace_flags &= ~(SEC_ACE_FLAG_OBJECT_INHERIT|SEC_ACE_FLAG_CONTAINER_INHERIT);
335                 }
336                 DEBUG(10, ("Windows mapped ace flags: 0x%x => 0x%x\n",
337                       ace->aceFlags, win_ace_flags));
338
339                 /* Windows clients expect SYNC on acls to
340                    correctly allow rename. See bug #7909. */
341                 mask = ace->aceMask | SMB_ACE4_SYNCHRONIZE;
342                 init_sec_ace(&nt_ace_list[good_aces++], &sid,
343                         ace->aceType, mask,
344                         win_ace_flags);
345         }
346
347         *ppnt_ace_list = nt_ace_list;
348         *pgood_aces = good_aces;
349
350         return True;
351 }
352
353 static NTSTATUS smb_get_nt_acl_nfs4_common(const SMB_STRUCT_STAT *sbuf,
354         uint32 security_info,
355         struct security_descriptor **ppdesc, SMB4ACL_T *theacl)
356 {
357         int     good_aces = 0;
358         struct dom_sid sid_owner, sid_group;
359         size_t sd_size = 0;
360         struct security_ace *nt_ace_list = NULL;
361         struct security_acl *psa = NULL;
362         TALLOC_CTX *mem_ctx = talloc_tos();
363
364         if (theacl==NULL || smb_get_naces(theacl)==0)
365                 return NT_STATUS_ACCESS_DENIED; /* special because we
366                                                  * shouldn't alloc 0 for
367                                                  * win */
368
369         uid_to_sid(&sid_owner, sbuf->st_ex_uid);
370         gid_to_sid(&sid_group, sbuf->st_ex_gid);
371
372         if (smbacl4_nfs42win(mem_ctx, theacl, &sid_owner, &sid_group,
373                              S_ISDIR(sbuf->st_ex_mode),
374                                 &nt_ace_list, &good_aces)==False) {
375                 DEBUG(8,("smbacl4_nfs42win failed\n"));
376                 return map_nt_error_from_unix(errno);
377         }
378
379         psa = make_sec_acl(mem_ctx, NT4_ACL_REVISION, good_aces, nt_ace_list);
380         if (psa == NULL) {
381                 DEBUG(2,("make_sec_acl failed\n"));
382                 return NT_STATUS_NO_MEMORY;
383         }
384
385         DEBUG(10,("after make sec_acl\n"));
386         *ppdesc = make_sec_desc(mem_ctx, SD_REVISION, SEC_DESC_SELF_RELATIVE,
387                                 (security_info & SECINFO_OWNER) ? &sid_owner : NULL,
388                                 (security_info & SECINFO_GROUP) ? &sid_group : NULL,
389                                 NULL, psa, &sd_size);
390         if (*ppdesc==NULL) {
391                 DEBUG(2,("make_sec_desc failed\n"));
392                 return NT_STATUS_NO_MEMORY;
393         }
394
395         DEBUG(10, ("smb_get_nt_acl_nfs4_common successfully exited with sd_size %d\n",
396                    (int)ndr_size_security_descriptor(*ppdesc, 0)));
397
398         return NT_STATUS_OK;
399 }
400
401 NTSTATUS smb_fget_nt_acl_nfs4(files_struct *fsp,
402                                uint32 security_info,
403                                struct security_descriptor **ppdesc, SMB4ACL_T *theacl)
404 {
405         SMB_STRUCT_STAT sbuf;
406
407         DEBUG(10, ("smb_fget_nt_acl_nfs4 invoked for %s\n", fsp_str_dbg(fsp)));
408
409         if (smbacl4_fGetFileOwner(fsp, &sbuf)) {
410                 return map_nt_error_from_unix(errno);
411         }
412
413         return smb_get_nt_acl_nfs4_common(&sbuf, security_info, ppdesc, theacl);
414 }
415
416 NTSTATUS smb_get_nt_acl_nfs4(struct connection_struct *conn,
417                               const char *name,
418                               uint32 security_info,
419                               struct security_descriptor **ppdesc, SMB4ACL_T *theacl)
420 {
421         SMB_STRUCT_STAT sbuf;
422
423         DEBUG(10, ("smb_get_nt_acl_nfs4 invoked for %s\n", name));
424
425         if (smbacl4_GetFileOwner(conn, name, &sbuf)) {
426                 return map_nt_error_from_unix(errno);
427         }
428
429         return smb_get_nt_acl_nfs4_common(&sbuf, security_info, ppdesc, theacl);
430 }
431
432 enum smbacl4_mode_enum {e_simple=0, e_special=1};
433 enum smbacl4_acedup_enum {e_dontcare=0, e_reject=1, e_ignore=2, e_merge=3};
434
435 typedef struct _smbacl4_vfs_params {
436         enum smbacl4_mode_enum mode;
437         bool do_chown;
438         enum smbacl4_acedup_enum acedup;
439         struct db_context *sid_mapping_table;
440 } smbacl4_vfs_params;
441
442 /*
443  * Gather special parameters for NFS4 ACL handling
444  */
445 static int smbacl4_get_vfs_params(
446         const char *type_name,
447         files_struct *fsp,
448         smbacl4_vfs_params *params
449 )
450 {
451         static const struct enum_list enum_smbacl4_modes[] = {
452                 { e_simple, "simple" },
453                 { e_special, "special" },
454                 { -1 , NULL }
455         };
456         static const struct enum_list enum_smbacl4_acedups[] = {
457                 { e_dontcare, "dontcare" },
458                 { e_reject, "reject" },
459                 { e_ignore, "ignore" },
460                 { e_merge, "merge" },
461                 { -1 , NULL }
462         };
463
464         memset(params, 0, sizeof(smbacl4_vfs_params));
465         params->mode = (enum smbacl4_mode_enum)lp_parm_enum(
466                 SNUM(fsp->conn), type_name,
467                 "mode", enum_smbacl4_modes, e_simple);
468         params->do_chown = lp_parm_bool(SNUM(fsp->conn), type_name,
469                 "chown", True);
470         params->acedup = (enum smbacl4_acedup_enum)lp_parm_enum(
471                 SNUM(fsp->conn), type_name,
472                 "acedup", enum_smbacl4_acedups, e_dontcare);
473
474         DEBUG(10, ("mode:%s, do_chown:%s, acedup: %s\n",
475                 enum_smbacl4_modes[params->mode].name,
476                 params->do_chown ? "true" : "false",
477                 enum_smbacl4_acedups[params->acedup].name));
478
479         return 0;
480 }
481
482 static void smbacl4_dump_nfs4acl(int level, SMB4ACL_T *theacl)
483 {
484         SMB_ACL4_INT_T *aclint = get_validated_aclint(theacl);
485         SMB_ACE4_INT_T *aceint;
486
487         DEBUG(level, ("NFS4ACL: size=%d\n", aclint->naces));
488
489         for(aceint = aclint->first; aceint!=NULL; aceint=(SMB_ACE4_INT_T *)aceint->next) {
490                 SMB_ACE4PROP_T *ace = &aceint->prop;
491
492                 DEBUG(level, ("\tACE: type=%d, flags=0x%x, fflags=0x%x, mask=0x%x, id=%d\n",
493                         ace->aceType,
494                         ace->aceFlags, ace->flags,
495                         ace->aceMask,
496                         ace->who.id));
497         }
498 }
499
500 /* 
501  * Find 2 NFS4 who-special ACE property (non-copy!!!)
502  * match nonzero if "special" and who is equal
503  * return ace if found matching; otherwise NULL
504  */
505 static SMB_ACE4PROP_T *smbacl4_find_equal_special(
506         SMB4ACL_T *theacl,
507         SMB_ACE4PROP_T *aceNew)
508 {
509         SMB_ACL4_INT_T *aclint = get_validated_aclint(theacl);
510         SMB_ACE4_INT_T *aceint;
511
512         for(aceint = aclint->first; aceint!=NULL; aceint=(SMB_ACE4_INT_T *)aceint->next) {
513                 SMB_ACE4PROP_T *ace = &aceint->prop;
514
515                 DEBUG(10,("ace type:0x%x flags:0x%x aceFlags:0x%x "
516                           "new type:0x%x flags:0x%x aceFlags:0x%x\n",
517                           ace->aceType, ace->flags, ace->aceFlags,
518                           aceNew->aceType, aceNew->flags,aceNew->aceFlags));
519
520                 if (ace->flags == aceNew->flags &&
521                         ace->aceType==aceNew->aceType &&
522                         ace->aceFlags==aceNew->aceFlags)
523                 {
524                         /* keep type safety; e.g. gid is an u.short */
525                         if (ace->flags & SMB_ACE4_ID_SPECIAL)
526                         {
527                                 if (ace->who.special_id==aceNew->who.special_id)
528                                         return ace;
529                         } else {
530                                 if (ace->aceFlags & SMB_ACE4_IDENTIFIER_GROUP)
531                                 {
532                                         if (ace->who.gid==aceNew->who.gid)
533                                                 return ace;
534                                 } else {
535                                         if (ace->who.uid==aceNew->who.uid)
536                                                 return ace;
537                                 }
538                         }
539                 }
540         }
541
542         return NULL;
543 }
544
545 static bool nfs4_map_sid(smbacl4_vfs_params *params, const struct dom_sid *src,
546                          struct dom_sid *dst)
547 {
548         static struct db_context *mapping_db = NULL;
549         TDB_DATA data;
550         
551         if (mapping_db == NULL) {
552                 const char *dbname = lp_parm_const_string(
553                         -1, SMBACL4_PARAM_TYPE_NAME, "sidmap", NULL);
554                 
555                 if (dbname == NULL) {
556                         DEBUG(10, ("%s:sidmap not defined\n",
557                                    SMBACL4_PARAM_TYPE_NAME));
558                         return False;
559                 }
560                 
561                 become_root();
562                 mapping_db = db_open(NULL, dbname, 0, TDB_DEFAULT,
563                                      O_RDONLY, 0600);
564                 unbecome_root();
565                 
566                 if (mapping_db == NULL) {
567                         DEBUG(1, ("could not open sidmap: %s\n",
568                                   strerror(errno)));
569                         return False;
570                 }
571         }
572         
573         if (mapping_db->fetch(mapping_db, NULL,
574                               string_term_tdb_data(sid_string_tos(src)),
575                               &data) != 0) {
576                 DEBUG(10, ("could not find mapping for SID %s\n",
577                            sid_string_dbg(src)));
578                 return False;
579         }
580         
581         if ((data.dptr == NULL) || (data.dsize <= 0)
582             || (data.dptr[data.dsize-1] != '\0')) {
583                 DEBUG(5, ("invalid mapping for SID %s\n",
584                           sid_string_dbg(src)));
585                 TALLOC_FREE(data.dptr);
586                 return False;
587         }
588         
589         if (!string_to_sid(dst, (char *)data.dptr)) {
590                 DEBUG(1, ("invalid mapping %s for SID %s\n",
591                           (char *)data.dptr, sid_string_dbg(src)));
592                 TALLOC_FREE(data.dptr);
593                 return False;
594         }
595
596         TALLOC_FREE(data.dptr);
597         
598         return True;
599 }
600
601 static bool smbacl4_fill_ace4(
602         TALLOC_CTX *mem_ctx,
603         const char *filename,
604         smbacl4_vfs_params *params,
605         uid_t ownerUID,
606         gid_t ownerGID,
607         const struct security_ace *ace_nt, /* input */
608         SMB_ACE4PROP_T *ace_v4 /* output */
609 )
610 {
611         DEBUG(10, ("got ace for %s\n", sid_string_dbg(&ace_nt->trustee)));
612
613         memset(ace_v4, 0, sizeof(SMB_ACE4PROP_T));
614         ace_v4->aceType = ace_nt->type; /* only ACCESS|DENY supported right now */
615         ace_v4->aceFlags = map_windows_ace_flags_to_nfs4_ace_flags(ace_nt->flags);
616         ace_v4->aceMask = ace_nt->access_mask &
617                 (SEC_STD_ALL | SEC_FILE_ALL);
618
619         se_map_generic(&ace_v4->aceMask, &file_generic_mapping);
620
621         if (ace_v4->aceFlags!=ace_nt->flags)
622                 DEBUG(9, ("ace_v4->aceFlags(0x%x)!=ace_nt->flags(0x%x)\n",
623                         ace_v4->aceFlags, ace_nt->flags));
624
625         if (ace_v4->aceMask!=ace_nt->access_mask)
626                 DEBUG(9, ("ace_v4->aceMask(0x%x)!=ace_nt->access_mask(0x%x)\n",
627                         ace_v4->aceMask, ace_nt->access_mask));
628
629         if (dom_sid_equal(&ace_nt->trustee, &global_sid_World)) {
630                 ace_v4->who.special_id = SMB_ACE4_WHO_EVERYONE;
631                 ace_v4->flags |= SMB_ACE4_ID_SPECIAL;
632         } else {
633                 const char *dom, *name;
634                 enum lsa_SidType type;
635                 uid_t uid;
636                 gid_t gid;
637                 struct dom_sid sid;
638                 
639                 sid_copy(&sid, &ace_nt->trustee);
640                 
641                 if (!lookup_sid(mem_ctx, &sid, &dom, &name, &type)) {
642                         
643                         struct dom_sid mapped;
644                         
645                         if (!nfs4_map_sid(params, &sid, &mapped)) {
646                                 DEBUG(1, ("nfs4_acls.c: file [%s]: SID %s "
647                                           "unknown\n", filename, sid_string_dbg(&sid)));
648                                 errno = EINVAL;
649                                 return False;
650                         }
651                         
652                         DEBUG(2, ("nfs4_acls.c: file [%s]: mapped SID %s "
653                                   "to %s\n", filename, sid_string_dbg(&sid), sid_string_dbg(&mapped)));
654                         
655                         if (!lookup_sid(mem_ctx, &mapped, &dom,
656                                         &name, &type)) {
657                                 DEBUG(1, ("nfs4_acls.c: file [%s]: SID %s "
658                                           "mapped from %s is unknown\n",
659                                           filename, sid_string_dbg(&mapped), sid_string_dbg(&sid)));
660                                 errno = EINVAL;
661                                 return False;
662                         }
663                         
664                         sid_copy(&sid, &mapped);
665                 }
666                 
667                 if (type == SID_NAME_USER) {
668                         if (!sid_to_uid(&sid, &uid)) {
669                                 DEBUG(1, ("nfs4_acls.c: file [%s]: could not "
670                                           "convert %s to uid\n", filename,
671                                           sid_string_dbg(&sid)));
672                                 return False;
673                         }
674
675                         if (params->mode==e_special && uid==ownerUID) {
676                                 ace_v4->flags |= SMB_ACE4_ID_SPECIAL;
677                                 ace_v4->who.special_id = SMB_ACE4_WHO_OWNER;
678                         } else {
679                                 ace_v4->who.uid = uid;
680                         }
681                 } else { /* else group? - TODO check it... */
682                         if (!sid_to_gid(&sid, &gid)) {
683                                 DEBUG(1, ("nfs4_acls.c: file [%s]: could not "
684                                           "convert %s to gid\n", filename,
685                                           sid_string_dbg(&sid)));
686                                 return False;
687                         }
688                                 
689                         ace_v4->aceFlags |= SMB_ACE4_IDENTIFIER_GROUP;
690
691                         if (params->mode==e_special && gid==ownerGID) {
692                                 ace_v4->flags |= SMB_ACE4_ID_SPECIAL;
693                                 ace_v4->who.special_id = SMB_ACE4_WHO_GROUP;
694                         } else {
695                                 ace_v4->who.gid = gid;
696                         }
697                 }
698         }
699
700         return True; /* OK */
701 }
702
703 static int smbacl4_MergeIgnoreReject(
704         enum smbacl4_acedup_enum acedup,
705         SMB4ACL_T *theacl, /* may modify it */
706         SMB_ACE4PROP_T *ace, /* the "new" ACE */
707         bool    *paddNewACE,
708         int     i
709 )
710 {
711         int     result = 0;
712         SMB_ACE4PROP_T *ace4found = smbacl4_find_equal_special(theacl, ace);
713         if (ace4found)
714         {
715                 switch(acedup)
716                 {
717                 case e_merge: /* "merge" flags */
718                         *paddNewACE = False;
719                         ace4found->aceFlags |= ace->aceFlags;
720                         ace4found->aceMask |= ace->aceMask;
721                         break;
722                 case e_ignore: /* leave out this record */
723                         *paddNewACE = False;
724                         break;
725                 case e_reject: /* do an error */
726                         DEBUG(8, ("ACL rejected by duplicate nt ace#%d\n", i));
727                         errno = EINVAL; /* SHOULD be set on any _real_ error */
728                         result = -1;
729                         break;
730                 default:
731                         break;
732                 }
733         }
734         return result;
735 }
736
737 static SMB4ACL_T *smbacl4_win2nfs4(
738         const char *filename,
739         const struct security_acl *dacl,
740         smbacl4_vfs_params *pparams,
741         uid_t ownerUID,
742         gid_t ownerGID
743 )
744 {
745         SMB4ACL_T *theacl;
746         uint32  i;
747         TALLOC_CTX *mem_ctx = talloc_tos();
748
749         DEBUG(10, ("smbacl4_win2nfs4 invoked\n"));
750
751         theacl = smb_create_smb4acl();
752         if (theacl==NULL)
753                 return NULL;
754
755         for(i=0; i<dacl->num_aces; i++) {
756                 SMB_ACE4PROP_T  ace_v4;
757                 bool    addNewACE = True;
758
759                 if (!smbacl4_fill_ace4(mem_ctx, filename, pparams,
760                                        ownerUID, ownerGID,
761                                        dacl->aces + i, &ace_v4)) {
762                         DEBUG(3, ("Could not fill ace for file %s, SID %s\n",
763                                   filename,
764                                   sid_string_dbg(&((dacl->aces+i)->trustee))));
765                         continue;
766                 }
767
768                 if (pparams->acedup!=e_dontcare) {
769                         if (smbacl4_MergeIgnoreReject(pparams->acedup, theacl,
770                                 &ace_v4, &addNewACE, i))
771                                 return NULL;
772                 }
773
774                 if (addNewACE)
775                         smb_add_ace4(theacl, &ace_v4);
776         }
777
778         return theacl;
779 }
780
781 NTSTATUS smb_set_nt_acl_nfs4(files_struct *fsp,
782         uint32 security_info_sent,
783         const struct security_descriptor *psd,
784         set_nfs4acl_native_fn_t set_nfs4_native)
785 {
786         smbacl4_vfs_params params;
787         SMB4ACL_T *theacl = NULL;
788         bool    result;
789
790         SMB_STRUCT_STAT sbuf;
791         bool set_acl_as_root = false;
792         uid_t newUID = (uid_t)-1;
793         gid_t newGID = (gid_t)-1;
794         int saved_errno;
795
796         DEBUG(10, ("smb_set_nt_acl_nfs4 invoked for %s\n", fsp_str_dbg(fsp)));
797
798         if ((security_info_sent & (SECINFO_DACL |
799                 SECINFO_GROUP | SECINFO_OWNER)) == 0)
800         {
801                 DEBUG(9, ("security_info_sent (0x%x) ignored\n",
802                         security_info_sent));
803                 return NT_STATUS_OK; /* won't show error - later to be refined... */
804         }
805
806         /* Special behaviours */
807         if (smbacl4_get_vfs_params(SMBACL4_PARAM_TYPE_NAME, fsp, &params))
808                 return NT_STATUS_NO_MEMORY;
809
810         if (smbacl4_fGetFileOwner(fsp, &sbuf))
811                 return map_nt_error_from_unix(errno);
812
813         if (params.do_chown) {
814                 /* chown logic is a copy/paste from posix_acl.c:set_nt_acl */
815                 NTSTATUS status = unpack_nt_owners(fsp->conn, &newUID, &newGID, security_info_sent, psd);
816                 if (!NT_STATUS_IS_OK(status)) {
817                         DEBUG(8, ("unpack_nt_owners failed"));
818                         return status;
819                 }
820                 if (((newUID != (uid_t)-1) && (sbuf.st_ex_uid != newUID)) ||
821                     ((newGID != (gid_t)-1) && (sbuf.st_ex_gid != newGID))) {
822
823                         status = try_chown(fsp, newUID, newGID);
824                         if (!NT_STATUS_IS_OK(status)) {
825                                 DEBUG(3,("chown %s, %u, %u failed. Error = "
826                                          "%s.\n", fsp_str_dbg(fsp),
827                                          (unsigned int)newUID,
828                                          (unsigned int)newGID,
829                                          nt_errstr(status)));
830                                 return status;
831                         }
832
833                         DEBUG(10,("chown %s, %u, %u succeeded.\n",
834                                   fsp_str_dbg(fsp), (unsigned int)newUID,
835                                   (unsigned int)newGID));
836                         if (smbacl4_GetFileOwner(fsp->conn,
837                                                  fsp->fsp_name->base_name,
838                                                  &sbuf))
839                                 return map_nt_error_from_unix(errno);
840
841                         /* If we successfully chowned, we know we must
842                          * be able to set the acl, so do it as root.
843                          */
844                         set_acl_as_root = true;
845                 }
846         }
847
848         if (!(security_info_sent & SECINFO_DACL) || psd->dacl ==NULL) {
849                 DEBUG(10, ("no dacl found; security_info_sent = 0x%x\n", security_info_sent));
850                 return NT_STATUS_OK;
851         }
852
853         theacl = smbacl4_win2nfs4(fsp->fsp_name->base_name, psd->dacl, &params,
854                                   sbuf.st_ex_uid, sbuf.st_ex_gid);
855         if (!theacl)
856                 return map_nt_error_from_unix(errno);
857
858         smbacl4_dump_nfs4acl(10, theacl);
859
860         if (set_acl_as_root) {
861                 become_root();
862         }
863         result = set_nfs4_native(fsp, theacl);
864         saved_errno = errno;
865         if (set_acl_as_root) {
866                 unbecome_root();
867         }
868         if (result!=True) {
869                 errno = saved_errno;
870                 DEBUG(10, ("set_nfs4_native failed with %s\n", strerror(errno)));
871                 return map_nt_error_from_unix(errno);
872         }
873
874         DEBUG(10, ("smb_set_nt_acl_nfs4 succeeded\n"));
875         return NT_STATUS_OK;
876 }