First cut toward adding WINS server failover.
[Samba/reqa.git] / source3 / smbd / unix_acls.c
blob7a2dc6ab6ec9b068da157e250f03cc45a973a4ce
1 #define OLD_NTDOMAIN 1
2 /*
3 Unix SMB/Netbios implementation.
4 Version 1.9.
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.
23 #include "includes.h"
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 /****************************************************************************
40 Map unix perms to NT.
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)
45 SEC_ACCESS sa;
46 uint32 nt_mask = 0;
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;
54 } else {
55 nt_mask |= (perm & r_mask) ? UNIX_ACCESS_R : 0;
56 if(is_directory)
57 nt_mask |= (perm & w_mask) ? UNIX_ACCESS_W : 0;
58 else
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);
63 return sa;
66 /****************************************************************************
67 Validate a SID.
68 ****************************************************************************/
70 static BOOL validate_unix_sid( DOM_SID *psid, uint32 *prid, DOM_SID *sd_sid)
72 extern DOM_SID global_sam_sid;
73 DOM_SID sid;
75 if(!sd_sid) {
76 DEBUG(5,("validate_unix_sid: sid missing.\n"));
77 return False;
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"));
85 return False;
88 if(!sid_equal( &sid, &global_sam_sid)) {
89 DEBUG(5,("validate_unix_sid: sid is not ours.\n"));
90 return False;
93 return True;
96 /****************************************************************************
97 Map NT perms to UNIX.
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)
106 mode_t mode = 0;
108 switch(type) {
109 case S_IRUSR:
110 if(sec_access.mask & GENERIC_ALL_ACCESS)
111 mode = S_IRUSR|S_IWUSR|S_IXUSR;
112 else {
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;
117 break;
118 case S_IRGRP:
119 if(sec_access.mask & GENERIC_ALL_ACCESS)
120 mode = S_IRGRP|S_IWGRP|S_IXGRP;
121 else {
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;
126 break;
127 case S_IROTH:
128 if(sec_access.mask & GENERIC_ALL_ACCESS)
129 mode = S_IROTH|S_IWOTH|S_IXOTH;
130 else {
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;
135 break;
138 return mode;
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;
149 DOM_SID owner_sid;
150 DOM_SID grp_sid;
151 DOM_SID file_owner_sid;
152 DOM_SID file_grp_sid;
153 uint32 owner_rid;
154 uint32 grp_rid;
155 SEC_ACL *dacl = psd->dacl;
156 BOOL all_aces_are_inherit_only = (is_directory ? True : False);
157 int i;
159 *pmode = 0;
160 *puser = (uid_t)-1;
161 *pgrp = (gid_t)-1;
163 if(security_info_sent == 0) {
164 DEBUG(0,("unpack_nt_permissions: no security info sent !\n"));
165 return False;
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) {
211 *pmode = 0;
212 return True;
216 * Now go through the DACL and ensure that
217 * any owner/group sids match.
220 for(i = 0; i < dacl->num_aces; i++) {
221 DOM_SID ace_sid;
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"));
227 return False;
231 * Ignore or remove bits we don't care about on a directory ACE.
234 if(is_directory) {
235 if(psa->flags & SEC_ACE_FLAG_INHERIT_ONLY) {
236 DEBUG(3,("unpack_nt_permissions: ignoring inherit only ACE.\n"));
237 continue;
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));
259 return False;
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);
284 else
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);
294 else
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);
304 else
305 *pmode &= ~(map_nt_perms( psa->info, S_IROTH));
307 } else {
308 DEBUG(0,("unpack_nt_permissions: unknown SID used in ACL.\n"));
309 return False;
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);
324 return True;
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;
338 SEC_ACE ace_list[6];
339 DOM_SID owner_sid;
340 DOM_SID group_sid;
341 size_t sd_size;
342 SEC_ACL *psa = NULL;
343 SEC_ACCESS owner_access;
344 int owner_acl_type;
345 SEC_ACCESS group_access;
346 int grp_acl_type;
347 SEC_ACCESS other_access;
348 int other_acl_type;
349 int num_acls = 0;
351 *ppdesc = NULL;
353 if(!lp_nt_acl_support()) {
354 sid_copy( &owner_sid, &global_sid_World);
355 sid_copy( &group_sid, &global_sid_World);
356 } else {
358 if(fsp->is_directory || fsp->fd == -1) {
359 if(dos_stat(fsp->fsp_name, &sbuf) != 0) {
360 return 0;
362 } else {
363 if(fsp->conn->vfs_ops.fstat(fsp->fd,&sbuf) != 0) {
364 return 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,
387 owner_access, 0);
389 if(group_access.mask)
390 init_sec_ace(&ace_list[num_acls++], &group_sid, grp_acl_type,
391 group_access, 0);
393 if(other_access.mask)
394 init_sec_ace(&ace_list[num_acls++], &global_sid_World, other_acl_type,
395 other_access, 0);
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);
425 if(num_acls)
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"));
428 return 0;
432 *ppdesc = make_standard_sec_desc( &owner_sid, &group_sid, psa, &sd_size);
434 if(!*ppdesc) {
435 DEBUG(0,("get_nt_acl: Unable to malloc space for security descriptor.\n"));
436 sd_size = 0;
439 free_sec_acl(&psa);
441 return sd_size;
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;
455 mode_t perms = 0;
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)
465 return False;
466 } else {
468 int ret;
470 if(fsp->fd == -1)
471 ret = conn->vfs_ops.stat(dos_to_unix(fsp->fsp_name,False), &sbuf);
472 else
473 ret = conn->vfs_ops.fstat(fsp->fd,&sbuf);
475 if(ret != 0)
476 return False;
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))
484 return False;
486 if (psd->dacl != NULL)
487 got_dacl = True;
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) ));
501 return False;
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) {
511 return False;
513 } else {
515 int ret;
517 if(fsp->fd == -1)
518 ret = conn->vfs_ops.stat(dos_to_unix(fsp->fsp_name,False), &sbuf);
519 else
520 ret = conn->vfs_ops.fstat(fsp->fd,&sbuf);
522 if(ret != 0)
523 return False;
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 ));
544 } else {
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) ));
569 return False;
574 return True;
576 #undef OLD_NTDOMAIN