Commit an updated configure, but the build_farm should run autoconf!
[Samba.git] / source3 / smbd / posix_acls.c
blobdb7e594a9ee4536450694ff10360cf4eaa43ebeb
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
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.
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.
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.
22 #include "includes.h"
24 /****************************************************************************
25 Data structures representing the internal ACE format.
26 ****************************************************************************/
28 enum ace_owner {UID_ACE, GID_ACE, WORLD_ACE};
29 enum ace_attribute {ALLOW_ACE, DENY_ACE}; /* Used for incoming NT ACLS. */
31 typedef union posix_id {
32 uid_t uid;
33 gid_t gid;
34 int world;
35 } posix_id;
37 typedef struct canon_ace {
38 struct canon_ace *next, *prev;
39 SMB_ACL_TAG_T type;
40 mode_t perms; /* Only use S_I(R|W|X)USR mode bits here. */
41 DOM_SID sid;
42 enum ace_owner owner_type;
43 enum ace_attribute attr;
44 posix_id unix_ug;
45 } canon_ace;
47 #define ALL_ACE_PERMS (S_IRUSR|S_IWUSR|S_IXUSR)
49 /****************************************************************************
50 Functions to manipulate the internal ACE format.
51 ****************************************************************************/
53 /****************************************************************************
54 Count a linked list of canonical ACE entries.
55 ****************************************************************************/
57 static size_t count_canon_ace_list( canon_ace *list_head )
59 size_t count = 0;
60 canon_ace *ace;
62 for (ace = list_head; ace; ace = ace->next)
63 count++;
65 return count;
68 /****************************************************************************
69 Free a linked list of canonical ACE entries.
70 ****************************************************************************/
72 static void free_canon_ace_list( canon_ace *list_head )
74 while (list_head) {
75 canon_ace *old_head = list_head;
76 DLIST_REMOVE(list_head, list_head);
77 free(old_head);
81 /****************************************************************************
82 Function to duplicate a canon_ace entry.
83 ****************************************************************************/
85 static canon_ace *dup_canon_ace( canon_ace *src_ace)
87 canon_ace *dst_ace = (canon_ace *)malloc(sizeof(canon_ace));
89 if (dst_ace == NULL)
90 return NULL;
92 *dst_ace = *src_ace;
93 dst_ace->prev = dst_ace->next = NULL;
94 return dst_ace;
97 /****************************************************************************
98 Print out a canon ace.
99 ****************************************************************************/
101 static void print_canon_ace(canon_ace *pace, int num)
103 fstring str;
105 dbgtext( "canon_ace index %d. Type = %s ", num, pace->attr == ALLOW_ACE ? "allow" : "deny" );
106 dbgtext( "SID = %s ", sid_to_string( str, &pace->sid));
107 if (pace->owner_type == UID_ACE) {
108 struct passwd *pass = sys_getpwuid(pace->unix_ug.uid);
109 dbgtext( "uid %u (%s) ", (unsigned int)pace->unix_ug.uid, pass ? pass->pw_name : "UNKNOWN");
110 } else if (pace->owner_type == GID_ACE) {
111 struct group *grp = getgrgid(pace->unix_ug.gid);
112 dbgtext( "gid %u (%s) ", (unsigned int)pace->unix_ug.gid, grp ? grp->gr_name : "UNKNOWN");
113 } else
114 dbgtext( "other ");
115 switch (pace->type) {
116 case SMB_ACL_USER:
117 dbgtext( "SMB_ACL_USER ");
118 break;
119 case SMB_ACL_USER_OBJ:
120 dbgtext( "SMB_ACL_USER_OBJ ");
121 break;
122 case SMB_ACL_GROUP:
123 dbgtext( "SMB_ACL_GROUP ");
124 break;
125 case SMB_ACL_GROUP_OBJ:
126 dbgtext( "SMB_ACL_GROUP_OBJ ");
127 break;
128 case SMB_ACL_OTHER:
129 dbgtext( "SMB_ACL_OTHER ");
130 break;
132 dbgtext( "perms ");
133 dbgtext( "%c", pace->perms & S_IRUSR ? 'r' : '-');
134 dbgtext( "%c", pace->perms & S_IWUSR ? 'w' : '-');
135 dbgtext( "%c\n", pace->perms & S_IXUSR ? 'x' : '-');
138 /****************************************************************************
139 Print out a canon ace list.
140 ****************************************************************************/
142 static void print_canon_ace_list(const char *name, canon_ace *ace_list)
144 int count = 0;
146 if( DEBUGLVL( 10 )) {
147 dbgtext( "print_canon_ace_list: %s\n", name );
148 for (;ace_list; ace_list = ace_list->next, count++)
149 print_canon_ace(ace_list, count );
153 /****************************************************************************
154 Map POSIX ACL perms to canon_ace permissions (a mode_t containing only S_(R|W|X)USR bits).
155 ****************************************************************************/
157 static mode_t convert_permset_to_mode_t(SMB_ACL_PERMSET_T permset)
159 mode_t ret = 0;
161 ret |= (sys_acl_get_perm(permset, SMB_ACL_READ) ? S_IRUSR : 0);
162 ret |= (sys_acl_get_perm(permset, SMB_ACL_WRITE) ? S_IWUSR : 0);
163 ret |= (sys_acl_get_perm(permset, SMB_ACL_EXECUTE) ? S_IXUSR : 0);
165 return ret;
168 /****************************************************************************
169 Map generic UNIX permissions to canon_ace permissions (a mode_t containing only S_(R|W|X)USR bits).
170 ****************************************************************************/
172 static mode_t unix_perms_to_acl_perms(mode_t mode, int r_mask, int w_mask, int x_mask)
174 mode_t ret = 0;
176 if (mode & r_mask)
177 ret |= S_IRUSR;
178 if (mode & w_mask)
179 ret |= S_IWUSR;
180 if (mode & x_mask)
181 ret |= S_IXUSR;
183 return ret;
186 /****************************************************************************
187 Map canon_ace permissions (a mode_t containing only S_(R|W|X)USR bits) to
188 an SMB_ACL_PERMSET_T.
189 ****************************************************************************/
191 static int map_acl_perms_to_permset(mode_t mode, SMB_ACL_PERMSET_T *p_permset)
193 if (sys_acl_clear_perms(*p_permset) == -1)
194 return -1;
195 if (mode & S_IRUSR) {
196 if (sys_acl_add_perm(*p_permset, SMB_ACL_READ) == -1)
197 return -1;
199 if (mode & S_IWUSR) {
200 if (sys_acl_add_perm(*p_permset, SMB_ACL_WRITE) == -1)
201 return -1;
203 if (mode & S_IXUSR) {
204 if (sys_acl_add_perm(*p_permset, SMB_ACL_EXECUTE) == -1)
205 return -1;
207 return 0;
209 /****************************************************************************
210 Function to create owner and group SIDs from a SMB_STRUCT_STAT.
211 ****************************************************************************/
213 static void create_file_sids(SMB_STRUCT_STAT *psbuf, DOM_SID *powner_sid, DOM_SID *pgroup_sid)
215 uid_to_sid( powner_sid, psbuf->st_uid );
216 gid_to_sid( pgroup_sid, psbuf->st_gid );
219 /****************************************************************************
220 Merge aces with a common sid - if both are allow or deny, OR the permissions together and
221 delete the second one. If the first is deny, mask the permissions off and delete the allow
222 if the permissions become zero, delete the deny if the permissions are non zero.
223 ****************************************************************************/
225 static void merge_aces( canon_ace **pp_list_head )
227 canon_ace *list_head = *pp_list_head;
228 canon_ace *curr_ace_outer;
229 canon_ace *curr_ace_outer_next;
232 * First, merge allow entries with identical SIDs, and deny entries
233 * with identical SIDs.
236 for (curr_ace_outer = list_head; curr_ace_outer; curr_ace_outer = curr_ace_outer_next) {
237 canon_ace *curr_ace;
238 canon_ace *curr_ace_next;
240 curr_ace_outer_next = curr_ace_outer->next; /* Save the link in case we delete. */
242 for (curr_ace = curr_ace_outer->next; curr_ace; curr_ace = curr_ace_next) {
244 curr_ace_next = curr_ace->next; /* Save the link in case of delete. */
246 if (sid_equal(&curr_ace->sid, &curr_ace_outer->sid) &&
247 (curr_ace->attr == curr_ace_outer->attr)) {
249 if( DEBUGLVL( 10 )) {
250 dbgtext("merge_aces: Merging ACE's\n");
251 print_canon_ace( curr_ace_outer, 0);
252 print_canon_ace( curr_ace, 0);
255 /* Merge two allow or two deny ACE's. */
257 curr_ace_outer->perms |= curr_ace->perms;
258 DLIST_REMOVE(list_head, curr_ace);
259 free(curr_ace);
260 curr_ace_outer_next = curr_ace_outer->next; /* We may have deleted the link. */
266 * Now go through and mask off allow permissions with deny permissions.
267 * We can delete either the allow or deny here as we know that each SID
268 * appears only once in the list.
271 for (curr_ace_outer = list_head; curr_ace_outer; curr_ace_outer = curr_ace_outer_next) {
272 canon_ace *curr_ace;
273 canon_ace *curr_ace_next;
275 curr_ace_outer_next = curr_ace_outer->next; /* Save the link in case we delete. */
277 for (curr_ace = curr_ace_outer->next; curr_ace; curr_ace = curr_ace_next) {
279 curr_ace_next = curr_ace->next; /* Save the link in case of delete. */
282 * Subtract ACE's with different entries. Due to the ordering constraints
283 * we've put on the ACL, we know the deny must be the first one.
286 if (sid_equal(&curr_ace->sid, &curr_ace_outer->sid) &&
287 (curr_ace_outer->attr == DENY_ACE) && (curr_ace->attr == ALLOW_ACE)) {
289 if( DEBUGLVL( 10 )) {
290 dbgtext("merge_aces: Masking ACE's\n");
291 print_canon_ace( curr_ace_outer, 0);
292 print_canon_ace( curr_ace, 0);
295 curr_ace->perms &= ~curr_ace_outer->perms;
297 if (curr_ace->perms == 0) {
300 * The deny overrides the allow. Remove the allow.
303 DLIST_REMOVE(list_head, curr_ace);
304 free(curr_ace);
305 curr_ace_outer_next = curr_ace_outer->next; /* We may have deleted the link. */
307 } else {
310 * Even after removing permissions, there
311 * are still allow permissions - delete the deny.
312 * It is safe to delete the deny here,
313 * as we are guarenteed by the deny first
314 * ordering that all the deny entries for
315 * this SID have already been merged into one
316 * before we can get to an allow ace.
319 DLIST_REMOVE(list_head, curr_ace_outer);
320 free(curr_ace_outer);
324 } /* end for curr_ace */
325 } /* end for curr_ace_outer */
327 /* We may have modified the list. */
329 *pp_list_head = list_head;
332 /****************************************************************************
333 Map canon_ace perms to permission bits NT.
334 The attr element is not used here - we only process deny entries on set,
335 not get. Deny entries are implicit on get with ace->perms = 0.
336 ****************************************************************************/
338 static SEC_ACCESS map_canon_ace_perms(int *pacl_type, DOM_SID *powner_sid, canon_ace *ace)
340 SEC_ACCESS sa;
341 uint32 nt_mask = 0;
343 *pacl_type = SEC_ACE_TYPE_ACCESS_ALLOWED;
345 if ((ace->perms & ALL_ACE_PERMS) == ALL_ACE_PERMS) {
346 nt_mask = UNIX_ACCESS_RWX;
347 } else if ((ace->perms & ALL_ACE_PERMS) == (mode_t)0) {
348 nt_mask = UNIX_ACCESS_NONE;
349 } else {
350 nt_mask |= ((ace->perms & S_IRUSR) ? UNIX_ACCESS_R : 0 );
351 nt_mask |= ((ace->perms & S_IWUSR) ? UNIX_ACCESS_W : 0 );
352 nt_mask |= ((ace->perms & S_IXUSR) ? UNIX_ACCESS_X : 0 );
355 DEBUG(10,("map_canon_ace_perms: Mapped (UNIX) %x to (NT) %x\n",
356 (unsigned int)ace->perms, (unsigned int)nt_mask ));
358 init_sec_access(&sa,nt_mask);
359 return sa;
362 /****************************************************************************
363 Map NT perms to a UNIX mode_t.
364 ****************************************************************************/
366 #define FILE_SPECIFIC_READ_BITS (FILE_READ_DATA|FILE_READ_EA|FILE_READ_ATTRIBUTES)
367 #define FILE_SPECIFIC_WRITE_BITS (FILE_WRITE_DATA|FILE_APPEND_DATA|FILE_WRITE_EA|FILE_WRITE_ATTRIBUTES)
368 #define FILE_SPECIFIC_EXECUTE_BITS (FILE_EXECUTE)
370 static mode_t map_nt_perms( SEC_ACCESS sec_access, int type)
372 mode_t mode = 0;
374 switch(type) {
375 case S_IRUSR:
376 if(sec_access.mask & GENERIC_ALL_ACCESS)
377 mode = S_IRUSR|S_IWUSR|S_IXUSR;
378 else {
379 mode |= (sec_access.mask & (GENERIC_READ_ACCESS|FILE_SPECIFIC_READ_BITS)) ? S_IRUSR : 0;
380 mode |= (sec_access.mask & (GENERIC_WRITE_ACCESS|FILE_SPECIFIC_WRITE_BITS)) ? S_IWUSR : 0;
381 mode |= (sec_access.mask & (GENERIC_EXECUTE_ACCESS|FILE_SPECIFIC_EXECUTE_BITS)) ? S_IXUSR : 0;
383 break;
384 case S_IRGRP:
385 if(sec_access.mask & GENERIC_ALL_ACCESS)
386 mode = S_IRGRP|S_IWGRP|S_IXGRP;
387 else {
388 mode |= (sec_access.mask & (GENERIC_READ_ACCESS|FILE_SPECIFIC_READ_BITS)) ? S_IRGRP : 0;
389 mode |= (sec_access.mask & (GENERIC_WRITE_ACCESS|FILE_SPECIFIC_WRITE_BITS)) ? S_IWGRP : 0;
390 mode |= (sec_access.mask & (GENERIC_EXECUTE_ACCESS|FILE_SPECIFIC_EXECUTE_BITS)) ? S_IXGRP : 0;
392 break;
393 case S_IROTH:
394 if(sec_access.mask & GENERIC_ALL_ACCESS)
395 mode = S_IROTH|S_IWOTH|S_IXOTH;
396 else {
397 mode |= (sec_access.mask & (GENERIC_READ_ACCESS|FILE_SPECIFIC_READ_BITS)) ? S_IROTH : 0;
398 mode |= (sec_access.mask & (GENERIC_WRITE_ACCESS|FILE_SPECIFIC_WRITE_BITS)) ? S_IWOTH : 0;
399 mode |= (sec_access.mask & (GENERIC_EXECUTE_ACCESS|FILE_SPECIFIC_EXECUTE_BITS)) ? S_IXOTH : 0;
401 break;
404 return mode;
407 /****************************************************************************
408 Unpack a SEC_DESC into a UNIX owner and group.
409 ****************************************************************************/
411 static BOOL unpack_nt_owners(SMB_STRUCT_STAT *psbuf, uid_t *puser, gid_t *pgrp, uint32 security_info_sent, SEC_DESC *psd)
413 DOM_SID owner_sid;
414 DOM_SID grp_sid;
415 enum SID_NAME_USE sid_type;
417 *puser = (uid_t)-1;
418 *pgrp = (gid_t)-1;
420 if(security_info_sent == 0) {
421 DEBUG(0,("unpack_nt_owners: no security info sent !\n"));
422 return False;
426 * Validate the owner and group SID's.
429 memset(&owner_sid, '\0', sizeof(owner_sid));
430 memset(&grp_sid, '\0', sizeof(grp_sid));
432 DEBUG(5,("unpack_nt_owners: validating owner_sids.\n"));
435 * Don't immediately fail if the owner sid cannot be validated.
436 * This may be a group chown only set.
439 if (security_info_sent & OWNER_SECURITY_INFORMATION) {
440 sid_copy(&owner_sid, psd->owner_sid);
441 if (!sid_to_uid( &owner_sid, puser, &sid_type))
442 DEBUG(3,("unpack_nt_owners: unable to validate owner sid.\n"));
446 * Don't immediately fail if the group sid cannot be validated.
447 * This may be an owner chown only set.
450 if (security_info_sent & GROUP_SECURITY_INFORMATION) {
451 sid_copy(&grp_sid, psd->grp_sid);
452 if (!sid_to_gid( &grp_sid, pgrp, &sid_type))
453 DEBUG(3,("unpack_nt_owners: unable to validate group sid.\n"));
456 DEBUG(5,("unpack_nt_owners: owner_sids validated.\n"));
458 return True;
461 /****************************************************************************
462 Ensure the enforced permissions for this share apply.
463 ****************************************************************************/
465 static mode_t apply_default_perms(files_struct *fsp, mode_t perms, mode_t type)
467 int snum = SNUM(fsp->conn);
468 mode_t and_bits = (mode_t)0;
469 mode_t or_bits = (mode_t)0;
471 /* Get the initial bits to apply. */
473 if (fsp->is_directory) {
474 and_bits = lp_dir_security_mask(snum);
475 or_bits = lp_force_dir_security_mode(snum);
476 } else {
477 and_bits = lp_security_mask(snum);
478 or_bits = lp_force_security_mode(snum);
481 /* Now bounce them into the S_USR space. */
482 switch(type) {
483 case S_IRUSR:
484 and_bits = unix_perms_to_acl_perms(and_bits, S_IRUSR, S_IWUSR, S_IXUSR);
485 or_bits = unix_perms_to_acl_perms(or_bits, S_IRUSR, S_IWUSR, S_IXUSR);
486 break;
487 case S_IRGRP:
488 and_bits = unix_perms_to_acl_perms(and_bits, S_IRGRP, S_IWGRP, S_IXGRP);
489 or_bits = unix_perms_to_acl_perms(or_bits, S_IRGRP, S_IWGRP, S_IXGRP);
490 break;
491 case S_IROTH:
492 and_bits = unix_perms_to_acl_perms(and_bits, S_IROTH, S_IWOTH, S_IXOTH);
493 or_bits = unix_perms_to_acl_perms(or_bits, S_IROTH, S_IWOTH, S_IXOTH);
494 break;
497 return ((perms & and_bits)|or_bits);
500 /****************************************************************************
501 A well formed POSIX file or default ACL has at least 3 entries, a
502 SMB_ACL_USER_OBJ, SMB_ACL_GROUP_OBJ, SMB_ACL_OTHER_OBJ.
503 In addition, the owner must always have at least read access.
504 When using this call on get_acl, the pst struct is valid and contains
505 the mode of the file. When using this call on set_acl, the pst struct has
506 been modified to have a mode containing the default for this file or directory
507 type.
508 ****************************************************************************/
510 static BOOL ensure_canon_entry_valid(canon_ace **pp_ace,
511 files_struct *fsp,
512 DOM_SID *pfile_owner_sid,
513 DOM_SID *pfile_grp_sid,
514 SMB_STRUCT_STAT *pst,
515 BOOL setting_acl)
517 extern DOM_SID global_sid_World;
518 canon_ace *pace;
519 BOOL got_user = False;
520 BOOL got_grp = False;
521 BOOL got_other = False;
523 for (pace = *pp_ace; pace; pace = pace->next) {
524 if (pace->type == SMB_ACL_USER_OBJ) {
526 if (setting_acl) {
527 /* Ensure owner has read access. */
528 pace->perms |= S_IRUSR;
529 if (fsp->is_directory)
530 pace->perms |= (S_IWUSR|S_IXUSR);
533 * Ensure create mask/force create mode is respected on set.
536 pace->perms = apply_default_perms(fsp, pace->perms, S_IRUSR);
539 got_user = True;
540 } else if (pace->type == SMB_ACL_GROUP_OBJ) {
543 * Ensure create mask/force create mode is respected on set.
546 if (setting_acl)
547 pace->perms = apply_default_perms(fsp, pace->perms, S_IRGRP);
548 got_grp = True;
549 } else if (pace->type == SMB_ACL_OTHER) {
552 * Ensure create mask/force create mode is respected on set.
555 if (setting_acl)
556 pace->perms = apply_default_perms(fsp, pace->perms, S_IROTH);
557 got_other = True;
561 if (!got_user) {
562 if ((pace = (canon_ace *)malloc(sizeof(canon_ace))) == NULL) {
563 DEBUG(0,("ensure_canon_entry_valid: malloc fail.\n"));
564 return False;
567 ZERO_STRUCTP(pace);
568 pace->type = SMB_ACL_USER_OBJ;
569 pace->owner_type = UID_ACE;
570 pace->unix_ug.uid = pst->st_uid;
571 pace->sid = *pfile_owner_sid;
572 pace->perms = unix_perms_to_acl_perms(pst->st_mode, S_IRUSR, S_IWUSR, S_IXUSR);
573 pace->attr = ALLOW_ACE;
575 DLIST_ADD(*pp_ace, pace);
578 if (!got_grp) {
579 if ((pace = (canon_ace *)malloc(sizeof(canon_ace))) == NULL) {
580 DEBUG(0,("ensure_canon_entry_valid: malloc fail.\n"));
581 return False;
584 ZERO_STRUCTP(pace);
585 pace->type = SMB_ACL_GROUP_OBJ;
586 pace->owner_type = GID_ACE;
587 pace->unix_ug.uid = pst->st_gid;
588 pace->sid = *pfile_grp_sid;
589 pace->perms = unix_perms_to_acl_perms(pst->st_mode, S_IRGRP, S_IWGRP, S_IXGRP);
590 pace->attr = ALLOW_ACE;
592 DLIST_ADD(*pp_ace, pace);
595 if (!got_other) {
596 if ((pace = (canon_ace *)malloc(sizeof(canon_ace))) == NULL) {
597 DEBUG(0,("ensure_canon_entry_valid: malloc fail.\n"));
598 return False;
601 ZERO_STRUCTP(pace);
602 pace->type = SMB_ACL_OTHER;
603 pace->owner_type = WORLD_ACE;
604 pace->unix_ug.world = -1;
605 pace->sid = global_sid_World;
606 pace->perms = unix_perms_to_acl_perms(pst->st_mode, S_IROTH, S_IWOTH, S_IXOTH);
607 pace->attr = ALLOW_ACE;
609 DLIST_ADD(*pp_ace, pace);
612 return True;
615 /****************************************************************************
616 Unpack a SEC_DESC into two canonical ace lists.
617 ****************************************************************************/
619 static BOOL create_canon_ace_lists(files_struct *fsp,
620 DOM_SID *pfile_owner_sid,
621 DOM_SID *pfile_grp_sid,
622 canon_ace **ppfile_ace, canon_ace **ppdir_ace,
623 SEC_ACL *dacl)
625 extern DOM_SID global_sid_World;
626 extern struct generic_mapping file_generic_mapping;
627 BOOL all_aces_are_inherit_only = (fsp->is_directory ? True : False);
628 canon_ace *file_ace = NULL;
629 canon_ace *dir_ace = NULL;
630 canon_ace *tmp_ace = NULL;
631 canon_ace *current_ace = NULL;
632 BOOL got_dir_allow = False;
633 BOOL got_file_allow = False;
634 int i, j;
636 *ppfile_ace = NULL;
637 *ppdir_ace = NULL;
640 * Convert the incoming ACL into a more regular form.
643 for(i = 0; i < dacl->num_aces; i++) {
644 SEC_ACE *psa = &dacl->ace[i];
646 if((psa->type != SEC_ACE_TYPE_ACCESS_ALLOWED) && (psa->type != SEC_ACE_TYPE_ACCESS_DENIED)) {
647 DEBUG(3,("create_canon_ace_lists: unable to set anything but an ALLOW or DENY ACE.\n"));
648 return False;
652 * The security mask may be UNIX_ACCESS_NONE which should map into
653 * no permissions (we overload the WRITE_OWNER bit for this) or it
654 * should be one of the ALL/EXECUTE/READ/WRITE bits. Arrange for this
655 * to be so. Any other bits override the UNIX_ACCESS_NONE bit.
659 * Convert GENERIC bits to specific bits.
662 se_map_generic(&psa->info.mask, &file_generic_mapping);
664 psa->info.mask &= (UNIX_ACCESS_NONE|FILE_ALL_ACCESS);
666 if(psa->info.mask != UNIX_ACCESS_NONE)
667 psa->info.mask &= ~UNIX_ACCESS_NONE;
671 * Deal with the fact that NT 4.x re-writes the canonical format
672 * that we return for default ACLs. If a directory ACE is identical
673 * to a inherited directory ACE then NT changes the bits so that the
674 * first ACE is set to OI|IO and the second ACE for this SID is set
675 * to CI. We need to repair this. JRA.
678 for(i = 0; i < dacl->num_aces; i++) {
679 SEC_ACE *psa1 = &dacl->ace[i];
681 for (j = i + 1; j < dacl->num_aces; j++) {
682 SEC_ACE *psa2 = &dacl->ace[j];
684 if (psa1->info.mask != psa2->info.mask)
685 continue;
687 if (!sid_equal(&psa1->sid, &psa2->sid))
688 continue;
691 * Ok - permission bits and SIDs are equal.
692 * Check if flags were re-written.
695 if (psa1->flags & SEC_ACE_FLAG_INHERIT_ONLY) {
697 psa1->flags |= (psa2->flags & (SEC_ACE_FLAG_CONTAINER_INHERIT|SEC_ACE_FLAG_OBJECT_INHERIT));
698 psa2->flags &= ~(SEC_ACE_FLAG_CONTAINER_INHERIT|SEC_ACE_FLAG_OBJECT_INHERIT);
700 } else if (psa2->flags & SEC_ACE_FLAG_INHERIT_ONLY) {
702 psa2->flags |= (psa1->flags & (SEC_ACE_FLAG_CONTAINER_INHERIT|SEC_ACE_FLAG_OBJECT_INHERIT));
703 psa1->flags &= ~(SEC_ACE_FLAG_CONTAINER_INHERIT|SEC_ACE_FLAG_OBJECT_INHERIT);
709 for(i = 0; i < dacl->num_aces; i++) {
710 enum SID_NAME_USE sid_type;
711 SEC_ACE *psa = &dacl->ace[i];
714 * Create a cannon_ace entry representing this NT DACL ACE.
717 if ((current_ace = (canon_ace *)malloc(sizeof(canon_ace))) == NULL) {
718 free_canon_ace_list(file_ace);
719 free_canon_ace_list(dir_ace);
720 DEBUG(0,("create_canon_ace_lists: malloc fail.\n"));
721 return False;
724 ZERO_STRUCTP(current_ace);
726 sid_copy(&current_ace->sid, &psa->sid);
729 * Try and work out if the SID is a user or group
730 * as we need to flag these differently for POSIX.
733 if( sid_equal(&current_ace->sid, &global_sid_World)) {
734 current_ace->owner_type = WORLD_ACE;
735 current_ace->unix_ug.world = -1;
736 } else if (sid_to_uid( &current_ace->sid, &current_ace->unix_ug.uid, &sid_type)) {
737 current_ace->owner_type = UID_ACE;
738 } else if (sid_to_gid( &current_ace->sid, &current_ace->unix_ug.gid, &sid_type)) {
739 current_ace->owner_type = GID_ACE;
740 } else {
741 fstring str;
743 free_canon_ace_list(file_ace);
744 free_canon_ace_list(dir_ace);
745 free(current_ace);
746 DEBUG(0,("create_canon_ace_lists: unable to map SID %s to uid or gid.\n",
747 sid_to_string(str, &current_ace->sid) ));
748 return False;
752 * Map the given NT permissions into a UNIX mode_t containing only
753 * S_I(R|W|X)USR bits.
756 current_ace->perms |= map_nt_perms( psa->info, S_IRUSR);
757 current_ace->attr = (psa->type == SEC_ACE_TYPE_ACCESS_ALLOWED) ? ALLOW_ACE : DENY_ACE;
760 * Now note what kind of a POSIX ACL this should map to.
763 if(sid_equal(&current_ace->sid, pfile_owner_sid)) {
765 current_ace->type = SMB_ACL_USER_OBJ;
767 } else if( sid_equal(&current_ace->sid, pfile_grp_sid)) {
769 current_ace->type = SMB_ACL_GROUP_OBJ;
771 } else if( sid_equal(&current_ace->sid, &global_sid_World)) {
773 current_ace->type = SMB_ACL_OTHER;
775 } else {
777 * Could be a SMB_ACL_USER or SMB_ACL_GROUP. Check by
778 * looking at owner_type.
781 current_ace->type = (current_ace->owner_type == UID_ACE) ? SMB_ACL_USER : SMB_ACL_GROUP;
785 * Now add the created ace to either the file list, the directory
786 * list, or both. We *MUST* preserve the order here (hence we use
787 * DLIST_ADD_END) as NT ACLs are order dependent.
790 if (fsp->is_directory) {
793 * We can only add to the default POSIX ACE list if the ACE is
794 * designed to be inherited by both files and directories.
797 if ((psa->flags & (SEC_ACE_FLAG_OBJECT_INHERIT|SEC_ACE_FLAG_CONTAINER_INHERIT)) ==
798 (SEC_ACE_FLAG_OBJECT_INHERIT|SEC_ACE_FLAG_CONTAINER_INHERIT)) {
800 DLIST_ADD_END(dir_ace, current_ace, tmp_ace);
803 * Note if this was an allow ace. We can't process
804 * any further deny ace's after this.
807 if (current_ace->attr == ALLOW_ACE)
808 got_dir_allow = True;
810 if ((current_ace->attr == DENY_ACE) && got_dir_allow) {
811 DEBUG(0,("create_canon_ace_lists: malformed ACL in inheritable ACL ! \
812 Deny entry after Allow entry. Failing to set on file %s.\n", fsp->fsp_name ));
813 free_canon_ace_list(file_ace);
814 free_canon_ace_list(dir_ace);
815 free(current_ace);
816 return False;
819 if( DEBUGLVL( 10 )) {
820 dbgtext("create_canon_ace_lists: adding dir ACL:\n");
821 print_canon_ace( current_ace, 0);
825 * If this is not an inherit only ACE we need to add a duplicate
826 * to the file acl.
829 if (!(psa->flags & SEC_ACE_FLAG_INHERIT_ONLY)) {
830 canon_ace *dup_ace = dup_canon_ace(current_ace);
832 if (!dup_ace) {
833 DEBUG(0,("create_canon_ace_lists: malloc fail !\n"));
834 free_canon_ace_list(file_ace);
835 free_canon_ace_list(dir_ace);
836 return False;
839 current_ace = dup_ace;
840 } else {
841 current_ace = NULL;
847 * Only add to the file ACL if not inherit only.
850 if (!(psa->flags & SEC_ACE_FLAG_INHERIT_ONLY)) {
851 DLIST_ADD_END(file_ace, current_ace, tmp_ace);
854 * Note if this was an allow ace. We can't process
855 * any further deny ace's after this.
858 if (current_ace->attr == ALLOW_ACE)
859 got_file_allow = True;
861 if ((current_ace->attr == DENY_ACE) && got_file_allow) {
862 DEBUG(0,("create_canon_ace_lists: malformed ACL in file ACL ! \
863 Deny entry after Allow entry. Failing to set on file %s.\n", fsp->fsp_name ));
864 free_canon_ace_list(file_ace);
865 free_canon_ace_list(dir_ace);
866 free(current_ace);
867 return False;
870 if( DEBUGLVL( 10 )) {
871 dbgtext("create_canon_ace_lists: adding file ACL:\n");
872 print_canon_ace( current_ace, 0);
874 all_aces_are_inherit_only = False;
875 current_ace = NULL;
879 * Free if ACE was not added.
882 if (current_ace)
883 free(current_ace);
886 if (fsp->is_directory && all_aces_are_inherit_only) {
888 * Windows 2000 is doing one of these weird 'inherit acl'
889 * traverses to conserve NTFS ACL resources. Just pretend
890 * there was no DACL sent. JRA.
893 DEBUG(10,("create_canon_ace_lists: Win2k inherit acl traverse. Ignoring DACL.\n"));
894 free_canon_ace_list(file_ace);
895 free_canon_ace_list(dir_ace);
896 file_ace = NULL;
897 dir_ace = NULL;
900 *ppfile_ace = file_ace;
901 *ppdir_ace = dir_ace;
903 return True;
906 /****************************************************************************
907 Check if a given uid/SID is in a group gid/SID. This is probably very
908 expensive and will need optimisation. A *lot* of optimisation :-). JRA.
909 ****************************************************************************/
911 static BOOL uid_entry_in_group( canon_ace *uid_ace, canon_ace *group_ace )
913 extern DOM_SID global_sid_World;
914 struct passwd *pass = NULL;
915 struct group *gptr = NULL;
917 /* "Everyone" always matches every uid. */
919 if (sid_equal(&group_ace->sid, &global_sid_World))
920 return True;
922 if (!(pass = sys_getpwuid(uid_ace->unix_ug.uid)))
923 return False;
925 if (!(gptr = getgrgid(group_ace->unix_ug.gid)))
926 return False;
929 * Due to the winbind interfaces we need to do this via names,
930 * not uids/gids.
933 return user_in_group_list(pass->pw_name, gptr->gr_name );
936 /****************************************************************************
937 ASCII art time again... JRA :-).
939 We have 3 cases to process when moving from an NT ACL to a POSIX ACL. Firstly,
940 we insist the ACL is in canonical form (ie. all DENY entries preceede ALLOW
941 entries). Secondly, the merge code has ensured that all duplicate SID entries for
942 allow or deny have been merged, so the same SID can only appear once in the deny
943 list or once in the allow list.
945 We then process as follows :
947 ---------------------------------------------------------------------------
948 First pass - look for a Everyone DENY entry.
950 If it is deny all (rwx) trunate the list at this point.
951 Else, walk the list from this point and use the deny permissions of this
952 entry as a mask on all following allow entries. Finally, delete
953 the Everyone DENY entry (we have applied it to everything possible).
955 In addition, in this pass we remove any DENY entries that have
956 no permissions (ie. they are a DENY nothing).
957 ---------------------------------------------------------------------------
958 Second pass - only deal with deny user entries.
960 DENY user1 (perms XXX)
962 new_perms = 0
963 for all following allow group entries where user1 is in group
964 new_perms |= group_perms;
966 user1 entry perms = new_perms & ~ XXX;
968 Convert the deny entry to an allow entry with the new perms and
969 push to the end of the list. Note if the user was in no groups
970 this maps to a specific allow nothing entry for this user.
972 The common case from the NT ACL choser (userX deny all) is
973 optimised so we don't do the group lookup - we just map to
974 an allow nothing entry.
976 What we're doing here is inferring the allow permissions the
977 person setting the ACE on user1 wanted by looking at the allow
978 permissions on the groups the user is currently in. This will
979 be a snapshot, depending on group membership but is the best
980 we can do and has the advantage of failing closed rather than
981 open.
982 ---------------------------------------------------------------------------
983 Third pass - only deal with deny group entries.
985 DENY group1 (perms XXX)
987 for all following allow user entries where user is in group1
988 user entry perms = user entry perms & ~ XXX;
990 If there is a group Everyone allow entry with permissions YYY,
991 convert the group1 entry to an allow entry and modify its
992 permissions to be :
994 new_perms = YYY & ~ XXX
996 and push to the end of the list.
998 If there is no group Everyone allow entry then convert the
999 group1 entry to a allow nothing entry and push to the end of the list.
1001 Note that the common case from the NT ACL choser (groupX deny all)
1002 cannot be optimised here as we need to modify user entries who are
1003 in the group to change them to a deny all also.
1005 What we're doing here is modifying the allow permissions of
1006 user entries (which are more specific in POSIX ACLs) to mask
1007 out the explicit deny set on the group they are in. This will
1008 be a snapshot depending on current group membership but is the
1009 best we can do and has the advantage of failing closed rather
1010 than open.
1011 ---------------------------------------------------------------------------
1013 Note we *MUST* do the deny user pass first as this will convert deny user
1014 entries into allow user entries which can then be processed by the deny
1015 group pass.
1017 The above algorithm took a *lot* of thinking about - hence this
1018 explaination :-). JRA.
1019 ****************************************************************************/
1021 /****************************************************************************
1022 Process a canon_ace list entries. This is very complex code. We need
1023 to go through and remove the "deny" permissions from any allow entry that matches
1024 the id of this entry. We have already refused any NT ACL that wasn't in correct
1025 order (DENY followed by ALLOW). If any allow entry ends up with zero permissions,
1026 we just remove it (to fail safe). We have already removed any duplicate ace
1027 entries. Treat an "Everyone" DENY_ACE as a special case - use it to mask all
1028 allow entries.
1029 ****************************************************************************/
1031 static void process_deny_list( canon_ace **pp_ace_list )
1033 extern DOM_SID global_sid_World;
1034 canon_ace *ace_list = *pp_ace_list;
1035 canon_ace *curr_ace = NULL;
1036 canon_ace *curr_ace_next = NULL;
1038 /* Pass 1 above - look for an Everyone, deny entry. */
1040 for (curr_ace = ace_list; curr_ace; curr_ace = curr_ace_next) {
1041 canon_ace *allow_ace_p;
1043 curr_ace_next = curr_ace->next; /* So we can't lose the link. */
1045 if (curr_ace->attr != DENY_ACE)
1046 continue;
1048 if (curr_ace->perms == (mode_t)0) {
1050 /* Deny nothing entry - delete. */
1052 DLIST_REMOVE(ace_list, curr_ace);
1053 continue;
1056 if (!sid_equal(&curr_ace->sid, &global_sid_World))
1057 continue;
1059 /* JRATEST - assert. */
1060 SMB_ASSERT(curr_ace->owner_type == WORLD_ACE);
1062 if (curr_ace->perms == ALL_ACE_PERMS) {
1065 * Optimisation. This is a DENY_ALL to Everyone. Truncate the
1066 * list at this point including this entry.
1069 canon_ace *prev_entry = curr_ace->prev;
1071 free_canon_ace_list( curr_ace );
1072 if (prev_entry)
1073 prev_entry->next = NULL;
1074 else {
1075 /* We deleted the entire list. */
1076 ace_list = NULL;
1078 break;
1081 for (allow_ace_p = curr_ace->next; allow_ace_p; allow_ace_p = allow_ace_p->next) {
1084 * Only mask off allow entries.
1087 if (allow_ace_p->attr != ALLOW_ACE)
1088 continue;
1090 allow_ace_p->perms &= ~curr_ace->perms;
1094 * Now it's been applied, remove it.
1097 DLIST_REMOVE(ace_list, curr_ace);
1100 /* Pass 2 above - deal with deny user entries. */
1102 for (curr_ace = ace_list; curr_ace; curr_ace = curr_ace_next) {
1103 mode_t new_perms = (mode_t)0;
1104 canon_ace *allow_ace_p;
1105 canon_ace *tmp_ace;
1107 curr_ace_next = curr_ace->next; /* So we can't lose the link. */
1109 if (curr_ace->attr != DENY_ACE)
1110 continue;
1112 if (curr_ace->owner_type != UID_ACE)
1113 continue;
1115 if (curr_ace->perms == ALL_ACE_PERMS) {
1118 * Optimisation - this is a deny everything to this user.
1119 * Convert to an allow nothing and push to the end of the list.
1122 curr_ace->attr = ALLOW_ACE;
1123 curr_ace->perms = (mode_t)0;
1124 DLIST_DEMOTE(ace_list, curr_ace, tmp_ace);
1125 continue;
1128 for (allow_ace_p = curr_ace->next; allow_ace_p; allow_ace_p = allow_ace_p->next) {
1130 if (allow_ace_p->attr != ALLOW_ACE)
1131 continue;
1133 /* We process GID_ACE and WORLD_ACE entries only. */
1135 if (allow_ace_p->owner_type == UID_ACE)
1136 continue;
1138 if (uid_entry_in_group( curr_ace, allow_ace_p))
1139 new_perms |= allow_ace_p->perms;
1143 * Convert to a allow entry, modify the perms and push to the end
1144 * of the list.
1147 curr_ace->attr = ALLOW_ACE;
1148 curr_ace->perms = (new_perms & ~curr_ace->perms);
1149 DLIST_DEMOTE(ace_list, curr_ace, tmp_ace);
1152 /* Pass 3 above - deal with deny group entries. */
1154 for (curr_ace = ace_list; curr_ace; curr_ace = curr_ace_next) {
1155 canon_ace *tmp_ace;
1156 canon_ace *allow_ace_p;
1157 canon_ace *allow_everyone_p = NULL;
1159 curr_ace_next = curr_ace->next; /* So we can't lose the link. */
1161 if (curr_ace->attr != DENY_ACE)
1162 continue;
1164 if (curr_ace->owner_type != GID_ACE)
1165 continue;
1167 for (allow_ace_p = curr_ace->next; allow_ace_p; allow_ace_p = allow_ace_p->next) {
1169 if (allow_ace_p->attr != ALLOW_ACE)
1170 continue;
1172 /* Store a pointer to the Everyone allow, if it exists. */
1173 if (allow_ace_p->owner_type == WORLD_ACE)
1174 allow_everyone_p = allow_ace_p;
1176 /* We process UID_ACE entries only. */
1178 if (allow_ace_p->owner_type != UID_ACE)
1179 continue;
1181 /* Mask off the deny group perms. */
1183 if (uid_entry_in_group( allow_ace_p, curr_ace))
1184 allow_ace_p->perms &= ~curr_ace->perms;
1188 * Convert the deny to an allow with the correct perms and
1189 * push to the end of the list.
1192 curr_ace->attr = ALLOW_ACE;
1193 if (allow_everyone_p)
1194 curr_ace->perms = allow_everyone_p->perms & ~curr_ace->perms;
1195 else
1196 curr_ace->perms = (mode_t)0;
1197 DLIST_DEMOTE(ace_list, curr_ace, tmp_ace);
1201 *pp_ace_list = ace_list;
1204 /****************************************************************************
1205 Create a default mode that will be used if a security descriptor entry has
1206 no user/group/world entries.
1207 ****************************************************************************/
1209 static mode_t create_default_mode(files_struct *fsp, BOOL interitable_mode)
1211 int snum = SNUM(fsp->conn);
1212 mode_t and_bits = (mode_t)0;
1213 mode_t or_bits = (mode_t)0;
1214 mode_t mode = interitable_mode ? unix_mode( fsp->conn, FILE_ATTRIBUTE_ARCHIVE, fsp->fsp_name) : S_IRUSR;
1216 if (fsp->is_directory)
1217 mode |= (S_IWUSR|S_IXUSR);
1220 * Now AND with the create mode/directory mode bits then OR with the
1221 * force create mode/force directory mode bits.
1224 if (fsp->is_directory) {
1225 and_bits = lp_dir_security_mask(snum);
1226 or_bits = lp_force_dir_security_mode(snum);
1227 } else {
1228 and_bits = lp_security_mask(snum);
1229 or_bits = lp_force_security_mode(snum);
1232 return ((mode & and_bits)|or_bits);
1235 /****************************************************************************
1236 Unpack a SEC_DESC into two canonical ace lists. We don't depend on this
1237 succeeding.
1238 ****************************************************************************/
1240 static BOOL unpack_canon_ace(files_struct *fsp,
1241 SMB_STRUCT_STAT *pst,
1242 DOM_SID *pfile_owner_sid,
1243 DOM_SID *pfile_grp_sid,
1244 canon_ace **ppfile_ace, canon_ace **ppdir_ace,
1245 uint32 security_info_sent, SEC_DESC *psd)
1247 canon_ace *file_ace = NULL;
1248 canon_ace *dir_ace = NULL;
1250 *ppfile_ace = NULL;
1251 *ppdir_ace = NULL;
1253 if(security_info_sent == 0) {
1254 DEBUG(0,("unpack_canon_ace: no security info sent !\n"));
1255 return False;
1259 * If no DACL then this is a chown only security descriptor.
1262 if(!(security_info_sent & DACL_SECURITY_INFORMATION) || !psd->dacl)
1263 return True;
1266 * Now go through the DACL and create the canon_ace lists.
1269 if (!create_canon_ace_lists( fsp, pfile_owner_sid, pfile_grp_sid,
1270 &file_ace, &dir_ace, psd->dacl))
1271 return False;
1273 if ((file_ace == NULL) && (dir_ace == NULL)) {
1274 /* W2K traverse DACL set - ignore. */
1275 return True;
1279 * Go through the canon_ace list and merge entries
1280 * belonging to identical users of identical allow or deny type.
1281 * We can do this as all deny entries come first, followed by
1282 * all allow entries (we have mandated this before accepting this acl).
1285 print_canon_ace_list( "file ace - before merge", file_ace);
1286 merge_aces( &file_ace );
1288 print_canon_ace_list( "dir ace - before merge", dir_ace);
1289 merge_aces( &dir_ace );
1292 * NT ACLs are order dependent. Go through the acl lists and
1293 * process DENY entries by masking the allow entries.
1296 print_canon_ace_list( "file ace - before deny", file_ace);
1297 process_deny_list( &file_ace);
1299 print_canon_ace_list( "dir ace - before deny", dir_ace);
1300 process_deny_list( &dir_ace);
1303 * A well formed POSIX file or default ACL has at least 3 entries, a
1304 * SMB_ACL_USER_OBJ, SMB_ACL_GROUP_OBJ, SMB_ACL_OTHER_OBJ
1305 * and optionally a mask entry. Ensure this is the case.
1308 print_canon_ace_list( "file ace - before valid", file_ace);
1311 * A default 3 element mode entry for a file should be r-- --- ---.
1312 * A default 3 element mode entry for a directory should be rwx --- ---.
1315 pst->st_mode = create_default_mode(fsp, False);
1317 if (!ensure_canon_entry_valid(&file_ace, fsp, pfile_owner_sid, pfile_grp_sid, pst, True)) {
1318 free_canon_ace_list(file_ace);
1319 free_canon_ace_list(dir_ace);
1320 return False;
1323 print_canon_ace_list( "dir ace - before valid", dir_ace);
1326 * A default inheritable 3 element mode entry for a directory should be the
1327 * mode Samba will use to create a file within. Ensure user rwx bits are set if
1328 * it's a directory.
1331 pst->st_mode = create_default_mode(fsp, True);
1333 if (!ensure_canon_entry_valid(&dir_ace, fsp, pfile_owner_sid, pfile_grp_sid, pst, True)) {
1334 free_canon_ace_list(file_ace);
1335 free_canon_ace_list(dir_ace);
1336 return False;
1339 print_canon_ace_list( "file ace - return", file_ace);
1340 print_canon_ace_list( "dir ace - return", dir_ace);
1342 *ppfile_ace = file_ace;
1343 *ppdir_ace = dir_ace;
1344 return True;
1348 /******************************************************************************
1349 When returning permissions, try and fit NT display
1350 semantics if possible. Note the the canon_entries here must have been malloced.
1351 The list format should be - first entry = owner, followed by group and other user
1352 entries, last entry = other.
1354 Note that this doesn't exactly match the NT semantics for an ACL. As POSIX entries
1355 are not ordered, and match on the most specific entry rather than walking a list,
1356 then a simple POSIX permission of rw-r--r-- should really map to 6 entries,
1358 Entry 0: owner : deny all except read and write.
1359 Entry 1: group : deny all except read.
1360 Entry 2: Everyone : deny all except read.
1361 Entry 3: owner : allow read and write.
1362 Entry 4: group : allow read.
1363 Entry 5: Everyone : allow read.
1365 But NT cannot display this in their ACL editor !
1366 ********************************************************************************/
1368 static void arrange_posix_perms( char *filename, canon_ace **pp_list_head)
1370 canon_ace *list_head = *pp_list_head;
1371 canon_ace *owner_ace = NULL;
1372 canon_ace *other_ace = NULL;
1373 canon_ace *ace = NULL;
1375 for (ace = list_head; ace; ace = ace->next) {
1376 if (ace->type == SMB_ACL_USER_OBJ)
1377 owner_ace = ace;
1378 else if (ace->type == SMB_ACL_OTHER) {
1379 /* Last ace - this is "other" */
1380 other_ace = ace;
1384 if (!owner_ace || !other_ace) {
1385 DEBUG(0,("arrange_posix_perms: Invalid POSIX permissions for file %s, missing owner or other.\n",
1386 filename ));
1387 return;
1391 * The POSIX algorithm applies to owner first, and other last,
1392 * so ensure they are arranged in this order.
1395 if (owner_ace) {
1396 DLIST_PROMOTE(list_head, owner_ace);
1399 if (other_ace) {
1400 DLIST_DEMOTE(list_head, other_ace, ace);
1403 /* We have probably changed the head of the list. */
1405 *pp_list_head = list_head;
1408 /****************************************************************************
1409 Create a linked list of canonical ACE entries.
1410 ****************************************************************************/
1412 static canon_ace *canonicalise_acl( files_struct *fsp, SMB_ACL_T posix_acl, SMB_STRUCT_STAT *psbuf,
1413 DOM_SID *powner, DOM_SID *pgroup)
1415 extern DOM_SID global_sid_World;
1416 mode_t acl_mask = (S_IRUSR|S_IWUSR|S_IXUSR);
1417 canon_ace *list_head = NULL;
1418 canon_ace *ace = NULL;
1419 canon_ace *next_ace = NULL;
1420 int entry_id = SMB_ACL_FIRST_ENTRY;
1421 SMB_ACL_ENTRY_T entry;
1422 size_t ace_count;
1424 while ( posix_acl && (sys_acl_get_entry(posix_acl, entry_id, &entry) == 1)) {
1425 SMB_ACL_TAG_T tagtype;
1426 SMB_ACL_PERMSET_T permset;
1427 DOM_SID sid;
1428 posix_id unix_ug;
1429 enum ace_owner owner_type;
1431 /* get_next... */
1432 if (entry_id == SMB_ACL_FIRST_ENTRY)
1433 entry_id = SMB_ACL_NEXT_ENTRY;
1435 /* Is this a MASK entry ? */
1436 if (sys_acl_get_tag_type(entry, &tagtype) == -1)
1437 continue;
1439 if (sys_acl_get_permset(entry, &permset) == -1)
1440 continue;
1442 /* Decide which SID to use based on the ACL type. */
1443 switch(tagtype) {
1444 case SMB_ACL_USER_OBJ:
1445 /* Get the SID from the owner. */
1446 uid_to_sid( &sid, psbuf->st_uid );
1447 unix_ug.uid = psbuf->st_uid;
1448 owner_type = UID_ACE;
1449 break;
1450 case SMB_ACL_USER:
1452 uid_t *puid = (uid_t *)sys_acl_get_qualifier(entry);
1453 if (puid == NULL) {
1454 DEBUG(0,("canonicalise_acl: Failed to get uid.\n"));
1455 continue;
1457 uid_to_sid( &sid, *puid);
1458 unix_ug.uid = *puid;
1459 owner_type = UID_ACE;
1460 sys_acl_free_qualifier((void *)puid);
1461 break;
1463 case SMB_ACL_GROUP_OBJ:
1464 /* Get the SID from the owning group. */
1465 gid_to_sid( &sid, psbuf->st_gid );
1466 unix_ug.gid = psbuf->st_gid;
1467 owner_type = GID_ACE;
1468 break;
1469 case SMB_ACL_GROUP:
1471 gid_t *pgid = (gid_t *)sys_acl_get_qualifier(entry);
1472 if (pgid == NULL) {
1473 DEBUG(0,("canonicalise_acl: Failed to get gid.\n"));
1474 continue;
1476 gid_to_sid( &sid, *pgid);
1477 unix_ug.gid = *pgid;
1478 owner_type = GID_ACE;
1479 sys_acl_free_qualifier((void *)pgid);
1480 break;
1482 case SMB_ACL_MASK:
1483 acl_mask = convert_permset_to_mode_t(permset);
1484 continue; /* Don't count the mask as an entry. */
1485 case SMB_ACL_OTHER:
1486 /* Use the Everyone SID */
1487 sid = global_sid_World;
1488 unix_ug.world = -1;
1489 owner_type = WORLD_ACE;
1490 break;
1491 default:
1492 DEBUG(0,("canonicalise_acl: Unknown tagtype %u\n", (unsigned int)tagtype));
1493 continue;
1497 * Add this entry to the list.
1500 if ((ace = (canon_ace *)malloc(sizeof(canon_ace))) == NULL)
1501 goto fail;
1503 ZERO_STRUCTP(ace);
1504 ace->type = tagtype;
1505 ace->perms = convert_permset_to_mode_t(permset);
1506 ace->attr = ALLOW_ACE;
1507 ace->sid = sid;
1508 ace->unix_ug = unix_ug;
1509 ace->owner_type = owner_type;
1511 DLIST_ADD(list_head, ace);
1515 * This next call will ensure we have at least a user/group/world set.
1518 if (!ensure_canon_entry_valid(&list_head, fsp, powner, pgroup, psbuf, False))
1519 goto fail;
1521 arrange_posix_perms(fsp->fsp_name,&list_head );
1524 * Now go through the list, masking the permissions with the
1525 * acl_mask. Ensure all DENY Entries are at the start of the list.
1528 DEBUG(10,("canonicalise_acl: ace entries before arrange :\n"));
1530 for ( ace_count = 0, ace = list_head; ace; ace = next_ace, ace_count++) {
1531 next_ace = ace->next;
1533 /* Masks are only applied to entries other than USER_OBJ and OTHER. */
1534 if (ace->type != SMB_ACL_OTHER && ace->type != SMB_ACL_USER_OBJ)
1535 ace->perms &= acl_mask;
1537 if (ace->perms == 0) {
1538 DLIST_PROMOTE(list_head, ace);
1541 if( DEBUGLVL( 10 ) ) {
1542 print_canon_ace(ace, ace_count);
1546 print_canon_ace_list( "canonicalise_acl: ace entries after arrange", list_head );
1548 return list_head;
1550 fail:
1552 free_canon_ace_list(list_head);
1553 return NULL;
1556 /****************************************************************************
1557 Attempt to apply an ACL to a file or directory.
1558 ****************************************************************************/
1560 static BOOL set_canon_ace_list(files_struct *fsp, canon_ace *the_ace, BOOL default_ace, BOOL *pacl_set_support)
1562 BOOL ret = False;
1563 SMB_ACL_T the_acl = sys_acl_init((int)count_canon_ace_list(the_ace) + 1);
1564 canon_ace *p_ace;
1565 int i;
1566 SMB_ACL_ENTRY_T mask_entry;
1567 SMB_ACL_PERMSET_T mask_permset;
1568 SMB_ACL_TYPE_T the_acl_type = (default_ace ? SMB_ACL_TYPE_DEFAULT : SMB_ACL_TYPE_ACCESS);
1570 if (the_acl == NULL) {
1572 if (errno != ENOSYS) {
1574 * Only print this error message if we have some kind of ACL
1575 * support that's not working. Otherwise we would always get this.
1577 DEBUG(0,("set_canon_ace_list: Unable to init %s ACL. (%s)\n",
1578 default_ace ? "default" : "file", strerror(errno) ));
1580 *pacl_set_support = False;
1581 return False;
1584 for (i = 0, p_ace = the_ace; p_ace; p_ace = p_ace->next, i++ ) {
1585 SMB_ACL_ENTRY_T the_entry;
1586 SMB_ACL_PERMSET_T the_permset;
1589 * Get the entry for this ACE.
1592 if (sys_acl_create_entry( &the_acl, &the_entry) == -1) {
1593 DEBUG(0,("set_canon_ace_list: Failed to create entry %d. (%s)\n",
1594 i, strerror(errno) ));
1595 goto done;
1599 * Ok - we now know the ACL calls should be working, don't
1600 * allow fallback to chmod.
1603 *pacl_set_support = True;
1606 * Initialise the entry from the canon_ace.
1610 * First tell the entry what type of ACE this is.
1613 if (sys_acl_set_tag_type(the_entry, p_ace->type) == -1) {
1614 DEBUG(0,("set_canon_ace_list: Failed to set tag type on entry %d. (%s)\n",
1615 i, strerror(errno) ));
1616 goto done;
1620 * Only set the qualifier (user or group id) if the entry is a user
1621 * or group id ACE.
1624 if ((p_ace->type == SMB_ACL_USER) || (p_ace->type == SMB_ACL_GROUP)) {
1625 if (sys_acl_set_qualifier(the_entry,(void *)&p_ace->unix_ug.uid) == -1) {
1626 DEBUG(0,("set_canon_ace_list: Failed to set qualifier on entry %d. (%s)\n",
1627 i, strerror(errno) ));
1628 goto done;
1633 * Convert the mode_t perms in the canon_ace to a POSIX permset.
1636 if (sys_acl_get_permset(the_entry, &the_permset) == -1) {
1637 DEBUG(0,("set_canon_ace_list: Failed to get permset on entry %d. (%s)\n",
1638 i, strerror(errno) ));
1639 goto done;
1642 if (map_acl_perms_to_permset(p_ace->perms, &the_permset) == -1) {
1643 DEBUG(0,("set_canon_ace_list: Failed to create permset for mode (%u) on entry %d. (%s)\n",
1644 (unsigned int)p_ace->perms, i, strerror(errno) ));
1645 goto done;
1649 * ..and apply them to the entry.
1652 if (sys_acl_set_permset(the_entry, the_permset) == -1) {
1653 DEBUG(0,("set_canon_ace_list: Failed to add permset on entry %d. (%s)\n",
1654 i, strerror(errno) ));
1655 goto done;
1658 if( DEBUGLVL( 10 ))
1659 print_canon_ace( p_ace, i);
1663 * Add in a mask of rwx.
1666 if (sys_acl_create_entry( &the_acl, &mask_entry) == -1) {
1667 DEBUG(0,("set_canon_ace_list: Failed to create mask entry. (%s)\n", strerror(errno) ));
1668 goto done;
1671 if (sys_acl_set_tag_type(mask_entry, SMB_ACL_MASK) == -1) {
1672 DEBUG(0,("set_canon_ace_list: Failed to set tag type on mask entry. (%s)\n",strerror(errno) ));
1673 goto done;
1676 if (sys_acl_get_permset(mask_entry, &mask_permset) == -1) {
1677 DEBUG(0,("set_canon_ace_list: Failed to get mask permset. (%s)\n", strerror(errno) ));
1678 goto done;
1681 if (map_acl_perms_to_permset(S_IRUSR|S_IWUSR|S_IXUSR, &mask_permset) == -1) {
1682 DEBUG(0,("set_canon_ace_list: Failed to create mask permset. (%s)\n", strerror(errno) ));
1683 goto done;
1686 if (sys_acl_set_permset(mask_entry, mask_permset) == -1) {
1687 DEBUG(0,("set_canon_ace_list: Failed to add mask permset. (%s)\n", strerror(errno) ));
1688 goto done;
1692 * Check if the ACL is valid.
1695 if (sys_acl_valid(the_acl) == -1) {
1696 DEBUG(0,("set_canon_ace_list: ACL type (%s) is invalid for set (%s).\n",
1697 the_acl_type == SMB_ACL_TYPE_DEFAULT ? "directory default" : "file",
1698 strerror(errno) ));
1699 goto done;
1703 * Finally apply it to the file or directory.
1706 if(default_ace || fsp->is_directory || fsp->fd == -1) {
1707 if (sys_acl_set_file(fsp->fsp_name, the_acl_type, the_acl) == -1) {
1708 DEBUG(0,("set_canon_ace_list: sys_acl_set_file type %s failed for file %s (%s).\n",
1709 the_acl_type == SMB_ACL_TYPE_DEFAULT ? "directory default" : "file",
1710 fsp->fsp_name, strerror(errno) ));
1711 goto done;
1713 } else {
1714 if (sys_acl_set_fd(fsp->fd, the_acl) == -1) {
1715 DEBUG(0,("set_canon_ace_list: sys_acl_set_file failed for file %s (%s).\n",
1716 fsp->fsp_name, strerror(errno) ));
1717 goto done;
1721 ret = True;
1723 done:
1725 if (the_acl != NULL)
1726 sys_acl_free_acl(the_acl);
1728 return ret;
1731 /****************************************************************************
1732 Convert a canon_ace to a generic 3 element permission - if possible.
1733 ****************************************************************************/
1735 #define MAP_PERM(p,mask,result) (((p) & (mask)) ? (result) : 0 )
1737 static BOOL convert_canon_ace_to_posix_perms( files_struct *fsp, canon_ace *file_ace_list, mode_t *posix_perms)
1739 int snum = SNUM(fsp->conn);
1740 size_t ace_count = count_canon_ace_list(file_ace_list);
1741 canon_ace *ace_p;
1742 canon_ace *owner_ace = NULL;
1743 canon_ace *group_ace = NULL;
1744 canon_ace *other_ace = NULL;
1745 mode_t and_bits;
1746 mode_t or_bits;
1748 if (ace_count != 3) {
1749 DEBUG(3,("convert_canon_ace_to_posix_perms: Too many ACE entries for file %s to convert to \
1750 posix perms.\n", fsp->fsp_name ));
1751 return False;
1754 for (ace_p = file_ace_list; ace_p; ace_p = ace_p->next) {
1755 if (ace_p->owner_type == UID_ACE)
1756 owner_ace = ace_p;
1757 else if (ace_p->owner_type == GID_ACE)
1758 group_ace = ace_p;
1759 else if (ace_p->owner_type == WORLD_ACE)
1760 other_ace = ace_p;
1763 if (!owner_ace || !group_ace || !other_ace) {
1764 DEBUG(3,("convert_canon_ace_to_posix_perms: Can't get standard entries for file %s.\n",
1765 fsp->fsp_name ));
1766 return False;
1769 *posix_perms = (mode_t)0;
1771 *posix_perms |= owner_ace->perms;
1772 *posix_perms |= MAP_PERM(group_ace->perms, S_IRUSR, S_IRGRP);
1773 *posix_perms |= MAP_PERM(group_ace->perms, S_IWUSR, S_IWGRP);
1774 *posix_perms |= MAP_PERM(group_ace->perms, S_IXUSR, S_IXGRP);
1775 *posix_perms |= MAP_PERM(other_ace->perms, S_IRUSR, S_IROTH);
1776 *posix_perms |= MAP_PERM(other_ace->perms, S_IWUSR, S_IWOTH);
1777 *posix_perms |= MAP_PERM(other_ace->perms, S_IXUSR, S_IXOTH);
1779 /* The owner must have at least read access. */
1781 *posix_perms |= S_IRUSR;
1782 if (fsp->is_directory)
1783 *posix_perms |= (S_IWUSR|S_IXUSR);
1785 /* If requested apply the masks. */
1787 /* Get the initial bits to apply. */
1789 if (fsp->is_directory) {
1790 and_bits = lp_dir_security_mask(snum);
1791 or_bits = lp_force_dir_security_mode(snum);
1792 } else {
1793 and_bits = lp_security_mask(snum);
1794 or_bits = lp_force_security_mode(snum);
1797 *posix_perms = (((*posix_perms) & and_bits)|or_bits);
1799 DEBUG(10,("convert_canon_ace_to_posix_perms: converted u=%o,g=%o,w=%o to perm=0%o for file %s.\n",
1800 (int)owner_ace->perms, (int)group_ace->perms, (int)other_ace->perms, (int)*posix_perms,
1801 fsp->fsp_name ));
1803 return True;
1806 static int nt_ace_comp( SEC_ACE *a1, SEC_ACE *a2)
1808 if (a1->type == a2->type)
1809 return 0;
1811 if (a1->type == SEC_ACE_TYPE_ACCESS_DENIED && a2->type == SEC_ACE_TYPE_ACCESS_ALLOWED)
1812 return -1;
1813 return 1;
1816 /****************************************************************************
1817 Reply to query a security descriptor from an fsp. If it succeeds it allocates
1818 the space for the return elements and returns the size needed to return the
1819 security descriptor. This should be the only external function needed for
1820 the UNIX style get ACL.
1821 ****************************************************************************/
1823 size_t get_nt_acl(files_struct *fsp, SEC_DESC **ppdesc)
1825 SMB_STRUCT_STAT sbuf;
1826 SEC_ACE *nt_ace_list = NULL;
1827 DOM_SID owner_sid;
1828 DOM_SID group_sid;
1829 size_t sd_size = 0;
1830 SEC_ACL *psa = NULL;
1831 size_t num_acls = 0;
1832 size_t num_dir_acls = 0;
1833 size_t num_aces = 0;
1834 SMB_ACL_T posix_acl = NULL;
1835 SMB_ACL_T dir_acl = NULL;
1836 canon_ace *file_ace = NULL;
1837 canon_ace *dir_ace = NULL;
1839 *ppdesc = NULL;
1841 DEBUG(10,("get_nt_acl: called for file %s\n", fsp->fsp_name ));
1843 if(fsp->is_directory || fsp->fd == -1) {
1845 /* Get the stat struct for the owner info. */
1846 if(vfs_stat(fsp->conn,fsp->fsp_name, &sbuf) != 0) {
1847 return 0;
1850 * Get the ACL from the path.
1853 posix_acl = sys_acl_get_file(fsp->fsp_name, SMB_ACL_TYPE_ACCESS);
1856 * If it's a directory get the default POSIX ACL.
1859 if(fsp->is_directory)
1860 dir_acl = sys_acl_get_file(fsp->fsp_name, SMB_ACL_TYPE_DEFAULT);
1862 } else {
1864 /* Get the stat struct for the owner info. */
1865 if(vfs_fstat(fsp,fsp->fd,&sbuf) != 0) {
1866 return 0;
1869 * Get the ACL from the fd.
1871 posix_acl = sys_acl_get_fd(fsp->fd);
1874 DEBUG(5,("get_nt_acl : file ACL %s, directory ACL %s\n",
1875 posix_acl ? "present" : "absent",
1876 dir_acl ? "present" : "absent" ));
1879 * Get the owner, group and world SIDs.
1882 create_file_sids(&sbuf, &owner_sid, &group_sid);
1884 /* Create the canon_ace lists. */
1885 file_ace = canonicalise_acl( fsp, posix_acl, &sbuf, &owner_sid, &group_sid);
1886 num_acls = count_canon_ace_list(file_ace);
1888 /* We must have *some* ACLS. */
1890 if (num_acls == 0) {
1891 DEBUG(0,("get_nt_acl : No ACLs on file (%s) !\n", fsp->fsp_name ));
1892 return 0;
1895 if (fsp->is_directory) {
1897 * If we have to fake a default ACL then this is the mode to use.
1899 sbuf.st_mode = unix_mode( fsp->conn, FILE_ATTRIBUTE_ARCHIVE, fsp->fsp_name);
1901 dir_ace = canonicalise_acl(fsp, dir_acl, &sbuf, &owner_sid, &group_sid);
1902 num_dir_acls = count_canon_ace_list(dir_ace);
1905 /* Allocate the ace list. */
1906 if ((nt_ace_list = (SEC_ACE *)malloc((num_acls + num_dir_acls)* sizeof(SEC_ACE))) == NULL) {
1907 DEBUG(0,("get_nt_acl: Unable to malloc space for nt_ace_list.\n"));
1908 goto done;
1911 memset(nt_ace_list, '\0', (num_acls + num_dir_acls) * sizeof(SEC_ACE) );
1914 * Create the NT ACE list from the canonical ace lists.
1918 canon_ace *ace;
1919 int nt_acl_type;
1920 int i;
1922 ace = file_ace;
1924 for (i = 0; i < num_acls; i++, ace = ace->next) {
1925 SEC_ACCESS acc = map_canon_ace_perms(&nt_acl_type, &owner_sid, ace );
1926 init_sec_ace(&nt_ace_list[num_aces++], &ace->sid, nt_acl_type, acc, 0);
1929 ace = dir_ace;
1931 for (i = 0; i < num_dir_acls; i++, ace = ace->next) {
1932 SEC_ACCESS acc = map_canon_ace_perms(&nt_acl_type, &owner_sid, ace );
1933 init_sec_ace(&nt_ace_list[num_aces++], &ace->sid, nt_acl_type, acc,
1934 SEC_ACE_FLAG_OBJECT_INHERIT|SEC_ACE_FLAG_CONTAINER_INHERIT|SEC_ACE_FLAG_INHERIT_ONLY);
1938 * Sort to force deny entries to the front.
1941 if (num_acls + num_dir_acls)
1942 qsort( nt_ace_list, num_acls + num_dir_acls, sizeof(nt_ace_list[0]), QSORT_CAST nt_ace_comp);
1945 if (num_acls) {
1946 if((psa = make_sec_acl( main_loop_talloc_get(), ACL_REVISION, num_aces, nt_ace_list)) == NULL) {
1947 DEBUG(0,("get_nt_acl: Unable to malloc space for acl.\n"));
1948 goto done;
1952 *ppdesc = make_standard_sec_desc( main_loop_talloc_get(), &owner_sid, &group_sid, psa, &sd_size);
1954 if(!*ppdesc) {
1955 DEBUG(0,("get_nt_acl: Unable to malloc space for security descriptor.\n"));
1956 sd_size = 0;
1959 done:
1961 if (posix_acl)
1962 sys_acl_free_acl(posix_acl);
1963 if (dir_acl)
1964 sys_acl_free_acl(dir_acl);
1965 free_canon_ace_list(file_ace);
1966 free_canon_ace_list(dir_ace);
1967 if (nt_ace_list)
1968 free(nt_ace_list);
1970 return sd_size;
1973 /****************************************************************************
1974 Reply to set a security descriptor on an fsp. security_info_sent is the
1975 description of the following NT ACL.
1976 This should be the only external function needed for the UNIX style set ACL.
1977 ****************************************************************************/
1979 BOOL set_nt_acl(files_struct *fsp, uint32 security_info_sent, SEC_DESC *psd)
1981 connection_struct *conn = fsp->conn;
1982 uid_t user = (uid_t)-1;
1983 gid_t grp = (gid_t)-1;
1984 SMB_STRUCT_STAT sbuf;
1985 DOM_SID file_owner_sid;
1986 DOM_SID file_grp_sid;
1987 canon_ace *file_ace_list = NULL;
1988 canon_ace *dir_ace_list = NULL;
1989 BOOL acl_perms = False;
1990 mode_t orig_mode = (mode_t)0;
1991 uid_t orig_uid;
1992 gid_t orig_gid;
1994 DEBUG(10,("set_nt_acl: called for file %s\n", fsp->fsp_name ));
1997 * Get the current state of the file.
2000 if(fsp->is_directory || fsp->fd == -1) {
2001 if(vfs_stat(fsp->conn,fsp->fsp_name, &sbuf) != 0)
2002 return False;
2003 } else {
2004 if(vfs_fstat(fsp,fsp->fd,&sbuf) != 0)
2005 return False;
2008 /* Save the original elements we check against. */
2009 orig_mode = sbuf.st_mode;
2010 orig_uid = sbuf.st_uid;
2011 orig_gid = sbuf.st_gid;
2014 * Unpack the user/group/world id's.
2017 if (!unpack_nt_owners( &sbuf, &user, &grp, security_info_sent, psd))
2018 return False;
2021 * Do we need to chown ?
2024 if((user != (uid_t)-1 || grp != (uid_t)-1) && (orig_uid != user || orig_gid != grp)) {
2026 DEBUG(3,("set_nt_acl: chown %s. uid = %u, gid = %u.\n",
2027 fsp->fsp_name, (unsigned int)user, (unsigned int)grp ));
2029 if(vfs_chown( fsp->conn, fsp->fsp_name, user, grp) == -1) {
2030 DEBUG(3,("set_nt_acl: chown %s, %u, %u failed. Error = %s.\n",
2031 fsp->fsp_name, (unsigned int)user, (unsigned int)grp, strerror(errno) ));
2032 return False;
2036 * Recheck the current state of the file, which may have changed.
2037 * (suid/sgid bits, for instance)
2040 if(fsp->is_directory) {
2041 if(vfs_stat(fsp->conn, fsp->fsp_name, &sbuf) != 0) {
2042 return False;
2044 } else {
2046 int ret;
2048 if(fsp->fd == -1)
2049 ret = vfs_stat(fsp->conn, fsp->fsp_name, &sbuf);
2050 else
2051 ret = vfs_fstat(fsp,fsp->fd,&sbuf);
2053 if(ret != 0)
2054 return False;
2057 /* Save the original elements we check against. */
2058 orig_mode = sbuf.st_mode;
2059 orig_uid = sbuf.st_uid;
2060 orig_gid = sbuf.st_gid;
2063 create_file_sids(&sbuf, &file_owner_sid, &file_grp_sid);
2065 acl_perms = unpack_canon_ace( fsp, &sbuf, &file_owner_sid, &file_grp_sid,
2066 &file_ace_list, &dir_ace_list, security_info_sent, psd);
2068 if ((file_ace_list == NULL) && (dir_ace_list == NULL)) {
2069 /* W2K traverse DACL set - ignore. */
2070 return True;
2073 if (!acl_perms) {
2074 DEBUG(3,("set_nt_acl: cannot set permissions\n"));
2075 free_canon_ace_list(file_ace_list);
2076 free_canon_ace_list(dir_ace_list);
2077 return False;
2081 * Only change security if we got a DACL.
2084 if((security_info_sent & DACL_SECURITY_INFORMATION) && (psd->dacl != NULL)) {
2086 BOOL acl_set_support = False;
2087 BOOL ret = False;
2090 * Try using the POSIX ACL set first. Fall back to chmod if
2091 * we have no ACL support on this filesystem.
2094 if (acl_perms && file_ace_list) {
2095 ret = set_canon_ace_list(fsp, file_ace_list, False, &acl_set_support);
2096 if (acl_set_support && ret == False) {
2097 DEBUG(3,("set_nt_acl: failed to set file acl on file %s (%s).\n", fsp->fsp_name, strerror(errno) ));
2098 free_canon_ace_list(file_ace_list);
2099 free_canon_ace_list(dir_ace_list);
2100 return False;
2104 if (acl_perms && acl_set_support && fsp->is_directory) {
2105 if (dir_ace_list) {
2106 if (!set_canon_ace_list(fsp, dir_ace_list, True, &acl_set_support)) {
2107 DEBUG(3,("set_nt_acl: failed to set default acl on directory %s (%s).\n", fsp->fsp_name, strerror(errno) ));
2108 free_canon_ace_list(file_ace_list);
2109 free_canon_ace_list(dir_ace_list);
2110 return False;
2112 } else {
2115 * No default ACL - delete one if it exists.
2118 if (sys_acl_delete_def_file(fsp->fsp_name) == -1) {
2119 DEBUG(3,("set_nt_acl: sys_acl_delete_def_file failed (%s)\n", strerror(errno)));
2120 free_canon_ace_list(file_ace_list);
2121 return False;
2127 * If we cannot set using POSIX ACLs we fall back to checking if we need to chmod.
2130 if(!acl_set_support && acl_perms) {
2131 mode_t posix_perms;
2133 if (!convert_canon_ace_to_posix_perms( fsp, file_ace_list, &posix_perms)) {
2134 free_canon_ace_list(file_ace_list);
2135 free_canon_ace_list(dir_ace_list);
2136 DEBUG(3,("set_nt_acl: failed to convert file acl to posix permissions for file %s.\n",
2137 fsp->fsp_name ));
2138 return False;
2141 if (orig_mode != posix_perms) {
2143 DEBUG(3,("set_nt_acl: chmod %s. perms = 0%o.\n",
2144 fsp->fsp_name, (unsigned int)posix_perms ));
2146 if(conn->vfs_ops.chmod(conn,fsp->fsp_name, posix_perms) == -1) {
2147 DEBUG(3,("set_nt_acl: chmod %s, 0%o failed. Error = %s.\n",
2148 fsp->fsp_name, (unsigned int)posix_perms, strerror(errno) ));
2149 free_canon_ace_list(file_ace_list);
2150 free_canon_ace_list(dir_ace_list);
2151 return False;
2157 free_canon_ace_list(file_ace_list);
2158 free_canon_ace_list(dir_ace_list);
2160 return True;
2163 /****************************************************************************
2164 Do a chmod by setting the ACL USER_OBJ, GROUP_OBJ and OTHER bits in an ACL
2165 and set the mask to rwx. Needed to preserve complex ACLs set by NT.
2166 ****************************************************************************/
2168 static int chmod_acl_internals( SMB_ACL_T posix_acl, mode_t mode)
2170 int entry_id = SMB_ACL_FIRST_ENTRY;
2171 SMB_ACL_ENTRY_T entry;
2172 int num_entries = 0;
2174 while ( sys_acl_get_entry(posix_acl, entry_id, &entry) == 1) {
2175 SMB_ACL_TAG_T tagtype;
2176 SMB_ACL_PERMSET_T permset;
2177 mode_t perms;
2179 /* get_next... */
2180 if (entry_id == SMB_ACL_FIRST_ENTRY)
2181 entry_id = SMB_ACL_NEXT_ENTRY;
2183 if (sys_acl_get_tag_type(entry, &tagtype) == -1)
2184 return -1;
2186 if (sys_acl_get_permset(entry, &permset) == -1)
2187 return -1;
2189 num_entries++;
2191 switch(tagtype) {
2192 case SMB_ACL_USER_OBJ:
2193 perms = unix_perms_to_acl_perms(mode, S_IRUSR, S_IWUSR, S_IXUSR);
2194 break;
2195 case SMB_ACL_GROUP_OBJ:
2196 perms = unix_perms_to_acl_perms(mode, S_IRGRP, S_IWGRP, S_IXGRP);
2197 break;
2198 case SMB_ACL_MASK:
2199 perms = S_IRUSR|S_IWUSR|S_IXUSR;
2200 break;
2201 case SMB_ACL_OTHER:
2202 perms = unix_perms_to_acl_perms(mode, S_IROTH, S_IWOTH, S_IXOTH);
2203 break;
2204 default:
2205 continue;
2208 if (map_acl_perms_to_permset(perms, &permset) == -1)
2209 return -1;
2211 if (sys_acl_set_permset(entry, permset) == -1)
2212 return -1;
2216 * If this is a simple 3 element ACL then it's a standard
2217 * UNIX permission set. Just use chmod...
2220 if (num_entries == 3)
2221 return -1;
2223 return 0;
2226 /****************************************************************************
2227 Do a chmod by setting the ACL USER_OBJ, GROUP_OBJ and OTHER bits in an ACL
2228 and set the mask to rwx. Needed to preserve complex ACLs set by NT.
2229 Note that name is in UNIX character set.
2230 ****************************************************************************/
2232 int chmod_acl(const char *name, mode_t mode)
2234 SMB_ACL_T posix_acl = NULL;
2235 int ret = -1;
2237 if ((posix_acl = sys_acl_get_file(name, SMB_ACL_TYPE_ACCESS)) == NULL)
2238 return -1;
2240 if ((ret = chmod_acl_internals(posix_acl, mode)) == -1)
2241 goto done;
2243 ret = sys_acl_set_file(name, SMB_ACL_TYPE_ACCESS, posix_acl);
2245 done:
2247 sys_acl_free_acl(posix_acl);
2248 return ret;
2251 /****************************************************************************
2252 Do an fchmod by setting the ACL USER_OBJ, GROUP_OBJ and OTHER bits in an ACL
2253 and set the mask to rwx. Needed to preserve complex ACLs set by NT.
2254 ****************************************************************************/
2256 int fchmod_acl(int fd, mode_t mode)
2258 SMB_ACL_T posix_acl = NULL;
2259 int ret = -1;
2261 if ((posix_acl = sys_acl_get_fd(fd)) == NULL)
2262 return -1;
2264 if ((ret = chmod_acl_internals(posix_acl, mode)) == -1)
2265 goto done;
2267 ret = sys_acl_set_fd(fd, posix_acl);
2269 done:
2271 sys_acl_free_acl(posix_acl);
2272 return ret;