1 /* security.cc: NT file access control functions
3 Copyright 1997, 1998, 1999, 2000, 2001, 2002, 2003, 2004, 2005,
4 2006, 2007, 2008, 2009, 2010, 2011 Red Hat, Inc.
6 Originaly written by Gunther Ebert, gunther.ebert@ixos-leipzig.de
7 Completely rewritten by Corinna Vinschen <corinna@vinschen.de>
9 This file is part of Cygwin.
11 This software is a copyrighted work licensed under the terms of the
12 Cygwin license. Please consult the file "CYGWIN_LICENSE" for
30 #define ALL_SECURITY_INFORMATION (DACL_SECURITY_INFORMATION \
31 | GROUP_SECURITY_INFORMATION \
32 | OWNER_SECURITY_INFORMATION)
34 static NO_COPY GENERIC_MAPPING file_mapping = { FILE_GENERIC_READ,
40 get_file_sd (HANDLE fh, path_conv &pc, security_descriptor &sd,
44 OBJECT_ATTRIBUTES attr;
46 ULONG len = SD_MAXIMUM_SIZE, rlen;
48 /* Allocate space for the security descriptor. */
54 /* Try to fetch the security descriptor if the handle is valid. */
57 status = NtQuerySecurityObject (fh, ALL_SECURITY_INFORMATION,
59 if (!NT_SUCCESS (status))
61 debug_printf ("NtQuerySecurityObject (%S), status %p",
62 pc.get_nt_native_path (), status);
66 /* If the handle was NULL, or fetching with the original handle didn't work,
67 try to reopen the file with READ_CONTROL and fetch the security descriptor
71 status = NtOpenFile (&fh, READ_CONTROL,
72 pc.get_object_attr (attr, sec_none_nih), &io,
73 FILE_SHARE_VALID_FLAGS, FILE_OPEN_FOR_BACKUP_INTENT);
74 if (!NT_SUCCESS (status))
77 __seterrno_from_nt_status (status);
80 status = NtQuerySecurityObject (fh, ALL_SECURITY_INFORMATION,
83 if (!NT_SUCCESS (status))
86 __seterrno_from_nt_status (status);
90 /* Ok, so we have a security descriptor now. Unfortunately, if you want
91 to know if an ACE is inherited from the parent object, you can't just
92 call NtQuerySecurityObject once. The problem is this:
94 In the simple case, the SDs control word contains one of the
95 SE_DACL_AUTO_INHERITED or SE_DACL_PROTECTED flags, or at least one of
96 the ACEs has the INHERITED_ACE flag set. In all of these cases the
97 GetSecurityInfo function calls NtQuerySecurityObject only once, too,
98 apparently because it figures that the DACL is self-sufficient, which
99 it usually is. Windows Explorer, for instance, takes great care to
100 set these flags in a security descriptor if you change the ACL in the
103 The tricky case is if none of these flags is set in the SD. That means
104 the information whether or not an ACE has been inherited is not available
105 in the DACL of the object. In this case GetSecurityInfo also fetches the
106 SD from the parent directory and tests if the object's SD contains
107 inherited ACEs from the parent. The below code is closly emulating the
108 behaviour of GetSecurityInfo so we can get rid of this advapi32 dependency.
110 However, this functionality is slow, and the extra information is only
111 required when the file has been created and the permissions are about
112 to be set to POSIX permissions. Therefore we only use it in case the
113 file just got created.
115 Note that GetSecurityInfo has a problem on 5.1 and 5.2 kernels. Sometimes
116 it returns ERROR_INVALID_ADDRESS if a former request for the parent
117 directories' SD used NtQuerySecurityObject, rather than GetSecurityInfo
118 as well. See http://cygwin.com/ml/cygwin-developers/2011-03/msg00027.html
119 for the solution. This problem does not occur with the below code, so
120 the workaround has been removed. */
123 SECURITY_DESCRIPTOR_CONTROL ctrl;
127 ACCESS_ALLOWED_ACE *ace;
128 UNICODE_STRING dirname;
129 PSECURITY_DESCRIPTOR psd, nsd;
132 /* Check SDs control flags. If SE_DACL_AUTO_INHERITED or
133 SE_DACL_PROTECTED is set we're done. */
134 RtlGetControlSecurityDescriptor (sd, &ctrl, &dummy);
135 if (ctrl & (SE_DACL_AUTO_INHERITED | SE_DACL_PROTECTED))
137 /* Otherwise iterate over the ACEs and see if any one of them has the
138 INHERITED_ACE flag set. If so, we're done. */
139 if (NT_SUCCESS (RtlGetDaclSecurityDescriptor (sd, &exists, &dacl, &def))
141 for (ULONG idx = 0; idx < dacl->AceCount; ++idx)
142 if (NT_SUCCESS (RtlGetAce (dacl, idx, (PVOID *) &ace))
143 && (ace->Header.AceFlags & INHERITED_ACE))
145 /* Otherwise, open the parent directory with READ_CONTROL... */
146 RtlSplitUnicodePath (pc.get_nt_native_path (), &dirname, NULL);
147 InitializeObjectAttributes (&attr, &dirname, pc.objcaseinsensitive (),
149 status = NtOpenFile (&fh, READ_CONTROL, &attr, &io,
150 FILE_SHARE_VALID_FLAGS,
151 FILE_OPEN_FOR_BACKUP_INTENT
152 | FILE_OPEN_REPARSE_POINT);
153 if (!NT_SUCCESS (status))
155 debug_printf ("NtOpenFile (%S), status %p", &dirname, status);
158 /* ... fetch the parent's security descriptor ... */
159 psd = (PSECURITY_DESCRIPTOR) tp.w_get ();
160 status = NtQuerySecurityObject (fh, ALL_SECURITY_INFORMATION,
163 if (!NT_SUCCESS (status))
165 debug_printf ("NtQuerySecurityObject (%S), status %p",
169 /* ... and create a new security descriptor in which all inherited ACEs
170 are marked with the INHERITED_ACE flag. For a description of the
171 undocumented RtlConvertToAutoInheritSecurityObject function from
172 ntdll.dll see the MSDN man page for the advapi32 function
173 ConvertToAutoInheritPrivateObjectSecurity. Fortunately the latter
175 status = RtlConvertToAutoInheritSecurityObject (psd, sd, &nsd, NULL,
178 if (!NT_SUCCESS (status))
180 debug_printf ("RtlConvertToAutoInheritSecurityObject (%S), status %p",
184 /* Eventually copy the new security descriptor into sd and delete the
185 original one created by RtlConvertToAutoInheritSecurityObject from
187 len = RtlLengthSecurityDescriptor (nsd);
188 memcpy ((PSECURITY_DESCRIPTOR) sd, nsd, len);
189 RtlDeleteSecurityObject (&nsd);
195 set_file_sd (HANDLE fh, path_conv &pc, security_descriptor &sd, bool is_chown)
197 NTSTATUS status = STATUS_SUCCESS;
201 for (; retry < 2; ++retry)
205 status = NtSetSecurityObject (fh,
206 is_chown ? ALL_SECURITY_INFORMATION
207 : DACL_SECURITY_INFORMATION,
209 if (NT_SUCCESS (status))
217 OBJECT_ATTRIBUTES attr;
219 status = NtOpenFile (&fh, (is_chown ? WRITE_OWNER : 0) | WRITE_DAC,
220 pc.get_object_attr (attr, sec_none_nih),
221 &io, FILE_SHARE_VALID_FLAGS,
222 FILE_OPEN_FOR_BACKUP_INTENT);
223 if (!NT_SUCCESS (status))
232 if (!NT_SUCCESS (status))
233 __seterrno_from_nt_status (status);
238 get_attribute_from_acl (mode_t *attribute, PACL acl, PSID owner_sid,
239 PSID group_sid, bool grp_member)
241 ACCESS_ALLOWED_ACE *ace;
246 for (DWORD i = 0; i < acl->AceCount; ++i)
248 if (!NT_SUCCESS (RtlGetAce (acl, i, (PVOID *) &ace)))
250 if (ace->Header.AceFlags & INHERIT_ONLY_ACE)
252 switch (ace->Header.AceType)
254 case ACCESS_ALLOWED_ACE_TYPE:
258 case ACCESS_DENIED_ACE_TYPE:
266 cygpsid ace_sid ((PSID) &ace->SidStart);
267 if (ace_sid == well_known_world_sid)
269 if (ace->Mask & FILE_READ_BITS)
270 *flags |= ((!(*anti & S_IROTH)) ? S_IROTH : 0)
271 | ((!(*anti & S_IRGRP)) ? S_IRGRP : 0)
272 | ((!(*anti & S_IRUSR)) ? S_IRUSR : 0);
273 if (ace->Mask & FILE_WRITE_BITS)
274 *flags |= ((!(*anti & S_IWOTH)) ? S_IWOTH : 0)
275 | ((!(*anti & S_IWGRP)) ? S_IWGRP : 0)
276 | ((!(*anti & S_IWUSR)) ? S_IWUSR : 0);
277 if (ace->Mask & FILE_EXEC_BITS)
278 *flags |= ((!(*anti & S_IXOTH)) ? S_IXOTH : 0)
279 | ((!(*anti & S_IXGRP)) ? S_IXGRP : 0)
280 | ((!(*anti & S_IXUSR)) ? S_IXUSR : 0);
281 if ((S_ISDIR (*attribute)) &&
282 (ace->Mask & (FILE_WRITE_DATA | FILE_EXECUTE | FILE_DELETE_CHILD))
283 == (FILE_WRITE_DATA | FILE_EXECUTE))
286 else if (ace_sid == well_known_null_sid)
288 /* Read SUID, SGID and VTX bits from NULL ACE. */
289 if (ace->Mask & FILE_READ_DATA)
291 if (ace->Mask & FILE_WRITE_DATA)
293 if (ace->Mask & FILE_APPEND_DATA)
296 else if (ace_sid == owner_sid)
298 if (ace->Mask & FILE_READ_BITS)
299 *flags |= ((!(*anti & S_IRUSR)) ? S_IRUSR : 0);
300 if (ace->Mask & FILE_WRITE_BITS)
301 *flags |= ((!(*anti & S_IWUSR)) ? S_IWUSR : 0);
302 if (ace->Mask & FILE_EXEC_BITS)
303 *flags |= ((!(*anti & S_IXUSR)) ? S_IXUSR : 0);
305 else if (ace_sid == group_sid)
307 if (ace->Mask & FILE_READ_BITS)
308 *flags |= ((!(*anti & S_IRGRP)) ? S_IRGRP : 0)
309 | ((grp_member && !(*anti & S_IRUSR)) ? S_IRUSR : 0);
310 if (ace->Mask & FILE_WRITE_BITS)
311 *flags |= ((!(*anti & S_IWGRP)) ? S_IWGRP : 0)
312 | ((grp_member && !(*anti & S_IWUSR)) ? S_IWUSR : 0);
313 if (ace->Mask & FILE_EXEC_BITS)
314 *flags |= ((!(*anti & S_IXGRP)) ? S_IXGRP : 0)
315 | ((grp_member && !(*anti & S_IXUSR)) ? S_IXUSR : 0);
318 *attribute &= ~(S_IRWXU | S_IRWXG | S_IRWXO | S_ISVTX | S_ISGID | S_ISUID);
319 if (owner_sid && group_sid && RtlEqualSid (owner_sid, group_sid)
320 /* FIXME: temporary exception for /var/empty */
321 && well_known_system_sid != group_sid)
323 allow &= ~(S_IRGRP | S_IWGRP | S_IXGRP);
324 allow |= (((allow & S_IRUSR) ? S_IRGRP : 0)
325 | ((allow & S_IWUSR) ? S_IWGRP : 0)
326 | ((allow & S_IXUSR) ? S_IXGRP : 0));
332 get_info_from_sd (PSECURITY_DESCRIPTOR psd, mode_t *attribute,
333 __uid32_t *uidret, __gid32_t *gidret)
337 /* If reading the security descriptor failed, treat the object
340 *attribute &= ~(S_IRWXU | S_IRWXG | S_IRWXO);
342 *uidret = ILLEGAL_UID;
344 *gidret = ILLEGAL_GID;
353 status = RtlGetOwnerSecurityDescriptor (psd, (PSID *) &owner_sid, &dummy);
354 if (!NT_SUCCESS (status))
355 debug_printf ("RtlGetOwnerSecurityDescriptor: %p", status);
356 status = RtlGetGroupSecurityDescriptor (psd, (PSID *) &group_sid, &dummy);
357 if (!NT_SUCCESS (status))
358 debug_printf ("RtlGetGroupSecurityDescriptor: %p", status);
362 bool grp_member = get_sids_info (owner_sid, group_sid, &uid, &gid);
370 syscall_printf ("uid %d, gid %d", uid, gid);
377 status = RtlGetDaclSecurityDescriptor (psd, &acl_exists, &acl, &dummy);
378 if (!NT_SUCCESS (status))
380 __seterrno_from_nt_status (status);
381 *attribute &= ~(S_IRWXU | S_IRWXG | S_IRWXO);
383 else if (!acl_exists || !acl)
384 *attribute |= S_IRWXU | S_IRWXG | S_IRWXO;
386 get_attribute_from_acl (attribute, acl, owner_sid, group_sid, grp_member);
388 syscall_printf ("%sACL %x, uid %d, gid %d",
389 (!acl_exists || !acl)?"NO ":"", *attribute, uid, gid);
393 get_reg_sd (HANDLE handle, security_descriptor &sd_ret)
398 ret = RegGetKeySecurity ((HKEY) handle, ALL_SECURITY_INFORMATION,
400 if (ret == ERROR_INSUFFICIENT_BUFFER)
402 if (!sd_ret.malloc (len))
405 ret = RegGetKeySecurity ((HKEY) handle, ALL_SECURITY_INFORMATION,
408 if (ret != ERROR_SUCCESS)
417 get_reg_attribute (HKEY hkey, mode_t *attribute, __uid32_t *uidret,
420 security_descriptor sd;
422 if (!get_reg_sd (hkey, sd))
424 get_info_from_sd (sd, attribute, uidret, gidret);
427 /* The entries are already set to default values */
432 get_file_attribute (HANDLE handle, path_conv &pc,
433 mode_t *attribute, __uid32_t *uidret, __gid32_t *gidret)
437 security_descriptor sd;
439 if (!get_file_sd (handle, pc, sd, false))
441 get_info_from_sd (sd, attribute, uidret, gidret);
444 /* ENOSYS is returned by get_file_sd if fetching the DACL from a remote
445 share returns STATUS_INVALID_NETWORK_RESPONSE, which in turn is
446 converted to ERROR_BAD_NET_RESP. This potentially occurs when trying
447 to fetch DACLs from a NT4 machine which is not part of the domain of
448 the requesting machine. */
449 else if (get_errno () != ENOSYS)
452 *uidret = ILLEGAL_UID;
454 *gidret = ILLEGAL_GID;
461 *uidret = myself->uid;
463 *gidret = myself->gid;
469 add_access_allowed_ace (PACL acl, int offset, DWORD attributes,
470 PSID sid, size_t &len_add, DWORD inherit)
472 NTSTATUS status = RtlAddAccessAllowedAce (acl, ACL_REVISION, attributes, sid);
473 if (!NT_SUCCESS (status))
475 __seterrno_from_nt_status (status);
478 ACCESS_ALLOWED_ACE *ace;
479 if (inherit && NT_SUCCESS (RtlGetAce (acl, offset, (PVOID *) &ace)))
480 ace->Header.AceFlags |= inherit;
481 len_add += sizeof (ACCESS_ALLOWED_ACE) - sizeof (DWORD) + RtlLengthSid (sid);
486 add_access_denied_ace (PACL acl, int offset, DWORD attributes,
487 PSID sid, size_t &len_add, DWORD inherit)
489 NTSTATUS status = RtlAddAccessDeniedAce (acl, ACL_REVISION, attributes, sid);
490 if (!NT_SUCCESS (status))
492 __seterrno_from_nt_status (status);
495 ACCESS_DENIED_ACE *ace;
496 if (inherit && NT_SUCCESS (RtlGetAce (acl, offset, (PVOID *) &ace)))
497 ace->Header.AceFlags |= inherit;
498 len_add += sizeof (ACCESS_DENIED_ACE) - sizeof (DWORD) + RtlLengthSid (sid);
502 static PSECURITY_DESCRIPTOR
503 alloc_sd (path_conv &pc, __uid32_t uid, __gid32_t gid, int attribute,
504 security_descriptor &sd_ret)
510 /* NOTE: If the high bit of attribute is set, we have just created
511 a file or directory. See below for an explanation. */
513 debug_printf("uid %d, gid %d, attribute %x", uid, gid, attribute);
515 /* Get owner and group from current security descriptor. */
516 PSID cur_owner_sid = NULL;
517 PSID cur_group_sid = NULL;
518 status = RtlGetOwnerSecurityDescriptor (sd_ret, &cur_owner_sid, &dummy);
519 if (!NT_SUCCESS (status))
520 debug_printf ("RtlGetOwnerSecurityDescriptor: %p", status);
521 status = RtlGetGroupSecurityDescriptor (sd_ret, &cur_group_sid, &dummy);
522 if (!NT_SUCCESS (status))
523 debug_printf ("RtlGetGroupSecurityDescriptor: %p", status);
525 /* Get SID of owner. */
527 /* Check for current user first */
528 if (uid == myself->uid)
529 owner_sid = cygheap->user.sid ();
530 else if (uid == ILLEGAL_UID)
531 owner_sid = cur_owner_sid;
532 else if (!owner_sid.getfrompw (internal_getpwuid (uid)))
537 owner_sid.debug_print ("alloc_sd: owner SID =");
539 /* Get SID of new group. */
541 /* Check for current user first */
542 if (gid == myself->gid)
543 group_sid = cygheap->user.groups.pgsid;
544 else if (gid == ILLEGAL_GID)
545 group_sid = cur_group_sid;
546 else if (!group_sid.getfromgr (internal_getgrgid (gid)))
551 group_sid.debug_print ("alloc_sd: group SID =");
553 /* Initialize local security descriptor. */
554 SECURITY_DESCRIPTOR sd;
555 RtlCreateSecurityDescriptor (&sd, SECURITY_DESCRIPTOR_REVISION);
557 /* We set the SE_DACL_PROTECTED flag here to prevent the DACL from being
558 modified by inheritable ACEs. */
559 RtlSetControlSecurityDescriptor (&sd, SE_DACL_PROTECTED, SE_DACL_PROTECTED);
561 /* Create owner for local security descriptor. */
562 status = RtlSetOwnerSecurityDescriptor (&sd, owner_sid, FALSE);
563 if (!NT_SUCCESS (status))
565 __seterrno_from_nt_status (status);
569 /* Create group for local security descriptor. */
570 status = RtlSetGroupSecurityDescriptor (&sd, group_sid, FALSE);
571 if (!NT_SUCCESS (status))
573 __seterrno_from_nt_status (status);
577 /* Initialize local access control list. */
578 PACL acl = (PACL) tp.w_get ();
579 RtlCreateAcl (acl, ACL_MAXIMUM_SIZE, ACL_REVISION);
581 /* From here fill ACL. */
582 size_t acl_len = sizeof (ACL);
584 /* Only used for sync objects (for ttys). The admins group should
585 always have the right to manipulate the ACL, so we have to make sure
586 that the ACL gives the admins group STANDARD_RIGHTS_ALL access. */
587 bool saw_admins = false;
589 /* Construct allow attribute for owner.
590 Don't set FILE_READ/WRITE_ATTRIBUTES unconditionally on Samba, otherwise
591 it enforces read permissions. Same for other's below. */
592 DWORD owner_allow = STANDARD_RIGHTS_ALL
594 ? 0 : (FILE_READ_ATTRIBUTES | FILE_WRITE_ATTRIBUTES));
595 if (attribute & S_IRUSR)
596 owner_allow |= FILE_GENERIC_READ;
597 if (attribute & S_IWUSR)
598 owner_allow |= FILE_GENERIC_WRITE;
599 if (attribute & S_IXUSR)
600 owner_allow |= FILE_GENERIC_EXECUTE & ~FILE_READ_ATTRIBUTES;
601 if (S_ISDIR (attribute)
602 && (attribute & (S_IWUSR | S_IXUSR)) == (S_IWUSR | S_IXUSR))
603 owner_allow |= FILE_DELETE_CHILD;
604 /* For sync objects note that the owner is admin. */
605 if (S_ISCHR (attribute) && owner_sid == well_known_admins_sid)
608 /* Construct allow attribute for group. */
609 DWORD group_allow = STANDARD_RIGHTS_READ | SYNCHRONIZE
610 | (pc.fs_is_samba () ? 0 : FILE_READ_ATTRIBUTES);
611 if (attribute & S_IRGRP)
612 group_allow |= FILE_GENERIC_READ;
613 if (attribute & S_IWGRP)
614 group_allow |= FILE_GENERIC_WRITE;
615 if (attribute & S_IXGRP)
616 group_allow |= FILE_GENERIC_EXECUTE & ~FILE_READ_ATTRIBUTES;
617 if (S_ISDIR (attribute)
618 && (attribute & (S_IWGRP | S_IXGRP)) == (S_IWGRP | S_IXGRP)
619 && !(attribute & S_ISVTX))
620 group_allow |= FILE_DELETE_CHILD;
621 /* For sync objects, add STANDARD_RIGHTS_ALL for admins group. */
622 if (S_ISCHR (attribute) && group_sid == well_known_admins_sid)
624 group_allow |= STANDARD_RIGHTS_ALL;
628 /* Construct allow attribute for everyone. */
629 DWORD other_allow = STANDARD_RIGHTS_READ | SYNCHRONIZE
630 | (pc.fs_is_samba () ? 0 : FILE_READ_ATTRIBUTES);
631 if (attribute & S_IROTH)
632 other_allow |= FILE_GENERIC_READ;
633 if (attribute & S_IWOTH)
634 other_allow |= FILE_GENERIC_WRITE;
635 if (attribute & S_IXOTH)
636 other_allow |= FILE_GENERIC_EXECUTE & ~FILE_READ_ATTRIBUTES;
637 if (S_ISDIR (attribute)
638 && (attribute & (S_IWOTH | S_IXOTH)) == (S_IWOTH | S_IXOTH)
639 && !(attribute & S_ISVTX))
640 other_allow |= FILE_DELETE_CHILD;
642 /* Construct SUID, SGID and VTX bits in NULL ACE. */
643 DWORD null_allow = 0L;
644 if (attribute & (S_ISUID | S_ISGID | S_ISVTX))
646 if (attribute & S_ISUID)
647 null_allow |= FILE_APPEND_DATA;
648 if (attribute & S_ISGID)
649 null_allow |= FILE_WRITE_DATA;
650 if (attribute & S_ISVTX)
651 null_allow |= FILE_READ_DATA;
654 /* Add owner and group permissions if SIDs are equal
655 and construct deny attributes for group and owner. */
657 if ((isownergroup = (owner_sid == group_sid)))
658 owner_allow |= group_allow;
660 DWORD owner_deny = ~owner_allow & (group_allow | other_allow);
661 owner_deny &= ~(STANDARD_RIGHTS_READ
662 | FILE_READ_ATTRIBUTES | FILE_WRITE_ATTRIBUTES);
664 DWORD group_deny = ~group_allow & other_allow;
665 group_deny &= ~(STANDARD_RIGHTS_READ | FILE_READ_ATTRIBUTES);
667 /* Set deny ACE for owner. */
669 && !add_access_denied_ace (acl, ace_off++, owner_deny,
670 owner_sid, acl_len, NO_INHERITANCE))
672 /* Set deny ACE for group here to respect the canonical order,
673 if this does not impact owner */
674 if (group_deny && !(group_deny & owner_allow) && !isownergroup
675 && !add_access_denied_ace (acl, ace_off++, group_deny,
676 group_sid, acl_len, NO_INHERITANCE))
678 /* Set allow ACE for owner. */
679 if (!add_access_allowed_ace (acl, ace_off++, owner_allow,
680 owner_sid, acl_len, NO_INHERITANCE))
682 /* Set deny ACE for group, if still needed. */
683 if (group_deny & owner_allow && !isownergroup
684 && !add_access_denied_ace (acl, ace_off++, group_deny,
685 group_sid, acl_len, NO_INHERITANCE))
687 /* Set allow ACE for group. */
689 && !add_access_allowed_ace (acl, ace_off++, group_allow,
690 group_sid, acl_len, NO_INHERITANCE))
693 /* For sync objects, if we didn't see the admins group so far, add entry
694 with STANDARD_RIGHTS_ALL access. */
695 if (S_ISCHR (attribute) && !saw_admins)
697 if (!add_access_allowed_ace (acl, ace_off++, STANDARD_RIGHTS_ALL,
698 well_known_admins_sid, acl_len,
704 /* Set allow ACE for everyone. */
705 if (!add_access_allowed_ace (acl, ace_off++, other_allow,
706 well_known_world_sid, acl_len, NO_INHERITANCE))
708 /* Set null ACE for special bits. */
710 && !add_access_allowed_ace (acl, ace_off++, null_allow,
711 well_known_null_sid, acl_len, NO_INHERITANCE))
714 /* Fill ACL with unrelated ACEs from current security descriptor. */
716 BOOLEAN acl_exists = FALSE;
717 ACCESS_ALLOWED_ACE *ace;
719 status = RtlGetDaclSecurityDescriptor (sd_ret, &acl_exists, &oacl, &dummy);
720 if (NT_SUCCESS (status) && acl_exists && oacl)
721 for (DWORD i = 0; i < oacl->AceCount; ++i)
722 if (NT_SUCCESS (RtlGetAce (oacl, i, (PVOID *) &ace)))
724 cygpsid ace_sid ((PSID) &ace->SidStart);
726 /* Always skip NULL SID as well as admins SID on virtual device files
728 if (ace_sid == well_known_null_sid
729 || (S_ISCHR (attribute) && ace_sid == well_known_admins_sid))
731 /* Check for ACEs which are always created in the preceding code
732 and check for the default inheritence ACEs which will be created
733 for just created directories. Skip them for just created
734 directories or if they are not inherited. If they are inherited,
735 make sure they are *only* inherited, so they don't collide with
736 the permissions set in this function. */
737 if ((ace_sid == cur_owner_sid)
738 || (ace_sid == owner_sid)
739 || (ace_sid == cur_group_sid)
740 || (ace_sid == group_sid)
741 || (ace_sid == well_known_creator_owner_sid)
742 || (ace_sid == well_known_creator_group_sid)
743 || (ace_sid == well_known_world_sid))
745 if ((S_ISDIR (attribute) && (attribute & S_JUSTCREATED))
746 || (ace->Header.AceFlags
747 & (CONTAINER_INHERIT_ACE | OBJECT_INHERIT_ACE)) == 0)
750 ace->Header.AceFlags |= INHERIT_ONLY_ACE;
752 if (attribute & S_JUSTCREATED)
754 /* Since files and dirs are created with a NULL descriptor,
755 inheritence rules kick in. If no inheritable entries exist
756 in the parent object, Windows will create entries from the
757 user token's default DACL in the file DACL. These entries
758 are not desired and we drop them silently. */
759 if (!(ace->Header.AceFlags & INHERITED_ACE))
761 /* Remove the INHERITED_ACE flag since on POSIX systems
762 inheritance is settled when the file has been created.
763 This also avoids error messages in Windows Explorer when
764 opening a file's security tab. Explorer complains if
765 inheritable ACEs are preceding non-inheritable ACEs. */
766 ace->Header.AceFlags &= ~INHERITED_ACE;
769 * Add unrelated ACCESS_DENIED_ACE to the beginning but
770 * behind the owner_deny, ACCESS_ALLOWED_ACE to the end.
771 * FIXME: this would break the order of the inherit-only ACEs
773 status = RtlAddAce (acl, ACL_REVISION,
774 ace->Header.AceType == ACCESS_DENIED_ACE_TYPE
775 ? (owner_deny ? 1 : 0) : MAXDWORD,
776 (LPVOID) ace, ace->Header.AceSize);
777 if (!NT_SUCCESS (status))
779 __seterrno_from_nt_status (status);
783 acl_len += ace->Header.AceSize;
786 /* Construct appropriate inherit attribute for new directories. Keep in
787 mind that we do this only for the sake of non-Cygwin applications.
788 Cygwin applications don't need this. */
789 if (S_ISDIR (attribute) && (attribute & S_JUSTCREATED))
791 const DWORD inherit = CONTAINER_INHERIT_ACE | OBJECT_INHERIT_ACE
793 #if 0 /* FIXME: Not done currently as this breaks the canonical order */
794 /* Set deny ACE for owner. */
796 && !add_access_denied_ace (acl, ace_off++, owner_deny,
797 well_known_creator_owner_sid, acl_len, inherit))
799 /* Set deny ACE for group here to respect the canonical order,
800 if this does not impact owner */
801 if (group_deny && !(group_deny & owner_allow)
802 && !add_access_denied_ace (acl, ace_off++, group_deny,
803 well_known_creator_group_sid, acl_len, inherit))
806 /* Set allow ACE for owner. */
807 if (!add_access_allowed_ace (acl, ace_off++, owner_allow,
808 well_known_creator_owner_sid, acl_len,
811 #if 0 /* FIXME: Not done currently as this breaks the canonical order and
812 won't be preserved on chown and chmod */
813 /* Set deny ACE for group, conflicting with owner_allow. */
814 if (group_deny & owner_allow
815 && !add_access_denied_ace (acl, ace_off++, group_deny,
816 well_known_creator_group_sid, acl_len, inherit))
819 /* Set allow ACE for group. */
820 if (!add_access_allowed_ace (acl, ace_off++, group_allow,
821 well_known_creator_group_sid, acl_len,
824 /* Set allow ACE for everyone. */
825 if (!add_access_allowed_ace (acl, ace_off++, other_allow,
826 well_known_world_sid, acl_len, inherit))
830 /* Set AclSize to computed value. */
831 acl->AclSize = acl_len;
832 debug_printf ("ACL-Size: %d", acl_len);
834 /* Create DACL for local security descriptor. */
835 status = RtlSetDaclSecurityDescriptor (&sd, TRUE, acl, FALSE);
836 if (!NT_SUCCESS (status))
838 __seterrno_from_nt_status (status);
842 /* Make self relative security descriptor. */
844 RtlAbsoluteToSelfRelativeSD (&sd, sd_ret, &sd_size);
850 if (!sd_ret.malloc (sd_size))
855 status = RtlAbsoluteToSelfRelativeSD (&sd, sd_ret, &sd_size);
856 if (!NT_SUCCESS (status))
858 __seterrno_from_nt_status (status);
861 debug_printf ("Created SD-Size: %u", sd_ret.size ());
867 set_security_attribute (path_conv &pc, int attribute, PSECURITY_ATTRIBUTES psa,
868 security_descriptor &sd)
870 psa->lpSecurityDescriptor = sd.malloc (SECURITY_DESCRIPTOR_MIN_LENGTH);
871 RtlCreateSecurityDescriptor ((PSECURITY_DESCRIPTOR) psa->lpSecurityDescriptor,
872 SECURITY_DESCRIPTOR_REVISION);
873 psa->lpSecurityDescriptor = alloc_sd (pc, geteuid32 (), getegid32 (),
878 get_object_sd (HANDLE handle, security_descriptor &sd)
883 status = NtQuerySecurityObject (handle, ALL_SECURITY_INFORMATION,
885 if (status != STATUS_BUFFER_TOO_SMALL)
887 __seterrno_from_nt_status (status);
890 if (!sd.malloc (len))
895 status = NtQuerySecurityObject (handle, ALL_SECURITY_INFORMATION,
897 if (!NT_SUCCESS (status))
899 __seterrno_from_nt_status (status);
906 get_object_attribute (HANDLE handle, __uid32_t *uidret, __gid32_t *gidret,
909 security_descriptor sd;
911 if (get_object_sd (handle, sd))
913 get_info_from_sd (sd, attribute, uidret, gidret);
918 create_object_sd_from_attribute (HANDLE handle, __uid32_t uid, __gid32_t gid,
919 mode_t attribute, security_descriptor &sd)
922 if ((handle && get_object_sd (handle, sd))
923 || !alloc_sd (pc, uid, gid, attribute, sd))
929 set_object_sd (HANDLE handle, security_descriptor &sd, bool chown)
932 status = NtSetSecurityObject (handle, chown ? ALL_SECURITY_INFORMATION
933 : DACL_SECURITY_INFORMATION, sd);
934 if (!NT_SUCCESS (status))
936 __seterrno_from_nt_status (status);
943 set_object_attribute (HANDLE handle, __uid32_t uid, __gid32_t gid,
946 security_descriptor sd;
948 if (create_object_sd_from_attribute (handle, uid, gid, attribute, sd)
949 || set_object_sd (handle, sd, uid != ILLEGAL_UID || gid != ILLEGAL_GID))
955 set_file_attribute (HANDLE handle, path_conv &pc,
956 __uid32_t uid, __gid32_t gid, mode_t attribute)
962 security_descriptor sd;
964 if (!get_file_sd (handle, pc, sd, (bool)(attribute & S_JUSTCREATED))
965 && alloc_sd (pc, uid, gid, attribute, sd))
966 ret = set_file_sd (handle, pc, sd,
967 uid != ILLEGAL_UID || gid != ILLEGAL_GID);
971 syscall_printf ("%d = set_file_attribute(%S, %d, %d, %p)",
972 ret, pc.get_nt_native_path (), uid, gid, attribute);
977 check_access (security_descriptor &sd, GENERIC_MAPPING &mapping,
978 ACCESS_MASK desired, int flags, bool effective)
981 NTSTATUS status, allow;
983 DWORD plen = sizeof (PRIVILEGE_SET) + 3 * sizeof (LUID_AND_ATTRIBUTES);
984 PPRIVILEGE_SET pset = (PPRIVILEGE_SET) alloca (plen);
985 HANDLE tok = ((effective && cygheap->user.issetuid ())
986 ? cygheap->user.imp_token ()
991 if (!DuplicateTokenEx (hProcToken, MAXIMUM_ALLOWED, NULL,
992 SecurityImpersonation, TokenImpersonation,
1001 status = NtAccessCheck (sd, tok, desired, &mapping, pset, &plen, &granted,
1003 if (!NT_SUCCESS (status))
1005 else if (!NT_SUCCESS (allow))
1007 /* CV, 2006-10-16: Now, that's really weird. Imagine a user who has no
1008 standard access to a file, but who has backup and restore privileges
1009 and these privileges are enabled in the access token. One would
1010 expect that the AccessCheck function takes this into consideration
1011 when returning the access status. Otherwise, why bother with the
1012 pset parameter, right?
1013 But not so. AccessCheck actually returns a status of "false" here,
1014 even though opening a file with backup resp. restore intent
1015 naturally succeeds for this user. This definitely spoils the results
1016 of access(2) for administrative users or the SYSTEM account. So, in
1017 case the access check fails, another check against the user's
1018 backup/restore privileges has to be made. Sigh. */
1019 int granted_flags = 0;
1024 pset->PrivilegeCount = 1;
1026 pset->Privilege[0].Luid.HighPart = 0L;
1027 pset->Privilege[0].Luid.LowPart = SE_BACKUP_PRIVILEGE;
1028 pset->Privilege[0].Attributes = 0;
1029 status = NtPrivilegeCheck (tok, pset, &has_priv);
1030 if (NT_SUCCESS (status) && has_priv)
1031 granted_flags |= R_OK;
1035 pset->PrivilegeCount = 1;
1037 pset->Privilege[0].Luid.HighPart = 0L;
1038 pset->Privilege[0].Luid.LowPart = SE_RESTORE_PRIVILEGE;
1039 pset->Privilege[0].Attributes = 0;
1040 status = NtPrivilegeCheck (tok, pset, &has_priv);
1041 if (NT_SUCCESS (status) && has_priv)
1042 granted_flags |= W_OK;
1044 if (granted_flags == flags)
1055 check_file_access (path_conv &pc, int flags, bool effective)
1057 security_descriptor sd;
1059 ACCESS_MASK desired = 0;
1061 desired |= FILE_READ_DATA;
1063 desired |= FILE_WRITE_DATA;
1065 desired |= FILE_EXECUTE;
1066 if (!get_file_sd (pc.handle (), pc, sd, false))
1067 ret = check_access (sd, file_mapping, desired, flags, effective);
1068 debug_printf ("flags %x, ret %d", flags, ret);
1073 check_registry_access (HANDLE hdl, int flags, bool effective)
1075 security_descriptor sd;
1077 static GENERIC_MAPPING NO_COPY reg_mapping = { KEY_READ,
1081 ACCESS_MASK desired = 0;
1083 desired |= KEY_ENUMERATE_SUB_KEYS;
1085 desired |= KEY_SET_VALUE;
1087 desired |= KEY_QUERY_VALUE;
1089 if ((HKEY) hdl == HKEY_PERFORMANCE_DATA)
1090 /* RegGetKeySecurity() always fails with ERROR_INVALID_HANDLE. */
1092 else if (!get_reg_sd (hdl, sd))
1093 ret = check_access (sd, reg_mapping, desired, flags, effective);
1095 /* As long as we can't write the registry... */
1101 debug_printf ("flags %x, ret %d", flags, ret);