Sync up with 2.2 ACL code.
[tprouty/samba.git] / source / smbd / posix_acls.c
1 /*
2    Unix SMB/Netbios implementation.
3    Version 1.9.
4    SMB NT Security Descriptor / Unix permission conversion.
5    Copyright (C) Jeremy Allison 1994-2000
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 sid;
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                 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  Function to create owner and group SIDs from a SMB_STRUCT_STAT.
99 ****************************************************************************/
100
101 static void create_file_sids(SMB_STRUCT_STAT *psbuf, DOM_SID *powner_sid, DOM_SID *pgroup_sid)
102 {
103         uid_to_sid( powner_sid, psbuf->st_uid );
104         gid_to_sid( pgroup_sid, psbuf->st_gid );
105 }
106
107 /****************************************************************************
108  Print out a canon ace.
109 ****************************************************************************/
110
111 static void print_canon_ace(canon_ace *pace, int num)
112 {
113         fstring str;
114
115         dbgtext( "canon_ace index %d. Type = %s ", num, pace->attr == ALLOW_ACE ? "allow" : "deny" );
116     dbgtext( "SID = %s ", sid_to_string( str, &pace->sid));
117         if (pace->owner_type == UID_ACE) {
118                 struct passwd *pass = sys_getpwuid(pace->unix_ug.uid);
119                 dbgtext( "uid %u (%s) ", (unsigned int)pace->unix_ug.uid, pass ? pass->pw_name : "UNKNOWN");
120         } else if (pace->owner_type == GID_ACE) {
121                 struct group *grp = getgrgid(pace->unix_ug.gid);
122                 dbgtext( "gid %u (%s) ", (unsigned int)pace->unix_ug.gid, grp ? grp->gr_name : "UNKNOWN");
123         } else
124                 dbgtext( "other ");
125         switch (pace->type) {
126                 case SMB_ACL_USER:
127                         dbgtext( "SMB_ACL_USER ");
128                         break;
129                 case SMB_ACL_USER_OBJ:
130                         dbgtext( "SMB_ACL_USER_OBJ ");
131                         break;
132                 case SMB_ACL_GROUP:
133                         dbgtext( "SMB_ACL_GROUP ");
134                         break;
135                 case SMB_ACL_GROUP_OBJ:
136                         dbgtext( "SMB_ACL_GROUP_OBJ ");
137                         break;
138                 case SMB_ACL_OTHER:
139                         dbgtext( "SMB_ACL_OTHER ");
140                         break;
141         }
142         dbgtext( "perms ");
143         dbgtext( "%c", pace->perms & S_IRUSR ? 'r' : '-');
144         dbgtext( "%c", pace->perms & S_IWUSR ? 'w' : '-');
145         dbgtext( "%c\n", pace->perms & S_IXUSR ? 'x' : '-');
146 }
147
148 /****************************************************************************
149  Print out a canon ace list.
150 ****************************************************************************/
151
152 static void print_canon_ace_list(const char *name, canon_ace *ace_list)
153 {
154         int count = 0;
155
156         if( DEBUGLVL( 10 )) {
157                 dbgtext( "print_canon_ace_list: %s\n", name );
158                 for (;ace_list; ace_list = ace_list->next, count++)
159                         print_canon_ace(ace_list, count );
160         }
161 }
162
163 /****************************************************************************
164  Merge aces with a common sid - if both are allow or deny, OR the permissions together and
165  delete the second one. If the first is deny, mask the permissions off and delete the allow
166  if the permissions become zero, delete the deny if the permissions are non zero.
167 ****************************************************************************/
168
169 static void merge_aces( canon_ace **pp_list_head )
170 {
171         canon_ace *list_head = *pp_list_head;
172         canon_ace *curr_ace_outer;
173         canon_ace *curr_ace_outer_next;
174
175         /*
176          * First, merge allow entries with identical SIDs, and deny entries
177          * with identical SIDs.
178          */
179
180         for (curr_ace_outer = list_head; curr_ace_outer; curr_ace_outer = curr_ace_outer_next) {
181                 canon_ace *curr_ace;
182                 canon_ace *curr_ace_next;
183
184                 curr_ace_outer_next = curr_ace_outer->next; /* Save the link in case we delete. */
185
186                 for (curr_ace = curr_ace_outer->next; curr_ace; curr_ace = curr_ace_next) {
187
188                         curr_ace_next = curr_ace->next; /* Save the link in case of delete. */
189
190                         if (sid_equal(&curr_ace->sid, &curr_ace_outer->sid) &&
191                                 (curr_ace->attr == curr_ace_outer->attr)) {
192
193                                 if( DEBUGLVL( 10 )) {
194                                         dbgtext("merge_aces: Merging ACE's\n");
195                                         print_canon_ace( curr_ace_outer, 0);
196                                         print_canon_ace( curr_ace, 0);
197                                 }
198
199                                 /* Merge two allow or two deny ACE's. */
200
201                                 curr_ace_outer->perms |= curr_ace->perms;
202                                 DLIST_REMOVE(list_head, curr_ace);
203                                 free(curr_ace);
204                                 curr_ace_outer_next = curr_ace_outer->next; /* We may have deleted the link. */
205                         }
206                 }
207         }
208
209         /*
210          * Now go through and mask off allow permissions with deny permissions.
211          * We can delete either the allow or deny here as we know that each SID
212          * appears only once in the list.
213          */
214
215         for (curr_ace_outer = list_head; curr_ace_outer; curr_ace_outer = curr_ace_outer_next) {
216                 canon_ace *curr_ace;
217                 canon_ace *curr_ace_next;
218
219                 curr_ace_outer_next = curr_ace_outer->next; /* Save the link in case we delete. */
220
221                 for (curr_ace = curr_ace_outer->next; curr_ace; curr_ace = curr_ace_next) {
222
223                         curr_ace_next = curr_ace->next; /* Save the link in case of delete. */
224
225                         /*
226                          * Subtract ACE's with different entries. Due to the ordering constraints
227                          * we've put on the ACL, we know the deny must be the first one.
228                          */
229
230                         if (sid_equal(&curr_ace->sid, &curr_ace_outer->sid) &&
231                                 (curr_ace_outer->attr == DENY_ACE) && (curr_ace->attr == ALLOW_ACE)) {
232
233                                 if( DEBUGLVL( 10 )) {
234                                         dbgtext("merge_aces: Masking ACE's\n");
235                                         print_canon_ace( curr_ace_outer, 0);
236                                         print_canon_ace( curr_ace, 0);
237                                 }
238
239                                 curr_ace->perms &= ~curr_ace_outer->perms;
240
241                                 if (curr_ace->perms == 0) {
242
243                                         /*
244                                          * The deny overrides the allow. Remove the allow.
245                                          */
246
247                                         DLIST_REMOVE(list_head, curr_ace);
248                                         free(curr_ace);
249                                         curr_ace_outer_next = curr_ace_outer->next; /* We may have deleted the link. */
250
251                                 } else {
252
253                                         /*
254                                          * Even after removing permissions, there
255                                          * are still allow permissions - delete the deny.
256                                          * It is safe to delete the deny here,
257                                          * as we are guarenteed by the deny first
258                                          * ordering that all the deny entries for
259                                          * this SID have already been merged into one
260                                          * before we can get to an allow ace.
261                                          */
262
263                                         DLIST_REMOVE(list_head, curr_ace_outer);
264                                         free(curr_ace_outer);
265                                 }
266                         }
267
268                 } /* end for curr_ace */
269         } /* end for curr_ace_outer */
270
271         /* We may have modified the list. */
272
273         *pp_list_head = list_head;
274 }
275
276 /****************************************************************************
277  Map canon_ace perms to permission bits NT.
278  The attr element is not used here - we only process deny entries on set,
279  not get. Deny entries are implicit on get with ace->perms = 0.
280 ****************************************************************************/
281
282 static SEC_ACCESS map_canon_ace_perms(int *pacl_type, DOM_SID *powner_sid, canon_ace *ace)
283 {
284         SEC_ACCESS sa;
285         uint32 nt_mask = 0;
286
287         *pacl_type = SEC_ACE_TYPE_ACCESS_ALLOWED;
288
289         if ((ace->perms & ALL_ACE_PERMS) == ALL_ACE_PERMS) {
290                         nt_mask = UNIX_ACCESS_RWX;
291         } else if ((ace->perms & ALL_ACE_PERMS) == (mode_t)0) {
292                 /*
293                  * Here we differentiate between the owner and any other user.
294                  */
295                 if (sid_equal(powner_sid, &ace->sid)) {
296                         nt_mask = UNIX_ACCESS_NONE;
297                 } else {
298                         /* Not owner, no access. */
299                         if (ace->type == SMB_ACL_USER) {
300                                 /* user objects can be deny entries. */
301                                 *pacl_type = SEC_ACE_TYPE_ACCESS_DENIED;
302                                 nt_mask = GENERIC_ALL_ACCESS;
303                         }
304                         else
305                                 nt_mask = UNIX_ACCESS_NONE;
306                 }
307         } else {
308                 nt_mask |= ((ace->perms & S_IRUSR) ? UNIX_ACCESS_R : 0 );
309                 nt_mask |= ((ace->perms & S_IWUSR) ? UNIX_ACCESS_W : 0 );
310                 nt_mask |= ((ace->perms & S_IXUSR) ? UNIX_ACCESS_X : 0 );
311         }
312
313         DEBUG(10,("map_canon_ace_perms: Mapped (UNIX) %x to (NT) %x\n",
314                         (unsigned int)ace->perms, (unsigned int)nt_mask ));
315
316         init_sec_access(&sa,nt_mask);
317         return sa;
318 }
319
320 /****************************************************************************
321  Map NT perms to a UNIX mode_t.
322 ****************************************************************************/
323
324 #define FILE_SPECIFIC_READ_BITS (FILE_READ_DATA|FILE_READ_EA|FILE_READ_ATTRIBUTES)
325 #define FILE_SPECIFIC_WRITE_BITS (FILE_WRITE_DATA|FILE_APPEND_DATA|FILE_WRITE_EA|FILE_WRITE_ATTRIBUTES)
326 #define FILE_SPECIFIC_EXECUTE_BITS (FILE_EXECUTE)
327
328 static mode_t map_nt_perms( SEC_ACCESS sec_access, int type)
329 {
330         mode_t mode = 0;
331
332         switch(type) {
333         case S_IRUSR:
334                 if(sec_access.mask & GENERIC_ALL_ACCESS)
335                         mode = S_IRUSR|S_IWUSR|S_IXUSR;
336                 else {
337                         mode |= (sec_access.mask & (GENERIC_READ_ACCESS|FILE_SPECIFIC_READ_BITS)) ? S_IRUSR : 0;
338                         mode |= (sec_access.mask & (GENERIC_WRITE_ACCESS|FILE_SPECIFIC_WRITE_BITS)) ? S_IWUSR : 0;
339                         mode |= (sec_access.mask & (GENERIC_EXECUTE_ACCESS|FILE_SPECIFIC_EXECUTE_BITS)) ? S_IXUSR : 0;
340                 }
341                 break;
342         case S_IRGRP:
343                 if(sec_access.mask & GENERIC_ALL_ACCESS)
344                         mode = S_IRGRP|S_IWGRP|S_IXGRP;
345                 else {
346                         mode |= (sec_access.mask & (GENERIC_READ_ACCESS|FILE_SPECIFIC_READ_BITS)) ? S_IRGRP : 0;
347                         mode |= (sec_access.mask & (GENERIC_WRITE_ACCESS|FILE_SPECIFIC_WRITE_BITS)) ? S_IWGRP : 0;
348                         mode |= (sec_access.mask & (GENERIC_EXECUTE_ACCESS|FILE_SPECIFIC_EXECUTE_BITS)) ? S_IXGRP : 0;
349                 }
350                 break;
351         case S_IROTH:
352                 if(sec_access.mask & GENERIC_ALL_ACCESS)
353                         mode = S_IROTH|S_IWOTH|S_IXOTH;
354                 else {
355                         mode |= (sec_access.mask & (GENERIC_READ_ACCESS|FILE_SPECIFIC_READ_BITS)) ? S_IROTH : 0;
356                         mode |= (sec_access.mask & (GENERIC_WRITE_ACCESS|FILE_SPECIFIC_WRITE_BITS)) ? S_IWOTH : 0;
357                         mode |= (sec_access.mask & (GENERIC_EXECUTE_ACCESS|FILE_SPECIFIC_EXECUTE_BITS)) ? S_IXOTH : 0;
358                 }
359                 break;
360         }
361
362         return mode;
363 }
364
365 /****************************************************************************
366  Unpack a SEC_DESC into a UNIX owner and group.
367 ****************************************************************************/
368
369 static BOOL unpack_nt_owners(SMB_STRUCT_STAT *psbuf, uid_t *puser, gid_t *pgrp, uint32 security_info_sent, SEC_DESC *psd)
370 {
371         DOM_SID owner_sid;
372         DOM_SID grp_sid;
373         enum SID_NAME_USE sid_type;
374
375         *puser = (uid_t)-1;
376         *pgrp = (gid_t)-1;
377
378         if(security_info_sent == 0) {
379                 DEBUG(0,("unpack_nt_owners: no security info sent !\n"));
380                 return False;
381         }
382
383         /*
384          * Validate the owner and group SID's.
385          */
386
387         memset(&owner_sid, '\0', sizeof(owner_sid));
388         memset(&grp_sid, '\0', sizeof(grp_sid));
389
390         DEBUG(5,("unpack_nt_owners: validating owner_sids.\n"));
391
392         /*
393          * Don't immediately fail if the owner sid cannot be validated.
394          * This may be a group chown only set.
395          */
396
397         if (security_info_sent & OWNER_SECURITY_INFORMATION) {
398                 sid_copy(&owner_sid, psd->owner_sid);
399                 if (!sid_to_uid( &owner_sid, puser, &sid_type))
400                         DEBUG(3,("unpack_nt_owners: unable to validate owner sid.\n"));
401         }
402
403         /*
404          * Don't immediately fail if the group sid cannot be validated.
405          * This may be an owner chown only set.
406          */
407
408         if (security_info_sent & GROUP_SECURITY_INFORMATION) {
409                 sid_copy(&grp_sid, psd->grp_sid);
410                 if (!sid_to_gid( &grp_sid, pgrp, &sid_type))
411                         DEBUG(3,("unpack_nt_owners: unable to validate group sid.\n"));
412         }
413
414         DEBUG(5,("unpack_nt_owners: owner_sids validated.\n"));
415
416         return True;
417 }
418
419 /****************************************************************************
420  Create a default mode for a directory default ACE.
421 ****************************************************************************/
422
423 static mode_t get_default_ace_mode(files_struct *fsp, int type)
424 {
425     mode_t force_mode = lp_force_dir_security_mode(SNUM(fsp->conn));
426         mode_t mode = 0;
427
428         switch(type) {
429                 case S_IRUSR:
430                         mode |= (force_mode & S_IRUSR) ? S_IRUSR : 0;
431                         mode |= (force_mode & S_IWUSR) ? S_IWUSR : 0;
432                         mode |= (force_mode & S_IXUSR) ? S_IXUSR : 0;
433                         break;
434                 case S_IRGRP:
435                         mode |= (force_mode & S_IRGRP) ? S_IRUSR : 0;
436                         mode |= (force_mode & S_IWGRP) ? S_IWUSR : 0;
437                         mode |= (force_mode & S_IXGRP) ? S_IXUSR : 0;
438                         break;
439                 case S_IROTH:
440                         mode |= (force_mode & S_IROTH) ? S_IRUSR : 0;
441                         mode |= (force_mode & S_IWOTH) ? S_IWUSR : 0;
442                         mode |= (force_mode & S_IXOTH) ? S_IXUSR : 0;
443                         break;
444         }
445
446         return mode;
447 }
448
449 /****************************************************************************
450  A well formed POSIX file or default ACL has at least 3 entries, a 
451  SMB_ACL_USER_OBJ, SMB_ACL_GROUP_OBJ, SMB_ACL_OTHER_OBJ.
452  In addition, the owner must always have at least read access.
453 ****************************************************************************/
454
455 static BOOL ensure_canon_entry_valid(canon_ace **pp_ace,
456                                                         files_struct *fsp,
457                                                         DOM_SID *pfile_owner_sid,
458                                                         DOM_SID *pfile_grp_sid,
459                                                         SMB_STRUCT_STAT *pst,
460                                                         BOOL default_acl)
461 {
462         extern DOM_SID global_sid_World;
463         canon_ace *pace;
464         BOOL got_user = False;
465         BOOL got_grp = False;
466         BOOL got_other = False;
467
468         for (pace = *pp_ace; pace; pace = pace->next) {
469                 if (pace->type == SMB_ACL_USER_OBJ) {
470                         /* Ensure owner has read access. */
471                         if (pace->perms == (mode_t)0)
472                                 pace->perms = S_IRUSR;
473                         got_user = True;
474                 } else if (pace->type == SMB_ACL_GROUP_OBJ)
475                         got_grp = True;
476                 else if (pace->type == SMB_ACL_OTHER)
477                         got_other = True;
478         }
479
480         if (!got_user) {
481                 if ((pace = (canon_ace *)malloc(sizeof(canon_ace))) == NULL) {
482                         DEBUG(0,("ensure_canon_entry_valid: malloc fail.\n"));
483                         return False;
484                 }
485
486                 ZERO_STRUCTP(pace);
487                 pace->type = SMB_ACL_USER_OBJ;
488                 pace->owner_type = UID_ACE;
489                 pace->unix_ug.uid = pst->st_uid;
490                 pace->sid = *pfile_owner_sid;
491                 /* Ensure owner has read access. */
492                 pace->perms = default_acl ? get_default_ace_mode(fsp, S_IRUSR): S_IRUSR;
493                 pace->attr = ALLOW_ACE;
494
495                 DLIST_ADD(*pp_ace, pace);
496         }
497
498         if (!got_grp) {
499                 if ((pace = (canon_ace *)malloc(sizeof(canon_ace))) == NULL) {
500                         DEBUG(0,("ensure_canon_entry_valid: malloc fail.\n"));
501                         return False;
502                 }
503
504                 ZERO_STRUCTP(pace);
505                 pace->type = SMB_ACL_GROUP_OBJ;
506                 pace->owner_type = GID_ACE;
507                 pace->unix_ug.uid = pst->st_gid;
508                 pace->sid = *pfile_grp_sid;
509                 pace->perms = default_acl ? get_default_ace_mode(fsp, S_IRGRP): 0;
510                 pace->attr = ALLOW_ACE;
511
512                 DLIST_ADD(*pp_ace, pace);
513         }
514
515         if (!got_other) {
516                 if ((pace = (canon_ace *)malloc(sizeof(canon_ace))) == NULL) {
517                         DEBUG(0,("ensure_canon_entry_valid: malloc fail.\n"));
518                         return False;
519                 }
520
521                 ZERO_STRUCTP(pace);
522                 pace->type = SMB_ACL_OTHER;
523                 pace->owner_type = WORLD_ACE;
524                 pace->unix_ug.world = -1;
525                 pace->sid = global_sid_World;
526                 pace->perms = default_acl ? get_default_ace_mode(fsp, S_IROTH): 0;
527                 pace->attr = ALLOW_ACE;
528
529                 DLIST_ADD(*pp_ace, pace);
530         }
531
532         return True;
533 }
534
535 /****************************************************************************
536  Unpack a SEC_DESC into two canonical ace lists.
537 ****************************************************************************/
538
539 static BOOL create_canon_ace_lists(files_struct *fsp, 
540                                                         DOM_SID *pfile_owner_sid,
541                                                         DOM_SID *pfile_grp_sid,
542                                                         canon_ace **ppfile_ace, canon_ace **ppdir_ace,
543                                                         SEC_ACL *dacl)
544 {
545         extern DOM_SID global_sid_World;
546         BOOL all_aces_are_inherit_only = (fsp->is_directory ? True : False);
547         canon_ace *file_ace = NULL;
548         canon_ace *dir_ace = NULL;
549         canon_ace *tmp_ace = NULL;
550         canon_ace *current_ace = NULL;
551         BOOL got_dir_allow = False;
552         BOOL got_file_allow = False;
553         int i;
554
555         *ppfile_ace = NULL;
556         *ppdir_ace = NULL;
557
558         for(i = 0; i < dacl->num_aces; i++) {
559                 enum SID_NAME_USE sid_type;
560                 SEC_ACE *psa = &dacl->ace[i];
561
562                 if((psa->type != SEC_ACE_TYPE_ACCESS_ALLOWED) && (psa->type != SEC_ACE_TYPE_ACCESS_DENIED)) {
563                         free_canon_ace_list(file_ace);
564                         free_canon_ace_list(dir_ace);
565                         DEBUG(3,("create_canon_ace_lists: unable to set anything but an ALLOW or DENY ACE.\n"));
566                         return False;
567                 }
568
569                 /*
570                  * The security mask may be UNIX_ACCESS_NONE which should map into
571                  * no permissions (we overload the WRITE_OWNER bit for this) or it
572                  * should be one of the ALL/EXECUTE/READ/WRITE bits. Arrange for this
573                  * to be so. Any other bits override the UNIX_ACCESS_NONE bit.
574                  */
575
576                 psa->info.mask &= (GENERIC_ALL_ACCESS|GENERIC_EXECUTE_ACCESS|GENERIC_WRITE_ACCESS|
577                                                         GENERIC_READ_ACCESS|UNIX_ACCESS_NONE|FILE_ALL_ATTRIBUTES);
578
579                 if(psa->info.mask != UNIX_ACCESS_NONE)
580                         psa->info.mask &= ~UNIX_ACCESS_NONE;
581
582                 /*
583                  * Create a cannon_ace entry representing this NT DACL ACE.
584                  */
585
586                 if ((current_ace = (canon_ace *)malloc(sizeof(canon_ace))) == NULL) {
587                         free_canon_ace_list(file_ace);
588                         free_canon_ace_list(dir_ace);
589                         DEBUG(0,("create_canon_ace_lists: malloc fail.\n"));
590                         return False;
591                 }
592
593                 ZERO_STRUCTP(current_ace);
594
595                 sid_copy(&current_ace->sid, &psa->sid);
596
597                 /*
598                  * Try and work out if the SID is a user or group
599                  * as we need to flag these differently for POSIX.
600                  */
601
602                 if( sid_equal(&current_ace->sid, &global_sid_World)) {
603                         current_ace->owner_type = WORLD_ACE;
604                         current_ace->unix_ug.world = -1;
605                 } else if (sid_to_uid( &current_ace->sid, &current_ace->unix_ug.uid, &sid_type)) {
606                         current_ace->owner_type = UID_ACE;
607                 } else if (sid_to_gid( &current_ace->sid, &current_ace->unix_ug.gid, &sid_type)) {
608                         current_ace->owner_type = GID_ACE;
609                 } else {
610                         fstring str;
611
612                         free_canon_ace_list(file_ace);
613                         free_canon_ace_list(dir_ace);
614                         free(current_ace);
615                         DEBUG(0,("create_canon_ace_lists: unable to map SID %s to uid or gid.\n",
616                                 sid_to_string(str, &current_ace->sid) ));
617                         return False;
618                 }
619
620                 /*
621                  * Map the given NT permissions into a UNIX mode_t containing only
622                  * S_I(R|W|X)USR bits.
623                  */
624
625                 current_ace->perms |= map_nt_perms( psa->info, S_IRUSR);
626                 current_ace->attr = (psa->type == SEC_ACE_TYPE_ACCESS_ALLOWED) ? ALLOW_ACE : DENY_ACE;
627
628                 /*
629                  * Now note what kind of a POSIX ACL this should map to.
630                  */
631
632                 if(sid_equal(&current_ace->sid, pfile_owner_sid)) {
633                         /* Note we should apply the default mode/mask here.... FIXME ! JRA */
634                         current_ace->type = SMB_ACL_USER_OBJ;
635                 } else if( sid_equal(&current_ace->sid, pfile_grp_sid)) {
636                         /* Note we should apply the default mode/mask here.... FIXME ! JRA */
637                         current_ace->type = SMB_ACL_GROUP_OBJ;
638                 } else if( sid_equal(&current_ace->sid, &global_sid_World)) {
639                         /* Note we should apply the default mode/mask here.... FIXME ! JRA */
640                         current_ace->type = SMB_ACL_OTHER;
641                 } else {
642                         /*
643                          * Could be a SMB_ACL_USER or SMB_ACL_GROUP. Check by
644                          * looking at owner_type.
645                          */
646
647                         current_ace->type = (current_ace->owner_type == UID_ACE) ? SMB_ACL_USER : SMB_ACL_GROUP;
648                 }
649
650                 /*
651                  * Now add the created ace to either the file list, the directory
652                  * list, or both. We *MUST* preserve the order here (hence we use
653                  * DLIST_ADD_END) as NT ACLs are order dependent.
654                  */
655
656                 if (fsp->is_directory) {
657
658                         /*
659                          * We can only add to the default POSIX ACE list if the ACE is
660                          * designed to be inherited by both files and directories.
661                          */
662
663                         if ((psa->flags & (SEC_ACE_FLAG_OBJECT_INHERIT|SEC_ACE_FLAG_CONTAINER_INHERIT)) ==
664                                 (SEC_ACE_FLAG_OBJECT_INHERIT|SEC_ACE_FLAG_CONTAINER_INHERIT)) {
665
666                                 DLIST_ADD_END(dir_ace, current_ace, tmp_ace);
667
668                                 /*
669                                  * Note if this was an allow ace. We can't process
670                                  * any further deny ace's after this.
671                                  */
672
673                                 if (current_ace->attr == ALLOW_ACE)
674                                         got_dir_allow = True;
675
676                                 if ((current_ace->attr == DENY_ACE) && got_dir_allow) {
677                                         DEBUG(0,("create_canon_ace_lists: malformed ACL in inheritable ACL ! \
678 Deny entry after Allow entry. Failing to set on file %s.\n", fsp->fsp_name ));
679                                         free_canon_ace_list(file_ace);
680                                         free_canon_ace_list(dir_ace);
681                                         free(current_ace);
682                                         return False;
683                                 }       
684
685                                 if( DEBUGLVL( 10 )) {
686                                         dbgtext("create_canon_ace_lists: adding dir ACL:\n");
687                                         print_canon_ace( current_ace, 0);
688                                 }
689
690                                 /*
691                                  * If this is not an inherit only ACE we need to add a duplicate
692                                  * to the file acl.
693                                  */
694
695                                 if (!(psa->flags & SEC_ACE_FLAG_INHERIT_ONLY)) {
696                                         canon_ace *dup_ace = dup_canon_ace(current_ace);
697
698                                         if (!dup_ace) {
699                                                 DEBUG(0,("create_canon_ace_lists: malloc fail !\n"));
700                                                 free_canon_ace_list(file_ace);
701                                                 free_canon_ace_list(dir_ace);
702                                                 return False;
703                                         }
704
705                                         current_ace = dup_ace;
706                                 } else {
707                                         current_ace = NULL;
708                                 }
709                         }
710                 }
711
712                 /*
713                  * Only add to the file ACL if not inherit only.
714                  */
715
716                 if (!(psa->flags & SEC_ACE_FLAG_INHERIT_ONLY)) {
717                         DLIST_ADD_END(file_ace, current_ace, tmp_ace);
718
719                         /*
720                          * Note if this was an allow ace. We can't process
721                          * any further deny ace's after this.
722                          */
723
724                         if (current_ace->attr == ALLOW_ACE)
725                                 got_file_allow = True;
726
727                         if ((current_ace->attr == DENY_ACE) && got_file_allow) {
728                                 DEBUG(0,("create_canon_ace_lists: malformed ACL in file ACL ! \
729 Deny entry after Allow entry. Failing to set on file %s.\n", fsp->fsp_name ));
730                                 free_canon_ace_list(file_ace);
731                                 free_canon_ace_list(dir_ace);
732                                 free(current_ace);
733                                 return False;
734                         }       
735
736                         if( DEBUGLVL( 10 )) {
737                                 dbgtext("create_canon_ace_lists: adding file ACL:\n");
738                                 print_canon_ace( current_ace, 0);
739                         }
740                         all_aces_are_inherit_only = False;
741                         current_ace = NULL;
742                 }
743
744                 /*
745                  * Free if ACE was not added.
746                  */
747
748                 if (current_ace)
749                         free(current_ace);
750         }
751
752         if (fsp->is_directory && all_aces_are_inherit_only) {
753                 /*
754                  * Windows 2000 is doing one of these weird 'inherit acl'
755                  * traverses to conserve NTFS ACL resources. Just pretend
756                  * there was no DACL sent. JRA.
757                  */
758
759                 DEBUG(10,("create_canon_ace_lists: Win2k inherit acl traverse. Ignoring DACL.\n"));
760                 free_canon_ace_list(file_ace);
761                 free_canon_ace_list(dir_ace);
762                 file_ace = NULL;
763                 dir_ace = NULL;
764         }
765
766         *ppfile_ace = file_ace;
767         *ppdir_ace = dir_ace;
768
769         return True;
770 }
771
772 /****************************************************************************
773  Check if a given uid/SID is in a group gid/SID. This is probably very
774  expensive and will need optimisation. A *lot* of optimisation :-). JRA.
775 ****************************************************************************/
776
777 static BOOL uid_entry_in_group( canon_ace *uid_ace, canon_ace *group_ace )
778 {
779         extern DOM_SID global_sid_World;
780         struct passwd *pass = NULL;
781         struct group *gptr = NULL;
782
783         /* "Everyone" always matches every uid. */
784
785         if (sid_equal(&group_ace->sid, &global_sid_World))
786                 return True;
787
788         if (!(pass = sys_getpwuid(uid_ace->unix_ug.uid)))
789                 return False;
790
791         if (!(gptr = getgrgid(group_ace->unix_ug.gid)))
792                 return False;
793
794         /*
795          * Due to the winbind interfaces we need to do this via names,
796          * not uids/gids.
797          */
798
799         return user_in_group_list(pass->pw_name, gptr->gr_name );
800 }
801
802 /****************************************************************************
803  ASCII art time again... JRA :-).
804
805  We have 3 cases to process when moving from an NT ACL to a POSIX ACL. Firstly,
806  we insist the ACL is in canonical form (ie. all DENY entries preceede ALLOW
807  entries). Secondly, the merge code has ensured that all duplicate SID entries for
808  allow or deny have been merged, so the same SID can only appear once in the deny
809  list or once in the allow list.
810
811  We then process as follows :
812
813  ---------------------------------------------------------------------------
814  First pass - look for a Everyone DENY entry.
815
816  If it is deny all (rwx) trunate the list at this point.
817  Else, walk the list from this point and use the deny permissions of this
818  entry as a mask on all following allow entries. Finally, delete
819  the Everyone DENY entry (we have applied it to everything possible).
820
821  In addition, in this pass we remove any DENY entries that have 
822  no permissions (ie. they are a DENY nothing).
823  ---------------------------------------------------------------------------
824  Second pass - only deal with deny user entries.
825
826  DENY user1 (perms XXX)
827
828  new_perms = 0
829  for all following allow group entries where user1 is in group
830         new_perms |= group_perms;
831
832  user1 entry perms = new_perms & ~ XXX;
833
834  Convert the deny entry to an allow entry with the new perms and
835  push to the end of the list. Note if the user was in no groups
836  this maps to a specific allow nothing entry for this user.
837
838  The common case from the NT ACL choser (userX deny all) is
839  optimised so we don't do the group lookup - we just map to
840  an allow nothing entry.
841
842  What we're doing here is inferring the allow permissions the
843  person setting the ACE on user1 wanted by looking at the allow
844  permissions on the groups the user is currently in. This will
845  be a snapshot, depending on group membership but is the best
846  we can do and has the advantage of failing closed rather than
847  open.
848  ---------------------------------------------------------------------------
849  Third pass - only deal with deny group entries.
850
851  DENY group1 (perms XXX)
852
853  for all following allow user entries where user is in group1
854    user entry perms = user entry perms & ~ XXX;
855
856  If there is a group Everyone allow entry with permissions YYY,
857  convert the group1 entry to an allow entry and modify its
858  permissions to be :
859
860  new_perms = YYY & ~ XXX
861
862  and push to the end of the list.
863
864  If there is no group Everyone allow entry then convert the
865  group1 entry to a allow nothing entry and push to the end of the list.
866
867  Note that the common case from the NT ACL choser (groupX deny all)
868  cannot be optimised here as we need to modify user entries who are
869  in the group to change them to a deny all also.
870
871  What we're doing here is modifying the allow permissions of
872  user entries (which are more specific in POSIX ACLs) to mask
873  out the explicit deny set on the group they are in. This will
874  be a snapshot depending on current group membership but is the
875  best we can do and has the advantage of failing closed rather
876  than open.
877  ---------------------------------------------------------------------------
878
879  Note we *MUST* do the deny user pass first as this will convert deny user
880  entries into allow user entries which can then be processed by the deny
881  group pass.
882
883  The above algorithm took a *lot* of thinking about - hence this
884  explaination :-). JRA.
885 ****************************************************************************/
886
887 /****************************************************************************
888  Process a canon_ace list entries. This is very complex code. We need
889  to go through and remove the "deny" permissions from any allow entry that matches
890  the id of this entry. We have already refused any NT ACL that wasn't in correct
891  order (DENY followed by ALLOW). If any allow entry ends up with zero permissions,
892  we just remove it (to fail safe). We have already removed any duplicate ace
893  entries. Treat an "Everyone" DENY_ACE as a special case - use it to mask all
894  allow entries.
895 ****************************************************************************/
896
897 static void process_deny_list( canon_ace **pp_ace_list )
898 {
899         extern DOM_SID global_sid_World;
900         canon_ace *ace_list = *pp_ace_list;
901         canon_ace *curr_ace = NULL;
902         canon_ace *curr_ace_next = NULL;
903
904         /* Pass 1 above - look for an Everyone, deny entry. */
905
906         for (curr_ace = ace_list; curr_ace; curr_ace = curr_ace_next) {
907                 canon_ace *allow_ace_p;
908
909                 curr_ace_next = curr_ace->next; /* So we can't lose the link. */
910
911                 if (curr_ace->attr != DENY_ACE)
912                         continue;
913
914                 if (curr_ace->perms == (mode_t)0) {
915
916                         /* Deny nothing entry - delete. */
917
918                         DLIST_REMOVE(ace_list, curr_ace);
919                         continue;
920                 }
921
922                 if (!sid_equal(&curr_ace->sid, &global_sid_World))
923                         continue;
924
925                 /* JRATEST - assert. */
926                 SMB_ASSERT(curr_ace->owner_type == WORLD_ACE);
927
928                 if (curr_ace->perms == ALL_ACE_PERMS) {
929
930                         /*
931                          * Optimisation. This is a DENY_ALL to Everyone. Truncate the
932                          * list at this point including this entry.
933                          */
934
935                         canon_ace *prev_entry = curr_ace->prev;
936
937                         free_canon_ace_list( curr_ace );
938                         if (prev_entry)
939                                 prev_entry->next = NULL;
940                         else {
941                                 /* We deleted the entire list. */
942                                 ace_list = NULL;
943                         }
944                         break;
945                 }
946
947                 for (allow_ace_p = curr_ace->next; allow_ace_p; allow_ace_p = allow_ace_p->next) {
948
949                         /* 
950                          * Only mask off allow entries.
951                          */
952
953                         if (allow_ace_p->attr != ALLOW_ACE)
954                                 continue;
955
956                         allow_ace_p->perms &= ~curr_ace->perms;
957                 }
958
959                 /*
960                  * Now it's been applied, remove it.
961                  */
962
963                 DLIST_REMOVE(ace_list, curr_ace);
964         }
965
966         /* Pass 2 above - deal with deny user entries. */
967
968         for (curr_ace = ace_list; curr_ace; curr_ace = curr_ace_next) {
969                 mode_t new_perms = (mode_t)0;
970                 canon_ace *allow_ace_p;
971                 canon_ace *tmp_ace;
972
973                 curr_ace_next = curr_ace->next; /* So we can't lose the link. */
974
975                 if (curr_ace->attr != DENY_ACE)
976                         continue;
977
978                 if (curr_ace->owner_type != UID_ACE)
979                         continue;
980
981                 if (curr_ace->perms == ALL_ACE_PERMS) {
982
983                         /*
984                          * Optimisation - this is a deny everything to this user.
985                          * Convert to an allow nothing and push to the end of the list.
986                          */
987
988                         curr_ace->attr = ALLOW_ACE;
989                         curr_ace->perms = (mode_t)0;
990                         DLIST_DEMOTE(ace_list, curr_ace, tmp_ace);
991                         continue;
992                 }
993
994                 for (allow_ace_p = curr_ace->next; allow_ace_p; allow_ace_p = allow_ace_p->next) {
995
996                         if (allow_ace_p->attr != ALLOW_ACE)
997                                 continue;
998
999                         /* We process GID_ACE and WORLD_ACE entries only. */
1000
1001                         if (allow_ace_p->owner_type == UID_ACE)
1002                                 continue;
1003
1004                         if (uid_entry_in_group( curr_ace, allow_ace_p))
1005                                 new_perms |= allow_ace_p->perms;
1006                 }
1007
1008                 /*
1009                  * Convert to a allow entry, modify the perms and push to the end
1010                  * of the list.
1011                  */
1012
1013                 curr_ace->attr = ALLOW_ACE;
1014                 curr_ace->perms = (new_perms & ~curr_ace->perms);
1015                 DLIST_DEMOTE(ace_list, curr_ace, tmp_ace);
1016         }
1017
1018         /* Pass 3 above - deal with deny group entries. */
1019
1020         for (curr_ace = ace_list; curr_ace; curr_ace = curr_ace_next) {
1021                 canon_ace *tmp_ace;
1022                 canon_ace *allow_ace_p;
1023                 canon_ace *allow_everyone_p = NULL;
1024
1025                 curr_ace_next = curr_ace->next; /* So we can't lose the link. */
1026
1027                 if (curr_ace->attr != DENY_ACE)
1028                         continue;
1029
1030                 if (curr_ace->owner_type != GID_ACE)
1031                         continue;
1032
1033                 for (allow_ace_p = curr_ace->next; allow_ace_p; allow_ace_p = allow_ace_p->next) {
1034
1035                         if (allow_ace_p->attr != ALLOW_ACE)
1036                                 continue;
1037
1038                         /* Store a pointer to the Everyone allow, if it exists. */
1039                         if (allow_ace_p->owner_type == WORLD_ACE)
1040                                 allow_everyone_p = allow_ace_p;
1041
1042                         /* We process UID_ACE entries only. */
1043
1044                         if (allow_ace_p->owner_type != UID_ACE)
1045                                 continue;
1046
1047                         /* Mask off the deny group perms. */
1048
1049                         if (uid_entry_in_group( allow_ace_p, curr_ace))
1050                                 allow_ace_p->perms &= ~curr_ace->perms;
1051                 }
1052
1053                 /*
1054                  * Convert the deny to an allow with the correct perms and
1055                  * push to the end of the list.
1056                  */
1057
1058                 curr_ace->attr = ALLOW_ACE;
1059                 if (allow_everyone_p)
1060                         curr_ace->perms = allow_everyone_p->perms & ~curr_ace->perms;
1061                 else
1062                         curr_ace->perms = (mode_t)0;
1063                 DLIST_DEMOTE(ace_list, curr_ace, tmp_ace);
1064
1065         }
1066
1067         *pp_ace_list = ace_list;
1068 }
1069
1070 /****************************************************************************
1071  Unpack a SEC_DESC into two canonical ace lists. We don't depend on this
1072  succeeding.
1073 ****************************************************************************/
1074
1075 static BOOL unpack_canon_ace(files_struct *fsp, 
1076                                                         SMB_STRUCT_STAT *pst,
1077                                                         DOM_SID *pfile_owner_sid,
1078                                                         DOM_SID *pfile_grp_sid,
1079                                                         canon_ace **ppfile_ace, canon_ace **ppdir_ace,
1080                                                         uint32 security_info_sent, SEC_DESC *psd)
1081 {
1082         canon_ace *file_ace = NULL;
1083         canon_ace *dir_ace = NULL;
1084
1085         *ppfile_ace = NULL;
1086         *ppdir_ace = NULL;
1087
1088         if(security_info_sent == 0) {
1089                 DEBUG(0,("unpack_canon_ace: no security info sent !\n"));
1090                 return False;
1091         }
1092
1093         /*
1094          * If no DACL then this is a chown only security descriptor.
1095          */
1096
1097         if(!(security_info_sent & DACL_SECURITY_INFORMATION) || !psd->dacl)
1098                 return True;
1099
1100         /*
1101          * Now go through the DACL and create the canon_ace lists.
1102          */
1103
1104         if (!create_canon_ace_lists( fsp, pfile_owner_sid, pfile_grp_sid,
1105                                                                 &file_ace, &dir_ace, psd->dacl))
1106                 return False;
1107
1108         if ((file_ace == NULL) && (dir_ace == NULL)) {
1109                 /* W2K traverse DACL set - ignore. */
1110                 return True;
1111         }
1112
1113         /*
1114          * Go through the canon_ace list and merge entries
1115          * belonging to identical users of identical allow or deny type.
1116          * We can do this as all deny entries come first, followed by
1117          * all allow entries (we have mandated this before accepting this acl).
1118          */
1119
1120         print_canon_ace_list( "file ace - before merge", file_ace);
1121         merge_aces( &file_ace );
1122
1123         print_canon_ace_list( "dir ace - before merge", dir_ace);
1124         merge_aces( &dir_ace );
1125
1126         /*
1127          * NT ACLs are order dependent. Go through the acl lists and
1128          * process DENY entries by masking the allow entries.
1129          */
1130
1131         print_canon_ace_list( "file ace - before deny", file_ace);
1132         process_deny_list( &file_ace);
1133
1134         print_canon_ace_list( "dir ace - before deny", dir_ace);
1135         process_deny_list( &dir_ace);
1136
1137         /*
1138          * A well formed POSIX file or default ACL has at least 3 entries, a 
1139          * SMB_ACL_USER_OBJ, SMB_ACL_GROUP_OBJ, SMB_ACL_OTHER_OBJ
1140          * and optionally a mask entry. Ensure this is the case.
1141          */
1142
1143         print_canon_ace_list( "file ace - before valid", file_ace);
1144
1145         if (!ensure_canon_entry_valid(&file_ace, fsp, pfile_owner_sid, pfile_grp_sid, pst, False)) {
1146                 free_canon_ace_list(file_ace);
1147                 free_canon_ace_list(dir_ace);
1148                 return False;
1149         }
1150
1151         print_canon_ace_list( "dir ace - before valid", dir_ace);
1152
1153         if (dir_ace && !ensure_canon_entry_valid(&dir_ace, fsp, pfile_owner_sid, pfile_grp_sid, pst, True)) {
1154                 free_canon_ace_list(file_ace);
1155                 free_canon_ace_list(dir_ace);
1156                 return False;
1157         }
1158
1159         print_canon_ace_list( "file ace - return", file_ace);
1160         print_canon_ace_list( "dir ace - return", dir_ace);
1161
1162         *ppfile_ace = file_ace;
1163         *ppdir_ace = dir_ace;
1164         return True;
1165
1166 }
1167
1168 /****************************************************************************
1169  Map POSIX ACL perms to canon_ace permissions (a mode_t containing only S_(R|W|X)USR bits).
1170 ****************************************************************************/
1171
1172 static mode_t convert_permset_to_mode_t(SMB_ACL_PERMSET_T permset)
1173 {
1174         mode_t ret = 0;
1175
1176         ret |= (sys_acl_get_perm(permset, SMB_ACL_READ) ? S_IRUSR : 0);
1177         ret |= (sys_acl_get_perm(permset, SMB_ACL_WRITE) ? S_IWUSR : 0);
1178         ret |= (sys_acl_get_perm(permset, SMB_ACL_EXECUTE) ? S_IXUSR : 0);
1179
1180         return ret;
1181 }
1182
1183 /****************************************************************************
1184  Map generic UNIX permissions to canon_ace permissions (a mode_t containing only S_(R|W|X)USR bits).
1185 ****************************************************************************/
1186
1187 static mode_t unix_perms_to_acl_perms(mode_t mode, int r_mask, int w_mask, int x_mask)
1188 {
1189         mode_t ret = 0;
1190
1191         if (mode & r_mask)
1192                 ret |= S_IRUSR;
1193         if (mode & w_mask)
1194                 ret |= S_IWUSR;
1195         if (mode & x_mask)
1196                 ret |= S_IXUSR;
1197
1198         return ret;
1199 }
1200
1201 /****************************************************************************
1202  Map canon_ace permissions (a mode_t containing only S_(R|W|X)USR bits) to
1203  an SMB_ACL_PERMSET_T.
1204 ****************************************************************************/
1205
1206 static int map_acl_perms_to_permset(mode_t mode, SMB_ACL_PERMSET_T *p_permset)
1207 {
1208         if (sys_acl_clear_perms(*p_permset) ==  -1)
1209                 return -1;
1210         if (mode & S_IRUSR) {
1211                 if (sys_acl_add_perm(*p_permset, SMB_ACL_READ) == -1)
1212                         return -1;
1213         }
1214         if (mode & S_IWUSR) {
1215                 if (sys_acl_add_perm(*p_permset, SMB_ACL_WRITE) == -1)
1216                         return -1;
1217         }
1218         if (mode & S_IXUSR) {
1219                 if (sys_acl_add_perm(*p_permset, SMB_ACL_EXECUTE) == -1)
1220                         return -1;
1221         }
1222         return 0;
1223 }
1224
1225 /******************************************************************************
1226  When returning permissions, try and fit NT display
1227  semantics if possible. Note the the canon_entries here must have been malloced.
1228  The list format should be - first entry = owner, followed by group and other user
1229  entries, last entry = other.
1230
1231  Note that this doesn't exactly match the NT semantics for an ACL. As POSIX entries
1232  are not ordered, and match on the most specific entry rather than walking a list,
1233  then a simple POSIX permission of rw-r--r-- should really map to 6 entries,
1234
1235  Entry 0: owner : deny all except read and write.
1236  Entry 1: group : deny all except read.
1237  Entry 2: Everyone : deny all except read.
1238  Entry 3: owner : allow read and write.
1239  Entry 4: group : allow read.
1240  Entry 5: Everyone : allow read.
1241
1242  But NT cannot display this in their ACL editor !
1243 ********************************************************************************/
1244
1245 static void arrange_posix_perms( char *filename, canon_ace **pp_list_head)
1246 {
1247         extern DOM_SID global_sid_World;
1248         canon_ace *list_head = *pp_list_head;
1249         canon_ace *owner_ace = NULL;
1250         canon_ace *other_ace = NULL;
1251         canon_ace *ace = NULL;
1252         mode_t owner_perms = 0;
1253         mode_t group_perms = 0;
1254         mode_t other_perms = 0;
1255
1256         for (ace = list_head; ace; ace = ace->next) {
1257                 if (ace->type == SMB_ACL_USER_OBJ)
1258                         owner_ace = ace;
1259                 else if (ace->type == SMB_ACL_OTHER) {
1260                         /* Last ace - this is "other" */
1261                         other_ace = ace;
1262                 } else {
1263                         /* Get the union of all the group and supplementary user perms. */
1264                         group_perms |= ace->perms;
1265                 }
1266         }
1267                 
1268         if (!owner_ace || !other_ace) {
1269                 DEBUG(0,("arrange_posix_perms: Invalid POSIX permissions for file %s, missing owner or other.\n",
1270                         filename ));
1271                 return;
1272         }
1273
1274         /*
1275          * The POSIX algorithm applies to owner first, and other last,
1276          * so ensure they are arranged in this order.
1277          */
1278
1279         if (owner_ace) {
1280                 DLIST_PROMOTE(list_head, owner_ace);
1281         }
1282
1283         if (other_ace) {
1284                 DLIST_DEMOTE(list_head, other_ace, ace);
1285         }
1286
1287         owner_perms = owner_ace->perms;
1288         other_perms = other_ace->perms;
1289
1290         /*
1291          * We have to be clever here. NT4.x won't display anything other
1292          * Than an "Everyone, No access" DENY acl. Truncate blank perms
1293          * from the end, but we can't truncate blank permissions from
1294          * anywhere except the end, as they have an effect on allowing access
1295          * under POSIX.
1296          */
1297
1298         if ((owner_perms || group_perms) && !other_perms) {
1299                 DLIST_REMOVE(list_head, other_ace);
1300                 safe_free(other_ace);
1301         }
1302
1303         if (owner_perms && !group_perms && !other_perms) {
1304                 /* Free everything except the list head. */
1305                 free_canon_ace_list(owner_ace->next);
1306                 owner_ace->next = NULL;
1307         }
1308
1309         if (!owner_perms && !group_perms && !other_perms) {
1310                 /*
1311                  * Special case - no one has any access.
1312                  * Return a 1 element ACL - other has "no access".
1313                  */
1314
1315                 if (owner_ace->next) {
1316                         free_canon_ace_list(owner_ace->next);
1317                         owner_ace->next = NULL;
1318                 }
1319
1320                 owner_ace->type = SMB_ACL_OTHER;
1321                 owner_ace->sid = global_sid_World;
1322                 owner_ace->unix_ug.world = -1;
1323                 owner_ace->owner_type = WORLD_ACE;
1324                 owner_ace->attr = DENY_ACE;
1325                 owner_ace->perms = 0;
1326         }
1327
1328         /* We have probably changed the head of the list. */
1329
1330         *pp_list_head = list_head;
1331 }
1332                 
1333 /******************************************************************************
1334  Fall back to the generic 3 element UNIX permissions.
1335 ********************************************************************************/
1336
1337 static canon_ace *unix_canonicalise_acl(files_struct *fsp, SMB_STRUCT_STAT *psbuf,
1338                                                                                 DOM_SID *powner, DOM_SID *pgroup, BOOL default_acl)
1339 {
1340         extern DOM_SID global_sid_World;
1341         canon_ace *list_head = NULL;
1342         canon_ace *owner_ace = NULL;
1343         canon_ace *group_ace = NULL;
1344         canon_ace *other_ace = NULL;
1345         mode_t mode;
1346
1347         if (default_acl)
1348                 return NULL;
1349
1350         /*
1351          * Create 3 linked list entries.
1352          */
1353
1354         if ((owner_ace = (canon_ace *)malloc(sizeof(canon_ace))) == NULL)
1355                 goto fail;
1356
1357         if ((group_ace = (canon_ace *)malloc(sizeof(canon_ace))) == NULL)
1358                 goto fail;
1359
1360         if ((other_ace = (canon_ace *)malloc(sizeof(canon_ace))) == NULL)
1361                 goto fail;
1362
1363         ZERO_STRUCTP(owner_ace);
1364         ZERO_STRUCTP(group_ace);
1365         ZERO_STRUCTP(other_ace);
1366
1367         owner_ace->type = SMB_ACL_USER_OBJ;
1368         owner_ace->sid = *powner;
1369         owner_ace->unix_ug.uid = psbuf->st_uid;
1370         owner_ace->owner_type = UID_ACE;
1371         owner_ace->attr = ALLOW_ACE;
1372
1373         group_ace->type = SMB_ACL_GROUP_OBJ;
1374         group_ace->sid = *pgroup;
1375         group_ace->unix_ug.gid = psbuf->st_gid;
1376         group_ace->owner_type = GID_ACE;
1377         group_ace->attr = ALLOW_ACE;
1378
1379         other_ace->type = SMB_ACL_OTHER;
1380         other_ace->sid = global_sid_World;
1381         other_ace->unix_ug.world = -1;
1382         other_ace->owner_type = WORLD_ACE;
1383         other_ace->attr = ALLOW_ACE;
1384
1385         mode = psbuf->st_mode;
1386
1387         owner_ace->perms = unix_perms_to_acl_perms(mode, S_IRUSR, S_IWUSR, S_IXUSR);
1388         owner_ace->attr = owner_ace->perms ? ALLOW_ACE : DENY_ACE;
1389         
1390         group_ace->perms = unix_perms_to_acl_perms(mode, S_IRGRP, S_IWGRP, S_IXGRP);
1391         group_ace->attr = group_ace->perms ? ALLOW_ACE : DENY_ACE;
1392
1393         other_ace->perms = unix_perms_to_acl_perms(mode, S_IROTH, S_IWOTH, S_IXOTH);
1394         other_ace->attr = other_ace->perms ? ALLOW_ACE : DENY_ACE;
1395
1396         DLIST_ADD(list_head, other_ace);
1397         DLIST_ADD(list_head, group_ace);
1398         DLIST_ADD(list_head, owner_ace);
1399
1400         arrange_posix_perms(fsp->fsp_name,&list_head );
1401
1402         return list_head;
1403
1404   fail:
1405
1406         safe_free(owner_ace);
1407         safe_free(group_ace);
1408         safe_free(other_ace);
1409
1410         return NULL;
1411 }
1412
1413 /****************************************************************************
1414  Create a linked list of canonical ACE entries.
1415 ****************************************************************************/
1416
1417 static canon_ace *canonicalise_acl( files_struct *fsp, SMB_ACL_T posix_acl, SMB_STRUCT_STAT *psbuf,
1418                                                                         DOM_SID *powner, DOM_SID *pgroup, BOOL default_acl)
1419 {
1420         extern DOM_SID global_sid_World;
1421         mode_t acl_mask = (S_IRUSR|S_IWUSR|S_IXUSR);
1422         canon_ace *list_head = NULL;
1423         canon_ace *ace = NULL;
1424         canon_ace *next_ace = NULL;
1425         canon_ace *owner_ace = NULL;
1426         canon_ace *group_ace = NULL;
1427         canon_ace *other_ace = NULL;
1428         int entry_id = SMB_ACL_FIRST_ENTRY;
1429         SMB_ACL_ENTRY_T entry;
1430         size_t ace_count;
1431
1432         if (posix_acl == NULL)
1433                 return unix_canonicalise_acl( fsp, psbuf, powner, pgroup, default_acl);
1434
1435         while ( sys_acl_get_entry(posix_acl, entry_id, &entry) == 1) {
1436                 SMB_ACL_TAG_T tagtype;
1437                 SMB_ACL_PERMSET_T permset;
1438                 DOM_SID sid;
1439                 posix_id unix_ug;
1440                 enum ace_owner owner_type;
1441
1442                 /* get_next... */
1443                 if (entry_id == SMB_ACL_FIRST_ENTRY)
1444                         entry_id = SMB_ACL_NEXT_ENTRY;
1445
1446                 /* Is this a MASK entry ? */
1447                 if (sys_acl_get_tag_type(entry, &tagtype) == -1)
1448                         continue;
1449
1450                 if (sys_acl_get_permset(entry, &permset) == -1)
1451                         continue;
1452
1453                 /* Decide which SID to use based on the ACL type. */
1454                 switch(tagtype) {
1455                         case SMB_ACL_USER_OBJ:
1456                                 /* Get the SID from the owner. */
1457                                 uid_to_sid( &sid, psbuf->st_uid );
1458                                 unix_ug.uid = psbuf->st_uid;
1459                                 owner_type = UID_ACE;
1460                                 break;
1461                         case SMB_ACL_USER:
1462                                 {
1463                                         uid_t *puid = (uid_t *)sys_acl_get_qualifier(entry);
1464                                         if (puid == NULL) {
1465                                                 DEBUG(0,("canonicalise_acl: Failed to get uid.\n"));
1466                                                 continue;
1467                                         }
1468                                         uid_to_sid( &sid, *puid);
1469                                         unix_ug.uid = *puid;
1470                                         owner_type = UID_ACE;
1471                                         break;
1472                                 }
1473                         case SMB_ACL_GROUP_OBJ:
1474                                 /* Get the SID from the owning group. */
1475                                 gid_to_sid( &sid, psbuf->st_gid );
1476                                 unix_ug.gid = psbuf->st_gid;
1477                                 owner_type = GID_ACE;
1478                                 break;
1479                         case SMB_ACL_GROUP:
1480                                 {
1481                                         gid_t *pgid = (gid_t *)sys_acl_get_qualifier(entry);
1482                                         if (pgid == NULL) {
1483                                                 DEBUG(0,("canonicalise_acl: Failed to get gid.\n"));
1484                                                 continue;
1485                                         }
1486                                         gid_to_sid( &sid, *pgid);
1487                                         unix_ug.gid = *pgid;
1488                                         owner_type = GID_ACE;
1489                                         break;
1490                                 }
1491                         case SMB_ACL_MASK:
1492                                 acl_mask = convert_permset_to_mode_t(permset);
1493                                 continue; /* Don't count the mask as an entry. */
1494                         case SMB_ACL_OTHER:
1495                                 /* Use the Everyone SID */
1496                                 sid = global_sid_World;
1497                                 unix_ug.world = -1;
1498                                 owner_type = WORLD_ACE;
1499                                 break;
1500                         default:
1501                                 DEBUG(0,("canonicalise_acl: Unknown tagtype %u\n", (unsigned int)tagtype));
1502                                 continue;
1503                 }
1504
1505                 /*
1506                  * Add this entry to the list.
1507                  */
1508
1509                 if ((ace = (canon_ace *)malloc(sizeof(canon_ace))) == NULL)
1510                         goto fail;
1511
1512                 ZERO_STRUCTP(ace);
1513                 ace->type = tagtype;
1514                 ace->perms = convert_permset_to_mode_t(permset);
1515                 ace->attr = ALLOW_ACE;
1516                 ace->sid = sid;
1517                 ace->unix_ug = unix_ug;
1518                 ace->owner_type = owner_type;
1519
1520                 /*
1521                  * Remember the user/group/other ACE entries.
1522                  */
1523
1524                 if (tagtype == SMB_ACL_USER_OBJ)
1525                         owner_ace = ace;
1526                 else if (tagtype == SMB_ACL_GROUP_OBJ)
1527                         group_ace = ace;
1528                 else if (tagtype == SMB_ACL_OTHER)
1529                         other_ace = ace;
1530
1531                 DLIST_ADD(list_head, ace);
1532         }
1533
1534         arrange_posix_perms(fsp->fsp_name,&list_head );
1535
1536         /*
1537          * Now go through the list, masking the permissions with the
1538          * acl_mask. Ensure all DENY Entries are at the start of the list.
1539          */
1540
1541         DEBUG(10,("canonicalize_acl: ace entries before arrange :\n"));
1542
1543         for ( ace_count = 0, ace = list_head; ace; ace = next_ace, ace_count++) {
1544                 next_ace = ace->next;
1545
1546                 /* Masks are only applied to entries other than USER_OBJ and OTHER. */
1547                 if (ace->type != SMB_ACL_OTHER && ace->type != SMB_ACL_USER_OBJ)
1548                         ace->perms &= acl_mask;
1549
1550                 if (ace->perms == 0) {
1551                         DLIST_PROMOTE(list_head, ace);
1552                 }
1553
1554                 if( DEBUGLVL( 10 ) ) {
1555                         print_canon_ace(ace, ace_count);
1556                 }
1557         }
1558
1559         print_canon_ace_list( "canonicalize_acl: ace entries after arrange", list_head );
1560
1561         return list_head;
1562
1563   fail:
1564
1565         free_canon_ace_list(list_head);
1566         return NULL;
1567 }
1568
1569 /****************************************************************************
1570  Attempt to apply an ACL to a file or directory.
1571 ****************************************************************************/
1572
1573 static BOOL set_canon_ace_list(files_struct *fsp, canon_ace *the_ace, BOOL default_ace, BOOL *pacl_set_support)
1574 {
1575         BOOL ret = False;
1576         SMB_ACL_T the_acl = sys_acl_init((int)count_canon_ace_list(the_ace) + 1);
1577         canon_ace *p_ace;
1578         int i;
1579         SMB_ACL_ENTRY_T mask_entry;
1580         SMB_ACL_PERMSET_T mask_permset;
1581         SMB_ACL_TYPE_T the_acl_type = (default_ace ? SMB_ACL_TYPE_DEFAULT : SMB_ACL_TYPE_ACCESS);
1582
1583         if (the_acl == NULL) {
1584
1585                 if (errno != ENOSYS) {
1586                         /*
1587                          * Only print this error message if we have some kind of ACL
1588                          * support that's not working. Otherwise we would always get this.
1589                          */
1590                         DEBUG(0,("set_canon_ace_list: Unable to init %s ACL. (%s)\n",
1591                                 default_ace ? "default" : "file", strerror(errno) ));
1592                 }
1593                 *pacl_set_support = False;
1594                 return False;
1595         }
1596
1597         for (i = 0, p_ace = the_ace; p_ace; p_ace = p_ace->next, i++ ) {
1598                 SMB_ACL_ENTRY_T the_entry;
1599                 SMB_ACL_PERMSET_T the_permset;
1600
1601                 /*
1602                  * Get the entry for this ACE.
1603                  */
1604
1605                 if (sys_acl_create_entry( &the_acl, &the_entry) == -1) {
1606                         DEBUG(0,("set_canon_ace_list: Failed to create entry %d. (%s)\n",
1607                                 i, strerror(errno) ));
1608                         goto done;
1609                 }
1610
1611                 /*
1612                  * Ok - we now know the ACL calls should be working, don't
1613                  * allow fallback to chmod.
1614                  */
1615
1616                 *pacl_set_support = True;
1617
1618                 /*
1619                  * Initialise the entry from the canon_ace.
1620                  */
1621
1622                 /*
1623                  * First tell the entry what type of ACE this is.
1624                  */
1625
1626                 if (sys_acl_set_tag_type(the_entry, p_ace->type) == -1) {
1627                         DEBUG(0,("set_canon_ace_list: Failed to set tag type on entry %d. (%s)\n",
1628                                 i, strerror(errno) ));
1629                         goto done;
1630                 }
1631
1632                 /*
1633                  * Only set the qualifier (user or group id) if the entry is a user
1634                  * or group id ACE.
1635                  */
1636
1637                 if ((p_ace->type == SMB_ACL_USER) || (p_ace->type == SMB_ACL_GROUP)) {
1638                         if (sys_acl_set_qualifier(the_entry,(void *)&p_ace->unix_ug.uid) == -1) {
1639                                 DEBUG(0,("set_canon_ace_list: Failed to set qualifier on entry %d. (%s)\n",
1640                                         i, strerror(errno) ));
1641                                 goto done;
1642                         }
1643                 }
1644
1645                 /*
1646                  * Convert the mode_t perms in the canon_ace to a POSIX permset.
1647                  */
1648
1649                 if (sys_acl_get_permset(the_entry, &the_permset) == -1) {
1650                         DEBUG(0,("set_canon_ace_list: Failed to get permset on entry %d. (%s)\n",
1651                                 i, strerror(errno) ));
1652                         goto done;
1653                 }
1654
1655                 if (map_acl_perms_to_permset(p_ace->perms, &the_permset) == -1) {
1656                         DEBUG(0,("set_canon_ace_list: Failed to create permset for mode (%u) on entry %d. (%s)\n",
1657                                 p_ace->perms, i, strerror(errno) ));
1658                         goto done;
1659                 }
1660
1661                 /*
1662                  * ..and apply them to the entry.
1663                  */
1664
1665                 if (sys_acl_set_permset(the_entry, the_permset) == -1) {
1666                         DEBUG(0,("set_canon_ace_list: Failed to add permset on entry %d. (%s)\n",
1667                                 i, strerror(errno) ));
1668                         goto done;
1669                 }
1670
1671                 if( DEBUGLVL( 10 ))
1672                         print_canon_ace( p_ace, i);
1673         }
1674
1675         /*
1676          * Add in a mask of rwx.
1677          */
1678
1679         if (sys_acl_create_entry( &the_acl, &mask_entry) == -1) {
1680                 DEBUG(0,("set_canon_ace_list: Failed to create mask entry. (%s)\n", strerror(errno) ));
1681                 goto done;
1682         }
1683
1684         if (sys_acl_set_tag_type(mask_entry, SMB_ACL_MASK) == -1) {
1685                 DEBUG(0,("set_canon_ace_list: Failed to set tag type on mask entry. (%s)\n",strerror(errno) ));
1686                 goto done;
1687         }
1688
1689         if (sys_acl_get_permset(mask_entry, &mask_permset) == -1) {
1690                 DEBUG(0,("set_canon_ace_list: Failed to get mask permset. (%s)\n", strerror(errno) ));
1691                 goto done;
1692         }
1693
1694         if (map_acl_perms_to_permset(S_IRUSR|S_IWUSR|S_IXUSR, &mask_permset) == -1) {
1695                 DEBUG(0,("set_canon_ace_list: Failed to create mask permset. (%s)\n", strerror(errno) ));
1696                 goto done;
1697         }
1698
1699         if (sys_acl_set_permset(mask_entry, mask_permset) == -1) {
1700                 DEBUG(0,("set_canon_ace_list: Failed to add mask permset. (%s)\n", strerror(errno) ));
1701                 goto done;
1702         }
1703
1704         /*
1705          * Check if the ACL is valid.
1706          */
1707
1708         if (sys_acl_valid(the_acl) == -1) {
1709                 DEBUG(0,("set_canon_ace_list: ACL type (%s) is invalid for set (%s).\n",
1710                                 the_acl_type == SMB_ACL_TYPE_DEFAULT ? "directory default" : "file",
1711                                 strerror(errno) ));
1712                 goto done;
1713         }
1714
1715         /*
1716          * Finally apply it to the file or directory.
1717          */
1718
1719         if(default_ace || fsp->is_directory || fsp->fd == -1) {
1720                 if (sys_acl_set_file(dos_to_unix(fsp->fsp_name,False), the_acl_type, the_acl) == -1) {
1721                         DEBUG(0,("set_canon_ace_list: sys_acl_set_file type %s failed for file %s (%s).\n",
1722                                         the_acl_type == SMB_ACL_TYPE_DEFAULT ? "directory default" : "file",
1723                                         fsp->fsp_name, strerror(errno) ));
1724                         goto done;
1725                 }
1726         } else {
1727                 if (sys_acl_set_fd(fsp->fd, the_acl) == -1) {
1728                         DEBUG(0,("set_canon_ace_list: sys_acl_set_file failed for file %s (%s).\n",
1729                                         fsp->fsp_name, strerror(errno) ));
1730                         goto done;
1731                 }
1732         }
1733
1734         ret = True;
1735
1736   done:
1737
1738         if (the_acl != NULL)
1739             sys_acl_free_acl(the_acl);
1740
1741         return ret;
1742 }
1743
1744 /****************************************************************************
1745  Convert a canon_ace to a generic 3 element permission - if possible.
1746 ****************************************************************************/
1747
1748 #define MAP_PERM(p,mask,result) (((p) & (mask)) ? (result) : 0 )
1749
1750 static BOOL convert_canon_ace_to_posix_perms( files_struct *fsp, canon_ace *file_ace_list, mode_t *posix_perms)
1751 {
1752         size_t ace_count = count_canon_ace_list(file_ace_list);
1753         canon_ace *ace_p;
1754         canon_ace *owner_ace = NULL;
1755         canon_ace *group_ace = NULL;
1756         canon_ace *other_ace = NULL;
1757
1758         if (ace_count != 3) {
1759                 DEBUG(3,("convert_canon_ace_to_posix_perms: Too many ACE entries for file %s to convert to \
1760 posix perms.\n", fsp->fsp_name ));
1761                 return False;
1762         }
1763
1764         for (ace_p = file_ace_list; ace_p; ace_p = ace_p->next) {
1765                 if (ace_p->owner_type == UID_ACE)
1766                         owner_ace = ace_p;
1767                 else if (ace_p->owner_type == GID_ACE)
1768                         group_ace = ace_p;
1769                 else if (ace_p->owner_type == WORLD_ACE)
1770                         other_ace = ace_p;
1771         }
1772
1773         if (!owner_ace || !group_ace || !other_ace) {
1774                 DEBUG(3,("convert_canon_ace_to_posix_perms: Can't get standard entries for file %s.\n",
1775                                 fsp->fsp_name ));
1776                 return False;
1777         }
1778
1779         *posix_perms = (mode_t)0;
1780
1781         *posix_perms |= owner_ace->perms;
1782         *posix_perms |= MAP_PERM(group_ace->perms, S_IRUSR, S_IRGRP);
1783         *posix_perms |= MAP_PERM(group_ace->perms, S_IWUSR, S_IWGRP);
1784         *posix_perms |= MAP_PERM(group_ace->perms, S_IXUSR, S_IXGRP);
1785         *posix_perms |= MAP_PERM(other_ace->perms, S_IRUSR, S_IROTH);
1786         *posix_perms |= MAP_PERM(other_ace->perms, S_IWUSR, S_IWOTH);
1787         *posix_perms |= MAP_PERM(other_ace->perms, S_IXUSR, S_IXOTH);
1788
1789         /* The owner must have at least read access. */
1790
1791         if (*posix_perms == (mode_t)0)
1792                 *posix_perms = S_IRUSR;
1793
1794         DEBUG(10,("convert_canon_ace_to_posix_perms: converted u=%o,g=%o,w=%o to perm=0%o for file %s.\n",
1795                 (int)owner_ace->perms, (int)group_ace->perms, (int)other_ace->perms, (int)*posix_perms,
1796                 fsp->fsp_name ));
1797
1798         return True;
1799 }
1800
1801 static int nt_ace_comp( SEC_ACE *a1, SEC_ACE *a2)
1802 {
1803         if (a1->type == a2->type)
1804                 return 0;
1805
1806         if (a1->type == SEC_ACE_TYPE_ACCESS_DENIED && a2->type == SEC_ACE_TYPE_ACCESS_ALLOWED)
1807                 return -1;
1808         return 1;
1809 }
1810
1811 /****************************************************************************
1812  Reply to query a security descriptor from an fsp. If it succeeds it allocates
1813  the space for the return elements and returns the size needed to return the
1814  security descriptor. This should be the only external function needed for
1815  the UNIX style get ACL.
1816 ****************************************************************************/
1817
1818 size_t get_nt_acl(files_struct *fsp, SEC_DESC **ppdesc)
1819 {
1820         SMB_STRUCT_STAT sbuf;
1821         SEC_ACE *nt_ace_list = NULL;
1822         DOM_SID owner_sid;
1823         DOM_SID group_sid;
1824         size_t sd_size = 0;
1825         SEC_ACL *psa = NULL;
1826         size_t num_acls = 0;
1827         size_t num_dir_acls = 0;
1828         size_t num_aces = 0;
1829         SMB_ACL_T posix_acl = NULL;
1830         SMB_ACL_T dir_acl = NULL;
1831         canon_ace *file_ace = NULL;
1832         canon_ace *dir_ace = NULL;
1833  
1834         *ppdesc = NULL;
1835
1836         DEBUG(10,("get_nt_acl: called for file %s\n", fsp->fsp_name ));
1837
1838         if(fsp->is_directory || fsp->fd == -1) {
1839
1840                 /* Get the stat struct for the owner info. */
1841                 if(vfs_stat(fsp->conn,fsp->fsp_name, &sbuf) != 0) {
1842                         return 0;
1843                 }
1844                 /*
1845                  * Get the ACL from the path.
1846                  */
1847
1848                 posix_acl = sys_acl_get_file( dos_to_unix(fsp->fsp_name, False), SMB_ACL_TYPE_ACCESS);
1849
1850                 /*
1851                  * If it's a directory get the default POSIX ACL.
1852                  */
1853
1854                 if(fsp->is_directory)
1855                         dir_acl = sys_acl_get_file( dos_to_unix(fsp->fsp_name, False), SMB_ACL_TYPE_DEFAULT);
1856
1857         } else {
1858
1859                 /* Get the stat struct for the owner info. */
1860                 if(vfs_fstat(fsp,fsp->fd,&sbuf) != 0) {
1861                         return 0;
1862                 }
1863                 /*
1864                  * Get the ACL from the fd.
1865                  */
1866                 posix_acl = sys_acl_get_fd(fsp->fd);
1867         }
1868
1869         DEBUG(5,("get_nt_acl : file ACL %s, directory ACL %s\n",
1870                         posix_acl ? "present" :  "absent",
1871                         dir_acl ? "present" :  "absent" ));
1872
1873         /*
1874          * Get the owner, group and world SIDs.
1875          */
1876
1877         create_file_sids(&sbuf, &owner_sid, &group_sid);
1878
1879         /* Create the canon_ace lists. */
1880         file_ace = canonicalise_acl( fsp, posix_acl, &sbuf,  &owner_sid, &group_sid, False);
1881         num_acls = count_canon_ace_list(file_ace);
1882
1883         /* We must have *some* ACLS. */
1884
1885         if (num_acls == 0) {
1886                 DEBUG(0,("get_nt_acl : No ACLs on file (%s) !\n", fsp->fsp_name ));
1887                 return 0;
1888         }
1889
1890         if (fsp->is_directory) { 
1891                 dir_ace = canonicalise_acl(fsp, dir_acl, &sbuf, &owner_sid, &group_sid, True);
1892                 num_dir_acls = count_canon_ace_list(dir_ace);
1893         }
1894
1895         /* Allocate the ace list. */
1896         if ((nt_ace_list = (SEC_ACE *)malloc((num_acls + num_dir_acls)* sizeof(SEC_ACE))) == NULL) {
1897                 DEBUG(0,("get_nt_acl: Unable to malloc space for nt_ace_list.\n"));
1898                 goto done;
1899         }
1900
1901         memset(nt_ace_list, '\0', (num_acls + num_dir_acls) * sizeof(SEC_ACE) );
1902
1903         /*
1904          * Create the NT ACE list from the canonical ace lists.
1905          */
1906
1907         {
1908                 canon_ace *ace;
1909                 int nt_acl_type;
1910                 int i;
1911
1912                 ace = file_ace;
1913
1914                 for (i = 0; i < num_acls; i++, ace = ace->next) {
1915                         SEC_ACCESS acc = map_canon_ace_perms(&nt_acl_type, &owner_sid, ace );
1916                         init_sec_ace(&nt_ace_list[num_aces++], &ace->sid, nt_acl_type, acc, 0);
1917                 }
1918
1919                 ace = dir_ace;
1920
1921                 for (i = 0; i < num_dir_acls; i++, ace = ace->next) {
1922                         SEC_ACCESS acc = map_canon_ace_perms(&nt_acl_type, &owner_sid, ace );
1923                         init_sec_ace(&nt_ace_list[num_aces++], &ace->sid, nt_acl_type, acc, 
1924                                         SEC_ACE_FLAG_OBJECT_INHERIT|SEC_ACE_FLAG_CONTAINER_INHERIT|SEC_ACE_FLAG_INHERIT_ONLY);
1925                 }
1926
1927                 /*
1928                  * Sort to force deny entries to the front.
1929                  */
1930
1931                 if (num_acls + num_dir_acls)
1932                         qsort( nt_ace_list, num_acls + num_dir_acls, sizeof(nt_ace_list[0]), QSORT_CAST nt_ace_comp);
1933         }
1934
1935         if (num_acls) {
1936                 if((psa = make_sec_acl( main_loop_talloc_get(), ACL_REVISION, num_aces, nt_ace_list)) == NULL) {
1937                         DEBUG(0,("get_nt_acl: Unable to malloc space for acl.\n"));
1938                         goto done;
1939                 }
1940         }
1941
1942         *ppdesc = make_standard_sec_desc( main_loop_talloc_get(), &owner_sid, &group_sid, psa, &sd_size);
1943
1944         if(!*ppdesc) {
1945                 DEBUG(0,("get_nt_acl: Unable to malloc space for security descriptor.\n"));
1946                 sd_size = 0;
1947         }
1948
1949   done:
1950
1951         if (posix_acl)  
1952                 sys_acl_free_acl(posix_acl);
1953         if (dir_acl)
1954                 sys_acl_free_acl(dir_acl);
1955         free_canon_ace_list(file_ace);
1956         free_canon_ace_list(dir_ace);
1957         if (nt_ace_list)
1958                 free(nt_ace_list);
1959
1960         return sd_size;
1961 }
1962
1963 /****************************************************************************
1964  Reply to set a security descriptor on an fsp. security_info_sent is the
1965  description of the following NT ACL.
1966  This should be the only external function needed for the UNIX style set ACL.
1967 ****************************************************************************/
1968
1969 BOOL set_nt_acl(files_struct *fsp, uint32 security_info_sent, SEC_DESC *psd)
1970 {
1971         connection_struct *conn = fsp->conn;
1972         uid_t user = (uid_t)-1;
1973         gid_t grp = (gid_t)-1;
1974         SMB_STRUCT_STAT sbuf;  
1975         DOM_SID file_owner_sid;
1976         DOM_SID file_grp_sid;
1977         canon_ace *file_ace_list = NULL;
1978         canon_ace *dir_ace_list = NULL;
1979         BOOL acl_perms = False;
1980
1981         DEBUG(10,("set_nt_acl: called for file %s\n", fsp->fsp_name ));
1982
1983         /*
1984          * Get the current state of the file.
1985          */
1986
1987         if(fsp->is_directory || fsp->fd == -1) {
1988                 if(vfs_stat(fsp->conn,fsp->fsp_name, &sbuf) != 0)
1989                         return False;
1990         } else {
1991                 if(vfs_fstat(fsp,fsp->fd,&sbuf) != 0)
1992                         return False;
1993         }
1994
1995         /*
1996          * Unpack the user/group/world id's.
1997          */
1998
1999         if (!unpack_nt_owners( &sbuf, &user, &grp, security_info_sent, psd))
2000                 return False;
2001
2002         /*
2003          * Do we need to chown ?
2004          */
2005
2006         if((user != (uid_t)-1 || grp != (uid_t)-1) && (sbuf.st_uid != user || sbuf.st_gid != grp)) {
2007
2008                 DEBUG(3,("set_nt_acl: chown %s. uid = %u, gid = %u.\n",
2009                                 fsp->fsp_name, (unsigned int)user, (unsigned int)grp ));
2010
2011                 if(vfs_chown( fsp->conn, fsp->fsp_name, user, grp) == -1) {
2012                         DEBUG(3,("set_nt_acl: chown %s, %u, %u failed. Error = %s.\n",
2013                                 fsp->fsp_name, (unsigned int)user, (unsigned int)grp, strerror(errno) ));
2014                         return False;
2015                 }
2016
2017                 /*
2018                  * Recheck the current state of the file, which may have changed.
2019                  * (suid/sgid bits, for instance)
2020                  */
2021
2022                 if(fsp->is_directory) {
2023                         if(vfs_stat(fsp->conn, fsp->fsp_name, &sbuf) != 0) {
2024                                 return False;
2025                         }
2026                 } else {
2027
2028                         int ret;
2029     
2030                         if(fsp->fd == -1)
2031                                 ret = vfs_stat(fsp->conn, fsp->fsp_name, &sbuf);
2032                         else
2033                                 ret = vfs_fstat(fsp,fsp->fd,&sbuf);
2034   
2035                         if(ret != 0)
2036                                 return False;
2037                 }
2038         }
2039
2040         create_file_sids(&sbuf, &file_owner_sid, &file_grp_sid);
2041
2042         acl_perms = unpack_canon_ace( fsp, &sbuf, &file_owner_sid, &file_grp_sid,
2043                                                                         &file_ace_list, &dir_ace_list, security_info_sent, psd);
2044
2045         if ((file_ace_list == NULL) && (dir_ace_list == NULL)) {
2046                 /* W2K traverse DACL set - ignore. */
2047                 return True;
2048     }
2049
2050         if (!acl_perms) {
2051                 DEBUG(3,("set_nt_acl: cannot set permissions\n"));
2052                 free_canon_ace_list(file_ace_list);
2053                 free_canon_ace_list(dir_ace_list); 
2054                 return False;
2055         }
2056
2057         /*
2058          * Only change security if we got a DACL.
2059          */
2060
2061         if((security_info_sent & DACL_SECURITY_INFORMATION) && (psd->dacl != NULL)) {
2062
2063                 BOOL acl_set_support = False;
2064                 BOOL ret = False;
2065
2066                 /*
2067                  * Try using the POSIX ACL set first. Fall back to chmod if
2068                  * we have no ACL support on this filesystem.
2069                  */
2070
2071                 if (acl_perms && file_ace_list) {
2072                         ret = set_canon_ace_list(fsp, file_ace_list, False, &acl_set_support);
2073                         if (acl_set_support && ret == False) {
2074                                 DEBUG(3,("set_nt_acl: failed to set file acl on file %s (%s).\n", fsp->fsp_name, strerror(errno) ));
2075                                 free_canon_ace_list(file_ace_list);
2076                                 free_canon_ace_list(dir_ace_list); 
2077                                 return False;
2078                         }
2079                 }
2080
2081                 if (acl_perms && acl_set_support && fsp->is_directory && dir_ace_list) {
2082                         if (!set_canon_ace_list(fsp, dir_ace_list, True, &acl_set_support)) {
2083                                 DEBUG(3,("set_nt_acl: failed to set default acl on directory %s (%s).\n", fsp->fsp_name, strerror(errno) ));
2084                                 free_canon_ace_list(file_ace_list);
2085                                 free_canon_ace_list(dir_ace_list); 
2086                                 return False;
2087                         }
2088                 }
2089
2090                 /*
2091                  * If we cannot set using POSIX ACLs we fall back to checking if we need to chmod.
2092                  */
2093
2094                 if(!acl_set_support && acl_perms) {
2095                         mode_t posix_perms;
2096
2097                         if (!convert_canon_ace_to_posix_perms( fsp, file_ace_list, &posix_perms)) {
2098                                 free_canon_ace_list(file_ace_list);
2099                                 free_canon_ace_list(dir_ace_list);
2100                                 DEBUG(3,("set_nt_acl: failed to convert file acl to posix permissions for file %s.\n",
2101                                         fsp->fsp_name ));
2102                                 return False;
2103                         }
2104
2105                         if (sbuf.st_mode != posix_perms) {
2106
2107                                 DEBUG(3,("set_nt_acl: chmod %s. perms = 0%o.\n",
2108                                         fsp->fsp_name, (unsigned int)posix_perms ));
2109
2110                                 if(conn->vfs_ops.chmod(conn,dos_to_unix(fsp->fsp_name, False), posix_perms) == -1) {
2111                                         DEBUG(3,("set_nt_acl: chmod %s, 0%o failed. Error = %s.\n",
2112                                                         fsp->fsp_name, (unsigned int)posix_perms, strerror(errno) ));
2113                                         free_canon_ace_list(file_ace_list);
2114                                         free_canon_ace_list(dir_ace_list);
2115                                         return False;
2116                                 }
2117                         }
2118                 }
2119         }
2120
2121         free_canon_ace_list(file_ace_list);
2122         free_canon_ace_list(dir_ace_list); 
2123
2124         return True;
2125 }
2126
2127 /****************************************************************************
2128  Do a chmod by setting the ACL USER_OBJ, GROUP_OBJ and OTHER bits in an ACL
2129  and set the mask to rwx. Needed to preserve complex ACLs set by NT.
2130 ****************************************************************************/
2131
2132 static int chmod_acl_internals( SMB_ACL_T posix_acl, mode_t mode)
2133 {
2134         int entry_id = SMB_ACL_FIRST_ENTRY;
2135         SMB_ACL_ENTRY_T entry;
2136         int num_entries = 0;
2137
2138         while ( sys_acl_get_entry(posix_acl, entry_id, &entry) == 1) {
2139                 SMB_ACL_TAG_T tagtype;
2140                 SMB_ACL_PERMSET_T permset;
2141                 mode_t perms;
2142
2143                 /* get_next... */
2144                 if (entry_id == SMB_ACL_FIRST_ENTRY)
2145                         entry_id = SMB_ACL_NEXT_ENTRY;
2146
2147                 if (sys_acl_get_tag_type(entry, &tagtype) == -1)
2148                         return -1;
2149
2150                 if (sys_acl_get_permset(entry, &permset) == -1)
2151                         return -1;
2152
2153                 num_entries++;
2154
2155                 switch(tagtype) {
2156                         case SMB_ACL_USER_OBJ:
2157                                 perms = unix_perms_to_acl_perms(mode, S_IRUSR, S_IWUSR, S_IXUSR);
2158                 break;
2159                         case SMB_ACL_GROUP_OBJ:
2160                                 perms = unix_perms_to_acl_perms(mode, S_IRGRP, S_IWGRP, S_IXGRP);
2161                                 break;
2162                         case SMB_ACL_MASK:
2163                                 perms = S_IRUSR|S_IWUSR|S_IXUSR;
2164                                 break;
2165                         case SMB_ACL_OTHER:
2166                                 perms = unix_perms_to_acl_perms(mode, S_IROTH, S_IWOTH, S_IXOTH);
2167                                 break;
2168                         default:
2169                                 continue;
2170                 }
2171
2172                 if (map_acl_perms_to_permset(perms, &permset) == -1)
2173                         return -1;
2174
2175                 if (sys_acl_set_permset(entry, permset) == -1)
2176                         return -1;
2177         }
2178
2179         /*
2180          * If this is a simple 3 element ACL then it's a standard
2181          * UNIX permission set. Just use chmod...       
2182          */
2183
2184         if (num_entries == 3)
2185                 return -1;
2186
2187         return 0;
2188 }
2189
2190 /****************************************************************************
2191  Do a chmod by setting the ACL USER_OBJ, GROUP_OBJ and OTHER bits in an ACL
2192  and set the mask to rwx. Needed to preserve complex ACLs set by NT.
2193  Note that name is in UNIX character set.
2194 ****************************************************************************/
2195
2196 int chmod_acl(char *name, mode_t mode)
2197 {
2198         SMB_ACL_T posix_acl = NULL;
2199         int ret = -1;
2200
2201         if ((posix_acl = sys_acl_get_file(name, SMB_ACL_TYPE_ACCESS)) == NULL)
2202                 return -1;
2203
2204         if ((ret = chmod_acl_internals(posix_acl, mode)) == -1)
2205                 goto done;
2206
2207         ret = sys_acl_set_file(name, SMB_ACL_TYPE_ACCESS, posix_acl);
2208
2209   done:
2210
2211         sys_acl_free_acl(posix_acl);
2212         return ret;
2213 }
2214
2215 /****************************************************************************
2216  Do an fchmod by setting the ACL USER_OBJ, GROUP_OBJ and OTHER bits in an ACL
2217  and set the mask to rwx. Needed to preserve complex ACLs set by NT.
2218 ****************************************************************************/
2219
2220 int fchmod_acl(int fd, mode_t mode)
2221 {
2222         SMB_ACL_T posix_acl = NULL;
2223         int ret = -1;
2224
2225         if ((posix_acl = sys_acl_get_fd(fd)) == NULL)
2226                 return -1;
2227
2228         if ((ret = chmod_acl_internals(posix_acl, mode)) == -1)
2229                 goto done;
2230
2231         ret = sys_acl_set_fd(fd, posix_acl);
2232
2233   done:
2234
2235         sys_acl_free_acl(posix_acl);
2236         return ret;
2237 }