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 if (!sid_to_uid( &owner_sid, puser, &sid_type))
190 DEBUG(3,("unpack_nt_permissions: unable to validate owner sid.\n"));
194 * Don't immediately fail if the group sid cannot be validated.
195 * This may be an owner chown only set.
198 if (security_info_sent & GROUP_SECURITY_INFORMATION) {
199 if (!sid_to_gid( &grp_sid, pgrp, &sid_type))
200 DEBUG(3,("unpack_nt_permissions: unable to validate group sid.\n"));
204 * If no DACL then this is a chown only security descriptor.
207 if(!(security_info_sent & DACL_SECURITY_INFORMATION) || !dacl) {
213 * Now go through the DACL and ensure that
214 * any owner/group sids match.
217 for(i = 0; i < dacl->num_aces; i++) {
219 SEC_ACE *psa = &dacl->ace[i];
221 if((psa->type != SEC_ACE_TYPE_ACCESS_ALLOWED) &&
222 (psa->type != SEC_ACE_TYPE_ACCESS_DENIED)) {
223 DEBUG(3,("unpack_nt_permissions: unable to set anything but an ALLOW or DENY ACE.\n"));
228 * Ignore or remove bits we don't care about on a directory ACE.
232 if(psa->flags & SEC_ACE_FLAG_INHERIT_ONLY) {
233 DEBUG(3,("unpack_nt_permissions: ignoring inherit only ACE.\n"));
238 * At least one of the ACE entries wasn't inherit only.
239 * Flag this so we know the returned mode is valid.
242 all_aces_are_inherit_only = False;
246 * Windows 2000 sets these flags even on *file* ACE's. This is wrong
247 * but we can ignore them for now. Revisit this when we go to POSIX
248 * ACLs on directories.
251 psa->flags &= ~(SEC_ACE_FLAG_OBJECT_INHERIT|SEC_ACE_FLAG_CONTAINER_INHERIT);
253 if(psa->flags != 0) {
254 DEBUG(1,("unpack_nt_permissions: unable to set ACE flags (%x).\n",
255 (unsigned int)psa->flags));
260 * The security mask may be UNIX_ACCESS_NONE which should map into
261 * no permissions (we overload the WRITE_OWNER bit for this) or it
262 * should be one of the ALL/EXECUTE/READ/WRITE bits. Arrange for this
263 * to be so. Any other bits override the UNIX_ACCESS_NONE bit.
266 psa->info.mask &= (GENERIC_ALL_ACCESS|GENERIC_EXECUTE_ACCESS|GENERIC_WRITE_ACCESS|
267 GENERIC_READ_ACCESS|UNIX_ACCESS_NONE|FILE_ALL_ATTRIBUTES);
269 if(psa->info.mask != UNIX_ACCESS_NONE)
270 psa->info.mask &= ~UNIX_ACCESS_NONE;
272 sid_copy(&ace_sid, &psa->sid);
274 if(sid_equal(&ace_sid, &file_owner_sid)) {
276 * Map the desired permissions into owner perms.
279 if(psa->type == SEC_ACE_TYPE_ACCESS_ALLOWED)
280 *pmode |= map_nt_perms( psa->info, S_IRUSR);
282 *pmode &= ~(map_nt_perms( psa->info, S_IRUSR));
284 } else if( sid_equal(&ace_sid, &file_grp_sid)) {
286 * Map the desired permissions into group perms.
289 if(psa->type == SEC_ACE_TYPE_ACCESS_ALLOWED)
290 *pmode |= map_nt_perms( psa->info, S_IRGRP);
292 *pmode &= ~(map_nt_perms( psa->info, S_IRGRP));
294 } else if( sid_equal(&ace_sid, &global_sid_World)) {
296 * Map the desired permissions into other perms.
299 if(psa->type == SEC_ACE_TYPE_ACCESS_ALLOWED)
300 *pmode |= map_nt_perms( psa->info, S_IROTH);
302 *pmode &= ~(map_nt_perms( psa->info, S_IROTH));
305 DEBUG(0,("unpack_nt_permissions: unknown SID used in ACL.\n"));
310 if (is_directory && all_aces_are_inherit_only) {
312 * Windows 2000 is doing one of these weird 'inherit acl'
313 * traverses to conserve NTFS ACL resources. Just pretend
314 * there was no DACL sent. JRA.
317 DEBUG(10,("unpack_nt_permissions: Win2k inherit acl traverse. Ignoring DACL.\n"));
318 free_sec_acl(&psd->dacl);
324 /****************************************************************************
325 Reply to query a security descriptor from an fsp. If it succeeds it allocates
326 the space for the return elements and returns the size needed to return the
327 security descriptor. This should be the only external function needed for
328 the UNIX style get ACL.
329 ****************************************************************************/
331 size_t get_nt_acl(files_struct *fsp, SEC_DESC **ppdesc)
333 extern DOM_SID global_sid_World;
334 SMB_STRUCT_STAT sbuf;
340 SEC_ACCESS owner_access;
342 SEC_ACCESS group_access;
344 SEC_ACCESS other_access;
350 if(!lp_nt_acl_support()) {
351 sid_copy( &owner_sid, &global_sid_World);
352 sid_copy( &group_sid, &global_sid_World);
355 if(fsp->is_directory || fsp->fd == -1) {
356 if(vfs_stat(fsp->conn,fsp->fsp_name, &sbuf) != 0) {
360 if(fsp->conn->vfs_ops.fstat(fsp,fsp->fd,&sbuf) != 0) {
366 * Get the owner, group and world SIDs.
369 create_file_sids(&sbuf, &owner_sid, &group_sid);
372 * Create the generic 3 element UNIX acl.
375 owner_access = map_unix_perms(&owner_acl_type, sbuf.st_mode,
376 S_IRUSR, S_IWUSR, S_IXUSR, fsp->is_directory);
377 group_access = map_unix_perms(&grp_acl_type, sbuf.st_mode,
378 S_IRGRP, S_IWGRP, S_IXGRP, fsp->is_directory);
379 other_access = map_unix_perms(&other_acl_type, sbuf.st_mode,
380 S_IROTH, S_IWOTH, S_IXOTH, fsp->is_directory);
382 if(owner_access.mask)
383 init_sec_ace(&ace_list[num_acls++], &owner_sid, owner_acl_type,
386 if(group_access.mask)
387 init_sec_ace(&ace_list[num_acls++], &group_sid, grp_acl_type,
390 if(other_access.mask)
391 init_sec_ace(&ace_list[num_acls++], &global_sid_World, other_acl_type,
394 if(fsp->is_directory) {
396 * For directory ACLs we also add in the inherited permissions
397 * ACE entries. These are the permissions a file would get when
398 * being created in the directory.
400 mode_t mode = unix_mode( fsp->conn, FILE_ATTRIBUTE_ARCHIVE, fsp->fsp_name);
402 owner_access = map_unix_perms(&owner_acl_type, mode,
403 S_IRUSR, S_IWUSR, S_IXUSR, fsp->is_directory);
404 group_access = map_unix_perms(&grp_acl_type, mode,
405 S_IRGRP, S_IWGRP, S_IXGRP, fsp->is_directory);
406 other_access = map_unix_perms(&other_acl_type, mode,
407 S_IROTH, S_IWOTH, S_IXOTH, fsp->is_directory);
409 if(owner_access.mask)
410 init_sec_ace(&ace_list[num_acls++], &owner_sid, owner_acl_type,
411 owner_access, SEC_ACE_FLAG_OBJECT_INHERIT|SEC_ACE_FLAG_INHERIT_ONLY);
413 if(group_access.mask)
414 init_sec_ace(&ace_list[num_acls++], &group_sid, grp_acl_type,
415 group_access, SEC_ACE_FLAG_OBJECT_INHERIT|SEC_ACE_FLAG_INHERIT_ONLY);
417 if(other_access.mask)
418 init_sec_ace(&ace_list[num_acls++], &global_sid_World, other_acl_type,
419 other_access, SEC_ACE_FLAG_OBJECT_INHERIT|SEC_ACE_FLAG_INHERIT_ONLY);
423 if((psa = make_sec_acl( ACL_REVISION, num_acls, ace_list)) == NULL) {
424 DEBUG(0,("get_nt_acl: Unable to malloc space for acl.\n"));
429 *ppdesc = make_standard_sec_desc( &owner_sid, &group_sid, psa, &sd_size);
432 DEBUG(0,("get_nt_acl: Unable to malloc space for security descriptor.\n"));
441 /****************************************************************************
442 Reply to set a security descriptor on an fsp. security_info_sent is the
443 description of the following NT ACL.
444 This should be the only external function needed for the UNIX style set ACL.
445 ****************************************************************************/
447 BOOL set_nt_acl(files_struct *fsp, uint32 security_info_sent, SEC_DESC *psd)
449 connection_struct *conn = fsp->conn;
450 uid_t user = (uid_t)-1;
451 gid_t grp = (gid_t)-1;
453 SMB_STRUCT_STAT sbuf;
454 BOOL got_dacl = False;
457 * Get the current state of the file.
460 if(fsp->is_directory || fsp->fd == -1) {
461 if(vfs_stat(fsp->conn,fsp->fsp_name, &sbuf) != 0)
464 if(conn->vfs_ops.fstat(fsp,fsp->fd,&sbuf) != 0)
469 * Unpack the user/group/world id's and permissions.
472 if (!unpack_nt_permissions( &sbuf, &user, &grp, &perms, security_info_sent, psd, fsp->is_directory))
475 if (psd->dacl != NULL)
479 * Do we need to chown ?
482 if((user != (uid_t)-1 || grp != (uid_t)-1) && (sbuf.st_uid != user || sbuf.st_gid != grp)) {
484 DEBUG(3,("call_nt_transact_set_security_desc: chown %s. uid = %u, gid = %u.\n",
485 fsp->fsp_name, (unsigned int)user, (unsigned int)grp ));
487 if(vfs_chown( fsp->conn, fsp->fsp_name, user, grp) == -1) {
488 DEBUG(3,("call_nt_transact_set_security_desc: chown %s, %u, %u failed. Error = %s.\n",
489 fsp->fsp_name, (unsigned int)user, (unsigned int)grp, strerror(errno) ));
494 * Recheck the current state of the file, which may have changed.
495 * (suid/sgid bits, for instance)
498 if(fsp->is_directory) {
499 if(vfs_stat(fsp->conn, fsp->fsp_name, &sbuf) != 0) {
507 ret = vfs_stat(fsp->conn, fsp->fsp_name, &sbuf);
509 ret = conn->vfs_ops.fstat(fsp,fsp->fd,&sbuf);
517 * Only change security if we got a DACL.
520 if((security_info_sent & DACL_SECURITY_INFORMATION) && got_dacl) {
523 * Check to see if we need to change anything.
524 * Enforce limits on modified bits *only*. Don't enforce masks
525 * on bits not changed by the user.
528 if(fsp->is_directory) {
530 perms &= (lp_dir_security_mask(SNUM(conn)) | sbuf.st_mode);
531 perms |= (lp_force_dir_security_mode(SNUM(conn)) & ( perms ^ sbuf.st_mode ));
535 perms &= (lp_security_mask(SNUM(conn)) | sbuf.st_mode);
536 perms |= (lp_force_security_mode(SNUM(conn)) & ( perms ^ sbuf.st_mode ));
541 * Preserve special bits.
544 perms |= (sbuf.st_mode & ~0777);
547 * Do we need to chmod ?
550 if(sbuf.st_mode != perms) {
552 DEBUG(3,("call_nt_transact_set_security_desc: chmod %s. perms = 0%o.\n",
553 fsp->fsp_name, (unsigned int)perms ));
555 if(conn->vfs_ops.chmod(conn,dos_to_unix(fsp->fsp_name, False), perms) == -1) {
556 DEBUG(3,("call_nt_transact_set_security_desc: chmod %s, 0%o failed. Error = %s.\n",
557 fsp->fsp_name, (unsigned int)perms, strerror(errno) ));