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