874 lines
		
	
	
		
			25 KiB
		
	
	
	
		
			C++
		
	
	
	
			
		
		
	
	
			874 lines
		
	
	
		
			25 KiB
		
	
	
	
		
			C++
		
	
	
	
| /* sec_helper.cc: NT security helper functions
 | |
| 
 | |
|    Written by Corinna Vinschen <corinna@vinschen.de>
 | |
| 
 | |
| This file is part of Cygwin.
 | |
| 
 | |
| This software is a copyrighted work licensed under the terms of the
 | |
| Cygwin license.  Please consult the file "CYGWIN_LICENSE" for
 | |
| details. */
 | |
| 
 | |
| #include "winsup.h"
 | |
| #include <stdlib.h>
 | |
| #include <stdarg.h>
 | |
| #include <cygwin/acl.h>
 | |
| #include <sys/queue.h>
 | |
| #include <authz.h>
 | |
| #include <wchar.h>
 | |
| #include "cygerrno.h"
 | |
| #include "security.h"
 | |
| #include "path.h"
 | |
| #include "fhandler.h"
 | |
| #include "dtable.h"
 | |
| #include "pinfo.h"
 | |
| #include "cygheap.h"
 | |
| #include "ntdll.h"
 | |
| #include "ldap.h"
 | |
| 
 | |
| /* General purpose security attribute objects for global use. */
 | |
| static NO_COPY_RO SECURITY_DESCRIPTOR null_sdp =
 | |
| 	{ SECURITY_DESCRIPTOR_REVISION, 0, SE_DACL_PRESENT,
 | |
| 	  NULL, NULL, NULL, NULL };
 | |
| SECURITY_ATTRIBUTES NO_COPY_RO sec_none =
 | |
| 	{ sizeof (SECURITY_ATTRIBUTES), NULL, TRUE };
 | |
| SECURITY_ATTRIBUTES NO_COPY_RO sec_none_nih =
 | |
| 	{ sizeof (SECURITY_ATTRIBUTES), NULL, FALSE };
 | |
| SECURITY_ATTRIBUTES NO_COPY_RO sec_all =
 | |
| 	{ sizeof (SECURITY_ATTRIBUTES), &null_sdp, TRUE };
 | |
| SECURITY_ATTRIBUTES NO_COPY_RO sec_all_nih =
 | |
| 	{ sizeof (SECURITY_ATTRIBUTES), &null_sdp, FALSE };
 | |
| 
 | |
| MKSID (well_known_null_sid, "S-1-0-0",
 | |
|        SECURITY_NULL_SID_AUTHORITY, 1, SECURITY_NULL_RID);
 | |
| MKSID (well_known_world_sid, "S-1-1-0",
 | |
|        SECURITY_WORLD_SID_AUTHORITY, 1, SECURITY_WORLD_RID);
 | |
| MKSID (well_known_local_sid, "S-1-2-0",
 | |
|        SECURITY_LOCAL_SID_AUTHORITY, 1, SECURITY_LOCAL_RID);
 | |
| MKSID (well_known_console_logon_sid, "S-1-2-1",
 | |
|        SECURITY_LOCAL_SID_AUTHORITY, 1, 1);
 | |
| MKSID (well_known_creator_owner_sid, "S-1-3-0",
 | |
|        SECURITY_CREATOR_SID_AUTHORITY, 1, SECURITY_CREATOR_OWNER_RID);
 | |
| MKSID (well_known_creator_group_sid, "S-1-3-1",
 | |
|        SECURITY_CREATOR_SID_AUTHORITY, 1, SECURITY_CREATOR_GROUP_RID);
 | |
| MKSID (well_known_dialup_sid, "S-1-5-1",
 | |
|        SECURITY_NT_AUTHORITY, 1, SECURITY_DIALUP_RID);
 | |
| MKSID (well_known_network_sid, "S-1-5-2",
 | |
|        SECURITY_NT_AUTHORITY, 1, SECURITY_NETWORK_RID);
 | |
| MKSID (well_known_batch_sid, "S-1-5-3",
 | |
|        SECURITY_NT_AUTHORITY, 1, SECURITY_BATCH_RID);
 | |
| MKSID (well_known_interactive_sid, "S-1-5-4",
 | |
|        SECURITY_NT_AUTHORITY, 1, SECURITY_INTERACTIVE_RID);
 | |
| MKSID (well_known_service_sid, "S-1-5-6",
 | |
|        SECURITY_NT_AUTHORITY, 1, SECURITY_SERVICE_RID);
 | |
| MKSID (well_known_authenticated_users_sid, "S-1-5-11",
 | |
|        SECURITY_NT_AUTHORITY, 1, SECURITY_AUTHENTICATED_USER_RID);
 | |
| MKSID (well_known_this_org_sid, "S-1-5-15",
 | |
|        SECURITY_NT_AUTHORITY, 1, 15);
 | |
| MKSID (well_known_system_sid, "S-1-5-18",
 | |
|        SECURITY_NT_AUTHORITY, 1, SECURITY_LOCAL_SYSTEM_RID);
 | |
| MKSID (well_known_local_service_sid, "S-1-5-19",
 | |
|        SECURITY_NT_AUTHORITY, 1, SECURITY_LOCAL_SERVICE_RID);
 | |
| MKSID (well_known_network_service_sid, "S-1-5-20",
 | |
|        SECURITY_NT_AUTHORITY, 1, SECURITY_NETWORK_SERVICE_RID);
 | |
| MKSID (well_known_builtin_sid, "S-1-5-32",
 | |
|        SECURITY_NT_AUTHORITY, 1, SECURITY_BUILTIN_DOMAIN_RID);
 | |
| MKSID (well_known_admins_sid, "S-1-5-32-544",
 | |
|        SECURITY_NT_AUTHORITY, 2, SECURITY_BUILTIN_DOMAIN_RID,
 | |
| 				 DOMAIN_ALIAS_RID_ADMINS);
 | |
| MKSID (well_known_users_sid, "S-1-5-32-545",
 | |
|        SECURITY_NT_AUTHORITY, 2, SECURITY_BUILTIN_DOMAIN_RID,
 | |
| 				 DOMAIN_ALIAS_RID_USERS);
 | |
| MKSID (trusted_installer_sid,
 | |
|        "S-1-5-80-956008885-3418522649-1831038044-1853292631-2271478464",
 | |
|        SECURITY_NT_AUTHORITY, SECURITY_SERVICE_ID_RID_COUNT,
 | |
|        SECURITY_SERVICE_ID_BASE_RID, 956008885U, 3418522649U, 1831038044U,
 | |
|        1853292631U, 2271478464U);
 | |
| MKSID (mandatory_medium_integrity_sid, "S-1-16-8192",
 | |
|        SECURITY_MANDATORY_LABEL_AUTHORITY, 1, SECURITY_MANDATORY_MEDIUM_RID);
 | |
| MKSID (mandatory_high_integrity_sid, "S-1-16-12288",
 | |
|        SECURITY_MANDATORY_LABEL_AUTHORITY, 1, SECURITY_MANDATORY_HIGH_RID);
 | |
| MKSID (mandatory_system_integrity_sid, "S-1-16-16384",
 | |
|        SECURITY_MANDATORY_LABEL_AUTHORITY, 1, SECURITY_MANDATORY_SYSTEM_RID);
 | |
| /* UNIX accounts on a Samba server have the SID prefix "S-1-22-1" */
 | |
| #define SECURITY_SAMBA_UNIX_AUTHORITY {0,0,0,0,0,22}
 | |
| MKSID (well_known_samba_unix_user_fake_sid, "S-1-22-1-0",
 | |
|        SECURITY_SAMBA_UNIX_AUTHORITY, 2, 1, 0);
 | |
| 
 | |
| bool
 | |
| cygpsid::operator== (const char *nsidstr) const
 | |
| {
 | |
|   cygsid nsid (nsidstr);
 | |
|   return psid == nsid;
 | |
| }
 | |
| 
 | |
| uid_t
 | |
| cygpsid::get_id (BOOL search_grp, int *type, cyg_ldap *pldap)
 | |
| {
 | |
|     /* First try to get SID from group, then passwd */
 | |
|   uid_t id = ILLEGAL_UID;
 | |
| 
 | |
|   if (search_grp)
 | |
|     {
 | |
|       struct group *gr;
 | |
|       if (cygheap->user.groups.pgsid == psid)
 | |
| 	id = myself->gid;
 | |
|       else if (sid_id_auth (psid) == 22 && cygheap->pg.nss_grp_db ())
 | |
| 	{
 | |
| 	  /* Samba UNIX group?  Try to map to Cygwin gid.  If there's no
 | |
| 	     mapping in the cache, try to fetch it from the configured
 | |
| 	     RFC 2307 domain (see last comment in cygheap_domain_info::init()
 | |
| 	     for more information) and add it to the mapping cache.
 | |
| 	     If this is a user, not a group, make sure to skip the subsequent
 | |
| 	     internal_getgrsid call, otherwise we end up with a fake group
 | |
| 	     entry for a UNIX user account. */
 | |
| 	  if (sid_sub_auth (psid, 0) == 2)
 | |
| 	    {
 | |
| 	      gid_t gid = sid_sub_auth_rid (psid);
 | |
| 	      gid_t map_gid = cygheap->ugid_cache.get_gid (gid);
 | |
| 	      if (map_gid == ILLEGAL_GID)
 | |
| 		{
 | |
| 		  if (pldap->open (cygheap->dom.get_rfc2307_domain ())
 | |
| 		      == NO_ERROR)
 | |
| 		    map_gid = pldap->remap_gid (gid);
 | |
| 		  if (map_gid == ILLEGAL_GID) 
 | |
| 		    map_gid = MAP_UNIX_TO_CYGWIN_ID (gid);
 | |
| 		  cygheap->ugid_cache.add_gid (gid, map_gid);
 | |
| 		}
 | |
| 	      id = (uid_t) map_gid;
 | |
| 	    }
 | |
| 	}
 | |
|       else if ((gr = internal_getgrsid (*this, pldap)))
 | |
| 	id = gr->gr_gid;
 | |
|       if ((gid_t) id != ILLEGAL_GID)
 | |
| 	{
 | |
| 	  if (type)
 | |
| 	    *type = GROUP;
 | |
| 	  return id;
 | |
| 	}
 | |
|     }
 | |
|   if (!search_grp || type)
 | |
|     {
 | |
|       struct passwd *pw;
 | |
|       if (*this == cygheap->user.sid ())
 | |
| 	id = myself->uid;
 | |
|       else if (sid_id_auth (psid) == 22 && sid_sub_auth (psid, 0) == 1
 | |
| 	       && cygheap->pg.nss_pwd_db ())
 | |
| 	{
 | |
| 	  /* Samba UNIX user.  See comment above. */
 | |
| 	  uid_t uid = sid_sub_auth_rid (psid);
 | |
| 	  uid_t map_uid = cygheap->ugid_cache.get_uid (uid);
 | |
| 	  if (map_uid == ILLEGAL_UID)
 | |
| 	    {
 | |
| 	      if (pldap->open (cygheap->dom.get_rfc2307_domain ()) == NO_ERROR)
 | |
| 		map_uid = pldap->remap_uid (uid);
 | |
| 	      if (map_uid == ILLEGAL_UID)
 | |
| 		map_uid = MAP_UNIX_TO_CYGWIN_ID (uid);
 | |
| 	      cygheap->ugid_cache.add_uid (uid, map_uid);
 | |
| 	    }
 | |
| 	  id = map_uid;
 | |
| 	}
 | |
|       else if ((pw = internal_getpwsid (*this, pldap)))
 | |
| 	id = pw->pw_uid;
 | |
|       if (id != ILLEGAL_UID && type)
 | |
| 	*type = USER;
 | |
|     }
 | |
|   return id;
 | |
| }
 | |
| 
 | |
| PWCHAR
 | |
| cygpsid::pstring (PWCHAR nsidstr) const
 | |
| {
 | |
|   UNICODE_STRING sid;
 | |
| 
 | |
|   if (!psid || !nsidstr)
 | |
|     return NULL;
 | |
|   RtlInitEmptyUnicodeString (&sid, nsidstr, 256);
 | |
|   RtlConvertSidToUnicodeString (&sid, psid, FALSE);
 | |
|   return nsidstr + sid.Length / sizeof (WCHAR);
 | |
| }
 | |
| 
 | |
| PWCHAR
 | |
| cygpsid::string (PWCHAR nsidstr) const
 | |
| {
 | |
|   if (pstring (nsidstr))
 | |
|     return nsidstr;
 | |
|   return NULL;
 | |
| }
 | |
| 
 | |
| char *
 | |
| cygpsid::pstring (char *nsidstr) const
 | |
| {
 | |
|   char *t;
 | |
|   DWORD i;
 | |
| 
 | |
|   if (!psid || !nsidstr)
 | |
|     return NULL;
 | |
|   strcpy (nsidstr, "S-1-");
 | |
|   t = nsidstr + sizeof ("S-1-") - 1;
 | |
|   t += __small_sprintf (t, "%u", sid_id_auth (psid));
 | |
|   for (i = 0; i < sid_sub_auth_count (psid); ++i)
 | |
|     t += __small_sprintf (t, "-%lu", sid_sub_auth (psid, i));
 | |
|   return t;
 | |
| }
 | |
| 
 | |
| char *
 | |
| cygpsid::string (char *nsidstr) const
 | |
| {
 | |
|   if (pstring (nsidstr))
 | |
|     return nsidstr;
 | |
|   return NULL;
 | |
| }
 | |
| 
 | |
| PSID
 | |
| cygsid::get_sid (DWORD s, DWORD cnt, DWORD *r, bool well_known)
 | |
| {
 | |
|   DWORD i;
 | |
|   SID_IDENTIFIER_AUTHORITY sid_auth = { SECURITY_NULL_SID_AUTHORITY };
 | |
| # define SECURITY_NT_AUTH 5
 | |
| 
 | |
|   /* 2015-10-22: Note that we let slip SIDs with a subauthority count of 0.
 | |
|      There are systems, which generate the SID S-1-0 as group ownership SID,
 | |
|      see https://cygwin.com/ml/cygwin/2015-10/msg00141.html. */
 | |
|   if (s > 255 || cnt > SID_MAX_SUB_AUTHORITIES)
 | |
|     {
 | |
|       psid = NO_SID;
 | |
|       return NULL;
 | |
|     }
 | |
|   sid_auth.Value[5] = s;
 | |
|   set ();
 | |
|   RtlInitializeSid (psid, &sid_auth, cnt);
 | |
|   PISID dsid = (PISID) psid;
 | |
|   for (i = 0; i < cnt; ++i)
 | |
|     dsid->SubAuthority[i] = r[i];
 | |
|   /* If the well_known flag isn't set explicitely, we check the SID
 | |
|      for being a well-known SID ourselves. That's necessary because this
 | |
|      cygsid is created from a SID string, usually from /etc/passwd or
 | |
|      /etc/group.  The calling code just doesn't know if the SID is well-known
 | |
|      or not.  All SIDs are well-known SIDs, except those in the non-unique NT
 | |
|      authority range. */
 | |
|   if (well_known)
 | |
|     well_known_sid = well_known;
 | |
|   else
 | |
|     well_known_sid = (s != SECURITY_NT_AUTH
 | |
| 		      || r[0] != SECURITY_NT_NON_UNIQUE);
 | |
|   return psid;
 | |
| }
 | |
| 
 | |
| const PSID
 | |
| cygsid::getfromstr (PCWSTR nsidstr, bool well_known)
 | |
| {
 | |
|   PWCHAR lasts;
 | |
|   DWORD s, cnt = 0;
 | |
|   DWORD r[SID_MAX_SUB_AUTHORITIES];
 | |
| 
 | |
|   if (nsidstr && !wcsncmp (nsidstr, L"S-1-", 4))
 | |
|     {
 | |
|       s = wcstoul (nsidstr + 4, &lasts, 10);
 | |
|       while (cnt < SID_MAX_SUB_AUTHORITIES && *lasts == '-')
 | |
| 	r[cnt++] = wcstoul (lasts + 1, &lasts, 10);
 | |
|       if (!*lasts)
 | |
| 	return get_sid (s, cnt, r, well_known);
 | |
|     }
 | |
|   return psid = NO_SID;
 | |
| }
 | |
| 
 | |
| const PSID
 | |
| cygsid::getfromstr (const char *nsidstr, bool well_known)
 | |
| {
 | |
|   char *lasts;
 | |
|   DWORD s, cnt = 0;
 | |
|   DWORD r[SID_MAX_SUB_AUTHORITIES];
 | |
| 
 | |
|   if (nsidstr && !strncmp (nsidstr, "S-1-", 4))
 | |
|     {
 | |
|       s = strtoul (nsidstr + 4, &lasts, 10);
 | |
|       while (cnt < SID_MAX_SUB_AUTHORITIES && *lasts == '-')
 | |
| 	r[cnt++] = strtoul (lasts + 1, &lasts, 10);
 | |
|       if (!*lasts)
 | |
| 	return get_sid (s, cnt, r, well_known);
 | |
|     }
 | |
|   return psid = NO_SID;
 | |
| }
 | |
| 
 | |
| const PSID
 | |
| cygsid::create (DWORD auth, DWORD subauth_cnt, ...)
 | |
| {
 | |
|   va_list ap;
 | |
|   PSID sid;
 | |
| 
 | |
|   if (subauth_cnt > SID_MAX_SUB_AUTHORITIES)
 | |
|     return NULL;
 | |
| 
 | |
|   DWORD subauth[subauth_cnt];
 | |
| 
 | |
|   va_start (ap, subauth_cnt);
 | |
|   for (DWORD i = 0; i < subauth_cnt; ++i)
 | |
|     subauth[i] = va_arg (ap, DWORD);
 | |
|   sid = get_sid (auth, subauth_cnt, subauth, false);
 | |
|   va_end (ap);
 | |
|   return sid;
 | |
| }
 | |
| 
 | |
| bool
 | |
| cygsid::append (DWORD rid)
 | |
| {
 | |
|   if (psid == NO_SID)
 | |
|     return false;
 | |
|   PISID dsid = (PISID) psid;
 | |
|   if (dsid->SubAuthorityCount >= SID_MAX_SUB_AUTHORITIES)
 | |
|     return false;
 | |
|   dsid->SubAuthority[dsid->SubAuthorityCount++] = rid;
 | |
|   return true;
 | |
| }
 | |
| 
 | |
| cygsid *
 | |
| cygsidlist::alloc_sids (int n)
 | |
| {
 | |
|   if (n > 0)
 | |
|     return (cygsid *) cmalloc (HEAP_STR, n * sizeof (cygsid));
 | |
|   else
 | |
|     return NULL;
 | |
| }
 | |
| 
 | |
| void
 | |
| cygsidlist::free_sids ()
 | |
| {
 | |
|   if (sids)
 | |
|     cfree (sids);
 | |
|   sids = NULL;
 | |
|   cnt = maxcnt = 0;
 | |
|   type = cygsidlist_empty;
 | |
| }
 | |
| 
 | |
| BOOL
 | |
| cygsidlist::add (const PSID nsi, bool well_known)
 | |
| {
 | |
|   if (contains (nsi))
 | |
|     return TRUE;
 | |
|   if (cnt >= maxcnt)
 | |
|     {
 | |
|       cygsid *tmp = new cygsid [2 * maxcnt];
 | |
|       if (!tmp)
 | |
| 	return FALSE;
 | |
|       maxcnt *= 2;
 | |
|       for (int i = 0; i < cnt; ++i)
 | |
| 	tmp[i] = sids[i];
 | |
|       delete [] sids;
 | |
|       sids = tmp;
 | |
|     }
 | |
|   if (well_known)
 | |
|     sids[cnt++] *= nsi;
 | |
|   else
 | |
|     sids[cnt++] = nsi;
 | |
|   return TRUE;
 | |
| }
 | |
| 
 | |
| PSECURITY_DESCRIPTOR
 | |
| security_descriptor::malloc (size_t nsize)
 | |
| {
 | |
|   free ();
 | |
|   if ((psd = (PSECURITY_DESCRIPTOR) ::malloc (nsize)))
 | |
|     sd_size = nsize;
 | |
|   return psd;
 | |
| }
 | |
| 
 | |
| PSECURITY_DESCRIPTOR
 | |
| security_descriptor::realloc (size_t nsize)
 | |
| {
 | |
|   PSECURITY_DESCRIPTOR tmp;
 | |
| 
 | |
|   /* Can't re-use buffer allocated by GetSecurityInfo. */
 | |
|   if (psd && !sd_size)
 | |
|     free ();
 | |
|   if (!(tmp = (PSECURITY_DESCRIPTOR) ::realloc (psd, nsize)))
 | |
|     return NULL;
 | |
|   sd_size = nsize;
 | |
|   return psd = tmp;
 | |
| }
 | |
| 
 | |
| void
 | |
| security_descriptor::free ()
 | |
| {
 | |
|   if (psd)
 | |
|     {
 | |
|       if (!sd_size)
 | |
| 	LocalFree (psd);
 | |
|       else
 | |
| 	::free (psd);
 | |
|     }
 | |
|   psd = NULL;
 | |
|   sd_size = 0;
 | |
| }
 | |
| 
 | |
| #undef TEXT
 | |
| #define TEXT(q) L##q
 | |
| 
 | |
| /* Index must match the corresponding foo_PRIVILEGE value, see security.h. */
 | |
| static const struct {
 | |
|   const wchar_t *name;
 | |
|   bool		 high_integrity; /* UAC: High Mandatory Label required to
 | |
| 				    be allowed to enable this privilege in
 | |
| 				    the user token. */
 | |
| } cygpriv[] =
 | |
| {
 | |
|   { L"",				false },
 | |
|   { L"",				false },
 | |
|   { SE_CREATE_TOKEN_NAME,		true  },
 | |
|   { SE_ASSIGNPRIMARYTOKEN_NAME,		true  },
 | |
|   { SE_LOCK_MEMORY_NAME,		false },
 | |
|   { SE_INCREASE_QUOTA_NAME,		true  },
 | |
|   { SE_MACHINE_ACCOUNT_NAME,		false },
 | |
|   { SE_TCB_NAME,			true  },
 | |
|   { SE_SECURITY_NAME,			true  },
 | |
|   { SE_TAKE_OWNERSHIP_NAME,		true  },
 | |
|   { SE_LOAD_DRIVER_NAME,		true  },
 | |
|   { SE_SYSTEM_PROFILE_NAME,		true  },
 | |
|   { SE_SYSTEMTIME_NAME,			true  },
 | |
|   { SE_PROF_SINGLE_PROCESS_NAME,	true  },
 | |
|   { SE_INC_BASE_PRIORITY_NAME,		true  },
 | |
|   { SE_CREATE_PAGEFILE_NAME,		true  },
 | |
|   { SE_CREATE_PERMANENT_NAME,		false },
 | |
|   { SE_BACKUP_NAME,			true  },
 | |
|   { SE_RESTORE_NAME,			true  },
 | |
|   { SE_SHUTDOWN_NAME,			false },
 | |
|   { SE_DEBUG_NAME,			true  },
 | |
|   { SE_AUDIT_NAME,			false },
 | |
|   { SE_SYSTEM_ENVIRONMENT_NAME,		true  },
 | |
|   { SE_CHANGE_NOTIFY_NAME,		false },
 | |
|   { SE_REMOTE_SHUTDOWN_NAME,		true  },
 | |
|   { SE_UNDOCK_NAME,			false },
 | |
|   { SE_SYNC_AGENT_NAME,			false },
 | |
|   { SE_ENABLE_DELEGATION_NAME,		false },
 | |
|   { SE_MANAGE_VOLUME_NAME,		true  },
 | |
|   { SE_IMPERSONATE_NAME,		true  },
 | |
|   { SE_CREATE_GLOBAL_NAME,		false },
 | |
|   { SE_TRUSTED_CREDMAN_ACCESS_NAME,	false },
 | |
|   { SE_RELABEL_NAME,			true  },
 | |
|   { SE_INC_WORKING_SET_NAME,		false },
 | |
|   { SE_TIME_ZONE_NAME,			true  },
 | |
|   { SE_CREATE_SYMBOLIC_LINK_NAME,	true  }
 | |
| };
 | |
| 
 | |
| bool
 | |
| privilege_luid (const PWCHAR pname, LUID &luid, bool &high_integrity)
 | |
| {
 | |
|   ULONG idx;
 | |
|   for (idx = SE_CREATE_TOKEN_PRIVILEGE;
 | |
|        idx <= SE_MAX_WELL_KNOWN_PRIVILEGE;
 | |
|        ++idx)
 | |
|     if (!wcscmp (cygpriv[idx].name, pname))
 | |
|       {
 | |
| 	luid.HighPart = 0;
 | |
| 	luid.LowPart = idx;
 | |
| 	high_integrity = cygpriv[idx].high_integrity;
 | |
| 	return true;
 | |
|       }
 | |
|   return false;
 | |
| }
 | |
| 
 | |
| static const wchar_t *
 | |
| privilege_name (const LUID &priv_luid)
 | |
| {
 | |
|   if (priv_luid.HighPart || priv_luid.LowPart < SE_CREATE_TOKEN_PRIVILEGE
 | |
|       || priv_luid.LowPart > SE_MAX_WELL_KNOWN_PRIVILEGE)
 | |
|     return L"<unknown privilege>";
 | |
|   return cygpriv[priv_luid.LowPart].name;
 | |
| }
 | |
| 
 | |
| int
 | |
| set_privilege (HANDLE token, DWORD privilege, bool enable)
 | |
| {
 | |
|   int ret = -1;
 | |
|   TOKEN_PRIVILEGES new_priv, orig_priv;
 | |
|   ULONG size;
 | |
|   NTSTATUS status;
 | |
| 
 | |
|   new_priv.PrivilegeCount = 1;
 | |
|   new_priv.Privileges[0].Luid.HighPart = 0L;
 | |
|   new_priv.Privileges[0].Luid.LowPart = privilege;
 | |
|   new_priv.Privileges[0].Attributes = enable ? SE_PRIVILEGE_ENABLED : 0;
 | |
| 
 | |
|   status = NtAdjustPrivilegesToken (token, FALSE, &new_priv, sizeof orig_priv,
 | |
| 				    &orig_priv, &size);
 | |
|   if (!NT_SUCCESS (status))
 | |
|     {
 | |
|       __seterrno_from_nt_status (status);
 | |
|       goto out;
 | |
|     }
 | |
| 
 | |
|   /* If orig_priv.PrivilegeCount is 0, the privilege hasn't been changed. */
 | |
|   if (!orig_priv.PrivilegeCount)
 | |
|     ret = enable ? 1 : 0;
 | |
|   else
 | |
|     ret = (orig_priv.Privileges[0].Attributes & SE_PRIVILEGE_ENABLED) ? 1 : 0;
 | |
| 
 | |
| out:
 | |
|   if (ret < 0)
 | |
|     debug_printf ("%d = set_privilege((token %p) %W, %d)", ret, token,
 | |
| 		  privilege_name (new_priv.Privileges[0].Luid), enable);
 | |
|   return ret;
 | |
| }
 | |
| 
 | |
| /* This is called very early in process initialization.  The code must
 | |
|    not depend on anything. */
 | |
| void
 | |
| set_cygwin_privileges (HANDLE token)
 | |
| {
 | |
|   /* Setting these rights at process startup allows processes running under
 | |
|      user tokens which are in the administrstors group to have root-like
 | |
|      permissions. */
 | |
|   /* Allow to access all files, independent of their ACL settings. */
 | |
|   set_privilege (token, SE_RESTORE_PRIVILEGE, true);
 | |
|   set_privilege (token, SE_BACKUP_PRIVILEGE, true);
 | |
|   /* Allow full access to other user's processes. */
 | |
|   set_privilege (token, SE_DEBUG_PRIVILEGE, true);
 | |
| #if 0
 | |
|   /* Allow to create global shared memory.  This isn't required anymore since
 | |
|      Cygwin 1.7.  It uses its own subdirectories in the global NT namespace
 | |
|      which isn't affected by the SE_CREATE_GLOBAL_PRIVILEGE restriction. */
 | |
|   set_privilege (token, SE_CREATE_GLOBAL_PRIVILEGE, true);
 | |
| #endif
 | |
| }
 | |
| 
 | |
| bool
 | |
| sec_acl (PACL acl, bool original, bool admins, PSID sid1, PSID sid2, DWORD access2)
 | |
| {
 | |
|   NTSTATUS status;
 | |
|   size_t acl_len = MAX_DACL_LEN (5);
 | |
|   LPVOID pAce;
 | |
|   cygpsid psid;
 | |
| 
 | |
| #ifdef DEBUGGING
 | |
|   if ((unsigned long) acl % 4)
 | |
|     api_fatal ("Incorrectly aligned incoming ACL buffer!");
 | |
| #endif
 | |
|   status = RtlCreateAcl (acl, acl_len, ACL_REVISION);
 | |
|   if (!NT_SUCCESS (status))
 | |
|     {
 | |
|       debug_printf ("RtlCreateAcl: %y", status);
 | |
|       return false;
 | |
|     }
 | |
|   if (sid1)
 | |
|     {
 | |
|       status = RtlAddAccessAllowedAce (acl, ACL_REVISION, GENERIC_ALL, sid1);
 | |
|       if (!NT_SUCCESS (status))
 | |
| 	debug_printf ("RtlAddAccessAllowedAce(sid1) %y", status);
 | |
|     }
 | |
|   if (original && (psid = cygheap->user.saved_sid ())
 | |
|       && psid != sid1 && psid != well_known_system_sid)
 | |
|     {
 | |
|       status = RtlAddAccessAllowedAce (acl, ACL_REVISION, GENERIC_ALL, psid);
 | |
|       if (!NT_SUCCESS (status))
 | |
| 	debug_printf ("RtlAddAccessAllowedAce(original) %y", status);
 | |
|     }
 | |
|   if (sid2)
 | |
|     {
 | |
|       status = RtlAddAccessAllowedAce (acl, ACL_REVISION, access2, sid2);
 | |
|       if (!NT_SUCCESS (status))
 | |
| 	debug_printf ("RtlAddAccessAllowedAce(sid2) %y", status);
 | |
|     }
 | |
|   if (admins)
 | |
|     {
 | |
|       status = RtlAddAccessAllowedAce (acl, ACL_REVISION, GENERIC_ALL,
 | |
| 				       well_known_admins_sid);
 | |
|       if (!NT_SUCCESS (status))
 | |
| 	debug_printf ("RtlAddAccessAllowedAce(admin) %y", status);
 | |
|     }
 | |
|   status = RtlAddAccessAllowedAce (acl, ACL_REVISION, GENERIC_ALL,
 | |
| 				   well_known_system_sid);
 | |
|   if (!NT_SUCCESS (status))
 | |
|     debug_printf ("RtlAddAccessAllowedAce(system) %y", status);
 | |
|   status = RtlFirstFreeAce (acl, &pAce);
 | |
|   if (NT_SUCCESS (status) && pAce)
 | |
|     acl->AclSize = (char *) pAce - (char *) acl;
 | |
|   else
 | |
|     debug_printf ("RtlFirstFreeAce: %y", status);
 | |
| 
 | |
|   return true;
 | |
| }
 | |
| 
 | |
| PSECURITY_ATTRIBUTES __reg3
 | |
| __sec_user (PVOID sa_buf, PSID sid1, PSID sid2, DWORD access2, BOOL inherit)
 | |
| {
 | |
|   PSECURITY_ATTRIBUTES psa = (PSECURITY_ATTRIBUTES) sa_buf;
 | |
|   PISECURITY_DESCRIPTOR psd = (PISECURITY_DESCRIPTOR)
 | |
| 			     ((char *) sa_buf + sizeof (*psa));
 | |
|   PACL acl = (PACL) ((char *) sa_buf + sizeof (*psa) + sizeof (*psd));
 | |
|   NTSTATUS status;
 | |
| 
 | |
| #ifdef DEBUGGING
 | |
|   if ((unsigned long) sa_buf % 4)
 | |
|     api_fatal ("Incorrectly aligned incoming SA buffer!");
 | |
| #endif
 | |
|   if (!sec_acl (acl, true, true, sid1, sid2, access2))
 | |
|     return inherit ? &sec_none : &sec_none_nih;
 | |
| 
 | |
|   RtlCreateSecurityDescriptor (psd, SECURITY_DESCRIPTOR_REVISION);
 | |
|   status = RtlSetDaclSecurityDescriptor (psd, TRUE, acl, FALSE);
 | |
|   if (!NT_SUCCESS (status))
 | |
|     debug_printf ("RtlSetDaclSecurityDescriptor %y", status);
 | |
| 
 | |
|   psa->nLength = sizeof (SECURITY_ATTRIBUTES);
 | |
|   psa->lpSecurityDescriptor = psd;
 | |
|   psa->bInheritHandle = inherit;
 | |
|   return psa;
 | |
| }
 | |
| 
 | |
| /* Helper function to create a file security descriptor which allows
 | |
|    full access to admins, system, and the sid given as parameter.  See
 | |
|    try_to_bin for how it's used. */
 | |
| 
 | |
| PSECURITY_DESCRIPTOR
 | |
| _recycler_sd (void *buf, bool users, bool dir)
 | |
| {
 | |
|   NTSTATUS status;
 | |
|   PISECURITY_DESCRIPTOR psd = (PISECURITY_DESCRIPTOR) buf;
 | |
| 
 | |
|   if (!psd)
 | |
|     return NULL;
 | |
|   RtlCreateSecurityDescriptor (psd, SECURITY_DESCRIPTOR_REVISION);
 | |
|   PACL dacl = (PACL) (psd + 1);
 | |
|   RtlCreateAcl (dacl, MAX_DACL_LEN (3), ACL_REVISION);
 | |
|   RtlAddAccessAllowedAceEx (dacl, ACL_REVISION,
 | |
| 			    dir ? SUB_CONTAINERS_AND_OBJECTS_INHERIT
 | |
| 				: NO_INHERITANCE,
 | |
| 			    FILE_ALL_ACCESS, well_known_admins_sid);
 | |
|   RtlAddAccessAllowedAceEx (dacl, ACL_REVISION,
 | |
| 			    dir ? SUB_CONTAINERS_AND_OBJECTS_INHERIT
 | |
| 				: NO_INHERITANCE,
 | |
| 			    FILE_ALL_ACCESS, well_known_system_sid);
 | |
|   if (users)
 | |
|     RtlAddAccessAllowedAceEx (dacl, ACL_REVISION, INHERIT_NO_PROPAGATE,
 | |
| 			      FILE_GENERIC_READ | FILE_GENERIC_EXECUTE
 | |
| 			      | FILE_APPEND_DATA | FILE_WRITE_ATTRIBUTES,
 | |
| 			      well_known_users_sid);
 | |
|   else
 | |
|     RtlAddAccessAllowedAceEx (dacl, ACL_REVISION,
 | |
| 			      dir ? SUB_CONTAINERS_AND_OBJECTS_INHERIT
 | |
| 				  : NO_INHERITANCE,
 | |
| 			      FILE_ALL_ACCESS, cygheap->user.sid ());
 | |
|   LPVOID ace;
 | |
|   status = RtlFirstFreeAce (dacl, &ace);
 | |
|   if (!NT_SUCCESS (status))
 | |
|     {
 | |
|       debug_printf ("RtlFirstFreeAce: %y", status);
 | |
|       return NULL;
 | |
|     }
 | |
|   dacl->AclSize = (char *) ace - (char *) dacl;
 | |
|   RtlSetDaclSecurityDescriptor (psd, TRUE, dacl, FALSE);
 | |
|   /* If the directory DACL is not marked as protected, shell32 thinks
 | |
|      the recycle dir is corrupted.  As soon as Explorer accesses the
 | |
|      Recycler, the user will get a GUI dialog "The Recycle Bin on X:\
 | |
|      is corrupted. Do you want to empty the Recycle Bin for this drive?"
 | |
|      Of course we want to avoid that. */
 | |
|   if (dir)
 | |
|     psd->Control |= SE_DACL_PROTECTED;
 | |
|   return psd;
 | |
| }
 | |
| 
 | |
| /* Helper function to create an event security descriptor which only allows
 | |
|    specific access to everyone.  Only the creating process has all access
 | |
|    rights. */
 | |
| 
 | |
| PSECURITY_DESCRIPTOR
 | |
| _everyone_sd (void *buf, ACCESS_MASK access)
 | |
| {
 | |
|   NTSTATUS status;
 | |
|   PISECURITY_DESCRIPTOR psd = (PISECURITY_DESCRIPTOR) buf;
 | |
| 
 | |
|   if (psd)
 | |
|     {
 | |
|       RtlCreateSecurityDescriptor (psd, SECURITY_DESCRIPTOR_REVISION);
 | |
|       PACL dacl = (PACL) (psd + 1);
 | |
|       RtlCreateAcl (dacl, MAX_DACL_LEN (1), ACL_REVISION);
 | |
|       status = RtlAddAccessAllowedAce (dacl, ACL_REVISION, access,
 | |
| 				       well_known_world_sid);
 | |
|       if (!NT_SUCCESS (status))
 | |
| 	{
 | |
| 	  debug_printf ("RtlAddAccessAllowedAce: %y", status);
 | |
| 	  return NULL;
 | |
| 	}
 | |
|       LPVOID ace;
 | |
|       status = RtlFirstFreeAce (dacl, &ace);
 | |
|       if (!NT_SUCCESS (status))
 | |
| 	{
 | |
| 	  debug_printf ("RtlFirstFreeAce: %y", status);
 | |
| 	  return NULL;
 | |
| 	}
 | |
|       dacl->AclSize = (char *) ace - (char *) dacl;
 | |
|       RtlSetDaclSecurityDescriptor (psd, TRUE, dacl, FALSE);
 | |
|     }
 | |
|   return psd;
 | |
| }
 | |
| 
 | |
| static NO_COPY muto authz_guard;
 | |
| static LUID authz_dummy_luid = { 0 };
 | |
| 
 | |
| class authz_ctx_cache_entry
 | |
| {
 | |
|   SLIST_ENTRY (authz_ctx_cache_entry)	ctx_next;
 | |
|   cygsid				sid;
 | |
|   AUTHZ_CLIENT_CONTEXT_HANDLE		ctx_hdl;
 | |
| 
 | |
|   authz_ctx_cache_entry ()
 | |
|   : sid (NO_SID), ctx_hdl (NULL)
 | |
|   {
 | |
|     ctx_next.sle_next = NULL;
 | |
|   }
 | |
|   authz_ctx_cache_entry (bool)
 | |
|   : sid (NO_SID), ctx_hdl (NULL)
 | |
|   {
 | |
|     ctx_next.sle_next = NULL;
 | |
|   }
 | |
|   void set (PSID psid, AUTHZ_CLIENT_CONTEXT_HANDLE hdl)
 | |
|   {
 | |
|     sid = psid;
 | |
|     ctx_hdl = hdl;
 | |
|   }
 | |
|   bool is (PSID psid) const { return RtlEqualSid (sid, psid); }
 | |
|   AUTHZ_CLIENT_CONTEXT_HANDLE context () const { return ctx_hdl; }
 | |
| 
 | |
|   friend class authz_ctx_cache;
 | |
| };
 | |
| 
 | |
| class authz_ctx_cache
 | |
| {
 | |
|   SLIST_HEAD (, authz_ctx_cache_entry) ctx_list;
 | |
| 
 | |
|   AUTHZ_CLIENT_CONTEXT_HANDLE context (PSID);
 | |
| 
 | |
|   friend class authz_ctx;
 | |
| };
 | |
| 
 | |
| class authz_ctx
 | |
| {
 | |
|   AUTHZ_RESOURCE_MANAGER_HANDLE authz_hdl;
 | |
|   AUTHZ_CLIENT_CONTEXT_HANDLE user_ctx_hdl;
 | |
|   authz_ctx_cache ctx_cache;
 | |
|   operator AUTHZ_RESOURCE_MANAGER_HANDLE ();
 | |
| 
 | |
|   friend class authz_ctx_cache;
 | |
| public:
 | |
|   bool get_user_attribute (mode_t *, PSECURITY_DESCRIPTOR, PSID);
 | |
| };
 | |
| 
 | |
| /* Authz handles are not inheritable. */
 | |
| static NO_COPY authz_ctx authz;
 | |
| 
 | |
| authz_ctx::operator AUTHZ_RESOURCE_MANAGER_HANDLE ()
 | |
| {
 | |
|   if (!authz_hdl)
 | |
|     {
 | |
|       /* Create handle to Authz resource manager */
 | |
|       authz_guard.init ("authz_guard")->acquire ();
 | |
|       if (!authz_hdl
 | |
| 	  && !AuthzInitializeResourceManager (AUTHZ_RM_FLAG_NO_AUDIT,
 | |
| 					      NULL, NULL, NULL, NULL,
 | |
| 					      &authz_hdl))
 | |
| 	debug_printf ("AuthzInitializeResourceManager, %E");
 | |
|       authz_guard.release ();
 | |
|     }
 | |
|   return authz_hdl;
 | |
| }
 | |
| 
 | |
| AUTHZ_CLIENT_CONTEXT_HANDLE
 | |
| authz_ctx_cache::context (PSID user_sid)
 | |
| {
 | |
|   authz_ctx_cache_entry *entry;
 | |
|   AUTHZ_CLIENT_CONTEXT_HANDLE ctx_hdl = NULL;
 | |
| 
 | |
|   SLIST_FOREACH (entry, &ctx_list, ctx_next)
 | |
|     {
 | |
|       if (entry->is (user_sid))
 | |
| 	return entry->context ();
 | |
|     }
 | |
|   entry = new authz_ctx_cache_entry (true);
 | |
|   /* If the user is the current user, prefer to create the context from the
 | |
|      token, as outlined in MSDN. */
 | |
|   if (RtlEqualSid (user_sid, cygheap->user.sid ())
 | |
|       && !AuthzInitializeContextFromToken (0, cygheap->user.issetuid ()
 | |
| 					   ?  cygheap->user.primary_token ()
 | |
| 					   : hProcToken,
 | |
| 					   authz, NULL, authz_dummy_luid,
 | |
| 					   NULL, &ctx_hdl))
 | |
|     debug_printf ("AuthzInitializeContextFromToken, %E");
 | |
|   /* In any other case, create the context from the user SID. */
 | |
|   else if (!AuthzInitializeContextFromSid (0, user_sid, authz, NULL,
 | |
| 					   authz_dummy_luid, NULL, &ctx_hdl))
 | |
|     debug_printf ("AuthzInitializeContextFromSid, %E");
 | |
|   else
 | |
|     {
 | |
|       entry->set (user_sid, ctx_hdl);
 | |
|       authz_guard.acquire ();
 | |
|       SLIST_INSERT_HEAD (&ctx_list, entry, ctx_next);
 | |
|       authz_guard.release ();
 | |
|       return entry->context ();
 | |
|     }
 | |
|   delete entry;
 | |
|   return NULL;
 | |
| }
 | |
| 
 | |
| /* Ask Authz for the effective user permissions of the user with SID user_sid
 | |
|    on the object with security descriptor psd.  We're caching the handles for
 | |
|    the Authz resource manager and the user contexts. */
 | |
| bool
 | |
| authz_ctx::get_user_attribute (mode_t *attribute, PSECURITY_DESCRIPTOR psd,
 | |
| 			       PSID user_sid)
 | |
| {
 | |
|   /* If the owner is the main user of the process token (not some impersonated
 | |
|      user), cache the user context in the global user_ctx_hdl variable. */
 | |
|   AUTHZ_CLIENT_CONTEXT_HANDLE ctx_hdl = NULL;
 | |
|   if (RtlEqualSid (user_sid, cygheap->user.sid ())
 | |
|       && !cygheap->user.issetuid ())
 | |
|     {
 | |
|       if (!user_ctx_hdl)
 | |
| 	{
 | |
| 	  authz_guard.acquire ();
 | |
| 	  if (!AuthzInitializeContextFromToken (0, hProcToken, authz, NULL,
 | |
| 						authz_dummy_luid, NULL,
 | |
| 						&user_ctx_hdl))
 | |
| 	    debug_printf ("AuthzInitializeContextFromToken, %E");
 | |
| 	  authz_guard.release ();
 | |
| 	}
 | |
|       if (user_ctx_hdl)
 | |
| 	ctx_hdl = user_ctx_hdl;
 | |
|     }
 | |
|   if (!ctx_hdl && !(ctx_hdl = ctx_cache.context (user_sid)))
 | |
|     return false;
 | |
|   /* All set, check access. */
 | |
|   ACCESS_MASK access = 0;
 | |
|   DWORD error = 0;
 | |
|   AUTHZ_ACCESS_REQUEST req = {
 | |
|     .DesiredAccess		= MAXIMUM_ALLOWED,
 | |
|     .PrincipalSelfSid		= NULL,
 | |
|     .ObjectTypeList		= NULL,
 | |
|     .ObjectTypeListLength	= 0,
 | |
|     .OptionalArguments		= NULL
 | |
|   };
 | |
|   AUTHZ_ACCESS_REPLY repl = {
 | |
|     .ResultListLength		= 1,
 | |
|     .GrantedAccessMask		= &access,
 | |
|     .SaclEvaluationResults	= NULL,
 | |
|     .Error			= &error
 | |
|   };
 | |
|   if (AuthzAccessCheck (0, ctx_hdl, &req, NULL, psd, NULL, 0, &repl, NULL))
 | |
|     {
 | |
|       if (access & FILE_READ_BITS)
 | |
| 	*attribute |= S_IROTH;
 | |
|       if (access & FILE_WRITE_BITS)
 | |
| 	*attribute |= S_IWOTH;
 | |
|       if (access & FILE_EXEC_BITS)
 | |
| 	*attribute |= S_IXOTH;
 | |
|       return true;
 | |
|     }
 | |
|   return false;
 | |
| }
 | |
| 
 | |
| bool
 | |
| authz_get_user_attribute (mode_t *attribute, PSECURITY_DESCRIPTOR psd,
 | |
| 			  PSID user_sid)
 | |
| {
 | |
|   *attribute = 0;
 | |
|   return authz.get_user_attribute (attribute, psd, user_sid);
 | |
| }
 |