Make smbd/posix_acls.c use abstract interface.
[tprouty/samba.git] / source / smbd / posix_acls.c
1 #ifdef HAVE_POSIX_ACLS
2 #define OLD_NTDOMAIN 1
3 /*
4    Unix SMB/Netbios implementation.
5    Version 1.9.
6    SMB NT Security Descriptor / Unix permission conversion.
7    Copyright (C) Jeremy Allison 1994-2000
8
9    This program is free software; you can redistribute it and/or modify
10    it under the terms of the GNU General Public License as published by
11    the Free Software Foundation; either version 2 of the License, or
12    (at your option) any later version.
13
14    This program is distributed in the hope that it will be useful,
15    but WITHOUT ANY WARRANTY; without even the implied warranty of
16    MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
17    GNU General Public License for more details.
18
19    You should have received a copy of the GNU General Public License
20    along with this program; if not, write to the Free Software
21    Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA.
22 */
23
24 #include "includes.h"
25
26 typedef struct canon_ace {
27         struct canon_ace *next, *prev;
28         SMB_ACL_TAG_T type;
29         SMB_ACL_PERMSET_T perms;
30         DOM_SID sid;
31 } canon_ace;
32
33 /****************************************************************************
34  Function to create owner and group SIDs from a SMB_STRUCT_STAT.
35 ****************************************************************************/
36
37 static void create_file_sids(SMB_STRUCT_STAT *psbuf, DOM_SID *powner_sid, DOM_SID *pgroup_sid)
38 {
39         uid_to_sid( powner_sid, psbuf->st_uid );
40         gid_to_sid( pgroup_sid, psbuf->st_gid );
41 }
42
43 /****************************************************************************
44  Map canon_ace perms to NT.
45 ****************************************************************************/
46
47 static SEC_ACCESS map_canon_ace_perms(int *pacl_type, DOM_SID *powner_sid, canon_ace *ace)
48 {
49         SEC_ACCESS sa;
50         uint32 nt_mask = 0;
51
52         *pacl_type = SEC_ACE_TYPE_ACCESS_ALLOWED;
53
54         if((ace->perms & (SMB_ACL_READ|SMB_ACL_WRITE|SMB_ACL_EXECUTE)) == (SMB_ACL_READ|SMB_ACL_WRITE|SMB_ACL_EXECUTE)) {
55                 nt_mask = UNIX_ACCESS_RWX;
56         } else if((ace->perms & (SMB_ACL_READ|SMB_ACL_WRITE|SMB_ACL_EXECUTE)) == 0) {
57                 /*
58                  * Here we differentiate between the owner and any other user.
59                  */
60                 if (sid_equal(powner_sid, &ace->sid)
61                         nt_mask = UNIX_ACCESS_NONE;
62                 } else {
63                         /* Not owner, this is an access denied ACE. */
64                         nt_mask = UNIX_ACCESS_RWX;
65                         *pacl_type = SEC_ACE_TYPE_ACCESS_DENIED;
66                 }
67         } else {
68                 nt_mask |= (perm & SMB_ACL_READ) ? UNIX_ACCESS_R : 0;
69                 nt_mask |= (perm & SMB_ACL_WRITE) ? UNIX_ACCESS_W : 0;
70                 nt_mask |= (perm & SMB_ACL_EXECUTE) ? UNIX_ACCESS_X : 0;
71         }
72         init_sec_access(&sa,nt_mask);
73         return sa;
74 }
75
76 /****************************************************************************
77  Map NT perms to UNIX.
78 ****************************************************************************/
79
80 #define FILE_SPECIFIC_READ_BITS (FILE_READ_DATA|FILE_READ_EA|FILE_READ_ATTRIBUTES)
81 #define FILE_SPECIFIC_WRITE_BITS (FILE_WRITE_DATA|FILE_APPEND_DATA|FILE_WRITE_EA|FILE_WRITE_ATTRIBUTES)
82 #define FILE_SPECIFIC_EXECUTE_BITS (FILE_EXECUTE)
83
84 static mode_t map_nt_perms( SEC_ACCESS sec_access, int type)
85 {
86   mode_t mode = 0;
87
88   switch(type) {
89   case S_IRUSR:
90     if(sec_access.mask & GENERIC_ALL_ACCESS)
91       mode = S_IRUSR|S_IWUSR|S_IXUSR;
92     else {
93       mode |= (sec_access.mask & (GENERIC_READ_ACCESS|FILE_SPECIFIC_READ_BITS)) ? S_IRUSR : 0;
94       mode |= (sec_access.mask & (GENERIC_WRITE_ACCESS|FILE_SPECIFIC_WRITE_BITS)) ? S_IWUSR : 0;
95       mode |= (sec_access.mask & (GENERIC_EXECUTE_ACCESS|FILE_SPECIFIC_EXECUTE_BITS)) ? S_IXUSR : 0;
96     }
97     break;
98   case S_IRGRP:
99     if(sec_access.mask & GENERIC_ALL_ACCESS)
100       mode = S_IRGRP|S_IWGRP|S_IXGRP;
101     else {
102       mode |= (sec_access.mask & (GENERIC_READ_ACCESS|FILE_SPECIFIC_READ_BITS)) ? S_IRGRP : 0;
103       mode |= (sec_access.mask & (GENERIC_WRITE_ACCESS|FILE_SPECIFIC_WRITE_BITS)) ? S_IWGRP : 0;
104       mode |= (sec_access.mask & (GENERIC_EXECUTE_ACCESS|FILE_SPECIFIC_EXECUTE_BITS)) ? S_IXGRP : 0;
105     }
106     break;
107   case S_IROTH:
108     if(sec_access.mask & GENERIC_ALL_ACCESS)
109       mode = S_IROTH|S_IWOTH|S_IXOTH;
110     else {
111       mode |= (sec_access.mask & (GENERIC_READ_ACCESS|FILE_SPECIFIC_READ_BITS)) ? S_IROTH : 0;
112       mode |= (sec_access.mask & (GENERIC_WRITE_ACCESS|FILE_SPECIFIC_WRITE_BITS)) ? S_IWOTH : 0;
113       mode |= (sec_access.mask & (GENERIC_EXECUTE_ACCESS|FILE_SPECIFIC_EXECUTE_BITS)) ? S_IXOTH : 0;
114     }
115     break;
116   }
117
118   return mode;
119 }
120
121 /****************************************************************************
122  Unpack a SEC_DESC into a owner, group and set of UNIX permissions.
123 ****************************************************************************/
124
125 static BOOL unpack_nt_permissions(SMB_STRUCT_STAT *psbuf, uid_t *puser, gid_t *pgrp, mode_t *pmode,
126                                   uint32 security_info_sent, SEC_DESC *psd, BOOL is_directory)
127 {
128   extern DOM_SID global_sid_World;
129   DOM_SID owner_sid;
130   DOM_SID grp_sid;
131   DOM_SID file_owner_sid;
132   DOM_SID file_grp_sid;
133   uint32 owner_rid;
134   uint32 grp_rid;
135   SEC_ACL *dacl = psd->dacl;
136   BOOL all_aces_are_inherit_only = (is_directory ? True : False);
137   int i;
138   enum SID_NAME_USE sid_type;
139
140   *pmode = 0;
141   *puser = (uid_t)-1;
142   *pgrp = (gid_t)-1;
143
144   if(security_info_sent == 0) {
145     DEBUG(0,("unpack_nt_permissions: no security info sent !\n"));
146     return False;
147   }
148
149   /*
150    * Windows 2000 sends the owner and group SIDs as the logged in
151    * user, not the connected user. But it still sends the file
152    * owner SIDs on an ACL set. So we need to check for the file
153    * owner and group SIDs as well as the owner SIDs. JRA.
154    */
155  
156   create_file_sids(psbuf, &file_owner_sid, &file_grp_sid);
157
158   /*
159    * Validate the owner and group SID's.
160    */
161
162   memset(&owner_sid, '\0', sizeof(owner_sid));
163   memset(&grp_sid, '\0', sizeof(grp_sid));
164
165   DEBUG(5,("unpack_nt_permissions: validating owner_sid.\n"));
166
167   /*
168    * Don't immediately fail if the owner sid cannot be validated.
169    * This may be a group chown only set.
170    */
171
172   if (security_info_sent & OWNER_SECURITY_INFORMATION) {
173     if (!sid_to_uid( &owner_sid, puser, &sid_type))
174       DEBUG(3,("unpack_nt_permissions: unable to validate owner sid.\n"));
175   }
176
177   /*
178    * Don't immediately fail if the group sid cannot be validated.
179    * This may be an owner chown only set.
180    */
181
182   if (security_info_sent & GROUP_SECURITY_INFORMATION) {
183     if (!sid_to_gid( &grp_sid, pgrp, &sid_type))
184       DEBUG(3,("unpack_nt_permissions: unable to validate group sid.\n"));
185   }
186
187   /*
188    * If no DACL then this is a chown only security descriptor.
189    */
190
191   if(!(security_info_sent & DACL_SECURITY_INFORMATION) || !dacl) {
192     *pmode = 0;
193     return True;
194   }
195
196   /*
197    * Now go through the DACL and ensure that
198    * any owner/group sids match.
199    */
200
201   for(i = 0; i < dacl->num_aces; i++) {
202     DOM_SID ace_sid;
203     SEC_ACE *psa = &dacl->ace[i];
204
205     if((psa->type != SEC_ACE_TYPE_ACCESS_ALLOWED) &&
206        (psa->type != SEC_ACE_TYPE_ACCESS_DENIED)) {
207       DEBUG(3,("unpack_nt_permissions: unable to set anything but an ALLOW or DENY ACE.\n"));
208       return False;
209     }
210
211     /*
212      * Ignore or remove bits we don't care about on a directory ACE.
213      */
214
215     if(is_directory) {
216       if(psa->flags & SEC_ACE_FLAG_INHERIT_ONLY) {
217         DEBUG(3,("unpack_nt_permissions: ignoring inherit only ACE.\n"));
218         continue;
219       }
220
221       /*
222        * At least one of the ACE entries wasn't inherit only.
223        * Flag this so we know the returned mode is valid.
224        */
225
226       all_aces_are_inherit_only = False;
227     }
228
229     /*
230      * Windows 2000 sets these flags even on *file* ACE's. This is wrong
231      * but we can ignore them for now. Revisit this when we go to POSIX
232      * ACLs on directories.
233      */
234
235     psa->flags &= ~(SEC_ACE_FLAG_OBJECT_INHERIT|SEC_ACE_FLAG_CONTAINER_INHERIT);
236
237     if(psa->flags != 0) {
238       DEBUG(1,("unpack_nt_permissions: unable to set ACE flags (%x).\n", 
239             (unsigned int)psa->flags));
240       return False;
241     }
242
243     /*
244      * The security mask may be UNIX_ACCESS_NONE which should map into
245      * no permissions (we overload the WRITE_OWNER bit for this) or it
246      * should be one of the ALL/EXECUTE/READ/WRITE bits. Arrange for this
247      * to be so. Any other bits override the UNIX_ACCESS_NONE bit.
248      */
249
250     psa->info.mask &= (GENERIC_ALL_ACCESS|GENERIC_EXECUTE_ACCESS|GENERIC_WRITE_ACCESS|
251                      GENERIC_READ_ACCESS|UNIX_ACCESS_NONE|FILE_ALL_ATTRIBUTES);
252
253     if(psa->info.mask != UNIX_ACCESS_NONE)
254       psa->info.mask &= ~UNIX_ACCESS_NONE;
255
256     sid_copy(&ace_sid, &psa->sid);
257
258     if(sid_equal(&ace_sid, &file_owner_sid)) {
259       /*
260        * Map the desired permissions into owner perms.
261        */
262
263       if(psa->type == SEC_ACE_TYPE_ACCESS_ALLOWED)
264         *pmode |= map_nt_perms( psa->info, S_IRUSR);
265       else
266         *pmode &= ~(map_nt_perms( psa->info, S_IRUSR));
267
268     } else if( sid_equal(&ace_sid, &file_grp_sid)) {
269       /*
270        * Map the desired permissions into group perms.
271        */
272
273       if(psa->type == SEC_ACE_TYPE_ACCESS_ALLOWED)
274         *pmode |= map_nt_perms( psa->info, S_IRGRP);
275       else
276         *pmode &= ~(map_nt_perms( psa->info, S_IRGRP));
277
278     } else if( sid_equal(&ace_sid, &global_sid_World)) {
279       /*
280        * Map the desired permissions into other perms.
281        */
282
283       if(psa->type == SEC_ACE_TYPE_ACCESS_ALLOWED)
284         *pmode |= map_nt_perms( psa->info, S_IROTH);
285       else
286         *pmode &= ~(map_nt_perms( psa->info, S_IROTH));
287
288     } else {
289       DEBUG(0,("unpack_nt_permissions: unknown SID used in ACL.\n"));
290       return False;
291     }
292   }
293
294   if (is_directory && all_aces_are_inherit_only) {
295     /*
296      * Windows 2000 is doing one of these weird 'inherit acl'
297      * traverses to conserve NTFS ACL resources. Just pretend
298      * there was no DACL sent. JRA.
299      */
300
301     DEBUG(10,("unpack_nt_permissions: Win2k inherit acl traverse. Ignoring DACL.\n"));
302     free_sec_acl(&psd->dacl);
303   }
304
305   return True;
306 }
307
308 /****************************************************************************
309  Map generic UNIX permissions to POSIX ACL perms.
310 ****************************************************************************/
311
312 static SMB_ACL_PERMSET_T unix_perms_to_acl_perms(mode_t mode, int r_mask, int w_mask, int x_mask)
313 {
314         SMB_ACL_PERMSET_T  ret = 0;
315
316         ret |= (mode & r_mask) ? SMB_ACL_READ : 0;
317         ret |= (mode & w_mask) ? SMB_ACL_WRITE : 0;
318         ret |= (mode & x_mask) ? SMB_ACL_EXECUTE : 0;
319
320         return ret;
321 }
322
323 /****************************************************************************
324  Count a linked list of canonical ACE entries.
325 ****************************************************************************/
326
327 static size_t count_canon_ace_list( canon_ace *list_head )
328 {
329         size_t count = 0;
330         canon_ace *ace;
331
332         for (ace = list_head; ace; ace = ace->next)
333                 count++;
334
335         return count;
336 }
337
338 /****************************************************************************
339  Free a linked list of canonical ACE entries.
340 ****************************************************************************/
341
342 static void free_canon_ace_list( canon_ace *list_head )
343 {
344         while (list_head) {
345                 canon_ace *old_head = list_head;
346                 DLIST_REMOVE(list_head, list_head);
347                 free(old_head);
348         }
349 }
350
351 /******************************************************************************
352  Fall back to the generic 3 element UNIX permissions.
353 ********************************************************************************/
354
355 static canon_ace *unix_canonicalise_acl(files_struct *fsp, SMB_STRUCT_STAT *psbuf,
356                                                                                 DOM_SID *powner, DOM_SID *pgroup)
357 {
358         extern DOM_SID global_sid_World;
359         canon_ace *list_head = NULL;
360         canon_ace *owner_ace = NULL;
361         canon_ace *group_ace = NULL;
362         canon_ace *other_ace = NULL;
363         SMB_ACL_TAG_T type;
364         SMB_ACL_PERMSET_T perms;
365         DOM_SID sid;
366
367         /*
368          * Create 3 linked list entries.
369          */
370
371         if ((owner_ace = (canon_ace *)malloc(sizeof(canon_ace))) == NULL)
372                 goto fail;
373
374         if ((gtoup_ace = (canon_ace *)malloc(sizeof(canon_ace))) == NULL)
375                 goto fail;
376
377         if ((other_ace = (canon_ace *)malloc(sizeof(canon_ace))) == NULL)
378                 goto fail;
379
380         ZERO_STRUCTP(owner_ace);
381         ZERO_STRUCTP(group_ace);
382         ZERO_STRUCTP(other_ace);
383
384         owner_ace->type = SMB_ACL_USER_OBJ;
385         owner_ace->sid = *powner;
386
387         group_ace->type = SMB_ACL_GROUP_OBJ;
388         group_ace->sid = *pgroup;
389
390         other_ace->type = SMB_ACL_OTHER_OBJ;
391         other_ace->sid = global_sid_World;
392
393         if (!fsp->is_directory) {
394                 owner_ace->perms = unix_perms_to_acl_perms(sbuf.st_mode, S_IRUSR, S_IWUSR, S_IXUSR);
395                 group_ace->perms = unix_perms_to_acl_perms(sbuf.st_mode, S_IRGRP, S_IWGRP, S_IXGRP);
396                 other_ace->perms = unix_perms_to_acl_perms(sbuf.st_mode, S_IROTH, S_IWOTH, S_IXOTH);
397         } else {
398                 mode_t mode = unix_mode( fsp->conn, FILE_ATTRIBUTE_ARCHIVE, fsp->fsp_name);
399
400                 owner_ace->perms = unix_perms_to_acl_perms(mode, S_IRUSR, S_IWUSR, S_IXUSR);
401                 group_ace->perms = unix_perms_to_acl_perms(mode, S_IRGRP, S_IWGRP, S_IXGRP);
402                 other_ace->perms = unix_perms_to_acl_perms(mode, S_IROTH, S_IWOTH, S_IXOTH);
403         }
404
405         DLIST_ADD(list_head, other_ace);
406         DLIST_ADD(list_head, group_ace);
407         DLIST_ADD(list_head, owner_ace);
408
409         return list_head;
410
411   fail:
412
413         safe_free(owner_ace);
414         safe_free(group_ace);
415         safe_free(other_ace);
416
417         return NULL;
418 }
419
420 /****************************************************************************
421  Create a linked list of canonical ACE entries. This is sorted so that DENY
422  entries are at the front of the list, as NT requires.
423 ****************************************************************************/
424
425 static canon_ace *canonicalise_acl( SMB_ACL_T posix_acl, SMB_STRUCT_STAT *psbuf)
426 {
427         extern DOM_SID global_sid_World;
428         SMB_ACL_PERMSET_T acl_mask = (SMB_ACL_READ|SMB_ACL_WRITE|SMB_ACL_EXECUTE);
429         canon_ace *list_head = NULL;
430         canon_ace *ace = NULL;
431         canon_ace *next_ace = NULL;
432         int entry_id = SMB_ACL_FIRST_ENTRY;
433         SMB_ACL_ENTRY_T entry;
434
435         while ( sys_acl_get_entry(posix_acl, entry_id, &entry) == 1) {
436                 SMB_ACL_TAG_T tagtype;
437                 SMB_ACL_PERMSET_T permset;
438                 DOM_SID sid;
439
440                 /* get_next... */
441                 if (entry_id == SMB_ACL_FIRST_ENTRY)
442                         entry_id = SMB_ACL_NEXT_ENTRY;
443
444                 /* Is this a MASK entry ? */
445                 if (sys_acl_get_tag_type(entry, &tagtype) == -1)
446                         continue;
447
448                 if (sys_acl_get_permset(entry, &permset) == -1)
449                         continue;
450
451                 /* Decide which SID to use based on the ACL type. */
452                 switch(tagtype) {
453                         SMB_ACL_USER_OBJ:
454                                 /* Get the SID from the owner. */
455                                 uid_to_sid( &sid, psbuf->st_uid );
456                                 break;
457                         SMB_ACL_USER:
458                                 {
459                                         uid_t *puid = (uid_t *)sys_acl_get_qualifier(entry);
460                                         if (puid == NULL) {
461                                                 DEBUG(0,("canonicalise_acl: Failed to get uid.\n"));
462                                                 continue;
463                                         }
464                                         uid_to_sid( &sid, *puid);
465                                         break;
466                                 }
467                         SMB_ACL_GROUP_OBJ:
468                                 /* Get the SID from the owning group. */
469                                 gid_to_sid( &sid, psbuf->st_gid );
470                                 break;
471                         SMB_ACL_GROUP:
472                                 {
473                                         gid_t *pgid = (gid_t *)sys_acl_get_qualifier(entry);
474                                         if (pgid == NULL) {
475                                                 DEBUG(0,("canonicalise_acl: Failed to get gid.\n"));
476                                                 continue;
477                                         }
478                                         gid_to_sid( &sid, *pgid);
479                                         break;
480                                 }
481                         SMB_ACL_MASK:
482                                 acl_mask = permset;
483                                 continue; /* Don't count the mask as an entry. */
484                         SMB_ACL_OTHER_OBJ:
485                                 /* Use the Everyone SID */
486                                 sid = global_sid_World;
487                                 break;
488                         default:
489                                 DEBUG(0,("canonicalise_acl: Unknown tagtype %u\n", (unsigned int)tagtype));
490                                 continue;
491                 }       
492
493                 /*
494                  * Add this entry to the list.
495                  */
496
497                 if ((ace = (canon_ace *)malloc(sizeof(canon_ace))) == NULL)
498                         goto fail;
499
500                 ZERO_STRUCTP(ace);
501                 ace->type = tagtype;
502                 ace->perms = permset;
503                 ace->sid = sid;
504                  
505                 DLIST_ADD(list_head, ace);
506         }
507
508         /*
509          * Now go through the list, masking the permissions with the
510          * acl_mask. If the permissions are 0 and the type is ACL_USER
511          * or ACL_GROUP then it's a DENY entry and should be listed
512          * first. If the permissions are 0 and the type is ACL_USER_OBJ,
513          * ACL_GROUP_OBJ or ACL_OTHER_OBJ then remove the entry as they
514          * can never apply.
515          */
516
517         for ( ace = list_head; ace; ace = next_ace) {
518                 next_ace = ace_next;
519                 ace->perms &= acl_mask;
520
521                 if (ace->perms == 0) {
522                         switch (ace->type) {
523                                 SMB_ACL_USER_OBJ:
524                                 SMB_ACL_GROUP_OBJ:
525                                 SMB_ACL_OTHER_OBJ:
526                                         DLIST_REMOVE(list_head, ace);
527                                         break;
528                                 SMB_ACL_USER:
529                                 SMB_ACL_GROUP:
530                                         DLIST_PROMOTE(list_head, ace);
531                                         break;
532                         }
533                 }
534         }
535
536         return list_head;
537 }
538
539 /****************************************************************************
540  Reply to query a security descriptor from an fsp. If it succeeds it allocates
541  the space for the return elements and returns the size needed to return the
542  security descriptor. This should be the only external function needed for
543  the UNIX style get ACL.
544 ****************************************************************************/
545
546 size_t get_nt_acl(files_struct *fsp, SEC_DESC **ppdesc)
547 {
548         extern DOM_SID global_sid_World;
549         SMB_STRUCT_STAT sbuf;
550         SEC_ACE *nt_ace_list;
551         DOM_SID owner_sid;
552         DOM_SID group_sid;
553         size_t sd_size = 0;
554         SEC_ACL *psa = NULL;
555         SEC_ACCESS owner_access;
556         int owner_acl_type;
557         SEC_ACCESS group_access;
558         int grp_acl_type;
559         SEC_ACCESS other_access;
560         int other_acl_type;
561         size_t num_acls = 0;
562         size_t num_dir_acls = 0;
563         size_t num_aces = 0;
564         SMB_ACL_T posix_acl = NULL;
565         SMB_ACL_T dir_acl = NULL;
566         canon_ace *file_ace = NULL;
567         canon_ace *dir_ace = NULL;
568  
569         *ppdesc = NULL;
570
571         if(fsp->is_directory || fsp->fd == -1) {
572
573                 /* Get the stat struct for the owner info. */
574                 if(vfs_stat(fsp,fsp->fsp_name, &sbuf) != 0) {
575                         return 0;
576                 }
577                 /*
578                  * Get the ACL from the path.
579                  */
580
581                 posix_acl = sys_acl_get_file( dos_to_unix(fsp->fsp_name, False), SMB_ACL_TYPE_ACCESS);
582
583                 /*
584                  * If it's a directory get the default POSIX ACL.
585                  */
586
587                 if(fsp->is_directory)
588                         dir_acl = sys_acl_get_file( dos_to_unix(fsp->fsp_name, False), SMB_ACL_TYPE_DEFAULT);
589
590         } else {
591
592                 /* Get the stat struct for the owner info. */
593                 if(fsp->conn->vfs_ops.fstat(fsp->fd,&sbuf) != 0) {
594                         return 0;
595                 }
596                 /*
597                  * Get the ACL from the fd.
598                  */
599                 posix_acl = sys_acl_get_fd(fsp->fd);
600         }
601
602         /*
603          * Get the owner, group and world SIDs.
604          */
605
606         create_file_sids(&sbuf, &owner_sid, &group_sid);
607
608         /* Create the canon_ace lists. */
609         if (posix_acl)
610                 file_ace = canonicalise_acl( posix_acl, &sbuf);
611         else
612                 file_ace = unix_canonicalise_acl(fsp, &sbuf, &owner_sid, &group_sid);
613
614         num_acls = count_canon_ace_list(file_ace);
615
616         if (fsp->is_directory) { 
617                 if (dir_ace)
618                         dir_ace = canonicalise_acl( dir_acl, &sbuf);
619                 else
620                         dir_ace = unix_canonicalise_acl(fsp, &sbuf, &owner_sid, &group_sid);
621
622                 num_dir_acls = count_canon_ace_list(dir_ace);
623         }
624
625         /* Allocate the ace list. */
626         if ((nt_ace_list = (SEC_ACE *)malloc((num_acls + num_dir_acls)* sizeof(SEC_ACE))) == NULL) {
627                 DEBUG(0,("get_nt_acl: Unable to malloc space for nt_ace_list.\n"));
628                 goto done:
629         }
630
631         memset(nt_ace_list, '\0', (num_acls + num_dir_acls) * sizeof(SEC_ACE) );
632
633         /*
634          * Create the NT ACE list from the canonical ace lists.
635          */
636
637         {
638                 canon_ace *ace;
639                 int nt_acl_type;
640
641                 ace = file_ace;
642
643                 for (i = 0; i < num_acls; i++, ace = ace->next) {
644                         SEC_ACCESS acc = map_canon_ace_perms(&nt_acl_type, &owner_sid, ace );
645                         init_sec_ace(&nt_ace_list[num_aces++], &ace->sid, nt_acl_type, acc, 0);
646                 }
647
648                 ace = dir_ace;
649
650                 for (i = 0; i < num_dir_acls; i++, ace = ace->next) {
651                         SEC_ACCESS acc = map_canon_ace_perms(&nt_acl_type, &owner_sid, ace );
652                         init_sec_ace(&nt_ace_list[num_aces++], &ace->sid, nt_acl_type, acc, 
653                                         SEC_ACE_FLAG_OBJECT_INHERIT|SEC_ACE_FLAG_CONTAINER_INHERIT|SEC_ACE_FLAG_INHERIT_ONLY);
654                 }
655         }
656
657         if (num_acls) {
658                 if((psa = make_sec_acl( ACL_REVISION, num_aces, ace_list)) == NULL) {
659                         DEBUG(0,("get_nt_acl: Unable to malloc space for acl.\n"));
660                         goto done;
661                 }
662         }
663
664         *ppdesc = make_standard_sec_desc( &owner_sid, &group_sid, psa, &sd_size);
665
666         if(!*ppdesc) {
667                 DEBUG(0,("get_nt_acl: Unable to malloc space for security descriptor.\n"));
668                 sd_size = 0;
669         }
670
671   done:
672
673         if (posix_acl)  
674                 sys_acl_free(posix_acl);
675         if (directory_acl)
676                 sys_acl_free(directory_acl);
677         if (file_ace)
678                 free_canon_ace_list(file_ace);
679         if (dir_ace)
680                 free_canon_ace_list(dir_ace);
681         if (nt_ace_list)
682                 free(nt_ace_list);
683         if (psa)
684                 free_sec_acl(&psa);
685
686         return sd_size;
687 }
688
689 /****************************************************************************
690  Reply to set a security descriptor on an fsp. security_info_sent is the
691  description of the following NT ACL.
692  This should be the only external function needed for the UNIX style set ACL.
693 ****************************************************************************/
694
695 BOOL set_nt_acl(files_struct *fsp, uint32 security_info_sent, SEC_DESC *psd)
696 {
697   connection_struct *conn = fsp->conn;
698   uid_t user = (uid_t)-1;
699   gid_t grp = (gid_t)-1;
700   mode_t perms = 0;
701   SMB_STRUCT_STAT sbuf;  
702   BOOL got_dacl = False;
703
704   /*
705    * Get the current state of the file.
706    */
707
708   if(fsp->is_directory) {
709     if(dos_stat(fsp->fsp_name, &sbuf) != 0)
710       return False;
711   } else {
712
713     int ret;
714
715     if(fsp->fd == -1)
716       ret = conn->vfs_ops.stat(dos_to_unix(fsp->fsp_name,False), &sbuf);
717     else
718       ret = conn->vfs_ops.fstat(fsp->fd,&sbuf);
719
720     if(ret != 0)
721       return False;
722   }
723
724   /*
725    * Unpack the user/group/world id's and permissions.
726    */
727
728   if (!unpack_nt_permissions( &sbuf, &user, &grp, &perms, security_info_sent, psd, fsp->is_directory))
729     return False;
730
731   if (psd->dacl != NULL)
732     got_dacl = True;
733
734   /*
735    * Do we need to chown ?
736    */
737
738   if((user != (uid_t)-1 || grp != (uid_t)-1) && (sbuf.st_uid != user || sbuf.st_gid != grp)) {
739
740     DEBUG(3,("call_nt_transact_set_security_desc: chown %s. uid = %u, gid = %u.\n",
741           fsp->fsp_name, (unsigned int)user, (unsigned int)grp ));
742
743     if(dos_chown( fsp->fsp_name, user, grp) == -1) {
744       DEBUG(3,("call_nt_transact_set_security_desc: chown %s, %u, %u failed. Error = %s.\n",
745             fsp->fsp_name, (unsigned int)user, (unsigned int)grp, strerror(errno) ));
746       return False;
747     }
748
749     /*
750      * Recheck the current state of the file, which may have changed.
751      * (suid/sgid bits, for instance)
752      */
753
754     if(fsp->is_directory) {
755       if(dos_stat(fsp->fsp_name, &sbuf) != 0) {
756         return False;
757       }
758     } else {
759
760       int ret;
761     
762       if(fsp->fd == -1)
763         ret = conn->vfs_ops.stat(dos_to_unix(fsp->fsp_name,False), &sbuf);
764       else
765         ret = conn->vfs_ops.fstat(fsp->fd,&sbuf);
766   
767       if(ret != 0)
768         return False;
769     }
770   }
771
772   /*
773    * Only change security if we got a DACL.
774    */
775
776   if((security_info_sent & DACL_SECURITY_INFORMATION) && got_dacl) {
777
778     /*
779      * Check to see if we need to change anything.
780      * Enforce limits on modified bits *only*. Don't enforce masks
781          * on bits not changed by the user.
782      */
783
784     if(fsp->is_directory) {
785
786       perms &= (lp_dir_security_mask(SNUM(conn)) | sbuf.st_mode);
787       perms |= (lp_force_dir_security_mode(SNUM(conn)) & ( perms ^ sbuf.st_mode ));
788
789     } else {
790
791       perms &= (lp_security_mask(SNUM(conn)) | sbuf.st_mode); 
792       perms |= (lp_force_security_mode(SNUM(conn)) & ( perms ^ sbuf.st_mode ));
793
794     }
795
796     /*
797      * Preserve special bits.
798      */
799
800     perms |= (sbuf.st_mode & ~0777);
801
802     /*
803      * Do we need to chmod ?
804      */
805
806     if(sbuf.st_mode != perms) {
807
808       DEBUG(3,("call_nt_transact_set_security_desc: chmod %s. perms = 0%o.\n",
809             fsp->fsp_name, (unsigned int)perms ));
810
811       if(conn->vfs_ops.chmod(dos_to_unix(fsp->fsp_name, False), perms) == -1) {
812         DEBUG(3,("call_nt_transact_set_security_desc: chmod %s, 0%o failed. Error = %s.\n",
813               fsp->fsp_name, (unsigned int)perms, strerror(errno) ));
814         return False;
815       }
816     }
817   }
818
819   return True;
820 }
821 #undef OLD_NTDOMAIN
822 #else /* HAVE_POSIX_ACLS */
823  void dummy_posix_acls(void) {;} /* So some compilers don't complain. */
824 #endif /* HAVE_POSIX_ACLS */