Merge of new 2.2 code into HEAD (Gerald I hate you :-) :-). Allows new SAMR
[sfrench/samba-autobuild/.git] / source3 / 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 enum ace_owner {UID_ACE, GID_ACE, WORLD_ACE};
25
26 typedef union posix_id {
27                 uid_t uid;
28                 gid_t gid;
29                 int world;
30 } posix_id;
31
32 typedef struct canon_ace {
33         struct canon_ace *next, *prev;
34         SMB_ACL_TAG_T type;
35         mode_t perms; /* Only use S_I(R|W|X)USR mode bits here. */
36         DOM_SID sid;
37         enum ace_owner owner_type;
38         posix_id unix_ug; 
39 } canon_ace;
40
41 static void free_canon_ace_list( canon_ace *list_head );
42
43 #if !defined(HAVE_NO_ACLS)
44 /****************************************************************************
45  Function to duplicate a canon_ace entry.
46 ****************************************************************************/
47
48 static canon_ace *dup_canon_ace( canon_ace *src_ace)
49 {
50         canon_ace *dst_ace = (canon_ace *)malloc(sizeof(canon_ace));
51
52         if (dst_ace == NULL)
53                 return NULL;
54
55         *dst_ace = *src_ace;
56         dst_ace->prev = dst_ace->next = NULL;
57         return dst_ace;
58 }
59 #endif /* HAVE_NO_ACLS */
60
61 /****************************************************************************
62  Function to create owner and group SIDs from a SMB_STRUCT_STAT.
63 ****************************************************************************/
64
65 static void create_file_sids(SMB_STRUCT_STAT *psbuf, DOM_SID *powner_sid, DOM_SID *pgroup_sid)
66 {
67         uid_to_sid( powner_sid, psbuf->st_uid );
68         gid_to_sid( pgroup_sid, psbuf->st_gid );
69 }
70
71 /****************************************************************************
72  Print out a canon ace.
73 ****************************************************************************/
74
75 static void print_canon_ace(canon_ace *ace, int num)
76 {
77         fstring str;
78
79         dbgtext( "canon_ace index %d.", num );
80     dbgtext( "SID = %s ", sid_to_string( str, &ace->sid));
81         if (ace->owner_type == UID_ACE) {
82                 struct passwd *pass = sys_getpwuid(ace->unix_ug.uid);
83                 dbgtext( "uid %u (%s) ", (unsigned int)ace->unix_ug.uid, pass ? pass->pw_name : "UNKNOWN");
84         } else if (ace->owner_type == GID_ACE) {
85                 struct group *grp = getgrgid(ace->unix_ug.gid);
86                 dbgtext( "gid %u (%s) ", (unsigned int)ace->unix_ug.gid, grp ? grp->gr_name : "UNKNOWN");
87         } else
88                 dbgtext( "other ");
89         switch (ace->type) {
90                 case SMB_ACL_USER:
91                         dbgtext( "SMB_ACL_USER ");
92                         break;
93                 case SMB_ACL_USER_OBJ:
94                         dbgtext( "SMB_ACL_USER_OBJ ");
95                         break;
96                 case SMB_ACL_GROUP:
97                         dbgtext( "SMB_ACL_GROUP ");
98                         break;
99                 case SMB_ACL_GROUP_OBJ:
100                         dbgtext( "SMB_ACL_GROUP_OBJ ");
101                         break;
102                 case SMB_ACL_OTHER:
103                         dbgtext( "SMB_ACL_OTHER ");
104                         break;
105         }
106         dbgtext( "perms ");
107         dbgtext( "%c", ace->perms & S_IRUSR ? 'r' : '-');
108         dbgtext( "%c", ace->perms & S_IWUSR ? 'w' : '-');
109         dbgtext( "%c\n", ace->perms & S_IXUSR ? 'x' : '-');
110 }
111
112 /****************************************************************************
113  Map canon_ace perms to permission bits NT.
114 ****************************************************************************/
115
116 static SEC_ACCESS map_canon_ace_perms(int *pacl_type, DOM_SID *powner_sid, canon_ace *ace)
117 {
118         SEC_ACCESS sa;
119         uint32 nt_mask = 0;
120
121         *pacl_type = SEC_ACE_TYPE_ACCESS_ALLOWED;
122
123         if((ace->perms & (S_IRUSR|S_IWUSR|S_IXUSR)) == (S_IRUSR|S_IWUSR|S_IXUSR)) {
124                         nt_mask = UNIX_ACCESS_RWX;
125         } else if((ace->perms & (S_IRUSR|S_IWUSR|S_IXUSR)) == 0) {
126                 /*
127                  * Here we differentiate between the owner and any other user.
128                  */
129                 if (sid_equal(powner_sid, &ace->sid)) {
130                         nt_mask = UNIX_ACCESS_NONE;
131                 } else {
132                         /* Not owner, no access. */
133                         nt_mask = 0;
134                 }
135         } else {
136                 nt_mask |= ((ace->perms & S_IRUSR) ? UNIX_ACCESS_R : 0 );
137                 nt_mask |= ((ace->perms & S_IWUSR) ? UNIX_ACCESS_W : 0 );
138                 nt_mask |= ((ace->perms & S_IXUSR) ? UNIX_ACCESS_X : 0 );
139         }
140
141         DEBUG(10,("map_canon_ace_perms: Mapped (UNIX) %x to (NT) %x\n",
142                         (unsigned int)ace->perms, (unsigned int)nt_mask ));
143
144         init_sec_access(&sa,nt_mask);
145         return sa;
146 }
147
148 /****************************************************************************
149  Map NT perms to a UNIX mode_t.
150 ****************************************************************************/
151
152 #define FILE_SPECIFIC_READ_BITS (FILE_READ_DATA|FILE_READ_EA|FILE_READ_ATTRIBUTES)
153 #define FILE_SPECIFIC_WRITE_BITS (FILE_WRITE_DATA|FILE_APPEND_DATA|FILE_WRITE_EA|FILE_WRITE_ATTRIBUTES)
154 #define FILE_SPECIFIC_EXECUTE_BITS (FILE_EXECUTE)
155
156 static mode_t map_nt_perms( SEC_ACCESS sec_access, int type)
157 {
158         mode_t mode = 0;
159
160         switch(type) {
161         case S_IRUSR:
162                 if(sec_access.mask & GENERIC_ALL_ACCESS)
163                         mode = S_IRUSR|S_IWUSR|S_IXUSR;
164                 else {
165                         mode |= (sec_access.mask & (GENERIC_READ_ACCESS|FILE_SPECIFIC_READ_BITS)) ? S_IRUSR : 0;
166                         mode |= (sec_access.mask & (GENERIC_WRITE_ACCESS|FILE_SPECIFIC_WRITE_BITS)) ? S_IWUSR : 0;
167                         mode |= (sec_access.mask & (GENERIC_EXECUTE_ACCESS|FILE_SPECIFIC_EXECUTE_BITS)) ? S_IXUSR : 0;
168                 }
169                 break;
170         case S_IRGRP:
171                 if(sec_access.mask & GENERIC_ALL_ACCESS)
172                         mode = S_IRGRP|S_IWGRP|S_IXGRP;
173                 else {
174                         mode |= (sec_access.mask & (GENERIC_READ_ACCESS|FILE_SPECIFIC_READ_BITS)) ? S_IRGRP : 0;
175                         mode |= (sec_access.mask & (GENERIC_WRITE_ACCESS|FILE_SPECIFIC_WRITE_BITS)) ? S_IWGRP : 0;
176                         mode |= (sec_access.mask & (GENERIC_EXECUTE_ACCESS|FILE_SPECIFIC_EXECUTE_BITS)) ? S_IXGRP : 0;
177                 }
178                 break;
179         case S_IROTH:
180                 if(sec_access.mask & GENERIC_ALL_ACCESS)
181                         mode = S_IROTH|S_IWOTH|S_IXOTH;
182                 else {
183                         mode |= (sec_access.mask & (GENERIC_READ_ACCESS|FILE_SPECIFIC_READ_BITS)) ? S_IROTH : 0;
184                         mode |= (sec_access.mask & (GENERIC_WRITE_ACCESS|FILE_SPECIFIC_WRITE_BITS)) ? S_IWOTH : 0;
185                         mode |= (sec_access.mask & (GENERIC_EXECUTE_ACCESS|FILE_SPECIFIC_EXECUTE_BITS)) ? S_IXOTH : 0;
186                 }
187                 break;
188         }
189
190         return mode;
191 }
192
193 /****************************************************************************
194  Unpack a SEC_DESC into a UNIX owner and group.
195 ****************************************************************************/
196
197 static BOOL unpack_nt_owners(SMB_STRUCT_STAT *psbuf, uid_t *puser, gid_t *pgrp, uint32 security_info_sent, SEC_DESC *psd)
198 {
199         DOM_SID owner_sid;
200         DOM_SID grp_sid;
201         enum SID_NAME_USE sid_type;
202
203         *puser = (uid_t)-1;
204         *pgrp = (gid_t)-1;
205
206         if(security_info_sent == 0) {
207                 DEBUG(0,("unpack_nt_owners: no security info sent !\n"));
208                 return False;
209         }
210
211         /*
212          * Validate the owner and group SID's.
213          */
214
215         memset(&owner_sid, '\0', sizeof(owner_sid));
216         memset(&grp_sid, '\0', sizeof(grp_sid));
217
218         DEBUG(5,("unpack_nt_owners: validating owner_sids.\n"));
219
220         /*
221          * Don't immediately fail if the owner sid cannot be validated.
222          * This may be a group chown only set.
223          */
224
225         if (security_info_sent & OWNER_SECURITY_INFORMATION) {
226                 sid_copy(&owner_sid, psd->owner_sid);
227                 if (!sid_to_uid( &owner_sid, puser, &sid_type))
228                         DEBUG(3,("unpack_nt_owners: unable to validate owner sid.\n"));
229         }
230
231         /*
232          * Don't immediately fail if the group sid cannot be validated.
233          * This may be an owner chown only set.
234          */
235
236         if (security_info_sent & GROUP_SECURITY_INFORMATION) {
237                 sid_copy(&grp_sid, psd->grp_sid);
238                 if (!sid_to_gid( &grp_sid, pgrp, &sid_type))
239                         DEBUG(3,("unpack_nt_owners: unable to validate group sid.\n"));
240         }
241
242         DEBUG(5,("unpack_nt_owners: owner_sids validated.\n"));
243
244         return True;
245 }
246
247 #if !defined(HAVE_NO_ACLS)
248 /****************************************************************************
249  Merge aces with a common user.
250 ****************************************************************************/
251
252 static BOOL merge_aces( canon_ace *list_head, canon_ace *p_ace)
253 {
254         canon_ace *curr_ace;
255
256         for (curr_ace = list_head; curr_ace; curr_ace = curr_ace->next) {
257                 if (curr_ace == p_ace)
258                         continue;
259
260                 if (curr_ace->type == p_ace->type && sid_equal(&curr_ace->sid, &p_ace->sid)) {
261                         if( DEBUGLVL( 10 )) {
262                                 dbgtext("Merging ACE's\n");
263                                 print_canon_ace( p_ace, 0);
264                                 print_canon_ace( curr_ace, 0);
265                         }
266                         p_ace->perms |= curr_ace->perms;
267                         DLIST_REMOVE(list_head, curr_ace);
268                         free(curr_ace);
269                         return True;
270                 }
271         }
272
273         return False;
274 }
275
276 /****************************************************************************
277  Create a default mode for a directory default ACE.
278 ****************************************************************************/
279
280 static mode_t get_default_ace_mode(files_struct *fsp, int type)
281 {
282     mode_t force_mode = lp_force_dir_security_mode(SNUM(fsp->conn));
283         mode_t mode = 0;
284
285         switch(type) {
286                 case S_IRUSR:
287                         mode |= (force_mode & S_IRUSR) ? S_IRUSR : 0;
288                         mode |= (force_mode & S_IWUSR) ? S_IWUSR : 0;
289                         mode |= (force_mode & S_IXUSR) ? S_IXUSR : 0;
290                         break;
291                 case S_IRGRP:
292                         mode |= (force_mode & S_IRGRP) ? S_IRUSR : 0;
293                         mode |= (force_mode & S_IWGRP) ? S_IWUSR : 0;
294                         mode |= (force_mode & S_IXGRP) ? S_IXUSR : 0;
295                         break;
296                 case S_IROTH:
297                         mode |= (force_mode & S_IROTH) ? S_IRUSR : 0;
298                         mode |= (force_mode & S_IWOTH) ? S_IWUSR : 0;
299                         mode |= (force_mode & S_IXOTH) ? S_IXUSR : 0;
300                         break;
301         }
302
303         return mode;
304 }
305
306 /****************************************************************************
307  A well formed POSIX file or default ACL has at least 3 entries, a 
308  SMB_ACL_USER_OBJ, SMB_ACL_GROUP_OBJ, SMB_ACL_OTHER_OBJ.
309 ****************************************************************************/
310
311 static BOOL ensure_canon_entry_valid(canon_ace **pp_ace,
312                                                         files_struct *fsp,
313                                                         DOM_SID *pfile_owner_sid,
314                                                         DOM_SID *pfile_grp_sid,
315                                                         SMB_STRUCT_STAT *pst,
316                                                         BOOL default_acl)
317 {
318         extern DOM_SID global_sid_World;
319         canon_ace *pace;
320         BOOL got_user = False;
321         BOOL got_grp = False;
322         BOOL got_other = False;
323
324         for (pace = *pp_ace; pace; pace = pace->next) {
325                 if (pace->type == SMB_ACL_USER_OBJ)
326                         got_user = True;
327                 else if (pace->type == SMB_ACL_GROUP_OBJ)
328                         got_grp = True;
329                 else if (pace->type == SMB_ACL_OTHER)
330                         got_other = True;
331         }
332
333         if (!got_user) {
334                 if ((pace = (canon_ace *)malloc(sizeof(canon_ace))) == NULL) {
335                         DEBUG(0,("ensure_canon_entry_valid: malloc fail.\n"));
336                         return False;
337                 }
338
339                 ZERO_STRUCTP(pace);
340                 pace->type = SMB_ACL_USER_OBJ;
341                 pace->owner_type = UID_ACE;
342                 pace->unix_ug.uid = pst->st_uid;
343                 pace->sid = *pfile_owner_sid;
344                 pace->perms = default_acl ? get_default_ace_mode(fsp, S_IRUSR): 0;
345
346                 DLIST_ADD(*pp_ace, pace);
347         }
348
349         if (!got_grp) {
350                 if ((pace = (canon_ace *)malloc(sizeof(canon_ace))) == NULL) {
351                         DEBUG(0,("ensure_canon_entry_valid: malloc fail.\n"));
352                         return False;
353                 }
354
355                 ZERO_STRUCTP(pace);
356                 pace->type = SMB_ACL_GROUP_OBJ;
357                 pace->owner_type = GID_ACE;
358                 pace->unix_ug.uid = pst->st_gid;
359                 pace->sid = *pfile_grp_sid;
360                 pace->perms = default_acl ? get_default_ace_mode(fsp, S_IRGRP): 0;
361
362                 DLIST_ADD(*pp_ace, pace);
363         }
364
365         if (!got_other) {
366                 if ((pace = (canon_ace *)malloc(sizeof(canon_ace))) == NULL) {
367                         DEBUG(0,("ensure_canon_entry_valid: malloc fail.\n"));
368                         return False;
369                 }
370
371                 ZERO_STRUCTP(pace);
372                 pace->type = SMB_ACL_OTHER;
373                 pace->owner_type = WORLD_ACE;
374                 pace->unix_ug.world = -1;
375                 pace->sid = global_sid_World;
376                 pace->perms = default_acl ? get_default_ace_mode(fsp, S_IROTH): 0;
377
378                 DLIST_ADD(*pp_ace, pace);
379         }
380
381         return True;
382 }
383 #endif /* HAVE_NO_ACLS */
384
385 /****************************************************************************
386  Unpack a SEC_DESC into two canonical ace lists. We don't depend on this
387  succeeding.
388 ****************************************************************************/
389
390 static BOOL unpack_canon_ace(files_struct *fsp, 
391                                                         SMB_STRUCT_STAT *pst,
392                                                         DOM_SID *pfile_owner_sid,
393                                                         DOM_SID *pfile_grp_sid,
394                                                         canon_ace **ppfile_ace, canon_ace **ppdir_ace,
395                                                         uint32 security_info_sent, SEC_DESC *psd)
396 {
397 #if !defined(HAVE_NO_ACLS)
398         extern DOM_SID global_sid_World;
399         BOOL all_aces_are_inherit_only = (fsp->is_directory ? True : False);
400         canon_ace *file_ace = NULL;
401         canon_ace *dir_ace = NULL;
402         canon_ace *current_ace = NULL;
403         enum SID_NAME_USE sid_type;
404         int i;
405 #endif /* HAVE_NO_ACLS */
406         SEC_ACL *dacl = psd->dacl;
407
408         *ppfile_ace = NULL;
409         *ppdir_ace = NULL;
410
411         if(security_info_sent == 0) {
412                 DEBUG(0,("unpack_canon_ace: no security info sent !\n"));
413                 return False;
414         }
415
416         /*
417          * If no DACL then this is a chown only security descriptor.
418          */
419
420         if(!(security_info_sent & DACL_SECURITY_INFORMATION) || !dacl)
421                 return True;
422
423 #if defined(HAVE_NO_ACLS)
424
425         /* No point in doing this if we have no ACL support. */
426         return False;
427
428 #else /* HAVE_NO_ACLS */
429
430         /*
431          * Now go through the DACL and create the canon_ace lists.
432          */
433
434         for(i = 0; i < dacl->num_aces; i++) {
435                 SEC_ACE *psa = &dacl->ace[i];
436
437                 if((psa->type != SEC_ACE_TYPE_ACCESS_ALLOWED) && (psa->type != SEC_ACE_TYPE_ACCESS_DENIED)) {
438                         DEBUG(3,("unpack_canon_ace: unable to set anything but an ALLOW or DENY ACE.\n"));
439                         return False;
440                 }
441
442                 /*
443                  * The security mask may be UNIX_ACCESS_NONE which should map into
444                  * no permissions (we overload the WRITE_OWNER bit for this) or it
445                  * should be one of the ALL/EXECUTE/READ/WRITE bits. Arrange for this
446                  * to be so. Any other bits override the UNIX_ACCESS_NONE bit.
447                  */
448
449                 psa->info.mask &= (GENERIC_ALL_ACCESS|GENERIC_EXECUTE_ACCESS|GENERIC_WRITE_ACCESS|
450                                                         GENERIC_READ_ACCESS|UNIX_ACCESS_NONE|FILE_ALL_ATTRIBUTES);
451
452                 if(psa->info.mask != UNIX_ACCESS_NONE)
453                         psa->info.mask &= ~UNIX_ACCESS_NONE;
454
455                 /*
456                  * Create a cannon_ace entry representing this NT DACL ACE.
457                  */
458
459                 if ((current_ace = (canon_ace *)malloc(sizeof(canon_ace))) == NULL) {
460                         free_canon_ace_list(file_ace);
461                         free_canon_ace_list(dir_ace);
462                         DEBUG(0,("unpack_canon_ace: malloc fail.\n"));
463                         return False;
464                 }
465
466                 ZERO_STRUCTP(current_ace);
467
468                 sid_copy(&current_ace->sid, &psa->sid);
469
470                 /*
471                  * Try and work out if the SID is a user or group
472                  * as we need to flag these differently for POSIX.
473                  */
474
475                 if( sid_equal(&current_ace->sid, &global_sid_World)) {
476                         current_ace->owner_type = WORLD_ACE;
477                         current_ace->unix_ug.world = -1;
478                 } else if (sid_to_uid( &current_ace->sid, &current_ace->unix_ug.uid, &sid_type)) {
479                         current_ace->owner_type = UID_ACE;
480                 } else if (sid_to_gid( &current_ace->sid, &current_ace->unix_ug.gid, &sid_type)) {
481                         current_ace->owner_type = GID_ACE;
482                 } else {
483                         fstring str;
484
485                         free_canon_ace_list(file_ace);
486                         free_canon_ace_list(dir_ace);
487                         free(current_ace);
488                         DEBUG(0,("unpack_canon_ace: unable to map SID %s to uid or gid.\n",
489                                 sid_to_string(str, &current_ace->sid) ));
490                         return False;
491                 }
492
493                 /*
494                  * Map the given NT permissions into a UNIX mode_t containing only
495                  * S_I(R|W|X)USR bits.
496                  */
497
498                 if(psa->type == SEC_ACE_TYPE_ACCESS_ALLOWED)
499                         current_ace->perms |= map_nt_perms( psa->info, S_IRUSR);
500                 else
501                         current_ace->perms = 0;
502
503                 /*
504                  * Now note what kind of a POSIX ACL this should map to.
505                  */
506
507                 if(sid_equal(&current_ace->sid, pfile_owner_sid)) {
508                         /* Note we should apply the default mode/mask here.... FIXME ! JRA */
509                         current_ace->type = SMB_ACL_USER_OBJ;
510                 } else if( sid_equal(&current_ace->sid, pfile_grp_sid)) {
511                         /* Note we should apply the default mode/mask here.... FIXME ! JRA */
512                         current_ace->type = SMB_ACL_GROUP_OBJ;
513                 } else if( sid_equal(&current_ace->sid, &global_sid_World)) {
514                         /* Note we should apply the default mode/mask here.... FIXME ! JRA */
515                         current_ace->type = SMB_ACL_OTHER;
516                 } else {
517                         /*
518                          * Could be a SMB_ACL_USER or SMB_ACL_GROUP. Check by
519                          * looking at owner_type.
520                          */
521
522                         current_ace->type = (current_ace->owner_type == UID_ACE) ? SMB_ACL_USER : SMB_ACL_GROUP;
523                 }
524
525                 if (fsp->is_directory) {
526
527                         /*
528                          * We can only add to the default POSIX ACE list if the ACE is
529                          * designed to be inherited by both files and directories.
530                          */
531                         if ((psa->flags & (SEC_ACE_FLAG_OBJECT_INHERIT|SEC_ACE_FLAG_CONTAINER_INHERIT)) ==
532                                 (SEC_ACE_FLAG_OBJECT_INHERIT|SEC_ACE_FLAG_CONTAINER_INHERIT)) {
533                                 DLIST_ADD(dir_ace, current_ace);
534
535                                 /*
536                                  * If this is not an inherit only ACE we need to add a duplicate
537                                  * to the file acl.
538                                  */
539
540                                 if (!(psa->flags & SEC_ACE_FLAG_INHERIT_ONLY)) {
541                                         canon_ace *dup_ace = dup_canon_ace(current_ace);
542
543                                         if (!dup_ace) {
544                                                 DEBUG(0,("unpack_canon_ace: malloc fail !\n"));
545                                                 free_canon_ace_list(file_ace);
546                                                 free_canon_ace_list(dir_ace);
547                                                 return False;
548                                         }
549
550                                         current_ace = dup_ace;
551                                 } else {
552                                         current_ace = NULL;
553                                 }
554                         }
555                 }
556
557                 /*
558                  * Only add to the file ACL if not inherit only.
559                  */
560
561                 if (!(psa->flags & SEC_ACE_FLAG_INHERIT_ONLY)) {
562                         DLIST_ADD(file_ace, current_ace);
563                         all_aces_are_inherit_only = False;
564                         current_ace = NULL;
565                 }
566
567                 /*
568                  * Free if ACE was not addedd.
569                  */
570
571                 if (current_ace)
572                         free(current_ace);
573         }
574
575         if (fsp->is_directory && all_aces_are_inherit_only) {
576                 /*
577                  * Windows 2000 is doing one of these weird 'inherit acl'
578                  * traverses to conserve NTFS ACL resources. Just pretend
579                  * there was no DACL sent. JRA.
580                  */
581
582                 DEBUG(10,("unpack_canon_ace: Win2k inherit acl traverse. Ignoring DACL.\n"));
583         }
584
585         /*
586          * Now go through the canon_ace lists and merge entries
587          * belonging to identical users.
588          */
589
590   again_file:
591
592         for (current_ace = file_ace; current_ace; current_ace = current_ace->next ) {
593                 if (merge_aces( file_ace, current_ace))
594                         goto again_file;
595         }
596
597   again_dir:
598
599         for (current_ace = dir_ace; current_ace; current_ace = current_ace->next ) {
600                 if (merge_aces( dir_ace, current_ace))
601                         goto again_dir;
602         }
603
604         /*
605          * A well formed POSIX file or default ACL has at least 3 entries, a 
606          * SMB_ACL_USER_OBJ, SMB_ACL_GROUP_OBJ, SMB_ACL_OTHER_OBJ
607          * and optionally a mask entry. Ensure this is the case.
608          */
609
610         if (!ensure_canon_entry_valid(&file_ace, fsp, pfile_owner_sid, pfile_grp_sid, pst, False)) {
611                 free_canon_ace_list(file_ace);
612                 free_canon_ace_list(dir_ace);
613                 return False;
614         }
615
616         if (!ensure_canon_entry_valid(&dir_ace, fsp, pfile_owner_sid, pfile_grp_sid, pst, True)) {
617                 free_canon_ace_list(file_ace);
618                 free_canon_ace_list(dir_ace);
619                 return False;
620         }
621
622         if( DEBUGLVL( 10 )) {
623                 dbgtext("unpack_canon_ace: File ACL:\n");
624                 for (i = 0, current_ace = file_ace; current_ace; current_ace = current_ace->next, i++ ) {
625                         print_canon_ace( current_ace, i);
626                 }
627
628                 dbgtext("unpack_canon_ace: Directory ACL:\n");
629                 for (i = 0, current_ace = dir_ace; current_ace; current_ace = current_ace->next, i++ ) {
630                         print_canon_ace( current_ace, i);
631                 }
632         }
633
634         *ppfile_ace = file_ace;
635         *ppdir_ace = dir_ace;
636         return True;
637
638 #endif /* HAVE_NO_ACLS */
639 }
640
641 /****************************************************************************
642  Unpack a SEC_DESC into a set of standard POSIX permissions.
643 ****************************************************************************/
644
645 static BOOL unpack_posix_permissions(files_struct *fsp, SMB_STRUCT_STAT *psbuf, mode_t *pmode,
646                                                                 uint32 security_info_sent, SEC_DESC *psd, BOOL posix_acls)
647 {
648   extern DOM_SID global_sid_World;
649   connection_struct *conn = fsp->conn;
650   DOM_SID file_owner_sid;
651   DOM_SID file_grp_sid;
652   SEC_ACL *dacl = psd->dacl;
653   BOOL all_aces_are_inherit_only = (fsp->is_directory ? True : False);
654   int i;
655
656   *pmode = 0;
657
658   if(security_info_sent == 0) {
659     DEBUG(0,("unpack_posix_permissions: no security info sent !\n"));
660     return False;
661   }
662
663   /*
664    * Windows 2000 sends the owner and group SIDs as the logged in
665    * user, not the connected user. But it still sends the file
666    * owner SIDs on an ACL set. So we need to check for the file
667    * owner and group SIDs as well as the owner SIDs. JRA.
668    */
669  
670   create_file_sids(psbuf, &file_owner_sid, &file_grp_sid);
671
672   /*
673    * If no DACL then this is a chown only security descriptor.
674    */
675
676   if(!(security_info_sent & DACL_SECURITY_INFORMATION) || !dacl) {
677     *pmode = 0;
678     return True;
679   }
680
681   /*
682    * Now go through the DACL and ensure that
683    * any owner/group sids match.
684    */
685
686   for(i = 0; i < dacl->num_aces; i++) {
687     DOM_SID ace_sid;
688     SEC_ACE *psa = &dacl->ace[i];
689
690     if((psa->type != SEC_ACE_TYPE_ACCESS_ALLOWED) &&
691        (psa->type != SEC_ACE_TYPE_ACCESS_DENIED)) {
692       DEBUG(3,("unpack_posix_permissions: unable to set anything but an ALLOW or DENY ACE.\n"));
693       return False;
694     }
695
696     /*
697      * Ignore or remove bits we don't care about on a directory ACE.
698      */
699
700     if(fsp->is_directory) {
701       if(psa->flags & SEC_ACE_FLAG_INHERIT_ONLY) {
702         DEBUG(3,("unpack_posix_permissions: ignoring inherit only ACE.\n"));
703         continue;
704       }
705
706       /*
707        * At least one of the ACE entries wasn't inherit only.
708        * Flag this so we know the returned mode is valid.
709        */
710
711       all_aces_are_inherit_only = False;
712     }
713
714     /*
715      * Windows 2000 sets these flags even on *file* ACE's. This is wrong
716      * but we can ignore them for now. Revisit this when we go to POSIX
717      * ACLs on directories.
718      */
719
720     psa->flags &= ~(SEC_ACE_FLAG_OBJECT_INHERIT|SEC_ACE_FLAG_CONTAINER_INHERIT|SEC_ACE_FLAG_INHERITED_ACE);
721
722     if(psa->flags != 0) {
723       DEBUG(1,("unpack_posix_permissions: unable to set ACE flags (%x).\n", 
724             (unsigned int)psa->flags));
725       return False;
726     }
727
728     /*
729      * The security mask may be UNIX_ACCESS_NONE which should map into
730      * no permissions (we overload the WRITE_OWNER bit for this) or it
731      * should be one of the ALL/EXECUTE/READ/WRITE bits. Arrange for this
732      * to be so. Any other bits override the UNIX_ACCESS_NONE bit.
733      */
734
735     psa->info.mask &= (GENERIC_ALL_ACCESS|GENERIC_EXECUTE_ACCESS|GENERIC_WRITE_ACCESS|
736                      GENERIC_READ_ACCESS|UNIX_ACCESS_NONE|FILE_ALL_ATTRIBUTES);
737
738     if(psa->info.mask != UNIX_ACCESS_NONE)
739       psa->info.mask &= ~UNIX_ACCESS_NONE;
740
741     sid_copy(&ace_sid, &psa->sid);
742
743     if(sid_equal(&ace_sid, &file_owner_sid)) {
744       /*
745        * Map the desired permissions into owner perms.
746        */
747
748       if(psa->type == SEC_ACE_TYPE_ACCESS_ALLOWED)
749         *pmode |= map_nt_perms( psa->info, S_IRUSR);
750       else
751         *pmode &= ~(map_nt_perms( psa->info, S_IRUSR));
752
753       
754     } else if( sid_equal(&ace_sid, &file_grp_sid)) {
755       /*
756        * Map the desired permissions into group perms.
757        */
758
759       if(psa->type == SEC_ACE_TYPE_ACCESS_ALLOWED)
760         *pmode |= map_nt_perms( psa->info, S_IRGRP);
761       else
762         *pmode &= ~(map_nt_perms( psa->info, S_IRGRP));
763
764     } else if( sid_equal(&ace_sid, &global_sid_World)) {
765       /*
766        * Map the desired permissions into other perms.
767        */
768
769       if(psa->type == SEC_ACE_TYPE_ACCESS_ALLOWED)
770         *pmode |= map_nt_perms( psa->info, S_IROTH);
771       else
772         *pmode &= ~(map_nt_perms( psa->info, S_IROTH));
773
774     } else {
775       /*
776        * Only bother printing the level zero error if we didn't get any
777        * POSIX ACLS.
778        */
779       if (!posix_acls)
780         DEBUG(0,("unpack_posix_permissions: unknown SID used in ACL.\n"));
781       return False;
782     }
783   }
784
785   if (fsp->is_directory && all_aces_are_inherit_only) {
786     /*
787      * Windows 2000 is doing one of these weird 'inherit acl'
788      * traverses to conserve NTFS ACL resources. Just pretend
789      * there was no DACL sent. JRA.
790      */
791
792     DEBUG(10,("unpack_posix_permissions: Win2k inherit acl traverse. Ignoring DACL.\n"));
793   }
794
795   /*
796    * Check to see if we need to change anything.
797    * Enforce limits on modified bits *only*. Don't enforce masks
798    * on bits not changed by the user.
799    */
800
801   if(fsp->is_directory) {
802
803     *pmode &= (lp_dir_security_mask(SNUM(conn)) | psbuf->st_mode);
804     *pmode |= (lp_force_dir_security_mode(SNUM(conn)) & ( *pmode ^ psbuf->st_mode ));
805
806   } else {
807
808     *pmode &= (lp_security_mask(SNUM(conn)) | psbuf->st_mode); 
809     *pmode |= (lp_force_security_mode(SNUM(conn)) & ( *pmode ^ psbuf->st_mode ));
810
811   }
812
813   /*
814    * Preserve special bits.
815    */
816
817   *pmode |= (psbuf->st_mode & ~0777);
818
819   return True;
820 }
821
822 /****************************************************************************
823  Map POSIX ACL perms to canon_ace permissions (a mode_t containing only S_(R|W|X)USR bits).
824 ****************************************************************************/
825
826 static mode_t convert_permset_to_mode_t(SMB_ACL_PERMSET_T permset)
827 {
828         mode_t ret = 0;
829
830         ret |= (sys_acl_get_perm(permset, SMB_ACL_READ) ? S_IRUSR : 0);
831         ret |= (sys_acl_get_perm(permset, SMB_ACL_WRITE) ? S_IWUSR : 0);
832         ret |= (sys_acl_get_perm(permset, SMB_ACL_EXECUTE) ? S_IXUSR : 0);
833
834         return ret;
835 }
836
837 /****************************************************************************
838  Map generic UNIX permissions to canon_ace permissions (a mode_t containing only S_(R|W|X)USR bits).
839 ****************************************************************************/
840
841 static mode_t unix_perms_to_acl_perms(mode_t mode, int r_mask, int w_mask, int x_mask)
842 {
843         mode_t ret = 0;
844
845         if (mode & r_mask)
846                 ret |= S_IRUSR;
847         if (mode & w_mask)
848                 ret |= S_IWUSR;
849         if (mode & x_mask)
850                 ret |= S_IXUSR;
851
852         return ret;
853 }
854
855 /****************************************************************************
856  Map canon_ace permissions (a mode_t containing only S_(R|W|X)USR bits) to
857  an SMB_ACL_PERMSET_T.
858 ****************************************************************************/
859
860 static int map_acl_perms_to_permset(mode_t mode, SMB_ACL_PERMSET_T *p_permset)
861 {
862         if (sys_acl_clear_perms(*p_permset) ==  -1)
863                 return -1;
864         if (mode & S_IRUSR) {
865                 if (sys_acl_add_perm(*p_permset, SMB_ACL_READ) == -1)
866                         return -1;
867         }
868         if (mode & S_IWUSR) {
869                 if (sys_acl_add_perm(*p_permset, SMB_ACL_WRITE) == -1)
870                         return -1;
871         }
872         if (mode & S_IXUSR) {
873                 if (sys_acl_add_perm(*p_permset, SMB_ACL_EXECUTE) == -1)
874                         return -1;
875         }
876         return 0;
877 }
878
879 /****************************************************************************
880  Count a linked list of canonical ACE entries.
881 ****************************************************************************/
882
883 static size_t count_canon_ace_list( canon_ace *list_head )
884 {
885         size_t count = 0;
886         canon_ace *ace;
887
888         for (ace = list_head; ace; ace = ace->next)
889                 count++;
890
891         return count;
892 }
893
894 /****************************************************************************
895  Free a linked list of canonical ACE entries.
896 ****************************************************************************/
897
898 static void free_canon_ace_list( canon_ace *list_head )
899 {
900         while (list_head) {
901                 canon_ace *old_head = list_head;
902                 DLIST_REMOVE(list_head, list_head);
903                 free(old_head);
904         }
905 }
906
907 /******************************************************************************
908  Fall back to the generic 3 element UNIX permissions.
909 ********************************************************************************/
910
911 static canon_ace *unix_canonicalise_acl(files_struct *fsp, SMB_STRUCT_STAT *psbuf,
912                                                                                 DOM_SID *powner, DOM_SID *pgroup)
913 {
914         extern DOM_SID global_sid_World;
915         canon_ace *list_head = NULL;
916         canon_ace *owner_ace = NULL;
917         canon_ace *group_ace = NULL;
918         canon_ace *other_ace = NULL;
919
920         /*
921          * Create 3 linked list entries.
922          */
923
924         if ((owner_ace = (canon_ace *)malloc(sizeof(canon_ace))) == NULL)
925                 goto fail;
926
927         if ((group_ace = (canon_ace *)malloc(sizeof(canon_ace))) == NULL)
928                 goto fail;
929
930         if ((other_ace = (canon_ace *)malloc(sizeof(canon_ace))) == NULL)
931                 goto fail;
932
933         ZERO_STRUCTP(owner_ace);
934         ZERO_STRUCTP(group_ace);
935         ZERO_STRUCTP(other_ace);
936
937         owner_ace->type = SMB_ACL_USER_OBJ;
938         owner_ace->sid = *powner;
939         owner_ace->unix_ug.uid = psbuf->st_uid;
940         owner_ace->owner_type = UID_ACE;
941
942         group_ace->type = SMB_ACL_GROUP_OBJ;
943         group_ace->sid = *pgroup;
944         owner_ace->unix_ug.gid = psbuf->st_gid;
945         owner_ace->owner_type = GID_ACE;
946
947         other_ace->type = SMB_ACL_OTHER;
948         other_ace->sid = global_sid_World;
949         owner_ace->unix_ug.world = -1;
950         owner_ace->owner_type = WORLD_ACE;
951
952         if (!fsp->is_directory) {
953                 owner_ace->perms = unix_perms_to_acl_perms(psbuf->st_mode, S_IRUSR, S_IWUSR, S_IXUSR);
954                 group_ace->perms = unix_perms_to_acl_perms(psbuf->st_mode, S_IRGRP, S_IWGRP, S_IXGRP);
955                 other_ace->perms = unix_perms_to_acl_perms(psbuf->st_mode, S_IROTH, S_IWOTH, S_IXOTH);
956         } else {
957                 mode_t mode = unix_mode( fsp->conn, FILE_ATTRIBUTE_ARCHIVE, fsp->fsp_name);
958
959                 owner_ace->perms = unix_perms_to_acl_perms(mode, S_IRUSR, S_IWUSR, S_IXUSR);
960                 group_ace->perms = unix_perms_to_acl_perms(mode, S_IRGRP, S_IWGRP, S_IXGRP);
961                 other_ace->perms = unix_perms_to_acl_perms(mode, S_IROTH, S_IWOTH, S_IXOTH);
962         }
963
964         DLIST_ADD(list_head, other_ace);
965         DLIST_ADD(list_head, group_ace);
966         DLIST_ADD(list_head, owner_ace);
967
968         return list_head;
969
970   fail:
971
972         safe_free(owner_ace);
973         safe_free(group_ace);
974         safe_free(other_ace);
975
976         return NULL;
977 }
978
979 /****************************************************************************
980  Create a linked list of canonical ACE entries. This is sorted so that DENY
981  entries are at the front of the list, as NT requires.
982 ****************************************************************************/
983
984 static canon_ace *canonicalise_acl( SMB_ACL_T posix_acl, SMB_STRUCT_STAT *psbuf)
985 {
986         extern DOM_SID global_sid_World;
987         mode_t acl_mask = (S_IRUSR|S_IWUSR|S_IXUSR);
988         canon_ace *list_head = NULL;
989         canon_ace *ace = NULL;
990         canon_ace *next_ace = NULL;
991         int entry_id = SMB_ACL_FIRST_ENTRY;
992         SMB_ACL_ENTRY_T entry;
993
994         while ( sys_acl_get_entry(posix_acl, entry_id, &entry) == 1) {
995                 SMB_ACL_TAG_T tagtype;
996                 SMB_ACL_PERMSET_T permset;
997                 DOM_SID sid;
998                 posix_id unix_ug;
999                 enum ace_owner owner_type;
1000
1001                 /* get_next... */
1002                 if (entry_id == SMB_ACL_FIRST_ENTRY)
1003                         entry_id = SMB_ACL_NEXT_ENTRY;
1004
1005                 /* Is this a MASK entry ? */
1006                 if (sys_acl_get_tag_type(entry, &tagtype) == -1)
1007                         continue;
1008
1009                 if (sys_acl_get_permset(entry, &permset) == -1)
1010                         continue;
1011
1012                 /* Decide which SID to use based on the ACL type. */
1013                 switch(tagtype) {
1014                         case SMB_ACL_USER_OBJ:
1015                                 /* Get the SID from the owner. */
1016                                 uid_to_sid( &sid, psbuf->st_uid );
1017                                 unix_ug.uid = psbuf->st_uid;
1018                                 owner_type = UID_ACE;
1019                                 break;
1020                         case SMB_ACL_USER:
1021                                 {
1022                                         uid_t *puid = (uid_t *)sys_acl_get_qualifier(entry);
1023                                         if (puid == NULL) {
1024                                                 DEBUG(0,("canonicalise_acl: Failed to get uid.\n"));
1025                                                 continue;
1026                                         }
1027                                         uid_to_sid( &sid, *puid);
1028                                         unix_ug.uid = *puid;
1029                                         owner_type = UID_ACE;
1030                                         break;
1031                                 }
1032                         case SMB_ACL_GROUP_OBJ:
1033                                 /* Get the SID from the owning group. */
1034                                 gid_to_sid( &sid, psbuf->st_gid );
1035                                 unix_ug.gid = psbuf->st_gid;
1036                                 owner_type = GID_ACE;
1037                                 break;
1038                         case SMB_ACL_GROUP:
1039                                 {
1040                                         gid_t *pgid = (gid_t *)sys_acl_get_qualifier(entry);
1041                                         if (pgid == NULL) {
1042                                                 DEBUG(0,("canonicalise_acl: Failed to get gid.\n"));
1043                                                 continue;
1044                                         }
1045                                         gid_to_sid( &sid, *pgid);
1046                                         unix_ug.gid = *pgid;
1047                                         owner_type = GID_ACE;
1048                                         break;
1049                                 }
1050                         case SMB_ACL_MASK:
1051                                 acl_mask = convert_permset_to_mode_t(permset);
1052                                 continue; /* Don't count the mask as an entry. */
1053                         case SMB_ACL_OTHER:
1054                                 /* Use the Everyone SID */
1055                                 sid = global_sid_World;
1056                                 unix_ug.world = -1;
1057                                 owner_type = WORLD_ACE;
1058                                 break;
1059                         default:
1060                                 DEBUG(0,("canonicalise_acl: Unknown tagtype %u\n", (unsigned int)tagtype));
1061                                 continue;
1062                 }
1063
1064                 /*
1065                  * Add this entry to the list.
1066                  */
1067
1068                 if ((ace = (canon_ace *)malloc(sizeof(canon_ace))) == NULL)
1069                         goto fail;
1070
1071                 ZERO_STRUCTP(ace);
1072                 ace->type = tagtype;
1073                 ace->perms = convert_permset_to_mode_t(permset);
1074                 ace->sid = sid;
1075                 ace->unix_ug = unix_ug;
1076                 ace->owner_type = owner_type;
1077
1078                 DLIST_ADD(list_head, ace);
1079         }
1080
1081         /*
1082          * Now go through the list, masking the permissions with the
1083          * acl_mask. If the permissions are 0 it should be listed
1084          * first.
1085          */
1086
1087         for ( ace = list_head; ace; ace = next_ace) {
1088                 next_ace = ace->next;
1089
1090                 /* Masks are only applied to entries other than USER_OBJ and OTHER. */
1091                 if (ace->type != SMB_ACL_OTHER && ace->type != SMB_ACL_USER_OBJ)
1092                         ace->perms &= acl_mask;
1093
1094                 if (ace->perms == 0)
1095                         DLIST_PROMOTE(list_head, ace);
1096         }
1097
1098         if( DEBUGLVL( 10 ) ) {
1099                 char *acl_text = sys_acl_to_text( posix_acl, NULL);
1100
1101                 dbgtext("canonicalize_acl: processed acl %s\n", acl_text == NULL ? "NULL" : acl_text );
1102                 if (acl_text)
1103                         sys_acl_free_text(acl_text);
1104         }
1105
1106         return list_head;
1107
1108   fail:
1109
1110         free_canon_ace_list(list_head);
1111         return NULL;
1112 }
1113
1114 /****************************************************************************
1115  Attempt to apply an ACL to a file or directory.
1116 ****************************************************************************/
1117
1118 static BOOL set_canon_ace_list(files_struct *fsp, canon_ace *the_ace, BOOL default_ace, BOOL *pacl_set_support)
1119 {
1120         BOOL ret = False;
1121         SMB_ACL_T the_acl = sys_acl_init((int)count_canon_ace_list(the_ace) + 1);
1122         canon_ace *p_ace;
1123         int i;
1124         SMB_ACL_ENTRY_T mask_entry;
1125         SMB_ACL_PERMSET_T mask_permset;
1126         SMB_ACL_TYPE_T the_acl_type = (default_ace ? SMB_ACL_TYPE_DEFAULT : SMB_ACL_TYPE_ACCESS);
1127
1128         if (the_acl == NULL) {
1129 #if !defined(HAVE_NO_ACLS)
1130                 /*
1131                  * Only print this error message if we have some kind of ACL
1132                  * support that's not working. Otherwise we would always get this.
1133                  */
1134                 DEBUG(0,("set_canon_ace_list: Unable to init %s ACL. (%s)\n",
1135                         default_ace ? "default" : "file", strerror(errno) ));
1136 #endif
1137                 *pacl_set_support = False;
1138                 return False;
1139         }
1140
1141         for (i = 0, p_ace = the_ace; p_ace; p_ace = p_ace->next, i++ ) {
1142                 SMB_ACL_ENTRY_T the_entry;
1143                 SMB_ACL_PERMSET_T the_permset;
1144
1145                 /*
1146                  * Get the entry for this ACE.
1147                  */
1148
1149                 if (sys_acl_create_entry( &the_acl, &the_entry) == -1) {
1150                         DEBUG(0,("set_canon_ace_list: Failed to create entry %d. (%s)\n",
1151                                 i, strerror(errno) ));
1152                         goto done;
1153                 }
1154
1155                 /*
1156                  * Ok - we now know the ACL calls should be working, don't
1157                  * allow fallback to chmod.
1158                  */
1159
1160                 *pacl_set_support = True;
1161
1162                 /*
1163                  * Initialise the entry from the canon_ace.
1164                  */
1165
1166                 /*
1167                  * First tell the entry what type of ACE this is.
1168                  */
1169
1170                 if (sys_acl_set_tag_type(the_entry, p_ace->type) == -1) {
1171                         DEBUG(0,("set_canon_ace_list: Failed to set tag type on entry %d. (%s)\n",
1172                                 i, strerror(errno) ));
1173                         goto done;
1174                 }
1175
1176                 /*
1177                  * Only set the qualifier (user or group id) if the entry is a user
1178                  * or group id ACE.
1179                  */
1180
1181                 if ((p_ace->type == SMB_ACL_USER) || (p_ace->type == SMB_ACL_GROUP)) {
1182                         if (sys_acl_set_qualifier(the_entry,(void *)&p_ace->unix_ug.uid) == -1) {
1183                                 DEBUG(0,("set_canon_ace_list: Failed to set qualifier on entry %d. (%s)\n",
1184                                         i, strerror(errno) ));
1185                                 goto done;
1186                         }
1187                 }
1188
1189                 /*
1190                  * Convert the mode_t perms in the canon_ace to a POSIX permset.
1191                  */
1192
1193                 if (sys_acl_get_permset(the_entry, &the_permset) == -1) {
1194                         DEBUG(0,("set_canon_ace_list: Failed to get permset on entry %d. (%s)\n",
1195                                 i, strerror(errno) ));
1196                         goto done;
1197                 }
1198
1199                 if (map_acl_perms_to_permset(p_ace->perms, &the_permset) == -1) {
1200                         DEBUG(0,("set_canon_ace_list: Failed to create permset for mode (%u) on entry %d. (%s)\n",
1201                                 p_ace->perms, i, strerror(errno) ));
1202                         goto done;
1203                 }
1204
1205                 /*
1206                  * ..and apply them to the entry.
1207                  */
1208
1209                 if (sys_acl_set_permset(the_entry, the_permset) == -1) {
1210                         DEBUG(0,("set_canon_ace_list: Failed to add permset on entry %d. (%s)\n",
1211                                 i, strerror(errno) ));
1212                         goto done;
1213                 }
1214
1215                 if( DEBUGLVL( 10 ))
1216                         print_canon_ace( p_ace, i);
1217         }
1218
1219         /*
1220          * Add in a mask of rwx.
1221          */
1222
1223         if (sys_acl_create_entry( &the_acl, &mask_entry) == -1) {
1224                 DEBUG(0,("set_canon_ace_list: Failed to create mask entry. (%s)\n", strerror(errno) ));
1225                 goto done;
1226         }
1227
1228         if (sys_acl_set_tag_type(mask_entry, SMB_ACL_MASK) == -1) {
1229                 DEBUG(0,("set_canon_ace_list: Failed to set tag type on mask entry. (%s)\n",strerror(errno) ));
1230                 goto done;
1231         }
1232
1233         if (sys_acl_get_permset(mask_entry, &mask_permset) == -1) {
1234                 DEBUG(0,("set_canon_ace_list: Failed to get mask permset. (%s)\n", strerror(errno) ));
1235                 goto done;
1236         }
1237
1238         if (map_acl_perms_to_permset(S_IRUSR|S_IWUSR|S_IXUSR, &mask_permset) == -1) {
1239                 DEBUG(0,("set_canon_ace_list: Failed to create mask permset. (%s)\n", strerror(errno) ));
1240                 goto done;
1241         }
1242
1243         if (sys_acl_set_permset(mask_entry, mask_permset) == -1) {
1244                 DEBUG(0,("set_canon_ace_list: Failed to add mask permset. (%s)\n", strerror(errno) ));
1245                 goto done;
1246         }
1247
1248         /*
1249          * Check if the ACL is valid.
1250          */
1251
1252         if (sys_acl_valid(the_acl) == -1) {
1253                 DEBUG(0,("set_canon_ace_list: ACL type (%s) is invalid for set (%s).\n",
1254                                 the_acl_type == SMB_ACL_TYPE_DEFAULT ? "directory default" : "file",
1255                                 strerror(errno) ));
1256                 goto done;
1257         }
1258
1259         /*
1260          * Finally apply it to the file or directory.
1261          */
1262
1263         if(default_ace || fsp->is_directory || fsp->fd == -1) {
1264                 if (sys_acl_set_file(dos_to_unix(fsp->fsp_name,False), the_acl_type, the_acl) == -1) {
1265                         DEBUG(0,("set_canon_ace_list: sys_acl_set_file type %s failed for file %s (%s).\n",
1266                                         the_acl_type == SMB_ACL_TYPE_DEFAULT ? "directory default" : "file",
1267                                         fsp->fsp_name, strerror(errno) ));
1268                         goto done;
1269                 }
1270         } else {
1271                 if (sys_acl_set_fd(fsp->fd, the_acl) == -1) {
1272                         DEBUG(0,("set_canon_ace_list: sys_acl_set_file failed for file %s (%s).\n",
1273                                         fsp->fsp_name, strerror(errno) ));
1274                         goto done;
1275                 }
1276         }
1277
1278         ret = True;
1279
1280   done:
1281
1282         if (the_acl != NULL)
1283             sys_acl_free_acl(the_acl);
1284
1285         return ret;
1286 }
1287
1288 /****************************************************************************
1289  Reply to query a security descriptor from an fsp. If it succeeds it allocates
1290  the space for the return elements and returns the size needed to return the
1291  security descriptor. This should be the only external function needed for
1292  the UNIX style get ACL.
1293 ****************************************************************************/
1294
1295 size_t get_nt_acl(files_struct *fsp, SEC_DESC **ppdesc)
1296 {
1297         SMB_STRUCT_STAT sbuf;
1298         SEC_ACE *nt_ace_list;
1299         DOM_SID owner_sid;
1300         DOM_SID group_sid;
1301         size_t sd_size = 0;
1302         SEC_ACL *psa = NULL;
1303         size_t num_acls = 0;
1304         size_t num_dir_acls = 0;
1305         size_t num_aces = 0;
1306         SMB_ACL_T posix_acl = NULL;
1307         SMB_ACL_T dir_acl = NULL;
1308         canon_ace *file_ace = NULL;
1309         canon_ace *dir_ace = NULL;
1310  
1311         *ppdesc = NULL;
1312
1313         DEBUG(10,("get_nt_acl: called for file %s\n", fsp->fsp_name ));
1314
1315         if(fsp->is_directory || fsp->fd == -1) {
1316
1317                 /* Get the stat struct for the owner info. */
1318                 if(vfs_stat(fsp->conn,fsp->fsp_name, &sbuf) != 0) {
1319                         return 0;
1320                 }
1321                 /*
1322                  * Get the ACL from the path.
1323                  */
1324
1325                 posix_acl = sys_acl_get_file( dos_to_unix(fsp->fsp_name, False), SMB_ACL_TYPE_ACCESS);
1326
1327                 /*
1328                  * If it's a directory get the default POSIX ACL.
1329                  */
1330
1331                 if(fsp->is_directory)
1332                         dir_acl = sys_acl_get_file( dos_to_unix(fsp->fsp_name, False), SMB_ACL_TYPE_DEFAULT);
1333
1334         } else {
1335
1336                 /* Get the stat struct for the owner info. */
1337                 if(vfs_fstat(fsp,fsp->fd,&sbuf) != 0) {
1338                         return 0;
1339                 }
1340                 /*
1341                  * Get the ACL from the fd.
1342                  */
1343                 posix_acl = sys_acl_get_fd(fsp->fd);
1344         }
1345
1346         DEBUG(5,("get_nt_acl : file ACL %s, directory ACL %s\n",
1347                         posix_acl ? "present" :  "absent",
1348                         dir_acl ? "present" :  "absent" ));
1349
1350         /*
1351          * Get the owner, group and world SIDs.
1352          */
1353
1354         create_file_sids(&sbuf, &owner_sid, &group_sid);
1355
1356         /* Create the canon_ace lists. */
1357         if (posix_acl)
1358                 file_ace = canonicalise_acl( posix_acl, &sbuf);
1359         else
1360                 file_ace = unix_canonicalise_acl(fsp, &sbuf, &owner_sid, &group_sid);
1361
1362         num_acls = count_canon_ace_list(file_ace);
1363
1364         if (fsp->is_directory) { 
1365                 if (dir_acl)
1366                         dir_ace = canonicalise_acl( dir_acl, &sbuf);
1367                 else
1368                         dir_ace = unix_canonicalise_acl(fsp, &sbuf, &owner_sid, &group_sid);
1369
1370                 num_dir_acls = count_canon_ace_list(dir_ace);
1371         }
1372
1373         /* Allocate the ace list. */
1374         if ((nt_ace_list = (SEC_ACE *)malloc((num_acls + num_dir_acls)* sizeof(SEC_ACE))) == NULL) {
1375                 DEBUG(0,("get_nt_acl: Unable to malloc space for nt_ace_list.\n"));
1376                 goto done;
1377         }
1378
1379         memset(nt_ace_list, '\0', (num_acls + num_dir_acls) * sizeof(SEC_ACE) );
1380
1381         /*
1382          * Create the NT ACE list from the canonical ace lists.
1383          */
1384
1385         {
1386                 canon_ace *ace;
1387                 int nt_acl_type;
1388                 int i;
1389
1390                 ace = file_ace;
1391
1392                 for (i = 0; i < num_acls; i++, ace = ace->next) {
1393                         SEC_ACCESS acc = map_canon_ace_perms(&nt_acl_type, &owner_sid, ace );
1394                         init_sec_ace(&nt_ace_list[num_aces++], &ace->sid, nt_acl_type, acc, 0);
1395                 }
1396
1397                 ace = dir_ace;
1398
1399                 for (i = 0; i < num_dir_acls; i++, ace = ace->next) {
1400                         SEC_ACCESS acc = map_canon_ace_perms(&nt_acl_type, &owner_sid, ace );
1401                         init_sec_ace(&nt_ace_list[num_aces++], &ace->sid, nt_acl_type, acc, 
1402                                         SEC_ACE_FLAG_OBJECT_INHERIT|SEC_ACE_FLAG_CONTAINER_INHERIT|SEC_ACE_FLAG_INHERIT_ONLY);
1403                 }
1404         }
1405
1406         if (num_acls) {
1407                 if((psa = make_sec_acl( main_loop_talloc_get(), ACL_REVISION, num_aces, nt_ace_list)) == NULL) {
1408                         DEBUG(0,("get_nt_acl: Unable to malloc space for acl.\n"));
1409                         goto done;
1410                 }
1411         }
1412
1413         *ppdesc = make_standard_sec_desc( main_loop_talloc_get(), &owner_sid, &group_sid, psa, &sd_size);
1414
1415         if(!*ppdesc) {
1416                 DEBUG(0,("get_nt_acl: Unable to malloc space for security descriptor.\n"));
1417                 sd_size = 0;
1418         }
1419
1420   done:
1421
1422         if (posix_acl)  
1423                 sys_acl_free_acl(posix_acl);
1424         if (dir_acl)
1425                 sys_acl_free_acl(dir_acl);
1426         free_canon_ace_list(file_ace);
1427         free_canon_ace_list(dir_ace);
1428         if (nt_ace_list)
1429                 free(nt_ace_list);
1430
1431         return sd_size;
1432 }
1433
1434 /****************************************************************************
1435  Reply to set a security descriptor on an fsp. security_info_sent is the
1436  description of the following NT ACL.
1437  This should be the only external function needed for the UNIX style set ACL.
1438 ****************************************************************************/
1439
1440 BOOL set_nt_acl(files_struct *fsp, uint32 security_info_sent, SEC_DESC *psd)
1441 {
1442         connection_struct *conn = fsp->conn;
1443         uid_t user = (uid_t)-1;
1444         gid_t grp = (gid_t)-1;
1445         mode_t perms = 0;
1446         SMB_STRUCT_STAT sbuf;  
1447         DOM_SID file_owner_sid;
1448         DOM_SID file_grp_sid;
1449         canon_ace *file_ace_list = NULL;
1450         canon_ace *dir_ace_list = NULL;
1451         BOOL posix_perms;
1452         BOOL acl_perms;
1453
1454         DEBUG(10,("set_nt_acl: called for file %s\n", fsp->fsp_name ));
1455
1456         /*
1457          * Get the current state of the file.
1458          */
1459
1460         if(fsp->is_directory || fsp->fd == -1) {
1461                 if(vfs_stat(fsp->conn,fsp->fsp_name, &sbuf) != 0)
1462                         return False;
1463         } else {
1464                 if(vfs_fstat(fsp,fsp->fd,&sbuf) != 0)
1465                         return False;
1466         }
1467
1468         /*
1469          * Unpack the user/group/world id's.
1470          */
1471
1472         if (!unpack_nt_owners( &sbuf, &user, &grp, security_info_sent, psd))
1473                 return False;
1474
1475         /*
1476          * Do we need to chown ?
1477          */
1478
1479         if((user != (uid_t)-1 || grp != (uid_t)-1) && (sbuf.st_uid != user || sbuf.st_gid != grp)) {
1480
1481                 DEBUG(3,("set_nt_acl: chown %s. uid = %u, gid = %u.\n",
1482                                 fsp->fsp_name, (unsigned int)user, (unsigned int)grp ));
1483
1484                 if(vfs_chown( fsp->conn, fsp->fsp_name, user, grp) == -1) {
1485                         DEBUG(3,("set_nt_acl: chown %s, %u, %u failed. Error = %s.\n",
1486                                 fsp->fsp_name, (unsigned int)user, (unsigned int)grp, strerror(errno) ));
1487                         return False;
1488                 }
1489
1490                 /*
1491                  * Recheck the current state of the file, which may have changed.
1492                  * (suid/sgid bits, for instance)
1493                  */
1494
1495                 if(fsp->is_directory) {
1496                         if(vfs_stat(fsp->conn, fsp->fsp_name, &sbuf) != 0) {
1497                                 return False;
1498                         }
1499                 } else {
1500
1501                         int ret;
1502     
1503                         if(fsp->fd == -1)
1504                                 ret = vfs_stat(fsp->conn, fsp->fsp_name, &sbuf);
1505                         else
1506                                 ret = vfs_fstat(fsp,fsp->fd,&sbuf);
1507   
1508                         if(ret != 0)
1509                                 return False;
1510                 }
1511         }
1512
1513         create_file_sids(&sbuf, &file_owner_sid, &file_grp_sid);
1514
1515         acl_perms = unpack_canon_ace( fsp, &sbuf, &file_owner_sid, &file_grp_sid,
1516                                                                         &file_ace_list, &dir_ace_list, security_info_sent, psd);
1517         posix_perms = unpack_posix_permissions( fsp, &sbuf, &perms, security_info_sent, psd, acl_perms);
1518
1519         if (!posix_perms && !acl_perms) {
1520                 /*
1521                  * Neither method of setting permissions can work. Fail here.
1522                  */
1523
1524                 DEBUG(3,("set_nt_acl: cannot set normal POSIX permissions or POSIX ACL permissions\n"));
1525                 free_canon_ace_list(file_ace_list);
1526                 free_canon_ace_list(dir_ace_list); 
1527                 return False;
1528         }
1529
1530         /*
1531          * Only change security if we got a DACL.
1532          */
1533
1534         if((security_info_sent & DACL_SECURITY_INFORMATION) && (psd->dacl != NULL)) {
1535
1536                 BOOL acl_set_support = False;
1537                 BOOL ret = False;
1538
1539                 /*
1540                  * Try using the POSIX ACL set first. All back to chmod if
1541                  * we have no ACL support on this filesystem.
1542                  */
1543
1544                 if (acl_perms && file_ace_list) {
1545                         ret = set_canon_ace_list(fsp, file_ace_list, False, &acl_set_support);
1546                         if (acl_set_support && ret == False) {
1547                                 DEBUG(3,("set_nt_acl: failed to set file acl on file %s (%s).\n", fsp->fsp_name, strerror(errno) ));
1548                                 free_canon_ace_list(file_ace_list);
1549                                 free_canon_ace_list(dir_ace_list); 
1550                                 return False;
1551                         }
1552                 }
1553
1554                 if (acl_perms && acl_set_support && fsp->is_directory && dir_ace_list) {
1555                         if (!set_canon_ace_list(fsp, dir_ace_list, True, &acl_set_support)) {
1556                                 DEBUG(3,("set_nt_acl: failed to set default acl on directory %s (%s).\n", fsp->fsp_name, strerror(errno) ));
1557                                 free_canon_ace_list(file_ace_list);
1558                                 free_canon_ace_list(dir_ace_list); 
1559                                 return False;
1560                         }
1561                 }
1562
1563                 /*
1564                  * If we cannot set using POSIX ACLs we fall back to checking if we need to chmod.
1565                  */
1566
1567                 if(!acl_set_support && posix_perms && (sbuf.st_mode != perms)) {
1568
1569                         free_canon_ace_list(file_ace_list);
1570                         free_canon_ace_list(dir_ace_list); 
1571                         file_ace_list = NULL;
1572                         dir_ace_list = NULL;
1573
1574                         DEBUG(3,("set_nt_acl: chmod %s. perms = 0%o.\n",
1575                                 fsp->fsp_name, (unsigned int)perms ));
1576
1577                         if(conn->vfs_ops.chmod(conn,dos_to_unix(fsp->fsp_name, False), perms) == -1) {
1578                                 DEBUG(3,("set_nt_acl: chmod %s, 0%o failed. Error = %s.\n",
1579                                                 fsp->fsp_name, (unsigned int)perms, strerror(errno) ));
1580                                 return False;
1581                         }
1582                 }
1583         }
1584
1585         free_canon_ace_list(file_ace_list);
1586         free_canon_ace_list(dir_ace_list); 
1587
1588         return True;
1589 }
1590
1591 /****************************************************************************
1592  Do a chmod by setting the ACL USER_OBJ, GROUP_OBJ and OTHER bits in an ACL
1593  and set the mask to rwx. Needed to preserve complex ACLs set by NT.
1594 ****************************************************************************/
1595
1596 static int chmod_acl_internals( SMB_ACL_T posix_acl, mode_t mode)
1597 {
1598         int entry_id = SMB_ACL_FIRST_ENTRY;
1599         SMB_ACL_ENTRY_T entry;
1600         int num_entries = 0;
1601
1602         while ( sys_acl_get_entry(posix_acl, entry_id, &entry) == 1) {
1603                 SMB_ACL_TAG_T tagtype;
1604                 SMB_ACL_PERMSET_T permset;
1605                 mode_t perms;
1606
1607                 /* get_next... */
1608                 if (entry_id == SMB_ACL_FIRST_ENTRY)
1609                         entry_id = SMB_ACL_NEXT_ENTRY;
1610
1611                 if (sys_acl_get_tag_type(entry, &tagtype) == -1)
1612                         return -1;
1613
1614                 if (sys_acl_get_permset(entry, &permset) == -1)
1615                         return -1;
1616
1617                 num_entries++;
1618
1619                 switch(tagtype) {
1620                         case SMB_ACL_USER_OBJ:
1621                                 perms = unix_perms_to_acl_perms(mode, S_IRUSR, S_IWUSR, S_IXUSR);
1622                 break;
1623                         case SMB_ACL_GROUP_OBJ:
1624                                 perms = unix_perms_to_acl_perms(mode, S_IRGRP, S_IWGRP, S_IXGRP);
1625                                 break;
1626                         case SMB_ACL_MASK:
1627                                 perms = S_IRUSR|S_IWUSR|S_IXUSR;
1628                                 break;
1629                         case SMB_ACL_OTHER:
1630                                 perms = unix_perms_to_acl_perms(mode, S_IROTH, S_IWOTH, S_IXOTH);
1631                                 break;
1632                         default:
1633                                 continue;
1634                 }
1635
1636                 if (map_acl_perms_to_permset(perms, &permset) == -1)
1637                         return -1;
1638
1639                 if (sys_acl_set_permset(entry, permset) == -1)
1640                         return -1;
1641         }
1642
1643         /*
1644          * If this is a simple 3 element ACL then it's a standard
1645          * UNIX permission set. Just use chmod...       
1646          */
1647
1648         if (num_entries == 3)
1649                 return -1;
1650
1651         return 0;
1652 }
1653
1654 /****************************************************************************
1655  Do a chmod by setting the ACL USER_OBJ, GROUP_OBJ and OTHER bits in an ACL
1656  and set the mask to rwx. Needed to preserve complex ACLs set by NT.
1657  Note that name is in UNIX character set.
1658 ****************************************************************************/
1659
1660 int chmod_acl(char *name, mode_t mode)
1661 {
1662         SMB_ACL_T posix_acl = NULL;
1663         int ret = -1;
1664
1665         if ((posix_acl = sys_acl_get_file(name, SMB_ACL_TYPE_ACCESS)) == NULL)
1666                 return -1;
1667
1668         if ((ret = chmod_acl_internals(posix_acl, mode)) == -1)
1669                 goto done;
1670
1671         ret = sys_acl_set_file(name, SMB_ACL_TYPE_ACCESS, posix_acl);
1672
1673   done:
1674
1675         sys_acl_free_acl(posix_acl);
1676         return ret;
1677 }
1678
1679 /****************************************************************************
1680  Do an fchmod by setting the ACL USER_OBJ, GROUP_OBJ and OTHER bits in an ACL
1681  and set the mask to rwx. Needed to preserve complex ACLs set by NT.
1682 ****************************************************************************/
1683
1684 int fchmod_acl(int fd, mode_t mode)
1685 {
1686         SMB_ACL_T posix_acl = NULL;
1687         int ret = -1;
1688
1689         if ((posix_acl = sys_acl_get_fd(fd)) == NULL)
1690                 return -1;
1691
1692         if ((ret = chmod_acl_internals(posix_acl, mode)) == -1)
1693                 goto done;
1694
1695         ret = sys_acl_set_fd(fd, posix_acl);
1696
1697   done:
1698
1699         sys_acl_free_acl(posix_acl);
1700         return ret;
1701 }