adding some initiaial code to sert %a to Win2K3 (using Native LanMan string from...
[Samba.git] / source / smbd / posix_acls.c
blob01e3c31ba78b63dd560b981945041a2ad45b7181
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 |= (conn->vfs_ops.sys_acl_get_perm(conn, permset, SMB_ACL_READ) ? S_IRUSR : 0);
162 ret |= (conn->vfs_ops.sys_acl_get_perm(conn, permset, SMB_ACL_WRITE) ? S_IWUSR : 0);
163 ret |= (conn->vfs_ops.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 (conn->vfs_ops.sys_acl_clear_perms(conn, *p_permset) == -1)
194 return -1;
195 if (mode & S_IRUSR) {
196 if (conn->vfs_ops.sys_acl_add_perm(conn, *p_permset, SMB_ACL_READ) == -1)
197 return -1;
199 if (mode & S_IWUSR) {
200 if (conn->vfs_ops.sys_acl_add_perm(conn, *p_permset, SMB_ACL_WRITE) == -1)
201 return -1;
203 if (mode & S_IXUSR) {
204 if (conn->vfs_ops.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;
446 enum SID_NAME_USE sid_type;
448 *puser = (uid_t)-1;
449 *pgrp = (gid_t)-1;
451 if(security_info_sent == 0) {
452 DEBUG(0,("unpack_nt_owners: no security info sent !\n"));
453 return True;
457 * Validate the owner and group SID's.
460 memset(&owner_sid, '\0', sizeof(owner_sid));
461 memset(&grp_sid, '\0', sizeof(grp_sid));
463 DEBUG(5,("unpack_nt_owners: validating owner_sids.\n"));
466 * Don't immediately fail if the owner sid cannot be validated.
467 * This may be a group chown only set.
470 if (security_info_sent & OWNER_SECURITY_INFORMATION) {
471 sid_copy(&owner_sid, psd->owner_sid);
472 if (!sid_to_uid( &owner_sid, puser, &sid_type)) {
473 #if ACL_FORCE_UNMAPPABLE
474 /* this allows take ownership to work reasonably */
475 extern struct current_user current_user;
476 *puser = current_user.uid;
477 #else
478 DEBUG(3,("unpack_nt_owners: unable to validate owner sid for %s\n",
479 sid_string_static(&owner_sid)));
480 return False;
481 #endif
486 * Don't immediately fail if the group sid cannot be validated.
487 * This may be an owner chown only set.
490 if (security_info_sent & GROUP_SECURITY_INFORMATION) {
491 sid_copy(&grp_sid, psd->grp_sid);
492 if (!sid_to_gid( &grp_sid, pgrp, &sid_type)) {
493 #if ACL_FORCE_UNMAPPABLE
494 /* this allows take group ownership to work reasonably */
495 extern struct current_user current_user;
496 *pgrp = current_user.gid;
497 #else
498 DEBUG(3,("unpack_nt_owners: unable to validate group sid.\n"));
499 return False;
500 #endif
504 DEBUG(5,("unpack_nt_owners: owner_sids validated.\n"));
506 return True;
509 /****************************************************************************
510 Ensure the enforced permissions for this share apply.
511 ****************************************************************************/
513 static void apply_default_perms(files_struct *fsp, canon_ace *pace, mode_t type)
515 int snum = SNUM(fsp->conn);
516 mode_t and_bits = (mode_t)0;
517 mode_t or_bits = (mode_t)0;
519 /* Get the initial bits to apply. */
521 if (fsp->is_directory) {
522 and_bits = lp_dir_security_mask(snum);
523 or_bits = lp_force_dir_security_mode(snum);
524 } else {
525 and_bits = lp_security_mask(snum);
526 or_bits = lp_force_security_mode(snum);
529 /* Now bounce them into the S_USR space. */
530 switch(type) {
531 case S_IRUSR:
532 /* Ensure owner has read access. */
533 pace->perms |= S_IRUSR;
534 if (fsp->is_directory)
535 pace->perms |= (S_IWUSR|S_IXUSR);
536 and_bits = unix_perms_to_acl_perms(and_bits, S_IRUSR, S_IWUSR, S_IXUSR);
537 or_bits = unix_perms_to_acl_perms(or_bits, S_IRUSR, S_IWUSR, S_IXUSR);
538 break;
539 case S_IRGRP:
540 and_bits = unix_perms_to_acl_perms(and_bits, S_IRGRP, S_IWGRP, S_IXGRP);
541 or_bits = unix_perms_to_acl_perms(or_bits, S_IRGRP, S_IWGRP, S_IXGRP);
542 break;
543 case S_IROTH:
544 and_bits = unix_perms_to_acl_perms(and_bits, S_IROTH, S_IWOTH, S_IXOTH);
545 or_bits = unix_perms_to_acl_perms(or_bits, S_IROTH, S_IWOTH, S_IXOTH);
546 break;
549 pace->perms = ((pace->perms & and_bits)|or_bits);
552 /****************************************************************************
553 Check if a given uid/SID is in a group gid/SID. This is probably very
554 expensive and will need optimisation. A *lot* of optimisation :-). JRA.
555 ****************************************************************************/
557 static BOOL uid_entry_in_group( canon_ace *uid_ace, canon_ace *group_ace )
559 extern DOM_SID global_sid_World;
560 fstring u_name;
561 fstring g_name;
562 extern struct current_user current_user;
564 /* "Everyone" always matches every uid. */
566 if (sid_equal(&group_ace->trustee, &global_sid_World))
567 return True;
569 /* Assume that the current user is in the current group (force group) */
571 if (uid_ace->unix_ug.uid == current_user.uid && group_ace->unix_ug.gid == current_user.gid)
572 return True;
574 fstrcpy(u_name, uidtoname(uid_ace->unix_ug.uid));
575 fstrcpy(g_name, gidtoname(group_ace->unix_ug.gid));
578 * Due to the winbind interfaces we need to do this via names,
579 * not uids/gids.
582 return user_in_group_list(u_name, g_name, NULL, 0);
585 /****************************************************************************
586 A well formed POSIX file or default ACL has at least 3 entries, a
587 SMB_ACL_USER_OBJ, SMB_ACL_GROUP_OBJ, SMB_ACL_OTHER_OBJ.
588 In addition, the owner must always have at least read access.
589 When using this call on get_acl, the pst struct is valid and contains
590 the mode of the file. When using this call on set_acl, the pst struct has
591 been modified to have a mode containing the default for this file or directory
592 type.
593 ****************************************************************************/
595 static BOOL ensure_canon_entry_valid(canon_ace **pp_ace,
596 files_struct *fsp,
597 DOM_SID *pfile_owner_sid,
598 DOM_SID *pfile_grp_sid,
599 SMB_STRUCT_STAT *pst,
600 BOOL setting_acl)
602 extern DOM_SID global_sid_World;
603 canon_ace *pace;
604 BOOL got_user = False;
605 BOOL got_grp = False;
606 BOOL got_other = False;
607 canon_ace *pace_other = NULL;
608 canon_ace *pace_group = NULL;
609 connection_struct *conn = fsp->conn;
610 SMB_ACL_T current_posix_acl = NULL;
611 mode_t current_user_perms = 0;
612 mode_t current_grp_perms = 0;
613 mode_t current_other_perms = 0;
614 BOOL got_current_user = False;
615 BOOL got_current_grp = False;
616 BOOL got_current_other = False;
618 for (pace = *pp_ace; pace; pace = pace->next) {
619 if (pace->type == SMB_ACL_USER_OBJ) {
621 if (setting_acl)
622 apply_default_perms(fsp, pace, S_IRUSR);
623 got_user = True;
625 } else if (pace->type == SMB_ACL_GROUP_OBJ) {
628 * Ensure create mask/force create mode is respected on set.
631 if (setting_acl)
632 apply_default_perms(fsp, pace, S_IRGRP);
633 got_grp = True;
634 pace_group = pace;
636 } else if (pace->type == SMB_ACL_OTHER) {
639 * Ensure create mask/force create mode is respected on set.
642 if (setting_acl)
643 apply_default_perms(fsp, pace, S_IROTH);
644 got_other = True;
645 pace_other = pace;
650 * When setting ACLs and missing one out of SMB_ACL_USER_OBJ,
651 * SMB_ACL_GROUP_OBJ, SMB_ACL_OTHER, try to retrieve current
652 * values. For user and other a simple vfs_stat would do, but
653 * we would get mask instead of group. Let's do it via ACL.
656 if (setting_acl && (!got_user || !got_grp || !got_other)) {
658 SMB_ACL_ENTRY_T entry;
659 int entry_id = SMB_ACL_FIRST_ENTRY;
661 if(fsp->is_directory || fsp->fd == -1) {
662 current_posix_acl = conn->vfs_ops.sys_acl_get_file(conn, fsp->fsp_name, SMB_ACL_TYPE_ACCESS);
663 } else {
664 current_posix_acl = conn->vfs_ops.sys_acl_get_fd(fsp, fsp->fd);
667 if (current_posix_acl) {
668 while (conn->vfs_ops.sys_acl_get_entry(conn, current_posix_acl, entry_id, &entry) == 1) {
669 SMB_ACL_TAG_T tagtype;
670 SMB_ACL_PERMSET_T permset;
672 /* get_next... */
673 if (entry_id == SMB_ACL_FIRST_ENTRY)
674 entry_id = SMB_ACL_NEXT_ENTRY;
676 /* Is this a MASK entry ? */
677 if (conn->vfs_ops.sys_acl_get_tag_type(conn, entry, &tagtype) == -1)
678 continue;
680 if (conn->vfs_ops.sys_acl_get_permset(conn, entry, &permset) == -1)
681 continue;
683 switch(tagtype) {
684 case SMB_ACL_USER_OBJ:
685 current_user_perms = convert_permset_to_mode_t(conn, permset);
686 got_current_user = True;
687 break;
688 case SMB_ACL_GROUP_OBJ:
689 current_grp_perms = convert_permset_to_mode_t(conn, permset);
690 got_current_grp = True;
691 break;
692 case SMB_ACL_OTHER:
693 current_other_perms = convert_permset_to_mode_t(conn, permset);
694 got_current_other = True;
695 break;
698 conn->vfs_ops.sys_acl_free_acl(conn, current_posix_acl);
699 } else {
700 DEBUG(10,("ensure_canon_entry_valid: failed to retrieve current ACL of %s\n",
701 fsp->fsp_name));
705 if (!got_user) {
706 if ((pace = (canon_ace *)malloc(sizeof(canon_ace))) == NULL) {
707 DEBUG(0,("ensure_canon_entry_valid: malloc fail.\n"));
708 return False;
711 ZERO_STRUCTP(pace);
712 pace->type = SMB_ACL_USER_OBJ;
713 pace->owner_type = UID_ACE;
714 pace->unix_ug.uid = pst->st_uid;
715 pace->trustee = *pfile_owner_sid;
716 pace->attr = ALLOW_ACE;
718 if (setting_acl) {
719 if (got_current_user) {
720 pace->perms = current_user_perms;
721 } else {
722 /* If we only got an "everyone" perm, just use that. */
723 if (!got_grp && got_other)
724 pace->perms = pace_other->perms;
725 else if (got_grp && uid_entry_in_group(pace, pace_group))
726 pace->perms = pace_group->perms;
727 else
728 pace->perms = 0;
732 apply_default_perms(fsp, pace, S_IRUSR);
733 } else {
734 pace->perms = unix_perms_to_acl_perms(pst->st_mode, S_IRUSR, S_IWUSR, S_IXUSR);
737 DLIST_ADD(*pp_ace, pace);
740 if (!got_grp) {
741 if ((pace = (canon_ace *)malloc(sizeof(canon_ace))) == NULL) {
742 DEBUG(0,("ensure_canon_entry_valid: malloc fail.\n"));
743 return False;
746 ZERO_STRUCTP(pace);
747 pace->type = SMB_ACL_GROUP_OBJ;
748 pace->owner_type = GID_ACE;
749 pace->unix_ug.uid = pst->st_gid;
750 pace->trustee = *pfile_grp_sid;
751 pace->attr = ALLOW_ACE;
752 if (setting_acl) {
753 if (got_current_grp) {
754 pace->perms = current_grp_perms;
755 } else {
756 /* If we only got an "everyone" perm, just use that. */
757 if (got_other)
758 pace->perms = pace_other->perms;
759 else
760 pace->perms = unix_perms_to_acl_perms(pst->st_mode, S_IRGRP, S_IWGRP, S_IXGRP);
762 apply_default_perms(fsp, pace, S_IRGRP);
763 } else {
764 pace->perms = unix_perms_to_acl_perms(pst->st_mode, S_IRGRP, S_IWGRP, S_IXGRP);
767 DLIST_ADD(*pp_ace, pace);
770 if (!got_other) {
771 if ((pace = (canon_ace *)malloc(sizeof(canon_ace))) == NULL) {
772 DEBUG(0,("ensure_canon_entry_valid: malloc fail.\n"));
773 return False;
776 ZERO_STRUCTP(pace);
777 pace->type = SMB_ACL_OTHER;
778 pace->owner_type = WORLD_ACE;
779 pace->unix_ug.world = -1;
780 pace->trustee = global_sid_World;
781 pace->attr = ALLOW_ACE;
782 if (setting_acl) {
783 if (got_current_other)
784 pace->perms = current_other_perms;
785 else
786 pace->perms = 0;
787 apply_default_perms(fsp, pace, S_IROTH);
788 } else
789 pace->perms = unix_perms_to_acl_perms(pst->st_mode, S_IROTH, S_IWOTH, S_IXOTH);
791 DLIST_ADD(*pp_ace, pace);
794 return True;
797 /****************************************************************************
798 Check if a POSIX ACL has the required SMB_ACL_USER_OBJ and SMB_ACL_GROUP_OBJ entries.
799 If it does not have them, check if there are any entries where the trustee is the
800 file owner or the owning group, and map these to SMB_ACL_USER_OBJ and SMB_ACL_GROUP_OBJ.
801 ****************************************************************************/
803 static void check_owning_objs(canon_ace *ace, DOM_SID *pfile_owner_sid, DOM_SID *pfile_grp_sid)
805 BOOL got_user_obj, got_group_obj;
806 canon_ace *current_ace;
807 int i, entries;
809 entries = count_canon_ace_list(ace);
810 got_user_obj = False;
811 got_group_obj = False;
813 for (i=0, current_ace = ace; i < entries; i++, current_ace = current_ace->next) {
814 if (current_ace->type == SMB_ACL_USER_OBJ)
815 got_user_obj = True;
816 else if (current_ace->type == SMB_ACL_GROUP_OBJ)
817 got_group_obj = True;
819 if (got_user_obj && got_group_obj) {
820 DEBUG(10,("check_owning_objs: ACL had owning user/group entries.\n"));
821 return;
824 for (i=0, current_ace = ace; i < entries; i++, current_ace = current_ace->next) {
825 if (!got_user_obj && current_ace->owner_type == UID_ACE &&
826 sid_equal(&current_ace->trustee, pfile_owner_sid)) {
827 current_ace->type = SMB_ACL_USER_OBJ;
828 got_user_obj = True;
830 if (!got_group_obj && current_ace->owner_type == GID_ACE &&
831 sid_equal(&current_ace->trustee, pfile_grp_sid)) {
832 current_ace->type = SMB_ACL_GROUP_OBJ;
833 got_group_obj = True;
836 if (!got_user_obj)
837 DEBUG(10,("check_owning_objs: ACL is missing an owner entry.\n"));
838 if (!got_group_obj)
839 DEBUG(10,("check_owning_objs: ACL is missing an owning group entry.\n"));
842 /****************************************************************************
843 Unpack a SEC_DESC into two canonical ace lists.
844 ****************************************************************************/
846 static BOOL create_canon_ace_lists(files_struct *fsp,
847 DOM_SID *pfile_owner_sid,
848 DOM_SID *pfile_grp_sid,
849 canon_ace **ppfile_ace, canon_ace **ppdir_ace,
850 SEC_ACL *dacl)
852 extern DOM_SID global_sid_Creator_Owner;
853 extern DOM_SID global_sid_Creator_Group;
854 extern DOM_SID global_sid_World;
855 extern struct generic_mapping file_generic_mapping;
856 BOOL all_aces_are_inherit_only = (fsp->is_directory ? True : False);
857 canon_ace *file_ace = NULL;
858 canon_ace *dir_ace = NULL;
859 canon_ace *tmp_ace = NULL;
860 canon_ace *current_ace = NULL;
861 BOOL got_dir_allow = False;
862 BOOL got_file_allow = False;
863 int i, j;
865 *ppfile_ace = NULL;
866 *ppdir_ace = NULL;
869 * Convert the incoming ACL into a more regular form.
872 for(i = 0; i < dacl->num_aces; i++) {
873 SEC_ACE *psa = &dacl->ace[i];
875 if((psa->type != SEC_ACE_TYPE_ACCESS_ALLOWED) && (psa->type != SEC_ACE_TYPE_ACCESS_DENIED)) {
876 DEBUG(3,("create_canon_ace_lists: unable to set anything but an ALLOW or DENY ACE.\n"));
877 return False;
880 if (nt4_compatible_acls()) {
882 * The security mask may be UNIX_ACCESS_NONE which should map into
883 * no permissions (we overload the WRITE_OWNER bit for this) or it
884 * should be one of the ALL/EXECUTE/READ/WRITE bits. Arrange for this
885 * to be so. Any other bits override the UNIX_ACCESS_NONE bit.
889 * Convert GENERIC bits to specific bits.
892 se_map_generic(&psa->info.mask, &file_generic_mapping);
894 psa->info.mask &= (UNIX_ACCESS_NONE|FILE_ALL_ACCESS);
896 if(psa->info.mask != UNIX_ACCESS_NONE)
897 psa->info.mask &= ~UNIX_ACCESS_NONE;
902 * Deal with the fact that NT 4.x re-writes the canonical format
903 * that we return for default ACLs. If a directory ACE is identical
904 * to a inherited directory ACE then NT changes the bits so that the
905 * first ACE is set to OI|IO and the second ACE for this SID is set
906 * to CI. We need to repair this. JRA.
909 for(i = 0; i < dacl->num_aces; i++) {
910 SEC_ACE *psa1 = &dacl->ace[i];
912 for (j = i + 1; j < dacl->num_aces; j++) {
913 SEC_ACE *psa2 = &dacl->ace[j];
915 if (psa1->info.mask != psa2->info.mask)
916 continue;
918 if (!sid_equal(&psa1->trustee, &psa2->trustee))
919 continue;
922 * Ok - permission bits and SIDs are equal.
923 * Check if flags were re-written.
926 if (psa1->flags & SEC_ACE_FLAG_INHERIT_ONLY) {
928 psa1->flags |= (psa2->flags & (SEC_ACE_FLAG_CONTAINER_INHERIT|SEC_ACE_FLAG_OBJECT_INHERIT));
929 psa2->flags &= ~(SEC_ACE_FLAG_CONTAINER_INHERIT|SEC_ACE_FLAG_OBJECT_INHERIT);
931 } else if (psa2->flags & SEC_ACE_FLAG_INHERIT_ONLY) {
933 psa2->flags |= (psa1->flags & (SEC_ACE_FLAG_CONTAINER_INHERIT|SEC_ACE_FLAG_OBJECT_INHERIT));
934 psa1->flags &= ~(SEC_ACE_FLAG_CONTAINER_INHERIT|SEC_ACE_FLAG_OBJECT_INHERIT);
940 for(i = 0; i < dacl->num_aces; i++) {
941 enum SID_NAME_USE sid_type;
942 SEC_ACE *psa = &dacl->ace[i];
945 * Ignore non-mappable SIDs (NT Authority, BUILTIN etc).
948 if (non_mappable_sid(&psa->trustee)) {
949 fstring str;
950 DEBUG(10,("create_canon_ace_lists: ignoring non-mappable SID %s\n",
951 sid_to_string(str, &psa->trustee) ));
952 continue;
956 * Create a cannon_ace entry representing this NT DACL ACE.
959 if ((current_ace = (canon_ace *)malloc(sizeof(canon_ace))) == NULL) {
960 free_canon_ace_list(file_ace);
961 free_canon_ace_list(dir_ace);
962 DEBUG(0,("create_canon_ace_lists: malloc fail.\n"));
963 return False;
966 ZERO_STRUCTP(current_ace);
968 sid_copy(&current_ace->trustee, &psa->trustee);
971 * Try and work out if the SID is a user or group
972 * as we need to flag these differently for POSIX.
973 * Note what kind of a POSIX ACL this should map to.
976 if( sid_equal(&current_ace->trustee, &global_sid_World)) {
977 current_ace->owner_type = WORLD_ACE;
978 current_ace->unix_ug.world = -1;
979 current_ace->type = SMB_ACL_OTHER;
980 } else if (sid_equal(&current_ace->trustee, &global_sid_Creator_Owner)) {
981 current_ace->owner_type = UID_ACE;
982 current_ace->unix_ug.world = -1;
983 current_ace->type = SMB_ACL_USER_OBJ;
986 * The Creator Owner entry only specifies inheritable permissions,
987 * never access permissions. WinNT doesn't always set the ACE to
988 *INHERIT_ONLY, though.
991 if (nt4_compatible_acls())
992 psa->flags |= SEC_ACE_FLAG_INHERIT_ONLY;
993 } else if (sid_equal(&current_ace->trustee, &global_sid_Creator_Group)) {
994 current_ace->owner_type = GID_ACE;
995 current_ace->unix_ug.world = -1;
996 current_ace->type = SMB_ACL_GROUP_OBJ;
999 * The Creator Group entry only specifies inheritable permissions,
1000 * never access permissions. WinNT doesn't always set the ACE to
1001 *INHERIT_ONLY, though.
1003 if (nt4_compatible_acls())
1004 psa->flags |= SEC_ACE_FLAG_INHERIT_ONLY;
1006 } else if (sid_to_uid( &current_ace->trustee, &current_ace->unix_ug.uid, &sid_type)) {
1007 current_ace->owner_type = UID_ACE;
1008 current_ace->type = SMB_ACL_USER;
1009 } else if (sid_to_gid( &current_ace->trustee, &current_ace->unix_ug.gid, &sid_type)) {
1010 current_ace->owner_type = GID_ACE;
1011 current_ace->type = SMB_ACL_GROUP;
1012 } else {
1013 fstring str;
1015 free_canon_ace_list(file_ace);
1016 free_canon_ace_list(dir_ace);
1017 DEBUG(0,("create_canon_ace_lists: unable to map SID %s to uid or gid.\n",
1018 sid_to_string(str, &current_ace->trustee) ));
1019 SAFE_FREE(current_ace);
1020 return False;
1024 * Map the given NT permissions into a UNIX mode_t containing only
1025 * S_I(R|W|X)USR bits.
1028 current_ace->perms |= map_nt_perms( psa->info, S_IRUSR);
1029 current_ace->attr = (psa->type == SEC_ACE_TYPE_ACCESS_ALLOWED) ? ALLOW_ACE : DENY_ACE;
1032 * Now add the created ace to either the file list, the directory
1033 * list, or both. We *MUST* preserve the order here (hence we use
1034 * DLIST_ADD_END) as NT ACLs are order dependent.
1037 if (fsp->is_directory) {
1040 * We can only add to the default POSIX ACE list if the ACE is
1041 * designed to be inherited by both files and directories.
1044 if ((psa->flags & (SEC_ACE_FLAG_OBJECT_INHERIT|SEC_ACE_FLAG_CONTAINER_INHERIT)) ==
1045 (SEC_ACE_FLAG_OBJECT_INHERIT|SEC_ACE_FLAG_CONTAINER_INHERIT)) {
1047 DLIST_ADD_END(dir_ace, current_ace, tmp_ace);
1050 * Note if this was an allow ace. We can't process
1051 * any further deny ace's after this.
1054 if (current_ace->attr == ALLOW_ACE)
1055 got_dir_allow = True;
1057 if ((current_ace->attr == DENY_ACE) && got_dir_allow) {
1058 DEBUG(0,("create_canon_ace_lists: malformed ACL in inheritable ACL ! \
1059 Deny entry after Allow entry. Failing to set on file %s.\n", fsp->fsp_name ));
1060 free_canon_ace_list(file_ace);
1061 free_canon_ace_list(dir_ace);
1062 SAFE_FREE(current_ace);
1063 return False;
1066 if( DEBUGLVL( 10 )) {
1067 dbgtext("create_canon_ace_lists: adding dir ACL:\n");
1068 print_canon_ace( current_ace, 0);
1072 * If this is not an inherit only ACE we need to add a duplicate
1073 * to the file acl.
1076 if (!(psa->flags & SEC_ACE_FLAG_INHERIT_ONLY)) {
1077 canon_ace *dup_ace = dup_canon_ace(current_ace);
1079 if (!dup_ace) {
1080 DEBUG(0,("create_canon_ace_lists: malloc fail !\n"));
1081 free_canon_ace_list(file_ace);
1082 free_canon_ace_list(dir_ace);
1083 return False;
1087 * We must not free current_ace here as its
1088 * pointer is now owned by the dir_ace list.
1090 current_ace = dup_ace;
1091 } else {
1093 * We must not free current_ace here as its
1094 * pointer is now owned by the dir_ace list.
1096 current_ace = NULL;
1102 * Only add to the file ACL if not inherit only.
1105 if (!(psa->flags & SEC_ACE_FLAG_INHERIT_ONLY)) {
1106 DLIST_ADD_END(file_ace, current_ace, tmp_ace);
1109 * Note if this was an allow ace. We can't process
1110 * any further deny ace's after this.
1113 if (current_ace->attr == ALLOW_ACE)
1114 got_file_allow = True;
1116 if ((current_ace->attr == DENY_ACE) && got_file_allow) {
1117 DEBUG(0,("create_canon_ace_lists: malformed ACL in file ACL ! \
1118 Deny entry after Allow entry. Failing to set on file %s.\n", fsp->fsp_name ));
1119 free_canon_ace_list(file_ace);
1120 free_canon_ace_list(dir_ace);
1121 SAFE_FREE(current_ace);
1122 return False;
1125 if( DEBUGLVL( 10 )) {
1126 dbgtext("create_canon_ace_lists: adding file ACL:\n");
1127 print_canon_ace( current_ace, 0);
1129 all_aces_are_inherit_only = False;
1131 * We must not free current_ace here as its
1132 * pointer is now owned by the file_ace list.
1134 current_ace = NULL;
1138 * Free if ACE was not added.
1141 SAFE_FREE(current_ace);
1144 if (fsp->is_directory && all_aces_are_inherit_only) {
1146 * Windows 2000 is doing one of these weird 'inherit acl'
1147 * traverses to conserve NTFS ACL resources. Just pretend
1148 * there was no DACL sent. JRA.
1151 DEBUG(10,("create_canon_ace_lists: Win2k inherit acl traverse. Ignoring DACL.\n"));
1152 free_canon_ace_list(file_ace);
1153 free_canon_ace_list(dir_ace);
1154 file_ace = NULL;
1155 dir_ace = NULL;
1156 } else {
1158 * Check if we have SMB_ACL_USER_OBJ and SMB_ACL_GROUP_OBJ entries in each
1159 * ACL. If we don't have them, check if any SMB_ACL_USER/SMB_ACL_GROUP
1160 * entries can be converted to *_OBJ. Usually we will already have these
1161 * entries in the Default ACL, and the Access ACL will not have them.
1163 check_owning_objs(file_ace, pfile_owner_sid, pfile_grp_sid);
1164 check_owning_objs(dir_ace, pfile_owner_sid, pfile_grp_sid);
1167 *ppfile_ace = file_ace;
1168 *ppdir_ace = dir_ace;
1170 return True;
1173 /****************************************************************************
1174 ASCII art time again... JRA :-).
1176 We have 3 cases to process when moving from an NT ACL to a POSIX ACL. Firstly,
1177 we insist the ACL is in canonical form (ie. all DENY entries preceede ALLOW
1178 entries). Secondly, the merge code has ensured that all duplicate SID entries for
1179 allow or deny have been merged, so the same SID can only appear once in the deny
1180 list or once in the allow list.
1182 We then process as follows :
1184 ---------------------------------------------------------------------------
1185 First pass - look for a Everyone DENY entry.
1187 If it is deny all (rwx) trunate the list at this point.
1188 Else, walk the list from this point and use the deny permissions of this
1189 entry as a mask on all following allow entries. Finally, delete
1190 the Everyone DENY entry (we have applied it to everything possible).
1192 In addition, in this pass we remove any DENY entries that have
1193 no permissions (ie. they are a DENY nothing).
1194 ---------------------------------------------------------------------------
1195 Second pass - only deal with deny user entries.
1197 DENY user1 (perms XXX)
1199 new_perms = 0
1200 for all following allow group entries where user1 is in group
1201 new_perms |= group_perms;
1203 user1 entry perms = new_perms & ~ XXX;
1205 Convert the deny entry to an allow entry with the new perms and
1206 push to the end of the list. Note if the user was in no groups
1207 this maps to a specific allow nothing entry for this user.
1209 The common case from the NT ACL choser (userX deny all) is
1210 optimised so we don't do the group lookup - we just map to
1211 an allow nothing entry.
1213 What we're doing here is inferring the allow permissions the
1214 person setting the ACE on user1 wanted by looking at the allow
1215 permissions on the groups the user is currently in. This will
1216 be a snapshot, depending on group membership but is the best
1217 we can do and has the advantage of failing closed rather than
1218 open.
1219 ---------------------------------------------------------------------------
1220 Third pass - only deal with deny group entries.
1222 DENY group1 (perms XXX)
1224 for all following allow user entries where user is in group1
1225 user entry perms = user entry perms & ~ XXX;
1227 If there is a group Everyone allow entry with permissions YYY,
1228 convert the group1 entry to an allow entry and modify its
1229 permissions to be :
1231 new_perms = YYY & ~ XXX
1233 and push to the end of the list.
1235 If there is no group Everyone allow entry then convert the
1236 group1 entry to a allow nothing entry and push to the end of the list.
1238 Note that the common case from the NT ACL choser (groupX deny all)
1239 cannot be optimised here as we need to modify user entries who are
1240 in the group to change them to a deny all also.
1242 What we're doing here is modifying the allow permissions of
1243 user entries (which are more specific in POSIX ACLs) to mask
1244 out the explicit deny set on the group they are in. This will
1245 be a snapshot depending on current group membership but is the
1246 best we can do and has the advantage of failing closed rather
1247 than open.
1248 ---------------------------------------------------------------------------
1250 Note we *MUST* do the deny user pass first as this will convert deny user
1251 entries into allow user entries which can then be processed by the deny
1252 group pass.
1254 The above algorithm took a *lot* of thinking about - hence this
1255 explaination :-). JRA.
1256 ****************************************************************************/
1258 /****************************************************************************
1259 Process a canon_ace list entries. This is very complex code. We need
1260 to go through and remove the "deny" permissions from any allow entry that matches
1261 the id of this entry. We have already refused any NT ACL that wasn't in correct
1262 order (DENY followed by ALLOW). If any allow entry ends up with zero permissions,
1263 we just remove it (to fail safe). We have already removed any duplicate ace
1264 entries. Treat an "Everyone" DENY_ACE as a special case - use it to mask all
1265 allow entries.
1266 ****************************************************************************/
1268 static void process_deny_list( canon_ace **pp_ace_list )
1270 extern DOM_SID global_sid_World;
1271 canon_ace *ace_list = *pp_ace_list;
1272 canon_ace *curr_ace = NULL;
1273 canon_ace *curr_ace_next = NULL;
1275 /* Pass 1 above - look for an Everyone, deny entry. */
1277 for (curr_ace = ace_list; curr_ace; curr_ace = curr_ace_next) {
1278 canon_ace *allow_ace_p;
1280 curr_ace_next = curr_ace->next; /* So we can't lose the link. */
1282 if (curr_ace->attr != DENY_ACE)
1283 continue;
1285 if (curr_ace->perms == (mode_t)0) {
1287 /* Deny nothing entry - delete. */
1289 DLIST_REMOVE(ace_list, curr_ace);
1290 continue;
1293 if (!sid_equal(&curr_ace->trustee, &global_sid_World))
1294 continue;
1296 /* JRATEST - assert. */
1297 SMB_ASSERT(curr_ace->owner_type == WORLD_ACE);
1299 if (curr_ace->perms == ALL_ACE_PERMS) {
1302 * Optimisation. This is a DENY_ALL to Everyone. Truncate the
1303 * list at this point including this entry.
1306 canon_ace *prev_entry = curr_ace->prev;
1308 free_canon_ace_list( curr_ace );
1309 if (prev_entry)
1310 prev_entry->next = NULL;
1311 else {
1312 /* We deleted the entire list. */
1313 ace_list = NULL;
1315 break;
1318 for (allow_ace_p = curr_ace->next; allow_ace_p; allow_ace_p = allow_ace_p->next) {
1321 * Only mask off allow entries.
1324 if (allow_ace_p->attr != ALLOW_ACE)
1325 continue;
1327 allow_ace_p->perms &= ~curr_ace->perms;
1331 * Now it's been applied, remove it.
1334 DLIST_REMOVE(ace_list, curr_ace);
1337 /* Pass 2 above - deal with deny user entries. */
1339 for (curr_ace = ace_list; curr_ace; curr_ace = curr_ace_next) {
1340 mode_t new_perms = (mode_t)0;
1341 canon_ace *allow_ace_p;
1342 canon_ace *tmp_ace;
1344 curr_ace_next = curr_ace->next; /* So we can't lose the link. */
1346 if (curr_ace->attr != DENY_ACE)
1347 continue;
1349 if (curr_ace->owner_type != UID_ACE)
1350 continue;
1352 if (curr_ace->perms == ALL_ACE_PERMS) {
1355 * Optimisation - this is a deny everything to this user.
1356 * Convert to an allow nothing and push to the end of the list.
1359 curr_ace->attr = ALLOW_ACE;
1360 curr_ace->perms = (mode_t)0;
1361 DLIST_DEMOTE(ace_list, curr_ace, tmp_ace);
1362 continue;
1365 for (allow_ace_p = curr_ace->next; allow_ace_p; allow_ace_p = allow_ace_p->next) {
1367 if (allow_ace_p->attr != ALLOW_ACE)
1368 continue;
1370 /* We process GID_ACE and WORLD_ACE entries only. */
1372 if (allow_ace_p->owner_type == UID_ACE)
1373 continue;
1375 if (uid_entry_in_group( curr_ace, allow_ace_p))
1376 new_perms |= allow_ace_p->perms;
1380 * Convert to a allow entry, modify the perms and push to the end
1381 * of the list.
1384 curr_ace->attr = ALLOW_ACE;
1385 curr_ace->perms = (new_perms & ~curr_ace->perms);
1386 DLIST_DEMOTE(ace_list, curr_ace, tmp_ace);
1389 /* Pass 3 above - deal with deny group entries. */
1391 for (curr_ace = ace_list; curr_ace; curr_ace = curr_ace_next) {
1392 canon_ace *tmp_ace;
1393 canon_ace *allow_ace_p;
1394 canon_ace *allow_everyone_p = NULL;
1396 curr_ace_next = curr_ace->next; /* So we can't lose the link. */
1398 if (curr_ace->attr != DENY_ACE)
1399 continue;
1401 if (curr_ace->owner_type != GID_ACE)
1402 continue;
1404 for (allow_ace_p = curr_ace->next; allow_ace_p; allow_ace_p = allow_ace_p->next) {
1406 if (allow_ace_p->attr != ALLOW_ACE)
1407 continue;
1409 /* Store a pointer to the Everyone allow, if it exists. */
1410 if (allow_ace_p->owner_type == WORLD_ACE)
1411 allow_everyone_p = allow_ace_p;
1413 /* We process UID_ACE entries only. */
1415 if (allow_ace_p->owner_type != UID_ACE)
1416 continue;
1418 /* Mask off the deny group perms. */
1420 if (uid_entry_in_group( allow_ace_p, curr_ace))
1421 allow_ace_p->perms &= ~curr_ace->perms;
1425 * Convert the deny to an allow with the correct perms and
1426 * push to the end of the list.
1429 curr_ace->attr = ALLOW_ACE;
1430 if (allow_everyone_p)
1431 curr_ace->perms = allow_everyone_p->perms & ~curr_ace->perms;
1432 else
1433 curr_ace->perms = (mode_t)0;
1434 DLIST_DEMOTE(ace_list, curr_ace, tmp_ace);
1438 *pp_ace_list = ace_list;
1441 /****************************************************************************
1442 Create a default mode that will be used if a security descriptor entry has
1443 no user/group/world entries.
1444 ****************************************************************************/
1446 static mode_t create_default_mode(files_struct *fsp, BOOL interitable_mode)
1448 int snum = SNUM(fsp->conn);
1449 mode_t and_bits = (mode_t)0;
1450 mode_t or_bits = (mode_t)0;
1451 mode_t mode = interitable_mode ? unix_mode( fsp->conn, FILE_ATTRIBUTE_ARCHIVE, fsp->fsp_name) : S_IRUSR;
1453 if (fsp->is_directory)
1454 mode |= (S_IWUSR|S_IXUSR);
1457 * Now AND with the create mode/directory mode bits then OR with the
1458 * force create mode/force directory mode bits.
1461 if (fsp->is_directory) {
1462 and_bits = lp_dir_security_mask(snum);
1463 or_bits = lp_force_dir_security_mode(snum);
1464 } else {
1465 and_bits = lp_security_mask(snum);
1466 or_bits = lp_force_security_mode(snum);
1469 return ((mode & and_bits)|or_bits);
1472 /****************************************************************************
1473 Unpack a SEC_DESC into two canonical ace lists. We don't depend on this
1474 succeeding.
1475 ****************************************************************************/
1477 static BOOL unpack_canon_ace(files_struct *fsp,
1478 SMB_STRUCT_STAT *pst,
1479 DOM_SID *pfile_owner_sid,
1480 DOM_SID *pfile_grp_sid,
1481 canon_ace **ppfile_ace, canon_ace **ppdir_ace,
1482 uint32 security_info_sent, SEC_DESC *psd)
1484 canon_ace *file_ace = NULL;
1485 canon_ace *dir_ace = NULL;
1487 *ppfile_ace = NULL;
1488 *ppdir_ace = NULL;
1490 if(security_info_sent == 0) {
1491 DEBUG(0,("unpack_canon_ace: no security info sent !\n"));
1492 return False;
1496 * If no DACL then this is a chown only security descriptor.
1499 if(!(security_info_sent & DACL_SECURITY_INFORMATION) || !psd->dacl)
1500 return True;
1503 * Now go through the DACL and create the canon_ace lists.
1506 if (!create_canon_ace_lists( fsp, pfile_owner_sid, pfile_grp_sid,
1507 &file_ace, &dir_ace, psd->dacl))
1508 return False;
1510 if ((file_ace == NULL) && (dir_ace == NULL)) {
1511 /* W2K traverse DACL set - ignore. */
1512 return True;
1516 * Go through the canon_ace list and merge entries
1517 * belonging to identical users of identical allow or deny type.
1518 * We can do this as all deny entries come first, followed by
1519 * all allow entries (we have mandated this before accepting this acl).
1522 print_canon_ace_list( "file ace - before merge", file_ace);
1523 merge_aces( &file_ace );
1525 print_canon_ace_list( "dir ace - before merge", dir_ace);
1526 merge_aces( &dir_ace );
1529 * NT ACLs are order dependent. Go through the acl lists and
1530 * process DENY entries by masking the allow entries.
1533 print_canon_ace_list( "file ace - before deny", file_ace);
1534 process_deny_list( &file_ace);
1536 print_canon_ace_list( "dir ace - before deny", dir_ace);
1537 process_deny_list( &dir_ace);
1540 * A well formed POSIX file or default ACL has at least 3 entries, a
1541 * SMB_ACL_USER_OBJ, SMB_ACL_GROUP_OBJ, SMB_ACL_OTHER_OBJ
1542 * and optionally a mask entry. Ensure this is the case.
1545 print_canon_ace_list( "file ace - before valid", file_ace);
1548 * A default 3 element mode entry for a file should be r-- --- ---.
1549 * A default 3 element mode entry for a directory should be rwx --- ---.
1552 pst->st_mode = create_default_mode(fsp, False);
1554 if (!ensure_canon_entry_valid(&file_ace, fsp, pfile_owner_sid, pfile_grp_sid, pst, True)) {
1555 free_canon_ace_list(file_ace);
1556 free_canon_ace_list(dir_ace);
1557 return False;
1560 print_canon_ace_list( "dir ace - before valid", dir_ace);
1563 * A default inheritable 3 element mode entry for a directory should be the
1564 * mode Samba will use to create a file within. Ensure user rwx bits are set if
1565 * it's a directory.
1568 pst->st_mode = create_default_mode(fsp, True);
1570 if (dir_ace && !ensure_canon_entry_valid(&dir_ace, fsp, pfile_owner_sid, pfile_grp_sid, pst, True)) {
1571 free_canon_ace_list(file_ace);
1572 free_canon_ace_list(dir_ace);
1573 return False;
1576 print_canon_ace_list( "file ace - return", file_ace);
1577 print_canon_ace_list( "dir ace - return", dir_ace);
1579 *ppfile_ace = file_ace;
1580 *ppdir_ace = dir_ace;
1581 return True;
1585 /******************************************************************************
1586 When returning permissions, try and fit NT display
1587 semantics if possible. Note the the canon_entries here must have been malloced.
1588 The list format should be - first entry = owner, followed by group and other user
1589 entries, last entry = other.
1591 Note that this doesn't exactly match the NT semantics for an ACL. As POSIX entries
1592 are not ordered, and match on the most specific entry rather than walking a list,
1593 then a simple POSIX permission of rw-r--r-- should really map to 5 entries,
1595 Entry 0: owner : deny all except read and write.
1596 Entry 1: group : deny all except read.
1597 Entry 2: owner : allow read and write.
1598 Entry 3: group : allow read.
1599 Entry 4: Everyone : allow read.
1601 But NT cannot display this in their ACL editor !
1602 ********************************************************************************/
1604 static void arrange_posix_perms( char *filename, canon_ace **pp_list_head)
1606 canon_ace *list_head = *pp_list_head;
1607 canon_ace *owner_ace = NULL;
1608 canon_ace *other_ace = NULL;
1609 canon_ace *ace = NULL;
1611 for (ace = list_head; ace; ace = ace->next) {
1612 if (ace->type == SMB_ACL_USER_OBJ)
1613 owner_ace = ace;
1614 else if (ace->type == SMB_ACL_OTHER) {
1615 /* Last ace - this is "other" */
1616 other_ace = ace;
1620 if (!owner_ace || !other_ace) {
1621 DEBUG(0,("arrange_posix_perms: Invalid POSIX permissions for file %s, missing owner or other.\n",
1622 filename ));
1623 return;
1627 * The POSIX algorithm applies to owner first, and other last,
1628 * so ensure they are arranged in this order.
1631 if (owner_ace) {
1632 DLIST_PROMOTE(list_head, owner_ace);
1635 if (other_ace) {
1636 DLIST_DEMOTE(list_head, other_ace, ace);
1639 /* We have probably changed the head of the list. */
1641 *pp_list_head = list_head;
1644 /****************************************************************************
1645 Create a linked list of canonical ACE entries.
1646 ****************************************************************************/
1648 static canon_ace *canonicalise_acl( files_struct *fsp, SMB_ACL_T posix_acl, SMB_STRUCT_STAT *psbuf,
1649 DOM_SID *powner, DOM_SID *pgroup, SMB_ACL_TYPE_T the_acl_type)
1651 extern DOM_SID global_sid_World;
1652 connection_struct *conn = fsp->conn;
1653 mode_t acl_mask = (S_IRUSR|S_IWUSR|S_IXUSR);
1654 canon_ace *list_head = NULL;
1655 canon_ace *ace = NULL;
1656 canon_ace *next_ace = NULL;
1657 int entry_id = SMB_ACL_FIRST_ENTRY;
1658 SMB_ACL_ENTRY_T entry;
1659 size_t ace_count;
1661 while ( posix_acl && (conn->vfs_ops.sys_acl_get_entry(conn, posix_acl, entry_id, &entry) == 1)) {
1662 SMB_ACL_TAG_T tagtype;
1663 SMB_ACL_PERMSET_T permset;
1664 DOM_SID sid;
1665 posix_id unix_ug;
1666 enum ace_owner owner_type;
1668 /* get_next... */
1669 if (entry_id == SMB_ACL_FIRST_ENTRY)
1670 entry_id = SMB_ACL_NEXT_ENTRY;
1672 /* Is this a MASK entry ? */
1673 if (conn->vfs_ops.sys_acl_get_tag_type(conn, entry, &tagtype) == -1)
1674 continue;
1676 if (conn->vfs_ops.sys_acl_get_permset(conn, entry, &permset) == -1)
1677 continue;
1679 /* Decide which SID to use based on the ACL type. */
1680 switch(tagtype) {
1681 case SMB_ACL_USER_OBJ:
1682 /* Get the SID from the owner. */
1683 sid_copy(&sid, powner);
1684 unix_ug.uid = psbuf->st_uid;
1685 owner_type = UID_ACE;
1686 break;
1687 case SMB_ACL_USER:
1689 uid_t *puid = (uid_t *)conn->vfs_ops.sys_acl_get_qualifier(conn, entry);
1690 if (puid == NULL) {
1691 DEBUG(0,("canonicalise_acl: Failed to get uid.\n"));
1692 continue;
1695 * A SMB_ACL_USER entry for the owner is shadowed by the
1696 * SMB_ACL_USER_OBJ entry and Windows also cannot represent
1697 * that entry, so we ignore it. We also don't create such
1698 * entries out of the blue when setting ACLs, so a get/set
1699 * cycle will drop them.
1701 if (the_acl_type == SMB_ACL_TYPE_ACCESS && *puid == psbuf->st_uid)
1702 continue;
1703 uid_to_sid( &sid, *puid);
1704 unix_ug.uid = *puid;
1705 owner_type = UID_ACE;
1706 conn->vfs_ops.sys_acl_free_qualifier(conn, (void *)puid,tagtype);
1707 break;
1709 case SMB_ACL_GROUP_OBJ:
1710 /* Get the SID from the owning group. */
1711 sid_copy(&sid, pgroup);
1712 unix_ug.gid = psbuf->st_gid;
1713 owner_type = GID_ACE;
1714 break;
1715 case SMB_ACL_GROUP:
1717 gid_t *pgid = (gid_t *)conn->vfs_ops.sys_acl_get_qualifier(conn, entry);
1718 if (pgid == NULL) {
1719 DEBUG(0,("canonicalise_acl: Failed to get gid.\n"));
1720 continue;
1722 gid_to_sid( &sid, *pgid);
1723 unix_ug.gid = *pgid;
1724 owner_type = GID_ACE;
1725 conn->vfs_ops.sys_acl_free_qualifier(conn, (void *)pgid,tagtype);
1726 break;
1728 case SMB_ACL_MASK:
1729 acl_mask = convert_permset_to_mode_t(conn, permset);
1730 continue; /* Don't count the mask as an entry. */
1731 case SMB_ACL_OTHER:
1732 /* Use the Everyone SID */
1733 sid = global_sid_World;
1734 unix_ug.world = -1;
1735 owner_type = WORLD_ACE;
1736 break;
1737 default:
1738 DEBUG(0,("canonicalise_acl: Unknown tagtype %u\n", (unsigned int)tagtype));
1739 continue;
1743 * Add this entry to the list.
1746 if ((ace = (canon_ace *)malloc(sizeof(canon_ace))) == NULL)
1747 goto fail;
1749 ZERO_STRUCTP(ace);
1750 ace->type = tagtype;
1751 ace->perms = convert_permset_to_mode_t(conn, permset);
1752 ace->attr = ALLOW_ACE;
1753 ace->trustee = sid;
1754 ace->unix_ug = unix_ug;
1755 ace->owner_type = owner_type;
1757 DLIST_ADD(list_head, ace);
1761 * This next call will ensure we have at least a user/group/world set.
1764 if (!ensure_canon_entry_valid(&list_head, fsp, powner, pgroup, psbuf, False))
1765 goto fail;
1767 arrange_posix_perms(fsp->fsp_name,&list_head );
1770 * Now go through the list, masking the permissions with the
1771 * acl_mask. Ensure all DENY Entries are at the start of the list.
1774 DEBUG(10,("canonicalise_acl: ace entries before arrange :\n"));
1776 for ( ace_count = 0, ace = list_head; ace; ace = next_ace, ace_count++) {
1777 next_ace = ace->next;
1779 /* Masks are only applied to entries other than USER_OBJ and OTHER. */
1780 if (ace->type != SMB_ACL_OTHER && ace->type != SMB_ACL_USER_OBJ)
1781 ace->perms &= acl_mask;
1783 if (ace->perms == 0) {
1784 DLIST_PROMOTE(list_head, ace);
1787 if( DEBUGLVL( 10 ) ) {
1788 print_canon_ace(ace, ace_count);
1792 print_canon_ace_list( "canonicalise_acl: ace entries after arrange", list_head );
1794 return list_head;
1796 fail:
1798 free_canon_ace_list(list_head);
1799 return NULL;
1802 /****************************************************************************
1803 Attempt to apply an ACL to a file or directory.
1804 ****************************************************************************/
1806 static BOOL set_canon_ace_list(files_struct *fsp, canon_ace *the_ace, BOOL default_ace, BOOL *pacl_set_support)
1808 connection_struct *conn = fsp->conn;
1809 BOOL ret = False;
1810 SMB_ACL_T the_acl = conn->vfs_ops.sys_acl_init(conn, (int)count_canon_ace_list(the_ace) + 1);
1811 canon_ace *p_ace;
1812 int i;
1813 SMB_ACL_ENTRY_T mask_entry;
1814 BOOL got_mask_entry = False;
1815 SMB_ACL_PERMSET_T mask_permset;
1816 SMB_ACL_TYPE_T the_acl_type = (default_ace ? SMB_ACL_TYPE_DEFAULT : SMB_ACL_TYPE_ACCESS);
1817 BOOL needs_mask = False;
1818 mode_t mask_perms = 0;
1820 #if defined(POSIX_ACL_NEEDS_MASK)
1821 /* HP-UX always wants to have a mask (called "class" there). */
1822 needs_mask = True;
1823 #endif
1825 if (the_acl == NULL) {
1827 if (errno != ENOSYS) {
1829 * Only print this error message if we have some kind of ACL
1830 * support that's not working. Otherwise we would always get this.
1832 DEBUG(0,("set_canon_ace_list: Unable to init %s ACL. (%s)\n",
1833 default_ace ? "default" : "file", strerror(errno) ));
1835 *pacl_set_support = False;
1836 return False;
1839 if( DEBUGLVL( 10 )) {
1840 dbgtext("set_canon_ace_list: setting ACL:\n");
1841 for (i = 0, p_ace = the_ace; p_ace; p_ace = p_ace->next, i++ ) {
1842 print_canon_ace( p_ace, i);
1846 for (i = 0, p_ace = the_ace; p_ace; p_ace = p_ace->next, i++ ) {
1847 SMB_ACL_ENTRY_T the_entry;
1848 SMB_ACL_PERMSET_T the_permset;
1851 * ACLs only "need" an ACL_MASK entry if there are any named user or
1852 * named group entries. But if there is an ACL_MASK entry, it applies
1853 * to ACL_USER, ACL_GROUP, and ACL_GROUP_OBJ entries. Set the mask
1854 * so that it doesn't deny (i.e., mask off) any permissions.
1857 if (p_ace->type == SMB_ACL_USER || p_ace->type == SMB_ACL_GROUP) {
1858 needs_mask = True;
1859 mask_perms |= p_ace->perms;
1860 } else if (p_ace->type == SMB_ACL_GROUP_OBJ) {
1861 mask_perms |= p_ace->perms;
1865 * Get the entry for this ACE.
1868 if (conn->vfs_ops.sys_acl_create_entry(conn, &the_acl, &the_entry) == -1) {
1869 DEBUG(0,("set_canon_ace_list: Failed to create entry %d. (%s)\n",
1870 i, strerror(errno) ));
1871 goto done;
1874 if (p_ace->type == SMB_ACL_MASK) {
1875 mask_entry = the_entry;
1876 got_mask_entry = True;
1880 * Ok - we now know the ACL calls should be working, don't
1881 * allow fallback to chmod.
1884 *pacl_set_support = True;
1887 * Initialise the entry from the canon_ace.
1891 * First tell the entry what type of ACE this is.
1894 if (conn->vfs_ops.sys_acl_set_tag_type(conn, the_entry, p_ace->type) == -1) {
1895 DEBUG(0,("set_canon_ace_list: Failed to set tag type on entry %d. (%s)\n",
1896 i, strerror(errno) ));
1897 goto done;
1901 * Only set the qualifier (user or group id) if the entry is a user
1902 * or group id ACE.
1905 if ((p_ace->type == SMB_ACL_USER) || (p_ace->type == SMB_ACL_GROUP)) {
1906 if (conn->vfs_ops.sys_acl_set_qualifier(conn, the_entry,(void *)&p_ace->unix_ug.uid) == -1) {
1907 DEBUG(0,("set_canon_ace_list: Failed to set qualifier on entry %d. (%s)\n",
1908 i, strerror(errno) ));
1909 goto done;
1914 * Convert the mode_t perms in the canon_ace to a POSIX permset.
1917 if (conn->vfs_ops.sys_acl_get_permset(conn, the_entry, &the_permset) == -1) {
1918 DEBUG(0,("set_canon_ace_list: Failed to get permset on entry %d. (%s)\n",
1919 i, strerror(errno) ));
1920 goto done;
1923 if (map_acl_perms_to_permset(conn, p_ace->perms, &the_permset) == -1) {
1924 DEBUG(0,("set_canon_ace_list: Failed to create permset for mode (%u) on entry %d. (%s)\n",
1925 (unsigned int)p_ace->perms, i, strerror(errno) ));
1926 goto done;
1930 * ..and apply them to the entry.
1933 if (conn->vfs_ops.sys_acl_set_permset(conn, the_entry, the_permset) == -1) {
1934 DEBUG(0,("set_canon_ace_list: Failed to add permset on entry %d. (%s)\n",
1935 i, strerror(errno) ));
1936 goto done;
1939 if( DEBUGLVL( 10 ))
1940 print_canon_ace( p_ace, i);
1943 if (needs_mask && !got_mask_entry) {
1944 if (conn->vfs_ops.sys_acl_create_entry(conn, &the_acl, &mask_entry) == -1) {
1945 DEBUG(0,("set_canon_ace_list: Failed to create mask entry. (%s)\n", strerror(errno) ));
1946 goto done;
1949 if (conn->vfs_ops.sys_acl_set_tag_type(conn, mask_entry, SMB_ACL_MASK) == -1) {
1950 DEBUG(0,("set_canon_ace_list: Failed to set tag type on mask entry. (%s)\n",strerror(errno) ));
1951 goto done;
1954 if (conn->vfs_ops.sys_acl_get_permset(conn, mask_entry, &mask_permset) == -1) {
1955 DEBUG(0,("set_canon_ace_list: Failed to get mask permset. (%s)\n", strerror(errno) ));
1956 goto done;
1959 if (map_acl_perms_to_permset(conn, S_IRUSR|S_IWUSR|S_IXUSR, &mask_permset) == -1) {
1960 DEBUG(0,("set_canon_ace_list: Failed to create mask permset. (%s)\n", strerror(errno) ));
1961 goto done;
1964 if (conn->vfs_ops.sys_acl_set_permset(conn, mask_entry, mask_permset) == -1) {
1965 DEBUG(0,("set_canon_ace_list: Failed to add mask permset. (%s)\n", strerror(errno) ));
1966 goto done;
1971 * Check if the ACL is valid.
1974 if (conn->vfs_ops.sys_acl_valid(conn, the_acl) == -1) {
1975 DEBUG(0,("set_canon_ace_list: ACL type (%s) is invalid for set (%s).\n",
1976 the_acl_type == SMB_ACL_TYPE_DEFAULT ? "directory default" : "file",
1977 strerror(errno) ));
1978 goto done;
1982 * Finally apply it to the file or directory.
1985 if(default_ace || fsp->is_directory || fsp->fd == -1) {
1986 if (conn->vfs_ops.sys_acl_set_file(conn, fsp->fsp_name, the_acl_type, the_acl) == -1) {
1988 * Some systems allow all the above calls and only fail with no ACL support
1989 * when attempting to apply the acl. HPUX with HFS is an example of this. JRA.
1991 if (errno == ENOSYS)
1992 *pacl_set_support = False;
1994 #ifdef ENOTSUP
1995 if (errno == ENOTSUP)
1996 *pacl_set_support = False;
1997 #endif
1999 DEBUG(2,("set_canon_ace_list: sys_acl_set_file type %s failed for file %s (%s).\n",
2000 the_acl_type == SMB_ACL_TYPE_DEFAULT ? "directory default" : "file",
2001 fsp->fsp_name, strerror(errno) ));
2002 goto done;
2004 } else {
2005 if (conn->vfs_ops.sys_acl_set_fd(fsp, fsp->fd, the_acl) == -1) {
2007 * Some systems allow all the above calls and only fail with no ACL support
2008 * when attempting to apply the acl. HPUX with HFS is an example of this. JRA.
2010 if (errno == ENOSYS)
2011 *pacl_set_support = False;
2013 #ifdef ENOTSUP
2014 if (errno == ENOTSUP)
2015 *pacl_set_support = False;
2016 #endif
2018 DEBUG(2,("set_canon_ace_list: sys_acl_set_file failed for file %s (%s).\n",
2019 fsp->fsp_name, strerror(errno) ));
2020 goto done;
2024 ret = True;
2026 done:
2028 if (the_acl != NULL)
2029 conn->vfs_ops.sys_acl_free_acl(conn, the_acl);
2031 return ret;
2034 /****************************************************************************
2035 Find a particular canon_ace entry.
2036 ****************************************************************************/
2038 static struct canon_ace *canon_ace_entry_for(struct canon_ace *list, SMB_ACL_TAG_T type, posix_id *id)
2040 while (list) {
2041 if (list->type == type && ((type != SMB_ACL_USER && type != SMB_ACL_GROUP) ||
2042 (type == SMB_ACL_USER && id && id->uid == list->unix_ug.uid) ||
2043 (type == SMB_ACL_GROUP && id && id->gid == list->unix_ug.gid)))
2044 break;
2045 list = list->next;
2047 return list;
2050 /****************************************************************************
2052 ****************************************************************************/
2054 SMB_ACL_T free_empty_sys_acl(connection_struct *conn, SMB_ACL_T acl)
2056 SMB_ACL_ENTRY_T entry;
2058 if (!acl)
2059 return NULL;
2060 if (conn->vfs_ops.sys_acl_get_entry(conn, acl, SMB_ACL_FIRST_ENTRY, &entry) != 1) {
2061 conn->vfs_ops.sys_acl_free_acl(conn, acl);
2062 return NULL;
2064 return acl;
2067 /****************************************************************************
2068 Convert a canon_ace to a generic 3 element permission - if possible.
2069 ****************************************************************************/
2071 #define MAP_PERM(p,mask,result) (((p) & (mask)) ? (result) : 0 )
2073 static BOOL convert_canon_ace_to_posix_perms( files_struct *fsp, canon_ace *file_ace_list, mode_t *posix_perms)
2075 int snum = SNUM(fsp->conn);
2076 size_t ace_count = count_canon_ace_list(file_ace_list);
2077 canon_ace *ace_p;
2078 canon_ace *owner_ace = NULL;
2079 canon_ace *group_ace = NULL;
2080 canon_ace *other_ace = NULL;
2081 mode_t and_bits;
2082 mode_t or_bits;
2084 if (ace_count != 3) {
2085 DEBUG(3,("convert_canon_ace_to_posix_perms: Too many ACE entries for file %s to convert to \
2086 posix perms.\n", fsp->fsp_name ));
2087 return False;
2090 for (ace_p = file_ace_list; ace_p; ace_p = ace_p->next) {
2091 if (ace_p->owner_type == UID_ACE)
2092 owner_ace = ace_p;
2093 else if (ace_p->owner_type == GID_ACE)
2094 group_ace = ace_p;
2095 else if (ace_p->owner_type == WORLD_ACE)
2096 other_ace = ace_p;
2099 if (!owner_ace || !group_ace || !other_ace) {
2100 DEBUG(3,("convert_canon_ace_to_posix_perms: Can't get standard entries for file %s.\n",
2101 fsp->fsp_name ));
2102 return False;
2105 *posix_perms = (mode_t)0;
2107 *posix_perms |= owner_ace->perms;
2108 *posix_perms |= MAP_PERM(group_ace->perms, S_IRUSR, S_IRGRP);
2109 *posix_perms |= MAP_PERM(group_ace->perms, S_IWUSR, S_IWGRP);
2110 *posix_perms |= MAP_PERM(group_ace->perms, S_IXUSR, S_IXGRP);
2111 *posix_perms |= MAP_PERM(other_ace->perms, S_IRUSR, S_IROTH);
2112 *posix_perms |= MAP_PERM(other_ace->perms, S_IWUSR, S_IWOTH);
2113 *posix_perms |= MAP_PERM(other_ace->perms, S_IXUSR, S_IXOTH);
2115 /* The owner must have at least read access. */
2117 *posix_perms |= S_IRUSR;
2118 if (fsp->is_directory)
2119 *posix_perms |= (S_IWUSR|S_IXUSR);
2121 /* If requested apply the masks. */
2123 /* Get the initial bits to apply. */
2125 if (fsp->is_directory) {
2126 and_bits = lp_dir_security_mask(snum);
2127 or_bits = lp_force_dir_security_mode(snum);
2128 } else {
2129 and_bits = lp_security_mask(snum);
2130 or_bits = lp_force_security_mode(snum);
2133 *posix_perms = (((*posix_perms) & and_bits)|or_bits);
2135 DEBUG(10,("convert_canon_ace_to_posix_perms: converted u=%o,g=%o,w=%o to perm=0%o for file %s.\n",
2136 (int)owner_ace->perms, (int)group_ace->perms, (int)other_ace->perms, (int)*posix_perms,
2137 fsp->fsp_name ));
2139 return True;
2142 static int nt_ace_comp( SEC_ACE *a1, SEC_ACE *a2)
2144 if (a1->type == a2->type)
2145 return 0;
2147 if (a1->type == SEC_ACE_TYPE_ACCESS_DENIED && a2->type == SEC_ACE_TYPE_ACCESS_ALLOWED)
2148 return -1;
2149 return 1;
2152 /****************************************************************************
2153 Incoming NT ACLs on a directory can be split into a default POSIX acl (CI|OI|IO) and
2154 a normal POSIX acl. Win2k needs these split acls re-merging into one ACL
2155 with CI|OI set so it is inherited and also applies to the directory.
2156 Based on code from "Jim McDonough" <jmcd@us.ibm.com>.
2157 ****************************************************************************/
2159 static size_t merge_default_aces( SEC_ACE *nt_ace_list, size_t num_aces)
2161 size_t i, j;
2163 for (i = 0; i < num_aces; i++) {
2164 for (j = i+1; j < num_aces; j++) {
2165 /* We know the lower number ACE's are file entries. */
2166 if ((nt_ace_list[i].type == nt_ace_list[j].type) &&
2167 (nt_ace_list[i].size == nt_ace_list[j].size) &&
2168 (nt_ace_list[i].info.mask == nt_ace_list[j].info.mask) &&
2169 sid_equal(&nt_ace_list[i].trustee, &nt_ace_list[j].trustee) &&
2170 (nt_ace_list[i].flags == 0) &&
2171 (nt_ace_list[j].flags == (SEC_ACE_FLAG_OBJECT_INHERIT|
2172 SEC_ACE_FLAG_CONTAINER_INHERIT|
2173 SEC_ACE_FLAG_INHERIT_ONLY))) {
2175 * These are identical except for the flags.
2176 * Merge the inherited ACE onto the non-inherited ACE.
2179 nt_ace_list[i].flags = SEC_ACE_FLAG_OBJECT_INHERIT|SEC_ACE_FLAG_CONTAINER_INHERIT;
2180 if (num_aces - j - 1 > 0)
2181 memmove(&nt_ace_list[j], &nt_ace_list[j+1], (num_aces-j-1) *
2182 sizeof(SEC_ACE));
2183 num_aces--;
2184 break;
2189 return num_aces;
2191 /****************************************************************************
2192 Reply to query a security descriptor from an fsp. If it succeeds it allocates
2193 the space for the return elements and returns the size needed to return the
2194 security descriptor. This should be the only external function needed for
2195 the UNIX style get ACL.
2196 ****************************************************************************/
2198 size_t get_nt_acl(files_struct *fsp, SEC_DESC **ppdesc)
2200 extern DOM_SID global_sid_Builtin_Administrators;
2201 extern DOM_SID global_sid_Builtin_Users;
2202 extern DOM_SID global_sid_Creator_Owner;
2203 extern DOM_SID global_sid_Creator_Group;
2204 connection_struct *conn = fsp->conn;
2205 SMB_STRUCT_STAT sbuf;
2206 SEC_ACE *nt_ace_list = NULL;
2207 DOM_SID owner_sid;
2208 DOM_SID group_sid;
2209 size_t sd_size = 0;
2210 SEC_ACL *psa = NULL;
2211 size_t num_acls = 0;
2212 size_t num_dir_acls = 0;
2213 size_t num_aces = 0;
2214 SMB_ACL_T posix_acl = NULL;
2215 SMB_ACL_T dir_acl = NULL;
2216 canon_ace *file_ace = NULL;
2217 canon_ace *dir_ace = NULL;
2218 size_t num_profile_acls = 0;
2220 *ppdesc = NULL;
2222 DEBUG(10,("get_nt_acl: called for file %s\n", fsp->fsp_name ));
2224 if(fsp->is_directory || fsp->fd == -1) {
2226 /* Get the stat struct for the owner info. */
2227 if(vfs_stat(fsp->conn,fsp->fsp_name, &sbuf) != 0) {
2228 return 0;
2231 * Get the ACL from the path.
2234 posix_acl = conn->vfs_ops.sys_acl_get_file(conn, fsp->fsp_name, SMB_ACL_TYPE_ACCESS);
2237 * If it's a directory get the default POSIX ACL.
2240 if(fsp->is_directory) {
2241 dir_acl = conn->vfs_ops.sys_acl_get_file(conn, fsp->fsp_name, SMB_ACL_TYPE_DEFAULT);
2242 dir_acl = free_empty_sys_acl(conn, dir_acl);
2245 } else {
2247 /* Get the stat struct for the owner info. */
2248 if(vfs_fstat(fsp,fsp->fd,&sbuf) != 0) {
2249 return 0;
2252 * Get the ACL from the fd.
2254 posix_acl = conn->vfs_ops.sys_acl_get_fd(fsp, fsp->fd);
2257 DEBUG(5,("get_nt_acl : file ACL %s, directory ACL %s\n",
2258 posix_acl ? "present" : "absent",
2259 dir_acl ? "present" : "absent" ));
2262 * Get the owner, group and world SIDs.
2265 if (lp_profile_acls(SNUM(fsp->conn))) {
2266 /* For WXP SP1 the owner must be administrators. */
2267 sid_copy(&owner_sid, &global_sid_Builtin_Administrators);
2268 sid_copy(&group_sid, &global_sid_Builtin_Users);
2269 num_profile_acls = 2;
2270 } else {
2271 create_file_sids(&sbuf, &owner_sid, &group_sid);
2275 * In the optimum case Creator Owner and Creator Group would be used for
2276 * the ACL_USER_OBJ and ACL_GROUP_OBJ entries, respectively, but this
2277 * would lead to usability problems under Windows: The Creator entries
2278 * are only available in browse lists of directories and not for files;
2279 * additionally the identity of the owning group couldn't be determined.
2280 * We therefore use those identities only for Default ACLs.
2283 /* Create the canon_ace lists. */
2284 file_ace = canonicalise_acl( fsp, posix_acl, &sbuf, &owner_sid, &group_sid, SMB_ACL_TYPE_ACCESS );
2286 /* We must have *some* ACLS. */
2288 if (count_canon_ace_list(file_ace) == 0) {
2289 DEBUG(0,("get_nt_acl : No ACLs on file (%s) !\n", fsp->fsp_name ));
2290 return 0;
2293 if (fsp->is_directory && dir_acl) {
2294 dir_ace = canonicalise_acl(fsp, dir_acl, &sbuf,
2295 &global_sid_Creator_Owner,
2296 &global_sid_Creator_Group, SMB_ACL_TYPE_DEFAULT );
2300 * Create the NT ACE list from the canonical ace lists.
2304 canon_ace *ace;
2305 int nt_acl_type;
2306 int i;
2308 if (nt4_compatible_acls()) {
2310 * NT 4 chokes if an ACL contains an INHERIT_ONLY entry
2311 * but no non-INHERIT_ONLY entry for one SID. So we only
2312 * remove entries from the Access ACL if the
2313 * corresponding Default ACL entries have also been
2314 * removed. ACEs for CREATOR-OWNER and CREATOR-GROUP
2315 * are exceptions. We can do nothing
2316 * intelligent if the Default ACL contains entries that
2317 * are not also contained in the Access ACL, so this
2318 * case will still fail under NT 4.
2321 if (!dir_ace)
2322 goto simplify_file_ace_only;
2324 ace = canon_ace_entry_for(dir_ace, SMB_ACL_OTHER, NULL);
2325 if (ace && !ace->perms) {
2326 DLIST_REMOVE(dir_ace, ace);
2327 SAFE_FREE(ace);
2329 ace = canon_ace_entry_for(file_ace, SMB_ACL_OTHER, NULL);
2330 if (ace && !ace->perms) {
2331 DLIST_REMOVE(file_ace, ace);
2332 SAFE_FREE(ace);
2337 * WinNT doesn't usually have Creator Group
2338 * in browse lists, so we send this entry to
2339 * WinNT even if it contains no relevant
2340 * permissions. Once we can add
2341 * Creator Group to browse lists we can
2342 * re-enable this.
2345 #if 0
2346 ace = canon_ace_entry_for(dir_ace, SMB_ACL_GROUP_OBJ, NULL);
2347 if (ace && !ace->perms) {
2348 DLIST_REMOVE(dir_ace, ace);
2349 SAFE_FREE(ace);
2351 #endif
2353 ace = canon_ace_entry_for(file_ace, SMB_ACL_GROUP_OBJ, NULL);
2354 if (ace && !ace->perms) {
2355 DLIST_REMOVE(file_ace, ace);
2356 SAFE_FREE(ace);
2358 } else {
2360 ace = canon_ace_entry_for(dir_ace, SMB_ACL_OTHER, NULL);
2361 if (ace && !ace->perms) {
2362 DLIST_REMOVE(dir_ace, ace);
2363 SAFE_FREE(ace);
2365 ace = canon_ace_entry_for(dir_ace, SMB_ACL_GROUP_OBJ, NULL);
2366 if (ace && !ace->perms) {
2367 DLIST_REMOVE(dir_ace, ace);
2368 SAFE_FREE(ace);
2371 simplify_file_ace_only:
2373 ace = canon_ace_entry_for(file_ace, SMB_ACL_OTHER, NULL);
2374 if (ace && !ace->perms) {
2375 DLIST_REMOVE(file_ace, ace);
2376 SAFE_FREE(ace);
2379 ace = canon_ace_entry_for(file_ace, SMB_ACL_GROUP_OBJ, NULL);
2380 if (ace && !ace->perms) {
2381 DLIST_REMOVE(file_ace, ace);
2382 SAFE_FREE(ace);
2386 num_acls = count_canon_ace_list(file_ace);
2387 num_dir_acls = count_canon_ace_list(dir_ace);
2389 /* Allocate the ace list. */
2390 if ((nt_ace_list = (SEC_ACE *)malloc((num_acls + num_profile_acls + num_dir_acls)* sizeof(SEC_ACE))) == NULL) {
2391 DEBUG(0,("get_nt_acl: Unable to malloc space for nt_ace_list.\n"));
2392 goto done;
2395 memset(nt_ace_list, '\0', (num_acls + num_dir_acls) * sizeof(SEC_ACE) );
2398 * Create the NT ACE list from the canonical ace lists.
2401 ace = file_ace;
2403 for (i = 0; i < num_acls; i++, ace = ace->next) {
2404 SEC_ACCESS acc;
2406 acc = map_canon_ace_perms(&nt_acl_type, &owner_sid, ace );
2407 init_sec_ace(&nt_ace_list[num_aces++], &ace->trustee, nt_acl_type, acc, 0);
2410 /* The User must have access to a profile share - even if we can't map the SID. */
2411 if (lp_profile_acls(SNUM(fsp->conn))) {
2412 SEC_ACCESS acc;
2414 init_sec_access(&acc,FILE_GENERIC_ALL);
2415 init_sec_ace(&nt_ace_list[num_aces++], &global_sid_Builtin_Users, SEC_ACE_TYPE_ACCESS_ALLOWED, acc, 0);
2418 ace = dir_ace;
2420 for (i = 0; i < num_dir_acls; i++, ace = ace->next) {
2421 SEC_ACCESS acc;
2423 acc = map_canon_ace_perms(&nt_acl_type, &owner_sid, ace );
2424 init_sec_ace(&nt_ace_list[num_aces++], &ace->trustee, nt_acl_type, acc,
2425 SEC_ACE_FLAG_OBJECT_INHERIT|SEC_ACE_FLAG_CONTAINER_INHERIT|SEC_ACE_FLAG_INHERIT_ONLY);
2428 /* The User must have access to a profile share - even if we can't map the SID. */
2429 if (lp_profile_acls(SNUM(fsp->conn))) {
2430 SEC_ACCESS acc;
2432 init_sec_access(&acc,FILE_GENERIC_ALL);
2433 init_sec_ace(&nt_ace_list[num_aces++], &global_sid_Builtin_Users, SEC_ACE_TYPE_ACCESS_ALLOWED, acc,
2434 SEC_ACE_FLAG_OBJECT_INHERIT|SEC_ACE_FLAG_CONTAINER_INHERIT|
2435 SEC_ACE_FLAG_INHERIT_ONLY);
2439 * Merge POSIX default ACLs and normal ACLs into one NT ACE.
2440 * Win2K needs this to get the inheritance correct when replacing ACLs
2441 * on a directory tree. Based on work by Jim @ IBM.
2444 num_aces = merge_default_aces(nt_ace_list, num_aces);
2447 * Sort to force deny entries to the front.
2450 if (num_aces)
2451 qsort( nt_ace_list, num_aces, sizeof(nt_ace_list[0]), QSORT_CAST nt_ace_comp);
2454 if (num_aces) {
2455 if((psa = make_sec_acl( main_loop_talloc_get(), ACL_REVISION, num_aces, nt_ace_list)) == NULL) {
2456 DEBUG(0,("get_nt_acl: Unable to malloc space for acl.\n"));
2457 goto done;
2461 *ppdesc = make_standard_sec_desc( main_loop_talloc_get(), &owner_sid, &group_sid, psa, &sd_size);
2463 if(!*ppdesc) {
2464 DEBUG(0,("get_nt_acl: Unable to malloc space for security descriptor.\n"));
2465 sd_size = 0;
2466 } else {
2468 * Windows 2000: The DACL_PROTECTED flag in the security
2469 * descriptor marks the ACL as non-inheriting, i.e., no
2470 * ACEs from higher level directories propagate to this
2471 * ACL. In the POSIX ACL model permissions are only
2472 * inherited at file create time, so ACLs never contain
2473 * any ACEs that are inherited dynamically. The DACL_PROTECTED
2474 * flag doesn't seem to bother Windows NT.
2476 (*ppdesc)->type |= SE_DESC_DACL_PROTECTED;
2479 done:
2481 if (posix_acl)
2482 conn->vfs_ops.sys_acl_free_acl(conn, posix_acl);
2483 if (dir_acl)
2484 conn->vfs_ops.sys_acl_free_acl(conn, dir_acl);
2485 free_canon_ace_list(file_ace);
2486 free_canon_ace_list(dir_ace);
2487 SAFE_FREE(nt_ace_list);
2489 return sd_size;
2492 /****************************************************************************
2493 Try to chown a file. We will be able to chown it under the following conditions.
2495 1) If we have root privileges, then it will just work.
2496 2) If we have write permission to the file and dos_filemodes is set
2497 then allow chown to the currently authenticated user.
2498 ****************************************************************************/
2500 static int try_chown(connection_struct *conn, const char *fname, uid_t uid, gid_t gid)
2502 int ret;
2503 extern struct current_user current_user;
2504 files_struct *fsp;
2505 SMB_STRUCT_STAT st;
2507 /* try the direct way first */
2508 ret = vfs_chown(conn, fname, uid, gid);
2509 if (ret == 0)
2510 return 0;
2512 if(!CAN_WRITE(conn) || !lp_dos_filemode(SNUM(conn)))
2513 return -1;
2515 if (vfs_stat(conn,fname,&st))
2516 return -1;
2518 fsp = open_file_fchmod(conn,fname,&st);
2519 if (!fsp)
2520 return -1;
2522 /* only allow chown to the current user. This is more secure,
2523 and also copes with the case where the SID in a take ownership ACL is
2524 a local SID on the users workstation
2526 uid = current_user.uid;
2528 become_root();
2529 /* Keep the current file gid the same. */
2530 ret = vfswrap_fchown(fsp, fsp->fd, uid, (gid_t)-1);
2531 unbecome_root();
2533 close_file_fchmod(fsp);
2535 return ret;
2538 /****************************************************************************
2539 Reply to set a security descriptor on an fsp. security_info_sent is the
2540 description of the following NT ACL.
2541 This should be the only external function needed for the UNIX style set ACL.
2542 ****************************************************************************/
2544 BOOL set_nt_acl(files_struct *fsp, uint32 security_info_sent, SEC_DESC *psd)
2546 connection_struct *conn = fsp->conn;
2547 uid_t user = (uid_t)-1;
2548 gid_t grp = (gid_t)-1;
2549 SMB_STRUCT_STAT sbuf;
2550 DOM_SID file_owner_sid;
2551 DOM_SID file_grp_sid;
2552 canon_ace *file_ace_list = NULL;
2553 canon_ace *dir_ace_list = NULL;
2554 BOOL acl_perms = False;
2555 mode_t orig_mode = (mode_t)0;
2556 uid_t orig_uid;
2557 gid_t orig_gid;
2558 BOOL need_chown = False;
2559 extern struct current_user current_user;
2561 DEBUG(10,("set_nt_acl: called for file %s\n", fsp->fsp_name ));
2563 if (!CAN_WRITE(conn)) {
2564 DEBUG(10,("set acl rejected on read-only share\n"));
2565 return False;
2569 * Get the current state of the file.
2572 if(fsp->is_directory || fsp->fd == -1) {
2573 if(vfs_stat(fsp->conn,fsp->fsp_name, &sbuf) != 0)
2574 return False;
2575 } else {
2576 if(vfs_fstat(fsp,fsp->fd,&sbuf) != 0)
2577 return False;
2580 /* Save the original elements we check against. */
2581 orig_mode = sbuf.st_mode;
2582 orig_uid = sbuf.st_uid;
2583 orig_gid = sbuf.st_gid;
2586 * Unpack the user/group/world id's.
2589 if (!unpack_nt_owners( &sbuf, &user, &grp, security_info_sent, psd))
2590 return False;
2593 * Do we need to chown ?
2596 if (((user != (uid_t)-1) && (orig_uid != user)) || (( grp != (uid_t)-1) && (orig_gid != grp)))
2597 need_chown = True;
2600 * Chown before setting ACL only if we don't change the user, or
2601 * if we change to the current user, but not if we want to give away
2602 * the file.
2605 if (need_chown && (user == (uid_t)-1 || user == current_user.uid)) {
2607 DEBUG(3,("set_nt_acl: chown %s. uid = %u, gid = %u.\n",
2608 fsp->fsp_name, (unsigned int)user, (unsigned int)grp ));
2610 if(try_chown( fsp->conn, fsp->fsp_name, user, grp) == -1) {
2611 DEBUG(3,("set_nt_acl: chown %s, %u, %u failed. Error = %s.\n",
2612 fsp->fsp_name, (unsigned int)user, (unsigned int)grp, strerror(errno) ));
2613 return False;
2617 * Recheck the current state of the file, which may have changed.
2618 * (suid/sgid bits, for instance)
2621 if(fsp->is_directory) {
2622 if(vfs_stat(fsp->conn, fsp->fsp_name, &sbuf) != 0) {
2623 return False;
2625 } else {
2627 int ret;
2629 if(fsp->fd == -1)
2630 ret = vfs_stat(fsp->conn, fsp->fsp_name, &sbuf);
2631 else
2632 ret = vfs_fstat(fsp,fsp->fd,&sbuf);
2634 if(ret != 0)
2635 return False;
2638 /* Save the original elements we check against. */
2639 orig_mode = sbuf.st_mode;
2640 orig_uid = sbuf.st_uid;
2641 orig_gid = sbuf.st_gid;
2643 /* We did it, don't try again */
2644 need_chown = False;
2647 create_file_sids(&sbuf, &file_owner_sid, &file_grp_sid);
2649 acl_perms = unpack_canon_ace( fsp, &sbuf, &file_owner_sid, &file_grp_sid,
2650 &file_ace_list, &dir_ace_list, security_info_sent, psd);
2652 /* Ignore W2K traverse DACL set. */
2653 if (file_ace_list || dir_ace_list) {
2655 if (!acl_perms) {
2656 DEBUG(3,("set_nt_acl: cannot set permissions\n"));
2657 free_canon_ace_list(file_ace_list);
2658 free_canon_ace_list(dir_ace_list);
2659 return False;
2663 * Only change security if we got a DACL.
2666 if((security_info_sent & DACL_SECURITY_INFORMATION) && (psd->dacl != NULL)) {
2668 BOOL acl_set_support = False;
2669 BOOL ret = False;
2672 * Try using the POSIX ACL set first. Fall back to chmod if
2673 * we have no ACL support on this filesystem.
2676 if (acl_perms && file_ace_list) {
2677 ret = set_canon_ace_list(fsp, file_ace_list, False, &acl_set_support);
2678 if (acl_set_support && ret == False) {
2679 DEBUG(3,("set_nt_acl: failed to set file acl on file %s (%s).\n", fsp->fsp_name, strerror(errno) ));
2680 free_canon_ace_list(file_ace_list);
2681 free_canon_ace_list(dir_ace_list);
2682 return False;
2686 if (acl_perms && acl_set_support && fsp->is_directory) {
2687 if (dir_ace_list) {
2688 if (!set_canon_ace_list(fsp, dir_ace_list, True, &acl_set_support)) {
2689 DEBUG(3,("set_nt_acl: failed to set default acl on directory %s (%s).\n", fsp->fsp_name, strerror(errno) ));
2690 free_canon_ace_list(file_ace_list);
2691 free_canon_ace_list(dir_ace_list);
2692 return False;
2694 } else {
2697 * No default ACL - delete one if it exists.
2700 if (conn->vfs_ops.sys_acl_delete_def_file(conn, fsp->fsp_name) == -1) {
2701 DEBUG(3,("set_nt_acl: sys_acl_delete_def_file failed (%s)\n", strerror(errno)));
2702 free_canon_ace_list(file_ace_list);
2703 free_canon_ace_list(dir_ace_list);
2704 return False;
2710 * If we cannot set using POSIX ACLs we fall back to checking if we need to chmod.
2713 if(!acl_set_support && acl_perms) {
2714 mode_t posix_perms;
2716 if (!convert_canon_ace_to_posix_perms( fsp, file_ace_list, &posix_perms)) {
2717 free_canon_ace_list(file_ace_list);
2718 free_canon_ace_list(dir_ace_list);
2719 DEBUG(3,("set_nt_acl: failed to convert file acl to posix permissions for file %s.\n",
2720 fsp->fsp_name ));
2721 return False;
2724 if (orig_mode != posix_perms) {
2726 DEBUG(3,("set_nt_acl: chmod %s. perms = 0%o.\n",
2727 fsp->fsp_name, (unsigned int)posix_perms ));
2729 if(conn->vfs_ops.chmod(conn,fsp->fsp_name, posix_perms) == -1) {
2730 DEBUG(3,("set_nt_acl: chmod %s, 0%o failed. Error = %s.\n",
2731 fsp->fsp_name, (unsigned int)posix_perms, strerror(errno) ));
2732 free_canon_ace_list(file_ace_list);
2733 free_canon_ace_list(dir_ace_list);
2734 return False;
2740 free_canon_ace_list(file_ace_list);
2741 free_canon_ace_list(dir_ace_list);
2744 /* Any chown pending? */
2745 if (need_chown) {
2747 DEBUG(3,("set_nt_acl: chown %s. uid = %u, gid = %u.\n",
2748 fsp->fsp_name, (unsigned int)user, (unsigned int)grp ));
2750 if(try_chown( fsp->conn, fsp->fsp_name, user, grp) == -1) {
2751 DEBUG(3,("set_nt_acl: chown %s, %u, %u failed. Error = %s.\n",
2752 fsp->fsp_name, (unsigned int)user, (unsigned int)grp, strerror(errno) ));
2753 return False;
2757 return True;
2760 /****************************************************************************
2761 Do a chmod by setting the ACL USER_OBJ, GROUP_OBJ and OTHER bits in an ACL
2762 and set the mask to rwx. Needed to preserve complex ACLs set by NT.
2763 ****************************************************************************/
2765 static int chmod_acl_internals( connection_struct *conn, SMB_ACL_T posix_acl, mode_t mode)
2767 int entry_id = SMB_ACL_FIRST_ENTRY;
2768 SMB_ACL_ENTRY_T entry;
2769 int num_entries = 0;
2771 while ( conn->vfs_ops.sys_acl_get_entry(conn, posix_acl, entry_id, &entry) == 1) {
2772 SMB_ACL_TAG_T tagtype;
2773 SMB_ACL_PERMSET_T permset;
2774 mode_t perms;
2776 /* get_next... */
2777 if (entry_id == SMB_ACL_FIRST_ENTRY)
2778 entry_id = SMB_ACL_NEXT_ENTRY;
2780 if (conn->vfs_ops.sys_acl_get_tag_type(conn, entry, &tagtype) == -1)
2781 return -1;
2783 if (conn->vfs_ops.sys_acl_get_permset(conn, entry, &permset) == -1)
2784 return -1;
2786 num_entries++;
2788 switch(tagtype) {
2789 case SMB_ACL_USER_OBJ:
2790 perms = unix_perms_to_acl_perms(mode, S_IRUSR, S_IWUSR, S_IXUSR);
2791 break;
2792 case SMB_ACL_GROUP_OBJ:
2793 perms = unix_perms_to_acl_perms(mode, S_IRGRP, S_IWGRP, S_IXGRP);
2794 break;
2795 case SMB_ACL_MASK:
2797 * FIXME: The ACL_MASK entry permissions should really be set to
2798 * the union of the permissions of all ACL_USER,
2799 * ACL_GROUP_OBJ, and ACL_GROUP entries. That's what
2800 * acl_calc_mask() does, but Samba ACLs doesn't provide it.
2802 perms = S_IRUSR|S_IWUSR|S_IXUSR;
2803 break;
2804 case SMB_ACL_OTHER:
2805 perms = unix_perms_to_acl_perms(mode, S_IROTH, S_IWOTH, S_IXOTH);
2806 break;
2807 default:
2808 continue;
2811 if (map_acl_perms_to_permset(conn, perms, &permset) == -1)
2812 return -1;
2814 if (conn->vfs_ops.sys_acl_set_permset(conn, entry, permset) == -1)
2815 return -1;
2819 * If this is a simple 3 element ACL or no elements then it's a standard
2820 * UNIX permission set. Just use chmod...
2823 if ((num_entries == 3) || (num_entries == 0))
2824 return -1;
2826 return 0;
2829 /****************************************************************************
2830 Get the access ACL of FROM, do a chmod by setting the ACL USER_OBJ,
2831 GROUP_OBJ and OTHER bits in an ACL and set the mask to rwx. Set the
2832 resulting ACL on TO. Note that name is in UNIX character set.
2833 ****************************************************************************/
2835 static int copy_access_acl(connection_struct *conn, const char *from, const char *to, mode_t mode)
2837 SMB_ACL_T posix_acl = NULL;
2838 int ret = -1;
2840 if ((posix_acl = conn->vfs_ops.sys_acl_get_file(conn, from, SMB_ACL_TYPE_ACCESS)) == NULL)
2841 return -1;
2843 if ((ret = chmod_acl_internals(conn, posix_acl, mode)) == -1)
2844 goto done;
2846 ret = conn->vfs_ops.sys_acl_set_file(conn, to, SMB_ACL_TYPE_ACCESS, posix_acl);
2848 done:
2850 conn->vfs_ops.sys_acl_free_acl(conn, posix_acl);
2851 return ret;
2854 /****************************************************************************
2855 Do a chmod by setting the ACL USER_OBJ, GROUP_OBJ and OTHER bits in an ACL
2856 and set the mask to rwx. Needed to preserve complex ACLs set by NT.
2857 Note that name is in UNIX character set.
2858 ****************************************************************************/
2860 int chmod_acl(connection_struct *conn, const char *name, mode_t mode)
2862 return copy_access_acl(conn, name, name, mode);
2865 /****************************************************************************
2866 If "inherit permissions" is set and the parent directory has no default
2867 ACL but it does have an Access ACL, inherit this Access ACL to file name.
2868 ****************************************************************************/
2870 int inherit_access_acl(connection_struct *conn, const char *name, mode_t mode)
2872 pstring dirname;
2873 pstrcpy(dirname, parent_dirname(name));
2875 if (!lp_inherit_perms(SNUM(conn)) || directory_has_default_acl(conn, dirname))
2876 return 0;
2878 return copy_access_acl(conn, dirname, name, mode);
2881 /****************************************************************************
2882 Do an fchmod by setting the ACL USER_OBJ, GROUP_OBJ and OTHER bits in an ACL
2883 and set the mask to rwx. Needed to preserve complex ACLs set by NT.
2884 ****************************************************************************/
2886 int fchmod_acl(files_struct *fsp, int fd, mode_t mode)
2888 connection_struct *conn = fsp->conn;
2889 SMB_ACL_T posix_acl = NULL;
2890 int ret = -1;
2892 if ((posix_acl = conn->vfs_ops.sys_acl_get_fd(fsp, fd)) == NULL)
2893 return -1;
2895 if ((ret = chmod_acl_internals(conn, posix_acl, mode)) == -1)
2896 goto done;
2898 ret = conn->vfs_ops.sys_acl_set_fd(fsp, fd, posix_acl);
2900 done:
2902 conn->vfs_ops.sys_acl_free_acl(conn, posix_acl);
2903 return ret;
2906 /****************************************************************************
2907 Check for an existing default POSIX ACL on a directory.
2908 ****************************************************************************/
2910 BOOL directory_has_default_acl(connection_struct *conn, const char *fname)
2912 SMB_ACL_T dir_acl = conn->vfs_ops.sys_acl_get_file( conn, fname, SMB_ACL_TYPE_DEFAULT);
2913 BOOL has_acl = False;
2914 SMB_ACL_ENTRY_T entry;
2916 if (dir_acl != NULL && (conn->vfs_ops.sys_acl_get_entry(conn, dir_acl, SMB_ACL_FIRST_ENTRY, &entry) == 1))
2917 has_acl = True;
2919 if (dir_acl)
2920 conn->vfs_ops.sys_acl_free_acl(conn, dir_acl);
2921 return has_acl;