*id_to_*id call reshape to return NTSTATUS errors
[ira/wip.git] / source3 / smbd / posix_acls.c
1 /*
2    Unix SMB/CIFS implementation.
3    SMB NT Security Descriptor / Unix permission conversion.
4    Copyright (C) Jeremy Allison 1994-2000.
5    Copyright (C) Andreas Gruenbacher 2002.
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 2 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, write to the Free Software
19    Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA.
20 */
21
22 #include "includes.h"
23
24 /****************************************************************************
25  Data structures representing the internal ACE format.
26 ****************************************************************************/
27
28 enum ace_owner {UID_ACE, GID_ACE, WORLD_ACE};
29 enum ace_attribute {ALLOW_ACE, DENY_ACE}; /* Used for incoming NT ACLS. */
30
31 typedef union posix_id {
32                 uid_t uid;
33                 gid_t gid;
34                 int world;
35 } posix_id;
36
37 typedef struct canon_ace {
38         struct canon_ace *next, *prev;
39         SMB_ACL_TAG_T type;
40         mode_t perms; /* Only use S_I(R|W|X)USR mode bits here. */
41         DOM_SID trustee;
42         enum ace_owner owner_type;
43         enum ace_attribute attr;
44         posix_id unix_ug; 
45 } canon_ace;
46
47 #define ALL_ACE_PERMS (S_IRUSR|S_IWUSR|S_IXUSR)
48
49 /****************************************************************************
50  Functions to manipulate the internal ACE format.
51 ****************************************************************************/
52
53 /****************************************************************************
54  Count a linked list of canonical ACE entries.
55 ****************************************************************************/
56
57 static size_t count_canon_ace_list( canon_ace *list_head )
58 {
59         size_t count = 0;
60         canon_ace *ace;
61
62         for (ace = list_head; ace; ace = ace->next)
63                 count++;
64
65         return count;
66 }
67
68 /****************************************************************************
69  Free a linked list of canonical ACE entries.
70 ****************************************************************************/
71
72 static void free_canon_ace_list( canon_ace *list_head )
73 {
74         while (list_head) {
75                 canon_ace *old_head = list_head;
76                 DLIST_REMOVE(list_head, list_head);
77                 SAFE_FREE(old_head);
78         }
79 }
80
81 /****************************************************************************
82  Function to duplicate a canon_ace entry.
83 ****************************************************************************/
84
85 static canon_ace *dup_canon_ace( canon_ace *src_ace)
86 {
87         canon_ace *dst_ace = (canon_ace *)malloc(sizeof(canon_ace));
88
89         if (dst_ace == NULL)
90                 return NULL;
91
92         *dst_ace = *src_ace;
93         dst_ace->prev = dst_ace->next = NULL;
94         return dst_ace;
95 }
96
97 /****************************************************************************
98  Print out a canon ace.
99 ****************************************************************************/
100
101 static void print_canon_ace(canon_ace *pace, int num)
102 {
103         fstring str;
104
105         dbgtext( "canon_ace index %d. Type = %s ", num, pace->attr == ALLOW_ACE ? "allow" : "deny" );
106         dbgtext( "SID = %s ", sid_to_string( str, &pace->trustee));
107         if (pace->owner_type == UID_ACE) {
108                 const char *u_name = uidtoname(pace->unix_ug.uid);
109                 dbgtext( "uid %u (%s) ", (unsigned int)pace->unix_ug.uid, u_name);
110         } else if (pace->owner_type == GID_ACE) {
111                 char *g_name = gidtoname(pace->unix_ug.gid);
112                 dbgtext( "gid %u (%s) ", (unsigned int)pace->unix_ug.gid, g_name);
113         } else
114                 dbgtext( "other ");
115         switch (pace->type) {
116                 case SMB_ACL_USER:
117                         dbgtext( "SMB_ACL_USER ");
118                         break;
119                 case SMB_ACL_USER_OBJ:
120                         dbgtext( "SMB_ACL_USER_OBJ ");
121                         break;
122                 case SMB_ACL_GROUP:
123                         dbgtext( "SMB_ACL_GROUP ");
124                         break;
125                 case SMB_ACL_GROUP_OBJ:
126                         dbgtext( "SMB_ACL_GROUP_OBJ ");
127                         break;
128                 case SMB_ACL_OTHER:
129                         dbgtext( "SMB_ACL_OTHER ");
130                         break;
131         }
132         dbgtext( "perms ");
133         dbgtext( "%c", pace->perms & S_IRUSR ? 'r' : '-');
134         dbgtext( "%c", pace->perms & S_IWUSR ? 'w' : '-');
135         dbgtext( "%c\n", pace->perms & S_IXUSR ? 'x' : '-');
136 }
137
138 /****************************************************************************
139  Print out a canon ace list.
140 ****************************************************************************/
141
142 static void print_canon_ace_list(const char *name, canon_ace *ace_list)
143 {
144         int count = 0;
145
146         if( DEBUGLVL( 10 )) {
147                 dbgtext( "print_canon_ace_list: %s\n", name );
148                 for (;ace_list; ace_list = ace_list->next, count++)
149                         print_canon_ace(ace_list, count );
150         }
151 }
152
153 /****************************************************************************
154  Map POSIX ACL perms to canon_ace permissions (a mode_t containing only S_(R|W|X)USR bits).
155 ****************************************************************************/
156
157 static mode_t convert_permset_to_mode_t(connection_struct *conn, SMB_ACL_PERMSET_T permset)
158 {
159         mode_t ret = 0;
160
161         ret |= (conn->vfs_ops.sys_acl_get_perm(conn, permset, SMB_ACL_READ) ? S_IRUSR : 0);
162         ret |= (conn->vfs_ops.sys_acl_get_perm(conn, permset, SMB_ACL_WRITE) ? S_IWUSR : 0);
163         ret |= (conn->vfs_ops.sys_acl_get_perm(conn, permset, SMB_ACL_EXECUTE) ? S_IXUSR : 0);
164
165         return ret;
166 }
167
168 /****************************************************************************
169  Map generic UNIX permissions to canon_ace permissions (a mode_t containing only S_(R|W|X)USR bits).
170 ****************************************************************************/
171
172 static mode_t unix_perms_to_acl_perms(mode_t mode, int r_mask, int w_mask, int x_mask)
173 {
174         mode_t ret = 0;
175
176         if (mode & r_mask)
177                 ret |= S_IRUSR;
178         if (mode & w_mask)
179                 ret |= S_IWUSR;
180         if (mode & x_mask)
181                 ret |= S_IXUSR;
182
183         return ret;
184 }
185
186 /****************************************************************************
187  Map canon_ace permissions (a mode_t containing only S_(R|W|X)USR bits) to
188  an SMB_ACL_PERMSET_T.
189 ****************************************************************************/
190
191 static int map_acl_perms_to_permset(connection_struct *conn, mode_t mode, SMB_ACL_PERMSET_T *p_permset)
192 {
193         if (conn->vfs_ops.sys_acl_clear_perms(conn, *p_permset) ==  -1)
194                 return -1;
195         if (mode & S_IRUSR) {
196                 if (conn->vfs_ops.sys_acl_add_perm(conn, *p_permset, SMB_ACL_READ) == -1)
197                         return -1;
198         }
199         if (mode & S_IWUSR) {
200                 if (conn->vfs_ops.sys_acl_add_perm(conn, *p_permset, SMB_ACL_WRITE) == -1)
201                         return -1;
202         }
203         if (mode & S_IXUSR) {
204                 if (conn->vfs_ops.sys_acl_add_perm(conn, *p_permset, SMB_ACL_EXECUTE) == -1)
205                         return -1;
206         }
207         return 0;
208 }
209 /****************************************************************************
210  Function to create owner and group SIDs from a SMB_STRUCT_STAT.
211 ****************************************************************************/
212
213 static void create_file_sids(SMB_STRUCT_STAT *psbuf, DOM_SID *powner_sid, DOM_SID *pgroup_sid)
214 {
215         uid_to_sid( powner_sid, psbuf->st_uid );
216         gid_to_sid( pgroup_sid, psbuf->st_gid );
217 }
218
219 /****************************************************************************
220  Merge aces with a common sid - if both are allow or deny, OR the permissions together and
221  delete the second one. If the first is deny, mask the permissions off and delete the allow
222  if the permissions become zero, delete the deny if the permissions are non zero.
223 ****************************************************************************/
224
225 static void merge_aces( canon_ace **pp_list_head )
226 {
227         canon_ace *list_head = *pp_list_head;
228         canon_ace *curr_ace_outer;
229         canon_ace *curr_ace_outer_next;
230
231         /*
232          * First, merge allow entries with identical SIDs, and deny entries
233          * with identical SIDs.
234          */
235
236         for (curr_ace_outer = list_head; curr_ace_outer; curr_ace_outer = curr_ace_outer_next) {
237                 canon_ace *curr_ace;
238                 canon_ace *curr_ace_next;
239
240                 curr_ace_outer_next = curr_ace_outer->next; /* Save the link in case we delete. */
241
242                 for (curr_ace = curr_ace_outer->next; curr_ace; curr_ace = curr_ace_next) {
243
244                         curr_ace_next = curr_ace->next; /* Save the link in case of delete. */
245
246                         if (sid_equal(&curr_ace->trustee, &curr_ace_outer->trustee) &&
247                                 (curr_ace->attr == curr_ace_outer->attr)) {
248
249                                 if( DEBUGLVL( 10 )) {
250                                         dbgtext("merge_aces: Merging ACE's\n");
251                                         print_canon_ace( curr_ace_outer, 0);
252                                         print_canon_ace( curr_ace, 0);
253                                 }
254
255                                 /* Merge two allow or two deny ACE's. */
256
257                                 curr_ace_outer->perms |= curr_ace->perms;
258                                 DLIST_REMOVE(list_head, curr_ace);
259                                 SAFE_FREE(curr_ace);
260                                 curr_ace_outer_next = curr_ace_outer->next; /* We may have deleted the link. */
261                         }
262                 }
263         }
264
265         /*
266          * Now go through and mask off allow permissions with deny permissions.
267          * We can delete either the allow or deny here as we know that each SID
268          * appears only once in the list.
269          */
270
271         for (curr_ace_outer = list_head; curr_ace_outer; curr_ace_outer = curr_ace_outer_next) {
272                 canon_ace *curr_ace;
273                 canon_ace *curr_ace_next;
274
275                 curr_ace_outer_next = curr_ace_outer->next; /* Save the link in case we delete. */
276
277                 for (curr_ace = curr_ace_outer->next; curr_ace; curr_ace = curr_ace_next) {
278
279                         curr_ace_next = curr_ace->next; /* Save the link in case of delete. */
280
281                         /*
282                          * Subtract ACE's with different entries. Due to the ordering constraints
283                          * we've put on the ACL, we know the deny must be the first one.
284                          */
285
286                         if (sid_equal(&curr_ace->trustee, &curr_ace_outer->trustee) &&
287                                 (curr_ace_outer->attr == DENY_ACE) && (curr_ace->attr == ALLOW_ACE)) {
288
289                                 if( DEBUGLVL( 10 )) {
290                                         dbgtext("merge_aces: Masking ACE's\n");
291                                         print_canon_ace( curr_ace_outer, 0);
292                                         print_canon_ace( curr_ace, 0);
293                                 }
294
295                                 curr_ace->perms &= ~curr_ace_outer->perms;
296
297                                 if (curr_ace->perms == 0) {
298
299                                         /*
300                                          * The deny overrides the allow. Remove the allow.
301                                          */
302
303                                         DLIST_REMOVE(list_head, curr_ace);
304                                         SAFE_FREE(curr_ace);
305                                         curr_ace_outer_next = curr_ace_outer->next; /* We may have deleted the link. */
306
307                                 } else {
308
309                                         /*
310                                          * Even after removing permissions, there
311                                          * are still allow permissions - delete the deny.
312                                          * It is safe to delete the deny here,
313                                          * as we are guarenteed by the deny first
314                                          * ordering that all the deny entries for
315                                          * this SID have already been merged into one
316                                          * before we can get to an allow ace.
317                                          */
318
319                                         DLIST_REMOVE(list_head, curr_ace_outer);
320                                         SAFE_FREE(curr_ace_outer);
321                                         break;
322                                 }
323                         }
324
325                 } /* end for curr_ace */
326         } /* end for curr_ace_outer */
327
328         /* We may have modified the list. */
329
330         *pp_list_head = list_head;
331 }
332
333 /****************************************************************************
334  Check if we need to return NT4.x compatible ACL entries.
335 ****************************************************************************/
336
337 static BOOL nt4_compatible_acls(void)
338 {
339         const char *compat = lp_acl_compatibility();
340
341         if (*compat == '\0') {
342                 enum remote_arch_types ra_type = get_remote_arch();
343
344                 /* Automatically adapt to client */
345                 return (ra_type <= RA_WINNT);
346         } else
347                 return (strequal(compat, "winnt"));
348 }
349
350
351 /****************************************************************************
352  Map canon_ace perms to permission bits NT.
353  The attr element is not used here - we only process deny entries on set,
354  not get. Deny entries are implicit on get with ace->perms = 0.
355 ****************************************************************************/
356
357 static SEC_ACCESS map_canon_ace_perms(int *pacl_type, DOM_SID *powner_sid, canon_ace *ace)
358 {
359         SEC_ACCESS sa;
360         uint32 nt_mask = 0;
361
362         *pacl_type = SEC_ACE_TYPE_ACCESS_ALLOWED;
363
364         if ((ace->perms & ALL_ACE_PERMS) == ALL_ACE_PERMS) {
365                         nt_mask = UNIX_ACCESS_RWX;
366         } else if ((ace->perms & ALL_ACE_PERMS) == (mode_t)0) {
367                 /*
368                  * Windows NT refuses to display ACEs with no permissions in them (but
369                  * they are perfectly legal with Windows 2000). If the ACE has empty
370                  * permissions we cannot use 0, so we use the otherwise unused
371                  * WRITE_OWNER permission, which we ignore when we set an ACL.
372                  * We abstract this into a #define of UNIX_ACCESS_NONE to allow this
373                  * to be changed in the future.
374                  */
375
376                 if (nt4_compatible_acls())
377                         nt_mask = UNIX_ACCESS_NONE;
378                 else
379                         nt_mask = 0;
380         } else {
381                 nt_mask |= ((ace->perms & S_IRUSR) ? UNIX_ACCESS_R : 0 );
382                 nt_mask |= ((ace->perms & S_IWUSR) ? UNIX_ACCESS_W : 0 );
383                 nt_mask |= ((ace->perms & S_IXUSR) ? UNIX_ACCESS_X : 0 );
384         }
385
386         DEBUG(10,("map_canon_ace_perms: Mapped (UNIX) %x to (NT) %x\n",
387                         (unsigned int)ace->perms, (unsigned int)nt_mask ));
388
389         init_sec_access(&sa,nt_mask);
390         return sa;
391 }
392
393 /****************************************************************************
394  Map NT perms to a UNIX mode_t.
395 ****************************************************************************/
396
397 #define FILE_SPECIFIC_READ_BITS (FILE_READ_DATA|FILE_READ_EA|FILE_READ_ATTRIBUTES)
398 #define FILE_SPECIFIC_WRITE_BITS (FILE_WRITE_DATA|FILE_APPEND_DATA|FILE_WRITE_EA|FILE_WRITE_ATTRIBUTES)
399 #define FILE_SPECIFIC_EXECUTE_BITS (FILE_EXECUTE)
400
401 static mode_t map_nt_perms( SEC_ACCESS sec_access, int type)
402 {
403         mode_t mode = 0;
404
405         switch(type) {
406         case S_IRUSR:
407                 if(sec_access.mask & GENERIC_ALL_ACCESS)
408                         mode = S_IRUSR|S_IWUSR|S_IXUSR;
409                 else {
410                         mode |= (sec_access.mask & (GENERIC_READ_ACCESS|FILE_SPECIFIC_READ_BITS)) ? S_IRUSR : 0;
411                         mode |= (sec_access.mask & (GENERIC_WRITE_ACCESS|FILE_SPECIFIC_WRITE_BITS)) ? S_IWUSR : 0;
412                         mode |= (sec_access.mask & (GENERIC_EXECUTE_ACCESS|FILE_SPECIFIC_EXECUTE_BITS)) ? S_IXUSR : 0;
413                 }
414                 break;
415         case S_IRGRP:
416                 if(sec_access.mask & GENERIC_ALL_ACCESS)
417                         mode = S_IRGRP|S_IWGRP|S_IXGRP;
418                 else {
419                         mode |= (sec_access.mask & (GENERIC_READ_ACCESS|FILE_SPECIFIC_READ_BITS)) ? S_IRGRP : 0;
420                         mode |= (sec_access.mask & (GENERIC_WRITE_ACCESS|FILE_SPECIFIC_WRITE_BITS)) ? S_IWGRP : 0;
421                         mode |= (sec_access.mask & (GENERIC_EXECUTE_ACCESS|FILE_SPECIFIC_EXECUTE_BITS)) ? S_IXGRP : 0;
422                 }
423                 break;
424         case S_IROTH:
425                 if(sec_access.mask & GENERIC_ALL_ACCESS)
426                         mode = S_IROTH|S_IWOTH|S_IXOTH;
427                 else {
428                         mode |= (sec_access.mask & (GENERIC_READ_ACCESS|FILE_SPECIFIC_READ_BITS)) ? S_IROTH : 0;
429                         mode |= (sec_access.mask & (GENERIC_WRITE_ACCESS|FILE_SPECIFIC_WRITE_BITS)) ? S_IWOTH : 0;
430                         mode |= (sec_access.mask & (GENERIC_EXECUTE_ACCESS|FILE_SPECIFIC_EXECUTE_BITS)) ? S_IXOTH : 0;
431                 }
432                 break;
433         }
434
435         return mode;
436 }
437
438 /****************************************************************************
439  Unpack a SEC_DESC into a UNIX owner and group.
440 ****************************************************************************/
441
442 static BOOL unpack_nt_owners(SMB_STRUCT_STAT *psbuf, uid_t *puser, gid_t *pgrp, uint32 security_info_sent, SEC_DESC *psd)
443 {
444         DOM_SID owner_sid;
445         DOM_SID grp_sid;
446
447         *puser = (uid_t)-1;
448         *pgrp = (gid_t)-1;
449
450         if(security_info_sent == 0) {
451                 DEBUG(0,("unpack_nt_owners: no security info sent !\n"));
452                 return True;
453         }
454
455         /*
456          * Validate the owner and group SID's.
457          */
458
459         memset(&owner_sid, '\0', sizeof(owner_sid));
460         memset(&grp_sid, '\0', sizeof(grp_sid));
461
462         DEBUG(5,("unpack_nt_owners: validating owner_sids.\n"));
463
464         /*
465          * Don't immediately fail if the owner sid cannot be validated.
466          * This may be a group chown only set.
467          */
468
469         if (security_info_sent & OWNER_SECURITY_INFORMATION) {
470                 sid_copy(&owner_sid, psd->owner_sid);
471                 if (NT_STATUS_IS_ERR(sid_to_uid(&owner_sid, puser))) {
472 #if ACL_FORCE_UNMAPPABLE
473                         /* this allows take ownership to work reasonably */
474                         extern struct current_user current_user;
475                         *puser = current_user.uid;
476 #else
477                         DEBUG(3,("unpack_nt_owners: unable to validate owner sid for %s\n",
478                                  sid_string_static(&owner_sid)));
479                         return False;
480 #endif
481                 }
482         }
483
484         /*
485          * Don't immediately fail if the group sid cannot be validated.
486          * This may be an owner chown only set.
487          */
488
489         if (security_info_sent & GROUP_SECURITY_INFORMATION) {
490                 sid_copy(&grp_sid, psd->grp_sid);
491                 if (NT_STATUS_IS_ERR(sid_to_gid( &grp_sid, pgrp))) {
492 #if ACL_FORCE_UNMAPPABLE
493                         /* this allows take group ownership to work reasonably */
494                         extern struct current_user current_user;
495                         *pgrp = current_user.gid;
496 #else
497                         DEBUG(3,("unpack_nt_owners: unable to validate group sid.\n"));
498                         return False;
499 #endif
500                 }
501         }
502
503         DEBUG(5,("unpack_nt_owners: owner_sids validated.\n"));
504
505         return True;
506 }
507
508 /****************************************************************************
509  Ensure the enforced permissions for this share apply.
510 ****************************************************************************/
511
512 static void apply_default_perms(files_struct *fsp, canon_ace *pace, mode_t type)
513 {
514         int snum = SNUM(fsp->conn);
515         mode_t and_bits = (mode_t)0;
516         mode_t or_bits = (mode_t)0;
517
518         /* Get the initial bits to apply. */
519
520         if (fsp->is_directory) {
521                 and_bits = lp_dir_security_mask(snum);
522                 or_bits = lp_force_dir_security_mode(snum);
523         } else {
524                 and_bits = lp_security_mask(snum);
525                 or_bits = lp_force_security_mode(snum);
526         }
527
528         /* Now bounce them into the S_USR space. */     
529         switch(type) {
530         case S_IRUSR:
531                 /* Ensure owner has read access. */
532                 pace->perms |= S_IRUSR;
533                 if (fsp->is_directory)
534                         pace->perms |= (S_IWUSR|S_IXUSR);
535                 and_bits = unix_perms_to_acl_perms(and_bits, S_IRUSR, S_IWUSR, S_IXUSR);
536                 or_bits = unix_perms_to_acl_perms(or_bits, S_IRUSR, S_IWUSR, S_IXUSR);
537                 break;
538         case S_IRGRP:
539                 and_bits = unix_perms_to_acl_perms(and_bits, S_IRGRP, S_IWGRP, S_IXGRP);
540                 or_bits = unix_perms_to_acl_perms(or_bits, S_IRGRP, S_IWGRP, S_IXGRP);
541                 break;
542         case S_IROTH:
543                 and_bits = unix_perms_to_acl_perms(and_bits, S_IROTH, S_IWOTH, S_IXOTH);
544                 or_bits = unix_perms_to_acl_perms(or_bits, S_IROTH, S_IWOTH, S_IXOTH);
545                 break;
546         }
547
548         pace->perms = ((pace->perms & and_bits)|or_bits);
549 }
550
551 /****************************************************************************
552  Check if a given uid/SID is in a group gid/SID. This is probably very
553  expensive and will need optimisation. A *lot* of optimisation :-). JRA.
554 ****************************************************************************/
555
556 static BOOL uid_entry_in_group( canon_ace *uid_ace, canon_ace *group_ace )
557 {
558         extern DOM_SID global_sid_World;
559         fstring u_name;
560         fstring g_name;
561         extern struct current_user current_user;
562
563         /* "Everyone" always matches every uid. */
564
565         if (sid_equal(&group_ace->trustee, &global_sid_World))
566                 return True;
567
568         /* Assume that the current user is in the current group (force group) */
569
570         if (uid_ace->unix_ug.uid == current_user.uid && group_ace->unix_ug.gid == current_user.gid)
571                 return True;
572
573         fstrcpy(u_name, uidtoname(uid_ace->unix_ug.uid));
574         fstrcpy(g_name, gidtoname(group_ace->unix_ug.gid));
575
576         /*
577          * Due to the winbind interfaces we need to do this via names,
578          * not uids/gids.
579          */
580
581         return user_in_group_list(u_name, g_name, NULL, 0);
582 }
583
584 /****************************************************************************
585  A well formed POSIX file or default ACL has at least 3 entries, a 
586  SMB_ACL_USER_OBJ, SMB_ACL_GROUP_OBJ, SMB_ACL_OTHER_OBJ.
587  In addition, the owner must always have at least read access.
588  When using this call on get_acl, the pst struct is valid and contains
589  the mode of the file. When using this call on set_acl, the pst struct has
590  been modified to have a mode containing the default for this file or directory
591  type.
592 ****************************************************************************/
593
594 static BOOL ensure_canon_entry_valid(canon_ace **pp_ace,
595                                                         files_struct *fsp,
596                                                         DOM_SID *pfile_owner_sid,
597                                                         DOM_SID *pfile_grp_sid,
598                                                         SMB_STRUCT_STAT *pst,
599                                                         BOOL setting_acl)
600 {
601         extern DOM_SID global_sid_World;
602         canon_ace *pace;
603         BOOL got_user = False;
604         BOOL got_grp = False;
605         BOOL got_other = False;
606         canon_ace *pace_other = NULL;
607         canon_ace *pace_group = NULL;
608         connection_struct *conn = fsp->conn;
609         SMB_ACL_T current_posix_acl = NULL;
610         mode_t current_user_perms = 0;
611         mode_t current_grp_perms = 0;
612         mode_t current_other_perms = 0;
613         BOOL got_current_user = False;
614         BOOL got_current_grp = False;
615         BOOL got_current_other = False;
616
617         for (pace = *pp_ace; pace; pace = pace->next) {
618                 if (pace->type == SMB_ACL_USER_OBJ) {
619
620                         if (setting_acl)
621                                 apply_default_perms(fsp, pace, S_IRUSR);
622                         got_user = True;
623
624                 } else if (pace->type == SMB_ACL_GROUP_OBJ) {
625
626                         /*
627                          * Ensure create mask/force create mode is respected on set.
628                          */
629
630                         if (setting_acl)
631                                 apply_default_perms(fsp, pace, S_IRGRP);
632                         got_grp = True;
633                         pace_group = pace;
634
635                 } else if (pace->type == SMB_ACL_OTHER) {
636
637                         /*
638                          * Ensure create mask/force create mode is respected on set.
639                          */
640
641                         if (setting_acl)
642                                 apply_default_perms(fsp, pace, S_IROTH);
643                         got_other = True;
644                         pace_other = pace;
645                 }
646         }
647
648         /*
649          * When setting ACLs and missing one out of SMB_ACL_USER_OBJ,
650          * SMB_ACL_GROUP_OBJ, SMB_ACL_OTHER, try to retrieve current
651          * values. For user and other a simple vfs_stat would do, but
652          * we would get mask instead of group. Let's do it via ACL.
653          */
654
655         if (setting_acl && (!got_user || !got_grp || !got_other)) {
656
657                 SMB_ACL_ENTRY_T entry;
658                 int entry_id = SMB_ACL_FIRST_ENTRY;
659
660                 if(fsp->is_directory || fsp->fd == -1) {
661                         current_posix_acl = conn->vfs_ops.sys_acl_get_file(conn, fsp->fsp_name, SMB_ACL_TYPE_ACCESS);
662                 } else {
663                         current_posix_acl = conn->vfs_ops.sys_acl_get_fd(fsp, fsp->fd);
664                 }
665
666                 if (current_posix_acl) {
667                         while (conn->vfs_ops.sys_acl_get_entry(conn, current_posix_acl, entry_id, &entry) == 1) {
668                                 SMB_ACL_TAG_T tagtype;
669                                 SMB_ACL_PERMSET_T permset;
670
671                                 /* get_next... */
672                                 if (entry_id == SMB_ACL_FIRST_ENTRY)
673                                         entry_id = SMB_ACL_NEXT_ENTRY;
674
675                                 /* Is this a MASK entry ? */
676                                 if (conn->vfs_ops.sys_acl_get_tag_type(conn, entry, &tagtype) == -1)
677                                         continue;
678
679                                 if (conn->vfs_ops.sys_acl_get_permset(conn, entry, &permset) == -1)
680                                         continue;
681
682                                 switch(tagtype) {
683                                         case SMB_ACL_USER_OBJ:
684                                                 current_user_perms = convert_permset_to_mode_t(conn, permset);
685                                                 got_current_user = True;
686                                                 break;
687                                         case SMB_ACL_GROUP_OBJ:
688                                                 current_grp_perms = convert_permset_to_mode_t(conn, permset);
689                                                 got_current_grp = True;
690                                                 break;
691                                         case SMB_ACL_OTHER:
692                                                 current_other_perms = convert_permset_to_mode_t(conn, permset);
693                                                 got_current_other = True;
694                                                 break;
695                                 }
696                         }
697                         conn->vfs_ops.sys_acl_free_acl(conn, current_posix_acl);
698                 } else {
699                         DEBUG(10,("ensure_canon_entry_valid: failed to retrieve current ACL of %s\n",
700                                 fsp->fsp_name));
701                 }
702         }
703
704         if (!got_user) {
705                 if ((pace = (canon_ace *)malloc(sizeof(canon_ace))) == NULL) {
706                         DEBUG(0,("ensure_canon_entry_valid: malloc fail.\n"));
707                         return False;
708                 }
709
710                 ZERO_STRUCTP(pace);
711                 pace->type = SMB_ACL_USER_OBJ;
712                 pace->owner_type = UID_ACE;
713                 pace->unix_ug.uid = pst->st_uid;
714                 pace->trustee = *pfile_owner_sid;
715                 pace->attr = ALLOW_ACE;
716
717                 if (setting_acl) {
718                         if (got_current_user) {
719                                 pace->perms = current_user_perms;
720                         } else {
721                                 /* If we only got an "everyone" perm, just use that. */
722                                 if (!got_grp && got_other)
723                                         pace->perms = pace_other->perms;
724                                 else if (got_grp && uid_entry_in_group(pace, pace_group))
725                                         pace->perms = pace_group->perms;
726                                 else
727                                         pace->perms = 0;
728
729                         }
730
731                         apply_default_perms(fsp, pace, S_IRUSR);
732                 } else {
733                         pace->perms = unix_perms_to_acl_perms(pst->st_mode, S_IRUSR, S_IWUSR, S_IXUSR);
734                 }
735
736                 DLIST_ADD(*pp_ace, pace);
737         }
738
739         if (!got_grp) {
740                 if ((pace = (canon_ace *)malloc(sizeof(canon_ace))) == NULL) {
741                         DEBUG(0,("ensure_canon_entry_valid: malloc fail.\n"));
742                         return False;
743                 }
744
745                 ZERO_STRUCTP(pace);
746                 pace->type = SMB_ACL_GROUP_OBJ;
747                 pace->owner_type = GID_ACE;
748                 pace->unix_ug.uid = pst->st_gid;
749                 pace->trustee = *pfile_grp_sid;
750                 pace->attr = ALLOW_ACE;
751                 if (setting_acl) {
752                         if (got_current_grp) {
753                                 pace->perms = current_grp_perms;
754                         } else {
755                                 /* If we only got an "everyone" perm, just use that. */
756                                 if (got_other)
757                                         pace->perms = pace_other->perms;
758                                 else
759                                         pace->perms = unix_perms_to_acl_perms(pst->st_mode, S_IRGRP, S_IWGRP, S_IXGRP);
760                         }
761                         apply_default_perms(fsp, pace, S_IRGRP);
762                 } else {
763                         pace->perms = unix_perms_to_acl_perms(pst->st_mode, S_IRGRP, S_IWGRP, S_IXGRP);
764                 }
765
766                 DLIST_ADD(*pp_ace, pace);
767         }
768
769         if (!got_other) {
770                 if ((pace = (canon_ace *)malloc(sizeof(canon_ace))) == NULL) {
771                         DEBUG(0,("ensure_canon_entry_valid: malloc fail.\n"));
772                         return False;
773                 }
774
775                 ZERO_STRUCTP(pace);
776                 pace->type = SMB_ACL_OTHER;
777                 pace->owner_type = WORLD_ACE;
778                 pace->unix_ug.world = -1;
779                 pace->trustee = global_sid_World;
780                 pace->attr = ALLOW_ACE;
781                 if (setting_acl) {
782                         if (got_current_other)
783                                 pace->perms = current_other_perms;
784                         else
785                                 pace->perms = 0;
786                         apply_default_perms(fsp, pace, S_IROTH);
787                 } else
788                         pace->perms = unix_perms_to_acl_perms(pst->st_mode, S_IROTH, S_IWOTH, S_IXOTH);
789
790                 DLIST_ADD(*pp_ace, pace);
791         }
792
793         return True;
794 }
795
796 /****************************************************************************
797  Check if a POSIX ACL has the required SMB_ACL_USER_OBJ and SMB_ACL_GROUP_OBJ entries.
798  If it does not have them, check if there are any entries where the trustee is the
799  file owner or the owning group, and map these to SMB_ACL_USER_OBJ and SMB_ACL_GROUP_OBJ.
800 ****************************************************************************/
801
802 static void check_owning_objs(canon_ace *ace, DOM_SID *pfile_owner_sid, DOM_SID *pfile_grp_sid)
803 {
804         BOOL got_user_obj, got_group_obj;
805         canon_ace *current_ace;
806         int i, entries;
807
808         entries = count_canon_ace_list(ace);
809         got_user_obj = False;
810         got_group_obj = False;
811
812         for (i=0, current_ace = ace; i < entries; i++, current_ace = current_ace->next) {
813                 if (current_ace->type == SMB_ACL_USER_OBJ)
814                         got_user_obj = True;
815                 else if (current_ace->type == SMB_ACL_GROUP_OBJ)
816                         got_group_obj = True;
817         }
818         if (got_user_obj && got_group_obj) {
819                 DEBUG(10,("check_owning_objs: ACL had owning user/group entries.\n"));
820                 return;
821         }
822
823         for (i=0, current_ace = ace; i < entries; i++, current_ace = current_ace->next) {
824                 if (!got_user_obj && current_ace->owner_type == UID_ACE &&
825                                 sid_equal(&current_ace->trustee, pfile_owner_sid)) {
826                         current_ace->type = SMB_ACL_USER_OBJ;
827                         got_user_obj = True;
828                 }
829                 if (!got_group_obj && current_ace->owner_type == GID_ACE &&
830                                 sid_equal(&current_ace->trustee, pfile_grp_sid)) {
831                         current_ace->type = SMB_ACL_GROUP_OBJ;
832                         got_group_obj = True;
833                 }
834         }
835         if (!got_user_obj)
836                 DEBUG(10,("check_owning_objs: ACL is missing an owner entry.\n"));
837         if (!got_group_obj)
838                 DEBUG(10,("check_owning_objs: ACL is missing an owning group entry.\n"));
839 }
840
841 /****************************************************************************
842  Unpack a SEC_DESC into two canonical ace lists.
843 ****************************************************************************/
844
845 static BOOL create_canon_ace_lists(files_struct *fsp, 
846                                                         DOM_SID *pfile_owner_sid,
847                                                         DOM_SID *pfile_grp_sid,
848                                                         canon_ace **ppfile_ace, canon_ace **ppdir_ace,
849                                                         SEC_ACL *dacl)
850 {
851         extern DOM_SID global_sid_Creator_Owner;
852         extern DOM_SID global_sid_Creator_Group;
853         extern DOM_SID global_sid_World;
854         extern struct generic_mapping file_generic_mapping;
855         BOOL all_aces_are_inherit_only = (fsp->is_directory ? True : False);
856         canon_ace *file_ace = NULL;
857         canon_ace *dir_ace = NULL;
858         canon_ace *tmp_ace = NULL;
859         canon_ace *current_ace = NULL;
860         BOOL got_dir_allow = False;
861         BOOL got_file_allow = False;
862         int i, j;
863
864         *ppfile_ace = NULL;
865         *ppdir_ace = NULL;
866
867         /*
868          * Convert the incoming ACL into a more regular form.
869          */
870
871         for(i = 0; i < dacl->num_aces; i++) {
872                 SEC_ACE *psa = &dacl->ace[i];
873
874                 if((psa->type != SEC_ACE_TYPE_ACCESS_ALLOWED) && (psa->type != SEC_ACE_TYPE_ACCESS_DENIED)) {
875                         DEBUG(3,("create_canon_ace_lists: unable to set anything but an ALLOW or DENY ACE.\n"));
876                         return False;
877                 }
878
879                 if (nt4_compatible_acls()) {
880                         /*
881                          * The security mask may be UNIX_ACCESS_NONE which should map into
882                          * no permissions (we overload the WRITE_OWNER bit for this) or it
883                          * should be one of the ALL/EXECUTE/READ/WRITE bits. Arrange for this
884                          * to be so. Any other bits override the UNIX_ACCESS_NONE bit.
885                          */
886
887                         /*
888                          * Convert GENERIC bits to specific bits.
889                          */
890  
891                         se_map_generic(&psa->info.mask, &file_generic_mapping);
892
893                         psa->info.mask &= (UNIX_ACCESS_NONE|FILE_ALL_ACCESS);
894
895                         if(psa->info.mask != UNIX_ACCESS_NONE)
896                                 psa->info.mask &= ~UNIX_ACCESS_NONE;
897                 }
898         }
899
900         /*
901          * Deal with the fact that NT 4.x re-writes the canonical format
902          * that we return for default ACLs. If a directory ACE is identical
903          * to a inherited directory ACE then NT changes the bits so that the
904          * first ACE is set to OI|IO and the second ACE for this SID is set
905          * to CI. We need to repair this. JRA.
906          */
907
908         for(i = 0; i < dacl->num_aces; i++) {
909                 SEC_ACE *psa1 = &dacl->ace[i];
910
911                 for (j = i + 1; j < dacl->num_aces; j++) {
912                         SEC_ACE *psa2 = &dacl->ace[j];
913
914                         if (psa1->info.mask != psa2->info.mask)
915                                 continue;
916
917                         if (!sid_equal(&psa1->trustee, &psa2->trustee))
918                                 continue;
919
920                         /*
921                          * Ok - permission bits and SIDs are equal.
922                          * Check if flags were re-written.
923                          */
924
925                         if (psa1->flags & SEC_ACE_FLAG_INHERIT_ONLY) {
926
927                                 psa1->flags |= (psa2->flags & (SEC_ACE_FLAG_CONTAINER_INHERIT|SEC_ACE_FLAG_OBJECT_INHERIT));
928                                 psa2->flags &= ~(SEC_ACE_FLAG_CONTAINER_INHERIT|SEC_ACE_FLAG_OBJECT_INHERIT);
929                                 
930                         } else if (psa2->flags & SEC_ACE_FLAG_INHERIT_ONLY) {
931
932                                 psa2->flags |= (psa1->flags & (SEC_ACE_FLAG_CONTAINER_INHERIT|SEC_ACE_FLAG_OBJECT_INHERIT));
933                                 psa1->flags &= ~(SEC_ACE_FLAG_CONTAINER_INHERIT|SEC_ACE_FLAG_OBJECT_INHERIT);
934                                 
935                         }
936                 }
937         }
938
939         for(i = 0; i < dacl->num_aces; i++) {
940                 SEC_ACE *psa = &dacl->ace[i];
941
942                 /*
943                  * Ignore non-mappable SIDs (NT Authority, BUILTIN etc).
944                  */
945
946                 if (non_mappable_sid(&psa->trustee)) {
947                         fstring str;
948                         DEBUG(10,("create_canon_ace_lists: ignoring non-mappable SID %s\n",
949                                 sid_to_string(str, &psa->trustee) ));
950                         continue;
951                 }
952
953                 /*
954                  * Create a cannon_ace entry representing this NT DACL ACE.
955                  */
956
957                 if ((current_ace = (canon_ace *)malloc(sizeof(canon_ace))) == NULL) {
958                         free_canon_ace_list(file_ace);
959                         free_canon_ace_list(dir_ace);
960                         DEBUG(0,("create_canon_ace_lists: malloc fail.\n"));
961                         return False;
962                 }
963
964                 ZERO_STRUCTP(current_ace);
965
966                 sid_copy(&current_ace->trustee, &psa->trustee);
967
968                 /*
969                  * Try and work out if the SID is a user or group
970                  * as we need to flag these differently for POSIX.
971                  * Note what kind of a POSIX ACL this should map to.
972                  */
973
974                 if( sid_equal(&current_ace->trustee, &global_sid_World)) {
975                         current_ace->owner_type = WORLD_ACE;
976                         current_ace->unix_ug.world = -1;
977                         current_ace->type = SMB_ACL_OTHER;
978                 } else if (sid_equal(&current_ace->trustee, &global_sid_Creator_Owner)) {
979                         current_ace->owner_type = UID_ACE;
980                         current_ace->unix_ug.world = -1;
981                         current_ace->type = SMB_ACL_USER_OBJ;
982
983                         /*
984                          * The Creator Owner entry only specifies inheritable permissions,
985                          * never access permissions. WinNT doesn't always set the ACE to
986                          *INHERIT_ONLY, though.
987                          */
988
989                         if (nt4_compatible_acls())
990                                 psa->flags |= SEC_ACE_FLAG_INHERIT_ONLY;
991                 } else if (sid_equal(&current_ace->trustee, &global_sid_Creator_Group)) {
992                         current_ace->owner_type = GID_ACE;
993                         current_ace->unix_ug.world = -1;
994                         current_ace->type = SMB_ACL_GROUP_OBJ;
995
996                         /*
997                          * The Creator Group entry only specifies inheritable permissions,
998                          * never access permissions. WinNT doesn't always set the ACE to
999                          *INHERIT_ONLY, though.
1000                          */
1001                         if (nt4_compatible_acls())
1002                                 psa->flags |= SEC_ACE_FLAG_INHERIT_ONLY;
1003
1004                 } else if (NT_STATUS_IS_OK(sid_to_gid( &current_ace->trustee, &current_ace->unix_ug.gid))) {
1005                         current_ace->owner_type = GID_ACE;
1006                         current_ace->type = SMB_ACL_GROUP;
1007                 } else if (NT_STATUS_IS_OK(sid_to_uid( &current_ace->trustee, &current_ace->unix_ug.uid))) {
1008                         current_ace->owner_type = UID_ACE;
1009                         current_ace->type = SMB_ACL_USER;
1010                 } else {
1011                         fstring str;
1012
1013                         free_canon_ace_list(file_ace);
1014                         free_canon_ace_list(dir_ace);
1015                         DEBUG(0,("create_canon_ace_lists: unable to map SID %s to uid or gid.\n",
1016                                 sid_to_string(str, &current_ace->trustee) ));
1017                         SAFE_FREE(current_ace);
1018                         return False;
1019                 }
1020
1021                 /*
1022                  * Map the given NT permissions into a UNIX mode_t containing only
1023                  * S_I(R|W|X)USR bits.
1024                  */
1025
1026                 current_ace->perms |= map_nt_perms( psa->info, S_IRUSR);
1027                 current_ace->attr = (psa->type == SEC_ACE_TYPE_ACCESS_ALLOWED) ? ALLOW_ACE : DENY_ACE;
1028
1029                 /*
1030                  * Now add the created ace to either the file list, the directory
1031                  * list, or both. We *MUST* preserve the order here (hence we use
1032                  * DLIST_ADD_END) as NT ACLs are order dependent.
1033                  */
1034
1035                 if (fsp->is_directory) {
1036
1037                         /*
1038                          * We can only add to the default POSIX ACE list if the ACE is
1039                          * designed to be inherited by both files and directories.
1040                          */
1041
1042                         if ((psa->flags & (SEC_ACE_FLAG_OBJECT_INHERIT|SEC_ACE_FLAG_CONTAINER_INHERIT)) ==
1043                                 (SEC_ACE_FLAG_OBJECT_INHERIT|SEC_ACE_FLAG_CONTAINER_INHERIT)) {
1044
1045                                 DLIST_ADD_END(dir_ace, current_ace, tmp_ace);
1046
1047                                 /*
1048                                  * Note if this was an allow ace. We can't process
1049                                  * any further deny ace's after this.
1050                                  */
1051
1052                                 if (current_ace->attr == ALLOW_ACE)
1053                                         got_dir_allow = True;
1054
1055                                 if ((current_ace->attr == DENY_ACE) && got_dir_allow) {
1056                                         DEBUG(0,("create_canon_ace_lists: malformed ACL in inheritable ACL ! \
1057 Deny entry after Allow entry. Failing to set on file %s.\n", fsp->fsp_name ));
1058                                         free_canon_ace_list(file_ace);
1059                                         free_canon_ace_list(dir_ace);
1060                                         SAFE_FREE(current_ace);
1061                                         return False;
1062                                 }       
1063
1064                                 if( DEBUGLVL( 10 )) {
1065                                         dbgtext("create_canon_ace_lists: adding dir ACL:\n");
1066                                         print_canon_ace( current_ace, 0);
1067                                 }
1068
1069                                 /*
1070                                  * If this is not an inherit only ACE we need to add a duplicate
1071                                  * to the file acl.
1072                                  */
1073
1074                                 if (!(psa->flags & SEC_ACE_FLAG_INHERIT_ONLY)) {
1075                                         canon_ace *dup_ace = dup_canon_ace(current_ace);
1076
1077                                         if (!dup_ace) {
1078                                                 DEBUG(0,("create_canon_ace_lists: malloc fail !\n"));
1079                                                 free_canon_ace_list(file_ace);
1080                                                 free_canon_ace_list(dir_ace);
1081                                                 return False;
1082                                         }
1083
1084                                         /*
1085                                          * We must not free current_ace here as its
1086                                          * pointer is now owned by the dir_ace list.
1087                                          */
1088                                         current_ace = dup_ace;
1089                                 } else {
1090                                         /*
1091                                          * We must not free current_ace here as its
1092                                          * pointer is now owned by the dir_ace list.
1093                                          */
1094                                         current_ace = NULL;
1095                                 }
1096                         }
1097                 }
1098
1099                 /*
1100                  * Only add to the file ACL if not inherit only.
1101                  */
1102
1103                 if (!(psa->flags & SEC_ACE_FLAG_INHERIT_ONLY)) {
1104                         DLIST_ADD_END(file_ace, current_ace, tmp_ace);
1105
1106                         /*
1107                          * Note if this was an allow ace. We can't process
1108                          * any further deny ace's after this.
1109                          */
1110
1111                         if (current_ace->attr == ALLOW_ACE)
1112                                 got_file_allow = True;
1113
1114                         if ((current_ace->attr == DENY_ACE) && got_file_allow) {
1115                                 DEBUG(0,("create_canon_ace_lists: malformed ACL in file ACL ! \
1116 Deny entry after Allow entry. Failing to set on file %s.\n", fsp->fsp_name ));
1117                                 free_canon_ace_list(file_ace);
1118                                 free_canon_ace_list(dir_ace);
1119                                 SAFE_FREE(current_ace);
1120                                 return False;
1121                         }       
1122
1123                         if( DEBUGLVL( 10 )) {
1124                                 dbgtext("create_canon_ace_lists: adding file ACL:\n");
1125                                 print_canon_ace( current_ace, 0);
1126                         }
1127                         all_aces_are_inherit_only = False;
1128                         /*
1129                          * We must not free current_ace here as its
1130                          * pointer is now owned by the file_ace list.
1131                          */
1132                         current_ace = NULL;
1133                 }
1134
1135                 /*
1136                  * Free if ACE was not added.
1137                  */
1138
1139                 SAFE_FREE(current_ace);
1140         }
1141
1142         if (fsp->is_directory && all_aces_are_inherit_only) {
1143                 /*
1144                  * Windows 2000 is doing one of these weird 'inherit acl'
1145                  * traverses to conserve NTFS ACL resources. Just pretend
1146                  * there was no DACL sent. JRA.
1147                  */
1148
1149                 DEBUG(10,("create_canon_ace_lists: Win2k inherit acl traverse. Ignoring DACL.\n"));
1150                 free_canon_ace_list(file_ace);
1151                 free_canon_ace_list(dir_ace);
1152                 file_ace = NULL;
1153                 dir_ace = NULL;
1154         } else {
1155                 /*
1156                  * Check if we have SMB_ACL_USER_OBJ and SMB_ACL_GROUP_OBJ entries in each
1157                  * ACL. If we don't have them, check if any SMB_ACL_USER/SMB_ACL_GROUP
1158                  * entries can be converted to *_OBJ. Usually we will already have these
1159                  * entries in the Default ACL, and the Access ACL will not have them.
1160                  */
1161                 check_owning_objs(file_ace, pfile_owner_sid, pfile_grp_sid);
1162                 check_owning_objs(dir_ace, pfile_owner_sid, pfile_grp_sid);
1163         }
1164
1165         *ppfile_ace = file_ace;
1166         *ppdir_ace = dir_ace;
1167
1168         return True;
1169 }
1170
1171 /****************************************************************************
1172  ASCII art time again... JRA :-).
1173
1174  We have 3 cases to process when moving from an NT ACL to a POSIX ACL. Firstly,
1175  we insist the ACL is in canonical form (ie. all DENY entries preceede ALLOW
1176  entries). Secondly, the merge code has ensured that all duplicate SID entries for
1177  allow or deny have been merged, so the same SID can only appear once in the deny
1178  list or once in the allow list.
1179
1180  We then process as follows :
1181
1182  ---------------------------------------------------------------------------
1183  First pass - look for a Everyone DENY entry.
1184
1185  If it is deny all (rwx) trunate the list at this point.
1186  Else, walk the list from this point and use the deny permissions of this
1187  entry as a mask on all following allow entries. Finally, delete
1188  the Everyone DENY entry (we have applied it to everything possible).
1189
1190  In addition, in this pass we remove any DENY entries that have 
1191  no permissions (ie. they are a DENY nothing).
1192  ---------------------------------------------------------------------------
1193  Second pass - only deal with deny user entries.
1194
1195  DENY user1 (perms XXX)
1196
1197  new_perms = 0
1198  for all following allow group entries where user1 is in group
1199         new_perms |= group_perms;
1200
1201  user1 entry perms = new_perms & ~ XXX;
1202
1203  Convert the deny entry to an allow entry with the new perms and
1204  push to the end of the list. Note if the user was in no groups
1205  this maps to a specific allow nothing entry for this user.
1206
1207  The common case from the NT ACL choser (userX deny all) is
1208  optimised so we don't do the group lookup - we just map to
1209  an allow nothing entry.
1210
1211  What we're doing here is inferring the allow permissions the
1212  person setting the ACE on user1 wanted by looking at the allow
1213  permissions on the groups the user is currently in. This will
1214  be a snapshot, depending on group membership but is the best
1215  we can do and has the advantage of failing closed rather than
1216  open.
1217  ---------------------------------------------------------------------------
1218  Third pass - only deal with deny group entries.
1219
1220  DENY group1 (perms XXX)
1221
1222  for all following allow user entries where user is in group1
1223    user entry perms = user entry perms & ~ XXX;
1224
1225  If there is a group Everyone allow entry with permissions YYY,
1226  convert the group1 entry to an allow entry and modify its
1227  permissions to be :
1228
1229  new_perms = YYY & ~ XXX
1230
1231  and push to the end of the list.
1232
1233  If there is no group Everyone allow entry then convert the
1234  group1 entry to a allow nothing entry and push to the end of the list.
1235
1236  Note that the common case from the NT ACL choser (groupX deny all)
1237  cannot be optimised here as we need to modify user entries who are
1238  in the group to change them to a deny all also.
1239
1240  What we're doing here is modifying the allow permissions of
1241  user entries (which are more specific in POSIX ACLs) to mask
1242  out the explicit deny set on the group they are in. This will
1243  be a snapshot depending on current group membership but is the
1244  best we can do and has the advantage of failing closed rather
1245  than open.
1246  ---------------------------------------------------------------------------
1247
1248  Note we *MUST* do the deny user pass first as this will convert deny user
1249  entries into allow user entries which can then be processed by the deny
1250  group pass.
1251
1252  The above algorithm took a *lot* of thinking about - hence this
1253  explaination :-). JRA.
1254 ****************************************************************************/
1255
1256 /****************************************************************************
1257  Process a canon_ace list entries. This is very complex code. We need
1258  to go through and remove the "deny" permissions from any allow entry that matches
1259  the id of this entry. We have already refused any NT ACL that wasn't in correct
1260  order (DENY followed by ALLOW). If any allow entry ends up with zero permissions,
1261  we just remove it (to fail safe). We have already removed any duplicate ace
1262  entries. Treat an "Everyone" DENY_ACE as a special case - use it to mask all
1263  allow entries.
1264 ****************************************************************************/
1265
1266 static void process_deny_list( canon_ace **pp_ace_list )
1267 {
1268         extern DOM_SID global_sid_World;
1269         canon_ace *ace_list = *pp_ace_list;
1270         canon_ace *curr_ace = NULL;
1271         canon_ace *curr_ace_next = NULL;
1272
1273         /* Pass 1 above - look for an Everyone, deny entry. */
1274
1275         for (curr_ace = ace_list; curr_ace; curr_ace = curr_ace_next) {
1276                 canon_ace *allow_ace_p;
1277
1278                 curr_ace_next = curr_ace->next; /* So we can't lose the link. */
1279
1280                 if (curr_ace->attr != DENY_ACE)
1281                         continue;
1282
1283                 if (curr_ace->perms == (mode_t)0) {
1284
1285                         /* Deny nothing entry - delete. */
1286
1287                         DLIST_REMOVE(ace_list, curr_ace);
1288                         continue;
1289                 }
1290
1291                 if (!sid_equal(&curr_ace->trustee, &global_sid_World))
1292                         continue;
1293
1294                 /* JRATEST - assert. */
1295                 SMB_ASSERT(curr_ace->owner_type == WORLD_ACE);
1296
1297                 if (curr_ace->perms == ALL_ACE_PERMS) {
1298
1299                         /*
1300                          * Optimisation. This is a DENY_ALL to Everyone. Truncate the
1301                          * list at this point including this entry.
1302                          */
1303
1304                         canon_ace *prev_entry = curr_ace->prev;
1305
1306                         free_canon_ace_list( curr_ace );
1307                         if (prev_entry)
1308                                 prev_entry->next = NULL;
1309                         else {
1310                                 /* We deleted the entire list. */
1311                                 ace_list = NULL;
1312                         }
1313                         break;
1314                 }
1315
1316                 for (allow_ace_p = curr_ace->next; allow_ace_p; allow_ace_p = allow_ace_p->next) {
1317
1318                         /* 
1319                          * Only mask off allow entries.
1320                          */
1321
1322                         if (allow_ace_p->attr != ALLOW_ACE)
1323                                 continue;
1324
1325                         allow_ace_p->perms &= ~curr_ace->perms;
1326                 }
1327
1328                 /*
1329                  * Now it's been applied, remove it.
1330                  */
1331
1332                 DLIST_REMOVE(ace_list, curr_ace);
1333         }
1334
1335         /* Pass 2 above - deal with deny user entries. */
1336
1337         for (curr_ace = ace_list; curr_ace; curr_ace = curr_ace_next) {
1338                 mode_t new_perms = (mode_t)0;
1339                 canon_ace *allow_ace_p;
1340                 canon_ace *tmp_ace;
1341
1342                 curr_ace_next = curr_ace->next; /* So we can't lose the link. */
1343
1344                 if (curr_ace->attr != DENY_ACE)
1345                         continue;
1346
1347                 if (curr_ace->owner_type != UID_ACE)
1348                         continue;
1349
1350                 if (curr_ace->perms == ALL_ACE_PERMS) {
1351
1352                         /*
1353                          * Optimisation - this is a deny everything to this user.
1354                          * Convert to an allow nothing and push to the end of the list.
1355                          */
1356
1357                         curr_ace->attr = ALLOW_ACE;
1358                         curr_ace->perms = (mode_t)0;
1359                         DLIST_DEMOTE(ace_list, curr_ace, tmp_ace);
1360                         continue;
1361                 }
1362
1363                 for (allow_ace_p = curr_ace->next; allow_ace_p; allow_ace_p = allow_ace_p->next) {
1364
1365                         if (allow_ace_p->attr != ALLOW_ACE)
1366                                 continue;
1367
1368                         /* We process GID_ACE and WORLD_ACE entries only. */
1369
1370                         if (allow_ace_p->owner_type == UID_ACE)
1371                                 continue;
1372
1373                         if (uid_entry_in_group( curr_ace, allow_ace_p))
1374                                 new_perms |= allow_ace_p->perms;
1375                 }
1376
1377                 /*
1378                  * Convert to a allow entry, modify the perms and push to the end
1379                  * of the list.
1380                  */
1381
1382                 curr_ace->attr = ALLOW_ACE;
1383                 curr_ace->perms = (new_perms & ~curr_ace->perms);
1384                 DLIST_DEMOTE(ace_list, curr_ace, tmp_ace);
1385         }
1386
1387         /* Pass 3 above - deal with deny group entries. */
1388
1389         for (curr_ace = ace_list; curr_ace; curr_ace = curr_ace_next) {
1390                 canon_ace *tmp_ace;
1391                 canon_ace *allow_ace_p;
1392                 canon_ace *allow_everyone_p = NULL;
1393
1394                 curr_ace_next = curr_ace->next; /* So we can't lose the link. */
1395
1396                 if (curr_ace->attr != DENY_ACE)
1397                         continue;
1398
1399                 if (curr_ace->owner_type != GID_ACE)
1400                         continue;
1401
1402                 for (allow_ace_p = curr_ace->next; allow_ace_p; allow_ace_p = allow_ace_p->next) {
1403
1404                         if (allow_ace_p->attr != ALLOW_ACE)
1405                                 continue;
1406
1407                         /* Store a pointer to the Everyone allow, if it exists. */
1408                         if (allow_ace_p->owner_type == WORLD_ACE)
1409                                 allow_everyone_p = allow_ace_p;
1410
1411                         /* We process UID_ACE entries only. */
1412
1413                         if (allow_ace_p->owner_type != UID_ACE)
1414                                 continue;
1415
1416                         /* Mask off the deny group perms. */
1417
1418                         if (uid_entry_in_group( allow_ace_p, curr_ace))
1419                                 allow_ace_p->perms &= ~curr_ace->perms;
1420                 }
1421
1422                 /*
1423                  * Convert the deny to an allow with the correct perms and
1424                  * push to the end of the list.
1425                  */
1426
1427                 curr_ace->attr = ALLOW_ACE;
1428                 if (allow_everyone_p)
1429                         curr_ace->perms = allow_everyone_p->perms & ~curr_ace->perms;
1430                 else
1431                         curr_ace->perms = (mode_t)0;
1432                 DLIST_DEMOTE(ace_list, curr_ace, tmp_ace);
1433
1434         }
1435
1436         *pp_ace_list = ace_list;
1437 }
1438
1439 /****************************************************************************
1440  Create a default mode that will be used if a security descriptor entry has
1441  no user/group/world entries.
1442 ****************************************************************************/
1443
1444 static mode_t create_default_mode(files_struct *fsp, BOOL interitable_mode)
1445 {
1446         int snum = SNUM(fsp->conn);
1447         mode_t and_bits = (mode_t)0;
1448         mode_t or_bits = (mode_t)0;
1449         mode_t mode = interitable_mode ? unix_mode( fsp->conn, FILE_ATTRIBUTE_ARCHIVE, fsp->fsp_name) : S_IRUSR;
1450
1451         if (fsp->is_directory)
1452                 mode |= (S_IWUSR|S_IXUSR);
1453
1454         /*
1455          * Now AND with the create mode/directory mode bits then OR with the
1456          * force create mode/force directory mode bits.
1457          */
1458
1459         if (fsp->is_directory) {
1460                 and_bits = lp_dir_security_mask(snum);
1461                 or_bits = lp_force_dir_security_mode(snum);
1462         } else {
1463                 and_bits = lp_security_mask(snum);
1464                 or_bits = lp_force_security_mode(snum);
1465         }
1466
1467         return ((mode & and_bits)|or_bits);
1468 }
1469
1470 /****************************************************************************
1471  Unpack a SEC_DESC into two canonical ace lists. We don't depend on this
1472  succeeding.
1473 ****************************************************************************/
1474
1475 static BOOL unpack_canon_ace(files_struct *fsp, 
1476                                                         SMB_STRUCT_STAT *pst,
1477                                                         DOM_SID *pfile_owner_sid,
1478                                                         DOM_SID *pfile_grp_sid,
1479                                                         canon_ace **ppfile_ace, canon_ace **ppdir_ace,
1480                                                         uint32 security_info_sent, SEC_DESC *psd)
1481 {
1482         canon_ace *file_ace = NULL;
1483         canon_ace *dir_ace = NULL;
1484
1485         *ppfile_ace = NULL;
1486         *ppdir_ace = NULL;
1487
1488         if(security_info_sent == 0) {
1489                 DEBUG(0,("unpack_canon_ace: no security info sent !\n"));
1490                 return False;
1491         }
1492
1493         /*
1494          * If no DACL then this is a chown only security descriptor.
1495          */
1496
1497         if(!(security_info_sent & DACL_SECURITY_INFORMATION) || !psd->dacl)
1498                 return True;
1499
1500         /*
1501          * Now go through the DACL and create the canon_ace lists.
1502          */
1503
1504         if (!create_canon_ace_lists( fsp, pfile_owner_sid, pfile_grp_sid,
1505                                                                 &file_ace, &dir_ace, psd->dacl))
1506                 return False;
1507
1508         if ((file_ace == NULL) && (dir_ace == NULL)) {
1509                 /* W2K traverse DACL set - ignore. */
1510                 return True;
1511         }
1512
1513         /*
1514          * Go through the canon_ace list and merge entries
1515          * belonging to identical users of identical allow or deny type.
1516          * We can do this as all deny entries come first, followed by
1517          * all allow entries (we have mandated this before accepting this acl).
1518          */
1519
1520         print_canon_ace_list( "file ace - before merge", file_ace);
1521         merge_aces( &file_ace );
1522
1523         print_canon_ace_list( "dir ace - before merge", dir_ace);
1524         merge_aces( &dir_ace );
1525
1526         /*
1527          * NT ACLs are order dependent. Go through the acl lists and
1528          * process DENY entries by masking the allow entries.
1529          */
1530
1531         print_canon_ace_list( "file ace - before deny", file_ace);
1532         process_deny_list( &file_ace);
1533
1534         print_canon_ace_list( "dir ace - before deny", dir_ace);
1535         process_deny_list( &dir_ace);
1536
1537         /*
1538          * A well formed POSIX file or default ACL has at least 3 entries, a 
1539          * SMB_ACL_USER_OBJ, SMB_ACL_GROUP_OBJ, SMB_ACL_OTHER_OBJ
1540          * and optionally a mask entry. Ensure this is the case.
1541          */
1542
1543         print_canon_ace_list( "file ace - before valid", file_ace);
1544
1545         /*
1546          * A default 3 element mode entry for a file should be r-- --- ---.
1547          * A default 3 element mode entry for a directory should be rwx --- ---.
1548          */
1549
1550         pst->st_mode = create_default_mode(fsp, False);
1551
1552         if (!ensure_canon_entry_valid(&file_ace, fsp, pfile_owner_sid, pfile_grp_sid, pst, True)) {
1553                 free_canon_ace_list(file_ace);
1554                 free_canon_ace_list(dir_ace);
1555                 return False;
1556         }
1557
1558         print_canon_ace_list( "dir ace - before valid", dir_ace);
1559
1560         /*
1561          * A default inheritable 3 element mode entry for a directory should be the
1562          * mode Samba will use to create a file within. Ensure user rwx bits are set if
1563          * it's a directory.
1564          */
1565
1566         pst->st_mode = create_default_mode(fsp, True);
1567
1568         if (dir_ace && !ensure_canon_entry_valid(&dir_ace, fsp, pfile_owner_sid, pfile_grp_sid, pst, True)) {
1569                 free_canon_ace_list(file_ace);
1570                 free_canon_ace_list(dir_ace);
1571                 return False;
1572         }
1573
1574         print_canon_ace_list( "file ace - return", file_ace);
1575         print_canon_ace_list( "dir ace - return", dir_ace);
1576
1577         *ppfile_ace = file_ace;
1578         *ppdir_ace = dir_ace;
1579         return True;
1580
1581 }
1582
1583 /******************************************************************************
1584  When returning permissions, try and fit NT display
1585  semantics if possible. Note the the canon_entries here must have been malloced.
1586  The list format should be - first entry = owner, followed by group and other user
1587  entries, last entry = other.
1588
1589  Note that this doesn't exactly match the NT semantics for an ACL. As POSIX entries
1590  are not ordered, and match on the most specific entry rather than walking a list,
1591  then a simple POSIX permission of rw-r--r-- should really map to 5 entries,
1592
1593  Entry 0: owner : deny all except read and write.
1594  Entry 1: group : deny all except read.
1595  Entry 2: owner : allow read and write.
1596  Entry 3: group : allow read.
1597  Entry 4: Everyone : allow read.
1598
1599  But NT cannot display this in their ACL editor !
1600 ********************************************************************************/
1601
1602 static void arrange_posix_perms( char *filename, canon_ace **pp_list_head)
1603 {
1604         canon_ace *list_head = *pp_list_head;
1605         canon_ace *owner_ace = NULL;
1606         canon_ace *other_ace = NULL;
1607         canon_ace *ace = NULL;
1608
1609         for (ace = list_head; ace; ace = ace->next) {
1610                 if (ace->type == SMB_ACL_USER_OBJ)
1611                         owner_ace = ace;
1612                 else if (ace->type == SMB_ACL_OTHER) {
1613                         /* Last ace - this is "other" */
1614                         other_ace = ace;
1615                 }
1616         }
1617                 
1618         if (!owner_ace || !other_ace) {
1619                 DEBUG(0,("arrange_posix_perms: Invalid POSIX permissions for file %s, missing owner or other.\n",
1620                         filename ));
1621                 return;
1622         }
1623
1624         /*
1625          * The POSIX algorithm applies to owner first, and other last,
1626          * so ensure they are arranged in this order.
1627          */
1628
1629         if (owner_ace) {
1630                 DLIST_PROMOTE(list_head, owner_ace);
1631         }
1632
1633         if (other_ace) {
1634                 DLIST_DEMOTE(list_head, other_ace, ace);
1635         }
1636
1637         /* We have probably changed the head of the list. */
1638
1639         *pp_list_head = list_head;
1640 }
1641                 
1642 /****************************************************************************
1643  Create a linked list of canonical ACE entries.
1644 ****************************************************************************/
1645
1646 static canon_ace *canonicalise_acl( files_struct *fsp, SMB_ACL_T posix_acl, SMB_STRUCT_STAT *psbuf,
1647                                         DOM_SID *powner, DOM_SID *pgroup, SMB_ACL_TYPE_T the_acl_type)
1648 {
1649         extern DOM_SID global_sid_World;
1650         connection_struct *conn = fsp->conn;
1651         mode_t acl_mask = (S_IRUSR|S_IWUSR|S_IXUSR);
1652         canon_ace *list_head = NULL;
1653         canon_ace *ace = NULL;
1654         canon_ace *next_ace = NULL;
1655         int entry_id = SMB_ACL_FIRST_ENTRY;
1656         SMB_ACL_ENTRY_T entry;
1657         size_t ace_count;
1658
1659         while ( posix_acl && (conn->vfs_ops.sys_acl_get_entry(conn, posix_acl, entry_id, &entry) == 1)) {
1660                 SMB_ACL_TAG_T tagtype;
1661                 SMB_ACL_PERMSET_T permset;
1662                 DOM_SID sid;
1663                 posix_id unix_ug;
1664                 enum ace_owner owner_type;
1665
1666                 /* get_next... */
1667                 if (entry_id == SMB_ACL_FIRST_ENTRY)
1668                         entry_id = SMB_ACL_NEXT_ENTRY;
1669
1670                 /* Is this a MASK entry ? */
1671                 if (conn->vfs_ops.sys_acl_get_tag_type(conn, entry, &tagtype) == -1)
1672                         continue;
1673
1674                 if (conn->vfs_ops.sys_acl_get_permset(conn, entry, &permset) == -1)
1675                         continue;
1676
1677                 /* Decide which SID to use based on the ACL type. */
1678                 switch(tagtype) {
1679                         case SMB_ACL_USER_OBJ:
1680                                 /* Get the SID from the owner. */
1681                                 sid_copy(&sid, powner);
1682                                 unix_ug.uid = psbuf->st_uid;
1683                                 owner_type = UID_ACE;
1684                                 break;
1685                         case SMB_ACL_USER:
1686                                 {
1687                                         uid_t *puid = (uid_t *)conn->vfs_ops.sys_acl_get_qualifier(conn, entry);
1688                                         if (puid == NULL) {
1689                                                 DEBUG(0,("canonicalise_acl: Failed to get uid.\n"));
1690                                                 continue;
1691                                         }
1692                                         /*
1693                                          * A SMB_ACL_USER entry for the owner is shadowed by the
1694                                          * SMB_ACL_USER_OBJ entry and Windows also cannot represent
1695                                          * that entry, so we ignore it. We also don't create such
1696                                          * entries out of the blue when setting ACLs, so a get/set
1697                                          * cycle will drop them.
1698                                          */
1699                                         if (the_acl_type == SMB_ACL_TYPE_ACCESS && *puid == psbuf->st_uid)
1700                                                 continue;
1701                                         uid_to_sid( &sid, *puid);
1702                                         unix_ug.uid = *puid;
1703                                         owner_type = UID_ACE;
1704                                         conn->vfs_ops.sys_acl_free_qualifier(conn, (void *)puid,tagtype);
1705                                         break;
1706                                 }
1707                         case SMB_ACL_GROUP_OBJ:
1708                                 /* Get the SID from the owning group. */
1709                                 sid_copy(&sid, pgroup);
1710                                 unix_ug.gid = psbuf->st_gid;
1711                                 owner_type = GID_ACE;
1712                                 break;
1713                         case SMB_ACL_GROUP:
1714                                 {
1715                                         gid_t *pgid = (gid_t *)conn->vfs_ops.sys_acl_get_qualifier(conn, entry);
1716                                         if (pgid == NULL) {
1717                                                 DEBUG(0,("canonicalise_acl: Failed to get gid.\n"));
1718                                                 continue;
1719                                         }
1720                                         gid_to_sid( &sid, *pgid);
1721                                         unix_ug.gid = *pgid;
1722                                         owner_type = GID_ACE;
1723                                         conn->vfs_ops.sys_acl_free_qualifier(conn, (void *)pgid,tagtype);
1724                                         break;
1725                                 }
1726                         case SMB_ACL_MASK:
1727                                 acl_mask = convert_permset_to_mode_t(conn, permset);
1728                                 continue; /* Don't count the mask as an entry. */
1729                         case SMB_ACL_OTHER:
1730                                 /* Use the Everyone SID */
1731                                 sid = global_sid_World;
1732                                 unix_ug.world = -1;
1733                                 owner_type = WORLD_ACE;
1734                                 break;
1735                         default:
1736                                 DEBUG(0,("canonicalise_acl: Unknown tagtype %u\n", (unsigned int)tagtype));
1737                                 continue;
1738                 }
1739
1740                 /*
1741                  * Add this entry to the list.
1742                  */
1743
1744                 if ((ace = (canon_ace *)malloc(sizeof(canon_ace))) == NULL)
1745                         goto fail;
1746
1747                 ZERO_STRUCTP(ace);
1748                 ace->type = tagtype;
1749                 ace->perms = convert_permset_to_mode_t(conn, permset);
1750                 ace->attr = ALLOW_ACE;
1751                 ace->trustee = sid;
1752                 ace->unix_ug = unix_ug;
1753                 ace->owner_type = owner_type;
1754
1755                 DLIST_ADD(list_head, ace);
1756         }
1757
1758         /*
1759          * This next call will ensure we have at least a user/group/world set.
1760          */
1761
1762         if (!ensure_canon_entry_valid(&list_head, fsp, powner, pgroup, psbuf, False))
1763                 goto fail;
1764
1765         arrange_posix_perms(fsp->fsp_name,&list_head );
1766
1767         /*
1768          * Now go through the list, masking the permissions with the
1769          * acl_mask. Ensure all DENY Entries are at the start of the list.
1770          */
1771
1772         DEBUG(10,("canonicalise_acl: ace entries before arrange :\n"));
1773
1774         for ( ace_count = 0, ace = list_head; ace; ace = next_ace, ace_count++) {
1775                 next_ace = ace->next;
1776
1777                 /* Masks are only applied to entries other than USER_OBJ and OTHER. */
1778                 if (ace->type != SMB_ACL_OTHER && ace->type != SMB_ACL_USER_OBJ)
1779                         ace->perms &= acl_mask;
1780
1781                 if (ace->perms == 0) {
1782                         DLIST_PROMOTE(list_head, ace);
1783                 }
1784
1785                 if( DEBUGLVL( 10 ) ) {
1786                         print_canon_ace(ace, ace_count);
1787                 }
1788         }
1789
1790         print_canon_ace_list( "canonicalise_acl: ace entries after arrange", list_head );
1791
1792         return list_head;
1793
1794   fail:
1795
1796         free_canon_ace_list(list_head);
1797         return NULL;
1798 }
1799
1800 /****************************************************************************
1801  Attempt to apply an ACL to a file or directory.
1802 ****************************************************************************/
1803
1804 static BOOL set_canon_ace_list(files_struct *fsp, canon_ace *the_ace, BOOL default_ace, BOOL *pacl_set_support)
1805 {
1806         connection_struct *conn = fsp->conn;
1807         BOOL ret = False;
1808         SMB_ACL_T the_acl = conn->vfs_ops.sys_acl_init(conn, (int)count_canon_ace_list(the_ace) + 1);
1809         canon_ace *p_ace;
1810         int i;
1811         SMB_ACL_ENTRY_T mask_entry;
1812         BOOL got_mask_entry = False;
1813         SMB_ACL_PERMSET_T mask_permset;
1814         SMB_ACL_TYPE_T the_acl_type = (default_ace ? SMB_ACL_TYPE_DEFAULT : SMB_ACL_TYPE_ACCESS);
1815         BOOL needs_mask = False;
1816         mode_t mask_perms = 0;
1817
1818 #if defined(POSIX_ACL_NEEDS_MASK)
1819         /* HP-UX always wants to have a mask (called "class" there). */
1820         needs_mask = True;
1821 #endif
1822
1823         if (the_acl == NULL) {
1824
1825                 if (errno != ENOSYS) {
1826                         /*
1827                          * Only print this error message if we have some kind of ACL
1828                          * support that's not working. Otherwise we would always get this.
1829                          */
1830                         DEBUG(0,("set_canon_ace_list: Unable to init %s ACL. (%s)\n",
1831                                 default_ace ? "default" : "file", strerror(errno) ));
1832                 }
1833                 *pacl_set_support = False;
1834                 return False;
1835         }
1836
1837         if( DEBUGLVL( 10 )) {
1838                 dbgtext("set_canon_ace_list: setting ACL:\n");
1839                 for (i = 0, p_ace = the_ace; p_ace; p_ace = p_ace->next, i++ ) {
1840                         print_canon_ace( p_ace, i);
1841                 }
1842         }
1843
1844         for (i = 0, p_ace = the_ace; p_ace; p_ace = p_ace->next, i++ ) {
1845                 SMB_ACL_ENTRY_T the_entry;
1846                 SMB_ACL_PERMSET_T the_permset;
1847
1848                 /*
1849                  * ACLs only "need" an ACL_MASK entry if there are any named user or
1850                  * named group entries. But if there is an ACL_MASK entry, it applies
1851                  * to ACL_USER, ACL_GROUP, and ACL_GROUP_OBJ entries. Set the mask
1852                  * so that it doesn't deny (i.e., mask off) any permissions.
1853                  */
1854
1855                 if (p_ace->type == SMB_ACL_USER || p_ace->type == SMB_ACL_GROUP) {
1856                         needs_mask = True;
1857                         mask_perms |= p_ace->perms;
1858                 } else if (p_ace->type == SMB_ACL_GROUP_OBJ) {
1859                         mask_perms |= p_ace->perms;
1860                 }
1861
1862                 /*
1863                  * Get the entry for this ACE.
1864                  */
1865
1866                 if (conn->vfs_ops.sys_acl_create_entry(conn, &the_acl, &the_entry) == -1) {
1867                         DEBUG(0,("set_canon_ace_list: Failed to create entry %d. (%s)\n",
1868                                 i, strerror(errno) ));
1869                         goto done;
1870                 }
1871
1872                 if (p_ace->type == SMB_ACL_MASK) {
1873                         mask_entry = the_entry;
1874                         got_mask_entry = True;
1875                 }
1876
1877                 /*
1878                  * Ok - we now know the ACL calls should be working, don't
1879                  * allow fallback to chmod.
1880                  */
1881
1882                 *pacl_set_support = True;
1883
1884                 /*
1885                  * Initialise the entry from the canon_ace.
1886                  */
1887
1888                 /*
1889                  * First tell the entry what type of ACE this is.
1890                  */
1891
1892                 if (conn->vfs_ops.sys_acl_set_tag_type(conn, the_entry, p_ace->type) == -1) {
1893                         DEBUG(0,("set_canon_ace_list: Failed to set tag type on entry %d. (%s)\n",
1894                                 i, strerror(errno) ));
1895                         goto done;
1896                 }
1897
1898                 /*
1899                  * Only set the qualifier (user or group id) if the entry is a user
1900                  * or group id ACE.
1901                  */
1902
1903                 if ((p_ace->type == SMB_ACL_USER) || (p_ace->type == SMB_ACL_GROUP)) {
1904                         if (conn->vfs_ops.sys_acl_set_qualifier(conn, the_entry,(void *)&p_ace->unix_ug.uid) == -1) {
1905                                 DEBUG(0,("set_canon_ace_list: Failed to set qualifier on entry %d. (%s)\n",
1906                                         i, strerror(errno) ));
1907                                 goto done;
1908                         }
1909                 }
1910
1911                 /*
1912                  * Convert the mode_t perms in the canon_ace to a POSIX permset.
1913                  */
1914
1915                 if (conn->vfs_ops.sys_acl_get_permset(conn, the_entry, &the_permset) == -1) {
1916                         DEBUG(0,("set_canon_ace_list: Failed to get permset on entry %d. (%s)\n",
1917                                 i, strerror(errno) ));
1918                         goto done;
1919                 }
1920
1921                 if (map_acl_perms_to_permset(conn, p_ace->perms, &the_permset) == -1) {
1922                         DEBUG(0,("set_canon_ace_list: Failed to create permset for mode (%u) on entry %d. (%s)\n",
1923                                 (unsigned int)p_ace->perms, i, strerror(errno) ));
1924                         goto done;
1925                 }
1926
1927                 /*
1928                  * ..and apply them to the entry.
1929                  */
1930
1931                 if (conn->vfs_ops.sys_acl_set_permset(conn, the_entry, the_permset) == -1) {
1932                         DEBUG(0,("set_canon_ace_list: Failed to add permset on entry %d. (%s)\n",
1933                                 i, strerror(errno) ));
1934                         goto done;
1935                 }
1936
1937                 if( DEBUGLVL( 10 ))
1938                         print_canon_ace( p_ace, i);
1939         }
1940
1941         if (needs_mask && !got_mask_entry) {
1942                 if (conn->vfs_ops.sys_acl_create_entry(conn, &the_acl, &mask_entry) == -1) {
1943                         DEBUG(0,("set_canon_ace_list: Failed to create mask entry. (%s)\n", strerror(errno) ));
1944                         goto done;
1945                 }
1946
1947                 if (conn->vfs_ops.sys_acl_set_tag_type(conn, mask_entry, SMB_ACL_MASK) == -1) {
1948                         DEBUG(0,("set_canon_ace_list: Failed to set tag type on mask entry. (%s)\n",strerror(errno) ));
1949                         goto done;
1950                 }
1951
1952                 if (conn->vfs_ops.sys_acl_get_permset(conn, mask_entry, &mask_permset) == -1) {
1953                         DEBUG(0,("set_canon_ace_list: Failed to get mask permset. (%s)\n", strerror(errno) ));
1954                         goto done;
1955                 }
1956
1957                 if (map_acl_perms_to_permset(conn, S_IRUSR|S_IWUSR|S_IXUSR, &mask_permset) == -1) {
1958                         DEBUG(0,("set_canon_ace_list: Failed to create mask permset. (%s)\n", strerror(errno) ));
1959                         goto done;
1960                 }
1961
1962                 if (conn->vfs_ops.sys_acl_set_permset(conn, mask_entry, mask_permset) == -1) {
1963                         DEBUG(0,("set_canon_ace_list: Failed to add mask permset. (%s)\n", strerror(errno) ));
1964                         goto done;
1965                 }
1966         }
1967
1968         /*
1969          * Check if the ACL is valid.
1970          */
1971
1972         if (conn->vfs_ops.sys_acl_valid(conn, the_acl) == -1) {
1973                 DEBUG(0,("set_canon_ace_list: ACL type (%s) is invalid for set (%s).\n",
1974                                 the_acl_type == SMB_ACL_TYPE_DEFAULT ? "directory default" : "file",
1975                                 strerror(errno) ));
1976                 goto done;
1977         }
1978
1979         /*
1980          * Finally apply it to the file or directory.
1981          */
1982
1983         if(default_ace || fsp->is_directory || fsp->fd == -1) {
1984                 if (conn->vfs_ops.sys_acl_set_file(conn, fsp->fsp_name, the_acl_type, the_acl) == -1) {
1985                         /*
1986                          * Some systems allow all the above calls and only fail with no ACL support
1987                          * when attempting to apply the acl. HPUX with HFS is an example of this. JRA.
1988                          */
1989                         if (errno == ENOSYS)
1990                                 *pacl_set_support = False;
1991
1992 #ifdef ENOTSUP
1993                         if (errno == ENOTSUP)
1994                                 *pacl_set_support = False;
1995 #endif
1996
1997                         DEBUG(2,("set_canon_ace_list: sys_acl_set_file type %s failed for file %s (%s).\n",
1998                                         the_acl_type == SMB_ACL_TYPE_DEFAULT ? "directory default" : "file",
1999                                         fsp->fsp_name, strerror(errno) ));
2000                         goto done;
2001                 }
2002         } else {
2003                 if (conn->vfs_ops.sys_acl_set_fd(fsp, fsp->fd, the_acl) == -1) {
2004                         /*
2005                          * Some systems allow all the above calls and only fail with no ACL support
2006                          * when attempting to apply the acl. HPUX with HFS is an example of this. JRA.
2007                          */
2008                         if (errno == ENOSYS)
2009                                 *pacl_set_support = False;
2010
2011 #ifdef ENOTSUP
2012                         if (errno == ENOTSUP)
2013                                 *pacl_set_support = False;
2014 #endif
2015
2016                         DEBUG(2,("set_canon_ace_list: sys_acl_set_file failed for file %s (%s).\n",
2017                                         fsp->fsp_name, strerror(errno) ));
2018                         goto done;
2019                 }
2020         }
2021
2022         ret = True;
2023
2024   done:
2025
2026         if (the_acl != NULL)
2027             conn->vfs_ops.sys_acl_free_acl(conn, the_acl);
2028
2029         return ret;
2030 }
2031
2032 /****************************************************************************
2033  Find a particular canon_ace entry.
2034 ****************************************************************************/
2035
2036 static struct canon_ace *canon_ace_entry_for(struct canon_ace *list, SMB_ACL_TAG_T type, posix_id *id)
2037 {
2038         while (list) {
2039                 if (list->type == type && ((type != SMB_ACL_USER && type != SMB_ACL_GROUP) ||
2040                                 (type == SMB_ACL_USER  && id && id->uid == list->unix_ug.uid) ||
2041                                 (type == SMB_ACL_GROUP && id && id->gid == list->unix_ug.gid)))
2042                         break;
2043                 list = list->next;
2044         }
2045         return list;
2046 }
2047
2048 /****************************************************************************
2049  
2050 ****************************************************************************/
2051
2052 SMB_ACL_T free_empty_sys_acl(connection_struct *conn, SMB_ACL_T acl)
2053 {
2054         SMB_ACL_ENTRY_T entry;
2055
2056         if (!acl)
2057                 return NULL;
2058         if (conn->vfs_ops.sys_acl_get_entry(conn, acl, SMB_ACL_FIRST_ENTRY, &entry) != 1) {
2059                 conn->vfs_ops.sys_acl_free_acl(conn, acl);
2060                 return NULL;
2061         }
2062         return acl;
2063 }
2064
2065 /****************************************************************************
2066  Convert a canon_ace to a generic 3 element permission - if possible.
2067 ****************************************************************************/
2068
2069 #define MAP_PERM(p,mask,result) (((p) & (mask)) ? (result) : 0 )
2070
2071 static BOOL convert_canon_ace_to_posix_perms( files_struct *fsp, canon_ace *file_ace_list, mode_t *posix_perms)
2072 {
2073         int snum = SNUM(fsp->conn);
2074         size_t ace_count = count_canon_ace_list(file_ace_list);
2075         canon_ace *ace_p;
2076         canon_ace *owner_ace = NULL;
2077         canon_ace *group_ace = NULL;
2078         canon_ace *other_ace = NULL;
2079         mode_t and_bits;
2080         mode_t or_bits;
2081
2082         if (ace_count != 3) {
2083                 DEBUG(3,("convert_canon_ace_to_posix_perms: Too many ACE entries for file %s to convert to \
2084 posix perms.\n", fsp->fsp_name ));
2085                 return False;
2086         }
2087
2088         for (ace_p = file_ace_list; ace_p; ace_p = ace_p->next) {
2089                 if (ace_p->owner_type == UID_ACE)
2090                         owner_ace = ace_p;
2091                 else if (ace_p->owner_type == GID_ACE)
2092                         group_ace = ace_p;
2093                 else if (ace_p->owner_type == WORLD_ACE)
2094                         other_ace = ace_p;
2095         }
2096
2097         if (!owner_ace || !group_ace || !other_ace) {
2098                 DEBUG(3,("convert_canon_ace_to_posix_perms: Can't get standard entries for file %s.\n",
2099                                 fsp->fsp_name ));
2100                 return False;
2101         }
2102
2103         *posix_perms = (mode_t)0;
2104
2105         *posix_perms |= owner_ace->perms;
2106         *posix_perms |= MAP_PERM(group_ace->perms, S_IRUSR, S_IRGRP);
2107         *posix_perms |= MAP_PERM(group_ace->perms, S_IWUSR, S_IWGRP);
2108         *posix_perms |= MAP_PERM(group_ace->perms, S_IXUSR, S_IXGRP);
2109         *posix_perms |= MAP_PERM(other_ace->perms, S_IRUSR, S_IROTH);
2110         *posix_perms |= MAP_PERM(other_ace->perms, S_IWUSR, S_IWOTH);
2111         *posix_perms |= MAP_PERM(other_ace->perms, S_IXUSR, S_IXOTH);
2112
2113         /* The owner must have at least read access. */
2114
2115         *posix_perms |= S_IRUSR;
2116         if (fsp->is_directory)
2117                 *posix_perms |= (S_IWUSR|S_IXUSR);
2118
2119         /* If requested apply the masks. */
2120
2121         /* Get the initial bits to apply. */
2122
2123         if (fsp->is_directory) {
2124                 and_bits = lp_dir_security_mask(snum);
2125                 or_bits = lp_force_dir_security_mode(snum);
2126         } else {
2127                 and_bits = lp_security_mask(snum);
2128                 or_bits = lp_force_security_mode(snum);
2129         }
2130
2131         *posix_perms = (((*posix_perms) & and_bits)|or_bits);
2132
2133         DEBUG(10,("convert_canon_ace_to_posix_perms: converted u=%o,g=%o,w=%o to perm=0%o for file %s.\n",
2134                 (int)owner_ace->perms, (int)group_ace->perms, (int)other_ace->perms, (int)*posix_perms,
2135                 fsp->fsp_name ));
2136
2137         return True;
2138 }
2139
2140 static int nt_ace_comp( SEC_ACE *a1, SEC_ACE *a2)
2141 {
2142         if (a1->type == a2->type)
2143                 return 0;
2144
2145         if (a1->type == SEC_ACE_TYPE_ACCESS_DENIED && a2->type == SEC_ACE_TYPE_ACCESS_ALLOWED)
2146                 return -1;
2147         return 1;
2148 }
2149
2150 /****************************************************************************
2151   Incoming NT ACLs on a directory can be split into a default POSIX acl (CI|OI|IO) and
2152   a normal POSIX acl. Win2k needs these split acls re-merging into one ACL
2153   with CI|OI set so it is inherited and also applies to the directory.
2154   Based on code from "Jim McDonough" <jmcd@us.ibm.com>.
2155 ****************************************************************************/
2156
2157 static size_t merge_default_aces( SEC_ACE *nt_ace_list, size_t num_aces)
2158 {
2159         size_t i, j;
2160
2161         for (i = 0; i < num_aces; i++) {
2162                 for (j = i+1; j < num_aces; j++) {
2163                         /* We know the lower number ACE's are file entries. */
2164                         if ((nt_ace_list[i].type == nt_ace_list[j].type) &&
2165                                 (nt_ace_list[i].size == nt_ace_list[j].size) &&
2166                                 (nt_ace_list[i].info.mask == nt_ace_list[j].info.mask) &&
2167                                 sid_equal(&nt_ace_list[i].trustee, &nt_ace_list[j].trustee) &&
2168                                 (nt_ace_list[i].flags == 0) &&
2169                                 (nt_ace_list[j].flags == (SEC_ACE_FLAG_OBJECT_INHERIT|
2170                                                           SEC_ACE_FLAG_CONTAINER_INHERIT|
2171                                                           SEC_ACE_FLAG_INHERIT_ONLY))) {
2172                                 /*
2173                                  * These are identical except for the flags.
2174                                  * Merge the inherited ACE onto the non-inherited ACE.
2175                                  */
2176
2177                                 nt_ace_list[i].flags = SEC_ACE_FLAG_OBJECT_INHERIT|SEC_ACE_FLAG_CONTAINER_INHERIT;
2178                                 if (num_aces - j - 1 > 0)
2179                                         memmove(&nt_ace_list[j], &nt_ace_list[j+1], (num_aces-j-1) *
2180                                                         sizeof(SEC_ACE));
2181                                 num_aces--;
2182                                 break;
2183                         }
2184                 }
2185         }
2186
2187         return num_aces;
2188 }
2189 /****************************************************************************
2190  Reply to query a security descriptor from an fsp. If it succeeds it allocates
2191  the space for the return elements and returns the size needed to return the
2192  security descriptor. This should be the only external function needed for
2193  the UNIX style get ACL.
2194 ****************************************************************************/
2195
2196 size_t get_nt_acl(files_struct *fsp, SEC_DESC **ppdesc)
2197 {
2198         extern DOM_SID global_sid_Builtin_Administrators;
2199         extern DOM_SID global_sid_Builtin_Users;
2200         extern DOM_SID global_sid_Creator_Owner;
2201         extern DOM_SID global_sid_Creator_Group;
2202         connection_struct *conn = fsp->conn;
2203         SMB_STRUCT_STAT sbuf;
2204         SEC_ACE *nt_ace_list = NULL;
2205         DOM_SID owner_sid;
2206         DOM_SID group_sid;
2207         size_t sd_size = 0;
2208         SEC_ACL *psa = NULL;
2209         size_t num_acls = 0;
2210         size_t num_dir_acls = 0;
2211         size_t num_aces = 0;
2212         SMB_ACL_T posix_acl = NULL;
2213         SMB_ACL_T dir_acl = NULL;
2214         canon_ace *file_ace = NULL;
2215         canon_ace *dir_ace = NULL;
2216         size_t num_profile_acls = 0;
2217  
2218         *ppdesc = NULL;
2219
2220         DEBUG(10,("get_nt_acl: called for file %s\n", fsp->fsp_name ));
2221
2222         if(fsp->is_directory || fsp->fd == -1) {
2223
2224                 /* Get the stat struct for the owner info. */
2225                 if(vfs_stat(fsp->conn,fsp->fsp_name, &sbuf) != 0) {
2226                         return 0;
2227                 }
2228                 /*
2229                  * Get the ACL from the path.
2230                  */
2231
2232                 posix_acl = conn->vfs_ops.sys_acl_get_file(conn, fsp->fsp_name, SMB_ACL_TYPE_ACCESS);
2233
2234                 /*
2235                  * If it's a directory get the default POSIX ACL.
2236                  */
2237
2238                 if(fsp->is_directory) {
2239                         dir_acl = conn->vfs_ops.sys_acl_get_file(conn, fsp->fsp_name, SMB_ACL_TYPE_DEFAULT);
2240                         dir_acl = free_empty_sys_acl(conn, dir_acl);
2241                 }
2242
2243         } else {
2244
2245                 /* Get the stat struct for the owner info. */
2246                 if(vfs_fstat(fsp,fsp->fd,&sbuf) != 0) {
2247                         return 0;
2248                 }
2249                 /*
2250                  * Get the ACL from the fd.
2251                  */
2252                 posix_acl = conn->vfs_ops.sys_acl_get_fd(fsp, fsp->fd);
2253         }
2254
2255         DEBUG(5,("get_nt_acl : file ACL %s, directory ACL %s\n",
2256                         posix_acl ? "present" :  "absent",
2257                         dir_acl ? "present" :  "absent" ));
2258
2259         /*
2260          * Get the owner, group and world SIDs.
2261          */
2262
2263         if (lp_profile_acls(SNUM(fsp->conn))) {
2264                 /* For WXP SP1 the owner must be administrators. */
2265                 sid_copy(&owner_sid, &global_sid_Builtin_Administrators);
2266                 sid_copy(&group_sid, &global_sid_Builtin_Users);
2267                 num_profile_acls = 2;
2268         } else {
2269                 create_file_sids(&sbuf, &owner_sid, &group_sid);
2270         }
2271
2272         /*
2273          * In the optimum case Creator Owner and Creator Group would be used for
2274          * the ACL_USER_OBJ and ACL_GROUP_OBJ entries, respectively, but this
2275          * would lead to usability problems under Windows: The Creator entries
2276          * are only available in browse lists of directories and not for files;
2277          * additionally the identity of the owning group couldn't be determined.
2278          * We therefore use those identities only for Default ACLs. 
2279          */
2280
2281         /* Create the canon_ace lists. */
2282         file_ace = canonicalise_acl( fsp, posix_acl, &sbuf, &owner_sid, &group_sid, SMB_ACL_TYPE_ACCESS );
2283
2284         /* We must have *some* ACLS. */
2285
2286         if (count_canon_ace_list(file_ace) == 0) {
2287                 DEBUG(0,("get_nt_acl : No ACLs on file (%s) !\n", fsp->fsp_name ));
2288                 return 0;
2289         }
2290
2291         if (fsp->is_directory && dir_acl) {
2292                 dir_ace = canonicalise_acl(fsp, dir_acl, &sbuf,
2293                                 &global_sid_Creator_Owner,
2294                                 &global_sid_Creator_Group, SMB_ACL_TYPE_DEFAULT );
2295         }
2296
2297         /*
2298          * Create the NT ACE list from the canonical ace lists.
2299          */
2300
2301         {
2302                 canon_ace *ace;
2303                 int nt_acl_type;
2304                 int i;
2305
2306                 if (nt4_compatible_acls()) {
2307                         /*
2308                          * NT 4 chokes if an ACL contains an INHERIT_ONLY entry
2309                          * but no non-INHERIT_ONLY entry for one SID. So we only
2310                          * remove entries from the Access ACL if the
2311                          * corresponding Default ACL entries have also been
2312                          * removed. ACEs for CREATOR-OWNER and CREATOR-GROUP
2313                          * are exceptions. We can do nothing
2314                          * intelligent if the Default ACL contains entries that
2315                          * are not also contained in the Access ACL, so this
2316                          * case will still fail under NT 4.
2317                          */
2318
2319                         if (!dir_ace)
2320                                 goto simplify_file_ace_only;
2321
2322                         ace = canon_ace_entry_for(dir_ace, SMB_ACL_OTHER, NULL);
2323                         if (ace && !ace->perms) {
2324                                 DLIST_REMOVE(dir_ace, ace);
2325                                 SAFE_FREE(ace);
2326
2327                                 ace = canon_ace_entry_for(file_ace, SMB_ACL_OTHER, NULL);
2328                                 if (ace && !ace->perms) {
2329                                         DLIST_REMOVE(file_ace, ace);
2330                                         SAFE_FREE(ace);
2331                                 }
2332                         }
2333
2334                         /*
2335                          * WinNT doesn't usually have Creator Group
2336                          * in browse lists, so we send this entry to
2337                          * WinNT even if it contains no relevant
2338                          * permissions. Once we can add
2339                          * Creator Group to browse lists we can
2340                          * re-enable this.
2341                          */
2342
2343 #if 0
2344                         ace = canon_ace_entry_for(dir_ace, SMB_ACL_GROUP_OBJ, NULL);
2345                         if (ace && !ace->perms) {
2346                                 DLIST_REMOVE(dir_ace, ace);
2347                                 SAFE_FREE(ace);
2348                         }
2349 #endif
2350
2351                         ace = canon_ace_entry_for(file_ace, SMB_ACL_GROUP_OBJ, NULL);
2352                         if (ace && !ace->perms) {
2353                                 DLIST_REMOVE(file_ace, ace);
2354                                 SAFE_FREE(ace);
2355                         }
2356                 } else {
2357
2358                         ace = canon_ace_entry_for(dir_ace, SMB_ACL_OTHER, NULL);
2359                         if (ace && !ace->perms) {
2360                                 DLIST_REMOVE(dir_ace, ace);
2361                                 SAFE_FREE(ace);
2362                         }
2363                         ace = canon_ace_entry_for(dir_ace, SMB_ACL_GROUP_OBJ, NULL);
2364                         if (ace && !ace->perms) {
2365                                 DLIST_REMOVE(dir_ace, ace);
2366                                 SAFE_FREE(ace);
2367                         }
2368
2369  simplify_file_ace_only:
2370
2371                         ace = canon_ace_entry_for(file_ace, SMB_ACL_OTHER, NULL);
2372                         if (ace && !ace->perms) {
2373                                 DLIST_REMOVE(file_ace, ace);
2374                                 SAFE_FREE(ace);
2375                         }
2376
2377                         ace = canon_ace_entry_for(file_ace, SMB_ACL_GROUP_OBJ, NULL);
2378                         if (ace && !ace->perms) {
2379                                 DLIST_REMOVE(file_ace, ace);
2380                                 SAFE_FREE(ace);
2381                         }
2382                 }
2383
2384                 num_acls = count_canon_ace_list(file_ace);
2385                 num_dir_acls = count_canon_ace_list(dir_ace);
2386
2387                 /* Allocate the ace list. */
2388                 if ((nt_ace_list = (SEC_ACE *)malloc((num_acls + num_profile_acls + num_dir_acls)* sizeof(SEC_ACE))) == NULL) {
2389                         DEBUG(0,("get_nt_acl: Unable to malloc space for nt_ace_list.\n"));
2390                         goto done;
2391                 }
2392
2393                 memset(nt_ace_list, '\0', (num_acls + num_dir_acls) * sizeof(SEC_ACE) );
2394                                                                                                 
2395                 /*
2396                  * Create the NT ACE list from the canonical ace lists.
2397                  */
2398
2399                 ace = file_ace;
2400
2401                 for (i = 0; i < num_acls; i++, ace = ace->next) {
2402                         SEC_ACCESS acc;
2403
2404                         acc = map_canon_ace_perms(&nt_acl_type, &owner_sid, ace );
2405                         init_sec_ace(&nt_ace_list[num_aces++], &ace->trustee, nt_acl_type, acc, 0);
2406                 }
2407
2408                 /* The User must have access to a profile share - even if we can't map the SID. */
2409                 if (lp_profile_acls(SNUM(fsp->conn))) {
2410                         SEC_ACCESS acc;
2411
2412                         init_sec_access(&acc,FILE_GENERIC_ALL);
2413                         init_sec_ace(&nt_ace_list[num_aces++], &global_sid_Builtin_Users, SEC_ACE_TYPE_ACCESS_ALLOWED, acc, 0);
2414                 }
2415
2416                 ace = dir_ace;
2417
2418                 for (i = 0; i < num_dir_acls; i++, ace = ace->next) {
2419                         SEC_ACCESS acc;
2420
2421                         acc = map_canon_ace_perms(&nt_acl_type, &owner_sid, ace );
2422                         init_sec_ace(&nt_ace_list[num_aces++], &ace->trustee, nt_acl_type, acc,
2423                                         SEC_ACE_FLAG_OBJECT_INHERIT|SEC_ACE_FLAG_CONTAINER_INHERIT|SEC_ACE_FLAG_INHERIT_ONLY);
2424                 }
2425
2426                 /* The User must have access to a profile share - even if we can't map the SID. */
2427                 if (lp_profile_acls(SNUM(fsp->conn))) {
2428                         SEC_ACCESS acc;
2429                         
2430                         init_sec_access(&acc,FILE_GENERIC_ALL);
2431                         init_sec_ace(&nt_ace_list[num_aces++], &global_sid_Builtin_Users, SEC_ACE_TYPE_ACCESS_ALLOWED, acc,
2432                                         SEC_ACE_FLAG_OBJECT_INHERIT|SEC_ACE_FLAG_CONTAINER_INHERIT|
2433                                         SEC_ACE_FLAG_INHERIT_ONLY);
2434                 }
2435
2436                 /*
2437                  * Merge POSIX default ACLs and normal ACLs into one NT ACE.
2438                  * Win2K needs this to get the inheritance correct when replacing ACLs
2439                  * on a directory tree. Based on work by Jim @ IBM.
2440                  */
2441
2442                 num_aces = merge_default_aces(nt_ace_list, num_aces);
2443
2444                 /*
2445                  * Sort to force deny entries to the front.
2446                  */
2447
2448                 if (num_aces)
2449                         qsort( nt_ace_list, num_aces, sizeof(nt_ace_list[0]), QSORT_CAST nt_ace_comp);
2450         }
2451
2452         if (num_aces) {
2453                 if((psa = make_sec_acl( main_loop_talloc_get(), ACL_REVISION, num_aces, nt_ace_list)) == NULL) {
2454                         DEBUG(0,("get_nt_acl: Unable to malloc space for acl.\n"));
2455                         goto done;
2456                 }
2457         }
2458
2459         *ppdesc = make_standard_sec_desc( main_loop_talloc_get(), &owner_sid, &group_sid, psa, &sd_size);
2460
2461         if(!*ppdesc) {
2462                 DEBUG(0,("get_nt_acl: Unable to malloc space for security descriptor.\n"));
2463                 sd_size = 0;
2464         } else {
2465                 /*
2466                  * Windows 2000: The DACL_PROTECTED flag in the security
2467                  * descriptor marks the ACL as non-inheriting, i.e., no
2468                  * ACEs from higher level directories propagate to this
2469                  * ACL. In the POSIX ACL model permissions are only
2470                  * inherited at file create time, so ACLs never contain
2471                  * any ACEs that are inherited dynamically. The DACL_PROTECTED
2472                  * flag doesn't seem to bother Windows NT.
2473                  */
2474                 (*ppdesc)->type |= SE_DESC_DACL_PROTECTED;
2475         }
2476
2477  done:
2478
2479         if (posix_acl)
2480                 conn->vfs_ops.sys_acl_free_acl(conn, posix_acl);
2481         if (dir_acl)
2482                 conn->vfs_ops.sys_acl_free_acl(conn, dir_acl);
2483         free_canon_ace_list(file_ace);
2484         free_canon_ace_list(dir_ace);
2485         SAFE_FREE(nt_ace_list);
2486
2487         return sd_size;
2488 }
2489
2490 /****************************************************************************
2491  Try to chown a file. We will be able to chown it under the following conditions.
2492
2493   1) If we have root privileges, then it will just work.
2494   2) If we have write permission to the file and dos_filemodes is set
2495      then allow chown to the currently authenticated user.
2496 ****************************************************************************/
2497
2498 static int try_chown(connection_struct *conn, const char *fname, uid_t uid, gid_t gid)
2499 {
2500         int ret;
2501         extern struct current_user current_user;
2502         files_struct *fsp;
2503         SMB_STRUCT_STAT st;
2504
2505         /* try the direct way first */
2506         ret = vfs_chown(conn, fname, uid, gid);
2507         if (ret == 0)
2508                 return 0;
2509
2510         if(!CAN_WRITE(conn) || !lp_dos_filemode(SNUM(conn)))
2511                 return -1;
2512
2513         if (vfs_stat(conn,fname,&st))
2514                 return -1;
2515
2516         fsp = open_file_fchmod(conn,fname,&st);
2517         if (!fsp)
2518                 return -1;
2519
2520         /* only allow chown to the current user. This is more secure,
2521            and also copes with the case where the SID in a take ownership ACL is
2522            a local SID on the users workstation 
2523         */
2524         uid = current_user.uid;
2525
2526         become_root();
2527         /* Keep the current file gid the same. */
2528         ret = vfswrap_fchown(fsp, fsp->fd, uid, (gid_t)-1);
2529         unbecome_root();
2530
2531         close_file_fchmod(fsp);
2532
2533         return ret;
2534 }
2535
2536 /****************************************************************************
2537  Reply to set a security descriptor on an fsp. security_info_sent is the
2538  description of the following NT ACL.
2539  This should be the only external function needed for the UNIX style set ACL.
2540 ****************************************************************************/
2541
2542 BOOL set_nt_acl(files_struct *fsp, uint32 security_info_sent, SEC_DESC *psd)
2543 {
2544         connection_struct *conn = fsp->conn;
2545         uid_t user = (uid_t)-1;
2546         gid_t grp = (gid_t)-1;
2547         SMB_STRUCT_STAT sbuf;  
2548         DOM_SID file_owner_sid;
2549         DOM_SID file_grp_sid;
2550         canon_ace *file_ace_list = NULL;
2551         canon_ace *dir_ace_list = NULL;
2552         BOOL acl_perms = False;
2553         mode_t orig_mode = (mode_t)0;
2554         uid_t orig_uid;
2555         gid_t orig_gid;
2556         BOOL need_chown = False;
2557         extern struct current_user current_user;
2558
2559         DEBUG(10,("set_nt_acl: called for file %s\n", fsp->fsp_name ));
2560
2561         if (!CAN_WRITE(conn)) {
2562                 DEBUG(10,("set acl rejected on read-only share\n"));
2563                 return False;
2564         }
2565
2566         /*
2567          * Get the current state of the file.
2568          */
2569
2570         if(fsp->is_directory || fsp->fd == -1) {
2571                 if(vfs_stat(fsp->conn,fsp->fsp_name, &sbuf) != 0)
2572                         return False;
2573         } else {
2574                 if(vfs_fstat(fsp,fsp->fd,&sbuf) != 0)
2575                         return False;
2576         }
2577
2578         /* Save the original elements we check against. */
2579         orig_mode = sbuf.st_mode;
2580         orig_uid = sbuf.st_uid;
2581         orig_gid = sbuf.st_gid;
2582
2583         /*
2584          * Unpack the user/group/world id's.
2585          */
2586
2587         if (!unpack_nt_owners( &sbuf, &user, &grp, security_info_sent, psd))
2588                 return False;
2589
2590         /*
2591          * Do we need to chown ?
2592          */
2593
2594         if (((user != (uid_t)-1) && (orig_uid != user)) || (( grp != (gid_t)-1) && (orig_gid != grp)))
2595                 need_chown = True;
2596
2597         /*
2598          * Chown before setting ACL only if we don't change the user, or
2599          * if we change to the current user, but not if we want to give away
2600          * the file.
2601          */
2602
2603         if (need_chown && (user == (uid_t)-1 || user == current_user.uid)) {
2604
2605                 DEBUG(3,("set_nt_acl: chown %s. uid = %u, gid = %u.\n",
2606                                 fsp->fsp_name, (unsigned int)user, (unsigned int)grp ));
2607
2608                 if(try_chown( fsp->conn, fsp->fsp_name, user, grp) == -1) {
2609                         DEBUG(3,("set_nt_acl: chown %s, %u, %u failed. Error = %s.\n",
2610                                 fsp->fsp_name, (unsigned int)user, (unsigned int)grp, strerror(errno) ));
2611                         return False;
2612                 }
2613
2614                 /*
2615                  * Recheck the current state of the file, which may have changed.
2616                  * (suid/sgid bits, for instance)
2617                  */
2618
2619                 if(fsp->is_directory) {
2620                         if(vfs_stat(fsp->conn, fsp->fsp_name, &sbuf) != 0) {
2621                                 return False;
2622                         }
2623                 } else {
2624
2625                         int ret;
2626     
2627                         if(fsp->fd == -1)
2628                                 ret = vfs_stat(fsp->conn, fsp->fsp_name, &sbuf);
2629                         else
2630                                 ret = vfs_fstat(fsp,fsp->fd,&sbuf);
2631   
2632                         if(ret != 0)
2633                                 return False;
2634                 }
2635
2636                 /* Save the original elements we check against. */
2637                 orig_mode = sbuf.st_mode;
2638                 orig_uid = sbuf.st_uid;
2639                 orig_gid = sbuf.st_gid;
2640
2641                 /* We did it, don't try again */
2642                 need_chown = False;
2643         }
2644
2645         create_file_sids(&sbuf, &file_owner_sid, &file_grp_sid);
2646
2647         acl_perms = unpack_canon_ace( fsp, &sbuf, &file_owner_sid, &file_grp_sid,
2648                                                                         &file_ace_list, &dir_ace_list, security_info_sent, psd);
2649
2650         /* Ignore W2K traverse DACL set. */
2651         if (file_ace_list || dir_ace_list) {
2652
2653                 if (!acl_perms) {
2654                         DEBUG(3,("set_nt_acl: cannot set permissions\n"));
2655                         free_canon_ace_list(file_ace_list);
2656                         free_canon_ace_list(dir_ace_list); 
2657                         return False;
2658                 }
2659
2660                 /*
2661                  * Only change security if we got a DACL.
2662                  */
2663
2664                 if((security_info_sent & DACL_SECURITY_INFORMATION) && (psd->dacl != NULL)) {
2665
2666                         BOOL acl_set_support = False;
2667                         BOOL ret = False;
2668
2669                         /*
2670                          * Try using the POSIX ACL set first. Fall back to chmod if
2671                          * we have no ACL support on this filesystem.
2672                          */
2673
2674                         if (acl_perms && file_ace_list) {
2675                                 ret = set_canon_ace_list(fsp, file_ace_list, False, &acl_set_support);
2676                                 if (acl_set_support && ret == False) {
2677                                         DEBUG(3,("set_nt_acl: failed to set file acl on file %s (%s).\n", fsp->fsp_name, strerror(errno) ));
2678                                         free_canon_ace_list(file_ace_list);
2679                                         free_canon_ace_list(dir_ace_list); 
2680                                         return False;
2681                                 }
2682                         }
2683
2684                         if (acl_perms && acl_set_support && fsp->is_directory) {
2685                                 if (dir_ace_list) {
2686                                         if (!set_canon_ace_list(fsp, dir_ace_list, True, &acl_set_support)) {
2687                                                 DEBUG(3,("set_nt_acl: failed to set default acl on directory %s (%s).\n", fsp->fsp_name, strerror(errno) ));
2688                                                 free_canon_ace_list(file_ace_list);
2689                                                 free_canon_ace_list(dir_ace_list); 
2690                                                 return False;
2691                                         }
2692                                 } else {
2693
2694                                         /*
2695                                          * No default ACL - delete one if it exists.
2696                                          */
2697
2698                                         if (conn->vfs_ops.sys_acl_delete_def_file(conn, fsp->fsp_name) == -1) {
2699                                                 DEBUG(3,("set_nt_acl: sys_acl_delete_def_file failed (%s)\n", strerror(errno)));
2700                                                 free_canon_ace_list(file_ace_list);
2701                                                 free_canon_ace_list(dir_ace_list);
2702                                                 return False;
2703                                         }
2704                                 }
2705                         }
2706
2707                         /*
2708                          * If we cannot set using POSIX ACLs we fall back to checking if we need to chmod.
2709                          */
2710
2711                         if(!acl_set_support && acl_perms) {
2712                                 mode_t posix_perms;
2713
2714                                 if (!convert_canon_ace_to_posix_perms( fsp, file_ace_list, &posix_perms)) {
2715                                         free_canon_ace_list(file_ace_list);
2716                                         free_canon_ace_list(dir_ace_list);
2717                                         DEBUG(3,("set_nt_acl: failed to convert file acl to posix permissions for file %s.\n",
2718                                                 fsp->fsp_name ));
2719                                         return False;
2720                                 }
2721
2722                                 if (orig_mode != posix_perms) {
2723
2724                                         DEBUG(3,("set_nt_acl: chmod %s. perms = 0%o.\n",
2725                                                 fsp->fsp_name, (unsigned int)posix_perms ));
2726
2727                                         if(conn->vfs_ops.chmod(conn,fsp->fsp_name, posix_perms) == -1) {
2728                                                 DEBUG(3,("set_nt_acl: chmod %s, 0%o failed. Error = %s.\n",
2729                                                                 fsp->fsp_name, (unsigned int)posix_perms, strerror(errno) ));
2730                                                 free_canon_ace_list(file_ace_list);
2731                                                 free_canon_ace_list(dir_ace_list);
2732                                                 return False;
2733                                         }
2734                                 }
2735                         }
2736                 }
2737
2738                 free_canon_ace_list(file_ace_list);
2739                 free_canon_ace_list(dir_ace_list); 
2740         }
2741
2742         /* Any chown pending? */
2743         if (need_chown) {
2744
2745                 DEBUG(3,("set_nt_acl: chown %s. uid = %u, gid = %u.\n",
2746                         fsp->fsp_name, (unsigned int)user, (unsigned int)grp ));
2747
2748                 if(try_chown( fsp->conn, fsp->fsp_name, user, grp) == -1) {
2749                         DEBUG(3,("set_nt_acl: chown %s, %u, %u failed. Error = %s.\n",
2750                                 fsp->fsp_name, (unsigned int)user, (unsigned int)grp, strerror(errno) ));
2751                         return False;
2752                 }
2753         }
2754
2755         return True;
2756 }
2757
2758 /****************************************************************************
2759  Do a chmod by setting the ACL USER_OBJ, GROUP_OBJ and OTHER bits in an ACL
2760  and set the mask to rwx. Needed to preserve complex ACLs set by NT.
2761 ****************************************************************************/
2762
2763 static int chmod_acl_internals( connection_struct *conn, SMB_ACL_T posix_acl, mode_t mode)
2764 {
2765         int entry_id = SMB_ACL_FIRST_ENTRY;
2766         SMB_ACL_ENTRY_T entry;
2767         int num_entries = 0;
2768
2769         while ( conn->vfs_ops.sys_acl_get_entry(conn, posix_acl, entry_id, &entry) == 1) {
2770                 SMB_ACL_TAG_T tagtype;
2771                 SMB_ACL_PERMSET_T permset;
2772                 mode_t perms;
2773
2774                 /* get_next... */
2775                 if (entry_id == SMB_ACL_FIRST_ENTRY)
2776                         entry_id = SMB_ACL_NEXT_ENTRY;
2777
2778                 if (conn->vfs_ops.sys_acl_get_tag_type(conn, entry, &tagtype) == -1)
2779                         return -1;
2780
2781                 if (conn->vfs_ops.sys_acl_get_permset(conn, entry, &permset) == -1)
2782                         return -1;
2783
2784                 num_entries++;
2785
2786                 switch(tagtype) {
2787                         case SMB_ACL_USER_OBJ:
2788                                 perms = unix_perms_to_acl_perms(mode, S_IRUSR, S_IWUSR, S_IXUSR);
2789                                 break;
2790                         case SMB_ACL_GROUP_OBJ:
2791                                 perms = unix_perms_to_acl_perms(mode, S_IRGRP, S_IWGRP, S_IXGRP);
2792                                 break;
2793                         case SMB_ACL_MASK:
2794                                 /*
2795                                  * FIXME: The ACL_MASK entry permissions should really be set to
2796                                  * the union of the permissions of all ACL_USER,
2797                                  * ACL_GROUP_OBJ, and ACL_GROUP entries. That's what
2798                                  * acl_calc_mask() does, but Samba ACLs doesn't provide it.
2799                                  */
2800                                 perms = S_IRUSR|S_IWUSR|S_IXUSR;
2801                                 break;
2802                         case SMB_ACL_OTHER:
2803                                 perms = unix_perms_to_acl_perms(mode, S_IROTH, S_IWOTH, S_IXOTH);
2804                                 break;
2805                         default:
2806                                 continue;
2807                 }
2808
2809                 if (map_acl_perms_to_permset(conn, perms, &permset) == -1)
2810                         return -1;
2811
2812                 if (conn->vfs_ops.sys_acl_set_permset(conn, entry, permset) == -1)
2813                         return -1;
2814         }
2815
2816         /*
2817          * If this is a simple 3 element ACL or no elements then it's a standard
2818          * UNIX permission set. Just use chmod...       
2819          */
2820
2821         if ((num_entries == 3) || (num_entries == 0))
2822                 return -1;
2823
2824         return 0;
2825 }
2826
2827 /****************************************************************************
2828  Get the access ACL of FROM, do a chmod by setting the ACL USER_OBJ,
2829  GROUP_OBJ and OTHER bits in an ACL and set the mask to rwx. Set the
2830  resulting ACL on TO.  Note that name is in UNIX character set.
2831 ****************************************************************************/
2832
2833 static int copy_access_acl(connection_struct *conn, const char *from, const char *to, mode_t mode)
2834 {
2835         SMB_ACL_T posix_acl = NULL;
2836         int ret = -1;
2837
2838         if ((posix_acl = conn->vfs_ops.sys_acl_get_file(conn, from, SMB_ACL_TYPE_ACCESS)) == NULL)
2839                 return -1;
2840
2841         if ((ret = chmod_acl_internals(conn, posix_acl, mode)) == -1)
2842                 goto done;
2843
2844         ret = conn->vfs_ops.sys_acl_set_file(conn, to, SMB_ACL_TYPE_ACCESS, posix_acl);
2845
2846  done:
2847
2848         conn->vfs_ops.sys_acl_free_acl(conn, posix_acl);
2849         return ret;
2850 }
2851
2852 /****************************************************************************
2853  Do a chmod by setting the ACL USER_OBJ, GROUP_OBJ and OTHER bits in an ACL
2854  and set the mask to rwx. Needed to preserve complex ACLs set by NT.
2855  Note that name is in UNIX character set.
2856 ****************************************************************************/
2857
2858 int chmod_acl(connection_struct *conn, const char *name, mode_t mode)
2859 {
2860         return copy_access_acl(conn, name, name, mode);
2861 }
2862
2863 /****************************************************************************
2864  If "inherit permissions" is set and the parent directory has no default
2865  ACL but it does have an Access ACL, inherit this Access ACL to file name.
2866 ****************************************************************************/
2867
2868 int inherit_access_acl(connection_struct *conn, const char *name, mode_t mode)
2869 {
2870         pstring dirname;
2871         pstrcpy(dirname, parent_dirname(name));
2872
2873         if (!lp_inherit_perms(SNUM(conn)) || directory_has_default_acl(conn, dirname))
2874                 return 0;
2875
2876         return copy_access_acl(conn, dirname, name, mode);
2877 }
2878
2879 /****************************************************************************
2880  Do an fchmod by setting the ACL USER_OBJ, GROUP_OBJ and OTHER bits in an ACL
2881  and set the mask to rwx. Needed to preserve complex ACLs set by NT.
2882 ****************************************************************************/
2883
2884 int fchmod_acl(files_struct *fsp, int fd, mode_t mode)
2885 {
2886         connection_struct *conn = fsp->conn;
2887         SMB_ACL_T posix_acl = NULL;
2888         int ret = -1;
2889
2890         if ((posix_acl = conn->vfs_ops.sys_acl_get_fd(fsp, fd)) == NULL)
2891                 return -1;
2892
2893         if ((ret = chmod_acl_internals(conn, posix_acl, mode)) == -1)
2894                 goto done;
2895
2896         ret = conn->vfs_ops.sys_acl_set_fd(fsp, fd, posix_acl);
2897
2898   done:
2899
2900         conn->vfs_ops.sys_acl_free_acl(conn, posix_acl);
2901         return ret;
2902 }
2903
2904 /****************************************************************************
2905  Check for an existing default POSIX ACL on a directory.
2906 ****************************************************************************/
2907
2908 BOOL directory_has_default_acl(connection_struct *conn, const char *fname)
2909 {
2910         SMB_ACL_T dir_acl = conn->vfs_ops.sys_acl_get_file( conn, fname, SMB_ACL_TYPE_DEFAULT);
2911         BOOL has_acl = False;
2912         SMB_ACL_ENTRY_T entry;
2913
2914         if (dir_acl != NULL && (conn->vfs_ops.sys_acl_get_entry(conn, dir_acl, SMB_ACL_FIRST_ENTRY, &entry) == 1))
2915                 has_acl = True;
2916
2917         if (dir_acl)
2918                 conn->vfs_ops.sys_acl_free_acl(conn, dir_acl);
2919         return has_acl;
2920 }