Replace all use of bzero with memset ...
[Samba/gbeck.git] / source / smbd / posix_acls.c
blob12eef46595d7525d04a97540afaf19b45278613d
1 /*
2 Unix SMB/CIFS implementation.
3 SMB NT Security Descriptor / Unix permission conversion.
4 Copyright (C) Jeremy Allison 1994-2000.
5 Copyright (C) Andreas Gruenbacher 2002.
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 trustee;
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 SAFE_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->trustee));
107 if (pace->owner_type == UID_ACE) {
108 const char *u_name = uidtoname(pace->unix_ug.uid);
109 dbgtext( "uid %u (%s) ", (unsigned int)pace->unix_ug.uid, u_name);
110 } else if (pace->owner_type == GID_ACE) {
111 char *g_name = gidtoname(pace->unix_ug.gid);
112 dbgtext( "gid %u (%s) ", (unsigned int)pace->unix_ug.gid, g_name);
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(connection_struct *conn, SMB_ACL_PERMSET_T permset)
159 mode_t ret = 0;
161 ret |= (SMB_VFS_SYS_ACL_GET_PERM(conn, permset, SMB_ACL_READ) ? S_IRUSR : 0);
162 ret |= (SMB_VFS_SYS_ACL_GET_PERM(conn, permset, SMB_ACL_WRITE) ? S_IWUSR : 0);
163 ret |= (SMB_VFS_SYS_ACL_GET_PERM(conn, 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(connection_struct *conn, mode_t mode, SMB_ACL_PERMSET_T *p_permset)
193 if (SMB_VFS_SYS_ACL_CLEAR_PERMS(conn, *p_permset) == -1)
194 return -1;
195 if (mode & S_IRUSR) {
196 if (SMB_VFS_SYS_ACL_ADD_PERM(conn, *p_permset, SMB_ACL_READ) == -1)
197 return -1;
199 if (mode & S_IWUSR) {
200 if (SMB_VFS_SYS_ACL_ADD_PERM(conn, *p_permset, SMB_ACL_WRITE) == -1)
201 return -1;
203 if (mode & S_IXUSR) {
204 if (SMB_VFS_SYS_ACL_ADD_PERM(conn, *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->trustee, &curr_ace_outer->trustee) &&
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 SAFE_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->trustee, &curr_ace_outer->trustee) &&
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 SAFE_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 SAFE_FREE(curr_ace_outer);
321 break;
325 } /* end for curr_ace */
326 } /* end for curr_ace_outer */
328 /* We may have modified the list. */
330 *pp_list_head = list_head;
333 /****************************************************************************
334 Check if we need to return NT4.x compatible ACL entries.
335 ****************************************************************************/
337 static BOOL nt4_compatible_acls(void)
339 const char *compat = lp_acl_compatibility();
341 if (*compat == '\0') {
342 enum remote_arch_types ra_type = get_remote_arch();
344 /* Automatically adapt to client */
345 return (ra_type <= RA_WINNT);
346 } else
347 return (strequal(compat, "winnt"));
351 /****************************************************************************
352 Map canon_ace perms to permission bits NT.
353 The attr element is not used here - we only process deny entries on set,
354 not get. Deny entries are implicit on get with ace->perms = 0.
355 ****************************************************************************/
357 static SEC_ACCESS map_canon_ace_perms(int *pacl_type, DOM_SID *powner_sid, canon_ace *ace)
359 SEC_ACCESS sa;
360 uint32 nt_mask = 0;
362 *pacl_type = SEC_ACE_TYPE_ACCESS_ALLOWED;
364 if ((ace->perms & ALL_ACE_PERMS) == ALL_ACE_PERMS) {
365 nt_mask = UNIX_ACCESS_RWX;
366 } else if ((ace->perms & ALL_ACE_PERMS) == (mode_t)0) {
368 * Windows NT refuses to display ACEs with no permissions in them (but
369 * they are perfectly legal with Windows 2000). If the ACE has empty
370 * permissions we cannot use 0, so we use the otherwise unused
371 * WRITE_OWNER permission, which we ignore when we set an ACL.
372 * We abstract this into a #define of UNIX_ACCESS_NONE to allow this
373 * to be changed in the future.
376 if (nt4_compatible_acls())
377 nt_mask = UNIX_ACCESS_NONE;
378 else
379 nt_mask = 0;
380 } else {
381 nt_mask |= ((ace->perms & S_IRUSR) ? UNIX_ACCESS_R : 0 );
382 nt_mask |= ((ace->perms & S_IWUSR) ? UNIX_ACCESS_W : 0 );
383 nt_mask |= ((ace->perms & S_IXUSR) ? UNIX_ACCESS_X : 0 );
386 DEBUG(10,("map_canon_ace_perms: Mapped (UNIX) %x to (NT) %x\n",
387 (unsigned int)ace->perms, (unsigned int)nt_mask ));
389 init_sec_access(&sa,nt_mask);
390 return sa;
393 /****************************************************************************
394 Map NT perms to a UNIX mode_t.
395 ****************************************************************************/
397 #define FILE_SPECIFIC_READ_BITS (FILE_READ_DATA|FILE_READ_EA|FILE_READ_ATTRIBUTES)
398 #define FILE_SPECIFIC_WRITE_BITS (FILE_WRITE_DATA|FILE_APPEND_DATA|FILE_WRITE_EA|FILE_WRITE_ATTRIBUTES)
399 #define FILE_SPECIFIC_EXECUTE_BITS (FILE_EXECUTE)
401 static mode_t map_nt_perms( SEC_ACCESS sec_access, int type)
403 mode_t mode = 0;
405 switch(type) {
406 case S_IRUSR:
407 if(sec_access.mask & GENERIC_ALL_ACCESS)
408 mode = S_IRUSR|S_IWUSR|S_IXUSR;
409 else {
410 mode |= (sec_access.mask & (GENERIC_READ_ACCESS|FILE_SPECIFIC_READ_BITS)) ? S_IRUSR : 0;
411 mode |= (sec_access.mask & (GENERIC_WRITE_ACCESS|FILE_SPECIFIC_WRITE_BITS)) ? S_IWUSR : 0;
412 mode |= (sec_access.mask & (GENERIC_EXECUTE_ACCESS|FILE_SPECIFIC_EXECUTE_BITS)) ? S_IXUSR : 0;
414 break;
415 case S_IRGRP:
416 if(sec_access.mask & GENERIC_ALL_ACCESS)
417 mode = S_IRGRP|S_IWGRP|S_IXGRP;
418 else {
419 mode |= (sec_access.mask & (GENERIC_READ_ACCESS|FILE_SPECIFIC_READ_BITS)) ? S_IRGRP : 0;
420 mode |= (sec_access.mask & (GENERIC_WRITE_ACCESS|FILE_SPECIFIC_WRITE_BITS)) ? S_IWGRP : 0;
421 mode |= (sec_access.mask & (GENERIC_EXECUTE_ACCESS|FILE_SPECIFIC_EXECUTE_BITS)) ? S_IXGRP : 0;
423 break;
424 case S_IROTH:
425 if(sec_access.mask & GENERIC_ALL_ACCESS)
426 mode = S_IROTH|S_IWOTH|S_IXOTH;
427 else {
428 mode |= (sec_access.mask & (GENERIC_READ_ACCESS|FILE_SPECIFIC_READ_BITS)) ? S_IROTH : 0;
429 mode |= (sec_access.mask & (GENERIC_WRITE_ACCESS|FILE_SPECIFIC_WRITE_BITS)) ? S_IWOTH : 0;
430 mode |= (sec_access.mask & (GENERIC_EXECUTE_ACCESS|FILE_SPECIFIC_EXECUTE_BITS)) ? S_IXOTH : 0;
432 break;
435 return mode;
438 /****************************************************************************
439 Unpack a SEC_DESC into a UNIX owner and group.
440 ****************************************************************************/
442 static BOOL unpack_nt_owners(SMB_STRUCT_STAT *psbuf, uid_t *puser, gid_t *pgrp, uint32 security_info_sent, SEC_DESC *psd)
444 DOM_SID owner_sid;
445 DOM_SID grp_sid;
447 *puser = (uid_t)-1;
448 *pgrp = (gid_t)-1;
450 if(security_info_sent == 0) {
451 DEBUG(0,("unpack_nt_owners: no security info sent !\n"));
452 return True;
456 * Validate the owner and group SID's.
459 memset(&owner_sid, '\0', sizeof(owner_sid));
460 memset(&grp_sid, '\0', sizeof(grp_sid));
462 DEBUG(5,("unpack_nt_owners: validating owner_sids.\n"));
465 * Don't immediately fail if the owner sid cannot be validated.
466 * This may be a group chown only set.
469 if (security_info_sent & OWNER_SECURITY_INFORMATION) {
470 sid_copy(&owner_sid, psd->owner_sid);
471 if (NT_STATUS_IS_ERR(sid_to_uid(&owner_sid, puser))) {
472 #if ACL_FORCE_UNMAPPABLE
473 /* this allows take ownership to work reasonably */
474 extern struct current_user current_user;
475 *puser = current_user.uid;
476 #else
477 DEBUG(3,("unpack_nt_owners: unable to validate owner sid for %s\n",
478 sid_string_static(&owner_sid)));
479 return False;
480 #endif
485 * Don't immediately fail if the group sid cannot be validated.
486 * This may be an owner chown only set.
489 if (security_info_sent & GROUP_SECURITY_INFORMATION) {
490 sid_copy(&grp_sid, psd->grp_sid);
491 if (NT_STATUS_IS_ERR(sid_to_gid( &grp_sid, pgrp))) {
492 #if ACL_FORCE_UNMAPPABLE
493 /* this allows take group ownership to work reasonably */
494 extern struct current_user current_user;
495 *pgrp = current_user.gid;
496 #else
497 DEBUG(3,("unpack_nt_owners: unable to validate group sid.\n"));
498 return False;
499 #endif
503 DEBUG(5,("unpack_nt_owners: owner_sids validated.\n"));
505 return True;
508 /****************************************************************************
509 Ensure the enforced permissions for this share apply.
510 ****************************************************************************/
512 static void apply_default_perms(files_struct *fsp, canon_ace *pace, mode_t type)
514 int snum = SNUM(fsp->conn);
515 mode_t and_bits = (mode_t)0;
516 mode_t or_bits = (mode_t)0;
518 /* Get the initial bits to apply. */
520 if (fsp->is_directory) {
521 and_bits = lp_dir_security_mask(snum);
522 or_bits = lp_force_dir_security_mode(snum);
523 } else {
524 and_bits = lp_security_mask(snum);
525 or_bits = lp_force_security_mode(snum);
528 /* Now bounce them into the S_USR space. */
529 switch(type) {
530 case S_IRUSR:
531 /* Ensure owner has read access. */
532 pace->perms |= S_IRUSR;
533 if (fsp->is_directory)
534 pace->perms |= (S_IWUSR|S_IXUSR);
535 and_bits = unix_perms_to_acl_perms(and_bits, S_IRUSR, S_IWUSR, S_IXUSR);
536 or_bits = unix_perms_to_acl_perms(or_bits, S_IRUSR, S_IWUSR, S_IXUSR);
537 break;
538 case S_IRGRP:
539 and_bits = unix_perms_to_acl_perms(and_bits, S_IRGRP, S_IWGRP, S_IXGRP);
540 or_bits = unix_perms_to_acl_perms(or_bits, S_IRGRP, S_IWGRP, S_IXGRP);
541 break;
542 case S_IROTH:
543 and_bits = unix_perms_to_acl_perms(and_bits, S_IROTH, S_IWOTH, S_IXOTH);
544 or_bits = unix_perms_to_acl_perms(or_bits, S_IROTH, S_IWOTH, S_IXOTH);
545 break;
548 pace->perms = ((pace->perms & and_bits)|or_bits);
551 /****************************************************************************
552 Check if a given uid/SID is in a group gid/SID. This is probably very
553 expensive and will need optimisation. A *lot* of optimisation :-). JRA.
554 ****************************************************************************/
556 static BOOL uid_entry_in_group( canon_ace *uid_ace, canon_ace *group_ace )
558 extern DOM_SID global_sid_World;
559 fstring u_name;
560 fstring g_name;
561 extern struct current_user current_user;
563 /* "Everyone" always matches every uid. */
565 if (sid_equal(&group_ace->trustee, &global_sid_World))
566 return True;
568 /* Assume that the current user is in the current group (force group) */
570 if (uid_ace->unix_ug.uid == current_user.uid && group_ace->unix_ug.gid == current_user.gid)
571 return True;
573 fstrcpy(u_name, uidtoname(uid_ace->unix_ug.uid));
574 fstrcpy(g_name, gidtoname(group_ace->unix_ug.gid));
577 * Due to the winbind interfaces we need to do this via names,
578 * not uids/gids.
581 return user_in_group_list(u_name, g_name, NULL, 0);
584 /****************************************************************************
585 A well formed POSIX file or default ACL has at least 3 entries, a
586 SMB_ACL_USER_OBJ, SMB_ACL_GROUP_OBJ, SMB_ACL_OTHER_OBJ.
587 In addition, the owner must always have at least read access.
588 When using this call on get_acl, the pst struct is valid and contains
589 the mode of the file. When using this call on set_acl, the pst struct has
590 been modified to have a mode containing the default for this file or directory
591 type.
592 ****************************************************************************/
594 static BOOL ensure_canon_entry_valid(canon_ace **pp_ace,
595 files_struct *fsp,
596 DOM_SID *pfile_owner_sid,
597 DOM_SID *pfile_grp_sid,
598 SMB_STRUCT_STAT *pst,
599 BOOL setting_acl)
601 extern DOM_SID global_sid_World;
602 canon_ace *pace;
603 BOOL got_user = False;
604 BOOL got_grp = False;
605 BOOL got_other = False;
606 canon_ace *pace_other = NULL;
607 canon_ace *pace_group = NULL;
609 for (pace = *pp_ace; pace; pace = pace->next) {
610 if (pace->type == SMB_ACL_USER_OBJ) {
612 if (setting_acl)
613 apply_default_perms(fsp, pace, S_IRUSR);
614 got_user = True;
616 } else if (pace->type == SMB_ACL_GROUP_OBJ) {
619 * Ensure create mask/force create mode is respected on set.
622 if (setting_acl)
623 apply_default_perms(fsp, pace, S_IRGRP);
624 got_grp = True;
625 pace_group = pace;
627 } else if (pace->type == SMB_ACL_OTHER) {
630 * Ensure create mask/force create mode is respected on set.
633 if (setting_acl)
634 apply_default_perms(fsp, pace, S_IROTH);
635 got_other = True;
636 pace_other = pace;
640 if (!got_user) {
641 if ((pace = (canon_ace *)malloc(sizeof(canon_ace))) == NULL) {
642 DEBUG(0,("ensure_canon_entry_valid: malloc fail.\n"));
643 return False;
646 ZERO_STRUCTP(pace);
647 pace->type = SMB_ACL_USER_OBJ;
648 pace->owner_type = UID_ACE;
649 pace->unix_ug.uid = pst->st_uid;
650 pace->trustee = *pfile_owner_sid;
651 pace->attr = ALLOW_ACE;
653 if (setting_acl) {
654 /* If we only got an "everyone" perm, just use that. */
655 if (!got_grp && got_other)
656 pace->perms = pace_other->perms;
657 else if (got_grp && uid_entry_in_group(pace, pace_group))
658 pace->perms = pace_group->perms;
659 else
660 pace->perms = 0;
662 apply_default_perms(fsp, pace, S_IRUSR);
663 } else {
664 pace->perms = unix_perms_to_acl_perms(pst->st_mode, S_IRUSR, S_IWUSR, S_IXUSR);
667 DLIST_ADD(*pp_ace, pace);
670 if (!got_grp) {
671 if ((pace = (canon_ace *)malloc(sizeof(canon_ace))) == NULL) {
672 DEBUG(0,("ensure_canon_entry_valid: malloc fail.\n"));
673 return False;
676 ZERO_STRUCTP(pace);
677 pace->type = SMB_ACL_GROUP_OBJ;
678 pace->owner_type = GID_ACE;
679 pace->unix_ug.uid = pst->st_gid;
680 pace->trustee = *pfile_grp_sid;
681 pace->attr = ALLOW_ACE;
682 if (setting_acl) {
683 /* If we only got an "everyone" perm, just use that. */
684 if (got_other)
685 pace->perms = pace_other->perms;
686 else
687 pace->perms = 0;
688 apply_default_perms(fsp, pace, S_IRGRP);
689 } else {
690 pace->perms = unix_perms_to_acl_perms(pst->st_mode, S_IRGRP, S_IWGRP, S_IXGRP);
693 DLIST_ADD(*pp_ace, pace);
696 if (!got_other) {
697 if ((pace = (canon_ace *)malloc(sizeof(canon_ace))) == NULL) {
698 DEBUG(0,("ensure_canon_entry_valid: malloc fail.\n"));
699 return False;
702 ZERO_STRUCTP(pace);
703 pace->type = SMB_ACL_OTHER;
704 pace->owner_type = WORLD_ACE;
705 pace->unix_ug.world = -1;
706 pace->trustee = global_sid_World;
707 pace->attr = ALLOW_ACE;
708 if (setting_acl) {
709 pace->perms = 0;
710 apply_default_perms(fsp, pace, S_IROTH);
711 } else
712 pace->perms = unix_perms_to_acl_perms(pst->st_mode, S_IROTH, S_IWOTH, S_IXOTH);
714 DLIST_ADD(*pp_ace, pace);
717 return True;
720 /****************************************************************************
721 Check if a POSIX ACL has the required SMB_ACL_USER_OBJ and SMB_ACL_GROUP_OBJ entries.
722 If it does not have them, check if there are any entries where the trustee is the
723 file owner or the owning group, and map these to SMB_ACL_USER_OBJ and SMB_ACL_GROUP_OBJ.
724 ****************************************************************************/
726 static void check_owning_objs(canon_ace *ace, DOM_SID *pfile_owner_sid, DOM_SID *pfile_grp_sid)
728 BOOL got_user_obj, got_group_obj;
729 canon_ace *current_ace;
730 int i, entries;
732 entries = count_canon_ace_list(ace);
733 got_user_obj = False;
734 got_group_obj = False;
736 for (i=0, current_ace = ace; i < entries; i++, current_ace = current_ace->next) {
737 if (current_ace->type == SMB_ACL_USER_OBJ)
738 got_user_obj = True;
739 else if (current_ace->type == SMB_ACL_GROUP_OBJ)
740 got_group_obj = True;
742 if (got_user_obj && got_group_obj) {
743 DEBUG(10,("check_owning_objs: ACL had owning user/group entries.\n"));
744 return;
747 for (i=0, current_ace = ace; i < entries; i++, current_ace = current_ace->next) {
748 if (!got_user_obj && current_ace->owner_type == UID_ACE &&
749 sid_equal(&current_ace->trustee, pfile_owner_sid)) {
750 current_ace->type = SMB_ACL_USER_OBJ;
751 got_user_obj = True;
753 if (!got_group_obj && current_ace->owner_type == GID_ACE &&
754 sid_equal(&current_ace->trustee, pfile_grp_sid)) {
755 current_ace->type = SMB_ACL_GROUP_OBJ;
756 got_group_obj = True;
759 if (!got_user_obj)
760 DEBUG(10,("check_owning_objs: ACL is missing an owner entry.\n"));
761 if (!got_group_obj)
762 DEBUG(10,("check_owning_objs: ACL is missing an owning group entry.\n"));
765 /****************************************************************************
766 Unpack a SEC_DESC into two canonical ace lists.
767 ****************************************************************************/
769 static BOOL create_canon_ace_lists(files_struct *fsp,
770 DOM_SID *pfile_owner_sid,
771 DOM_SID *pfile_grp_sid,
772 canon_ace **ppfile_ace, canon_ace **ppdir_ace,
773 SEC_ACL *dacl)
775 extern DOM_SID global_sid_Creator_Owner;
776 extern DOM_SID global_sid_Creator_Group;
777 extern DOM_SID global_sid_World;
778 extern struct generic_mapping file_generic_mapping;
779 BOOL all_aces_are_inherit_only = (fsp->is_directory ? True : False);
780 canon_ace *file_ace = NULL;
781 canon_ace *dir_ace = NULL;
782 canon_ace *tmp_ace = NULL;
783 canon_ace *current_ace = NULL;
784 BOOL got_dir_allow = False;
785 BOOL got_file_allow = False;
786 int i, j;
788 *ppfile_ace = NULL;
789 *ppdir_ace = NULL;
792 * Convert the incoming ACL into a more regular form.
795 for(i = 0; i < dacl->num_aces; i++) {
796 SEC_ACE *psa = &dacl->ace[i];
798 if((psa->type != SEC_ACE_TYPE_ACCESS_ALLOWED) && (psa->type != SEC_ACE_TYPE_ACCESS_DENIED)) {
799 DEBUG(3,("create_canon_ace_lists: unable to set anything but an ALLOW or DENY ACE.\n"));
800 return False;
803 if (nt4_compatible_acls()) {
805 * The security mask may be UNIX_ACCESS_NONE which should map into
806 * no permissions (we overload the WRITE_OWNER bit for this) or it
807 * should be one of the ALL/EXECUTE/READ/WRITE bits. Arrange for this
808 * to be so. Any other bits override the UNIX_ACCESS_NONE bit.
812 * Convert GENERIC bits to specific bits.
815 se_map_generic(&psa->info.mask, &file_generic_mapping);
817 psa->info.mask &= (UNIX_ACCESS_NONE|FILE_ALL_ACCESS);
819 if(psa->info.mask != UNIX_ACCESS_NONE)
820 psa->info.mask &= ~UNIX_ACCESS_NONE;
825 * Deal with the fact that NT 4.x re-writes the canonical format
826 * that we return for default ACLs. If a directory ACE is identical
827 * to a inherited directory ACE then NT changes the bits so that the
828 * first ACE is set to OI|IO and the second ACE for this SID is set
829 * to CI. We need to repair this. JRA.
832 for(i = 0; i < dacl->num_aces; i++) {
833 SEC_ACE *psa1 = &dacl->ace[i];
835 for (j = i + 1; j < dacl->num_aces; j++) {
836 SEC_ACE *psa2 = &dacl->ace[j];
838 if (psa1->info.mask != psa2->info.mask)
839 continue;
841 if (!sid_equal(&psa1->trustee, &psa2->trustee))
842 continue;
845 * Ok - permission bits and SIDs are equal.
846 * Check if flags were re-written.
849 if (psa1->flags & SEC_ACE_FLAG_INHERIT_ONLY) {
851 psa1->flags |= (psa2->flags & (SEC_ACE_FLAG_CONTAINER_INHERIT|SEC_ACE_FLAG_OBJECT_INHERIT));
852 psa2->flags &= ~(SEC_ACE_FLAG_CONTAINER_INHERIT|SEC_ACE_FLAG_OBJECT_INHERIT);
854 } else if (psa2->flags & SEC_ACE_FLAG_INHERIT_ONLY) {
856 psa2->flags |= (psa1->flags & (SEC_ACE_FLAG_CONTAINER_INHERIT|SEC_ACE_FLAG_OBJECT_INHERIT));
857 psa1->flags &= ~(SEC_ACE_FLAG_CONTAINER_INHERIT|SEC_ACE_FLAG_OBJECT_INHERIT);
863 for(i = 0; i < dacl->num_aces; i++) {
864 SEC_ACE *psa = &dacl->ace[i];
867 * Ignore non-mappable SIDs (NT Authority, BUILTIN etc).
870 if (non_mappable_sid(&psa->trustee)) {
871 fstring str;
872 DEBUG(10,("create_canon_ace_lists: ignoring non-mappable SID %s\n",
873 sid_to_string(str, &psa->trustee) ));
874 continue;
878 * Create a cannon_ace entry representing this NT DACL ACE.
881 if ((current_ace = (canon_ace *)malloc(sizeof(canon_ace))) == NULL) {
882 free_canon_ace_list(file_ace);
883 free_canon_ace_list(dir_ace);
884 DEBUG(0,("create_canon_ace_lists: malloc fail.\n"));
885 return False;
888 ZERO_STRUCTP(current_ace);
890 sid_copy(&current_ace->trustee, &psa->trustee);
893 * Try and work out if the SID is a user or group
894 * as we need to flag these differently for POSIX.
895 * Note what kind of a POSIX ACL this should map to.
898 if( sid_equal(&current_ace->trustee, &global_sid_World)) {
899 current_ace->owner_type = WORLD_ACE;
900 current_ace->unix_ug.world = -1;
901 current_ace->type = SMB_ACL_OTHER;
902 } else if (sid_equal(&current_ace->trustee, &global_sid_Creator_Owner)) {
903 current_ace->owner_type = UID_ACE;
904 current_ace->unix_ug.world = -1;
905 current_ace->type = SMB_ACL_USER_OBJ;
908 * The Creator Owner entry only specifies inheritable permissions,
909 * never access permissions. WinNT doesn't always set the ACE to
910 *INHERIT_ONLY, though.
913 if (nt4_compatible_acls())
914 psa->flags |= SEC_ACE_FLAG_INHERIT_ONLY;
915 } else if (sid_equal(&current_ace->trustee, &global_sid_Creator_Group)) {
916 current_ace->owner_type = GID_ACE;
917 current_ace->unix_ug.world = -1;
918 current_ace->type = SMB_ACL_GROUP_OBJ;
921 * The Creator Group entry only specifies inheritable permissions,
922 * never access permissions. WinNT doesn't always set the ACE to
923 *INHERIT_ONLY, though.
925 if (nt4_compatible_acls())
926 psa->flags |= SEC_ACE_FLAG_INHERIT_ONLY;
928 } else if (NT_STATUS_IS_OK(sid_to_gid( &current_ace->trustee, &current_ace->unix_ug.gid))) {
929 current_ace->owner_type = GID_ACE;
930 current_ace->type = SMB_ACL_GROUP;
931 } else if (NT_STATUS_IS_OK(sid_to_uid( &current_ace->trustee, &current_ace->unix_ug.uid))) {
932 current_ace->owner_type = UID_ACE;
933 current_ace->type = SMB_ACL_USER;
934 } else {
935 fstring str;
937 free_canon_ace_list(file_ace);
938 free_canon_ace_list(dir_ace);
939 DEBUG(0,("create_canon_ace_lists: unable to map SID %s to uid or gid.\n",
940 sid_to_string(str, &current_ace->trustee) ));
941 SAFE_FREE(current_ace);
942 return False;
946 * Map the given NT permissions into a UNIX mode_t containing only
947 * S_I(R|W|X)USR bits.
950 current_ace->perms |= map_nt_perms( psa->info, S_IRUSR);
951 current_ace->attr = (psa->type == SEC_ACE_TYPE_ACCESS_ALLOWED) ? ALLOW_ACE : DENY_ACE;
954 * Now add the created ace to either the file list, the directory
955 * list, or both. We *MUST* preserve the order here (hence we use
956 * DLIST_ADD_END) as NT ACLs are order dependent.
959 if (fsp->is_directory) {
962 * We can only add to the default POSIX ACE list if the ACE is
963 * designed to be inherited by both files and directories.
966 if ((psa->flags & (SEC_ACE_FLAG_OBJECT_INHERIT|SEC_ACE_FLAG_CONTAINER_INHERIT)) ==
967 (SEC_ACE_FLAG_OBJECT_INHERIT|SEC_ACE_FLAG_CONTAINER_INHERIT)) {
969 DLIST_ADD_END(dir_ace, current_ace, tmp_ace);
972 * Note if this was an allow ace. We can't process
973 * any further deny ace's after this.
976 if (current_ace->attr == ALLOW_ACE)
977 got_dir_allow = True;
979 if ((current_ace->attr == DENY_ACE) && got_dir_allow) {
980 DEBUG(0,("create_canon_ace_lists: malformed ACL in inheritable ACL ! \
981 Deny entry after Allow entry. Failing to set on file %s.\n", fsp->fsp_name ));
982 free_canon_ace_list(file_ace);
983 free_canon_ace_list(dir_ace);
984 SAFE_FREE(current_ace);
985 return False;
988 if( DEBUGLVL( 10 )) {
989 dbgtext("create_canon_ace_lists: adding dir ACL:\n");
990 print_canon_ace( current_ace, 0);
994 * If this is not an inherit only ACE we need to add a duplicate
995 * to the file acl.
998 if (!(psa->flags & SEC_ACE_FLAG_INHERIT_ONLY)) {
999 canon_ace *dup_ace = dup_canon_ace(current_ace);
1001 if (!dup_ace) {
1002 DEBUG(0,("create_canon_ace_lists: malloc fail !\n"));
1003 free_canon_ace_list(file_ace);
1004 free_canon_ace_list(dir_ace);
1005 return False;
1009 * We must not free current_ace here as its
1010 * pointer is now owned by the dir_ace list.
1012 current_ace = dup_ace;
1013 } else {
1015 * We must not free current_ace here as its
1016 * pointer is now owned by the dir_ace list.
1018 current_ace = NULL;
1024 * Only add to the file ACL if not inherit only.
1027 if (!(psa->flags & SEC_ACE_FLAG_INHERIT_ONLY)) {
1028 DLIST_ADD_END(file_ace, current_ace, tmp_ace);
1031 * Note if this was an allow ace. We can't process
1032 * any further deny ace's after this.
1035 if (current_ace->attr == ALLOW_ACE)
1036 got_file_allow = True;
1038 if ((current_ace->attr == DENY_ACE) && got_file_allow) {
1039 DEBUG(0,("create_canon_ace_lists: malformed ACL in file ACL ! \
1040 Deny entry after Allow entry. Failing to set on file %s.\n", fsp->fsp_name ));
1041 free_canon_ace_list(file_ace);
1042 free_canon_ace_list(dir_ace);
1043 SAFE_FREE(current_ace);
1044 return False;
1047 if( DEBUGLVL( 10 )) {
1048 dbgtext("create_canon_ace_lists: adding file ACL:\n");
1049 print_canon_ace( current_ace, 0);
1051 all_aces_are_inherit_only = False;
1053 * We must not free current_ace here as its
1054 * pointer is now owned by the file_ace list.
1056 current_ace = NULL;
1060 * Free if ACE was not added.
1063 SAFE_FREE(current_ace);
1066 if (fsp->is_directory && all_aces_are_inherit_only) {
1068 * Windows 2000 is doing one of these weird 'inherit acl'
1069 * traverses to conserve NTFS ACL resources. Just pretend
1070 * there was no DACL sent. JRA.
1073 DEBUG(10,("create_canon_ace_lists: Win2k inherit acl traverse. Ignoring DACL.\n"));
1074 free_canon_ace_list(file_ace);
1075 free_canon_ace_list(dir_ace);
1076 file_ace = NULL;
1077 dir_ace = NULL;
1078 } else {
1080 * Check if we have SMB_ACL_USER_OBJ and SMB_ACL_GROUP_OBJ entries in each
1081 * ACL. If we don't have them, check if any SMB_ACL_USER/SMB_ACL_GROUP
1082 * entries can be converted to *_OBJ. Usually we will already have these
1083 * entries in the Default ACL, and the Access ACL will not have them.
1085 check_owning_objs(file_ace, pfile_owner_sid, pfile_grp_sid);
1086 check_owning_objs(dir_ace, pfile_owner_sid, pfile_grp_sid);
1089 *ppfile_ace = file_ace;
1090 *ppdir_ace = dir_ace;
1092 return True;
1095 /****************************************************************************
1096 ASCII art time again... JRA :-).
1098 We have 4 cases to process when moving from an NT ACL to a POSIX ACL. Firstly,
1099 we insist the ACL is in canonical form (ie. all DENY entries preceede ALLOW
1100 entries). Secondly, the merge code has ensured that all duplicate SID entries for
1101 allow or deny have been merged, so the same SID can only appear once in the deny
1102 list or once in the allow list.
1104 We then process as follows :
1106 ---------------------------------------------------------------------------
1107 First pass - look for a Everyone DENY entry.
1109 If it is deny all (rwx) trunate the list at this point.
1110 Else, walk the list from this point and use the deny permissions of this
1111 entry as a mask on all following allow entries. Finally, delete
1112 the Everyone DENY entry (we have applied it to everything possible).
1114 In addition, in this pass we remove any DENY entries that have
1115 no permissions (ie. they are a DENY nothing).
1116 ---------------------------------------------------------------------------
1117 Second pass - only deal with deny user entries.
1119 DENY user1 (perms XXX)
1121 new_perms = 0
1122 for all following allow group entries where user1 is in group
1123 new_perms |= group_perms;
1125 user1 entry perms = new_perms & ~ XXX;
1127 Convert the deny entry to an allow entry with the new perms and
1128 push to the end of the list. Note if the user was in no groups
1129 this maps to a specific allow nothing entry for this user.
1131 The common case from the NT ACL choser (userX deny all) is
1132 optimised so we don't do the group lookup - we just map to
1133 an allow nothing entry.
1135 What we're doing here is inferring the allow permissions the
1136 person setting the ACE on user1 wanted by looking at the allow
1137 permissions on the groups the user is currently in. This will
1138 be a snapshot, depending on group membership but is the best
1139 we can do and has the advantage of failing closed rather than
1140 open.
1141 ---------------------------------------------------------------------------
1142 Third pass - only deal with deny group entries.
1144 DENY group1 (perms XXX)
1146 for all following allow user entries where user is in group1
1147 user entry perms = user entry perms & ~ XXX;
1149 If there is a group Everyone allow entry with permissions YYY,
1150 convert the group1 entry to an allow entry and modify its
1151 permissions to be :
1153 new_perms = YYY & ~ XXX
1155 and push to the end of the list.
1157 If there is no group Everyone allow entry then convert the
1158 group1 entry to a allow nothing entry and push to the end of the list.
1160 Note that the common case from the NT ACL choser (groupX deny all)
1161 cannot be optimised here as we need to modify user entries who are
1162 in the group to change them to a deny all also.
1164 What we're doing here is modifying the allow permissions of
1165 user entries (which are more specific in POSIX ACLs) to mask
1166 out the explicit deny set on the group they are in. This will
1167 be a snapshot depending on current group membership but is the
1168 best we can do and has the advantage of failing closed rather
1169 than open.
1170 ---------------------------------------------------------------------------
1171 Fourth pass - cope with cumulative permissions.
1173 for all allow user entries, if there exists an allow group entry with
1174 more permissive permissions, and the user is in that group, rewrite the
1175 allow user permissions to contain both sets of permissions.
1177 Currently the code for this is #ifdef'ed out as these semantics make
1178 no sense to me. JRA.
1179 ---------------------------------------------------------------------------
1181 Note we *MUST* do the deny user pass first as this will convert deny user
1182 entries into allow user entries which can then be processed by the deny
1183 group pass.
1185 The above algorithm took a *lot* of thinking about - hence this
1186 explaination :-). JRA.
1187 ****************************************************************************/
1189 /****************************************************************************
1190 Process a canon_ace list entries. This is very complex code. We need
1191 to go through and remove the "deny" permissions from any allow entry that matches
1192 the id of this entry. We have already refused any NT ACL that wasn't in correct
1193 order (DENY followed by ALLOW). If any allow entry ends up with zero permissions,
1194 we just remove it (to fail safe). We have already removed any duplicate ace
1195 entries. Treat an "Everyone" DENY_ACE as a special case - use it to mask all
1196 allow entries.
1197 ****************************************************************************/
1199 static void process_deny_list( canon_ace **pp_ace_list )
1201 extern DOM_SID global_sid_World;
1202 canon_ace *ace_list = *pp_ace_list;
1203 canon_ace *curr_ace = NULL;
1204 canon_ace *curr_ace_next = NULL;
1206 /* Pass 1 above - look for an Everyone, deny entry. */
1208 for (curr_ace = ace_list; curr_ace; curr_ace = curr_ace_next) {
1209 canon_ace *allow_ace_p;
1211 curr_ace_next = curr_ace->next; /* So we can't lose the link. */
1213 if (curr_ace->attr != DENY_ACE)
1214 continue;
1216 if (curr_ace->perms == (mode_t)0) {
1218 /* Deny nothing entry - delete. */
1220 DLIST_REMOVE(ace_list, curr_ace);
1221 continue;
1224 if (!sid_equal(&curr_ace->trustee, &global_sid_World))
1225 continue;
1227 /* JRATEST - assert. */
1228 SMB_ASSERT(curr_ace->owner_type == WORLD_ACE);
1230 if (curr_ace->perms == ALL_ACE_PERMS) {
1233 * Optimisation. This is a DENY_ALL to Everyone. Truncate the
1234 * list at this point including this entry.
1237 canon_ace *prev_entry = curr_ace->prev;
1239 free_canon_ace_list( curr_ace );
1240 if (prev_entry)
1241 prev_entry->next = NULL;
1242 else {
1243 /* We deleted the entire list. */
1244 ace_list = NULL;
1246 break;
1249 for (allow_ace_p = curr_ace->next; allow_ace_p; allow_ace_p = allow_ace_p->next) {
1252 * Only mask off allow entries.
1255 if (allow_ace_p->attr != ALLOW_ACE)
1256 continue;
1258 allow_ace_p->perms &= ~curr_ace->perms;
1262 * Now it's been applied, remove it.
1265 DLIST_REMOVE(ace_list, curr_ace);
1268 /* Pass 2 above - deal with deny user entries. */
1270 for (curr_ace = ace_list; curr_ace; curr_ace = curr_ace_next) {
1271 mode_t new_perms = (mode_t)0;
1272 canon_ace *allow_ace_p;
1273 canon_ace *tmp_ace;
1275 curr_ace_next = curr_ace->next; /* So we can't lose the link. */
1277 if (curr_ace->attr != DENY_ACE)
1278 continue;
1280 if (curr_ace->owner_type != UID_ACE)
1281 continue;
1283 if (curr_ace->perms == ALL_ACE_PERMS) {
1286 * Optimisation - this is a deny everything to this user.
1287 * Convert to an allow nothing and push to the end of the list.
1290 curr_ace->attr = ALLOW_ACE;
1291 curr_ace->perms = (mode_t)0;
1292 DLIST_DEMOTE(ace_list, curr_ace, tmp_ace);
1293 continue;
1296 for (allow_ace_p = curr_ace->next; allow_ace_p; allow_ace_p = allow_ace_p->next) {
1298 if (allow_ace_p->attr != ALLOW_ACE)
1299 continue;
1301 /* We process GID_ACE and WORLD_ACE entries only. */
1303 if (allow_ace_p->owner_type == UID_ACE)
1304 continue;
1306 if (uid_entry_in_group( curr_ace, allow_ace_p))
1307 new_perms |= allow_ace_p->perms;
1311 * Convert to a allow entry, modify the perms and push to the end
1312 * of the list.
1315 curr_ace->attr = ALLOW_ACE;
1316 curr_ace->perms = (new_perms & ~curr_ace->perms);
1317 DLIST_DEMOTE(ace_list, curr_ace, tmp_ace);
1320 /* Pass 3 above - deal with deny group entries. */
1322 for (curr_ace = ace_list; curr_ace; curr_ace = curr_ace_next) {
1323 canon_ace *tmp_ace;
1324 canon_ace *allow_ace_p;
1325 canon_ace *allow_everyone_p = NULL;
1327 curr_ace_next = curr_ace->next; /* So we can't lose the link. */
1329 if (curr_ace->attr != DENY_ACE)
1330 continue;
1332 if (curr_ace->owner_type != GID_ACE)
1333 continue;
1335 for (allow_ace_p = curr_ace->next; allow_ace_p; allow_ace_p = allow_ace_p->next) {
1337 if (allow_ace_p->attr != ALLOW_ACE)
1338 continue;
1340 /* Store a pointer to the Everyone allow, if it exists. */
1341 if (allow_ace_p->owner_type == WORLD_ACE)
1342 allow_everyone_p = allow_ace_p;
1344 /* We process UID_ACE entries only. */
1346 if (allow_ace_p->owner_type != UID_ACE)
1347 continue;
1349 /* Mask off the deny group perms. */
1351 if (uid_entry_in_group( allow_ace_p, curr_ace))
1352 allow_ace_p->perms &= ~curr_ace->perms;
1356 * Convert the deny to an allow with the correct perms and
1357 * push to the end of the list.
1360 curr_ace->attr = ALLOW_ACE;
1361 if (allow_everyone_p)
1362 curr_ace->perms = allow_everyone_p->perms & ~curr_ace->perms;
1363 else
1364 curr_ace->perms = (mode_t)0;
1365 DLIST_DEMOTE(ace_list, curr_ace, tmp_ace);
1369 /* Doing this fourth pass allows Windows semantics to be layered
1370 * on top of POSIX semantics. I'm not sure if this is desirable.
1371 * For example, in W2K ACLs there is no way to say, "Group X no
1372 * access, user Y full access" if user Y is a member of group X.
1373 * This seems completely broken semantics to me.... JRA.
1376 #if 0
1377 /* Pass 4 above - deal with allow entries. */
1379 for (curr_ace = ace_list; curr_ace; curr_ace = curr_ace_next) {
1380 canon_ace *allow_ace_p;
1382 curr_ace_next = curr_ace->next; /* So we can't lose the link. */
1384 if (curr_ace->attr != ALLOW_ACE)
1385 continue;
1387 if (curr_ace->owner_type != UID_ACE)
1388 continue;
1390 for (allow_ace_p = ace_list; allow_ace_p; allow_ace_p = allow_ace_p->next) {
1392 if (allow_ace_p->attr != ALLOW_ACE)
1393 continue;
1395 /* We process GID_ACE entries only. */
1397 if (allow_ace_p->owner_type != GID_ACE)
1398 continue;
1400 /* OR in the group perms. */
1402 if (uid_entry_in_group( curr_ace, allow_ace_p))
1403 curr_ace->perms |= allow_ace_p->perms;
1406 #endif
1408 *pp_ace_list = ace_list;
1411 /****************************************************************************
1412 Create a default mode that will be used if a security descriptor entry has
1413 no user/group/world entries.
1414 ****************************************************************************/
1416 static mode_t create_default_mode(files_struct *fsp, BOOL interitable_mode)
1418 int snum = SNUM(fsp->conn);
1419 mode_t and_bits = (mode_t)0;
1420 mode_t or_bits = (mode_t)0;
1421 mode_t mode = interitable_mode ? unix_mode( fsp->conn, FILE_ATTRIBUTE_ARCHIVE, fsp->fsp_name) : S_IRUSR;
1423 if (fsp->is_directory)
1424 mode |= (S_IWUSR|S_IXUSR);
1427 * Now AND with the create mode/directory mode bits then OR with the
1428 * force create mode/force directory mode bits.
1431 if (fsp->is_directory) {
1432 and_bits = lp_dir_security_mask(snum);
1433 or_bits = lp_force_dir_security_mode(snum);
1434 } else {
1435 and_bits = lp_security_mask(snum);
1436 or_bits = lp_force_security_mode(snum);
1439 return ((mode & and_bits)|or_bits);
1442 /****************************************************************************
1443 Unpack a SEC_DESC into two canonical ace lists. We don't depend on this
1444 succeeding.
1445 ****************************************************************************/
1447 static BOOL unpack_canon_ace(files_struct *fsp,
1448 SMB_STRUCT_STAT *pst,
1449 DOM_SID *pfile_owner_sid,
1450 DOM_SID *pfile_grp_sid,
1451 canon_ace **ppfile_ace, canon_ace **ppdir_ace,
1452 uint32 security_info_sent, SEC_DESC *psd)
1454 canon_ace *file_ace = NULL;
1455 canon_ace *dir_ace = NULL;
1457 *ppfile_ace = NULL;
1458 *ppdir_ace = NULL;
1460 if(security_info_sent == 0) {
1461 DEBUG(0,("unpack_canon_ace: no security info sent !\n"));
1462 return False;
1466 * If no DACL then this is a chown only security descriptor.
1469 if(!(security_info_sent & DACL_SECURITY_INFORMATION) || !psd->dacl)
1470 return True;
1473 * Now go through the DACL and create the canon_ace lists.
1476 if (!create_canon_ace_lists( fsp, pfile_owner_sid, pfile_grp_sid,
1477 &file_ace, &dir_ace, psd->dacl))
1478 return False;
1480 if ((file_ace == NULL) && (dir_ace == NULL)) {
1481 /* W2K traverse DACL set - ignore. */
1482 return True;
1486 * Go through the canon_ace list and merge entries
1487 * belonging to identical users of identical allow or deny type.
1488 * We can do this as all deny entries come first, followed by
1489 * all allow entries (we have mandated this before accepting this acl).
1492 print_canon_ace_list( "file ace - before merge", file_ace);
1493 merge_aces( &file_ace );
1495 print_canon_ace_list( "dir ace - before merge", dir_ace);
1496 merge_aces( &dir_ace );
1499 * NT ACLs are order dependent. Go through the acl lists and
1500 * process DENY entries by masking the allow entries.
1503 print_canon_ace_list( "file ace - before deny", file_ace);
1504 process_deny_list( &file_ace);
1506 print_canon_ace_list( "dir ace - before deny", dir_ace);
1507 process_deny_list( &dir_ace);
1510 * A well formed POSIX file or default ACL has at least 3 entries, a
1511 * SMB_ACL_USER_OBJ, SMB_ACL_GROUP_OBJ, SMB_ACL_OTHER_OBJ
1512 * and optionally a mask entry. Ensure this is the case.
1515 print_canon_ace_list( "file ace - before valid", file_ace);
1518 * A default 3 element mode entry for a file should be r-- --- ---.
1519 * A default 3 element mode entry for a directory should be rwx --- ---.
1522 pst->st_mode = create_default_mode(fsp, False);
1524 if (!ensure_canon_entry_valid(&file_ace, fsp, pfile_owner_sid, pfile_grp_sid, pst, True)) {
1525 free_canon_ace_list(file_ace);
1526 free_canon_ace_list(dir_ace);
1527 return False;
1530 print_canon_ace_list( "dir ace - before valid", dir_ace);
1533 * A default inheritable 3 element mode entry for a directory should be the
1534 * mode Samba will use to create a file within. Ensure user rwx bits are set if
1535 * it's a directory.
1538 pst->st_mode = create_default_mode(fsp, True);
1540 if (dir_ace && !ensure_canon_entry_valid(&dir_ace, fsp, pfile_owner_sid, pfile_grp_sid, pst, True)) {
1541 free_canon_ace_list(file_ace);
1542 free_canon_ace_list(dir_ace);
1543 return False;
1546 print_canon_ace_list( "file ace - return", file_ace);
1547 print_canon_ace_list( "dir ace - return", dir_ace);
1549 *ppfile_ace = file_ace;
1550 *ppdir_ace = dir_ace;
1551 return True;
1555 /******************************************************************************
1556 When returning permissions, try and fit NT display
1557 semantics if possible. Note the the canon_entries here must have been malloced.
1558 The list format should be - first entry = owner, followed by group and other user
1559 entries, last entry = other.
1561 Note that this doesn't exactly match the NT semantics for an ACL. As POSIX entries
1562 are not ordered, and match on the most specific entry rather than walking a list,
1563 then a simple POSIX permission of rw-r--r-- should really map to 5 entries,
1565 Entry 0: owner : deny all except read and write.
1566 Entry 1: group : deny all except read.
1567 Entry 2: owner : allow read and write.
1568 Entry 3: group : allow read.
1569 Entry 4: Everyone : allow read.
1571 But NT cannot display this in their ACL editor !
1572 ********************************************************************************/
1574 static void arrange_posix_perms( char *filename, canon_ace **pp_list_head)
1576 canon_ace *list_head = *pp_list_head;
1577 canon_ace *owner_ace = NULL;
1578 canon_ace *other_ace = NULL;
1579 canon_ace *ace = NULL;
1581 for (ace = list_head; ace; ace = ace->next) {
1582 if (ace->type == SMB_ACL_USER_OBJ)
1583 owner_ace = ace;
1584 else if (ace->type == SMB_ACL_OTHER) {
1585 /* Last ace - this is "other" */
1586 other_ace = ace;
1590 if (!owner_ace || !other_ace) {
1591 DEBUG(0,("arrange_posix_perms: Invalid POSIX permissions for file %s, missing owner or other.\n",
1592 filename ));
1593 return;
1597 * The POSIX algorithm applies to owner first, and other last,
1598 * so ensure they are arranged in this order.
1601 if (owner_ace) {
1602 DLIST_PROMOTE(list_head, owner_ace);
1605 if (other_ace) {
1606 DLIST_DEMOTE(list_head, other_ace, ace);
1609 /* We have probably changed the head of the list. */
1611 *pp_list_head = list_head;
1614 /****************************************************************************
1615 Create a linked list of canonical ACE entries.
1616 ****************************************************************************/
1618 static canon_ace *canonicalise_acl( files_struct *fsp, SMB_ACL_T posix_acl, SMB_STRUCT_STAT *psbuf,
1619 DOM_SID *powner, DOM_SID *pgroup, SMB_ACL_TYPE_T the_acl_type)
1621 extern DOM_SID global_sid_World;
1622 connection_struct *conn = fsp->conn;
1623 mode_t acl_mask = (S_IRUSR|S_IWUSR|S_IXUSR);
1624 canon_ace *list_head = NULL;
1625 canon_ace *ace = NULL;
1626 canon_ace *next_ace = NULL;
1627 int entry_id = SMB_ACL_FIRST_ENTRY;
1628 SMB_ACL_ENTRY_T entry;
1629 size_t ace_count;
1631 while ( posix_acl && (SMB_VFS_SYS_ACL_GET_ENTRY(conn, posix_acl, entry_id, &entry) == 1)) {
1632 SMB_ACL_TAG_T tagtype;
1633 SMB_ACL_PERMSET_T permset;
1634 DOM_SID sid;
1635 posix_id unix_ug;
1636 enum ace_owner owner_type;
1638 /* get_next... */
1639 if (entry_id == SMB_ACL_FIRST_ENTRY)
1640 entry_id = SMB_ACL_NEXT_ENTRY;
1642 /* Is this a MASK entry ? */
1643 if (SMB_VFS_SYS_ACL_GET_TAG_TYPE(conn, entry, &tagtype) == -1)
1644 continue;
1646 if (SMB_VFS_SYS_ACL_GET_PERMSET(conn, entry, &permset) == -1)
1647 continue;
1649 /* Decide which SID to use based on the ACL type. */
1650 switch(tagtype) {
1651 case SMB_ACL_USER_OBJ:
1652 /* Get the SID from the owner. */
1653 sid_copy(&sid, powner);
1654 unix_ug.uid = psbuf->st_uid;
1655 owner_type = UID_ACE;
1656 break;
1657 case SMB_ACL_USER:
1659 uid_t *puid = (uid_t *)SMB_VFS_SYS_ACL_GET_QUALIFIER(conn, entry);
1660 if (puid == NULL) {
1661 DEBUG(0,("canonicalise_acl: Failed to get uid.\n"));
1662 continue;
1665 * A SMB_ACL_USER entry for the owner is shadowed by the
1666 * SMB_ACL_USER_OBJ entry and Windows also cannot represent
1667 * that entry, so we ignore it. We also don't create such
1668 * entries out of the blue when setting ACLs, so a get/set
1669 * cycle will drop them.
1671 if (the_acl_type == SMB_ACL_TYPE_ACCESS && *puid == psbuf->st_uid)
1672 continue;
1673 uid_to_sid( &sid, *puid);
1674 unix_ug.uid = *puid;
1675 owner_type = UID_ACE;
1676 SMB_VFS_SYS_ACL_FREE_QUALIFIER(conn, (void *)puid,tagtype);
1677 break;
1679 case SMB_ACL_GROUP_OBJ:
1680 /* Get the SID from the owning group. */
1681 sid_copy(&sid, pgroup);
1682 unix_ug.gid = psbuf->st_gid;
1683 owner_type = GID_ACE;
1684 break;
1685 case SMB_ACL_GROUP:
1687 gid_t *pgid = (gid_t *)SMB_VFS_SYS_ACL_GET_QUALIFIER(conn, entry);
1688 if (pgid == NULL) {
1689 DEBUG(0,("canonicalise_acl: Failed to get gid.\n"));
1690 continue;
1692 gid_to_sid( &sid, *pgid);
1693 unix_ug.gid = *pgid;
1694 owner_type = GID_ACE;
1695 SMB_VFS_SYS_ACL_FREE_QUALIFIER(conn, (void *)pgid,tagtype);
1696 break;
1698 case SMB_ACL_MASK:
1699 acl_mask = convert_permset_to_mode_t(conn, permset);
1700 continue; /* Don't count the mask as an entry. */
1701 case SMB_ACL_OTHER:
1702 /* Use the Everyone SID */
1703 sid = global_sid_World;
1704 unix_ug.world = -1;
1705 owner_type = WORLD_ACE;
1706 break;
1707 default:
1708 DEBUG(0,("canonicalise_acl: Unknown tagtype %u\n", (unsigned int)tagtype));
1709 continue;
1713 * Add this entry to the list.
1716 if ((ace = (canon_ace *)malloc(sizeof(canon_ace))) == NULL)
1717 goto fail;
1719 ZERO_STRUCTP(ace);
1720 ace->type = tagtype;
1721 ace->perms = convert_permset_to_mode_t(conn, permset);
1722 ace->attr = ALLOW_ACE;
1723 ace->trustee = sid;
1724 ace->unix_ug = unix_ug;
1725 ace->owner_type = owner_type;
1727 DLIST_ADD(list_head, ace);
1731 * This next call will ensure we have at least a user/group/world set.
1734 if (!ensure_canon_entry_valid(&list_head, fsp, powner, pgroup, psbuf, False))
1735 goto fail;
1737 arrange_posix_perms(fsp->fsp_name,&list_head );
1740 * Now go through the list, masking the permissions with the
1741 * acl_mask. Ensure all DENY Entries are at the start of the list.
1744 DEBUG(10,("canonicalise_acl: ace entries before arrange :\n"));
1746 for ( ace_count = 0, ace = list_head; ace; ace = next_ace, ace_count++) {
1747 next_ace = ace->next;
1749 /* Masks are only applied to entries other than USER_OBJ and OTHER. */
1750 if (ace->type != SMB_ACL_OTHER && ace->type != SMB_ACL_USER_OBJ)
1751 ace->perms &= acl_mask;
1753 if (ace->perms == 0) {
1754 DLIST_PROMOTE(list_head, ace);
1757 if( DEBUGLVL( 10 ) ) {
1758 print_canon_ace(ace, ace_count);
1762 print_canon_ace_list( "canonicalise_acl: ace entries after arrange", list_head );
1764 return list_head;
1766 fail:
1768 free_canon_ace_list(list_head);
1769 return NULL;
1772 /****************************************************************************
1773 Attempt to apply an ACL to a file or directory.
1774 ****************************************************************************/
1776 static BOOL set_canon_ace_list(files_struct *fsp, canon_ace *the_ace, BOOL default_ace, BOOL *pacl_set_support)
1778 connection_struct *conn = fsp->conn;
1779 BOOL ret = False;
1780 SMB_ACL_T the_acl = SMB_VFS_SYS_ACL_INIT(conn, (int)count_canon_ace_list(the_ace) + 1);
1781 canon_ace *p_ace;
1782 int i;
1783 SMB_ACL_ENTRY_T mask_entry;
1784 BOOL got_mask_entry = False;
1785 SMB_ACL_PERMSET_T mask_permset;
1786 SMB_ACL_TYPE_T the_acl_type = (default_ace ? SMB_ACL_TYPE_DEFAULT : SMB_ACL_TYPE_ACCESS);
1787 BOOL needs_mask = False;
1788 mode_t mask_perms = 0;
1790 #if defined(POSIX_ACL_NEEDS_MASK)
1791 /* HP-UX always wants to have a mask (called "class" there). */
1792 needs_mask = True;
1793 #endif
1795 if (the_acl == NULL) {
1797 if (errno != ENOSYS) {
1799 * Only print this error message if we have some kind of ACL
1800 * support that's not working. Otherwise we would always get this.
1802 DEBUG(0,("set_canon_ace_list: Unable to init %s ACL. (%s)\n",
1803 default_ace ? "default" : "file", strerror(errno) ));
1805 *pacl_set_support = False;
1806 return False;
1809 if( DEBUGLVL( 10 )) {
1810 dbgtext("set_canon_ace_list: setting ACL:\n");
1811 for (i = 0, p_ace = the_ace; p_ace; p_ace = p_ace->next, i++ ) {
1812 print_canon_ace( p_ace, i);
1816 for (i = 0, p_ace = the_ace; p_ace; p_ace = p_ace->next, i++ ) {
1817 SMB_ACL_ENTRY_T the_entry;
1818 SMB_ACL_PERMSET_T the_permset;
1821 * ACLs only "need" an ACL_MASK entry if there are any named user or
1822 * named group entries. But if there is an ACL_MASK entry, it applies
1823 * to ACL_USER, ACL_GROUP, and ACL_GROUP_OBJ entries. Set the mask
1824 * so that it doesn't deny (i.e., mask off) any permissions.
1827 if (p_ace->type == SMB_ACL_USER || p_ace->type == SMB_ACL_GROUP) {
1828 needs_mask = True;
1829 mask_perms |= p_ace->perms;
1830 } else if (p_ace->type == SMB_ACL_GROUP_OBJ) {
1831 mask_perms |= p_ace->perms;
1835 * Get the entry for this ACE.
1838 if (SMB_VFS_SYS_ACL_CREATE_ENTRY(conn, &the_acl, &the_entry) == -1) {
1839 DEBUG(0,("set_canon_ace_list: Failed to create entry %d. (%s)\n",
1840 i, strerror(errno) ));
1841 goto done;
1844 if (p_ace->type == SMB_ACL_MASK) {
1845 mask_entry = the_entry;
1846 got_mask_entry = True;
1850 * Ok - we now know the ACL calls should be working, don't
1851 * allow fallback to chmod.
1854 *pacl_set_support = True;
1857 * Initialise the entry from the canon_ace.
1861 * First tell the entry what type of ACE this is.
1864 if (SMB_VFS_SYS_ACL_SET_TAG_TYPE(conn, the_entry, p_ace->type) == -1) {
1865 DEBUG(0,("set_canon_ace_list: Failed to set tag type on entry %d. (%s)\n",
1866 i, strerror(errno) ));
1867 goto done;
1871 * Only set the qualifier (user or group id) if the entry is a user
1872 * or group id ACE.
1875 if ((p_ace->type == SMB_ACL_USER) || (p_ace->type == SMB_ACL_GROUP)) {
1876 if (SMB_VFS_SYS_ACL_SET_QUALIFIER(conn, the_entry,(void *)&p_ace->unix_ug.uid) == -1) {
1877 DEBUG(0,("set_canon_ace_list: Failed to set qualifier on entry %d. (%s)\n",
1878 i, strerror(errno) ));
1879 goto done;
1884 * Convert the mode_t perms in the canon_ace to a POSIX permset.
1887 if (SMB_VFS_SYS_ACL_GET_PERMSET(conn, the_entry, &the_permset) == -1) {
1888 DEBUG(0,("set_canon_ace_list: Failed to get permset on entry %d. (%s)\n",
1889 i, strerror(errno) ));
1890 goto done;
1893 if (map_acl_perms_to_permset(conn, p_ace->perms, &the_permset) == -1) {
1894 DEBUG(0,("set_canon_ace_list: Failed to create permset for mode (%u) on entry %d. (%s)\n",
1895 (unsigned int)p_ace->perms, i, strerror(errno) ));
1896 goto done;
1900 * ..and apply them to the entry.
1903 if (SMB_VFS_SYS_ACL_SET_PERMSET(conn, the_entry, the_permset) == -1) {
1904 DEBUG(0,("set_canon_ace_list: Failed to add permset on entry %d. (%s)\n",
1905 i, strerror(errno) ));
1906 goto done;
1909 if( DEBUGLVL( 10 ))
1910 print_canon_ace( p_ace, i);
1913 if (needs_mask && !got_mask_entry) {
1914 if (SMB_VFS_SYS_ACL_CREATE_ENTRY(conn, &the_acl, &mask_entry) == -1) {
1915 DEBUG(0,("set_canon_ace_list: Failed to create mask entry. (%s)\n", strerror(errno) ));
1916 goto done;
1919 if (SMB_VFS_SYS_ACL_SET_TAG_TYPE(conn, mask_entry, SMB_ACL_MASK) == -1) {
1920 DEBUG(0,("set_canon_ace_list: Failed to set tag type on mask entry. (%s)\n",strerror(errno) ));
1921 goto done;
1924 if (SMB_VFS_SYS_ACL_GET_PERMSET(conn, mask_entry, &mask_permset) == -1) {
1925 DEBUG(0,("set_canon_ace_list: Failed to get mask permset. (%s)\n", strerror(errno) ));
1926 goto done;
1929 if (map_acl_perms_to_permset(conn, S_IRUSR|S_IWUSR|S_IXUSR, &mask_permset) == -1) {
1930 DEBUG(0,("set_canon_ace_list: Failed to create mask permset. (%s)\n", strerror(errno) ));
1931 goto done;
1934 if (SMB_VFS_SYS_ACL_SET_PERMSET(conn, mask_entry, mask_permset) == -1) {
1935 DEBUG(0,("set_canon_ace_list: Failed to add mask permset. (%s)\n", strerror(errno) ));
1936 goto done;
1941 * Check if the ACL is valid.
1944 if (SMB_VFS_SYS_ACL_VALID(conn, the_acl) == -1) {
1945 DEBUG(0,("set_canon_ace_list: ACL type (%s) is invalid for set (%s).\n",
1946 the_acl_type == SMB_ACL_TYPE_DEFAULT ? "directory default" : "file",
1947 strerror(errno) ));
1948 goto done;
1952 * Finally apply it to the file or directory.
1955 if(default_ace || fsp->is_directory || fsp->fd == -1) {
1956 if (SMB_VFS_SYS_ACL_SET_FILE(conn, fsp->fsp_name, the_acl_type, the_acl) == -1) {
1958 * Some systems allow all the above calls and only fail with no ACL support
1959 * when attempting to apply the acl. HPUX with HFS is an example of this. JRA.
1961 if (errno == ENOSYS)
1962 *pacl_set_support = False;
1964 #ifdef ENOTSUP
1965 if (errno == ENOTSUP)
1966 *pacl_set_support = False;
1967 #endif
1969 DEBUG(2,("set_canon_ace_list: sys_acl_set_file type %s failed for file %s (%s).\n",
1970 the_acl_type == SMB_ACL_TYPE_DEFAULT ? "directory default" : "file",
1971 fsp->fsp_name, strerror(errno) ));
1972 goto done;
1974 } else {
1975 if (SMB_VFS_SYS_ACL_SET_FD(fsp, fsp->fd, the_acl) == -1) {
1977 * Some systems allow all the above calls and only fail with no ACL support
1978 * when attempting to apply the acl. HPUX with HFS is an example of this. JRA.
1980 if (errno == ENOSYS)
1981 *pacl_set_support = False;
1983 #ifdef ENOTSUP
1984 if (errno == ENOTSUP)
1985 *pacl_set_support = False;
1986 #endif
1988 DEBUG(2,("set_canon_ace_list: sys_acl_set_file failed for file %s (%s).\n",
1989 fsp->fsp_name, strerror(errno) ));
1990 goto done;
1994 ret = True;
1996 done:
1998 if (the_acl != NULL)
1999 SMB_VFS_SYS_ACL_FREE_ACL(conn, the_acl);
2001 return ret;
2004 /****************************************************************************
2005 Find a particular canon_ace entry.
2006 ****************************************************************************/
2008 static struct canon_ace *canon_ace_entry_for(struct canon_ace *list, SMB_ACL_TAG_T type, posix_id *id)
2010 while (list) {
2011 if (list->type == type && ((type != SMB_ACL_USER && type != SMB_ACL_GROUP) ||
2012 (type == SMB_ACL_USER && id && id->uid == list->unix_ug.uid) ||
2013 (type == SMB_ACL_GROUP && id && id->gid == list->unix_ug.gid)))
2014 break;
2015 list = list->next;
2017 return list;
2020 /****************************************************************************
2022 ****************************************************************************/
2024 SMB_ACL_T free_empty_sys_acl(connection_struct *conn, SMB_ACL_T the_acl)
2026 SMB_ACL_ENTRY_T entry;
2028 if (!the_acl)
2029 return NULL;
2030 if (SMB_VFS_SYS_ACL_GET_ENTRY(conn, the_acl, SMB_ACL_FIRST_ENTRY, &entry) != 1) {
2031 SMB_VFS_SYS_ACL_FREE_ACL(conn, the_acl);
2032 return NULL;
2034 return the_acl;
2037 /****************************************************************************
2038 Convert a canon_ace to a generic 3 element permission - if possible.
2039 ****************************************************************************/
2041 #define MAP_PERM(p,mask,result) (((p) & (mask)) ? (result) : 0 )
2043 static BOOL convert_canon_ace_to_posix_perms( files_struct *fsp, canon_ace *file_ace_list, mode_t *posix_perms)
2045 int snum = SNUM(fsp->conn);
2046 size_t ace_count = count_canon_ace_list(file_ace_list);
2047 canon_ace *ace_p;
2048 canon_ace *owner_ace = NULL;
2049 canon_ace *group_ace = NULL;
2050 canon_ace *other_ace = NULL;
2051 mode_t and_bits;
2052 mode_t or_bits;
2054 if (ace_count != 3) {
2055 DEBUG(3,("convert_canon_ace_to_posix_perms: Too many ACE entries for file %s to convert to \
2056 posix perms.\n", fsp->fsp_name ));
2057 return False;
2060 for (ace_p = file_ace_list; ace_p; ace_p = ace_p->next) {
2061 if (ace_p->owner_type == UID_ACE)
2062 owner_ace = ace_p;
2063 else if (ace_p->owner_type == GID_ACE)
2064 group_ace = ace_p;
2065 else if (ace_p->owner_type == WORLD_ACE)
2066 other_ace = ace_p;
2069 if (!owner_ace || !group_ace || !other_ace) {
2070 DEBUG(3,("convert_canon_ace_to_posix_perms: Can't get standard entries for file %s.\n",
2071 fsp->fsp_name ));
2072 return False;
2075 *posix_perms = (mode_t)0;
2077 *posix_perms |= owner_ace->perms;
2078 *posix_perms |= MAP_PERM(group_ace->perms, S_IRUSR, S_IRGRP);
2079 *posix_perms |= MAP_PERM(group_ace->perms, S_IWUSR, S_IWGRP);
2080 *posix_perms |= MAP_PERM(group_ace->perms, S_IXUSR, S_IXGRP);
2081 *posix_perms |= MAP_PERM(other_ace->perms, S_IRUSR, S_IROTH);
2082 *posix_perms |= MAP_PERM(other_ace->perms, S_IWUSR, S_IWOTH);
2083 *posix_perms |= MAP_PERM(other_ace->perms, S_IXUSR, S_IXOTH);
2085 /* The owner must have at least read access. */
2087 *posix_perms |= S_IRUSR;
2088 if (fsp->is_directory)
2089 *posix_perms |= (S_IWUSR|S_IXUSR);
2091 /* If requested apply the masks. */
2093 /* Get the initial bits to apply. */
2095 if (fsp->is_directory) {
2096 and_bits = lp_dir_security_mask(snum);
2097 or_bits = lp_force_dir_security_mode(snum);
2098 } else {
2099 and_bits = lp_security_mask(snum);
2100 or_bits = lp_force_security_mode(snum);
2103 *posix_perms = (((*posix_perms) & and_bits)|or_bits);
2105 DEBUG(10,("convert_canon_ace_to_posix_perms: converted u=%o,g=%o,w=%o to perm=0%o for file %s.\n",
2106 (int)owner_ace->perms, (int)group_ace->perms, (int)other_ace->perms, (int)*posix_perms,
2107 fsp->fsp_name ));
2109 return True;
2112 static int nt_ace_comp( SEC_ACE *a1, SEC_ACE *a2)
2114 if (a1->type == a2->type)
2115 return 0;
2117 if (a1->type == SEC_ACE_TYPE_ACCESS_DENIED && a2->type == SEC_ACE_TYPE_ACCESS_ALLOWED)
2118 return -1;
2119 return 1;
2122 /****************************************************************************
2123 Incoming NT ACLs on a directory can be split into a default POSIX acl (CI|OI|IO) and
2124 a normal POSIX acl. Win2k needs these split acls re-merging into one ACL
2125 with CI|OI set so it is inherited and also applies to the directory.
2126 Based on code from "Jim McDonough" <jmcd@us.ibm.com>.
2127 ****************************************************************************/
2129 static size_t merge_default_aces( SEC_ACE *nt_ace_list, size_t num_aces)
2131 size_t i, j;
2133 for (i = 0; i < num_aces; i++) {
2134 for (j = i+1; j < num_aces; j++) {
2135 /* We know the lower number ACE's are file entries. */
2136 if ((nt_ace_list[i].type == nt_ace_list[j].type) &&
2137 (nt_ace_list[i].size == nt_ace_list[j].size) &&
2138 (nt_ace_list[i].info.mask == nt_ace_list[j].info.mask) &&
2139 sid_equal(&nt_ace_list[i].trustee, &nt_ace_list[j].trustee) &&
2140 (nt_ace_list[i].flags == 0) &&
2141 (nt_ace_list[j].flags == (SEC_ACE_FLAG_OBJECT_INHERIT|
2142 SEC_ACE_FLAG_CONTAINER_INHERIT|
2143 SEC_ACE_FLAG_INHERIT_ONLY))) {
2145 * These are identical except for the flags.
2146 * Merge the inherited ACE onto the non-inherited ACE.
2149 nt_ace_list[i].flags = SEC_ACE_FLAG_OBJECT_INHERIT|SEC_ACE_FLAG_CONTAINER_INHERIT;
2150 if (num_aces - j - 1 > 0)
2151 memmove(&nt_ace_list[j], &nt_ace_list[j+1], (num_aces-j-1) *
2152 sizeof(SEC_ACE));
2153 num_aces--;
2154 break;
2159 return num_aces;
2161 /****************************************************************************
2162 Reply to query a security descriptor from an fsp. If it succeeds it allocates
2163 the space for the return elements and returns the size needed to return the
2164 security descriptor. This should be the only external function needed for
2165 the UNIX style get ACL.
2166 ****************************************************************************/
2168 size_t get_nt_acl(files_struct *fsp, uint32 security_info, SEC_DESC **ppdesc)
2170 extern DOM_SID global_sid_Builtin_Administrators;
2171 extern DOM_SID global_sid_Builtin_Users;
2172 extern DOM_SID global_sid_Creator_Owner;
2173 extern DOM_SID global_sid_Creator_Group;
2174 connection_struct *conn = fsp->conn;
2175 SMB_STRUCT_STAT sbuf;
2176 SEC_ACE *nt_ace_list = NULL;
2177 DOM_SID owner_sid;
2178 DOM_SID group_sid;
2179 size_t sd_size = 0;
2180 SEC_ACL *psa = NULL;
2181 size_t num_acls = 0;
2182 size_t num_dir_acls = 0;
2183 size_t num_aces = 0;
2184 SMB_ACL_T posix_acl = NULL;
2185 SMB_ACL_T dir_acl = NULL;
2186 canon_ace *file_ace = NULL;
2187 canon_ace *dir_ace = NULL;
2188 size_t num_profile_acls = 0;
2190 *ppdesc = NULL;
2192 DEBUG(10,("get_nt_acl: called for file %s\n", fsp->fsp_name ));
2194 if(fsp->is_directory || fsp->fd == -1) {
2196 /* Get the stat struct for the owner info. */
2197 if(SMB_VFS_STAT(fsp->conn,fsp->fsp_name, &sbuf) != 0) {
2198 return 0;
2201 * Get the ACL from the path.
2204 posix_acl = SMB_VFS_SYS_ACL_GET_FILE(conn, fsp->fsp_name, SMB_ACL_TYPE_ACCESS);
2207 * If it's a directory get the default POSIX ACL.
2210 if(fsp->is_directory) {
2211 dir_acl = SMB_VFS_SYS_ACL_GET_FILE(conn, fsp->fsp_name, SMB_ACL_TYPE_DEFAULT);
2212 dir_acl = free_empty_sys_acl(conn, dir_acl);
2215 } else {
2217 /* Get the stat struct for the owner info. */
2218 if(SMB_VFS_FSTAT(fsp,fsp->fd,&sbuf) != 0) {
2219 return 0;
2222 * Get the ACL from the fd.
2224 posix_acl = SMB_VFS_SYS_ACL_GET_FD(fsp, fsp->fd);
2227 DEBUG(5,("get_nt_acl : file ACL %s, directory ACL %s\n",
2228 posix_acl ? "present" : "absent",
2229 dir_acl ? "present" : "absent" ));
2232 * Get the owner, group and world SIDs.
2235 if (lp_profile_acls(SNUM(fsp->conn))) {
2236 /* For WXP SP1 the owner must be administrators. */
2237 sid_copy(&owner_sid, &global_sid_Builtin_Administrators);
2238 sid_copy(&group_sid, &global_sid_Builtin_Users);
2239 num_profile_acls = 2;
2240 } else {
2241 create_file_sids(&sbuf, &owner_sid, &group_sid);
2244 if (security_info & DACL_SECURITY_INFORMATION) {
2247 * In the optimum case Creator Owner and Creator Group would be used for
2248 * the ACL_USER_OBJ and ACL_GROUP_OBJ entries, respectively, but this
2249 * would lead to usability problems under Windows: The Creator entries
2250 * are only available in browse lists of directories and not for files;
2251 * additionally the identity of the owning group couldn't be determined.
2252 * We therefore use those identities only for Default ACLs.
2255 /* Create the canon_ace lists. */
2256 file_ace = canonicalise_acl( fsp, posix_acl, &sbuf, &owner_sid, &group_sid, SMB_ACL_TYPE_ACCESS );
2258 /* We must have *some* ACLS. */
2260 if (count_canon_ace_list(file_ace) == 0) {
2261 DEBUG(0,("get_nt_acl : No ACLs on file (%s) !\n", fsp->fsp_name ));
2262 return 0;
2265 if (fsp->is_directory && dir_acl) {
2266 dir_ace = canonicalise_acl(fsp, dir_acl, &sbuf,
2267 &global_sid_Creator_Owner,
2268 &global_sid_Creator_Group, SMB_ACL_TYPE_DEFAULT );
2272 * Create the NT ACE list from the canonical ace lists.
2276 canon_ace *ace;
2277 int nt_acl_type;
2278 int i;
2280 if (nt4_compatible_acls() && dir_ace) {
2282 * NT 4 chokes if an ACL contains an INHERIT_ONLY entry
2283 * but no non-INHERIT_ONLY entry for one SID. So we only
2284 * remove entries from the Access ACL if the
2285 * corresponding Default ACL entries have also been
2286 * removed. ACEs for CREATOR-OWNER and CREATOR-GROUP
2287 * are exceptions. We can do nothing
2288 * intelligent if the Default ACL contains entries that
2289 * are not also contained in the Access ACL, so this
2290 * case will still fail under NT 4.
2293 ace = canon_ace_entry_for(dir_ace, SMB_ACL_OTHER, NULL);
2294 if (ace && !ace->perms) {
2295 DLIST_REMOVE(dir_ace, ace);
2296 SAFE_FREE(ace);
2298 ace = canon_ace_entry_for(file_ace, SMB_ACL_OTHER, NULL);
2299 if (ace && !ace->perms) {
2300 DLIST_REMOVE(file_ace, ace);
2301 SAFE_FREE(ace);
2306 * WinNT doesn't usually have Creator Group
2307 * in browse lists, so we send this entry to
2308 * WinNT even if it contains no relevant
2309 * permissions. Once we can add
2310 * Creator Group to browse lists we can
2311 * re-enable this.
2314 #if 0
2315 ace = canon_ace_entry_for(dir_ace, SMB_ACL_GROUP_OBJ, NULL);
2316 if (ace && !ace->perms) {
2317 DLIST_REMOVE(dir_ace, ace);
2318 SAFE_FREE(ace);
2320 #endif
2322 ace = canon_ace_entry_for(file_ace, SMB_ACL_GROUP_OBJ, NULL);
2323 if (ace && !ace->perms) {
2324 DLIST_REMOVE(file_ace, ace);
2325 SAFE_FREE(ace);
2329 num_acls = count_canon_ace_list(file_ace);
2330 num_dir_acls = count_canon_ace_list(dir_ace);
2332 /* Allocate the ace list. */
2333 if ((nt_ace_list = (SEC_ACE *)malloc((num_acls + num_profile_acls + num_dir_acls)* sizeof(SEC_ACE))) == NULL) {
2334 DEBUG(0,("get_nt_acl: Unable to malloc space for nt_ace_list.\n"));
2335 goto done;
2338 memset(nt_ace_list, '\0', (num_acls + num_dir_acls) * sizeof(SEC_ACE) );
2341 * Create the NT ACE list from the canonical ace lists.
2344 ace = file_ace;
2346 for (i = 0; i < num_acls; i++, ace = ace->next) {
2347 SEC_ACCESS acc;
2349 acc = map_canon_ace_perms(&nt_acl_type, &owner_sid, ace );
2350 init_sec_ace(&nt_ace_list[num_aces++], &ace->trustee, nt_acl_type, acc, 0);
2353 /* The User must have access to a profile share - even if we can't map the SID. */
2354 if (lp_profile_acls(SNUM(fsp->conn))) {
2355 SEC_ACCESS acc;
2357 init_sec_access(&acc,FILE_GENERIC_ALL);
2358 init_sec_ace(&nt_ace_list[num_aces++], &global_sid_Builtin_Users, SEC_ACE_TYPE_ACCESS_ALLOWED, acc, 0);
2361 ace = dir_ace;
2363 for (i = 0; i < num_dir_acls; i++, ace = ace->next) {
2364 SEC_ACCESS acc;
2366 acc = map_canon_ace_perms(&nt_acl_type, &owner_sid, ace );
2367 init_sec_ace(&nt_ace_list[num_aces++], &ace->trustee, nt_acl_type, acc,
2368 SEC_ACE_FLAG_OBJECT_INHERIT|SEC_ACE_FLAG_CONTAINER_INHERIT|SEC_ACE_FLAG_INHERIT_ONLY);
2371 /* The User must have access to a profile share - even if we can't map the SID. */
2372 if (lp_profile_acls(SNUM(fsp->conn))) {
2373 SEC_ACCESS acc;
2375 init_sec_access(&acc,FILE_GENERIC_ALL);
2376 init_sec_ace(&nt_ace_list[num_aces++], &global_sid_Builtin_Users, SEC_ACE_TYPE_ACCESS_ALLOWED, acc,
2377 SEC_ACE_FLAG_OBJECT_INHERIT|SEC_ACE_FLAG_CONTAINER_INHERIT|
2378 SEC_ACE_FLAG_INHERIT_ONLY);
2382 * Merge POSIX default ACLs and normal ACLs into one NT ACE.
2383 * Win2K needs this to get the inheritance correct when replacing ACLs
2384 * on a directory tree. Based on work by Jim @ IBM.
2387 num_aces = merge_default_aces(nt_ace_list, num_aces);
2390 * Sort to force deny entries to the front.
2393 if (num_aces)
2394 qsort( nt_ace_list, num_aces, sizeof(nt_ace_list[0]), QSORT_CAST nt_ace_comp);
2397 if (num_aces) {
2398 if((psa = make_sec_acl( main_loop_talloc_get(), ACL_REVISION, num_aces, nt_ace_list)) == NULL) {
2399 DEBUG(0,("get_nt_acl: Unable to malloc space for acl.\n"));
2400 goto done;
2403 } /* security_info & DACL_SECURITY_INFORMATION */
2405 *ppdesc = make_standard_sec_desc( main_loop_talloc_get(),
2406 (security_info & OWNER_SECURITY_INFORMATION) ? &owner_sid : NULL,
2407 (security_info & GROUP_SECURITY_INFORMATION) ? &group_sid : NULL,
2408 psa,
2409 &sd_size);
2411 if(!*ppdesc) {
2412 DEBUG(0,("get_nt_acl: Unable to malloc space for security descriptor.\n"));
2413 sd_size = 0;
2414 } else {
2415 #if 1
2417 * JRA. Setting this flag causes W2K clients not to
2418 * propagate ACL sets down a directory tree correctly.
2421 * Windows 2000: The DACL_PROTECTED flag in the security
2422 * descriptor marks the ACL as non-inheriting, i.e., no
2423 * ACEs from higher level directories propagate to this
2424 * ACL. In the POSIX ACL model permissions are only
2425 * inherited at file create time, so ACLs never contain
2426 * any ACEs that are inherited dynamically. The DACL_PROTECTED
2427 * flag doesn't seem to bother Windows NT.
2429 (*ppdesc)->type |= SE_DESC_DACL_PROTECTED;
2430 #endif
2433 done:
2435 if (posix_acl)
2436 SMB_VFS_SYS_ACL_FREE_ACL(conn, posix_acl);
2437 if (dir_acl)
2438 SMB_VFS_SYS_ACL_FREE_ACL(conn, dir_acl);
2439 free_canon_ace_list(file_ace);
2440 free_canon_ace_list(dir_ace);
2441 SAFE_FREE(nt_ace_list);
2443 return sd_size;
2446 /****************************************************************************
2447 Try to chown a file. We will be able to chown it under the following conditions.
2449 1) If we have root privileges, then it will just work.
2450 2) If we have write permission to the file and dos_filemodes is set
2451 then allow chown to the currently authenticated user.
2452 ****************************************************************************/
2454 static int try_chown(connection_struct *conn, const char *fname, uid_t uid, gid_t gid)
2456 int ret;
2457 extern struct current_user current_user;
2458 files_struct *fsp;
2459 SMB_STRUCT_STAT st;
2461 /* try the direct way first */
2462 ret = SMB_VFS_CHOWN(conn, fname, uid, gid);
2463 if (ret == 0)
2464 return 0;
2466 if(!CAN_WRITE(conn) || !lp_dos_filemode(SNUM(conn)))
2467 return -1;
2469 if (SMB_VFS_STAT(conn,fname,&st))
2470 return -1;
2472 fsp = open_file_fchmod(conn,fname,&st);
2473 if (!fsp)
2474 return -1;
2476 /* only allow chown to the current user. This is more secure,
2477 and also copes with the case where the SID in a take ownership ACL is
2478 a local SID on the users workstation
2480 uid = current_user.uid;
2482 become_root();
2483 /* Keep the current file gid the same. */
2484 ret = SMB_VFS_FCHOWN(fsp, fsp->fd, uid, (gid_t)-1);
2485 unbecome_root();
2487 close_file_fchmod(fsp);
2489 return ret;
2492 /****************************************************************************
2493 Reply to set a security descriptor on an fsp. security_info_sent is the
2494 description of the following NT ACL.
2495 This should be the only external function needed for the UNIX style set ACL.
2496 ****************************************************************************/
2498 BOOL set_nt_acl(files_struct *fsp, uint32 security_info_sent, SEC_DESC *psd)
2500 connection_struct *conn = fsp->conn;
2501 uid_t user = (uid_t)-1;
2502 gid_t grp = (gid_t)-1;
2503 SMB_STRUCT_STAT sbuf;
2504 DOM_SID file_owner_sid;
2505 DOM_SID file_grp_sid;
2506 canon_ace *file_ace_list = NULL;
2507 canon_ace *dir_ace_list = NULL;
2508 BOOL acl_perms = False;
2509 mode_t orig_mode = (mode_t)0;
2510 uid_t orig_uid;
2511 gid_t orig_gid;
2512 BOOL need_chown = False;
2513 extern struct current_user current_user;
2515 DEBUG(10,("set_nt_acl: called for file %s\n", fsp->fsp_name ));
2517 if (!CAN_WRITE(conn)) {
2518 DEBUG(10,("set acl rejected on read-only share\n"));
2519 return False;
2523 * Get the current state of the file.
2526 if(fsp->is_directory || fsp->fd == -1) {
2527 if(SMB_VFS_STAT(fsp->conn,fsp->fsp_name, &sbuf) != 0)
2528 return False;
2529 } else {
2530 if(SMB_VFS_FSTAT(fsp,fsp->fd,&sbuf) != 0)
2531 return False;
2534 /* Save the original elements we check against. */
2535 orig_mode = sbuf.st_mode;
2536 orig_uid = sbuf.st_uid;
2537 orig_gid = sbuf.st_gid;
2540 * Unpack the user/group/world id's.
2543 if (!unpack_nt_owners( &sbuf, &user, &grp, security_info_sent, psd))
2544 return False;
2547 * Do we need to chown ?
2550 if (((user != (uid_t)-1) && (orig_uid != user)) || (( grp != (gid_t)-1) && (orig_gid != grp)))
2551 need_chown = True;
2554 * Chown before setting ACL only if we don't change the user, or
2555 * if we change to the current user, but not if we want to give away
2556 * the file.
2559 if (need_chown && (user == (uid_t)-1 || user == current_user.uid)) {
2561 DEBUG(3,("set_nt_acl: chown %s. uid = %u, gid = %u.\n",
2562 fsp->fsp_name, (unsigned int)user, (unsigned int)grp ));
2564 if(try_chown( fsp->conn, fsp->fsp_name, user, grp) == -1) {
2565 DEBUG(3,("set_nt_acl: chown %s, %u, %u failed. Error = %s.\n",
2566 fsp->fsp_name, (unsigned int)user, (unsigned int)grp, strerror(errno) ));
2567 return False;
2571 * Recheck the current state of the file, which may have changed.
2572 * (suid/sgid bits, for instance)
2575 if(fsp->is_directory) {
2576 if(SMB_VFS_STAT(fsp->conn, fsp->fsp_name, &sbuf) != 0) {
2577 return False;
2579 } else {
2581 int ret;
2583 if(fsp->fd == -1)
2584 ret = SMB_VFS_STAT(fsp->conn, fsp->fsp_name, &sbuf);
2585 else
2586 ret = SMB_VFS_FSTAT(fsp,fsp->fd,&sbuf);
2588 if(ret != 0)
2589 return False;
2592 /* Save the original elements we check against. */
2593 orig_mode = sbuf.st_mode;
2594 orig_uid = sbuf.st_uid;
2595 orig_gid = sbuf.st_gid;
2597 /* We did it, don't try again */
2598 need_chown = False;
2601 create_file_sids(&sbuf, &file_owner_sid, &file_grp_sid);
2603 acl_perms = unpack_canon_ace( fsp, &sbuf, &file_owner_sid, &file_grp_sid,
2604 &file_ace_list, &dir_ace_list, security_info_sent, psd);
2606 /* Ignore W2K traverse DACL set. */
2607 if (file_ace_list || dir_ace_list) {
2609 if (!acl_perms) {
2610 DEBUG(3,("set_nt_acl: cannot set permissions\n"));
2611 free_canon_ace_list(file_ace_list);
2612 free_canon_ace_list(dir_ace_list);
2613 return False;
2617 * Only change security if we got a DACL.
2620 if((security_info_sent & DACL_SECURITY_INFORMATION) && (psd->dacl != NULL)) {
2622 BOOL acl_set_support = False;
2623 BOOL ret = False;
2626 * Try using the POSIX ACL set first. Fall back to chmod if
2627 * we have no ACL support on this filesystem.
2630 if (acl_perms && file_ace_list) {
2631 ret = set_canon_ace_list(fsp, file_ace_list, False, &acl_set_support);
2632 if (acl_set_support && ret == False) {
2633 DEBUG(3,("set_nt_acl: failed to set file acl on file %s (%s).\n", fsp->fsp_name, strerror(errno) ));
2634 free_canon_ace_list(file_ace_list);
2635 free_canon_ace_list(dir_ace_list);
2636 return False;
2640 if (acl_perms && acl_set_support && fsp->is_directory) {
2641 if (dir_ace_list) {
2642 if (!set_canon_ace_list(fsp, dir_ace_list, True, &acl_set_support)) {
2643 DEBUG(3,("set_nt_acl: failed to set default acl on directory %s (%s).\n", fsp->fsp_name, strerror(errno) ));
2644 free_canon_ace_list(file_ace_list);
2645 free_canon_ace_list(dir_ace_list);
2646 return False;
2648 } else {
2651 * No default ACL - delete one if it exists.
2654 if (SMB_VFS_SYS_ACL_DELETE_DEF_FILE(conn, fsp->fsp_name) == -1) {
2655 DEBUG(3,("set_nt_acl: sys_acl_delete_def_file failed (%s)\n", strerror(errno)));
2656 free_canon_ace_list(file_ace_list);
2657 free_canon_ace_list(dir_ace_list);
2658 return False;
2664 * If we cannot set using POSIX ACLs we fall back to checking if we need to chmod.
2667 if(!acl_set_support && acl_perms) {
2668 mode_t posix_perms;
2670 if (!convert_canon_ace_to_posix_perms( fsp, file_ace_list, &posix_perms)) {
2671 free_canon_ace_list(file_ace_list);
2672 free_canon_ace_list(dir_ace_list);
2673 DEBUG(3,("set_nt_acl: failed to convert file acl to posix permissions for file %s.\n",
2674 fsp->fsp_name ));
2675 return False;
2678 if (orig_mode != posix_perms) {
2680 DEBUG(3,("set_nt_acl: chmod %s. perms = 0%o.\n",
2681 fsp->fsp_name, (unsigned int)posix_perms ));
2683 if(SMB_VFS_CHMOD(conn,fsp->fsp_name, posix_perms) == -1) {
2684 DEBUG(3,("set_nt_acl: chmod %s, 0%o failed. Error = %s.\n",
2685 fsp->fsp_name, (unsigned int)posix_perms, strerror(errno) ));
2686 free_canon_ace_list(file_ace_list);
2687 free_canon_ace_list(dir_ace_list);
2688 return False;
2694 free_canon_ace_list(file_ace_list);
2695 free_canon_ace_list(dir_ace_list);
2698 /* Any chown pending? */
2699 if (need_chown) {
2701 DEBUG(3,("set_nt_acl: chown %s. uid = %u, gid = %u.\n",
2702 fsp->fsp_name, (unsigned int)user, (unsigned int)grp ));
2704 if(try_chown( fsp->conn, fsp->fsp_name, user, grp) == -1) {
2705 DEBUG(3,("set_nt_acl: chown %s, %u, %u failed. Error = %s.\n",
2706 fsp->fsp_name, (unsigned int)user, (unsigned int)grp, strerror(errno) ));
2707 return False;
2711 return True;
2714 /****************************************************************************
2715 Do a chmod by setting the ACL USER_OBJ, GROUP_OBJ and OTHER bits in an ACL
2716 and set the mask to rwx. Needed to preserve complex ACLs set by NT.
2717 ****************************************************************************/
2719 static int chmod_acl_internals( connection_struct *conn, SMB_ACL_T posix_acl, mode_t mode)
2721 int entry_id = SMB_ACL_FIRST_ENTRY;
2722 SMB_ACL_ENTRY_T entry;
2723 int num_entries = 0;
2725 while ( SMB_VFS_SYS_ACL_GET_ENTRY(conn, posix_acl, entry_id, &entry) == 1) {
2726 SMB_ACL_TAG_T tagtype;
2727 SMB_ACL_PERMSET_T permset;
2728 mode_t perms;
2730 /* get_next... */
2731 if (entry_id == SMB_ACL_FIRST_ENTRY)
2732 entry_id = SMB_ACL_NEXT_ENTRY;
2734 if (SMB_VFS_SYS_ACL_GET_TAG_TYPE(conn, entry, &tagtype) == -1)
2735 return -1;
2737 if (SMB_VFS_SYS_ACL_GET_PERMSET(conn, entry, &permset) == -1)
2738 return -1;
2740 num_entries++;
2742 switch(tagtype) {
2743 case SMB_ACL_USER_OBJ:
2744 perms = unix_perms_to_acl_perms(mode, S_IRUSR, S_IWUSR, S_IXUSR);
2745 break;
2746 case SMB_ACL_GROUP_OBJ:
2747 perms = unix_perms_to_acl_perms(mode, S_IRGRP, S_IWGRP, S_IXGRP);
2748 break;
2749 case SMB_ACL_MASK:
2751 * FIXME: The ACL_MASK entry permissions should really be set to
2752 * the union of the permissions of all ACL_USER,
2753 * ACL_GROUP_OBJ, and ACL_GROUP entries. That's what
2754 * acl_calc_mask() does, but Samba ACLs doesn't provide it.
2756 perms = S_IRUSR|S_IWUSR|S_IXUSR;
2757 break;
2758 case SMB_ACL_OTHER:
2759 perms = unix_perms_to_acl_perms(mode, S_IROTH, S_IWOTH, S_IXOTH);
2760 break;
2761 default:
2762 continue;
2765 if (map_acl_perms_to_permset(conn, perms, &permset) == -1)
2766 return -1;
2768 if (SMB_VFS_SYS_ACL_SET_PERMSET(conn, entry, permset) == -1)
2769 return -1;
2773 * If this is a simple 3 element ACL or no elements then it's a standard
2774 * UNIX permission set. Just use chmod...
2777 if ((num_entries == 3) || (num_entries == 0))
2778 return -1;
2780 return 0;
2783 /****************************************************************************
2784 Get the access ACL of FROM, do a chmod by setting the ACL USER_OBJ,
2785 GROUP_OBJ and OTHER bits in an ACL and set the mask to rwx. Set the
2786 resulting ACL on TO. Note that name is in UNIX character set.
2787 ****************************************************************************/
2789 static int copy_access_acl(connection_struct *conn, const char *from, const char *to, mode_t mode)
2791 SMB_ACL_T posix_acl = NULL;
2792 int ret = -1;
2794 if ((posix_acl = SMB_VFS_SYS_ACL_GET_FILE(conn, from, SMB_ACL_TYPE_ACCESS)) == NULL)
2795 return -1;
2797 if ((ret = chmod_acl_internals(conn, posix_acl, mode)) == -1)
2798 goto done;
2800 ret = SMB_VFS_SYS_ACL_SET_FILE(conn, to, SMB_ACL_TYPE_ACCESS, posix_acl);
2802 done:
2804 SMB_VFS_SYS_ACL_FREE_ACL(conn, posix_acl);
2805 return ret;
2808 /****************************************************************************
2809 Do a chmod by setting the ACL USER_OBJ, GROUP_OBJ and OTHER bits in an ACL
2810 and set the mask to rwx. Needed to preserve complex ACLs set by NT.
2811 Note that name is in UNIX character set.
2812 ****************************************************************************/
2814 int chmod_acl(connection_struct *conn, const char *name, mode_t mode)
2816 return copy_access_acl(conn, name, name, mode);
2819 /****************************************************************************
2820 If "inherit permissions" is set and the parent directory has no default
2821 ACL but it does have an Access ACL, inherit this Access ACL to file name.
2822 ****************************************************************************/
2824 int inherit_access_acl(connection_struct *conn, const char *name, mode_t mode)
2826 pstring dirname;
2827 pstrcpy(dirname, parent_dirname(name));
2829 if (!lp_inherit_perms(SNUM(conn)) || directory_has_default_acl(conn, dirname))
2830 return 0;
2832 return copy_access_acl(conn, dirname, name, mode);
2835 /****************************************************************************
2836 Do an fchmod by setting the ACL USER_OBJ, GROUP_OBJ and OTHER bits in an ACL
2837 and set the mask to rwx. Needed to preserve complex ACLs set by NT.
2838 ****************************************************************************/
2840 int fchmod_acl(files_struct *fsp, int fd, mode_t mode)
2842 connection_struct *conn = fsp->conn;
2843 SMB_ACL_T posix_acl = NULL;
2844 int ret = -1;
2846 if ((posix_acl = SMB_VFS_SYS_ACL_GET_FD(fsp, fd)) == NULL)
2847 return -1;
2849 if ((ret = chmod_acl_internals(conn, posix_acl, mode)) == -1)
2850 goto done;
2852 ret = SMB_VFS_SYS_ACL_SET_FD(fsp, fd, posix_acl);
2854 done:
2856 SMB_VFS_SYS_ACL_FREE_ACL(conn, posix_acl);
2857 return ret;
2860 /****************************************************************************
2861 Check for an existing default POSIX ACL on a directory.
2862 ****************************************************************************/
2864 BOOL directory_has_default_acl(connection_struct *conn, const char *fname)
2866 SMB_ACL_T dir_acl = SMB_VFS_SYS_ACL_GET_FILE( conn, fname, SMB_ACL_TYPE_DEFAULT);
2867 BOOL has_acl = False;
2868 SMB_ACL_ENTRY_T entry;
2870 if (dir_acl != NULL && (SMB_VFS_SYS_ACL_GET_ENTRY(conn, dir_acl, SMB_ACL_FIRST_ENTRY, &entry) == 1))
2871 has_acl = True;
2873 if (dir_acl)
2874 SMB_VFS_SYS_ACL_FREE_ACL(conn, dir_acl);
2875 return has_acl;