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