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 /****************************************************************************
26 Function to create owner and group SIDs from a SMB_STRUCT_STAT.
27 ****************************************************************************/
29 static void create_file_sids(SMB_STRUCT_STAT
*psbuf
, DOM_SID
*powner_sid
, DOM_SID
*pgroup_sid
)
31 extern DOM_SID global_sam_sid
;
33 sid_copy(powner_sid
, &global_sam_sid
);
34 sid_copy(pgroup_sid
, &global_sam_sid
);
35 sid_append_rid(powner_sid
, pdb_uid_to_user_rid(psbuf
->st_uid
));
36 sid_append_rid(pgroup_sid
, pdb_gid_to_group_rid(psbuf
->st_gid
));
39 /****************************************************************************
41 ****************************************************************************/
43 static SEC_ACCESS
map_unix_perms( int *pacl_type
, mode_t perm
, int r_mask
, int w_mask
, int x_mask
, BOOL is_directory
)
48 *pacl_type
= SEC_ACE_TYPE_ACCESS_ALLOWED
;
50 if((perm
& (r_mask
|w_mask
|x_mask
)) == (r_mask
|w_mask
|x_mask
)) {
51 nt_mask
= UNIX_ACCESS_RWX
;
52 } else if((perm
& (r_mask
|w_mask
|x_mask
)) == 0) {
53 nt_mask
= UNIX_ACCESS_NONE
;
55 nt_mask
|= (perm
& r_mask
) ? UNIX_ACCESS_R
: 0;
57 nt_mask
|= (perm
& w_mask
) ? UNIX_ACCESS_W
: 0;
59 nt_mask
|= (perm
& w_mask
) ? UNIX_ACCESS_W
: 0;
60 nt_mask
|= (perm
& x_mask
) ? UNIX_ACCESS_X
: 0;
62 init_sec_access(&sa
,nt_mask
);
66 /****************************************************************************
68 ****************************************************************************/
70 static BOOL
validate_unix_sid( DOM_SID
*psid
, uint32
*prid
, DOM_SID
*sd_sid
)
72 extern DOM_SID global_sam_sid
;
76 DEBUG(5,("validate_unix_sid: sid missing.\n"));
80 sid_copy(psid
, sd_sid
);
81 sid_copy(&sid
, sd_sid
);
83 if(!sid_split_rid(&sid
, prid
)) {
84 DEBUG(5,("validate_unix_sid: cannot get RID from sid.\n"));
88 if(!sid_equal( &sid
, &global_sam_sid
)) {
89 DEBUG(5,("validate_unix_sid: sid is not ours.\n"));
96 /****************************************************************************
98 ****************************************************************************/
100 #define FILE_SPECIFIC_READ_BITS (FILE_READ_DATA|FILE_READ_EA|FILE_READ_ATTRIBUTES)
101 #define FILE_SPECIFIC_WRITE_BITS (FILE_WRITE_DATA|FILE_APPEND_DATA|FILE_WRITE_EA|FILE_WRITE_ATTRIBUTES)
102 #define FILE_SPECIFIC_EXECUTE_BITS (FILE_EXECUTE)
104 static mode_t
map_nt_perms( SEC_ACCESS sec_access
, int type
)
110 if(sec_access
.mask
& GENERIC_ALL_ACCESS
)
111 mode
= S_IRUSR
|S_IWUSR
|S_IXUSR
;
113 mode
|= (sec_access
.mask
& (GENERIC_READ_ACCESS
|FILE_SPECIFIC_READ_BITS
)) ? S_IRUSR
: 0;
114 mode
|= (sec_access
.mask
& (GENERIC_WRITE_ACCESS
|FILE_SPECIFIC_WRITE_BITS
)) ? S_IWUSR
: 0;
115 mode
|= (sec_access
.mask
& (GENERIC_EXECUTE_ACCESS
|FILE_SPECIFIC_EXECUTE_BITS
)) ? S_IXUSR
: 0;
119 if(sec_access
.mask
& GENERIC_ALL_ACCESS
)
120 mode
= S_IRGRP
|S_IWGRP
|S_IXGRP
;
122 mode
|= (sec_access
.mask
& (GENERIC_READ_ACCESS
|FILE_SPECIFIC_READ_BITS
)) ? S_IRGRP
: 0;
123 mode
|= (sec_access
.mask
& (GENERIC_WRITE_ACCESS
|FILE_SPECIFIC_WRITE_BITS
)) ? S_IWGRP
: 0;
124 mode
|= (sec_access
.mask
& (GENERIC_EXECUTE_ACCESS
|FILE_SPECIFIC_EXECUTE_BITS
)) ? S_IXGRP
: 0;
128 if(sec_access
.mask
& GENERIC_ALL_ACCESS
)
129 mode
= S_IROTH
|S_IWOTH
|S_IXOTH
;
131 mode
|= (sec_access
.mask
& (GENERIC_READ_ACCESS
|FILE_SPECIFIC_READ_BITS
)) ? S_IROTH
: 0;
132 mode
|= (sec_access
.mask
& (GENERIC_WRITE_ACCESS
|FILE_SPECIFIC_WRITE_BITS
)) ? S_IWOTH
: 0;
133 mode
|= (sec_access
.mask
& (GENERIC_EXECUTE_ACCESS
|FILE_SPECIFIC_EXECUTE_BITS
)) ? S_IXOTH
: 0;
141 /****************************************************************************
142 Unpack a SEC_DESC into a owner, group and set of UNIX permissions.
143 ****************************************************************************/
145 static BOOL
unpack_nt_permissions(SMB_STRUCT_STAT
*psbuf
, uid_t
*puser
, gid_t
*pgrp
, mode_t
*pmode
,
146 uint32 security_info_sent
, SEC_DESC
*psd
, BOOL is_directory
)
148 extern DOM_SID global_sid_World
;
151 DOM_SID file_owner_sid
;
152 DOM_SID file_grp_sid
;
155 SEC_ACL
*dacl
= psd
->dacl
;
156 BOOL all_aces_are_inherit_only
= (is_directory
? True
: False
);
163 if(security_info_sent
== 0) {
164 DEBUG(0,("unpack_nt_permissions: no security info sent !\n"));
169 * Windows 2000 sends the owner and group SIDs as the logged in
170 * user, not the connected user. But it still sends the file
171 * owner SIDs on an ACL set. So we need to check for the file
172 * owner and group SIDs as well as the owner SIDs. JRA.
175 create_file_sids(psbuf
, &file_owner_sid
, &file_grp_sid
);
178 * Validate the owner and group SID's.
181 memset(&owner_sid
, '\0', sizeof(owner_sid
));
182 memset(&grp_sid
, '\0', sizeof(grp_sid
));
184 DEBUG(5,("unpack_nt_permissions: validating owner_sid.\n"));
187 * Don't immediately fail if the owner sid cannot be validated.
188 * This may be a group chown only set.
191 if(!validate_unix_sid( &owner_sid
, &owner_rid
, psd
->owner_sid
))
192 DEBUG(3,("unpack_nt_permissions: unable to validate owner sid.\n"));
193 else if(security_info_sent
& OWNER_SECURITY_INFORMATION
)
194 *puser
= pdb_user_rid_to_uid(owner_rid
);
197 * Don't immediately fail if the group sid cannot be validated.
198 * This may be an owner chown only set.
201 if(!validate_unix_sid( &grp_sid
, &grp_rid
, psd
->grp_sid
))
202 DEBUG(3,("unpack_nt_permissions: unable to validate group sid.\n"));
203 else if(security_info_sent
& GROUP_SECURITY_INFORMATION
)
204 *pgrp
= pdb_user_rid_to_gid(grp_rid
);
207 * If no DACL then this is a chown only security descriptor.
210 if(!(security_info_sent
& DACL_SECURITY_INFORMATION
) || !dacl
) {
216 * Now go through the DACL and ensure that
217 * any owner/group sids match.
220 for(i
= 0; i
< dacl
->num_aces
; i
++) {
222 SEC_ACE
*psa
= &dacl
->ace
[i
];
224 if((psa
->type
!= SEC_ACE_TYPE_ACCESS_ALLOWED
) &&
225 (psa
->type
!= SEC_ACE_TYPE_ACCESS_DENIED
)) {
226 DEBUG(3,("unpack_nt_permissions: unable to set anything but an ALLOW or DENY ACE.\n"));
231 * Ignore or remove bits we don't care about on a directory ACE.
235 if(psa
->flags
& SEC_ACE_FLAG_INHERIT_ONLY
) {
236 DEBUG(3,("unpack_nt_permissions: ignoring inherit only ACE.\n"));
241 * At least one of the ACE entries wasn't inherit only.
242 * Flag this so we know the returned mode is valid.
245 all_aces_are_inherit_only
= False
;
249 * Windows 2000 sets these flags even on *file* ACE's. This is wrong
250 * but we can ignore them for now. Revisit this when we go to POSIX
251 * ACLs on directories.
254 psa
->flags
&= ~(SEC_ACE_FLAG_OBJECT_INHERIT
|SEC_ACE_FLAG_CONTAINER_INHERIT
);
256 if(psa
->flags
!= 0) {
257 DEBUG(1,("unpack_nt_permissions: unable to set ACE flags (%x).\n",
258 (unsigned int)psa
->flags
));
263 * The security mask may be UNIX_ACCESS_NONE which should map into
264 * no permissions (we overload the WRITE_OWNER bit for this) or it
265 * should be one of the ALL/EXECUTE/READ/WRITE bits. Arrange for this
266 * to be so. Any other bits override the UNIX_ACCESS_NONE bit.
269 psa
->info
.mask
&= (GENERIC_ALL_ACCESS
|GENERIC_EXECUTE_ACCESS
|GENERIC_WRITE_ACCESS
|
270 GENERIC_READ_ACCESS
|UNIX_ACCESS_NONE
|FILE_ALL_ATTRIBUTES
);
272 if(psa
->info
.mask
!= UNIX_ACCESS_NONE
)
273 psa
->info
.mask
&= ~UNIX_ACCESS_NONE
;
275 sid_copy(&ace_sid
, &psa
->sid
);
277 if(sid_equal(&ace_sid
, &file_owner_sid
)) {
279 * Map the desired permissions into owner perms.
282 if(psa
->type
== SEC_ACE_TYPE_ACCESS_ALLOWED
)
283 *pmode
|= map_nt_perms( psa
->info
, S_IRUSR
);
285 *pmode
&= ~(map_nt_perms( psa
->info
, S_IRUSR
));
287 } else if( sid_equal(&ace_sid
, &file_grp_sid
)) {
289 * Map the desired permissions into group perms.
292 if(psa
->type
== SEC_ACE_TYPE_ACCESS_ALLOWED
)
293 *pmode
|= map_nt_perms( psa
->info
, S_IRGRP
);
295 *pmode
&= ~(map_nt_perms( psa
->info
, S_IRGRP
));
297 } else if( sid_equal(&ace_sid
, &global_sid_World
)) {
299 * Map the desired permissions into other perms.
302 if(psa
->type
== SEC_ACE_TYPE_ACCESS_ALLOWED
)
303 *pmode
|= map_nt_perms( psa
->info
, S_IROTH
);
305 *pmode
&= ~(map_nt_perms( psa
->info
, S_IROTH
));
308 DEBUG(0,("unpack_nt_permissions: unknown SID used in ACL.\n"));
313 if (is_directory
&& all_aces_are_inherit_only
) {
315 * Windows 2000 is doing one of these weird 'inherit acl'
316 * traverses to conserve NTFS ACL resources. Just pretend
317 * there was no DACL sent. JRA.
320 DEBUG(10,("unpack_nt_permissions: Win2k inherit acl traverse. Ignoring DACL.\n"));
321 free_sec_acl(&psd
->dacl
);
327 /****************************************************************************
328 Reply to query a security descriptor from an fsp. If it succeeds it allocates
329 the space for the return elements and returns the size needed to return the
330 security descriptor. This should be the only external function needed for
331 the UNIX style get ACL.
332 ****************************************************************************/
334 size_t get_nt_acl(files_struct
*fsp
, SEC_DESC
**ppdesc
)
336 extern DOM_SID global_sid_World
;
337 SMB_STRUCT_STAT sbuf
;
343 SEC_ACCESS owner_access
;
345 SEC_ACCESS group_access
;
347 SEC_ACCESS other_access
;
353 if(!lp_nt_acl_support()) {
354 sid_copy( &owner_sid
, &global_sid_World
);
355 sid_copy( &group_sid
, &global_sid_World
);
358 if(fsp
->is_directory
|| fsp
->fd
== -1) {
359 if(dos_stat(fsp
->fsp_name
, &sbuf
) != 0) {
363 if(fsp
->conn
->vfs_ops
.fstat(fsp
->fd
,&sbuf
) != 0) {
369 * Get the owner, group and world SIDs.
372 create_file_sids(&sbuf
, &owner_sid
, &group_sid
);
375 * Create the generic 3 element UNIX acl.
378 owner_access
= map_unix_perms(&owner_acl_type
, sbuf
.st_mode
,
379 S_IRUSR
, S_IWUSR
, S_IXUSR
, fsp
->is_directory
);
380 group_access
= map_unix_perms(&grp_acl_type
, sbuf
.st_mode
,
381 S_IRGRP
, S_IWGRP
, S_IXGRP
, fsp
->is_directory
);
382 other_access
= map_unix_perms(&other_acl_type
, sbuf
.st_mode
,
383 S_IROTH
, S_IWOTH
, S_IXOTH
, fsp
->is_directory
);
385 if(owner_access
.mask
)
386 init_sec_ace(&ace_list
[num_acls
++], &owner_sid
, owner_acl_type
,
389 if(group_access
.mask
)
390 init_sec_ace(&ace_list
[num_acls
++], &group_sid
, grp_acl_type
,
393 if(other_access
.mask
)
394 init_sec_ace(&ace_list
[num_acls
++], &global_sid_World
, other_acl_type
,
397 if(fsp
->is_directory
) {
399 * For directory ACLs we also add in the inherited permissions
400 * ACE entries. These are the permissions a file would get when
401 * being created in the directory.
403 mode_t mode
= unix_mode( fsp
->conn
, FILE_ATTRIBUTE_ARCHIVE
, fsp
->fsp_name
);
405 owner_access
= map_unix_perms(&owner_acl_type
, mode
,
406 S_IRUSR
, S_IWUSR
, S_IXUSR
, fsp
->is_directory
);
407 group_access
= map_unix_perms(&grp_acl_type
, mode
,
408 S_IRGRP
, S_IWGRP
, S_IXGRP
, fsp
->is_directory
);
409 other_access
= map_unix_perms(&other_acl_type
, mode
,
410 S_IROTH
, S_IWOTH
, S_IXOTH
, fsp
->is_directory
);
412 if(owner_access
.mask
)
413 init_sec_ace(&ace_list
[num_acls
++], &owner_sid
, owner_acl_type
,
414 owner_access
, SEC_ACE_FLAG_OBJECT_INHERIT
|SEC_ACE_FLAG_INHERIT_ONLY
);
416 if(group_access
.mask
)
417 init_sec_ace(&ace_list
[num_acls
++], &group_sid
, grp_acl_type
,
418 group_access
, SEC_ACE_FLAG_OBJECT_INHERIT
|SEC_ACE_FLAG_INHERIT_ONLY
);
420 if(other_access
.mask
)
421 init_sec_ace(&ace_list
[num_acls
++], &global_sid_World
, other_acl_type
,
422 other_access
, SEC_ACE_FLAG_OBJECT_INHERIT
|SEC_ACE_FLAG_INHERIT_ONLY
);
426 if((psa
= make_sec_acl( ACL_REVISION
, num_acls
, ace_list
)) == NULL
) {
427 DEBUG(0,("get_nt_acl: Unable to malloc space for acl.\n"));
432 *ppdesc
= make_standard_sec_desc( &owner_sid
, &group_sid
, psa
, &sd_size
);
435 DEBUG(0,("get_nt_acl: Unable to malloc space for security descriptor.\n"));
444 /****************************************************************************
445 Reply to set a security descriptor on an fsp. security_info_sent is the
446 description of the following NT ACL.
447 This should be the only external function needed for the UNIX style set ACL.
448 ****************************************************************************/
450 BOOL
set_nt_acl(files_struct
*fsp
, uint32 security_info_sent
, SEC_DESC
*psd
)
452 connection_struct
*conn
= fsp
->conn
;
453 uid_t user
= (uid_t
)-1;
454 gid_t grp
= (gid_t
)-1;
456 SMB_STRUCT_STAT sbuf
;
457 BOOL got_dacl
= False
;
460 * Get the current state of the file.
463 if(fsp
->is_directory
) {
464 if(dos_stat(fsp
->fsp_name
, &sbuf
) != 0)
471 ret
= conn
->vfs_ops
.stat(dos_to_unix(fsp
->fsp_name
,False
), &sbuf
);
473 ret
= conn
->vfs_ops
.fstat(fsp
->fd
,&sbuf
);
480 * Unpack the user/group/world id's and permissions.
483 if (!unpack_nt_permissions( &sbuf
, &user
, &grp
, &perms
, security_info_sent
, psd
, fsp
->is_directory
))
486 if (psd
->dacl
!= NULL
)
490 * Do we need to chown ?
493 if((user
!= (uid_t
)-1 || grp
!= (uid_t
)-1) && (sbuf
.st_uid
!= user
|| sbuf
.st_gid
!= grp
)) {
495 DEBUG(3,("call_nt_transact_set_security_desc: chown %s. uid = %u, gid = %u.\n",
496 fsp
->fsp_name
, (unsigned int)user
, (unsigned int)grp
));
498 if(dos_chown( fsp
->fsp_name
, user
, grp
) == -1) {
499 DEBUG(3,("call_nt_transact_set_security_desc: chown %s, %u, %u failed. Error = %s.\n",
500 fsp
->fsp_name
, (unsigned int)user
, (unsigned int)grp
, strerror(errno
) ));
505 * Recheck the current state of the file, which may have changed.
506 * (suid/sgid bits, for instance)
509 if(fsp
->is_directory
) {
510 if(dos_stat(fsp
->fsp_name
, &sbuf
) != 0) {
518 ret
= conn
->vfs_ops
.stat(dos_to_unix(fsp
->fsp_name
,False
), &sbuf
);
520 ret
= conn
->vfs_ops
.fstat(fsp
->fd
,&sbuf
);
528 * Only change security if we got a DACL.
531 if((security_info_sent
& DACL_SECURITY_INFORMATION
) && got_dacl
) {
534 * Check to see if we need to change anything.
535 * Enforce limits on modified bits *only*. Don't enforce masks
536 * on bits not changed by the user.
539 if(fsp
->is_directory
) {
541 perms
&= (lp_dir_security_mask(SNUM(conn
)) | sbuf
.st_mode
);
542 perms
|= (lp_force_dir_security_mode(SNUM(conn
)) & ( perms
^ sbuf
.st_mode
));
546 perms
&= (lp_security_mask(SNUM(conn
)) | sbuf
.st_mode
);
547 perms
|= (lp_force_security_mode(SNUM(conn
)) & ( perms
^ sbuf
.st_mode
));
552 * Preserve special bits.
555 perms
|= (sbuf
.st_mode
& ~0777);
558 * Do we need to chmod ?
561 if(sbuf
.st_mode
!= perms
) {
563 DEBUG(3,("call_nt_transact_set_security_desc: chmod %s. perms = 0%o.\n",
564 fsp
->fsp_name
, (unsigned int)perms
));
566 if(conn
->vfs_ops
.chmod(dos_to_unix(fsp
->fsp_name
, False
), perms
) == -1) {
567 DEBUG(3,("call_nt_transact_set_security_desc: chmod %s, 0%o failed. Error = %s.\n",
568 fsp
->fsp_name
, (unsigned int)perms
, strerror(errno
) ));