Cygwin: strptime: add release note
[newlib-cygwin.git] / winsup / cygwin / sec / helper.cc
blob45bf65dac4675e56d7604314357f0fa384da8f0b
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
9 details. */
11 #include "winsup.h"
12 #include <stdlib.h>
13 #include <stdarg.h>
14 #include <cygwin/acl.h>
15 #include <sys/queue.h>
16 #include <authz.h>
17 #include <wchar.h>
18 #include "cygerrno.h"
19 #include "security.h"
20 #include "path.h"
21 #include "fhandler.h"
22 #include "dtable.h"
23 #include "pinfo.h"
24 #include "cygheap.h"
25 #include "ntdll.h"
26 #include "ldap.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);
97 bool
98 cygpsid::operator== (const char *nsidstr) const
100 cygsid nsid (nsidstr);
101 return psid == nsid;
104 uid_t
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;
110 if (search_grp)
112 struct group *gr;
113 if (cygheap->user.groups.pgsid == psid)
114 id = myself->gid;
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 ())
131 == NO_ERROR)
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)))
141 id = gr->gr_gid;
142 if ((gid_t) id != ILLEGAL_GID)
144 if (type)
145 *type = GROUP;
146 return id;
149 if (!search_grp || type)
151 struct passwd *pw;
152 if (*this == cygheap->user.sid ())
153 id = myself->uid;
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);
168 id = map_uid;
170 else if ((pw = internal_getpwsid (*this, pldap)))
171 id = pw->pw_uid;
172 if (id != ILLEGAL_UID)
174 if (type)
175 *type = USER;
176 return id;
179 if (type)
180 *type = 0; /* undefined type */
181 return ILLEGAL_UID;
184 PWCHAR
185 cygpsid::pstring (PWCHAR nsidstr) const
187 UNICODE_STRING sid;
189 if (!psid || !nsidstr)
190 return NULL;
191 RtlInitEmptyUnicodeString (&sid, nsidstr, 256);
192 RtlConvertSidToUnicodeString (&sid, psid, FALSE);
193 return nsidstr + sid.Length / sizeof (WCHAR);
196 PWCHAR
197 cygpsid::string (PWCHAR nsidstr) const
199 if (pstring (nsidstr))
200 return nsidstr;
201 return NULL;
204 char *
205 cygpsid::pstring (char *nsidstr) const
207 char *t;
208 DWORD i;
210 if (!psid || !nsidstr)
211 return NULL;
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));
217 return t;
220 char *
221 cygpsid::string (char *nsidstr) const
223 if (pstring (nsidstr))
224 return nsidstr;
225 return NULL;
228 PSID
229 cygsid::get_sid (DWORD s, DWORD cnt, DWORD *r, bool well_known)
231 DWORD i;
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)
240 psid = NO_SID;
241 return NULL;
243 sid_auth.Value[5] = s;
244 set ();
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
254 authority range. */
255 if (well_known)
256 well_known_sid = well_known;
257 else
258 well_known_sid = (s != SECURITY_NT_AUTH
259 || r[0] != SECURITY_NT_NON_UNIQUE);
260 return psid;
263 const PSID
264 cygsid::getfromstr (PCWSTR nsidstr, bool well_known)
266 PWCHAR lasts;
267 DWORD s, cnt = 0;
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);
275 if (!*lasts)
276 return get_sid (s, cnt, r, well_known);
278 return psid = NO_SID;
281 const PSID
282 cygsid::getfromstr (const char *nsidstr, bool well_known)
284 char *lasts;
285 DWORD s, cnt = 0;
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);
293 if (!*lasts)
294 return get_sid (s, cnt, r, well_known);
296 return psid = NO_SID;
299 const PSID
300 cygsid::create (DWORD auth, DWORD subauth_cnt, ...)
302 va_list ap;
303 PSID sid;
305 if (subauth_cnt > SID_MAX_SUB_AUTHORITIES)
306 return NULL;
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);
314 va_end (ap);
315 return sid;
318 bool
319 cygsid::append (DWORD rid)
321 if (psid == NO_SID)
322 return false;
323 PISID dsid = (PISID) psid;
324 if (dsid->SubAuthorityCount >= SID_MAX_SUB_AUTHORITIES)
325 return false;
326 dsid->SubAuthority[dsid->SubAuthorityCount++] = rid;
327 return true;
330 cygsid *
331 cygsidlist::alloc_sids (int n)
333 if (n > 0)
334 return (cygsid *) cmalloc (HEAP_STR, n * sizeof (cygsid));
335 else
336 return NULL;
339 void
340 cygsidlist::free_sids ()
342 if (sids)
343 cfree (sids);
344 sids = NULL;
345 cnt = maxcnt = 0;
346 type = cygsidlist_empty;
349 BOOL
350 cygsidlist::add (const PSID nsi, bool well_known)
352 if (contains (nsi))
353 return TRUE;
354 if (cnt >= maxcnt)
356 cygsid *tmp = new cygsid [2 * maxcnt];
357 if (!tmp)
358 return FALSE;
359 maxcnt *= 2;
360 for (int i = 0; i < cnt; ++i)
361 tmp[i] = sids[i];
362 delete [] sids;
363 sids = tmp;
365 if (well_known)
366 sids[cnt++] *= nsi;
367 else
368 sids[cnt++] = nsi;
369 return TRUE;
372 PSECURITY_DESCRIPTOR
373 security_descriptor::malloc (size_t nsize)
375 free ();
376 if ((psd = (PSECURITY_DESCRIPTOR) ::malloc (nsize)))
377 sd_size = nsize;
378 return psd;
381 PSECURITY_DESCRIPTOR
382 security_descriptor::realloc (size_t nsize)
384 PSECURITY_DESCRIPTOR tmp;
386 /* Can't re-use buffer allocated by GetSecurityInfo. */
387 if (psd && !sd_size)
388 free ();
389 if (!(tmp = (PSECURITY_DESCRIPTOR) ::realloc (psd, nsize)))
390 return NULL;
391 sd_size = nsize;
392 return psd = tmp;
395 void
396 security_descriptor::free ()
398 if (psd)
400 if (!sd_size)
401 LocalFree (psd);
402 else
403 ::free (psd);
405 psd = NULL;
406 sd_size = 0;
409 #undef TEXT
410 #define TEXT(q) L##q
412 /* Index must match the corresponding foo_PRIVILEGE value, see security.h. */
413 static const struct {
414 const wchar_t *name;
415 bool high_integrity; /* UAC: High Mandatory Label required to
416 be allowed to enable this privilege in
417 the user token. */
418 } cygpriv[] =
420 { L"", false },
421 { L"", false },
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 }
458 bool
459 privilege_luid (const PWCHAR pname, LUID &luid, bool &high_integrity)
461 ULONG idx;
462 for (idx = SE_CREATE_TOKEN_PRIVILEGE;
463 idx <= SE_MAX_WELL_KNOWN_PRIVILEGE;
464 ++idx)
465 if (!wcscmp (cygpriv[idx].name, pname))
467 luid.HighPart = 0;
468 luid.LowPart = idx;
469 high_integrity = cygpriv[idx].high_integrity;
470 return true;
472 return false;
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)
487 int ret = -1;
488 TOKEN_PRIVILEGES new_priv, orig_priv;
489 ULONG size;
490 NTSTATUS status;
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,
498 &orig_priv, &size);
499 if (!NT_SUCCESS (status))
501 __seterrno_from_nt_status (status);
502 goto out;
505 /* If orig_priv.PrivilegeCount is 0, the privilege hasn't been changed. */
506 if (!orig_priv.PrivilegeCount)
507 ret = enable ? 1 : 0;
508 else
509 ret = (orig_priv.Privileges[0].Attributes & SE_PRIVILEGE_ENABLED) ? 1 : 0;
511 out:
512 if (ret < 0)
513 debug_printf ("%d = set_privilege((token %p) %W, %d)", ret, token,
514 privilege_name (new_priv.Privileges[0].Luid), enable);
515 return ret;
518 /* This is called very early in process initialization. The code must
519 not depend on anything. */
520 void
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
525 permissions. */
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);
531 #if 0
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);
536 #endif
539 bool
540 sec_acl (PACL acl, bool original, bool admins, PSID sid1, PSID sid2, DWORD access2)
542 NTSTATUS status;
543 size_t acl_len = MAX_DACL_LEN (5);
544 LPVOID pAce;
545 cygpsid psid;
547 #ifdef DEBUGGING
548 if ((unsigned long) acl % 4)
549 api_fatal ("Incorrectly aligned incoming ACL buffer!");
550 #endif
551 status = RtlCreateAcl (acl, acl_len, ACL_REVISION);
552 if (!NT_SUCCESS (status))
554 debug_printf ("RtlCreateAcl: %y", status);
555 return false;
557 if (sid1)
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);
570 if (sid2)
572 status = RtlAddAccessAllowedAce (acl, ACL_REVISION, access2, sid2);
573 if (!NT_SUCCESS (status))
574 debug_printf ("RtlAddAccessAllowedAce(sid2) %y", status);
576 if (admins)
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;
590 else
591 debug_printf ("RtlFirstFreeAce: %y", status);
593 return true;
596 PSECURITY_ATTRIBUTES
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));
603 NTSTATUS status;
605 #ifdef DEBUGGING
606 if ((unsigned long) sa_buf % 4)
607 api_fatal ("Incorrectly aligned incoming SA buffer!");
608 #endif
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;
620 return psa;
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. */
627 PSECURITY_DESCRIPTOR
628 _recycler_sd (void *buf, bool users, bool dir)
630 NTSTATUS status;
631 PISECURITY_DESCRIPTOR psd = (PISECURITY_DESCRIPTOR) buf;
633 if (!psd)
634 return NULL;
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
640 : NO_INHERITANCE,
641 FILE_ALL_ACCESS, well_known_admins_sid);
642 RtlAddAccessAllowedAceEx (dacl, ACL_REVISION,
643 dir ? SUB_CONTAINERS_AND_OBJECTS_INHERIT
644 : NO_INHERITANCE,
645 FILE_ALL_ACCESS, well_known_system_sid);
646 if (users)
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);
651 else
652 RtlAddAccessAllowedAceEx (dacl, ACL_REVISION,
653 dir ? SUB_CONTAINERS_AND_OBJECTS_INHERIT
654 : NO_INHERITANCE,
655 FILE_ALL_ACCESS, cygheap->user.sid ());
656 LPVOID ace;
657 status = RtlFirstFreeAce (dacl, &ace);
658 if (!NT_SUCCESS (status))
660 debug_printf ("RtlFirstFreeAce: %y", status);
661 return NULL;
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. */
670 if (dir)
671 psd->Control |= SE_DACL_PROTECTED;
672 return psd;
675 /* Helper function to create an event security descriptor which only allows
676 specific access to everyone. Only the creating process has all access
677 rights. */
679 PSECURITY_DESCRIPTOR
680 _everyone_sd (void *buf, ACCESS_MASK access)
682 NTSTATUS status;
683 PISECURITY_DESCRIPTOR psd = (PISECURITY_DESCRIPTOR) buf;
685 if (psd)
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);
695 return NULL;
697 LPVOID ace;
698 status = RtlFirstFreeAce (dacl, &ace);
699 if (!NT_SUCCESS (status))
701 debug_printf ("RtlFirstFreeAce: %y", status);
702 return NULL;
704 dacl->AclSize = (char *) ace - (char *) dacl;
705 RtlSetDaclSecurityDescriptor (psd, TRUE, dacl, FALSE);
707 return psd;
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;
727 cygsid sid;
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)
742 sid = psid;
743 ctx_hdl = 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;
760 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;
768 public:
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 ()
777 if (!authz_hdl)
779 /* Create handle to Authz resource manager */
780 AUTHZ_LOCK ();
781 if (!authz_hdl
782 && !AuthzInitializeResourceManager (AUTHZ_RM_FLAG_NO_AUDIT,
783 NULL, NULL, NULL, NULL,
784 &authz_hdl))
785 debug_printf ("AuthzInitializeResourceManager, %E");
786 AUTHZ_UNLOCK ();
788 return authz_hdl;
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 ()
808 : hProcToken,
809 authz, NULL, authz_dummy_luid,
810 NULL, &ctx_hdl))
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");
816 else
818 entry->set (user_sid, ctx_hdl);
819 SLIST_LOCK ();
820 SLIST_INSERT_HEAD (&ctx_list, entry, ctx_next);
821 SLIST_UNLOCK ();
822 return entry->context ();
824 delete entry;
825 return NULL;
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. */
831 bool
832 authz_ctx::get_user_attribute (mode_t *attribute, PSECURITY_DESCRIPTOR psd,
833 PSID user_sid)
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. */
842 if (!user_ctx_hdl)
844 USER_CTX_LOCK ();
845 /* Check user_ctx_hdl again under lock to avoid overwriting
846 user_ctx_hdl if it has already been initialized. */
847 if (!user_ctx_hdl
848 && !AuthzInitializeContextFromToken (0, hProcToken, authz, NULL,
849 authz_dummy_luid, NULL,
850 &user_ctx_hdl))
851 debug_printf ("AuthzInitializeContextFromToken, %E");
852 USER_CTX_UNLOCK ();
854 if (user_ctx_hdl)
855 ctx_hdl = user_ctx_hdl;
857 if (!ctx_hdl && !(ctx_hdl = ctx_cache.context (user_sid)))
858 return false;
859 /* All set, check access. */
860 ACCESS_MASK access = 0;
861 DWORD error = 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,
873 .Error = &error
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;
883 return true;
885 return false;
888 bool
889 authz_get_user_attribute (mode_t *attribute, PSECURITY_DESCRIPTOR psd,
890 PSID user_sid)
892 *attribute = 0;
893 return authz.get_user_attribute (attribute, psd, user_sid);