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 typedef struct canon_ace {
26 struct canon_ace *next, *prev;
32 /****************************************************************************
33 Function to create owner and group SIDs from a SMB_STRUCT_STAT.
34 ****************************************************************************/
36 static void create_file_sids(SMB_STRUCT_STAT *psbuf, DOM_SID *powner_sid, DOM_SID *pgroup_sid)
38 uid_to_sid( powner_sid, psbuf->st_uid );
39 gid_to_sid( pgroup_sid, psbuf->st_gid );
42 /****************************************************************************
43 Map canon_ace perms to NT.
44 ****************************************************************************/
46 static SEC_ACCESS map_canon_ace_perms(int *pacl_type, DOM_SID *powner_sid, canon_ace *ace)
51 *pacl_type = SEC_ACE_TYPE_ACCESS_ALLOWED;
53 if((ace->perms & (S_IRWXU|S_IWUSR|S_IXUSR)) == (S_IRWXU|S_IWUSR|S_IXUSR)) {
54 nt_mask = UNIX_ACCESS_RWX;
55 } else if((ace->perms & (S_IRWXU|S_IWUSR|S_IXUSR)) == 0) {
57 * Here we differentiate between the owner and any other user.
59 if (sid_equal(powner_sid, &ace->sid)) {
60 nt_mask = UNIX_ACCESS_NONE;
62 /* Not owner, no access. */
66 nt_mask |= ((ace->perms & S_IRWXU) ? UNIX_ACCESS_R : 0 );
67 nt_mask |= ((ace->perms & S_IWUSR) ? UNIX_ACCESS_W : 0 );
68 nt_mask |= ((ace->perms & S_IXUSR) ? UNIX_ACCESS_X : 0 );
71 DEBUG(10,("map_canon_ace_perms: Mapped (UNIX) %x to (NT) %x\n",
72 (unsigned int)ace->perms, (unsigned int)nt_mask ));
74 init_sec_access(&sa,nt_mask);
78 /****************************************************************************
80 ****************************************************************************/
82 #define FILE_SPECIFIC_READ_BITS (FILE_READ_DATA|FILE_READ_EA|FILE_READ_ATTRIBUTES)
83 #define FILE_SPECIFIC_WRITE_BITS (FILE_WRITE_DATA|FILE_APPEND_DATA|FILE_WRITE_EA|FILE_WRITE_ATTRIBUTES)
84 #define FILE_SPECIFIC_EXECUTE_BITS (FILE_EXECUTE)
86 static mode_t map_nt_perms( SEC_ACCESS sec_access, int type)
92 if(sec_access.mask & GENERIC_ALL_ACCESS)
93 mode = S_IRUSR|S_IWUSR|S_IXUSR;
95 mode |= (sec_access.mask & (GENERIC_READ_ACCESS|FILE_SPECIFIC_READ_BITS)) ? S_IRUSR : 0;
96 mode |= (sec_access.mask & (GENERIC_WRITE_ACCESS|FILE_SPECIFIC_WRITE_BITS)) ? S_IWUSR : 0;
97 mode |= (sec_access.mask & (GENERIC_EXECUTE_ACCESS|FILE_SPECIFIC_EXECUTE_BITS)) ? S_IXUSR : 0;
101 if(sec_access.mask & GENERIC_ALL_ACCESS)
102 mode = S_IRGRP|S_IWGRP|S_IXGRP;
104 mode |= (sec_access.mask & (GENERIC_READ_ACCESS|FILE_SPECIFIC_READ_BITS)) ? S_IRGRP : 0;
105 mode |= (sec_access.mask & (GENERIC_WRITE_ACCESS|FILE_SPECIFIC_WRITE_BITS)) ? S_IWGRP : 0;
106 mode |= (sec_access.mask & (GENERIC_EXECUTE_ACCESS|FILE_SPECIFIC_EXECUTE_BITS)) ? S_IXGRP : 0;
110 if(sec_access.mask & GENERIC_ALL_ACCESS)
111 mode = S_IROTH|S_IWOTH|S_IXOTH;
113 mode |= (sec_access.mask & (GENERIC_READ_ACCESS|FILE_SPECIFIC_READ_BITS)) ? S_IROTH : 0;
114 mode |= (sec_access.mask & (GENERIC_WRITE_ACCESS|FILE_SPECIFIC_WRITE_BITS)) ? S_IWOTH : 0;
115 mode |= (sec_access.mask & (GENERIC_EXECUTE_ACCESS|FILE_SPECIFIC_EXECUTE_BITS)) ? S_IXOTH : 0;
123 /****************************************************************************
124 Unpack a SEC_DESC into a owner, group and set of UNIX permissions.
125 ****************************************************************************/
127 static BOOL unpack_nt_permissions(SMB_STRUCT_STAT *psbuf, uid_t *puser, gid_t *pgrp, mode_t *pmode,
128 uint32 security_info_sent, SEC_DESC *psd, BOOL is_directory)
130 extern DOM_SID global_sid_World;
133 DOM_SID file_owner_sid;
134 DOM_SID file_grp_sid;
135 SEC_ACL *dacl = psd->dacl;
136 BOOL all_aces_are_inherit_only = (is_directory ? True : False);
138 enum SID_NAME_USE sid_type;
144 if(security_info_sent == 0) {
145 DEBUG(0,("unpack_nt_permissions: no security info sent !\n"));
150 * Windows 2000 sends the owner and group SIDs as the logged in
151 * user, not the connected user. But it still sends the file
152 * owner SIDs on an ACL set. So we need to check for the file
153 * owner and group SIDs as well as the owner SIDs. JRA.
156 create_file_sids(psbuf, &file_owner_sid, &file_grp_sid);
159 * Validate the owner and group SID's.
162 memset(&owner_sid, '\0', sizeof(owner_sid));
163 memset(&grp_sid, '\0', sizeof(grp_sid));
165 DEBUG(5,("unpack_nt_permissions: validating owner_sid.\n"));
168 * Don't immediately fail if the owner sid cannot be validated.
169 * This may be a group chown only set.
172 if (security_info_sent & OWNER_SECURITY_INFORMATION) {
173 sid_copy(&owner_sid, psd->owner_sid);
174 if (!sid_to_uid( &owner_sid, puser, &sid_type))
175 DEBUG(3,("unpack_nt_permissions: unable to validate owner sid.\n"));
179 * Don't immediately fail if the group sid cannot be validated.
180 * This may be an owner chown only set.
183 if (security_info_sent & GROUP_SECURITY_INFORMATION) {
184 sid_copy(&grp_sid, psd->grp_sid);
185 if (!sid_to_gid( &grp_sid, pgrp, &sid_type))
186 DEBUG(3,("unpack_nt_permissions: unable to validate group sid.\n"));
190 * If no DACL then this is a chown only security descriptor.
193 if(!(security_info_sent & DACL_SECURITY_INFORMATION) || !dacl) {
199 * Now go through the DACL and ensure that
200 * any owner/group sids match.
203 for(i = 0; i < dacl->num_aces; i++) {
205 SEC_ACE *psa = &dacl->ace[i];
207 if((psa->type != SEC_ACE_TYPE_ACCESS_ALLOWED) &&
208 (psa->type != SEC_ACE_TYPE_ACCESS_DENIED)) {
209 DEBUG(3,("unpack_nt_permissions: unable to set anything but an ALLOW or DENY ACE.\n"));
214 * Ignore or remove bits we don't care about on a directory ACE.
218 if(psa->flags & SEC_ACE_FLAG_INHERIT_ONLY) {
219 DEBUG(3,("unpack_nt_permissions: ignoring inherit only ACE.\n"));
224 * At least one of the ACE entries wasn't inherit only.
225 * Flag this so we know the returned mode is valid.
228 all_aces_are_inherit_only = False;
232 * Windows 2000 sets these flags even on *file* ACE's. This is wrong
233 * but we can ignore them for now. Revisit this when we go to POSIX
234 * ACLs on directories.
237 psa->flags &= ~(SEC_ACE_FLAG_OBJECT_INHERIT|SEC_ACE_FLAG_CONTAINER_INHERIT);
239 if(psa->flags != 0) {
240 DEBUG(1,("unpack_nt_permissions: unable to set ACE flags (%x).\n",
241 (unsigned int)psa->flags));
246 * The security mask may be UNIX_ACCESS_NONE which should map into
247 * no permissions (we overload the WRITE_OWNER bit for this) or it
248 * should be one of the ALL/EXECUTE/READ/WRITE bits. Arrange for this
249 * to be so. Any other bits override the UNIX_ACCESS_NONE bit.
252 psa->info.mask &= (GENERIC_ALL_ACCESS|GENERIC_EXECUTE_ACCESS|GENERIC_WRITE_ACCESS|
253 GENERIC_READ_ACCESS|UNIX_ACCESS_NONE|FILE_ALL_ATTRIBUTES);
255 if(psa->info.mask != UNIX_ACCESS_NONE)
256 psa->info.mask &= ~UNIX_ACCESS_NONE;
258 sid_copy(&ace_sid, &psa->sid);
260 if(sid_equal(&ace_sid, &file_owner_sid)) {
262 * Map the desired permissions into owner perms.
265 if(psa->type == SEC_ACE_TYPE_ACCESS_ALLOWED)
266 *pmode |= map_nt_perms( psa->info, S_IRUSR);
268 *pmode &= ~(map_nt_perms( psa->info, S_IRUSR));
270 } else if( sid_equal(&ace_sid, &file_grp_sid)) {
272 * Map the desired permissions into group perms.
275 if(psa->type == SEC_ACE_TYPE_ACCESS_ALLOWED)
276 *pmode |= map_nt_perms( psa->info, S_IRGRP);
278 *pmode &= ~(map_nt_perms( psa->info, S_IRGRP));
280 } else if( sid_equal(&ace_sid, &global_sid_World)) {
282 * Map the desired permissions into other perms.
285 if(psa->type == SEC_ACE_TYPE_ACCESS_ALLOWED)
286 *pmode |= map_nt_perms( psa->info, S_IROTH);
288 *pmode &= ~(map_nt_perms( psa->info, S_IROTH));
291 DEBUG(0,("unpack_nt_permissions: unknown SID used in ACL.\n"));
296 if (is_directory && all_aces_are_inherit_only) {
298 * Windows 2000 is doing one of these weird 'inherit acl'
299 * traverses to conserve NTFS ACL resources. Just pretend
300 * there was no DACL sent. JRA.
303 DEBUG(10,("unpack_nt_permissions: Win2k inherit acl traverse. Ignoring DACL.\n"));
304 free_sec_acl(&psd->dacl);
310 /****************************************************************************
311 Map generic UNIX permissions to POSIX ACL perms.
312 ****************************************************************************/
314 static mode_t convert_permset_to_mode_t(SMB_ACL_PERMSET_T permset)
318 ret |= (sys_acl_get_perm(permset, SMB_ACL_READ) ? S_IRUSR : 0);
319 ret |= (sys_acl_get_perm(permset, SMB_ACL_WRITE) ? S_IWUSR : 0);
320 ret |= (sys_acl_get_perm(permset, SMB_ACL_EXECUTE) ? S_IXUSR : 0);
325 /****************************************************************************
326 Map generic UNIX permissions to POSIX ACL perms.
327 ****************************************************************************/
329 static mode_t unix_perms_to_acl_perms(mode_t mode, int r_mask, int w_mask, int x_mask)
343 /****************************************************************************
344 Count a linked list of canonical ACE entries.
345 ****************************************************************************/
347 static size_t count_canon_ace_list( canon_ace *list_head )
352 for (ace = list_head; ace; ace = ace->next)
358 /****************************************************************************
359 Free a linked list of canonical ACE entries.
360 ****************************************************************************/
362 static void free_canon_ace_list( canon_ace *list_head )
365 canon_ace *old_head = list_head;
366 DLIST_REMOVE(list_head, list_head);
371 /******************************************************************************
372 Fall back to the generic 3 element UNIX permissions.
373 ********************************************************************************/
375 static canon_ace *unix_canonicalise_acl(files_struct *fsp, SMB_STRUCT_STAT *psbuf,
376 DOM_SID *powner, DOM_SID *pgroup)
378 extern DOM_SID global_sid_World;
379 canon_ace *list_head = NULL;
380 canon_ace *owner_ace = NULL;
381 canon_ace *group_ace = NULL;
382 canon_ace *other_ace = NULL;
385 * Create 3 linked list entries.
388 if ((owner_ace = (canon_ace *)malloc(sizeof(canon_ace))) == NULL)
391 if ((group_ace = (canon_ace *)malloc(sizeof(canon_ace))) == NULL)
394 if ((other_ace = (canon_ace *)malloc(sizeof(canon_ace))) == NULL)
397 ZERO_STRUCTP(owner_ace);
398 ZERO_STRUCTP(group_ace);
399 ZERO_STRUCTP(other_ace);
401 owner_ace->type = SMB_ACL_USER_OBJ;
402 owner_ace->sid = *powner;
404 group_ace->type = SMB_ACL_GROUP_OBJ;
405 group_ace->sid = *pgroup;
407 other_ace->type = SMB_ACL_OTHER;
408 other_ace->sid = global_sid_World;
410 if (!fsp->is_directory) {
411 owner_ace->perms = unix_perms_to_acl_perms(psbuf->st_mode, S_IRUSR, S_IWUSR, S_IXUSR);
412 group_ace->perms = unix_perms_to_acl_perms(psbuf->st_mode, S_IRGRP, S_IWGRP, S_IXGRP);
413 other_ace->perms = unix_perms_to_acl_perms(psbuf->st_mode, S_IROTH, S_IWOTH, S_IXOTH);
415 mode_t mode = unix_mode( fsp->conn, FILE_ATTRIBUTE_ARCHIVE, fsp->fsp_name);
417 owner_ace->perms = unix_perms_to_acl_perms(mode, S_IRUSR, S_IWUSR, S_IXUSR);
418 group_ace->perms = unix_perms_to_acl_perms(mode, S_IRGRP, S_IWGRP, S_IXGRP);
419 other_ace->perms = unix_perms_to_acl_perms(mode, S_IROTH, S_IWOTH, S_IXOTH);
422 DLIST_ADD(list_head, other_ace);
423 DLIST_ADD(list_head, group_ace);
424 DLIST_ADD(list_head, owner_ace);
430 safe_free(owner_ace);
431 safe_free(group_ace);
432 safe_free(other_ace);
437 /****************************************************************************
438 Create a linked list of canonical ACE entries. This is sorted so that DENY
439 entries are at the front of the list, as NT requires.
440 ****************************************************************************/
442 static canon_ace *canonicalise_acl( SMB_ACL_T posix_acl, SMB_STRUCT_STAT *psbuf)
444 extern DOM_SID global_sid_World;
445 mode_t acl_mask = (S_IRUSR|S_IWUSR|S_IXUSR);
446 canon_ace *list_head = NULL;
447 canon_ace *ace = NULL;
448 canon_ace *next_ace = NULL;
449 int entry_id = SMB_ACL_FIRST_ENTRY;
450 SMB_ACL_ENTRY_T entry;
452 while ( sys_acl_get_entry(posix_acl, entry_id, &entry) == 1) {
453 SMB_ACL_TAG_T tagtype;
454 SMB_ACL_PERMSET_T permset;
458 if (entry_id == SMB_ACL_FIRST_ENTRY)
459 entry_id = SMB_ACL_NEXT_ENTRY;
461 /* Is this a MASK entry ? */
462 if (sys_acl_get_tag_type(entry, &tagtype) == -1)
465 if (sys_acl_get_permset(entry, &permset) == -1)
468 /* Decide which SID to use based on the ACL type. */
470 case SMB_ACL_USER_OBJ:
471 /* Get the SID from the owner. */
472 uid_to_sid( &sid, psbuf->st_uid );
476 uid_t *puid = (uid_t *)sys_acl_get_qualifier(entry);
478 DEBUG(0,("canonicalise_acl: Failed to get uid.\n"));
481 uid_to_sid( &sid, *puid);
484 case SMB_ACL_GROUP_OBJ:
485 /* Get the SID from the owning group. */
486 gid_to_sid( &sid, psbuf->st_gid );
490 gid_t *pgid = (gid_t *)sys_acl_get_qualifier(entry);
492 DEBUG(0,("canonicalise_acl: Failed to get gid.\n"));
495 gid_to_sid( &sid, *pgid);
499 acl_mask = convert_permset_to_mode_t(permset);
500 continue; /* Don't count the mask as an entry. */
502 /* Use the Everyone SID */
503 sid = global_sid_World;
506 DEBUG(0,("canonicalise_acl: Unknown tagtype %u\n", (unsigned int)tagtype));
511 * Add this entry to the list.
514 if ((ace = (canon_ace *)malloc(sizeof(canon_ace))) == NULL)
519 ace->perms = convert_permset_to_mode_t(permset);
522 DLIST_ADD(list_head, ace);
526 * Now go through the list, masking the permissions with the
527 * acl_mask. If the permissions are 0 it should be listed
531 for ( ace = list_head; ace; ace = next_ace) {
532 next_ace = ace->next;
534 /* Masks are only applied to entries other than USER_OBJ and OTHER. */
535 if (ace->type != SMB_ACL_OTHER && ace->type != SMB_ACL_USER_OBJ)
536 ace->perms &= acl_mask;
539 DLIST_PROMOTE(list_head, ace);
542 if( DEBUGLVL( 10 ) ) {
543 char *acl_text = sys_acl_to_text( posix_acl, NULL);
545 dbgtext("canonicalize_acl: processed acl %s\n", acl_text == NULL ? "NULL" : acl_text );
547 sys_acl_free_text(acl_text);
554 free_canon_ace_list(list_head);
558 /****************************************************************************
559 Reply to query a security descriptor from an fsp. If it succeeds it allocates
560 the space for the return elements and returns the size needed to return the
561 security descriptor. This should be the only external function needed for
562 the UNIX style get ACL.
563 ****************************************************************************/
565 size_t get_nt_acl(files_struct *fsp, SEC_DESC **ppdesc)
567 SMB_STRUCT_STAT sbuf;
568 SEC_ACE *nt_ace_list;
574 size_t num_dir_acls = 0;
576 SMB_ACL_T posix_acl = NULL;
577 SMB_ACL_T dir_acl = NULL;
578 canon_ace *file_ace = NULL;
579 canon_ace *dir_ace = NULL;
583 if(fsp->is_directory || fsp->fd == -1) {
585 /* Get the stat struct for the owner info. */
586 if(vfs_stat(fsp->conn,fsp->fsp_name, &sbuf) != 0) {
590 * Get the ACL from the path.
593 posix_acl = sys_acl_get_file( dos_to_unix(fsp->fsp_name, False), SMB_ACL_TYPE_ACCESS);
596 * If it's a directory get the default POSIX ACL.
599 if(fsp->is_directory)
600 dir_acl = sys_acl_get_file( dos_to_unix(fsp->fsp_name, False), SMB_ACL_TYPE_DEFAULT);
604 /* Get the stat struct for the owner info. */
605 if(vfs_fstat(fsp,fsp->fd,&sbuf) != 0) {
609 * Get the ACL from the fd.
611 posix_acl = sys_acl_get_fd(fsp->fd);
614 DEBUG(5,("get_nt_acl : file ACL %s, directory ACL %s\n",
615 posix_acl ? "present" : "absent",
616 dir_acl ? "present" : "absent" ));
619 * Get the owner, group and world SIDs.
622 create_file_sids(&sbuf, &owner_sid, &group_sid);
624 /* Create the canon_ace lists. */
626 file_ace = canonicalise_acl( posix_acl, &sbuf);
628 file_ace = unix_canonicalise_acl(fsp, &sbuf, &owner_sid, &group_sid);
630 num_acls = count_canon_ace_list(file_ace);
632 if (fsp->is_directory) {
634 dir_ace = canonicalise_acl( dir_acl, &sbuf);
636 dir_ace = unix_canonicalise_acl(fsp, &sbuf, &owner_sid, &group_sid);
638 num_dir_acls = count_canon_ace_list(dir_ace);
641 /* Allocate the ace list. */
642 if ((nt_ace_list = (SEC_ACE *)malloc((num_acls + num_dir_acls)* sizeof(SEC_ACE))) == NULL) {
643 DEBUG(0,("get_nt_acl: Unable to malloc space for nt_ace_list.\n"));
647 memset(nt_ace_list, '\0', (num_acls + num_dir_acls) * sizeof(SEC_ACE) );
650 * Create the NT ACE list from the canonical ace lists.
660 for (i = 0; i < num_acls; i++, ace = ace->next) {
661 SEC_ACCESS acc = map_canon_ace_perms(&nt_acl_type, &owner_sid, ace );
662 init_sec_ace(&nt_ace_list[num_aces++], &ace->sid, nt_acl_type, acc, 0);
667 for (i = 0; i < num_dir_acls; i++, ace = ace->next) {
668 SEC_ACCESS acc = map_canon_ace_perms(&nt_acl_type, &owner_sid, ace );
669 init_sec_ace(&nt_ace_list[num_aces++], &ace->sid, nt_acl_type, acc,
670 SEC_ACE_FLAG_OBJECT_INHERIT|SEC_ACE_FLAG_CONTAINER_INHERIT|SEC_ACE_FLAG_INHERIT_ONLY);
675 if((psa = make_sec_acl( ACL_REVISION, num_aces, nt_ace_list)) == NULL) {
676 DEBUG(0,("get_nt_acl: Unable to malloc space for acl.\n"));
681 *ppdesc = make_standard_sec_desc( &owner_sid, &group_sid, psa, &sd_size);
684 DEBUG(0,("get_nt_acl: Unable to malloc space for security descriptor.\n"));
691 sys_acl_free_acl(posix_acl);
693 sys_acl_free_acl(dir_acl);
695 free_canon_ace_list(file_ace);
697 free_canon_ace_list(dir_ace);
706 /****************************************************************************
707 Reply to set a security descriptor on an fsp. security_info_sent is the
708 description of the following NT ACL.
709 This should be the only external function needed for the UNIX style set ACL.
710 ****************************************************************************/
712 BOOL set_nt_acl(files_struct *fsp, uint32 security_info_sent, SEC_DESC *psd)
714 connection_struct *conn = fsp->conn;
715 uid_t user = (uid_t)-1;
716 gid_t grp = (gid_t)-1;
718 SMB_STRUCT_STAT sbuf;
719 BOOL got_dacl = False;
722 * Get the current state of the file.
725 if(fsp->is_directory || fsp->fd == -1) {
726 if(vfs_stat(fsp->conn,fsp->fsp_name, &sbuf) != 0)
729 if(conn->vfs_ops.fstat(fsp,fsp->fd,&sbuf) != 0)
734 * Unpack the user/group/world id's and permissions.
737 if (!unpack_nt_permissions( &sbuf, &user, &grp, &perms, security_info_sent, psd, fsp->is_directory))
740 if (psd->dacl != NULL)
744 * Do we need to chown ?
747 if((user != (uid_t)-1 || grp != (uid_t)-1) && (sbuf.st_uid != user || sbuf.st_gid != grp)) {
749 DEBUG(3,("call_nt_transact_set_security_desc: chown %s. uid = %u, gid = %u.\n",
750 fsp->fsp_name, (unsigned int)user, (unsigned int)grp ));
752 if(vfs_chown( fsp->conn, fsp->fsp_name, user, grp) == -1) {
753 DEBUG(3,("call_nt_transact_set_security_desc: chown %s, %u, %u failed. Error = %s.\n",
754 fsp->fsp_name, (unsigned int)user, (unsigned int)grp, strerror(errno) ));
759 * Recheck the current state of the file, which may have changed.
760 * (suid/sgid bits, for instance)
763 if(fsp->is_directory) {
764 if(vfs_stat(fsp->conn, fsp->fsp_name, &sbuf) != 0) {
772 ret = vfs_stat(fsp->conn, fsp->fsp_name, &sbuf);
774 ret = conn->vfs_ops.fstat(fsp,fsp->fd,&sbuf);
782 * Only change security if we got a DACL.
785 if((security_info_sent & DACL_SECURITY_INFORMATION) && got_dacl) {
788 * Check to see if we need to change anything.
789 * Enforce limits on modified bits *only*. Don't enforce masks
790 * on bits not changed by the user.
793 if(fsp->is_directory) {
795 perms &= (lp_dir_security_mask(SNUM(conn)) | sbuf.st_mode);
796 perms |= (lp_force_dir_security_mode(SNUM(conn)) & ( perms ^ sbuf.st_mode ));
800 perms &= (lp_security_mask(SNUM(conn)) | sbuf.st_mode);
801 perms |= (lp_force_security_mode(SNUM(conn)) & ( perms ^ sbuf.st_mode ));
806 * Preserve special bits.
809 perms |= (sbuf.st_mode & ~0777);
812 * Do we need to chmod ?
815 if(sbuf.st_mode != perms) {
817 DEBUG(3,("call_nt_transact_set_security_desc: chmod %s. perms = 0%o.\n",
818 fsp->fsp_name, (unsigned int)perms ));
820 if(conn->vfs_ops.chmod(conn,dos_to_unix(fsp->fsp_name, False), perms) == -1) {
821 DEBUG(3,("call_nt_transact_set_security_desc: chmod %s, 0%o failed. Error = %s.\n",
822 fsp->fsp_name, (unsigned int)perms, strerror(errno) ));