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 uid_to_sid( powner_sid, psbuf->st_uid );
32 gid_to_sid( pgroup_sid, psbuf->st_gid );
35 /****************************************************************************
37 ****************************************************************************/
39 static SEC_ACCESS map_unix_perms( int *pacl_type, mode_t perm, int r_mask, int w_mask, int x_mask, BOOL is_directory)
44 *pacl_type = SEC_ACE_TYPE_ACCESS_ALLOWED;
46 if((perm & (r_mask|w_mask|x_mask)) == (r_mask|w_mask|x_mask)) {
47 nt_mask = UNIX_ACCESS_RWX;
48 } else if((perm & (r_mask|w_mask|x_mask)) == 0) {
49 nt_mask = UNIX_ACCESS_NONE;
51 nt_mask |= (perm & r_mask) ? UNIX_ACCESS_R : 0;
53 nt_mask |= (perm & w_mask) ? UNIX_ACCESS_W : 0;
55 nt_mask |= (perm & w_mask) ? UNIX_ACCESS_W : 0;
56 nt_mask |= (perm & x_mask) ? UNIX_ACCESS_X : 0;
58 init_sec_access(&sa,nt_mask);
63 /****************************************************************************
65 ****************************************************************************/
67 static BOOL validate_unix_sid( DOM_SID *psid, uint32 *prid, DOM_SID *sd_sid)
69 extern DOM_SID global_sam_sid;
73 DEBUG(5,("validate_unix_sid: sid missing.\n"));
77 sid_copy(psid, sd_sid);
78 sid_copy(&sid, sd_sid);
80 if(!sid_split_rid(&sid, prid)) {
81 DEBUG(5,("validate_unix_sid: cannot get RID from sid.\n"));
85 if(!sid_equal( &sid, &global_sam_sid)) {
86 DEBUG(5,("validate_unix_sid: sid is not ours.\n"));
94 /****************************************************************************
96 ****************************************************************************/
98 #define FILE_SPECIFIC_READ_BITS (FILE_READ_DATA|FILE_READ_EA|FILE_READ_ATTRIBUTES)
99 #define FILE_SPECIFIC_WRITE_BITS (FILE_WRITE_DATA|FILE_APPEND_DATA|FILE_WRITE_EA|FILE_WRITE_ATTRIBUTES)
100 #define FILE_SPECIFIC_EXECUTE_BITS (FILE_EXECUTE)
102 static mode_t map_nt_perms( SEC_ACCESS sec_access, int type)
108 if(sec_access.mask & GENERIC_ALL_ACCESS)
109 mode = S_IRUSR|S_IWUSR|S_IXUSR;
111 mode |= (sec_access.mask & (GENERIC_READ_ACCESS|FILE_SPECIFIC_READ_BITS)) ? S_IRUSR : 0;
112 mode |= (sec_access.mask & (GENERIC_WRITE_ACCESS|FILE_SPECIFIC_WRITE_BITS)) ? S_IWUSR : 0;
113 mode |= (sec_access.mask & (GENERIC_EXECUTE_ACCESS|FILE_SPECIFIC_EXECUTE_BITS)) ? S_IXUSR : 0;
117 if(sec_access.mask & GENERIC_ALL_ACCESS)
118 mode = S_IRGRP|S_IWGRP|S_IXGRP;
120 mode |= (sec_access.mask & (GENERIC_READ_ACCESS|FILE_SPECIFIC_READ_BITS)) ? S_IRGRP : 0;
121 mode |= (sec_access.mask & (GENERIC_WRITE_ACCESS|FILE_SPECIFIC_WRITE_BITS)) ? S_IWGRP : 0;
122 mode |= (sec_access.mask & (GENERIC_EXECUTE_ACCESS|FILE_SPECIFIC_EXECUTE_BITS)) ? S_IXGRP : 0;
126 if(sec_access.mask & GENERIC_ALL_ACCESS)
127 mode = S_IROTH|S_IWOTH|S_IXOTH;
129 mode |= (sec_access.mask & (GENERIC_READ_ACCESS|FILE_SPECIFIC_READ_BITS)) ? S_IROTH : 0;
130 mode |= (sec_access.mask & (GENERIC_WRITE_ACCESS|FILE_SPECIFIC_WRITE_BITS)) ? S_IWOTH : 0;
131 mode |= (sec_access.mask & (GENERIC_EXECUTE_ACCESS|FILE_SPECIFIC_EXECUTE_BITS)) ? S_IXOTH : 0;
139 /****************************************************************************
140 Unpack a SEC_DESC into a owner, group and set of UNIX permissions.
141 ****************************************************************************/
143 static BOOL unpack_nt_permissions(SMB_STRUCT_STAT *psbuf, uid_t *puser, gid_t *pgrp, mode_t *pmode,
144 uint32 security_info_sent, SEC_DESC *psd, BOOL is_directory)
146 extern DOM_SID global_sid_World;
149 DOM_SID file_owner_sid;
150 DOM_SID file_grp_sid;
151 SEC_ACL *dacl = psd->dacl;
152 BOOL all_aces_are_inherit_only = (is_directory ? True : False);
154 enum SID_NAME_USE sid_type;
160 if(security_info_sent == 0) {
161 DEBUG(0,("unpack_nt_permissions: no security info sent !\n"));
166 * Windows 2000 sends the owner and group SIDs as the logged in
167 * user, not the connected user. But it still sends the file
168 * owner SIDs on an ACL set. So we need to check for the file
169 * owner and group SIDs as well as the owner SIDs. JRA.
172 create_file_sids(psbuf, &file_owner_sid, &file_grp_sid);
175 * Validate the owner and group SID's.
178 memset(&owner_sid, '\0', sizeof(owner_sid));
179 memset(&grp_sid, '\0', sizeof(grp_sid));
181 DEBUG(5,("unpack_nt_permissions: validating owner_sid.\n"));
184 * Don't immediately fail if the owner sid cannot be validated.
185 * This may be a group chown only set.
188 if (security_info_sent & OWNER_SECURITY_INFORMATION) {
189 sid_copy(&owner_sid, psd->owner_sid);
190 if (!sid_to_uid( &owner_sid, puser, &sid_type))
191 DEBUG(3,("unpack_nt_permissions: unable to validate owner sid.\n"));
195 * Don't immediately fail if the group sid cannot be validated.
196 * This may be an owner chown only set.
199 if (security_info_sent & GROUP_SECURITY_INFORMATION) {
200 sid_copy(&grp_sid, psd->grp_sid);
201 if (!sid_to_gid( &grp_sid, pgrp, &sid_type))
202 DEBUG(3,("unpack_nt_permissions: unable to validate group sid.\n"));
206 * If no DACL then this is a chown only security descriptor.
209 if(!(security_info_sent & DACL_SECURITY_INFORMATION) || !dacl) {
215 * Now go through the DACL and ensure that
216 * any owner/group sids match.
219 for(i = 0; i < dacl->num_aces; i++) {
221 SEC_ACE *psa = &dacl->ace[i];
223 if((psa->type != SEC_ACE_TYPE_ACCESS_ALLOWED) &&
224 (psa->type != SEC_ACE_TYPE_ACCESS_DENIED)) {
225 DEBUG(3,("unpack_nt_permissions: unable to set anything but an ALLOW or DENY ACE.\n"));
230 * Ignore or remove bits we don't care about on a directory ACE.
234 if(psa->flags & SEC_ACE_FLAG_INHERIT_ONLY) {
235 DEBUG(3,("unpack_nt_permissions: ignoring inherit only ACE.\n"));
240 * At least one of the ACE entries wasn't inherit only.
241 * Flag this so we know the returned mode is valid.
244 all_aces_are_inherit_only = False;
248 * Windows 2000 sets these flags even on *file* ACE's. This is wrong
249 * but we can ignore them for now. Revisit this when we go to POSIX
250 * ACLs on directories.
253 psa->flags &= ~(SEC_ACE_FLAG_OBJECT_INHERIT|SEC_ACE_FLAG_CONTAINER_INHERIT);
255 if(psa->flags != 0) {
256 DEBUG(1,("unpack_nt_permissions: unable to set ACE flags (%x).\n",
257 (unsigned int)psa->flags));
262 * The security mask may be UNIX_ACCESS_NONE which should map into
263 * no permissions (we overload the WRITE_OWNER bit for this) or it
264 * should be one of the ALL/EXECUTE/READ/WRITE bits. Arrange for this
265 * to be so. Any other bits override the UNIX_ACCESS_NONE bit.
268 psa->info.mask &= (GENERIC_ALL_ACCESS|GENERIC_EXECUTE_ACCESS|GENERIC_WRITE_ACCESS|
269 GENERIC_READ_ACCESS|UNIX_ACCESS_NONE|FILE_ALL_ATTRIBUTES);
271 if(psa->info.mask != UNIX_ACCESS_NONE)
272 psa->info.mask &= ~UNIX_ACCESS_NONE;
274 sid_copy(&ace_sid, &psa->sid);
276 if(sid_equal(&ace_sid, &file_owner_sid)) {
278 * Map the desired permissions into owner perms.
281 if(psa->type == SEC_ACE_TYPE_ACCESS_ALLOWED)
282 *pmode |= map_nt_perms( psa->info, S_IRUSR);
284 *pmode &= ~(map_nt_perms( psa->info, S_IRUSR));
286 } else if( sid_equal(&ace_sid, &file_grp_sid)) {
288 * Map the desired permissions into group perms.
291 if(psa->type == SEC_ACE_TYPE_ACCESS_ALLOWED)
292 *pmode |= map_nt_perms( psa->info, S_IRGRP);
294 *pmode &= ~(map_nt_perms( psa->info, S_IRGRP));
296 } else if( sid_equal(&ace_sid, &global_sid_World)) {
298 * Map the desired permissions into other perms.
301 if(psa->type == SEC_ACE_TYPE_ACCESS_ALLOWED)
302 *pmode |= map_nt_perms( psa->info, S_IROTH);
304 *pmode &= ~(map_nt_perms( psa->info, S_IROTH));
307 DEBUG(0,("unpack_nt_permissions: unknown SID used in ACL.\n"));
312 if (is_directory && all_aces_are_inherit_only) {
314 * Windows 2000 is doing one of these weird 'inherit acl'
315 * traverses to conserve NTFS ACL resources. Just pretend
316 * there was no DACL sent. JRA.
319 DEBUG(10,("unpack_nt_permissions: Win2k inherit acl traverse. Ignoring DACL.\n"));
320 free_sec_acl(&psd->dacl);
326 /****************************************************************************
327 Reply to query a security descriptor from an fsp. If it succeeds it allocates
328 the space for the return elements and returns the size needed to return the
329 security descriptor. This should be the only external function needed for
330 the UNIX style get ACL.
331 ****************************************************************************/
333 size_t get_nt_acl(files_struct *fsp, SEC_DESC **ppdesc)
335 extern DOM_SID global_sid_World;
336 SMB_STRUCT_STAT sbuf;
342 SEC_ACCESS owner_access;
344 SEC_ACCESS group_access;
346 SEC_ACCESS other_access;
352 if(!lp_nt_acl_support()) {
353 sid_copy( &owner_sid, &global_sid_World);
354 sid_copy( &group_sid, &global_sid_World);
357 if(fsp->is_directory || fsp->fd == -1) {
358 if(vfs_stat(fsp->conn,fsp->fsp_name, &sbuf) != 0) {
362 if(fsp->conn->vfs_ops.fstat(fsp,fsp->fd,&sbuf) != 0) {
368 * Get the owner, group and world SIDs.
371 create_file_sids(&sbuf, &owner_sid, &group_sid);
374 * Create the generic 3 element UNIX acl.
377 owner_access = map_unix_perms(&owner_acl_type, sbuf.st_mode,
378 S_IRUSR, S_IWUSR, S_IXUSR, fsp->is_directory);
379 group_access = map_unix_perms(&grp_acl_type, sbuf.st_mode,
380 S_IRGRP, S_IWGRP, S_IXGRP, fsp->is_directory);
381 other_access = map_unix_perms(&other_acl_type, sbuf.st_mode,
382 S_IROTH, S_IWOTH, S_IXOTH, fsp->is_directory);
384 if(owner_access.mask)
385 init_sec_ace(&ace_list[num_acls++], &owner_sid, owner_acl_type,
388 if(group_access.mask)
389 init_sec_ace(&ace_list[num_acls++], &group_sid, grp_acl_type,
392 if(other_access.mask)
393 init_sec_ace(&ace_list[num_acls++], &global_sid_World, other_acl_type,
396 if(fsp->is_directory) {
398 * For directory ACLs we also add in the inherited permissions
399 * ACE entries. These are the permissions a file would get when
400 * being created in the directory.
402 mode_t mode = unix_mode( fsp->conn, FILE_ATTRIBUTE_ARCHIVE, fsp->fsp_name);
404 owner_access = map_unix_perms(&owner_acl_type, mode,
405 S_IRUSR, S_IWUSR, S_IXUSR, fsp->is_directory);
406 group_access = map_unix_perms(&grp_acl_type, mode,
407 S_IRGRP, S_IWGRP, S_IXGRP, fsp->is_directory);
408 other_access = map_unix_perms(&other_acl_type, mode,
409 S_IROTH, S_IWOTH, S_IXOTH, fsp->is_directory);
411 if(owner_access.mask)
412 init_sec_ace(&ace_list[num_acls++], &owner_sid, owner_acl_type,
413 owner_access, SEC_ACE_FLAG_OBJECT_INHERIT|SEC_ACE_FLAG_INHERIT_ONLY);
415 if(group_access.mask)
416 init_sec_ace(&ace_list[num_acls++], &group_sid, grp_acl_type,
417 group_access, SEC_ACE_FLAG_OBJECT_INHERIT|SEC_ACE_FLAG_INHERIT_ONLY);
419 if(other_access.mask)
420 init_sec_ace(&ace_list[num_acls++], &global_sid_World, other_acl_type,
421 other_access, SEC_ACE_FLAG_OBJECT_INHERIT|SEC_ACE_FLAG_INHERIT_ONLY);
425 if((psa = make_sec_acl( ACL_REVISION, num_acls, ace_list)) == NULL) {
426 DEBUG(0,("get_nt_acl: Unable to malloc space for acl.\n"));
431 *ppdesc = make_standard_sec_desc( &owner_sid, &group_sid, psa, &sd_size);
434 DEBUG(0,("get_nt_acl: Unable to malloc space for security descriptor.\n"));
443 /****************************************************************************
444 Reply to set a security descriptor on an fsp. security_info_sent is the
445 description of the following NT ACL.
446 This should be the only external function needed for the UNIX style set ACL.
447 ****************************************************************************/
449 BOOL set_nt_acl(files_struct *fsp, uint32 security_info_sent, SEC_DESC *psd)
451 connection_struct *conn = fsp->conn;
452 uid_t user = (uid_t)-1;
453 gid_t grp = (gid_t)-1;
455 SMB_STRUCT_STAT sbuf;
456 BOOL got_dacl = False;
459 * Get the current state of the file.
462 if(fsp->is_directory || fsp->fd == -1) {
463 if(vfs_stat(fsp->conn,fsp->fsp_name, &sbuf) != 0)
466 if(conn->vfs_ops.fstat(fsp,fsp->fd,&sbuf) != 0)
471 * Unpack the user/group/world id's and permissions.
474 if (!unpack_nt_permissions( &sbuf, &user, &grp, &perms, security_info_sent, psd, fsp->is_directory))
477 if (psd->dacl != NULL)
481 * Do we need to chown ?
484 if((user != (uid_t)-1 || grp != (uid_t)-1) && (sbuf.st_uid != user || sbuf.st_gid != grp)) {
486 DEBUG(3,("call_nt_transact_set_security_desc: chown %s. uid = %u, gid = %u.\n",
487 fsp->fsp_name, (unsigned int)user, (unsigned int)grp ));
489 if(vfs_chown( fsp->conn, fsp->fsp_name, user, grp) == -1) {
490 DEBUG(3,("call_nt_transact_set_security_desc: chown %s, %u, %u failed. Error = %s.\n",
491 fsp->fsp_name, (unsigned int)user, (unsigned int)grp, strerror(errno) ));
496 * Recheck the current state of the file, which may have changed.
497 * (suid/sgid bits, for instance)
500 if(fsp->is_directory) {
501 if(vfs_stat(fsp->conn, fsp->fsp_name, &sbuf) != 0) {
509 ret = vfs_stat(fsp->conn, fsp->fsp_name, &sbuf);
511 ret = conn->vfs_ops.fstat(fsp,fsp->fd,&sbuf);
519 * Only change security if we got a DACL.
522 if((security_info_sent & DACL_SECURITY_INFORMATION) && got_dacl) {
525 * Check to see if we need to change anything.
526 * Enforce limits on modified bits *only*. Don't enforce masks
527 * on bits not changed by the user.
530 if(fsp->is_directory) {
532 perms &= (lp_dir_security_mask(SNUM(conn)) | sbuf.st_mode);
533 perms |= (lp_force_dir_security_mode(SNUM(conn)) & ( perms ^ sbuf.st_mode ));
537 perms &= (lp_security_mask(SNUM(conn)) | sbuf.st_mode);
538 perms |= (lp_force_security_mode(SNUM(conn)) & ( perms ^ sbuf.st_mode ));
543 * Preserve special bits.
546 perms |= (sbuf.st_mode & ~0777);
549 * Do we need to chmod ?
552 if(sbuf.st_mode != perms) {
554 DEBUG(3,("call_nt_transact_set_security_desc: chmod %s. perms = 0%o.\n",
555 fsp->fsp_name, (unsigned int)perms ));
557 if(conn->vfs_ops.chmod(conn,dos_to_unix(fsp->fsp_name, False), perms) == -1) {
558 DEBUG(3,("call_nt_transact_set_security_desc: chmod %s, 0%o failed. Error = %s.\n",
559 fsp->fsp_name, (unsigned int)perms, strerror(errno) ));