1 /* sec_helper.cc: NT security helper functions
3 Copyright 2000, 2001, 2002, 2003, 2004, 2006, 2007, 2008, 2009,
4 2010, 2011 Red Hat, Inc.
6 Written by Corinna Vinschen <corinna@vinschen.de>
8 This file is part of Cygwin.
10 This software is a copyrighted work licensed under the terms of the
11 Cygwin license. Please consult the file "CYGWIN_LICENSE" for
28 /* General purpose security attribute objects for global use. */
29 SECURITY_ATTRIBUTES NO_COPY sec_none;
30 SECURITY_ATTRIBUTES NO_COPY sec_none_nih;
31 SECURITY_ATTRIBUTES NO_COPY sec_all;
32 SECURITY_ATTRIBUTES NO_COPY sec_all_nih;
34 MKSID (well_known_null_sid, "S-1-0-0",
35 SECURITY_NULL_SID_AUTHORITY, 1, SECURITY_NULL_RID);
36 MKSID (well_known_world_sid, "S-1-1-0",
37 SECURITY_WORLD_SID_AUTHORITY, 1, SECURITY_WORLD_RID);
38 MKSID (well_known_local_sid, "S-1-2-0",
39 SECURITY_LOCAL_SID_AUTHORITY, 1, SECURITY_LOCAL_RID);
40 MKSID (well_known_console_logon_sid, "S-1-2-1",
41 SECURITY_LOCAL_SID_AUTHORITY, 1, 1);
42 MKSID (well_known_creator_owner_sid, "S-1-3-0",
43 SECURITY_CREATOR_SID_AUTHORITY, 1, SECURITY_CREATOR_OWNER_RID);
44 MKSID (well_known_creator_group_sid, "S-1-3-1",
45 SECURITY_CREATOR_SID_AUTHORITY, 1, SECURITY_CREATOR_GROUP_RID);
46 MKSID (well_known_dialup_sid, "S-1-5-1",
47 SECURITY_NT_AUTHORITY, 1, SECURITY_DIALUP_RID);
48 MKSID (well_known_network_sid, "S-1-5-2",
49 SECURITY_NT_AUTHORITY, 1, SECURITY_NETWORK_RID);
50 MKSID (well_known_batch_sid, "S-1-5-3",
51 SECURITY_NT_AUTHORITY, 1, SECURITY_BATCH_RID);
52 MKSID (well_known_interactive_sid, "S-1-5-4",
53 SECURITY_NT_AUTHORITY, 1, SECURITY_INTERACTIVE_RID);
54 MKSID (well_known_service_sid, "S-1-5-6",
55 SECURITY_NT_AUTHORITY, 1, SECURITY_SERVICE_RID);
56 MKSID (well_known_authenticated_users_sid, "S-1-5-11",
57 SECURITY_NT_AUTHORITY, 1, SECURITY_AUTHENTICATED_USER_RID);
58 MKSID (well_known_this_org_sid, "S-1-5-15",
59 SECURITY_NT_AUTHORITY, 1, 15);
60 MKSID (well_known_system_sid, "S-1-5-18",
61 SECURITY_NT_AUTHORITY, 1, SECURITY_LOCAL_SYSTEM_RID);
62 MKSID (well_known_builtin_sid, "S-1-5-32",
63 SECURITY_NT_AUTHORITY, 1, SECURITY_BUILTIN_DOMAIN_RID);
64 MKSID (well_known_admins_sid, "S-1-5-32-544",
65 SECURITY_NT_AUTHORITY, 2, SECURITY_BUILTIN_DOMAIN_RID,
66 DOMAIN_ALIAS_RID_ADMINS);
67 MKSID (well_known_users_sid, "S-1-5-32-545",
68 SECURITY_NT_AUTHORITY, 2, SECURITY_BUILTIN_DOMAIN_RID,
69 DOMAIN_ALIAS_RID_USERS);
70 MKSID (fake_logon_sid, "S-1-5-5-0-0",
71 SECURITY_NT_AUTHORITY, 3, SECURITY_LOGON_IDS_RID, 0, 0);
72 MKSID (mandatory_medium_integrity_sid, "S-1-16-8192",
73 SECURITY_MANDATORY_LABEL_AUTHORITY, 1, SECURITY_MANDATORY_MEDIUM_RID);
74 MKSID (mandatory_high_integrity_sid, "S-1-16-12288",
75 SECURITY_MANDATORY_LABEL_AUTHORITY, 1, SECURITY_MANDATORY_HIGH_RID);
76 MKSID (mandatory_system_integrity_sid, "S-1-16-16384",
77 SECURITY_MANDATORY_LABEL_AUTHORITY, 1, SECURITY_MANDATORY_SYSTEM_RID);
78 /* UNIX accounts on a Samba server have the SID prefix "S-1-22-1" */
79 #define SECURITY_SAMBA_UNIX_AUTHORITY {0,0,0,0,0,22}
80 MKSID (well_known_samba_unix_user_fake_sid, "S-1-22-1-0",
81 SECURITY_SAMBA_UNIX_AUTHORITY, 2, 1, 0);
84 cygpsid::operator== (const char *nsidstr) const
86 cygsid nsid (nsidstr);
91 cygpsid::get_id (BOOL search_grp, int *type)
93 /* First try to get SID from group, then passwd */
94 __uid32_t id = ILLEGAL_UID;
99 if (cygheap->user.groups.pgsid == psid)
101 else if ((gr = internal_getgrsid (*this)))
103 if (id != ILLEGAL_UID)
110 if (!search_grp || type)
113 if (*this == cygheap->user.sid ())
115 else if ((pw = internal_getpwsid (*this)))
117 if (id != ILLEGAL_UID && type)
124 cygpsid::string (PWCHAR nsidstr) const
128 if (!psid || !nsidstr)
130 RtlInitEmptyUnicodeString (&sid, nsidstr, 256);
131 RtlConvertSidToUnicodeString (&sid, psid, FALSE);
136 cygpsid::string (char *nsidstr) const
141 if (!psid || !nsidstr)
143 strcpy (nsidstr, "S-1-");
144 t = nsidstr + sizeof ("S-1-") - 1;
145 t += __small_sprintf (t, "%u", RtlIdentifierAuthoritySid (psid)->Value[5]);
146 for (i = 0; i < *RtlSubAuthorityCountSid (psid); ++i)
147 t += __small_sprintf (t, "-%lu", *RtlSubAuthoritySid (psid, i));
152 cygsid::get_sid (DWORD s, DWORD cnt, DWORD *r, bool well_known)
155 SID_IDENTIFIER_AUTHORITY sid_auth = { SECURITY_NULL_SID_AUTHORITY };
156 # define SECURITY_NT_AUTH 5
158 if (s > 255 || cnt < 1 || cnt > 8)
163 sid_auth.Value[5] = s;
165 RtlInitializeSid (psid, &sid_auth, cnt);
166 for (i = 0; i < cnt; ++i)
167 memcpy ((char *) psid + 8 + sizeof (DWORD) * i, &r[i], sizeof (DWORD));
168 /* If the well_known flag isn't set explicitely, we check the SID
169 for being a well-known SID ourselves. That's necessary because this
170 cygsid is created from a SID string, usually from /etc/passwd or
171 /etc/group. The calling code just doesn't know if the SID is well-known
172 or not. All SIDs are well-known SIDs, except those in the non-unique NT
175 well_known_sid = well_known;
177 well_known_sid = (s != SECURITY_NT_AUTH
178 || r[0] != SECURITY_NT_NON_UNIQUE_RID);
183 cygsid::getfromstr (const char *nsidstr, bool well_known)
189 if (nsidstr && !strncmp (nsidstr, "S-1-", 4))
191 s = strtoul (nsidstr + 4, &lasts, 10);
192 while (cnt < 8 && *lasts == '-')
193 r[cnt++] = strtoul (lasts + 1, &lasts, 10);
195 return get_sid (s, cnt, r, well_known);
197 return psid = NO_SID;
201 cygsid::getfrompw (const struct passwd *pw)
203 char *sp = (pw && pw->pw_gecos) ? strrchr (pw->pw_gecos, ',') : NULL;
204 return (*this = sp ? sp + 1 : sp) != NULL;
208 cygsid::getfromgr (const struct __group32 *gr)
210 char *sp = (gr && gr->gr_passwd) ? gr->gr_passwd : NULL;
211 return (*this = sp) != NULL;
215 cygsidlist::alloc_sids (int n)
218 return (cygsid *) cmalloc (HEAP_STR, n * sizeof (cygsid));
224 cygsidlist::free_sids ()
230 type = cygsidlist_empty;
234 cygsidlist::add (const PSID nsi, bool well_known)
240 cygsid *tmp = new cygsid [2 * maxcnt];
244 for (int i = 0; i < cnt; ++i)
257 get_sids_info (cygpsid owner_sid, cygpsid group_sid, __uid32_t * uidret, __gid32_t * gidret)
260 struct __group32 *gr = NULL;
263 owner_sid.debug_print ("get_sids_info: owner SID =");
264 group_sid.debug_print ("get_sids_info: group SID =");
266 if (group_sid == cygheap->user.groups.pgsid)
267 *gidret = myself->gid;
268 else if ((gr = internal_getgrsid (group_sid)))
269 *gidret = gr->gr_gid;
271 *gidret = ILLEGAL_GID;
273 if (owner_sid == cygheap->user.sid ())
275 *uidret = myself->uid;
276 if (*gidret == myself->gid)
279 ret = (internal_getgroups (0, NULL, &group_sid) > 0);
281 else if ((pw = internal_getpwsid (owner_sid)))
283 *uidret = pw->pw_uid;
284 if (gr || (*gidret != ILLEGAL_GID
285 && (gr = internal_getgrgid (*gidret))))
286 for (int idx = 0; gr->gr_mem[idx]; ++idx)
287 if ((ret = strcasematch (pw->pw_name, gr->gr_mem[idx])))
291 *uidret = ILLEGAL_UID;
297 security_descriptor::malloc (size_t nsize)
300 if ((psd = (PSECURITY_DESCRIPTOR) ::malloc (nsize)))
306 security_descriptor::realloc (size_t nsize)
308 PSECURITY_DESCRIPTOR tmp;
310 /* Can't re-use buffer allocated by GetSecurityInfo. */
313 if (!(tmp = (PSECURITY_DESCRIPTOR) ::realloc (psd, nsize)))
320 security_descriptor::free ()
336 /* Index must match the corresponding foo_PRIVILEGE value, see security.h. */
337 static const struct {
339 bool high_integrity; /* UAC: High Mandatory Label required to
340 be allowed to enable this privilege in
346 { SE_CREATE_TOKEN_NAME, true },
347 { SE_ASSIGNPRIMARYTOKEN_NAME, true },
348 { SE_LOCK_MEMORY_NAME, false },
349 { SE_INCREASE_QUOTA_NAME, true },
350 { SE_MACHINE_ACCOUNT_NAME, false },
351 { SE_TCB_NAME, true },
352 { SE_SECURITY_NAME, true },
353 { SE_TAKE_OWNERSHIP_NAME, true },
354 { SE_LOAD_DRIVER_NAME, true },
355 { SE_SYSTEM_PROFILE_NAME, true },
356 { SE_SYSTEMTIME_NAME, true },
357 { SE_PROF_SINGLE_PROCESS_NAME, true },
358 { SE_INC_BASE_PRIORITY_NAME, true },
359 { SE_CREATE_PAGEFILE_NAME, true },
360 { SE_CREATE_PERMANENT_NAME, false },
361 { SE_BACKUP_NAME, true },
362 { SE_RESTORE_NAME, true },
363 { SE_SHUTDOWN_NAME, false },
364 { SE_DEBUG_NAME, true },
365 { SE_AUDIT_NAME, false },
366 { SE_SYSTEM_ENVIRONMENT_NAME, true },
367 { SE_CHANGE_NOTIFY_NAME, false },
368 { SE_REMOTE_SHUTDOWN_NAME, true },
369 { SE_UNDOCK_NAME, false },
370 { SE_SYNC_AGENT_NAME, false },
371 { SE_ENABLE_DELEGATION_NAME, false },
372 { SE_MANAGE_VOLUME_NAME, true },
373 { SE_IMPERSONATE_NAME, true },
374 { SE_CREATE_GLOBAL_NAME, false },
375 { SE_TRUSTED_CREDMAN_ACCESS_NAME, false },
376 { SE_RELABEL_NAME, true },
377 { SE_INCREASE_WORKING_SET_NAME, false },
378 { SE_TIME_ZONE_NAME, true },
379 { SE_CREATE_SYMBOLIC_LINK_NAME, true }
383 privilege_luid (const PWCHAR pname, LUID &luid, bool &high_integrity)
386 for (idx = SE_CREATE_TOKEN_PRIVILEGE;
387 idx <= SE_MAX_WELL_KNOWN_PRIVILEGE;
389 if (!wcscmp (cygpriv[idx].name, pname))
393 high_integrity = cygpriv[idx].high_integrity;
399 static const wchar_t *
400 privilege_name (const LUID &priv_luid)
402 if (priv_luid.HighPart || priv_luid.LowPart < SE_CREATE_TOKEN_PRIVILEGE
403 || priv_luid.LowPart > SE_MAX_WELL_KNOWN_PRIVILEGE)
404 return L"<unknown privilege>";
405 return cygpriv[priv_luid.LowPart].name;
409 set_privilege (HANDLE token, DWORD privilege, bool enable)
412 TOKEN_PRIVILEGES new_priv, orig_priv;
416 new_priv.PrivilegeCount = 1;
417 new_priv.Privileges[0].Luid.HighPart = 0L;
418 new_priv.Privileges[0].Luid.LowPart = privilege;
419 new_priv.Privileges[0].Attributes = enable ? SE_PRIVILEGE_ENABLED : 0;
421 status = NtAdjustPrivilegesToken (token, FALSE, &new_priv, sizeof orig_priv,
423 if (!NT_SUCCESS (status))
425 __seterrno_from_nt_status (status);
429 /* If orig_priv.PrivilegeCount is 0, the privilege hasn't been changed. */
430 if (!orig_priv.PrivilegeCount)
431 ret = enable ? 1 : 0;
433 ret = (orig_priv.Privileges[0].Attributes & SE_PRIVILEGE_ENABLED) ? 1 : 0;
437 debug_printf ("%d = set_privilege((token %x) %W, %d)", ret, token,
438 privilege_name (new_priv.Privileges[0].Luid), enable);
442 /* This is called very early in process initialization. The code must
443 not depend on anything. */
445 set_cygwin_privileges (HANDLE token)
447 /* Setting these rights at process startup allows processes running under
448 user tokens which are in the administrstors group to have root-like
450 /* Allow to access all files, independent of their ACL settings. */
451 set_privilege (token, SE_RESTORE_PRIVILEGE, true);
452 set_privilege (token, SE_BACKUP_PRIVILEGE, true);
453 /* Allow full access to other user's processes. */
454 set_privilege (token, SE_DEBUG_PRIVILEGE, true);
456 /* Allow to create global shared memory. This isn't required anymore since
457 Cygwin 1.7. It uses its own subdirectories in the global NT namespace
458 which isn't affected by the SE_CREATE_GLOBAL_PRIVILEGE restriction. */
459 if (wincap.has_create_global_privilege ())
460 set_privilege (token, SE_CREATE_GLOBAL_PRIVILEGE, true);
464 /* Function to return a common SECURITY_DESCRIPTOR that
465 allows all access. */
467 static inline PSECURITY_DESCRIPTOR
470 static NO_COPY SECURITY_DESCRIPTOR sd;
471 static NO_COPY PSECURITY_DESCRIPTOR null_sdp;
475 RtlCreateSecurityDescriptor (&sd, SECURITY_DESCRIPTOR_REVISION);
476 RtlSetDaclSecurityDescriptor (&sd, TRUE, NULL, FALSE);
482 /* Initialize global security attributes.
483 Called from dcrt0.cc (_dll_crt0). */
486 init_global_security ()
488 sec_none.nLength = sec_none_nih.nLength =
489 sec_all.nLength = sec_all_nih.nLength = sizeof (SECURITY_ATTRIBUTES);
490 sec_none.bInheritHandle = sec_all.bInheritHandle = TRUE;
491 sec_none_nih.bInheritHandle = sec_all_nih.bInheritHandle = FALSE;
492 sec_none.lpSecurityDescriptor = sec_none_nih.lpSecurityDescriptor = NULL;
493 sec_all.lpSecurityDescriptor = sec_all_nih.lpSecurityDescriptor =
498 sec_acl (PACL acl, bool original, bool admins, PSID sid1, PSID sid2, DWORD access2)
501 size_t acl_len = MAX_DACL_LEN (5);
506 if ((unsigned long) acl % 4)
507 api_fatal ("Incorrectly aligned incoming ACL buffer!");
509 status = RtlCreateAcl (acl, acl_len, ACL_REVISION);
510 if (!NT_SUCCESS (status))
512 debug_printf ("RtlCreateAcl: %p", status);
517 status = RtlAddAccessAllowedAce (acl, ACL_REVISION, GENERIC_ALL, sid1);
518 if (!NT_SUCCESS (status))
519 debug_printf ("RtlAddAccessAllowedAce(sid1) %p", status);
521 if (original && (psid = cygheap->user.saved_sid ())
522 && psid != sid1 && psid != well_known_system_sid)
524 status = RtlAddAccessAllowedAce (acl, ACL_REVISION, GENERIC_ALL, psid);
525 if (!NT_SUCCESS (status))
526 debug_printf ("RtlAddAccessAllowedAce(original) %p", status);
530 status = RtlAddAccessAllowedAce (acl, ACL_REVISION, access2, sid2);
531 if (!NT_SUCCESS (status))
532 debug_printf ("RtlAddAccessAllowedAce(sid2) %p", status);
536 status = RtlAddAccessAllowedAce (acl, ACL_REVISION, GENERIC_ALL,
537 well_known_admins_sid);
538 if (!NT_SUCCESS (status))
539 debug_printf ("RtlAddAccessAllowedAce(admin) %p", status);
541 status = RtlAddAccessAllowedAce (acl, ACL_REVISION, GENERIC_ALL,
542 well_known_system_sid);
543 if (!NT_SUCCESS (status))
544 debug_printf ("RtlAddAccessAllowedAce(system) %p", status);
545 status = RtlFirstFreeAce (acl, &pAce);
546 if (NT_SUCCESS (status) && pAce)
547 acl->AclSize = (char *) pAce - (char *) acl;
549 debug_printf ("RtlFirstFreeAce: %p", status);
554 PSECURITY_ATTRIBUTES __stdcall
555 __sec_user (PVOID sa_buf, PSID sid1, PSID sid2, DWORD access2, BOOL inherit)
557 PSECURITY_ATTRIBUTES psa = (PSECURITY_ATTRIBUTES) sa_buf;
558 PSECURITY_DESCRIPTOR psd = (PSECURITY_DESCRIPTOR)
559 ((char *) sa_buf + sizeof (*psa));
560 PACL acl = (PACL) ((char *) sa_buf + sizeof (*psa) + sizeof (*psd));
564 if ((unsigned long) sa_buf % 4)
565 api_fatal ("Incorrectly aligned incoming SA buffer!");
567 if (!sec_acl (acl, true, true, sid1, sid2, access2))
568 return inherit ? &sec_none : &sec_none_nih;
570 RtlCreateSecurityDescriptor (psd, SECURITY_DESCRIPTOR_REVISION);
571 status = RtlSetDaclSecurityDescriptor (psd, TRUE, acl, FALSE);
572 if (!NT_SUCCESS (status))
573 debug_printf ("RtlSetDaclSecurityDescriptor %p", status);
575 psa->nLength = sizeof (SECURITY_ATTRIBUTES);
576 psa->lpSecurityDescriptor = psd;
577 psa->bInheritHandle = inherit;
581 /* Helper function to create an event security descriptor which only allows
582 specific access to everyone. Only the creating process has all access
586 _everyone_sd (void *buf, ACCESS_MASK access)
589 PSECURITY_DESCRIPTOR psd = (PSECURITY_DESCRIPTOR) buf;
593 RtlCreateSecurityDescriptor (psd, SECURITY_DESCRIPTOR_REVISION);
594 PACL dacl = (PACL) (psd + 1);
595 RtlCreateAcl (dacl, MAX_DACL_LEN (1), ACL_REVISION);
596 status = RtlAddAccessAllowedAce (dacl, ACL_REVISION, access,
597 well_known_world_sid);
598 if (!NT_SUCCESS (status))
600 debug_printf ("RtlAddAccessAllowedAce: %p", status);
604 status = RtlFirstFreeAce (dacl, &ace);
605 if (!NT_SUCCESS (status))
607 debug_printf ("RtlFirstFreeAce: %p", status);
610 dacl->AclSize = (char *) ace - (char *) dacl;
611 RtlSetDaclSecurityDescriptor (psd, TRUE, dacl, FALSE);