1 /* sec/helper.cc: NT security helper functions
3 Written by Corinna Vinschen <corinna@vinschen.de>
5 This file is part of Cygwin.
7 This software is a copyrighted work licensed under the terms of the
8 Cygwin license. Please consult the file "CYGWIN_LICENSE" for
14 #include <cygwin/acl.h>
15 #include <sys/queue.h>
28 /* General purpose security attribute objects for global use. */
29 static NO_COPY_RO SECURITY_DESCRIPTOR null_sdp
=
30 { SECURITY_DESCRIPTOR_REVISION
, 0, SE_DACL_PRESENT
,
31 NULL
, NULL
, NULL
, NULL
};
32 SECURITY_ATTRIBUTES NO_COPY_RO sec_none
=
33 { sizeof (SECURITY_ATTRIBUTES
), NULL
, TRUE
};
34 SECURITY_ATTRIBUTES NO_COPY_RO sec_none_nih
=
35 { sizeof (SECURITY_ATTRIBUTES
), NULL
, FALSE
};
36 SECURITY_ATTRIBUTES NO_COPY_RO sec_all
=
37 { sizeof (SECURITY_ATTRIBUTES
), &null_sdp
, TRUE
};
38 SECURITY_ATTRIBUTES NO_COPY_RO sec_all_nih
=
39 { sizeof (SECURITY_ATTRIBUTES
), &null_sdp
, FALSE
};
41 MKSID (well_known_null_sid
, "S-1-0-0",
42 SECURITY_NULL_SID_AUTHORITY
, 1, SECURITY_NULL_RID
);
43 MKSID (well_known_world_sid
, "S-1-1-0",
44 SECURITY_WORLD_SID_AUTHORITY
, 1, SECURITY_WORLD_RID
);
45 MKSID (well_known_local_sid
, "S-1-2-0",
46 SECURITY_LOCAL_SID_AUTHORITY
, 1, SECURITY_LOCAL_RID
);
47 MKSID (well_known_console_logon_sid
, "S-1-2-1",
48 SECURITY_LOCAL_SID_AUTHORITY
, 1, 1);
49 MKSID (well_known_creator_owner_sid
, "S-1-3-0",
50 SECURITY_CREATOR_SID_AUTHORITY
, 1, SECURITY_CREATOR_OWNER_RID
);
51 MKSID (well_known_creator_group_sid
, "S-1-3-1",
52 SECURITY_CREATOR_SID_AUTHORITY
, 1, SECURITY_CREATOR_GROUP_RID
);
53 MKSID (well_known_dialup_sid
, "S-1-5-1",
54 SECURITY_NT_AUTHORITY
, 1, SECURITY_DIALUP_RID
);
55 MKSID (well_known_network_sid
, "S-1-5-2",
56 SECURITY_NT_AUTHORITY
, 1, SECURITY_NETWORK_RID
);
57 MKSID (well_known_batch_sid
, "S-1-5-3",
58 SECURITY_NT_AUTHORITY
, 1, SECURITY_BATCH_RID
);
59 MKSID (well_known_interactive_sid
, "S-1-5-4",
60 SECURITY_NT_AUTHORITY
, 1, SECURITY_INTERACTIVE_RID
);
61 MKSID (well_known_service_sid
, "S-1-5-6",
62 SECURITY_NT_AUTHORITY
, 1, SECURITY_SERVICE_RID
);
63 MKSID (well_known_authenticated_users_sid
, "S-1-5-11",
64 SECURITY_NT_AUTHORITY
, 1, SECURITY_AUTHENTICATED_USER_RID
);
65 MKSID (well_known_this_org_sid
, "S-1-5-15",
66 SECURITY_NT_AUTHORITY
, 1, 15);
67 MKSID (well_known_system_sid
, "S-1-5-18",
68 SECURITY_NT_AUTHORITY
, 1, SECURITY_LOCAL_SYSTEM_RID
);
69 MKSID (well_known_local_service_sid
, "S-1-5-19",
70 SECURITY_NT_AUTHORITY
, 1, SECURITY_LOCAL_SERVICE_RID
);
71 MKSID (well_known_network_service_sid
, "S-1-5-20",
72 SECURITY_NT_AUTHORITY
, 1, SECURITY_NETWORK_SERVICE_RID
);
73 MKSID (well_known_builtin_sid
, "S-1-5-32",
74 SECURITY_NT_AUTHORITY
, 1, SECURITY_BUILTIN_DOMAIN_RID
);
75 MKSID (well_known_admins_sid
, "S-1-5-32-544",
76 SECURITY_NT_AUTHORITY
, 2, SECURITY_BUILTIN_DOMAIN_RID
,
77 DOMAIN_ALIAS_RID_ADMINS
);
78 MKSID (well_known_users_sid
, "S-1-5-32-545",
79 SECURITY_NT_AUTHORITY
, 2, SECURITY_BUILTIN_DOMAIN_RID
,
80 DOMAIN_ALIAS_RID_USERS
);
81 MKSID (trusted_installer_sid
,
82 "S-1-5-80-956008885-3418522649-1831038044-1853292631-2271478464",
83 SECURITY_NT_AUTHORITY
, SECURITY_SERVICE_ID_RID_COUNT
,
84 SECURITY_SERVICE_ID_BASE_RID
, 956008885U, 3418522649U, 1831038044U,
85 1853292631U, 2271478464U);
86 MKSID (mandatory_medium_integrity_sid
, "S-1-16-8192",
87 SECURITY_MANDATORY_LABEL_AUTHORITY
, 1, SECURITY_MANDATORY_MEDIUM_RID
);
88 MKSID (mandatory_high_integrity_sid
, "S-1-16-12288",
89 SECURITY_MANDATORY_LABEL_AUTHORITY
, 1, SECURITY_MANDATORY_HIGH_RID
);
90 MKSID (mandatory_system_integrity_sid
, "S-1-16-16384",
91 SECURITY_MANDATORY_LABEL_AUTHORITY
, 1, SECURITY_MANDATORY_SYSTEM_RID
);
92 /* UNIX accounts on a Samba server have the SID prefix "S-1-22-1" */
93 #define SECURITY_SAMBA_UNIX_AUTHORITY {0,0,0,0,0,22}
94 MKSID (well_known_samba_unix_user_fake_sid
, "S-1-22-1-0",
95 SECURITY_SAMBA_UNIX_AUTHORITY
, 2, 1, 0);
98 cygpsid::operator== (const char *nsidstr
) const
100 cygsid
nsid (nsidstr
);
105 cygpsid::get_id (BOOL search_grp
, int *type
, cyg_ldap
*pldap
)
107 /* First try to get SID from group, then passwd */
108 uid_t id
= ILLEGAL_UID
;
113 if (cygheap
->user
.groups
.pgsid
== psid
)
115 else if (sid_id_auth (psid
) == 22 && cygheap
->pg
.nss_grp_db ())
117 /* Samba UNIX group? Try to map to Cygwin gid. If there's no
118 mapping in the cache, try to fetch it from the configured
119 RFC 2307 domain (see last comment in cygheap_domain_info::init()
120 for more information) and add it to the mapping cache.
121 If this is a user, not a group, make sure to skip the subsequent
122 internal_getgrsid call, otherwise we end up with a fake group
123 entry for a UNIX user account. */
124 if (sid_sub_auth (psid
, 0) == 2)
126 gid_t gid
= sid_sub_auth_rid (psid
);
127 gid_t map_gid
= cygheap
->ugid_cache
.get_gid (gid
);
128 if (map_gid
== ILLEGAL_GID
)
130 if (pldap
->open (cygheap
->dom
.get_rfc2307_domain ())
132 map_gid
= pldap
->remap_gid (gid
);
133 if (map_gid
== ILLEGAL_GID
)
134 map_gid
= MAP_UNIX_TO_CYGWIN_ID (gid
);
135 cygheap
->ugid_cache
.add_gid (gid
, map_gid
);
137 id
= (uid_t
) map_gid
;
140 else if ((gr
= internal_getgrsid (*this, pldap
)))
142 if ((gid_t
) id
!= ILLEGAL_GID
)
149 if (!search_grp
|| type
)
152 if (*this == cygheap
->user
.sid ())
154 else if (sid_id_auth (psid
) == 22 && sid_sub_auth (psid
, 0) == 1
155 && cygheap
->pg
.nss_pwd_db ())
157 /* Samba UNIX user. See comment above. */
158 uid_t uid
= sid_sub_auth_rid (psid
);
159 uid_t map_uid
= cygheap
->ugid_cache
.get_uid (uid
);
160 if (map_uid
== ILLEGAL_UID
)
162 if (pldap
->open (cygheap
->dom
.get_rfc2307_domain ()) == NO_ERROR
)
163 map_uid
= pldap
->remap_uid (uid
);
164 if (map_uid
== ILLEGAL_UID
)
165 map_uid
= MAP_UNIX_TO_CYGWIN_ID (uid
);
166 cygheap
->ugid_cache
.add_uid (uid
, map_uid
);
170 else if ((pw
= internal_getpwsid (*this, pldap
)))
172 if (id
!= ILLEGAL_UID
)
180 *type
= 0; /* undefined type */
185 cygpsid::pstring (PWCHAR nsidstr
) const
189 if (!psid
|| !nsidstr
)
191 RtlInitEmptyUnicodeString (&sid
, nsidstr
, 256);
192 RtlConvertSidToUnicodeString (&sid
, psid
, FALSE
);
193 return nsidstr
+ sid
.Length
/ sizeof (WCHAR
);
197 cygpsid::string (PWCHAR nsidstr
) const
199 if (pstring (nsidstr
))
205 cygpsid::pstring (char *nsidstr
) const
210 if (!psid
|| !nsidstr
)
212 strcpy (nsidstr
, "S-1-");
213 t
= nsidstr
+ sizeof ("S-1-") - 1;
214 t
+= __small_sprintf (t
, "%u", sid_id_auth (psid
));
215 for (i
= 0; i
< sid_sub_auth_count (psid
); ++i
)
216 t
+= __small_sprintf (t
, "-%lu", sid_sub_auth (psid
, i
));
221 cygpsid::string (char *nsidstr
) const
223 if (pstring (nsidstr
))
229 cygsid::get_sid (DWORD s
, DWORD cnt
, DWORD
*r
, bool well_known
)
232 SID_IDENTIFIER_AUTHORITY sid_auth
= { SECURITY_NULL_SID_AUTHORITY
};
233 # define SECURITY_NT_AUTH 5
235 /* 2015-10-22: Note that we let slip SIDs with a subauthority count of 0.
236 There are systems, which generate the SID S-1-0 as group ownership SID,
237 see https://cygwin.com/ml/cygwin/2015-10/msg00141.html. */
238 if (s
> 255 || cnt
> SID_MAX_SUB_AUTHORITIES
)
243 sid_auth
.Value
[5] = s
;
245 RtlInitializeSid (psid
, &sid_auth
, cnt
);
246 PISID dsid
= (PISID
) psid
;
247 for (i
= 0; i
< cnt
; ++i
)
248 dsid
->SubAuthority
[i
] = r
[i
];
249 /* If the well_known flag isn't set explicitely, we check the SID
250 for being a well-known SID ourselves. That's necessary because this
251 cygsid is created from a SID string, usually from /etc/passwd or
252 /etc/group. The calling code just doesn't know if the SID is well-known
253 or not. All SIDs are well-known SIDs, except those in the non-unique NT
256 well_known_sid
= well_known
;
258 well_known_sid
= (s
!= SECURITY_NT_AUTH
259 || r
[0] != SECURITY_NT_NON_UNIQUE
);
264 cygsid::getfromstr (PCWSTR nsidstr
, bool well_known
)
268 DWORD r
[SID_MAX_SUB_AUTHORITIES
];
270 if (nsidstr
&& !wcsncmp (nsidstr
, L
"S-1-", 4))
272 s
= wcstoul (nsidstr
+ 4, &lasts
, 10);
273 while (cnt
< SID_MAX_SUB_AUTHORITIES
&& *lasts
== '-')
274 r
[cnt
++] = wcstoul (lasts
+ 1, &lasts
, 10);
276 return get_sid (s
, cnt
, r
, well_known
);
278 return psid
= NO_SID
;
282 cygsid::getfromstr (const char *nsidstr
, bool well_known
)
286 DWORD r
[SID_MAX_SUB_AUTHORITIES
];
288 if (nsidstr
&& !strncmp (nsidstr
, "S-1-", 4))
290 s
= strtoul (nsidstr
+ 4, &lasts
, 10);
291 while (cnt
< SID_MAX_SUB_AUTHORITIES
&& *lasts
== '-')
292 r
[cnt
++] = strtoul (lasts
+ 1, &lasts
, 10);
294 return get_sid (s
, cnt
, r
, well_known
);
296 return psid
= NO_SID
;
300 cygsid::create (DWORD auth
, DWORD subauth_cnt
, ...)
305 if (subauth_cnt
> SID_MAX_SUB_AUTHORITIES
)
308 DWORD subauth
[subauth_cnt
];
310 va_start (ap
, subauth_cnt
);
311 for (DWORD i
= 0; i
< subauth_cnt
; ++i
)
312 subauth
[i
] = va_arg (ap
, DWORD
);
313 sid
= get_sid (auth
, subauth_cnt
, subauth
, false);
319 cygsid::append (DWORD rid
)
323 PISID dsid
= (PISID
) psid
;
324 if (dsid
->SubAuthorityCount
>= SID_MAX_SUB_AUTHORITIES
)
326 dsid
->SubAuthority
[dsid
->SubAuthorityCount
++] = rid
;
331 cygsidlist::alloc_sids (int n
)
334 return (cygsid
*) cmalloc (HEAP_STR
, n
* sizeof (cygsid
));
340 cygsidlist::free_sids ()
346 type
= cygsidlist_empty
;
350 cygsidlist::add (const PSID nsi
, bool well_known
)
356 cygsid
*tmp
= new cygsid
[2 * maxcnt
];
360 for (int i
= 0; i
< cnt
; ++i
)
373 security_descriptor::malloc (size_t nsize
)
376 if ((psd
= (PSECURITY_DESCRIPTOR
) ::malloc (nsize
)))
382 security_descriptor::realloc (size_t nsize
)
384 PSECURITY_DESCRIPTOR tmp
;
386 /* Can't re-use buffer allocated by GetSecurityInfo. */
389 if (!(tmp
= (PSECURITY_DESCRIPTOR
) ::realloc (psd
, nsize
)))
396 security_descriptor::free ()
412 /* Index must match the corresponding foo_PRIVILEGE value, see security.h. */
413 static const struct {
415 bool high_integrity
; /* UAC: High Mandatory Label required to
416 be allowed to enable this privilege in
422 { SE_CREATE_TOKEN_NAME
, true },
423 { SE_ASSIGNPRIMARYTOKEN_NAME
, true },
424 { SE_LOCK_MEMORY_NAME
, false },
425 { SE_INCREASE_QUOTA_NAME
, true },
426 { SE_MACHINE_ACCOUNT_NAME
, false },
427 { SE_TCB_NAME
, true },
428 { SE_SECURITY_NAME
, true },
429 { SE_TAKE_OWNERSHIP_NAME
, true },
430 { SE_LOAD_DRIVER_NAME
, true },
431 { SE_SYSTEM_PROFILE_NAME
, true },
432 { SE_SYSTEMTIME_NAME
, true },
433 { SE_PROF_SINGLE_PROCESS_NAME
, true },
434 { SE_INC_BASE_PRIORITY_NAME
, true },
435 { SE_CREATE_PAGEFILE_NAME
, true },
436 { SE_CREATE_PERMANENT_NAME
, false },
437 { SE_BACKUP_NAME
, true },
438 { SE_RESTORE_NAME
, true },
439 { SE_SHUTDOWN_NAME
, false },
440 { SE_DEBUG_NAME
, true },
441 { SE_AUDIT_NAME
, false },
442 { SE_SYSTEM_ENVIRONMENT_NAME
, true },
443 { SE_CHANGE_NOTIFY_NAME
, false },
444 { SE_REMOTE_SHUTDOWN_NAME
, true },
445 { SE_UNDOCK_NAME
, false },
446 { SE_SYNC_AGENT_NAME
, false },
447 { SE_ENABLE_DELEGATION_NAME
, false },
448 { SE_MANAGE_VOLUME_NAME
, true },
449 { SE_IMPERSONATE_NAME
, true },
450 { SE_CREATE_GLOBAL_NAME
, false },
451 { SE_TRUSTED_CREDMAN_ACCESS_NAME
, false },
452 { SE_RELABEL_NAME
, true },
453 { SE_INC_WORKING_SET_NAME
, false },
454 { SE_TIME_ZONE_NAME
, true },
455 { SE_CREATE_SYMBOLIC_LINK_NAME
, true }
459 privilege_luid (const PWCHAR pname
, LUID
&luid
, bool &high_integrity
)
462 for (idx
= SE_CREATE_TOKEN_PRIVILEGE
;
463 idx
<= SE_MAX_WELL_KNOWN_PRIVILEGE
;
465 if (!wcscmp (cygpriv
[idx
].name
, pname
))
469 high_integrity
= cygpriv
[idx
].high_integrity
;
475 static const wchar_t *
476 privilege_name (const LUID
&priv_luid
)
478 if (priv_luid
.HighPart
|| priv_luid
.LowPart
< SE_CREATE_TOKEN_PRIVILEGE
479 || priv_luid
.LowPart
> SE_MAX_WELL_KNOWN_PRIVILEGE
)
480 return L
"<unknown privilege>";
481 return cygpriv
[priv_luid
.LowPart
].name
;
485 set_privilege (HANDLE token
, DWORD privilege
, bool enable
)
488 TOKEN_PRIVILEGES new_priv
, orig_priv
;
492 new_priv
.PrivilegeCount
= 1;
493 new_priv
.Privileges
[0].Luid
.HighPart
= 0L;
494 new_priv
.Privileges
[0].Luid
.LowPart
= privilege
;
495 new_priv
.Privileges
[0].Attributes
= enable
? SE_PRIVILEGE_ENABLED
: 0;
497 status
= NtAdjustPrivilegesToken (token
, FALSE
, &new_priv
, sizeof orig_priv
,
499 if (!NT_SUCCESS (status
))
501 __seterrno_from_nt_status (status
);
505 /* If orig_priv.PrivilegeCount is 0, the privilege hasn't been changed. */
506 if (!orig_priv
.PrivilegeCount
)
507 ret
= enable
? 1 : 0;
509 ret
= (orig_priv
.Privileges
[0].Attributes
& SE_PRIVILEGE_ENABLED
) ? 1 : 0;
513 debug_printf ("%d = set_privilege((token %p) %W, %d)", ret
, token
,
514 privilege_name (new_priv
.Privileges
[0].Luid
), enable
);
518 /* This is called very early in process initialization. The code must
519 not depend on anything. */
521 set_cygwin_privileges (HANDLE token
)
523 /* Setting these rights at process startup allows processes running under
524 user tokens which are in the administrstors group to have root-like
526 /* Allow to access all files, independent of their ACL settings. */
527 set_privilege (token
, SE_RESTORE_PRIVILEGE
, true);
528 set_privilege (token
, SE_BACKUP_PRIVILEGE
, true);
529 /* Allow full access to other user's processes. */
530 set_privilege (token
, SE_DEBUG_PRIVILEGE
, true);
532 /* Allow to create global shared memory. This isn't required anymore since
533 Cygwin 1.7. It uses its own subdirectories in the global NT namespace
534 which isn't affected by the SE_CREATE_GLOBAL_PRIVILEGE restriction. */
535 set_privilege (token
, SE_CREATE_GLOBAL_PRIVILEGE
, true);
540 sec_acl (PACL acl
, bool original
, bool admins
, PSID sid1
, PSID sid2
, DWORD access2
)
543 size_t acl_len
= MAX_DACL_LEN (5);
548 if ((unsigned long) acl
% 4)
549 api_fatal ("Incorrectly aligned incoming ACL buffer!");
551 status
= RtlCreateAcl (acl
, acl_len
, ACL_REVISION
);
552 if (!NT_SUCCESS (status
))
554 debug_printf ("RtlCreateAcl: %y", status
);
559 status
= RtlAddAccessAllowedAce (acl
, ACL_REVISION
, GENERIC_ALL
, sid1
);
560 if (!NT_SUCCESS (status
))
561 debug_printf ("RtlAddAccessAllowedAce(sid1) %y", status
);
563 if (original
&& (psid
= cygheap
->user
.saved_sid ())
564 && psid
!= sid1
&& psid
!= well_known_system_sid
)
566 status
= RtlAddAccessAllowedAce (acl
, ACL_REVISION
, GENERIC_ALL
, psid
);
567 if (!NT_SUCCESS (status
))
568 debug_printf ("RtlAddAccessAllowedAce(original) %y", status
);
572 status
= RtlAddAccessAllowedAce (acl
, ACL_REVISION
, access2
, sid2
);
573 if (!NT_SUCCESS (status
))
574 debug_printf ("RtlAddAccessAllowedAce(sid2) %y", status
);
578 status
= RtlAddAccessAllowedAce (acl
, ACL_REVISION
, GENERIC_ALL
,
579 well_known_admins_sid
);
580 if (!NT_SUCCESS (status
))
581 debug_printf ("RtlAddAccessAllowedAce(admin) %y", status
);
583 status
= RtlAddAccessAllowedAce (acl
, ACL_REVISION
, GENERIC_ALL
,
584 well_known_system_sid
);
585 if (!NT_SUCCESS (status
))
586 debug_printf ("RtlAddAccessAllowedAce(system) %y", status
);
587 status
= RtlFirstFreeAce (acl
, &pAce
);
588 if (NT_SUCCESS (status
) && pAce
)
589 acl
->AclSize
= (char *) pAce
- (char *) acl
;
591 debug_printf ("RtlFirstFreeAce: %y", status
);
597 __sec_user (PVOID sa_buf
, PSID sid1
, PSID sid2
, DWORD access2
, BOOL inherit
)
599 PSECURITY_ATTRIBUTES psa
= (PSECURITY_ATTRIBUTES
) sa_buf
;
600 PISECURITY_DESCRIPTOR psd
= (PISECURITY_DESCRIPTOR
)
601 ((char *) sa_buf
+ sizeof (*psa
));
602 PACL acl
= (PACL
) ((char *) sa_buf
+ sizeof (*psa
) + sizeof (*psd
));
606 if ((unsigned long) sa_buf
% 4)
607 api_fatal ("Incorrectly aligned incoming SA buffer!");
609 if (!sec_acl (acl
, true, true, sid1
, sid2
, access2
))
610 return inherit
? &sec_none
: &sec_none_nih
;
612 RtlCreateSecurityDescriptor (psd
, SECURITY_DESCRIPTOR_REVISION
);
613 status
= RtlSetDaclSecurityDescriptor (psd
, TRUE
, acl
, FALSE
);
614 if (!NT_SUCCESS (status
))
615 debug_printf ("RtlSetDaclSecurityDescriptor %y", status
);
617 psa
->nLength
= sizeof (SECURITY_ATTRIBUTES
);
618 psa
->lpSecurityDescriptor
= psd
;
619 psa
->bInheritHandle
= inherit
;
623 /* Helper function to create a file security descriptor which allows
624 full access to admins, system, and the sid given as parameter. See
625 try_to_bin for how it's used. */
628 _recycler_sd (void *buf
, bool users
, bool dir
)
631 PISECURITY_DESCRIPTOR psd
= (PISECURITY_DESCRIPTOR
) buf
;
635 RtlCreateSecurityDescriptor (psd
, SECURITY_DESCRIPTOR_REVISION
);
636 PACL dacl
= (PACL
) (psd
+ 1);
637 RtlCreateAcl (dacl
, MAX_DACL_LEN (3), ACL_REVISION
);
638 RtlAddAccessAllowedAceEx (dacl
, ACL_REVISION
,
639 dir
? SUB_CONTAINERS_AND_OBJECTS_INHERIT
641 FILE_ALL_ACCESS
, well_known_admins_sid
);
642 RtlAddAccessAllowedAceEx (dacl
, ACL_REVISION
,
643 dir
? SUB_CONTAINERS_AND_OBJECTS_INHERIT
645 FILE_ALL_ACCESS
, well_known_system_sid
);
647 RtlAddAccessAllowedAceEx (dacl
, ACL_REVISION
, INHERIT_NO_PROPAGATE
,
648 FILE_GENERIC_READ
| FILE_GENERIC_EXECUTE
649 | FILE_APPEND_DATA
| FILE_WRITE_ATTRIBUTES
,
650 well_known_users_sid
);
652 RtlAddAccessAllowedAceEx (dacl
, ACL_REVISION
,
653 dir
? SUB_CONTAINERS_AND_OBJECTS_INHERIT
655 FILE_ALL_ACCESS
, cygheap
->user
.sid ());
657 status
= RtlFirstFreeAce (dacl
, &ace
);
658 if (!NT_SUCCESS (status
))
660 debug_printf ("RtlFirstFreeAce: %y", status
);
663 dacl
->AclSize
= (char *) ace
- (char *) dacl
;
664 RtlSetDaclSecurityDescriptor (psd
, TRUE
, dacl
, FALSE
);
665 /* If the directory DACL is not marked as protected, shell32 thinks
666 the recycle dir is corrupted. As soon as Explorer accesses the
667 Recycler, the user will get a GUI dialog "The Recycle Bin on X:\
668 is corrupted. Do you want to empty the Recycle Bin for this drive?"
669 Of course we want to avoid that. */
671 psd
->Control
|= SE_DACL_PROTECTED
;
675 /* Helper function to create an event security descriptor which only allows
676 specific access to everyone. Only the creating process has all access
680 _everyone_sd (void *buf
, ACCESS_MASK access
)
683 PISECURITY_DESCRIPTOR psd
= (PISECURITY_DESCRIPTOR
) buf
;
687 RtlCreateSecurityDescriptor (psd
, SECURITY_DESCRIPTOR_REVISION
);
688 PACL dacl
= (PACL
) (psd
+ 1);
689 RtlCreateAcl (dacl
, MAX_DACL_LEN (1), ACL_REVISION
);
690 status
= RtlAddAccessAllowedAce (dacl
, ACL_REVISION
, access
,
691 well_known_world_sid
);
692 if (!NT_SUCCESS (status
))
694 debug_printf ("RtlAddAccessAllowedAce: %y", status
);
698 status
= RtlFirstFreeAce (dacl
, &ace
);
699 if (!NT_SUCCESS (status
))
701 debug_printf ("RtlFirstFreeAce: %y", status
);
704 dacl
->AclSize
= (char *) ace
- (char *) dacl
;
705 RtlSetDaclSecurityDescriptor (psd
, TRUE
, dacl
, FALSE
);
710 static NO_COPY SRWLOCK authz_lock
= SRWLOCK_INIT
;
711 #define AUTHZ_LOCK() (AcquireSRWLockExclusive (&authz_lock))
712 #define AUTHZ_UNLOCK() (ReleaseSRWLockExclusive (&authz_lock))
714 static NO_COPY SRWLOCK user_ctx_lock
= SRWLOCK_INIT
;
715 #define USER_CTX_LOCK() (AcquireSRWLockExclusive (&user_ctx_lock))
716 #define USER_CTX_UNLOCK() (ReleaseSRWLockExclusive (&user_ctx_lock))
718 static NO_COPY SRWLOCK slist_lock
= SRWLOCK_INIT
;
719 #define SLIST_LOCK() (AcquireSRWLockExclusive (&slist_lock))
720 #define SLIST_UNLOCK() (ReleaseSRWLockExclusive (&slist_lock))
722 static LUID authz_dummy_luid
= { 0 };
724 class authz_ctx_cache_entry
726 SLIST_ENTRY (authz_ctx_cache_entry
) ctx_next
;
728 AUTHZ_CLIENT_CONTEXT_HANDLE ctx_hdl
;
730 authz_ctx_cache_entry ()
731 : sid (NO_SID
), ctx_hdl (NULL
)
733 ctx_next
.sle_next
= NULL
;
735 authz_ctx_cache_entry (bool)
736 : sid (NO_SID
), ctx_hdl (NULL
)
738 ctx_next
.sle_next
= NULL
;
740 void set (PSID psid
, AUTHZ_CLIENT_CONTEXT_HANDLE hdl
)
745 bool is (PSID psid
) const { return RtlEqualSid (sid
, psid
); }
746 AUTHZ_CLIENT_CONTEXT_HANDLE
context () const { return ctx_hdl
; }
748 friend class authz_ctx_cache
;
751 class authz_ctx_cache
753 SLIST_HEAD (, authz_ctx_cache_entry
) ctx_list
;
755 AUTHZ_CLIENT_CONTEXT_HANDLE
context (PSID
);
757 friend class authz_ctx
;
762 AUTHZ_RESOURCE_MANAGER_HANDLE authz_hdl
;
763 AUTHZ_CLIENT_CONTEXT_HANDLE user_ctx_hdl
;
764 authz_ctx_cache ctx_cache
;
765 operator AUTHZ_RESOURCE_MANAGER_HANDLE ();
767 friend class authz_ctx_cache
;
769 bool get_user_attribute (mode_t
*, PSECURITY_DESCRIPTOR
, PSID
);
772 /* Authz handles are not inheritable. */
773 static NO_COPY authz_ctx authz
;
775 authz_ctx::operator AUTHZ_RESOURCE_MANAGER_HANDLE ()
779 /* Create handle to Authz resource manager */
782 && !AuthzInitializeResourceManager (AUTHZ_RM_FLAG_NO_AUDIT
,
783 NULL
, NULL
, NULL
, NULL
,
785 debug_printf ("AuthzInitializeResourceManager, %E");
791 AUTHZ_CLIENT_CONTEXT_HANDLE
792 authz_ctx_cache::context (PSID user_sid
)
794 authz_ctx_cache_entry
*entry
;
795 AUTHZ_CLIENT_CONTEXT_HANDLE ctx_hdl
= NULL
;
797 SLIST_FOREACH (entry
, &ctx_list
, ctx_next
)
799 if (entry
->is (user_sid
))
800 return entry
->context ();
802 entry
= new authz_ctx_cache_entry (true);
803 /* If the user is the current user, prefer to create the context from the
804 token, as outlined in MSDN. */
805 if (RtlEqualSid (user_sid
, cygheap
->user
.sid ())
806 && !AuthzInitializeContextFromToken (0, cygheap
->user
.issetuid ()
807 ? cygheap
->user
.primary_token ()
809 authz
, NULL
, authz_dummy_luid
,
811 debug_printf ("AuthzInitializeContextFromToken, %E");
812 /* In any other case, create the context from the user SID. */
813 else if (!AuthzInitializeContextFromSid (0, user_sid
, authz
, NULL
,
814 authz_dummy_luid
, NULL
, &ctx_hdl
))
815 debug_printf ("AuthzInitializeContextFromSid, %E");
818 entry
->set (user_sid
, ctx_hdl
);
820 SLIST_INSERT_HEAD (&ctx_list
, entry
, ctx_next
);
822 return entry
->context ();
828 /* Ask Authz for the effective user permissions of the user with SID user_sid
829 on the object with security descriptor psd. We're caching the handles for
830 the Authz resource manager and the user contexts. */
832 authz_ctx::get_user_attribute (mode_t
*attribute
, PSECURITY_DESCRIPTOR psd
,
835 /* If the owner is the main user of the process token (not some impersonated
836 user), cache the user context in the global user_ctx_hdl variable. */
837 AUTHZ_CLIENT_CONTEXT_HANDLE ctx_hdl
= NULL
;
838 if (RtlEqualSid (user_sid
, cygheap
->user
.sid ())
839 && !cygheap
->user
.issetuid ())
841 /* Avoid lock in default case. */
845 /* Check user_ctx_hdl again under lock to avoid overwriting
846 user_ctx_hdl if it has already been initialized. */
848 && !AuthzInitializeContextFromToken (0, hProcToken
, authz
, NULL
,
849 authz_dummy_luid
, NULL
,
851 debug_printf ("AuthzInitializeContextFromToken, %E");
855 ctx_hdl
= user_ctx_hdl
;
857 if (!ctx_hdl
&& !(ctx_hdl
= ctx_cache
.context (user_sid
)))
859 /* All set, check access. */
860 ACCESS_MASK access
= 0;
862 AUTHZ_ACCESS_REQUEST req
= {
863 .DesiredAccess
= MAXIMUM_ALLOWED
,
864 .PrincipalSelfSid
= NULL
,
865 .ObjectTypeList
= NULL
,
866 .ObjectTypeListLength
= 0,
867 .OptionalArguments
= NULL
869 AUTHZ_ACCESS_REPLY repl
= {
870 .ResultListLength
= 1,
871 .GrantedAccessMask
= &access
,
872 .SaclEvaluationResults
= NULL
,
875 if (AuthzAccessCheck (0, ctx_hdl
, &req
, NULL
, psd
, NULL
, 0, &repl
, NULL
))
877 if (access
& FILE_READ_BITS
)
878 *attribute
|= S_IROTH
;
879 if (access
& FILE_WRITE_BITS
)
880 *attribute
|= S_IWOTH
;
881 if (access
& FILE_EXEC_BITS
)
882 *attribute
|= S_IXOTH
;
889 authz_get_user_attribute (mode_t
*attribute
, PSECURITY_DESCRIPTOR psd
,
893 return authz
.get_user_attribute (attribute
, psd
, user_sid
);