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