3 Unix SMB/Netbios implementation.
5 SMB NT Security Descriptor / Unix permission conversion.
6 Copyright (C) Jeremy Allison 1994-2000
8 This program is free software; you can redistribute it and/or modify
9 it under the terms of the GNU General Public License as published by
10 the Free Software Foundation; either version 2 of the License, or
11 (at your option) any later version.
13 This program is distributed in the hope that it will be useful,
14 but WITHOUT ANY WARRANTY; without even the implied warranty of
15 MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
16 GNU General Public License for more details.
18 You should have received a copy of the GNU General Public License
19 along with this program; if not, write to the Free Software
20 Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA.
25 enum ace_owner {UID_ACE, GID_ACE, WORLD_ACE};
27 typedef union posix_id {
33 typedef struct canon_ace {
34 struct canon_ace *next, *prev;
36 mode_t perms; /* Only use S_I(R|W|X)USR mode bits here. */
38 enum ace_owner owner_type;
42 static void free_canon_ace_list( canon_ace *list_head );
45 /****************************************************************************
46 Function to duplicate a canon_ace struct.
47 ****************************************************************************/
49 static canon_ace *dup_canon_ace( canon_ace *c_ace)
51 canon_ace *new_ace = (canon_ace *)malloc(sizeof(canon_ace));
62 /****************************************************************************
63 Function to create owner and group SIDs from a SMB_STRUCT_STAT.
64 ****************************************************************************/
66 static void create_file_sids(SMB_STRUCT_STAT *psbuf, DOM_SID *powner_sid, DOM_SID *pgroup_sid)
68 uid_to_sid( powner_sid, psbuf->st_uid );
69 gid_to_sid( pgroup_sid, psbuf->st_gid );
72 /****************************************************************************
73 Map canon_ace perms to permission bits NT.
74 ****************************************************************************/
76 static SEC_ACCESS map_canon_ace_perms(int *pacl_type, DOM_SID *powner_sid, canon_ace *ace)
81 *pacl_type = SEC_ACE_TYPE_ACCESS_ALLOWED;
83 if((ace->perms & (S_IRWXU|S_IWUSR|S_IXUSR)) == (S_IRWXU|S_IWUSR|S_IXUSR)) {
84 nt_mask = UNIX_ACCESS_RWX;
85 } else if((ace->perms & (S_IRWXU|S_IWUSR|S_IXUSR)) == 0) {
87 * Here we differentiate between the owner and any other user.
89 if (sid_equal(powner_sid, &ace->sid)) {
90 nt_mask = UNIX_ACCESS_NONE;
92 /* Not owner, no access. */
96 nt_mask |= ((ace->perms & S_IRWXU) ? UNIX_ACCESS_R : 0 );
97 nt_mask |= ((ace->perms & S_IWUSR) ? UNIX_ACCESS_W : 0 );
98 nt_mask |= ((ace->perms & S_IXUSR) ? UNIX_ACCESS_X : 0 );
101 DEBUG(10,("map_canon_ace_perms: Mapped (UNIX) %x to (NT) %x\n",
102 (unsigned int)ace->perms, (unsigned int)nt_mask ));
104 init_sec_access(&sa,nt_mask);
108 /****************************************************************************
109 Map NT perms to a UNIX mode_t.
110 ****************************************************************************/
112 #define FILE_SPECIFIC_READ_BITS (FILE_READ_DATA|FILE_READ_EA|FILE_READ_ATTRIBUTES)
113 #define FILE_SPECIFIC_WRITE_BITS (FILE_WRITE_DATA|FILE_APPEND_DATA|FILE_WRITE_EA|FILE_WRITE_ATTRIBUTES)
114 #define FILE_SPECIFIC_EXECUTE_BITS (FILE_EXECUTE)
116 static mode_t map_nt_perms( SEC_ACCESS sec_access, int type)
122 if(sec_access.mask & GENERIC_ALL_ACCESS)
123 mode = S_IRUSR|S_IWUSR|S_IXUSR;
125 mode |= (sec_access.mask & (GENERIC_READ_ACCESS|FILE_SPECIFIC_READ_BITS)) ? S_IRUSR : 0;
126 mode |= (sec_access.mask & (GENERIC_WRITE_ACCESS|FILE_SPECIFIC_WRITE_BITS)) ? S_IWUSR : 0;
127 mode |= (sec_access.mask & (GENERIC_EXECUTE_ACCESS|FILE_SPECIFIC_EXECUTE_BITS)) ? S_IXUSR : 0;
131 if(sec_access.mask & GENERIC_ALL_ACCESS)
132 mode = S_IRGRP|S_IWGRP|S_IXGRP;
134 mode |= (sec_access.mask & (GENERIC_READ_ACCESS|FILE_SPECIFIC_READ_BITS)) ? S_IRGRP : 0;
135 mode |= (sec_access.mask & (GENERIC_WRITE_ACCESS|FILE_SPECIFIC_WRITE_BITS)) ? S_IWGRP : 0;
136 mode |= (sec_access.mask & (GENERIC_EXECUTE_ACCESS|FILE_SPECIFIC_EXECUTE_BITS)) ? S_IXGRP : 0;
140 if(sec_access.mask & GENERIC_ALL_ACCESS)
141 mode = S_IROTH|S_IWOTH|S_IXOTH;
143 mode |= (sec_access.mask & (GENERIC_READ_ACCESS|FILE_SPECIFIC_READ_BITS)) ? S_IROTH : 0;
144 mode |= (sec_access.mask & (GENERIC_WRITE_ACCESS|FILE_SPECIFIC_WRITE_BITS)) ? S_IWOTH : 0;
145 mode |= (sec_access.mask & (GENERIC_EXECUTE_ACCESS|FILE_SPECIFIC_EXECUTE_BITS)) ? S_IXOTH : 0;
153 /****************************************************************************
154 Unpack a SEC_DESC into a UNIX owner and group.
155 ****************************************************************************/
157 static BOOL unpack_nt_owners(SMB_STRUCT_STAT *psbuf, uid_t *puser, gid_t *pgrp, uint32 security_info_sent, SEC_DESC *psd)
161 enum SID_NAME_USE sid_type;
166 if(security_info_sent == 0) {
167 DEBUG(0,("unpack_nt_owners: no security info sent !\n"));
172 * Validate the owner and group SID's.
175 memset(&owner_sid, '\0', sizeof(owner_sid));
176 memset(&grp_sid, '\0', sizeof(grp_sid));
178 DEBUG(5,("unpack_nt_owners: validating owner_sids.\n"));
181 * Don't immediately fail if the owner sid cannot be validated.
182 * This may be a group chown only set.
185 if (security_info_sent & OWNER_SECURITY_INFORMATION) {
186 sid_copy(&owner_sid, psd->owner_sid);
187 if (!sid_to_uid( &owner_sid, puser, &sid_type))
188 DEBUG(3,("unpack_nt_owners: unable to validate owner sid.\n"));
192 * Don't immediately fail if the group sid cannot be validated.
193 * This may be an owner chown only set.
196 if (security_info_sent & GROUP_SECURITY_INFORMATION) {
197 sid_copy(&grp_sid, psd->grp_sid);
198 if (!sid_to_gid( &grp_sid, pgrp, &sid_type))
199 DEBUG(3,("unpack_nt_owners: unable to validate group sid.\n"));
205 /****************************************************************************
206 Unpack a SEC_DESC into two canonical ace lists. We don't depend on this
208 ****************************************************************************/
210 static BOOL unpack_canon_ace(files_struct *fsp,
211 DOM_SID *pfile_owner_sid,
212 DOM_SID *pfile_grp_sid,
213 canon_ace **ppfile_ace, canon_ace **ppdir_ace,
214 uint32 security_info_sent, SEC_DESC *psd)
216 extern DOM_SID global_sid_World;
217 SEC_ACL *dacl = psd->dacl;
218 BOOL all_aces_are_inherit_only = (fsp->is_directory ? True : False);
219 canon_ace *file_ace = NULL;
220 canon_ace *dir_ace = NULL;
221 enum SID_NAME_USE sid_type;
227 if(security_info_sent == 0) {
228 DEBUG(0,("unpack_canon_ace: no security info sent !\n"));
233 * If no DACL then this is a chown only security descriptor.
236 if(!(security_info_sent & DACL_SECURITY_INFORMATION) || !dacl)
240 * Now go through the DACL and create the canon_ace lists.
243 for(i = 0; i < dacl->num_aces; i++) {
244 SEC_ACE *psa = &dacl->ace[i];
245 canon_ace *current_ace = NULL;
247 if((psa->type != SEC_ACE_TYPE_ACCESS_ALLOWED) && (psa->type != SEC_ACE_TYPE_ACCESS_DENIED)) {
248 DEBUG(3,("unpack_canon_ace: unable to set anything but an ALLOW or DENY ACE.\n"));
253 * The security mask may be UNIX_ACCESS_NONE which should map into
254 * no permissions (we overload the WRITE_OWNER bit for this) or it
255 * should be one of the ALL/EXECUTE/READ/WRITE bits. Arrange for this
256 * to be so. Any other bits override the UNIX_ACCESS_NONE bit.
259 psa->info.mask &= (GENERIC_ALL_ACCESS|GENERIC_EXECUTE_ACCESS|GENERIC_WRITE_ACCESS|
260 GENERIC_READ_ACCESS|UNIX_ACCESS_NONE|FILE_ALL_ATTRIBUTES);
262 if(psa->info.mask != UNIX_ACCESS_NONE)
263 psa->info.mask &= ~UNIX_ACCESS_NONE;
266 * Create a cannon_ace entry representing this NT DACL ACE.
269 if ((current_ace = (canon_ace *)malloc(sizeof(canon_ace))) == NULL) {
270 free_canon_ace_list(file_ace);
271 free_canon_ace_list(dir_ace);
272 DEBUG(0,("unpack_canon_ace: malloc fail.\n"));
276 ZERO_STRUCTP(current_ace);
278 sid_copy(¤t_ace->sid, &psa->sid);
281 * Try and work out if the SID is a user or group
282 * as we need to flag these differently for POSIX.
285 if( sid_equal(¤t_ace->sid, &global_sid_World)) {
286 current_ace->owner_type = WORLD_ACE;
287 current_ace->unix_ug.world = -1;
288 } else if (sid_to_uid( ¤t_ace->sid, ¤t_ace->unix_ug.uid, &sid_type)) {
289 current_ace->owner_type = UID_ACE;
290 } else if (sid_to_gid( ¤t_ace->sid, ¤t_ace->unix_ug.gid, &sid_type)) {
291 current_ace->owner_type = GID_ACE;
295 free_canon_ace_list(file_ace);
296 free_canon_ace_list(dir_ace);
298 DEBUG(0,("unpack_canon_ace: unable to map SID %s to uid or gid.\n",
299 sid_to_string(str, ¤t_ace->sid) ));
304 * Map the given NT permissions into a UNIX mode_t containing only
305 * S_I(R|W|X)USR bits.
308 if(psa->type == SEC_ACE_TYPE_ACCESS_ALLOWED)
309 current_ace->perms |= map_nt_perms( psa->info, S_IRUSR);
311 current_ace->perms &= ~(map_nt_perms( psa->info, S_IRUSR));
314 * Now note what kind of a POSIX ACL this should map to.
317 if(sid_equal(¤t_ace->sid, pfile_owner_sid)) {
318 /* Note we should apply the default mode/mask here.... FIXME ! JRA */
319 current_ace->type = SMB_ACL_USER_OBJ;
320 } else if( sid_equal(¤t_ace->sid, pfile_grp_sid)) {
321 /* Note we should apply the default mode/mask here.... FIXME ! JRA */
322 current_ace->type = SMB_ACL_GROUP_OBJ;
323 } else if( sid_equal(¤t_ace->sid, &global_sid_World)) {
324 /* Note we should apply the default mode/mask here.... FIXME ! JRA */
325 current_ace->type = SMB_ACL_OTHER;
328 * Could be a SMB_ACL_USER or SMB_ACL_GROUP. Check by
329 * looking at owner_type.
332 current_ace->type = (current_ace->owner_type == UID_ACE) ? SMB_ACL_USER : SMB_ACL_GROUP;
335 if (fsp->is_directory && (psa->flags & SEC_ACE_FLAG_INHERIT_ONLY)) {
337 * We can only add to the default POSIX ACE list if the ACE is
338 * designed to be inherited by both files and directories.
340 if ((psa->flags & (SEC_ACE_FLAG_OBJECT_INHERIT|SEC_ACE_FLAG_CONTAINER_INHERIT)) ==
341 (SEC_ACE_FLAG_OBJECT_INHERIT|SEC_ACE_FLAG_CONTAINER_INHERIT)) {
342 DLIST_ADD(dir_ace, current_ace);
344 DEBUG(0,("unpack_canon_ace: unable to use a non-generic default ACE.\n"));
348 DLIST_ADD(file_ace, current_ace);
349 all_aces_are_inherit_only = False;
353 if (fsp->is_directory && all_aces_are_inherit_only) {
355 * Windows 2000 is doing one of these weird 'inherit acl'
356 * traverses to conserve NTFS ACL resources. Just pretend
357 * there was no DACL sent. JRA.
360 DEBUG(10,("unpack_posix_permissions: Win2k inherit acl traverse. Ignoring DACL.\n"));
361 free_sec_acl(&psd->dacl);
364 *ppfile_ace = file_ace;
365 *ppdir_ace = dir_ace;
369 /****************************************************************************
370 Unpack a SEC_DESC into a set of standard POSIX permissions.
371 ****************************************************************************/
373 static BOOL unpack_posix_permissions(files_struct *fsp, SMB_STRUCT_STAT *psbuf, mode_t *pmode,
374 uint32 security_info_sent, SEC_DESC *psd)
376 extern DOM_SID global_sid_World;
377 connection_struct *conn = fsp->conn;
378 DOM_SID file_owner_sid;
379 DOM_SID file_grp_sid;
380 SEC_ACL *dacl = psd->dacl;
381 BOOL all_aces_are_inherit_only = (fsp->is_directory ? True : False);
386 if(security_info_sent == 0) {
387 DEBUG(0,("unpack_posix_permissions: no security info sent !\n"));
392 * Windows 2000 sends the owner and group SIDs as the logged in
393 * user, not the connected user. But it still sends the file
394 * owner SIDs on an ACL set. So we need to check for the file
395 * owner and group SIDs as well as the owner SIDs. JRA.
398 create_file_sids(psbuf, &file_owner_sid, &file_grp_sid);
401 * If no DACL then this is a chown only security descriptor.
404 if(!(security_info_sent & DACL_SECURITY_INFORMATION) || !dacl) {
410 * Now go through the DACL and ensure that
411 * any owner/group sids match.
414 for(i = 0; i < dacl->num_aces; i++) {
416 SEC_ACE *psa = &dacl->ace[i];
418 if((psa->type != SEC_ACE_TYPE_ACCESS_ALLOWED) &&
419 (psa->type != SEC_ACE_TYPE_ACCESS_DENIED)) {
420 DEBUG(3,("unpack_posix_permissions: unable to set anything but an ALLOW or DENY ACE.\n"));
425 * Ignore or remove bits we don't care about on a directory ACE.
428 if(fsp->is_directory) {
429 if(psa->flags & SEC_ACE_FLAG_INHERIT_ONLY) {
430 DEBUG(3,("unpack_posix_permissions: ignoring inherit only ACE.\n"));
435 * At least one of the ACE entries wasn't inherit only.
436 * Flag this so we know the returned mode is valid.
439 all_aces_are_inherit_only = False;
443 * Windows 2000 sets these flags even on *file* ACE's. This is wrong
444 * but we can ignore them for now. Revisit this when we go to POSIX
445 * ACLs on directories.
448 psa->flags &= ~(SEC_ACE_FLAG_OBJECT_INHERIT|SEC_ACE_FLAG_CONTAINER_INHERIT);
450 if(psa->flags != 0) {
451 DEBUG(1,("unpack_posix_permissions: unable to set ACE flags (%x).\n",
452 (unsigned int)psa->flags));
457 * The security mask may be UNIX_ACCESS_NONE which should map into
458 * no permissions (we overload the WRITE_OWNER bit for this) or it
459 * should be one of the ALL/EXECUTE/READ/WRITE bits. Arrange for this
460 * to be so. Any other bits override the UNIX_ACCESS_NONE bit.
463 psa->info.mask &= (GENERIC_ALL_ACCESS|GENERIC_EXECUTE_ACCESS|GENERIC_WRITE_ACCESS|
464 GENERIC_READ_ACCESS|UNIX_ACCESS_NONE|FILE_ALL_ATTRIBUTES);
466 if(psa->info.mask != UNIX_ACCESS_NONE)
467 psa->info.mask &= ~UNIX_ACCESS_NONE;
469 sid_copy(&ace_sid, &psa->sid);
471 if(sid_equal(&ace_sid, &file_owner_sid)) {
473 * Map the desired permissions into owner perms.
476 if(psa->type == SEC_ACE_TYPE_ACCESS_ALLOWED)
477 *pmode |= map_nt_perms( psa->info, S_IRUSR);
479 *pmode &= ~(map_nt_perms( psa->info, S_IRUSR));
481 } else if( sid_equal(&ace_sid, &file_grp_sid)) {
483 * Map the desired permissions into group perms.
486 if(psa->type == SEC_ACE_TYPE_ACCESS_ALLOWED)
487 *pmode |= map_nt_perms( psa->info, S_IRGRP);
489 *pmode &= ~(map_nt_perms( psa->info, S_IRGRP));
491 } else if( sid_equal(&ace_sid, &global_sid_World)) {
493 * Map the desired permissions into other perms.
496 if(psa->type == SEC_ACE_TYPE_ACCESS_ALLOWED)
497 *pmode |= map_nt_perms( psa->info, S_IROTH);
499 *pmode &= ~(map_nt_perms( psa->info, S_IROTH));
502 DEBUG(0,("unpack_posix_permissions: unknown SID used in ACL.\n"));
507 if (fsp->is_directory && all_aces_are_inherit_only) {
509 * Windows 2000 is doing one of these weird 'inherit acl'
510 * traverses to conserve NTFS ACL resources. Just pretend
511 * there was no DACL sent. JRA.
514 DEBUG(10,("unpack_posix_permissions: Win2k inherit acl traverse. Ignoring DACL.\n"));
515 free_sec_acl(&psd->dacl);
519 * Check to see if we need to change anything.
520 * Enforce limits on modified bits *only*. Don't enforce masks
521 * on bits not changed by the user.
524 if(fsp->is_directory) {
526 *pmode &= (lp_dir_security_mask(SNUM(conn)) | psbuf->st_mode);
527 *pmode |= (lp_force_dir_security_mode(SNUM(conn)) & ( *pmode ^ psbuf->st_mode ));
531 *pmode &= (lp_security_mask(SNUM(conn)) | psbuf->st_mode);
532 *pmode |= (lp_force_security_mode(SNUM(conn)) & ( *pmode ^ psbuf->st_mode ));
537 * Preserve special bits.
540 *pmode |= (psbuf->st_mode & ~0777);
545 /****************************************************************************
546 Map POSIX ACL perms to canon_ace permissions (a mode_t containing only S_(R|W|X)USR bits).
547 ****************************************************************************/
549 static mode_t convert_permset_to_mode_t(SMB_ACL_PERMSET_T permset)
553 ret |= (sys_acl_get_perm(permset, SMB_ACL_READ) ? S_IRUSR : 0);
554 ret |= (sys_acl_get_perm(permset, SMB_ACL_WRITE) ? S_IWUSR : 0);
555 ret |= (sys_acl_get_perm(permset, SMB_ACL_EXECUTE) ? S_IXUSR : 0);
560 /****************************************************************************
561 Map generic UNIX permissions to canon_ace permissions (a mode_t containing only S_(R|W|X)USR bits).
562 ****************************************************************************/
564 static mode_t unix_perms_to_acl_perms(mode_t mode, int r_mask, int w_mask, int x_mask)
578 /****************************************************************************
579 Map canon_ace permissions (a mode_t containing only S_(R|W|X)USR bits) to
580 an SMB_ACL_PERMSET_T.
581 ****************************************************************************/
583 static int map_acl_perms_to_permset(mode_t mode, SMB_ACL_PERMSET_T *p_permset)
585 if (sys_acl_clear_perms(*p_permset) == -1)
587 if (mode & S_IRUSR) {
588 if (sys_acl_add_perm(*p_permset, SMB_ACL_READ) == -1)
591 if (mode & S_IWUSR) {
592 if (sys_acl_add_perm(*p_permset, SMB_ACL_WRITE) == -1)
595 if (mode & S_IXUSR) {
596 if (sys_acl_add_perm(*p_permset, SMB_ACL_EXECUTE) == -1)
602 /****************************************************************************
603 Count a linked list of canonical ACE entries.
604 ****************************************************************************/
606 static size_t count_canon_ace_list( canon_ace *list_head )
611 for (ace = list_head; ace; ace = ace->next)
617 /****************************************************************************
618 Free a linked list of canonical ACE entries.
619 ****************************************************************************/
621 static void free_canon_ace_list( canon_ace *list_head )
624 canon_ace *old_head = list_head;
625 DLIST_REMOVE(list_head, list_head);
630 /******************************************************************************
631 Fall back to the generic 3 element UNIX permissions.
632 ********************************************************************************/
634 static canon_ace *unix_canonicalise_acl(files_struct *fsp, SMB_STRUCT_STAT *psbuf,
635 DOM_SID *powner, DOM_SID *pgroup)
637 extern DOM_SID global_sid_World;
638 canon_ace *list_head = NULL;
639 canon_ace *owner_ace = NULL;
640 canon_ace *group_ace = NULL;
641 canon_ace *other_ace = NULL;
644 * Create 3 linked list entries.
647 if ((owner_ace = (canon_ace *)malloc(sizeof(canon_ace))) == NULL)
650 if ((group_ace = (canon_ace *)malloc(sizeof(canon_ace))) == NULL)
653 if ((other_ace = (canon_ace *)malloc(sizeof(canon_ace))) == NULL)
656 ZERO_STRUCTP(owner_ace);
657 ZERO_STRUCTP(group_ace);
658 ZERO_STRUCTP(other_ace);
660 owner_ace->type = SMB_ACL_USER_OBJ;
661 owner_ace->sid = *powner;
662 owner_ace->unix_ug.uid = psbuf->st_uid;
663 owner_ace->owner_type = UID_ACE;
665 group_ace->type = SMB_ACL_GROUP_OBJ;
666 group_ace->sid = *pgroup;
667 owner_ace->unix_ug.gid = psbuf->st_gid;
668 owner_ace->owner_type = GID_ACE;
670 other_ace->type = SMB_ACL_OTHER;
671 other_ace->sid = global_sid_World;
672 owner_ace->unix_ug.world = -1;
673 owner_ace->owner_type = WORLD_ACE;
675 if (!fsp->is_directory) {
676 owner_ace->perms = unix_perms_to_acl_perms(psbuf->st_mode, S_IRUSR, S_IWUSR, S_IXUSR);
677 group_ace->perms = unix_perms_to_acl_perms(psbuf->st_mode, S_IRGRP, S_IWGRP, S_IXGRP);
678 other_ace->perms = unix_perms_to_acl_perms(psbuf->st_mode, S_IROTH, S_IWOTH, S_IXOTH);
680 mode_t mode = unix_mode( fsp->conn, FILE_ATTRIBUTE_ARCHIVE, fsp->fsp_name);
682 owner_ace->perms = unix_perms_to_acl_perms(mode, S_IRUSR, S_IWUSR, S_IXUSR);
683 group_ace->perms = unix_perms_to_acl_perms(mode, S_IRGRP, S_IWGRP, S_IXGRP);
684 other_ace->perms = unix_perms_to_acl_perms(mode, S_IROTH, S_IWOTH, S_IXOTH);
687 DLIST_ADD(list_head, other_ace);
688 DLIST_ADD(list_head, group_ace);
689 DLIST_ADD(list_head, owner_ace);
695 safe_free(owner_ace);
696 safe_free(group_ace);
697 safe_free(other_ace);
702 /****************************************************************************
703 Create a linked list of canonical ACE entries. This is sorted so that DENY
704 entries are at the front of the list, as NT requires.
705 ****************************************************************************/
707 static canon_ace *canonicalise_acl( SMB_ACL_T posix_acl, SMB_STRUCT_STAT *psbuf)
709 extern DOM_SID global_sid_World;
710 mode_t acl_mask = (S_IRUSR|S_IWUSR|S_IXUSR);
711 canon_ace *list_head = NULL;
712 canon_ace *ace = NULL;
713 canon_ace *next_ace = NULL;
714 int entry_id = SMB_ACL_FIRST_ENTRY;
715 SMB_ACL_ENTRY_T entry;
717 while ( sys_acl_get_entry(posix_acl, entry_id, &entry) == 1) {
718 SMB_ACL_TAG_T tagtype;
719 SMB_ACL_PERMSET_T permset;
722 enum ace_owner owner_type;
725 if (entry_id == SMB_ACL_FIRST_ENTRY)
726 entry_id = SMB_ACL_NEXT_ENTRY;
728 /* Is this a MASK entry ? */
729 if (sys_acl_get_tag_type(entry, &tagtype) == -1)
732 if (sys_acl_get_permset(entry, &permset) == -1)
735 /* Decide which SID to use based on the ACL type. */
737 case SMB_ACL_USER_OBJ:
738 /* Get the SID from the owner. */
739 uid_to_sid( &sid, psbuf->st_uid );
740 unix_ug.uid = psbuf->st_uid;
741 owner_type = UID_ACE;
745 uid_t *puid = (uid_t *)sys_acl_get_qualifier(entry);
747 DEBUG(0,("canonicalise_acl: Failed to get uid.\n"));
750 uid_to_sid( &sid, *puid);
752 owner_type = UID_ACE;
755 case SMB_ACL_GROUP_OBJ:
756 /* Get the SID from the owning group. */
757 gid_to_sid( &sid, psbuf->st_gid );
758 unix_ug.gid = psbuf->st_gid;
759 owner_type = GID_ACE;
763 gid_t *pgid = (gid_t *)sys_acl_get_qualifier(entry);
765 DEBUG(0,("canonicalise_acl: Failed to get gid.\n"));
768 gid_to_sid( &sid, *pgid);
770 owner_type = GID_ACE;
774 acl_mask = convert_permset_to_mode_t(permset);
775 continue; /* Don't count the mask as an entry. */
777 /* Use the Everyone SID */
778 sid = global_sid_World;
780 owner_type = WORLD_ACE;
783 DEBUG(0,("canonicalise_acl: Unknown tagtype %u\n", (unsigned int)tagtype));
788 * Add this entry to the list.
791 if ((ace = (canon_ace *)malloc(sizeof(canon_ace))) == NULL)
796 ace->perms = convert_permset_to_mode_t(permset);
798 ace->unix_ug = unix_ug;
799 ace->owner_type = owner_type;
801 DLIST_ADD(list_head, ace);
805 * Now go through the list, masking the permissions with the
806 * acl_mask. If the permissions are 0 it should be listed
810 for ( ace = list_head; ace; ace = next_ace) {
811 next_ace = ace->next;
813 /* Masks are only applied to entries other than USER_OBJ and OTHER. */
814 if (ace->type != SMB_ACL_OTHER && ace->type != SMB_ACL_USER_OBJ)
815 ace->perms &= acl_mask;
818 DLIST_PROMOTE(list_head, ace);
821 if( DEBUGLVL( 10 ) ) {
822 char *acl_text = sys_acl_to_text( posix_acl, NULL);
824 dbgtext("canonicalize_acl: processed acl %s\n", acl_text == NULL ? "NULL" : acl_text );
826 sys_acl_free_text(acl_text);
833 free_canon_ace_list(list_head);
837 /****************************************************************************
838 Attempt to apply an ACL to a file or directory.
839 ****************************************************************************/
841 static BOOL set_canon_ace_list(files_struct *fsp, canon_ace *the_ace, BOOL default_ace)
844 SMB_ACL_T the_acl = sys_acl_init((int)count_canon_ace_list(the_ace) + 1);
847 SMB_ACL_ENTRY_T mask_entry;
848 SMB_ACL_PERMSET_T mask_permset;
849 SMB_ACL_TYPE_T the_acl_type = (default_ace ? SMB_ACL_TYPE_DEFAULT : SMB_ACL_TYPE_ACCESS);
851 if (the_acl == NULL) {
852 #if !defined(HAVE_NO_ACLS)
854 * Only print this error message if we have some kind of ACL
855 * support that's not working. Otherwise we would always get this.
857 DEBUG(0,("set_canon_ace_list: Unable to init %s ACL. (%s)\n",
858 default_ace ? "default" : "file", strerror(errno) ));
863 for (i = 0, p_ace = the_ace; p_ace; p_ace = p_ace->next, i++ ) {
864 SMB_ACL_ENTRY_T the_entry;
865 SMB_ACL_PERMSET_T the_permset;
868 * Get the entry for this ACE.
871 if (sys_acl_create_entry( &the_acl, &the_entry) == -1) {
872 DEBUG(0,("set_canon_ace_list: Failed to create entry %d. (%s)\n",
873 i, strerror(errno) ));
878 * Initialise the entry from the canon_ace.
882 * First tell the entry what type of ACE this is.
885 if (sys_acl_set_tag_type(the_entry, p_ace->type) == -1) {
886 DEBUG(0,("set_canon_ace_list: Failed to set tag type on entry %d. (%s)\n",
887 i, strerror(errno) ));
892 * Only set the qualifier (user or group id) if the entry is a user
896 if ((p_ace->type == SMB_ACL_USER) || (p_ace->type == SMB_ACL_GROUP)) {
897 if (sys_acl_set_qualifier(the_entry,(void *)&p_ace->unix_ug.uid) == -1) {
898 DEBUG(0,("set_canon_ace_list: Failed to set qualifier on entry %d. (%s)\n",
899 i, strerror(errno) ));
905 * Convert the mode_t perms in the canon_ace to a POSIX permset.
908 if (sys_acl_get_permset(the_entry, &the_permset) == -1) {
909 DEBUG(0,("set_canon_ace_list: Failed to get permset on entry %d. (%s)\n",
910 i, strerror(errno) ));
914 if (map_acl_perms_to_permset(p_ace->perms, &the_permset) == -1) {
915 DEBUG(0,("set_canon_ace_list: Failed to create permset for mode (%u) on entry %d. (%s)\n",
916 p_ace->perms, i, strerror(errno) ));
921 * ..and apply them to the entry.
924 if (sys_acl_set_permset(the_entry, the_permset) == -1) {
925 DEBUG(0,("set_canon_ace_list: Failed to add permset on entry %d. (%s)\n",
926 i, strerror(errno) ));
932 * Add in a mask of rwx.
935 if (sys_acl_create_entry( &the_acl, &mask_entry) == -1) {
936 DEBUG(0,("set_canon_ace_list: Failed to create mask entry. (%s)\n", strerror(errno) ));
940 if (sys_acl_set_tag_type(mask_entry, SMB_ACL_MASK) == -1) {
941 DEBUG(0,("set_canon_ace_list: Failed to set tag type on mask entry. (%s)\n",strerror(errno) ));
945 if (sys_acl_get_permset(mask_entry, &mask_permset) == -1) {
946 DEBUG(0,("set_canon_ace_list: Failed to get mask permset. (%s)\n", strerror(errno) ));
950 if (map_acl_perms_to_permset(S_IRUSR|S_IWUSR|S_IXUSR, &mask_permset) == -1) {
951 DEBUG(0,("set_canon_ace_list: Failed to create mask permset. (%s)\n", strerror(errno) ));
955 if (sys_acl_set_permset(mask_entry, mask_permset) == -1) {
956 DEBUG(0,("set_canon_ace_list: Failed to add mask permset. (%s)\n", strerror(errno) ));
961 * Check if the ACL is valid.
964 if (sys_acl_valid(the_acl) == -1) {
965 DEBUG(0,("set_canon_ace_list: ACL is invalid for set (%s).\n", strerror(errno) ));
970 * Finally apply it to the file or directory.
973 if(default_ace || fsp->is_directory || fsp->fd == -1) {
974 if (sys_acl_set_file(fsp->fsp_name, the_acl_type, the_acl) == -1) {
975 DEBUG(0,("set_canon_ace_list: sys_acl_set_file failed for file %s (%s).\n",
976 fsp->fsp_name, strerror(errno) ));
980 if (sys_acl_set_fd(fsp->fd, the_acl) == -1) {
981 DEBUG(0,("set_canon_ace_list: sys_acl_set_file failed for file %s (%s).\n",
982 fsp->fsp_name, strerror(errno) ));
992 sys_acl_free_acl(the_acl);
997 /****************************************************************************
998 Reply to query a security descriptor from an fsp. If it succeeds it allocates
999 the space for the return elements and returns the size needed to return the
1000 security descriptor. This should be the only external function needed for
1001 the UNIX style get ACL.
1002 ****************************************************************************/
1004 size_t get_nt_acl(files_struct *fsp, SEC_DESC **ppdesc)
1006 SMB_STRUCT_STAT sbuf;
1007 SEC_ACE *nt_ace_list;
1011 SEC_ACL *psa = NULL;
1012 size_t num_acls = 0;
1013 size_t num_dir_acls = 0;
1014 size_t num_aces = 0;
1015 SMB_ACL_T posix_acl = NULL;
1016 SMB_ACL_T dir_acl = NULL;
1017 canon_ace *file_ace = NULL;
1018 canon_ace *dir_ace = NULL;
1022 if(fsp->is_directory || fsp->fd == -1) {
1024 /* Get the stat struct for the owner info. */
1025 if(vfs_stat(fsp->conn,fsp->fsp_name, &sbuf) != 0) {
1029 * Get the ACL from the path.
1032 posix_acl = sys_acl_get_file( dos_to_unix(fsp->fsp_name, False), SMB_ACL_TYPE_ACCESS);
1035 * If it's a directory get the default POSIX ACL.
1038 if(fsp->is_directory)
1039 dir_acl = sys_acl_get_file( dos_to_unix(fsp->fsp_name, False), SMB_ACL_TYPE_DEFAULT);
1043 /* Get the stat struct for the owner info. */
1044 if(vfs_fstat(fsp,fsp->fd,&sbuf) != 0) {
1048 * Get the ACL from the fd.
1050 posix_acl = sys_acl_get_fd(fsp->fd);
1053 DEBUG(5,("get_nt_acl : file ACL %s, directory ACL %s\n",
1054 posix_acl ? "present" : "absent",
1055 dir_acl ? "present" : "absent" ));
1058 * Get the owner, group and world SIDs.
1061 create_file_sids(&sbuf, &owner_sid, &group_sid);
1063 /* Create the canon_ace lists. */
1065 file_ace = canonicalise_acl( posix_acl, &sbuf);
1067 file_ace = unix_canonicalise_acl(fsp, &sbuf, &owner_sid, &group_sid);
1069 num_acls = count_canon_ace_list(file_ace);
1071 if (fsp->is_directory) {
1073 dir_ace = canonicalise_acl( dir_acl, &sbuf);
1075 dir_ace = unix_canonicalise_acl(fsp, &sbuf, &owner_sid, &group_sid);
1077 num_dir_acls = count_canon_ace_list(dir_ace);
1080 /* Allocate the ace list. */
1081 if ((nt_ace_list = (SEC_ACE *)malloc((num_acls + num_dir_acls)* sizeof(SEC_ACE))) == NULL) {
1082 DEBUG(0,("get_nt_acl: Unable to malloc space for nt_ace_list.\n"));
1086 memset(nt_ace_list, '\0', (num_acls + num_dir_acls) * sizeof(SEC_ACE) );
1089 * Create the NT ACE list from the canonical ace lists.
1099 for (i = 0; i < num_acls; i++, ace = ace->next) {
1100 SEC_ACCESS acc = map_canon_ace_perms(&nt_acl_type, &owner_sid, ace );
1101 init_sec_ace(&nt_ace_list[num_aces++], &ace->sid, nt_acl_type, acc, 0);
1106 for (i = 0; i < num_dir_acls; i++, ace = ace->next) {
1107 SEC_ACCESS acc = map_canon_ace_perms(&nt_acl_type, &owner_sid, ace );
1108 init_sec_ace(&nt_ace_list[num_aces++], &ace->sid, nt_acl_type, acc,
1109 SEC_ACE_FLAG_OBJECT_INHERIT|SEC_ACE_FLAG_CONTAINER_INHERIT|SEC_ACE_FLAG_INHERIT_ONLY);
1114 if((psa = make_sec_acl( ACL_REVISION, num_aces, nt_ace_list)) == NULL) {
1115 DEBUG(0,("get_nt_acl: Unable to malloc space for acl.\n"));
1120 *ppdesc = make_standard_sec_desc( &owner_sid, &group_sid, psa, &sd_size);
1123 DEBUG(0,("get_nt_acl: Unable to malloc space for security descriptor.\n"));
1130 sys_acl_free_acl(posix_acl);
1132 sys_acl_free_acl(dir_acl);
1133 free_canon_ace_list(file_ace);
1134 free_canon_ace_list(dir_ace);
1143 /****************************************************************************
1144 Reply to set a security descriptor on an fsp. security_info_sent is the
1145 description of the following NT ACL.
1146 This should be the only external function needed for the UNIX style set ACL.
1147 ****************************************************************************/
1149 BOOL set_nt_acl(files_struct *fsp, uint32 security_info_sent, SEC_DESC *psd)
1151 connection_struct *conn = fsp->conn;
1152 uid_t user = (uid_t)-1;
1153 gid_t grp = (gid_t)-1;
1155 SMB_STRUCT_STAT sbuf;
1156 DOM_SID file_owner_sid;
1157 DOM_SID file_grp_sid;
1158 canon_ace *file_ace_list = NULL;
1159 canon_ace *dir_ace_list = NULL;
1164 * Get the current state of the file.
1167 if(fsp->is_directory || fsp->fd == -1) {
1168 if(vfs_stat(fsp->conn,fsp->fsp_name, &sbuf) != 0)
1171 if(vfs_fstat(fsp,fsp->fd,&sbuf) != 0)
1176 * Unpack the user/group/world id's.
1179 if (!unpack_nt_owners( &sbuf, &user, &grp, security_info_sent, psd))
1183 * Do we need to chown ?
1186 if((user != (uid_t)-1 || grp != (uid_t)-1) && (sbuf.st_uid != user || sbuf.st_gid != grp)) {
1188 DEBUG(3,("set_nt_acl: chown %s. uid = %u, gid = %u.\n",
1189 fsp->fsp_name, (unsigned int)user, (unsigned int)grp ));
1191 if(vfs_chown( fsp->conn, fsp->fsp_name, user, grp) == -1) {
1192 DEBUG(3,("set_nt_acl: chown %s, %u, %u failed. Error = %s.\n",
1193 fsp->fsp_name, (unsigned int)user, (unsigned int)grp, strerror(errno) ));
1198 * Recheck the current state of the file, which may have changed.
1199 * (suid/sgid bits, for instance)
1202 if(fsp->is_directory) {
1203 if(vfs_stat(fsp->conn, fsp->fsp_name, &sbuf) != 0) {
1211 ret = vfs_stat(fsp->conn, fsp->fsp_name, &sbuf);
1213 ret = vfs_fstat(fsp,fsp->fd,&sbuf);
1220 create_file_sids(&sbuf, &file_owner_sid, &file_grp_sid);
1222 posix_perms = unpack_posix_permissions( fsp, &sbuf, &perms, security_info_sent, psd);
1223 acl_perms = unpack_canon_ace( fsp, &file_owner_sid, &file_grp_sid,
1224 &file_ace_list, &dir_ace_list, security_info_sent, psd);
1226 if (!posix_perms && !acl_perms) {
1228 * Neither method of setting permissions can work. Fail here.
1231 DEBUG(3,("set_nt_acl: cannot set normal POSIX permissions or POSIX ACL permissions\n"));
1232 free_canon_ace_list(file_ace_list);
1233 free_canon_ace_list(dir_ace_list);
1238 * Only change security if we got a DACL.
1241 if((security_info_sent & DACL_SECURITY_INFORMATION) && (psd->dacl != NULL)) {
1243 BOOL acl_set_support = False;
1246 * Try using the POSIX ACL set first. All back to chmod if
1247 * we have no ACL support on this filesystem.
1250 if (acl_perms && file_ace_list && set_canon_ace_list(fsp, file_ace_list, False))
1251 acl_set_support = True;
1253 if (acl_perms && acl_set_support && fsp->is_directory && dir_ace_list)
1254 set_canon_ace_list(fsp, dir_ace_list, True);
1257 * If we cannot set using POSIX ACLs we fall back to checking if we need to chmod.
1260 if(!acl_set_support && (sbuf.st_mode != perms)) {
1262 free_canon_ace_list(file_ace_list);
1263 free_canon_ace_list(dir_ace_list);
1264 file_ace_list = NULL;
1265 dir_ace_list = NULL;
1267 DEBUG(3,("call_nt_transact_set_security_desc: chmod %s. perms = 0%o.\n",
1268 fsp->fsp_name, (unsigned int)perms ));
1270 if(conn->vfs_ops.chmod(conn,dos_to_unix(fsp->fsp_name, False), perms) == -1) {
1271 DEBUG(3,("set_nt_acl: chmod %s, 0%o failed. Error = %s.\n",
1272 fsp->fsp_name, (unsigned int)perms, strerror(errno) ));
1278 free_canon_ace_list(file_ace_list);
1279 free_canon_ace_list(dir_ace_list);