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 );
44 /****************************************************************************
45 Function to create owner and group SIDs from a SMB_STRUCT_STAT.
46 ****************************************************************************/
48 static void create_file_sids(SMB_STRUCT_STAT *psbuf, DOM_SID *powner_sid, DOM_SID *pgroup_sid)
50 uid_to_sid( powner_sid, psbuf->st_uid );
51 gid_to_sid( pgroup_sid, psbuf->st_gid );
54 /****************************************************************************
55 Print out a canon ace.
56 ****************************************************************************/
58 static void print_canon_ace(canon_ace *ace, int num)
62 dbgtext( "canon_ace index %d.", num );
63 dbgtext( "SID = %s ", sid_to_string( str, &ace->sid));
64 if (ace->owner_type == UID_ACE) {
65 struct passwd *pass = sys_getpwuid(ace->unix_ug.uid);
66 dbgtext( "uid %u (%s) ", (unsigned int)ace->unix_ug.uid, pass ? pass->pw_name : "UNKNOWN");
67 } else if (ace->owner_type == GID_ACE) {
68 struct group *grp = getgrgid(ace->unix_ug.gid);
69 dbgtext( "gid %u (%s) ", (unsigned int)ace->unix_ug.gid, grp ? grp->gr_name : "UNKNOWN");
74 dbgtext( "SMB_ACL_USER ");
76 case SMB_ACL_USER_OBJ:
77 dbgtext( "SMB_ACL_USER_OBJ ");
80 dbgtext( "SMB_ACL_GROUP ");
82 case SMB_ACL_GROUP_OBJ:
83 dbgtext( "SMB_ACL_GROUP_OBJ ");
86 dbgtext( "SMB_ACL_OTHER ");
90 dbgtext( "%c", ace->perms & S_IRUSR ? 'r' : '-');
91 dbgtext( "%c", ace->perms & S_IWUSR ? 'w' : '-');
92 dbgtext( "%c\n", ace->perms & S_IXUSR ? 'x' : '-');
95 /****************************************************************************
96 Map canon_ace perms to permission bits NT.
97 ****************************************************************************/
99 static SEC_ACCESS map_canon_ace_perms(int *pacl_type, DOM_SID *powner_sid, canon_ace *ace)
104 *pacl_type = SEC_ACE_TYPE_ACCESS_ALLOWED;
106 if((ace->perms & (S_IRUSR|S_IWUSR|S_IXUSR)) == (S_IRUSR|S_IWUSR|S_IXUSR)) {
107 nt_mask = UNIX_ACCESS_RWX;
108 } else if((ace->perms & (S_IRUSR|S_IWUSR|S_IXUSR)) == 0) {
110 * Here we differentiate between the owner and any other user.
112 if (sid_equal(powner_sid, &ace->sid)) {
113 nt_mask = UNIX_ACCESS_NONE;
115 /* Not owner, no access. */
119 nt_mask |= ((ace->perms & S_IRUSR) ? UNIX_ACCESS_R : 0 );
120 nt_mask |= ((ace->perms & S_IWUSR) ? UNIX_ACCESS_W : 0 );
121 nt_mask |= ((ace->perms & S_IXUSR) ? UNIX_ACCESS_X : 0 );
124 DEBUG(10,("map_canon_ace_perms: Mapped (UNIX) %x to (NT) %x\n",
125 (unsigned int)ace->perms, (unsigned int)nt_mask ));
127 init_sec_access(&sa,nt_mask);
131 /****************************************************************************
132 Map NT perms to a UNIX mode_t.
133 ****************************************************************************/
135 #define FILE_SPECIFIC_READ_BITS (FILE_READ_DATA|FILE_READ_EA|FILE_READ_ATTRIBUTES)
136 #define FILE_SPECIFIC_WRITE_BITS (FILE_WRITE_DATA|FILE_APPEND_DATA|FILE_WRITE_EA|FILE_WRITE_ATTRIBUTES)
137 #define FILE_SPECIFIC_EXECUTE_BITS (FILE_EXECUTE)
139 static mode_t map_nt_perms( SEC_ACCESS sec_access, int type)
145 if(sec_access.mask & GENERIC_ALL_ACCESS)
146 mode = S_IRUSR|S_IWUSR|S_IXUSR;
148 mode |= (sec_access.mask & (GENERIC_READ_ACCESS|FILE_SPECIFIC_READ_BITS)) ? S_IRUSR : 0;
149 mode |= (sec_access.mask & (GENERIC_WRITE_ACCESS|FILE_SPECIFIC_WRITE_BITS)) ? S_IWUSR : 0;
150 mode |= (sec_access.mask & (GENERIC_EXECUTE_ACCESS|FILE_SPECIFIC_EXECUTE_BITS)) ? S_IXUSR : 0;
154 if(sec_access.mask & GENERIC_ALL_ACCESS)
155 mode = S_IRGRP|S_IWGRP|S_IXGRP;
157 mode |= (sec_access.mask & (GENERIC_READ_ACCESS|FILE_SPECIFIC_READ_BITS)) ? S_IRGRP : 0;
158 mode |= (sec_access.mask & (GENERIC_WRITE_ACCESS|FILE_SPECIFIC_WRITE_BITS)) ? S_IWGRP : 0;
159 mode |= (sec_access.mask & (GENERIC_EXECUTE_ACCESS|FILE_SPECIFIC_EXECUTE_BITS)) ? S_IXGRP : 0;
163 if(sec_access.mask & GENERIC_ALL_ACCESS)
164 mode = S_IROTH|S_IWOTH|S_IXOTH;
166 mode |= (sec_access.mask & (GENERIC_READ_ACCESS|FILE_SPECIFIC_READ_BITS)) ? S_IROTH : 0;
167 mode |= (sec_access.mask & (GENERIC_WRITE_ACCESS|FILE_SPECIFIC_WRITE_BITS)) ? S_IWOTH : 0;
168 mode |= (sec_access.mask & (GENERIC_EXECUTE_ACCESS|FILE_SPECIFIC_EXECUTE_BITS)) ? S_IXOTH : 0;
176 /****************************************************************************
177 Unpack a SEC_DESC into a UNIX owner and group.
178 ****************************************************************************/
180 static BOOL unpack_nt_owners(SMB_STRUCT_STAT *psbuf, uid_t *puser, gid_t *pgrp, uint32 security_info_sent, SEC_DESC *psd)
184 enum SID_NAME_USE sid_type;
189 if(security_info_sent == 0) {
190 DEBUG(0,("unpack_nt_owners: no security info sent !\n"));
195 * Validate the owner and group SID's.
198 memset(&owner_sid, '\0', sizeof(owner_sid));
199 memset(&grp_sid, '\0', sizeof(grp_sid));
201 DEBUG(5,("unpack_nt_owners: validating owner_sids.\n"));
204 * Don't immediately fail if the owner sid cannot be validated.
205 * This may be a group chown only set.
208 if (security_info_sent & OWNER_SECURITY_INFORMATION) {
209 sid_copy(&owner_sid, psd->owner_sid);
210 if (!sid_to_uid( &owner_sid, puser, &sid_type))
211 DEBUG(3,("unpack_nt_owners: unable to validate owner sid.\n"));
215 * Don't immediately fail if the group sid cannot be validated.
216 * This may be an owner chown only set.
219 if (security_info_sent & GROUP_SECURITY_INFORMATION) {
220 sid_copy(&grp_sid, psd->grp_sid);
221 if (!sid_to_gid( &grp_sid, pgrp, &sid_type))
222 DEBUG(3,("unpack_nt_owners: unable to validate group sid.\n"));
228 /****************************************************************************
229 Merge aces with a common user.
230 ****************************************************************************/
232 static BOOL merge_aces( canon_ace *list_head, canon_ace *p_ace)
236 for (curr_ace = list_head; curr_ace; curr_ace = curr_ace->next) {
237 if (curr_ace == p_ace)
240 if (curr_ace->type == p_ace->type && sid_equal(&curr_ace->sid, &p_ace->sid)) {
241 if( DEBUGLVL( 10 )) {
242 dbgtext("Merging ACE's\n");
243 print_canon_ace( p_ace, 0);
244 print_canon_ace( curr_ace, 0);
246 p_ace->perms |= curr_ace->perms;
247 DLIST_REMOVE(list_head, curr_ace);
256 /****************************************************************************
257 Unpack a SEC_DESC into two canonical ace lists. We don't depend on this
259 ****************************************************************************/
261 static BOOL unpack_canon_ace(files_struct *fsp,
262 DOM_SID *pfile_owner_sid,
263 DOM_SID *pfile_grp_sid,
264 canon_ace **ppfile_ace, canon_ace **ppdir_ace,
265 uint32 security_info_sent, SEC_DESC *psd)
267 extern DOM_SID global_sid_World;
268 SEC_ACL *dacl = psd->dacl;
269 BOOL all_aces_are_inherit_only = (fsp->is_directory ? True : False);
270 canon_ace *file_ace = NULL;
271 canon_ace *dir_ace = NULL;
272 canon_ace *current_ace = NULL;
273 enum SID_NAME_USE sid_type;
279 if(security_info_sent == 0) {
280 DEBUG(0,("unpack_canon_ace: no security info sent !\n"));
285 * If no DACL then this is a chown only security descriptor.
288 if(!(security_info_sent & DACL_SECURITY_INFORMATION) || !dacl)
292 * Now go through the DACL and create the canon_ace lists.
295 for(i = 0; i < dacl->num_aces; i++) {
296 SEC_ACE *psa = &dacl->ace[i];
298 if((psa->type != SEC_ACE_TYPE_ACCESS_ALLOWED) && (psa->type != SEC_ACE_TYPE_ACCESS_DENIED)) {
299 DEBUG(3,("unpack_canon_ace: unable to set anything but an ALLOW or DENY ACE.\n"));
304 * The security mask may be UNIX_ACCESS_NONE which should map into
305 * no permissions (we overload the WRITE_OWNER bit for this) or it
306 * should be one of the ALL/EXECUTE/READ/WRITE bits. Arrange for this
307 * to be so. Any other bits override the UNIX_ACCESS_NONE bit.
310 psa->info.mask &= (GENERIC_ALL_ACCESS|GENERIC_EXECUTE_ACCESS|GENERIC_WRITE_ACCESS|
311 GENERIC_READ_ACCESS|UNIX_ACCESS_NONE|FILE_ALL_ATTRIBUTES);
313 if(psa->info.mask != UNIX_ACCESS_NONE)
314 psa->info.mask &= ~UNIX_ACCESS_NONE;
317 * Create a cannon_ace entry representing this NT DACL ACE.
320 if ((current_ace = (canon_ace *)malloc(sizeof(canon_ace))) == NULL) {
321 free_canon_ace_list(file_ace);
322 free_canon_ace_list(dir_ace);
323 DEBUG(0,("unpack_canon_ace: malloc fail.\n"));
327 ZERO_STRUCTP(current_ace);
329 sid_copy(¤t_ace->sid, &psa->sid);
332 * Try and work out if the SID is a user or group
333 * as we need to flag these differently for POSIX.
336 if( sid_equal(¤t_ace->sid, &global_sid_World)) {
337 current_ace->owner_type = WORLD_ACE;
338 current_ace->unix_ug.world = -1;
339 } else if (sid_to_uid( ¤t_ace->sid, ¤t_ace->unix_ug.uid, &sid_type)) {
340 current_ace->owner_type = UID_ACE;
341 } else if (sid_to_gid( ¤t_ace->sid, ¤t_ace->unix_ug.gid, &sid_type)) {
342 current_ace->owner_type = GID_ACE;
346 free_canon_ace_list(file_ace);
347 free_canon_ace_list(dir_ace);
349 DEBUG(0,("unpack_canon_ace: unable to map SID %s to uid or gid.\n",
350 sid_to_string(str, ¤t_ace->sid) ));
355 * Map the given NT permissions into a UNIX mode_t containing only
356 * S_I(R|W|X)USR bits.
359 if(psa->type == SEC_ACE_TYPE_ACCESS_ALLOWED)
360 current_ace->perms |= map_nt_perms( psa->info, S_IRUSR);
362 current_ace->perms = 0;
365 * Now note what kind of a POSIX ACL this should map to.
368 if(sid_equal(¤t_ace->sid, pfile_owner_sid)) {
369 /* Note we should apply the default mode/mask here.... FIXME ! JRA */
370 current_ace->type = SMB_ACL_USER_OBJ;
371 } else if( sid_equal(¤t_ace->sid, pfile_grp_sid)) {
372 /* Note we should apply the default mode/mask here.... FIXME ! JRA */
373 current_ace->type = SMB_ACL_GROUP_OBJ;
374 } else if( sid_equal(¤t_ace->sid, &global_sid_World)) {
375 /* Note we should apply the default mode/mask here.... FIXME ! JRA */
376 current_ace->type = SMB_ACL_OTHER;
379 * Could be a SMB_ACL_USER or SMB_ACL_GROUP. Check by
380 * looking at owner_type.
383 current_ace->type = (current_ace->owner_type == UID_ACE) ? SMB_ACL_USER : SMB_ACL_GROUP;
386 if (fsp->is_directory && (psa->flags & SEC_ACE_FLAG_INHERIT_ONLY)) {
388 * We can only add to the default POSIX ACE list if the ACE is
389 * designed to be inherited by both files and directories.
391 if ((psa->flags & (SEC_ACE_FLAG_OBJECT_INHERIT|SEC_ACE_FLAG_CONTAINER_INHERIT)) ==
392 (SEC_ACE_FLAG_OBJECT_INHERIT|SEC_ACE_FLAG_CONTAINER_INHERIT)) {
393 DLIST_ADD(dir_ace, current_ace);
395 DEBUG(0,("unpack_canon_ace: unable to use a non-generic default ACE.\n"));
399 DLIST_ADD(file_ace, current_ace);
400 all_aces_are_inherit_only = False;
404 if (fsp->is_directory && all_aces_are_inherit_only) {
406 * Windows 2000 is doing one of these weird 'inherit acl'
407 * traverses to conserve NTFS ACL resources. Just pretend
408 * there was no DACL sent. JRA.
411 DEBUG(10,("unpack_canon_ace: Win2k inherit acl traverse. Ignoring DACL.\n"));
412 free_sec_acl(&psd->dacl);
416 * Now go through the canon_ace lists and merge entries
417 * belonging to identical users.
422 for (current_ace = file_ace; current_ace; current_ace = current_ace->next ) {
423 if (merge_aces( file_ace, current_ace))
429 for (current_ace = dir_ace; current_ace; current_ace = current_ace->next ) {
430 if (merge_aces( dir_ace, current_ace))
434 *ppfile_ace = file_ace;
435 *ppdir_ace = dir_ace;
439 /****************************************************************************
440 Unpack a SEC_DESC into a set of standard POSIX permissions.
441 ****************************************************************************/
443 static BOOL unpack_posix_permissions(files_struct *fsp, SMB_STRUCT_STAT *psbuf, mode_t *pmode,
444 uint32 security_info_sent, SEC_DESC *psd, BOOL posix_acls)
446 extern DOM_SID global_sid_World;
447 connection_struct *conn = fsp->conn;
448 DOM_SID file_owner_sid;
449 DOM_SID file_grp_sid;
450 SEC_ACL *dacl = psd->dacl;
451 BOOL all_aces_are_inherit_only = (fsp->is_directory ? True : False);
456 if(security_info_sent == 0) {
457 DEBUG(0,("unpack_posix_permissions: no security info sent !\n"));
462 * Windows 2000 sends the owner and group SIDs as the logged in
463 * user, not the connected user. But it still sends the file
464 * owner SIDs on an ACL set. So we need to check for the file
465 * owner and group SIDs as well as the owner SIDs. JRA.
468 create_file_sids(psbuf, &file_owner_sid, &file_grp_sid);
471 * If no DACL then this is a chown only security descriptor.
474 if(!(security_info_sent & DACL_SECURITY_INFORMATION) || !dacl) {
480 * Now go through the DACL and ensure that
481 * any owner/group sids match.
484 for(i = 0; i < dacl->num_aces; i++) {
486 SEC_ACE *psa = &dacl->ace[i];
488 if((psa->type != SEC_ACE_TYPE_ACCESS_ALLOWED) &&
489 (psa->type != SEC_ACE_TYPE_ACCESS_DENIED)) {
490 DEBUG(3,("unpack_posix_permissions: unable to set anything but an ALLOW or DENY ACE.\n"));
495 * Ignore or remove bits we don't care about on a directory ACE.
498 if(fsp->is_directory) {
499 if(psa->flags & SEC_ACE_FLAG_INHERIT_ONLY) {
500 DEBUG(3,("unpack_posix_permissions: ignoring inherit only ACE.\n"));
505 * At least one of the ACE entries wasn't inherit only.
506 * Flag this so we know the returned mode is valid.
509 all_aces_are_inherit_only = False;
513 * Windows 2000 sets these flags even on *file* ACE's. This is wrong
514 * but we can ignore them for now. Revisit this when we go to POSIX
515 * ACLs on directories.
518 psa->flags &= ~(SEC_ACE_FLAG_OBJECT_INHERIT|SEC_ACE_FLAG_CONTAINER_INHERIT);
520 if(psa->flags != 0) {
521 DEBUG(1,("unpack_posix_permissions: unable to set ACE flags (%x).\n",
522 (unsigned int)psa->flags));
527 * The security mask may be UNIX_ACCESS_NONE which should map into
528 * no permissions (we overload the WRITE_OWNER bit for this) or it
529 * should be one of the ALL/EXECUTE/READ/WRITE bits. Arrange for this
530 * to be so. Any other bits override the UNIX_ACCESS_NONE bit.
533 psa->info.mask &= (GENERIC_ALL_ACCESS|GENERIC_EXECUTE_ACCESS|GENERIC_WRITE_ACCESS|
534 GENERIC_READ_ACCESS|UNIX_ACCESS_NONE|FILE_ALL_ATTRIBUTES);
536 if(psa->info.mask != UNIX_ACCESS_NONE)
537 psa->info.mask &= ~UNIX_ACCESS_NONE;
539 sid_copy(&ace_sid, &psa->sid);
541 if(sid_equal(&ace_sid, &file_owner_sid)) {
543 * Map the desired permissions into owner perms.
546 if(psa->type == SEC_ACE_TYPE_ACCESS_ALLOWED)
547 *pmode |= map_nt_perms( psa->info, S_IRUSR);
549 *pmode &= ~(map_nt_perms( psa->info, S_IRUSR));
552 } else if( sid_equal(&ace_sid, &file_grp_sid)) {
554 * Map the desired permissions into group perms.
557 if(psa->type == SEC_ACE_TYPE_ACCESS_ALLOWED)
558 *pmode |= map_nt_perms( psa->info, S_IRGRP);
560 *pmode &= ~(map_nt_perms( psa->info, S_IRGRP));
562 } else if( sid_equal(&ace_sid, &global_sid_World)) {
564 * Map the desired permissions into other perms.
567 if(psa->type == SEC_ACE_TYPE_ACCESS_ALLOWED)
568 *pmode |= map_nt_perms( psa->info, S_IROTH);
570 *pmode &= ~(map_nt_perms( psa->info, S_IROTH));
574 * Only bother printing the level zero error if we didn't get any
578 DEBUG(0,("unpack_posix_permissions: unknown SID used in ACL.\n"));
583 if (fsp->is_directory && all_aces_are_inherit_only) {
585 * Windows 2000 is doing one of these weird 'inherit acl'
586 * traverses to conserve NTFS ACL resources. Just pretend
587 * there was no DACL sent. JRA.
590 DEBUG(10,("unpack_posix_permissions: Win2k inherit acl traverse. Ignoring DACL.\n"));
591 free_sec_acl(&psd->dacl);
595 * Check to see if we need to change anything.
596 * Enforce limits on modified bits *only*. Don't enforce masks
597 * on bits not changed by the user.
600 if(fsp->is_directory) {
602 *pmode &= (lp_dir_security_mask(SNUM(conn)) | psbuf->st_mode);
603 *pmode |= (lp_force_dir_security_mode(SNUM(conn)) & ( *pmode ^ psbuf->st_mode ));
607 *pmode &= (lp_security_mask(SNUM(conn)) | psbuf->st_mode);
608 *pmode |= (lp_force_security_mode(SNUM(conn)) & ( *pmode ^ psbuf->st_mode ));
613 * Preserve special bits.
616 *pmode |= (psbuf->st_mode & ~0777);
621 /****************************************************************************
622 Map POSIX ACL perms to canon_ace permissions (a mode_t containing only S_(R|W|X)USR bits).
623 ****************************************************************************/
625 static mode_t convert_permset_to_mode_t(SMB_ACL_PERMSET_T permset)
629 ret |= (sys_acl_get_perm(permset, SMB_ACL_READ) ? S_IRUSR : 0);
630 ret |= (sys_acl_get_perm(permset, SMB_ACL_WRITE) ? S_IWUSR : 0);
631 ret |= (sys_acl_get_perm(permset, SMB_ACL_EXECUTE) ? S_IXUSR : 0);
636 /****************************************************************************
637 Map generic UNIX permissions to canon_ace permissions (a mode_t containing only S_(R|W|X)USR bits).
638 ****************************************************************************/
640 static mode_t unix_perms_to_acl_perms(mode_t mode, int r_mask, int w_mask, int x_mask)
654 /****************************************************************************
655 Map canon_ace permissions (a mode_t containing only S_(R|W|X)USR bits) to
656 an SMB_ACL_PERMSET_T.
657 ****************************************************************************/
659 static int map_acl_perms_to_permset(mode_t mode, SMB_ACL_PERMSET_T *p_permset)
661 if (sys_acl_clear_perms(*p_permset) == -1)
663 if (mode & S_IRUSR) {
664 if (sys_acl_add_perm(*p_permset, SMB_ACL_READ) == -1)
667 if (mode & S_IWUSR) {
668 if (sys_acl_add_perm(*p_permset, SMB_ACL_WRITE) == -1)
671 if (mode & S_IXUSR) {
672 if (sys_acl_add_perm(*p_permset, SMB_ACL_EXECUTE) == -1)
678 /****************************************************************************
679 Count a linked list of canonical ACE entries.
680 ****************************************************************************/
682 static size_t count_canon_ace_list( canon_ace *list_head )
687 for (ace = list_head; ace; ace = ace->next)
693 /****************************************************************************
694 Free a linked list of canonical ACE entries.
695 ****************************************************************************/
697 static void free_canon_ace_list( canon_ace *list_head )
700 canon_ace *old_head = list_head;
701 DLIST_REMOVE(list_head, list_head);
706 /******************************************************************************
707 Fall back to the generic 3 element UNIX permissions.
708 ********************************************************************************/
710 static canon_ace *unix_canonicalise_acl(files_struct *fsp, SMB_STRUCT_STAT *psbuf,
711 DOM_SID *powner, DOM_SID *pgroup)
713 extern DOM_SID global_sid_World;
714 canon_ace *list_head = NULL;
715 canon_ace *owner_ace = NULL;
716 canon_ace *group_ace = NULL;
717 canon_ace *other_ace = NULL;
720 * Create 3 linked list entries.
723 if ((owner_ace = (canon_ace *)malloc(sizeof(canon_ace))) == NULL)
726 if ((group_ace = (canon_ace *)malloc(sizeof(canon_ace))) == NULL)
729 if ((other_ace = (canon_ace *)malloc(sizeof(canon_ace))) == NULL)
732 ZERO_STRUCTP(owner_ace);
733 ZERO_STRUCTP(group_ace);
734 ZERO_STRUCTP(other_ace);
736 owner_ace->type = SMB_ACL_USER_OBJ;
737 owner_ace->sid = *powner;
738 owner_ace->unix_ug.uid = psbuf->st_uid;
739 owner_ace->owner_type = UID_ACE;
741 group_ace->type = SMB_ACL_GROUP_OBJ;
742 group_ace->sid = *pgroup;
743 owner_ace->unix_ug.gid = psbuf->st_gid;
744 owner_ace->owner_type = GID_ACE;
746 other_ace->type = SMB_ACL_OTHER;
747 other_ace->sid = global_sid_World;
748 owner_ace->unix_ug.world = -1;
749 owner_ace->owner_type = WORLD_ACE;
751 if (!fsp->is_directory) {
752 owner_ace->perms = unix_perms_to_acl_perms(psbuf->st_mode, S_IRUSR, S_IWUSR, S_IXUSR);
753 group_ace->perms = unix_perms_to_acl_perms(psbuf->st_mode, S_IRGRP, S_IWGRP, S_IXGRP);
754 other_ace->perms = unix_perms_to_acl_perms(psbuf->st_mode, S_IROTH, S_IWOTH, S_IXOTH);
756 mode_t mode = unix_mode( fsp->conn, FILE_ATTRIBUTE_ARCHIVE, fsp->fsp_name);
758 owner_ace->perms = unix_perms_to_acl_perms(mode, S_IRUSR, S_IWUSR, S_IXUSR);
759 group_ace->perms = unix_perms_to_acl_perms(mode, S_IRGRP, S_IWGRP, S_IXGRP);
760 other_ace->perms = unix_perms_to_acl_perms(mode, S_IROTH, S_IWOTH, S_IXOTH);
763 DLIST_ADD(list_head, other_ace);
764 DLIST_ADD(list_head, group_ace);
765 DLIST_ADD(list_head, owner_ace);
771 safe_free(owner_ace);
772 safe_free(group_ace);
773 safe_free(other_ace);
778 /****************************************************************************
779 Create a linked list of canonical ACE entries. This is sorted so that DENY
780 entries are at the front of the list, as NT requires.
781 ****************************************************************************/
783 static canon_ace *canonicalise_acl( SMB_ACL_T posix_acl, SMB_STRUCT_STAT *psbuf)
785 extern DOM_SID global_sid_World;
786 mode_t acl_mask = (S_IRUSR|S_IWUSR|S_IXUSR);
787 canon_ace *list_head = NULL;
788 canon_ace *ace = NULL;
789 canon_ace *next_ace = NULL;
790 int entry_id = SMB_ACL_FIRST_ENTRY;
791 SMB_ACL_ENTRY_T entry;
793 while ( sys_acl_get_entry(posix_acl, entry_id, &entry) == 1) {
794 SMB_ACL_TAG_T tagtype;
795 SMB_ACL_PERMSET_T permset;
798 enum ace_owner owner_type;
801 if (entry_id == SMB_ACL_FIRST_ENTRY)
802 entry_id = SMB_ACL_NEXT_ENTRY;
804 /* Is this a MASK entry ? */
805 if (sys_acl_get_tag_type(entry, &tagtype) == -1)
808 if (sys_acl_get_permset(entry, &permset) == -1)
811 /* Decide which SID to use based on the ACL type. */
813 case SMB_ACL_USER_OBJ:
814 /* Get the SID from the owner. */
815 uid_to_sid( &sid, psbuf->st_uid );
816 unix_ug.uid = psbuf->st_uid;
817 owner_type = UID_ACE;
821 uid_t *puid = (uid_t *)sys_acl_get_qualifier(entry);
823 DEBUG(0,("canonicalise_acl: Failed to get uid.\n"));
826 uid_to_sid( &sid, *puid);
828 owner_type = UID_ACE;
831 case SMB_ACL_GROUP_OBJ:
832 /* Get the SID from the owning group. */
833 gid_to_sid( &sid, psbuf->st_gid );
834 unix_ug.gid = psbuf->st_gid;
835 owner_type = GID_ACE;
839 gid_t *pgid = (gid_t *)sys_acl_get_qualifier(entry);
841 DEBUG(0,("canonicalise_acl: Failed to get gid.\n"));
844 gid_to_sid( &sid, *pgid);
846 owner_type = GID_ACE;
850 acl_mask = convert_permset_to_mode_t(permset);
851 continue; /* Don't count the mask as an entry. */
853 /* Use the Everyone SID */
854 sid = global_sid_World;
856 owner_type = WORLD_ACE;
859 DEBUG(0,("canonicalise_acl: Unknown tagtype %u\n", (unsigned int)tagtype));
864 * Add this entry to the list.
867 if ((ace = (canon_ace *)malloc(sizeof(canon_ace))) == NULL)
872 ace->perms = convert_permset_to_mode_t(permset);
874 ace->unix_ug = unix_ug;
875 ace->owner_type = owner_type;
877 DLIST_ADD(list_head, ace);
881 * Now go through the list, masking the permissions with the
882 * acl_mask. If the permissions are 0 it should be listed
886 for ( ace = list_head; ace; ace = next_ace) {
887 next_ace = ace->next;
889 /* Masks are only applied to entries other than USER_OBJ and OTHER. */
890 if (ace->type != SMB_ACL_OTHER && ace->type != SMB_ACL_USER_OBJ)
891 ace->perms &= acl_mask;
894 DLIST_PROMOTE(list_head, ace);
897 if( DEBUGLVL( 10 ) ) {
898 char *acl_text = sys_acl_to_text( posix_acl, NULL);
900 dbgtext("canonicalize_acl: processed acl %s\n", acl_text == NULL ? "NULL" : acl_text );
902 sys_acl_free_text(acl_text);
909 free_canon_ace_list(list_head);
913 /****************************************************************************
914 Attempt to apply an ACL to a file or directory.
915 ****************************************************************************/
917 static BOOL set_canon_ace_list(files_struct *fsp, canon_ace *the_ace, BOOL default_ace)
920 SMB_ACL_T the_acl = sys_acl_init((int)count_canon_ace_list(the_ace) + 1);
923 SMB_ACL_ENTRY_T mask_entry;
924 SMB_ACL_PERMSET_T mask_permset;
925 SMB_ACL_TYPE_T the_acl_type = (default_ace ? SMB_ACL_TYPE_DEFAULT : SMB_ACL_TYPE_ACCESS);
927 if (the_acl == NULL) {
928 #if !defined(HAVE_NO_ACLS)
930 * Only print this error message if we have some kind of ACL
931 * support that's not working. Otherwise we would always get this.
933 DEBUG(0,("set_canon_ace_list: Unable to init %s ACL. (%s)\n",
934 default_ace ? "default" : "file", strerror(errno) ));
939 for (i = 0, p_ace = the_ace; p_ace; p_ace = p_ace->next, i++ ) {
940 SMB_ACL_ENTRY_T the_entry;
941 SMB_ACL_PERMSET_T the_permset;
944 * Get the entry for this ACE.
947 if (sys_acl_create_entry( &the_acl, &the_entry) == -1) {
948 DEBUG(0,("set_canon_ace_list: Failed to create entry %d. (%s)\n",
949 i, strerror(errno) ));
954 * Initialise the entry from the canon_ace.
958 * First tell the entry what type of ACE this is.
961 if (sys_acl_set_tag_type(the_entry, p_ace->type) == -1) {
962 DEBUG(0,("set_canon_ace_list: Failed to set tag type on entry %d. (%s)\n",
963 i, strerror(errno) ));
968 * Only set the qualifier (user or group id) if the entry is a user
972 if ((p_ace->type == SMB_ACL_USER) || (p_ace->type == SMB_ACL_GROUP)) {
973 if (sys_acl_set_qualifier(the_entry,(void *)&p_ace->unix_ug.uid) == -1) {
974 DEBUG(0,("set_canon_ace_list: Failed to set qualifier on entry %d. (%s)\n",
975 i, strerror(errno) ));
981 * Convert the mode_t perms in the canon_ace to a POSIX permset.
984 if (sys_acl_get_permset(the_entry, &the_permset) == -1) {
985 DEBUG(0,("set_canon_ace_list: Failed to get permset on entry %d. (%s)\n",
986 i, strerror(errno) ));
990 if (map_acl_perms_to_permset(p_ace->perms, &the_permset) == -1) {
991 DEBUG(0,("set_canon_ace_list: Failed to create permset for mode (%u) on entry %d. (%s)\n",
992 p_ace->perms, i, strerror(errno) ));
997 * ..and apply them to the entry.
1000 if (sys_acl_set_permset(the_entry, the_permset) == -1) {
1001 DEBUG(0,("set_canon_ace_list: Failed to add permset on entry %d. (%s)\n",
1002 i, strerror(errno) ));
1007 print_canon_ace( p_ace, i);
1011 * Add in a mask of rwx.
1014 if (sys_acl_create_entry( &the_acl, &mask_entry) == -1) {
1015 DEBUG(0,("set_canon_ace_list: Failed to create mask entry. (%s)\n", strerror(errno) ));
1019 if (sys_acl_set_tag_type(mask_entry, SMB_ACL_MASK) == -1) {
1020 DEBUG(0,("set_canon_ace_list: Failed to set tag type on mask entry. (%s)\n",strerror(errno) ));
1024 if (sys_acl_get_permset(mask_entry, &mask_permset) == -1) {
1025 DEBUG(0,("set_canon_ace_list: Failed to get mask permset. (%s)\n", strerror(errno) ));
1029 if (map_acl_perms_to_permset(S_IRUSR|S_IWUSR|S_IXUSR, &mask_permset) == -1) {
1030 DEBUG(0,("set_canon_ace_list: Failed to create mask permset. (%s)\n", strerror(errno) ));
1034 if (sys_acl_set_permset(mask_entry, mask_permset) == -1) {
1035 DEBUG(0,("set_canon_ace_list: Failed to add mask permset. (%s)\n", strerror(errno) ));
1040 * Check if the ACL is valid.
1043 if (sys_acl_valid(the_acl) == -1) {
1044 DEBUG(0,("set_canon_ace_list: ACL is invalid for set (%s).\n", strerror(errno) ));
1049 * Finally apply it to the file or directory.
1052 if(default_ace || fsp->is_directory || fsp->fd == -1) {
1053 if (sys_acl_set_file(fsp->fsp_name, the_acl_type, the_acl) == -1) {
1054 DEBUG(0,("set_canon_ace_list: sys_acl_set_file failed for file %s (%s).\n",
1055 fsp->fsp_name, strerror(errno) ));
1059 if (sys_acl_set_fd(fsp->fd, the_acl) == -1) {
1060 DEBUG(0,("set_canon_ace_list: sys_acl_set_file failed for file %s (%s).\n",
1061 fsp->fsp_name, strerror(errno) ));
1070 if (the_acl != NULL)
1071 sys_acl_free_acl(the_acl);
1076 /****************************************************************************
1077 Reply to query a security descriptor from an fsp. If it succeeds it allocates
1078 the space for the return elements and returns the size needed to return the
1079 security descriptor. This should be the only external function needed for
1080 the UNIX style get ACL.
1081 ****************************************************************************/
1083 size_t get_nt_acl(files_struct *fsp, SEC_DESC **ppdesc)
1085 SMB_STRUCT_STAT sbuf;
1086 SEC_ACE *nt_ace_list;
1090 SEC_ACL *psa = NULL;
1091 size_t num_acls = 0;
1092 size_t num_dir_acls = 0;
1093 size_t num_aces = 0;
1094 SMB_ACL_T posix_acl = NULL;
1095 SMB_ACL_T dir_acl = NULL;
1096 canon_ace *file_ace = NULL;
1097 canon_ace *dir_ace = NULL;
1101 DEBUG(10,("get_nt_acl: called for file %s\n", fsp->fsp_name ));
1103 if(fsp->is_directory || fsp->fd == -1) {
1105 /* Get the stat struct for the owner info. */
1106 if(vfs_stat(fsp->conn,fsp->fsp_name, &sbuf) != 0) {
1110 * Get the ACL from the path.
1113 posix_acl = sys_acl_get_file( dos_to_unix(fsp->fsp_name, False), SMB_ACL_TYPE_ACCESS);
1116 * If it's a directory get the default POSIX ACL.
1119 if(fsp->is_directory)
1120 dir_acl = sys_acl_get_file( dos_to_unix(fsp->fsp_name, False), SMB_ACL_TYPE_DEFAULT);
1124 /* Get the stat struct for the owner info. */
1125 if(vfs_fstat(fsp,fsp->fd,&sbuf) != 0) {
1129 * Get the ACL from the fd.
1131 posix_acl = sys_acl_get_fd(fsp->fd);
1134 DEBUG(5,("get_nt_acl : file ACL %s, directory ACL %s\n",
1135 posix_acl ? "present" : "absent",
1136 dir_acl ? "present" : "absent" ));
1139 * Get the owner, group and world SIDs.
1142 create_file_sids(&sbuf, &owner_sid, &group_sid);
1144 /* Create the canon_ace lists. */
1146 file_ace = canonicalise_acl( posix_acl, &sbuf);
1148 file_ace = unix_canonicalise_acl(fsp, &sbuf, &owner_sid, &group_sid);
1150 num_acls = count_canon_ace_list(file_ace);
1152 if (fsp->is_directory) {
1154 dir_ace = canonicalise_acl( dir_acl, &sbuf);
1156 dir_ace = unix_canonicalise_acl(fsp, &sbuf, &owner_sid, &group_sid);
1158 num_dir_acls = count_canon_ace_list(dir_ace);
1161 /* Allocate the ace list. */
1162 if ((nt_ace_list = (SEC_ACE *)malloc((num_acls + num_dir_acls)* sizeof(SEC_ACE))) == NULL) {
1163 DEBUG(0,("get_nt_acl: Unable to malloc space for nt_ace_list.\n"));
1167 memset(nt_ace_list, '\0', (num_acls + num_dir_acls) * sizeof(SEC_ACE) );
1170 * Create the NT ACE list from the canonical ace lists.
1180 for (i = 0; i < num_acls; i++, ace = ace->next) {
1181 SEC_ACCESS acc = map_canon_ace_perms(&nt_acl_type, &owner_sid, ace );
1182 init_sec_ace(&nt_ace_list[num_aces++], &ace->sid, nt_acl_type, acc, 0);
1187 for (i = 0; i < num_dir_acls; i++, ace = ace->next) {
1188 SEC_ACCESS acc = map_canon_ace_perms(&nt_acl_type, &owner_sid, ace );
1189 init_sec_ace(&nt_ace_list[num_aces++], &ace->sid, nt_acl_type, acc,
1190 SEC_ACE_FLAG_OBJECT_INHERIT|SEC_ACE_FLAG_CONTAINER_INHERIT|SEC_ACE_FLAG_INHERIT_ONLY);
1195 if((psa = make_sec_acl( ACL_REVISION, num_aces, nt_ace_list)) == NULL) {
1196 DEBUG(0,("get_nt_acl: Unable to malloc space for acl.\n"));
1201 *ppdesc = make_standard_sec_desc( &owner_sid, &group_sid, psa, &sd_size);
1204 DEBUG(0,("get_nt_acl: Unable to malloc space for security descriptor.\n"));
1211 sys_acl_free_acl(posix_acl);
1213 sys_acl_free_acl(dir_acl);
1214 free_canon_ace_list(file_ace);
1215 free_canon_ace_list(dir_ace);
1224 /****************************************************************************
1225 Reply to set a security descriptor on an fsp. security_info_sent is the
1226 description of the following NT ACL.
1227 This should be the only external function needed for the UNIX style set ACL.
1228 ****************************************************************************/
1230 BOOL set_nt_acl(files_struct *fsp, uint32 security_info_sent, SEC_DESC *psd)
1232 connection_struct *conn = fsp->conn;
1233 uid_t user = (uid_t)-1;
1234 gid_t grp = (gid_t)-1;
1236 SMB_STRUCT_STAT sbuf;
1237 DOM_SID file_owner_sid;
1238 DOM_SID file_grp_sid;
1239 canon_ace *file_ace_list = NULL;
1240 canon_ace *dir_ace_list = NULL;
1244 DEBUG(10,("set_nt_acl: called for file %s\n", fsp->fsp_name ));
1247 * Get the current state of the file.
1250 if(fsp->is_directory || fsp->fd == -1) {
1251 if(vfs_stat(fsp->conn,fsp->fsp_name, &sbuf) != 0)
1254 if(vfs_fstat(fsp,fsp->fd,&sbuf) != 0)
1259 * Unpack the user/group/world id's.
1262 if (!unpack_nt_owners( &sbuf, &user, &grp, security_info_sent, psd))
1266 * Do we need to chown ?
1269 if((user != (uid_t)-1 || grp != (uid_t)-1) && (sbuf.st_uid != user || sbuf.st_gid != grp)) {
1271 DEBUG(3,("set_nt_acl: chown %s. uid = %u, gid = %u.\n",
1272 fsp->fsp_name, (unsigned int)user, (unsigned int)grp ));
1274 if(vfs_chown( fsp->conn, fsp->fsp_name, user, grp) == -1) {
1275 DEBUG(3,("set_nt_acl: chown %s, %u, %u failed. Error = %s.\n",
1276 fsp->fsp_name, (unsigned int)user, (unsigned int)grp, strerror(errno) ));
1281 * Recheck the current state of the file, which may have changed.
1282 * (suid/sgid bits, for instance)
1285 if(fsp->is_directory) {
1286 if(vfs_stat(fsp->conn, fsp->fsp_name, &sbuf) != 0) {
1294 ret = vfs_stat(fsp->conn, fsp->fsp_name, &sbuf);
1296 ret = vfs_fstat(fsp,fsp->fd,&sbuf);
1303 create_file_sids(&sbuf, &file_owner_sid, &file_grp_sid);
1305 acl_perms = unpack_canon_ace( fsp, &file_owner_sid, &file_grp_sid,
1306 &file_ace_list, &dir_ace_list, security_info_sent, psd);
1307 posix_perms = unpack_posix_permissions( fsp, &sbuf, &perms, security_info_sent, psd, acl_perms);
1309 if (!posix_perms && !acl_perms) {
1311 * Neither method of setting permissions can work. Fail here.
1314 DEBUG(3,("set_nt_acl: cannot set normal POSIX permissions or POSIX ACL permissions\n"));
1315 free_canon_ace_list(file_ace_list);
1316 free_canon_ace_list(dir_ace_list);
1321 * Only change security if we got a DACL.
1324 if((security_info_sent & DACL_SECURITY_INFORMATION) && (psd->dacl != NULL)) {
1326 BOOL acl_set_support = False;
1329 * Try using the POSIX ACL set first. All back to chmod if
1330 * we have no ACL support on this filesystem.
1333 if (acl_perms && file_ace_list && set_canon_ace_list(fsp, file_ace_list, False))
1334 acl_set_support = True;
1336 if (acl_perms && acl_set_support && fsp->is_directory && dir_ace_list)
1337 set_canon_ace_list(fsp, dir_ace_list, True);
1340 * If we cannot set using POSIX ACLs we fall back to checking if we need to chmod.
1343 if(!acl_set_support && (sbuf.st_mode != perms)) {
1345 free_canon_ace_list(file_ace_list);
1346 free_canon_ace_list(dir_ace_list);
1347 file_ace_list = NULL;
1348 dir_ace_list = NULL;
1350 DEBUG(3,("call_nt_transact_set_security_desc: chmod %s. perms = 0%o.\n",
1351 fsp->fsp_name, (unsigned int)perms ));
1353 if(conn->vfs_ops.chmod(conn,dos_to_unix(fsp->fsp_name, False), perms) == -1) {
1354 DEBUG(3,("set_nt_acl: chmod %s, 0%o failed. Error = %s.\n",
1355 fsp->fsp_name, (unsigned int)perms, strerror(errno) ));
1361 free_canon_ace_list(file_ace_list);
1362 free_canon_ace_list(dir_ace_list);