--- /dev/null
+/* sec_auth.cc: NT authentication functions
+
+ Copyright 1997, 1998, 1999, 2000, 2001, 2002, 2003, 2004, 2005,
+ 2006, 2007, 2008 Red Hat, Inc.
+
+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 <wchar.h>
+#include <wininet.h>
+#include <ntsecapi.h>
+#include <dsgetdc.h>
+#include "cygerrno.h"
+#include "security.h"
+#include "path.h"
+#include "fhandler.h"
+#include "dtable.h"
+#include "cygheap.h"
+#include "ntdll.h"
+#include "tls_pbuf.h"
+#include <lm.h>
+#include <iptypes.h>
+#include "pwdgrp.h"
+#include "cyglsa.h"
+#include <cygwin/version.h>
+
+extern "C" void
+cygwin_set_impersonation_token (const HANDLE hToken)
+{
+ debug_printf ("set_impersonation_token (%d)", hToken);
+ cygheap->user.external_token = hToken == INVALID_HANDLE_VALUE ? NO_IMPERSONATION : hToken;
+}
+
+void
+extract_nt_dom_user (const struct passwd *pw, PWCHAR domain, PWCHAR user)
+{
+
+ cygsid psid;
+ DWORD ulen = UNLEN + 1;
+ DWORD dlen = MAX_DOMAIN_NAME_LEN + 1;
+ SID_NAME_USE use;
+
+ debug_printf ("pw_gecos %x (%s)", pw->pw_gecos, pw->pw_gecos);
+
+ if (psid.getfrompw (pw)
+ && LookupAccountSidW (NULL, psid, user, &ulen, domain, &dlen, &use))
+ return;
+
+ char *d, *u, *c;
+ domain[0] = L'\0';
+ sys_mbstowcs (user, UNLEN + 1, pw->pw_name);
+ if ((d = strstr (pw->pw_gecos, "U-")) != NULL &&
+ (d == pw->pw_gecos || d[-1] == ','))
+ {
+ c = strechr (d + 2, ',');
+ if ((u = strechr (d + 2, '\\')) >= c)
+ u = d + 1;
+ else if (u - d <= MAX_DOMAIN_NAME_LEN + 2)
+ sys_mbstowcs (domain, MAX_DOMAIN_NAME_LEN + 1, d + 2, u - d - 1);
+ if (c - u <= UNLEN + 1)
+ sys_mbstowcs (user, UNLEN + 1, u + 1, c - u);
+ }
+}
+
+extern "C" HANDLE
+cygwin_logon_user (const struct passwd *pw, const char *password)
+{
+ if (!pw || !password)
+ {
+ set_errno (EINVAL);
+ return INVALID_HANDLE_VALUE;
+ }
+
+ WCHAR nt_domain[MAX_DOMAIN_NAME_LEN + 1];
+ WCHAR nt_user[UNLEN + 1];
+ PWCHAR passwd;
+ HANDLE hToken;
+ tmp_pathbuf tp;
+
+ extract_nt_dom_user (pw, nt_domain, nt_user);
+ debug_printf ("LogonUserW (%W, %W, ...)", nt_user, nt_domain);
+ sys_mbstowcs (passwd = tp.w_get (), NT_MAX_PATH, password);
+ /* CV 2005-06-08: LogonUser should run under the primary process token,
+ otherwise it returns with ERROR_ACCESS_DENIED. */
+ cygheap->user.deimpersonate ();
+ if (!LogonUserW (nt_user, *nt_domain ? nt_domain : NULL, passwd,
+ LOGON32_LOGON_INTERACTIVE, LOGON32_PROVIDER_DEFAULT,
+ &hToken))
+ {
+ __seterrno ();
+ hToken = INVALID_HANDLE_VALUE;
+ }
+ else if (!SetHandleInformation (hToken,
+ HANDLE_FLAG_INHERIT,
+ HANDLE_FLAG_INHERIT))
+ {
+ __seterrno ();
+ CloseHandle (hToken);
+ hToken = INVALID_HANDLE_VALUE;
+ }
+ cygheap->user.reimpersonate ();
+ debug_printf ("%d = logon_user(%s,...)", hToken, pw->pw_name);
+ return hToken;
+}
+
+static void
+str2lsa (LSA_STRING &tgt, const char *srcstr)
+{
+ tgt.Length = strlen (srcstr);
+ tgt.MaximumLength = tgt.Length + 1;
+ tgt.Buffer = (PCHAR) srcstr;
+}
+
+static void
+str2buf2lsa (LSA_STRING &tgt, char *buf, const char *srcstr)
+{
+ tgt.Length = strlen (srcstr);
+ tgt.MaximumLength = tgt.Length + 1;
+ tgt.Buffer = (PCHAR) buf;
+ memcpy (buf, srcstr, tgt.MaximumLength);
+}
+
+/* The dimension of buf is assumed to be at least strlen(srcstr) + 1,
+ The result will be shorter if the input has multibyte chars */
+void
+str2buf2uni (UNICODE_STRING &tgt, WCHAR *buf, const char *srcstr)
+{
+ tgt.Buffer = (PWCHAR) buf;
+ tgt.MaximumLength = (strlen (srcstr) + 1) * sizeof (WCHAR);
+ tgt.Length = sys_mbstowcs (buf, tgt.MaximumLength / sizeof (WCHAR), srcstr)
+ * sizeof (WCHAR);
+ if (tgt.Length)
+ tgt.Length -= sizeof (WCHAR);
+}
+
+void
+str2uni_cat (UNICODE_STRING &tgt, const char *srcstr)
+{
+ int len = sys_mbstowcs (tgt.Buffer + tgt.Length / sizeof (WCHAR),
+ (tgt.MaximumLength - tgt.Length) / sizeof (WCHAR),
+ srcstr);
+ if (len)
+ tgt.Length += (len - 1) * sizeof (WCHAR);
+ else
+ tgt.Length = tgt.MaximumLength = 0;
+}
+
+static LSA_HANDLE
+open_local_policy ()
+{
+ LSA_OBJECT_ATTRIBUTES oa = { 0, 0, 0, 0, 0, 0 };
+ LSA_HANDLE lsa = INVALID_HANDLE_VALUE;
+
+ NTSTATUS ret = LsaOpenPolicy (NULL, &oa, POLICY_EXECUTE, &lsa);
+ if (ret != STATUS_SUCCESS)
+ __seterrno_from_win_error (LsaNtStatusToWinError (ret));
+ return lsa;
+}
+
+static void
+close_local_policy (LSA_HANDLE &lsa)
+{
+ if (lsa != INVALID_HANDLE_VALUE)
+ LsaClose (lsa);
+ lsa = INVALID_HANDLE_VALUE;
+}
+
+bool
+get_logon_server (PWCHAR domain, WCHAR *server, bool rediscovery)
+{
+ DWORD dret;
+ PDOMAIN_CONTROLLER_INFOW pci;
+ WCHAR *buf;
+ DWORD size = MAX_COMPUTERNAME_LENGTH + 1;
+
+ /* Empty domain is interpreted as local system */
+ if ((GetComputerNameW (server + 2, &size)) &&
+ (!wcscasecmp (domain, server + 2) || !domain[0]))
+ {
+ server[0] = server[1] = L'\\';
+ return true;
+ }
+
+ /* Try to get any available domain controller for this domain */
+ dret = DsGetDcNameW (NULL, domain, NULL, NULL,
+ rediscovery ? DS_FORCE_REDISCOVERY : 0, &pci);
+ if (dret == ERROR_SUCCESS)
+ {
+ wcscpy (server, pci->DomainControllerName);
+ NetApiBufferFree (pci);
+ debug_printf ("DC: rediscovery: %d, server: %W", rediscovery, server);
+ return true;
+ }
+ else if (dret == ERROR_PROC_NOT_FOUND)
+ {
+ /* NT4 w/o DSClient */
+ if (rediscovery)
+ dret = NetGetAnyDCName (NULL, domain, (LPBYTE *) &buf);
+ else
+ dret = NetGetDCName (NULL, domain, (LPBYTE *) &buf);
+ if (dret == NERR_Success)
+ {
+ wcscpy (server, buf);
+ NetApiBufferFree (buf);
+ debug_printf ("NT: rediscovery: %d, server: %W", rediscovery, server);
+ return true;
+ }
+ }
+ __seterrno_from_win_error (dret);
+ return false;
+}
+
+static bool
+get_user_groups (WCHAR *logonserver, cygsidlist &grp_list,
+ PWCHAR user, PWCHAR domain)
+{
+ WCHAR dgroup[MAX_DOMAIN_NAME_LEN + GNLEN + 2];
+ LPGROUP_USERS_INFO_0 buf;
+ DWORD cnt, tot, len;
+ NET_API_STATUS ret;
+
+ /* Look only on logonserver */
+ ret = NetUserGetGroups (logonserver, user, 0, (LPBYTE *) &buf,
+ MAX_PREFERRED_LENGTH, &cnt, &tot);
+ if (ret)
+ {
+ __seterrno_from_win_error (ret);
+ /* It's no error when the user name can't be found. */
+ return ret == NERR_UserNotFound;
+ }
+
+ len = wcslen (domain);
+ wcscpy (dgroup, domain);
+ dgroup[len++] = L'\\';
+
+ for (DWORD i = 0; i < cnt; ++i)
+ {
+ cygsid gsid;
+ DWORD glen = MAX_SID_LEN;
+ WCHAR dom[MAX_DOMAIN_NAME_LEN + 1];
+ DWORD dlen = sizeof (dom);
+ SID_NAME_USE use = SidTypeInvalid;
+
+ wcscpy (dgroup + len, buf[i].grui0_name);
+ if (!LookupAccountNameW (NULL, dgroup, gsid, &glen, dom, &dlen, &use))
+ debug_printf ("LookupAccountName(%W), %E", dgroup);
+ else if (legal_sid_type (use))
+ grp_list += gsid;
+ else
+ debug_printf ("Global group %W invalid. Use: %d", dgroup, use);
+ }
+
+ NetApiBufferFree (buf);
+ return true;
+}
+
+static bool
+is_group_member (PWCHAR logonserver, PWCHAR group, PSID pusersid,
+ cygsidlist &grp_list)
+{
+ LPLOCALGROUP_MEMBERS_INFO_1 buf;
+ DWORD cnt, tot;
+ NET_API_STATUS ret;
+
+ /* Members can be users or global groups */
+ ret = NetLocalGroupGetMembers (logonserver, group, 1, (LPBYTE *) &buf,
+ MAX_PREFERRED_LENGTH, &cnt, &tot, NULL);
+ if (ret)
+ return false;
+
+ bool retval = true;
+ for (DWORD bidx = 0; bidx < cnt; ++bidx)
+ if (EqualSid (pusersid, buf[bidx].lgrmi1_sid))
+ goto done;
+ else
+ {
+ /* The extra test for the group being a global group or a well-known
+ group is necessary, since apparently also aliases (for instance
+ Administrators or Users) can be members of local groups, even
+ though MSDN states otherwise. The GUI refuses to put aliases into
+ local groups, but the CLI interface allows it. However, a normal
+ logon token does not contain groups, in which the user is only
+ indirectly a member by being a member of an alias in this group.
+ So we also should not put them into the token group list.
+ Note: Allowing those groups in our group list renders external
+ tokens invalid, so that it becomes impossible to logon with
+ password and valid logon token. */
+ for (int glidx = 0; glidx < grp_list.count (); ++glidx)
+ if ((buf[bidx].lgrmi1_sidusage == SidTypeGroup
+ || buf[bidx].lgrmi1_sidusage == SidTypeWellKnownGroup)
+ && EqualSid (grp_list.sids[glidx], buf[bidx].lgrmi1_sid))
+ goto done;
+ }
+
+ retval = false;
+ done:
+ NetApiBufferFree (buf);
+ return retval;
+}
+
+static bool
+get_user_local_groups (PWCHAR logonserver, PWCHAR domain,
+ cygsidlist &grp_list, PSID pusersid)
+{
+ LPLOCALGROUP_INFO_0 buf;
+ DWORD cnt, tot;
+ NET_API_STATUS ret;
+
+ ret = NetLocalGroupEnum (logonserver, 0, (LPBYTE *) &buf,
+ MAX_PREFERRED_LENGTH, &cnt, &tot, NULL);
+ if (ret)
+ {
+ __seterrno_from_win_error (ret);
+ return false;
+ }
+
+ WCHAR domlocal_grp[MAX_DOMAIN_NAME_LEN + GNLEN + 2];
+ WCHAR builtin_grp[sizeof ("BUILTIN\\") + GNLEN + 2];
+ PWCHAR dg_ptr, bg_ptr;
+ SID_NAME_USE use;
+
+ dg_ptr = wcpcpy (domlocal_grp, domain);
+ *dg_ptr++ = L'\\';
+ bg_ptr = wcpcpy (builtin_grp, L"BUILTIN\\");
+
+ for (DWORD i = 0; i < cnt; ++i)
+ if (is_group_member (logonserver, buf[i].lgrpi0_name, pusersid, grp_list))
+ {
+ cygsid gsid;
+ DWORD glen = MAX_SID_LEN;
+ WCHAR dom[MAX_DOMAIN_NAME_LEN + 1];
+ DWORD domlen = sizeof (dom);
+ bool builtin = false;
+
+ use = SidTypeInvalid;
+ wcscpy (dg_ptr, buf[i].lgrpi0_name);
+ if (!LookupAccountNameW (NULL, domlocal_grp, gsid, &glen,
+ dom, &domlen, &use))
+ {
+ if (GetLastError () != ERROR_NONE_MAPPED)
+ debug_printf ("LookupAccountName(%W), %E", domlocal_grp);
+ wcscpy (bg_ptr, dg_ptr);
+ if (!LookupAccountNameW (NULL, builtin_grp, gsid, &glen,
+ dom, &domlen, &use))
+ debug_printf ("LookupAccountName(%W), %E", builtin_grp);
+ builtin = true;
+ }
+ if (!legal_sid_type (use))
+ debug_printf ("Rejecting local %W. use: %d", dg_ptr, use);
+ else if (builtin)
+ grp_list *= gsid;
+ else
+ grp_list += gsid;
+ }
+ NetApiBufferFree (buf);
+ return true;
+}
+
+static bool
+sid_in_token_groups (PTOKEN_GROUPS grps, cygpsid sid)
+{
+ if (!grps)
+ return false;
+ for (DWORD i = 0; i < grps->GroupCount; ++i)
+ if (sid == grps->Groups[i].Sid)
+ return true;
+ return false;
+}
+
+static void
+get_unix_group_sidlist (struct passwd *pw, cygsidlist &grp_list)
+{
+ struct __group32 *gr;
+ cygsid gsid;
+
+ for (int gidx = 0; (gr = internal_getgrent (gidx)); ++gidx)
+ {
+ if (gr->gr_gid == (__gid32_t) pw->pw_gid)
+ goto found;
+ else if (gr->gr_mem)
+ for (int gi = 0; gr->gr_mem[gi]; ++gi)
+ if (strcasematch (pw->pw_name, gr->gr_mem[gi]))
+ goto found;
+ continue;
+ found:
+ if (gsid.getfromgr (gr))
+ grp_list += gsid;
+
+ }
+}
+
+static void
+get_token_group_sidlist (cygsidlist &grp_list, PTOKEN_GROUPS my_grps,
+ LUID auth_luid, int &auth_pos)
+{
+ auth_pos = -1;
+ if (my_grps)
+ {
+ grp_list += well_known_local_sid;
+ if (sid_in_token_groups (my_grps, well_known_dialup_sid))
+ grp_list *= well_known_dialup_sid;
+ if (sid_in_token_groups (my_grps, well_known_network_sid))
+ grp_list *= well_known_network_sid;
+ if (sid_in_token_groups (my_grps, well_known_batch_sid))
+ grp_list *= well_known_batch_sid;
+ grp_list *= well_known_interactive_sid;
+ if (sid_in_token_groups (my_grps, well_known_service_sid))
+ grp_list *= well_known_service_sid;
+ if (sid_in_token_groups (my_grps, well_known_this_org_sid))
+ grp_list *= well_known_this_org_sid;
+ }
+ else
+ {
+ grp_list += well_known_local_sid;
+ grp_list *= well_known_interactive_sid;
+ }
+ if (get_ll (auth_luid) != 999LL) /* != SYSTEM_LUID */
+ {
+ for (DWORD i = 0; i < my_grps->GroupCount; ++i)
+ if (my_grps->Groups[i].Attributes & SE_GROUP_LOGON_ID)
+ {
+ grp_list += my_grps->Groups[i].Sid;
+ auth_pos = grp_list.count () - 1;
+ break;
+ }
+ }
+}
+
+bool
+get_server_groups (cygsidlist &grp_list, PSID usersid, struct passwd *pw)
+{
+ WCHAR user[UNLEN + 1];
+ WCHAR domain[MAX_DOMAIN_NAME_LEN + 1];
+ WCHAR server[INTERNET_MAX_HOST_NAME_LENGTH + 3];
+ DWORD ulen = UNLEN + 1;
+ DWORD dlen = MAX_DOMAIN_NAME_LEN + 1;
+ SID_NAME_USE use;
+
+ if (well_known_system_sid == usersid)
+ {
+ grp_list *= well_known_admins_sid;
+ get_unix_group_sidlist (pw, grp_list);
+ return true;
+ }
+
+ grp_list *= well_known_world_sid;
+ grp_list *= well_known_authenticated_users_sid;
+
+ if (!LookupAccountSidW (NULL, usersid, user, &ulen, domain, &dlen, &use))
+ {
+ __seterrno ();
+ return false;
+ }
+ if (get_logon_server (domain, server, false)
+ && !get_user_groups (server, grp_list, user, domain)
+ && get_logon_server (domain, server, true))
+ get_user_groups (server, grp_list, user, domain);
+ if (get_user_local_groups (server, domain, grp_list, usersid))
+ {
+ get_unix_group_sidlist (pw, grp_list);
+ return true;
+ }
+ return false;
+}
+
+static bool
+get_initgroups_sidlist (cygsidlist &grp_list,
+ PSID usersid, PSID pgrpsid, struct passwd *pw,
+ PTOKEN_GROUPS my_grps, LUID auth_luid, int &auth_pos)
+{
+ grp_list *= well_known_world_sid;
+ grp_list *= well_known_authenticated_users_sid;
+ if (well_known_system_sid == usersid)
+ auth_pos = -1;
+ else
+ get_token_group_sidlist (grp_list, my_grps, auth_luid, auth_pos);
+ if (!get_server_groups (grp_list, usersid, pw))
+ return false;
+
+ /* special_pgrp true if pgrpsid is not in normal groups */
+ grp_list += pgrpsid;
+ return true;
+}
+
+static void
+get_setgroups_sidlist (cygsidlist &tmp_list, PSID usersid, struct passwd *pw,
+ PTOKEN_GROUPS my_grps, user_groups &groups,
+ LUID auth_luid, int &auth_pos)
+{
+ tmp_list *= well_known_world_sid;
+ tmp_list *= well_known_authenticated_users_sid;
+ get_token_group_sidlist (tmp_list, my_grps, auth_luid, auth_pos);
+ get_server_groups (tmp_list, usersid, pw);
+ for (int gidx = 0; gidx < groups.sgsids.count (); gidx++)
+ tmp_list += groups.sgsids.sids[gidx];
+ tmp_list += groups.pgsid;
+}
+
+static ULONG sys_privs[] = {
+ SE_CREATE_TOKEN_PRIVILEGE,
+ SE_ASSIGNPRIMARYTOKEN_PRIVILEGE,
+ SE_LOCK_MEMORY_PRIVILEGE,
+ SE_INCREASE_QUOTA_PRIVILEGE,
+ SE_TCB_PRIVILEGE,
+ SE_SECURITY_PRIVILEGE,
+ SE_TAKE_OWNERSHIP_PRIVILEGE,
+ SE_LOAD_DRIVER_PRIVILEGE,
+ SE_SYSTEM_PROFILE_PRIVILEGE, /* Vista ONLY */
+ SE_SYSTEMTIME_PRIVILEGE,
+ SE_PROF_SINGLE_PROCESS_PRIVILEGE,
+ SE_INC_BASE_PRIORITY_PRIVILEGE,
+ SE_CREATE_PAGEFILE_PRIVILEGE,
+ SE_CREATE_PERMANENT_PRIVILEGE,
+ SE_BACKUP_PRIVILEGE,
+ SE_RESTORE_PRIVILEGE,
+ SE_SHUTDOWN_PRIVILEGE,
+ SE_DEBUG_PRIVILEGE,
+ SE_AUDIT_PRIVILEGE,
+ SE_SYSTEM_ENVIRONMENT_PRIVILEGE,
+ SE_CHANGE_NOTIFY_PRIVILEGE,
+ SE_UNDOCK_PRIVILEGE,
+ SE_MANAGE_VOLUME_PRIVILEGE,
+ SE_IMPERSONATE_PRIVILEGE,
+ SE_CREATE_GLOBAL_PRIVILEGE,
+ SE_INCREASE_WORKING_SET_PRIVILEGE,
+ SE_TIME_ZONE_PRIVILEGE,
+ SE_CREATE_SYMBOLIC_LINK_PRIVILEGE
+};
+
+#define SYSTEM_PRIVILEGES_COUNT (sizeof sys_privs / sizeof *sys_privs)
+
+static PTOKEN_PRIVILEGES
+get_system_priv_list (size_t &size)
+{
+ ULONG max_idx = 0;
+ while (max_idx < SYSTEM_PRIVILEGES_COUNT
+ && sys_privs[max_idx] != wincap.max_sys_priv ())
+ ++max_idx;
+ if (max_idx >= SYSTEM_PRIVILEGES_COUNT)
+ api_fatal ("Coding error: wincap privilege %u doesn't exist in sys_privs",
+ wincap.max_sys_priv ());
+ size = sizeof (ULONG) + (max_idx + 1) * sizeof (LUID_AND_ATTRIBUTES);
+ PTOKEN_PRIVILEGES privs = (PTOKEN_PRIVILEGES) malloc (size);
+ if (!privs)
+ {
+ debug_printf ("malloc (system_privs) failed.");
+ return NULL;
+ }
+ privs->PrivilegeCount = 0;
+ for (ULONG i = 0; i <= max_idx; ++i)
+ {
+ privs->Privileges[privs->PrivilegeCount].Luid.HighPart = 0L;
+ privs->Privileges[privs->PrivilegeCount].Luid.LowPart = sys_privs[i];
+ privs->Privileges[privs->PrivilegeCount].Attributes =
+ SE_PRIVILEGE_ENABLED | SE_PRIVILEGE_ENABLED_BY_DEFAULT;
+ ++privs->PrivilegeCount;
+ }
+ return privs;
+}
+
+static PTOKEN_PRIVILEGES
+get_priv_list (LSA_HANDLE lsa, cygsid &usersid, cygsidlist &grp_list,
+ size_t &size)
+{
+ PLSA_UNICODE_STRING privstrs;
+ ULONG cnt;
+ PTOKEN_PRIVILEGES privs = NULL;
+ NTSTATUS ret;
+
+ if (usersid == well_known_system_sid)
+ return get_system_priv_list (size);
+
+ for (int grp = -1; grp < grp_list.count (); ++grp)
+ {
+ if (grp == -1)
+ {
+ if ((ret = LsaEnumerateAccountRights (lsa, usersid, &privstrs,
+ &cnt)) != STATUS_SUCCESS)
+ continue;
+ }
+ else if ((ret = LsaEnumerateAccountRights (lsa, grp_list.sids[grp],
+ &privstrs, &cnt))
+ != STATUS_SUCCESS)
+ continue;
+ for (ULONG i = 0; i < cnt; ++i)
+ {
+ LUID priv;
+ PTOKEN_PRIVILEGES tmp;
+ DWORD tmp_count;
+
+ if (!privilege_luid (privstrs[i].Buffer, &priv))
+ continue;
+
+ if (privs)
+ {
+ DWORD pcnt = privs->PrivilegeCount;
+ LUID_AND_ATTRIBUTES *p = privs->Privileges;
+ for (; pcnt > 0; --pcnt, ++p)
+ if (priv.HighPart == p->Luid.HighPart
+ && priv.LowPart == p->Luid.LowPart)
+ goto next_account_right;
+ }
+
+ tmp_count = privs ? privs->PrivilegeCount : 0;
+ size = sizeof (DWORD)
+ + (tmp_count + 1) * sizeof (LUID_AND_ATTRIBUTES);
+ tmp = (PTOKEN_PRIVILEGES) realloc (privs, size);
+ if (!tmp)
+ {
+ if (privs)
+ free (privs);
+ LsaFreeMemory (privstrs);
+ debug_printf ("realloc (privs) failed.");
+ return NULL;
+ }
+ tmp->PrivilegeCount = tmp_count;
+ privs = tmp;
+ privs->Privileges[privs->PrivilegeCount].Luid = priv;
+ privs->Privileges[privs->PrivilegeCount].Attributes =
+ SE_PRIVILEGE_ENABLED | SE_PRIVILEGE_ENABLED_BY_DEFAULT;
+ ++privs->PrivilegeCount;
+
+ next_account_right:
+ ;
+ }
+ LsaFreeMemory (privstrs);
+ }
+ return privs;
+}
+
+/* Accept a token if
+ - the requested usersid matches the TokenUser and
+ - if setgroups has been called:
+ the token groups that are listed in /etc/group match the union of
+ the requested primary and supplementary groups in gsids.
+ - else the (unknown) implicitly requested supplementary groups and those
+ in the token are the groups associated with the usersid. We assume
+ they match and verify only the primary groups.
+ The requested primary group must appear in the token.
+ The primary group in the token is a group associated with the usersid,
+ except if the token is internal and the group is in the token SD
+ (see create_token). In that latter case that group must match the
+ requested primary group. */
+bool
+verify_token (HANDLE token, cygsid &usersid, user_groups &groups, bool *pintern)
+{
+ DWORD size;
+ bool intern = false;
+
+ if (pintern)
+ {
+ TOKEN_SOURCE ts;
+ if (!GetTokenInformation (token, TokenSource,
+ &ts, sizeof ts, &size))
+ debug_printf ("GetTokenInformation(), %E");
+ else
+ *pintern = intern = !memcmp (ts.SourceName, "Cygwin.1", 8);
+ }
+ /* Verify usersid */
+ cygsid tok_usersid = NO_SID;
+ if (!GetTokenInformation (token, TokenUser,
+ &tok_usersid, sizeof tok_usersid, &size))
+ debug_printf ("GetTokenInformation(), %E");
+ if (usersid != tok_usersid)
+ return false;
+
+ /* For an internal token, if setgroups was not called and if the sd group
+ is not well_known_null_sid, it must match pgrpsid */
+ if (intern && !groups.issetgroups ())
+ {
+ const DWORD sd_buf_siz = MAX_SID_LEN + sizeof (SECURITY_DESCRIPTOR);
+ PSECURITY_DESCRIPTOR sd_buf = (PSECURITY_DESCRIPTOR) alloca (sd_buf_siz);
+ cygpsid gsid (NO_SID);
+ if (!GetKernelObjectSecurity (token, GROUP_SECURITY_INFORMATION,
+ sd_buf, sd_buf_siz, &size))
+ debug_printf ("GetKernelObjectSecurity(), %E");
+ else if (!GetSecurityDescriptorGroup (sd_buf, (PSID *) &gsid,
+ (BOOL *) &size))
+ debug_printf ("GetSecurityDescriptorGroup(), %E");
+ if (well_known_null_sid != gsid)
+ return gsid == groups.pgsid;
+ }
+
+ PTOKEN_GROUPS my_grps;
+ bool sawpg = false, ret = false;
+
+ if (!GetTokenInformation (token, TokenGroups, NULL, 0, &size) &&
+ GetLastError () != ERROR_INSUFFICIENT_BUFFER)
+ debug_printf ("GetTokenInformation(token, TokenGroups), %E");
+ else if (!(my_grps = (PTOKEN_GROUPS) alloca (size)))
+ debug_printf ("alloca (my_grps) failed.");
+ else if (!GetTokenInformation (token, TokenGroups, my_grps, size, &size))
+ debug_printf ("GetTokenInformation(my_token, TokenGroups), %E");
+ else
+ {
+ if (groups.issetgroups ()) /* setgroups was called */
+ {
+ cygsid gsid;
+ struct __group32 *gr;
+ bool saw[groups.sgsids.count ()];
+ memset (saw, 0, sizeof(saw));
+
+ /* token groups found in /etc/group match the user.gsids ? */
+ for (int gidx = 0; (gr = internal_getgrent (gidx)); ++gidx)
+ if (gsid.getfromgr (gr) && sid_in_token_groups (my_grps, gsid))
+ {
+ int pos = groups.sgsids.position (gsid);
+ if (pos >= 0)
+ saw[pos] = true;
+ else if (groups.pgsid == gsid)
+ sawpg = true;
+#if 0
+ /* With this `else', verify_token returns false if we find
+ groups in the token, which are not in the group list set
+ with setgroups(). That's rather dangerous. What we're
+ really interested in is that all groups in the setgroups()
+ list are in the token. A token created through ADVAPI
+ should be allowed to contain more groups than requested
+ through setgroups(), esecially since Vista and the
+ addition of integrity groups. So we disable this statement
+ for now. */
+ else if (gsid != well_known_world_sid
+ && gsid != usersid)
+ goto done;
+#endif
+ }
+ /* user.sgsids groups must be in the token, except for builtin groups.
+ These can be different on domain member machines compared to
+ domain controllers, so these builtin groups may be validly missing
+ from a token created through password or lsaauth logon. */
+ for (int gidx = 0; gidx < groups.sgsids.count (); gidx++)
+ if (!saw[gidx]
+ && !groups.sgsids.sids[gidx].is_well_known_sid ()
+ && !sid_in_token_groups (my_grps, groups.sgsids.sids[gidx]))
+ goto done;
+ }
+ /* The primary group must be in the token */
+ ret = sawpg
+ || sid_in_token_groups (my_grps, groups.pgsid)
+ || groups.pgsid == usersid;
+ }
+done:
+ return ret;
+}
+
+HANDLE
+create_token (cygsid &usersid, user_groups &new_groups, struct passwd *pw)
+{
+ NTSTATUS ret;
+ LSA_HANDLE lsa = INVALID_HANDLE_VALUE;
+
+ cygsidlist tmp_gsids (cygsidlist_auto, 12);
+
+ SECURITY_QUALITY_OF_SERVICE sqos =
+ { sizeof sqos, SecurityImpersonation, SECURITY_STATIC_TRACKING, FALSE };
+ OBJECT_ATTRIBUTES oa = { sizeof oa, 0, 0, 0, 0, &sqos };
+ LUID auth_luid = SYSTEM_LUID;
+ LARGE_INTEGER exp = { QuadPart:INT64_MAX };
+
+ TOKEN_USER user;
+ PTOKEN_GROUPS new_tok_gsids = NULL;
+ PTOKEN_PRIVILEGES privs = NULL;
+ TOKEN_OWNER owner;
+ TOKEN_PRIMARY_GROUP pgrp;
+ TOKEN_DEFAULT_DACL dacl = {};
+ TOKEN_SOURCE source;
+ TOKEN_STATISTICS stats;
+ memcpy (source.SourceName, "Cygwin.1", 8);
+ source.SourceIdentifier.HighPart = 0;
+ source.SourceIdentifier.LowPart = 0x0101;
+
+ HANDLE token = INVALID_HANDLE_VALUE;
+ HANDLE primary_token = INVALID_HANDLE_VALUE;
+
+ PTOKEN_GROUPS my_tok_gsids = NULL;
+ DWORD size;
+ size_t psize = 0;
+
+ /* SE_CREATE_TOKEN_NAME privilege needed to call NtCreateToken. */
+ push_self_privilege (SE_CREATE_TOKEN_PRIVILEGE, true);
+
+ /* Open policy object. */
+ if ((lsa = open_local_policy ()) == INVALID_HANDLE_VALUE)
+ goto out;
+
+ /* User, owner, primary group. */
+ user.User.Sid = usersid;
+ user.User.Attributes = 0;
+ owner.Owner = usersid;
+
+ /* Retrieve authentication id and group list from own process. */
+ if (hProcToken)
+ {
+ /* Switching user context to SYSTEM doesn't inherit the authentication
+ id of the user account running current process. */
+ if (usersid != well_known_system_sid)
+ if (!GetTokenInformation (hProcToken, TokenStatistics,
+ &stats, sizeof stats, &size))
+ debug_printf
+ ("GetTokenInformation(hProcToken, TokenStatistics), %E");
+ else
+ auth_luid = stats.AuthenticationId;
+
+ /* Retrieving current processes group list to be able to inherit
+ some important well known group sids. */
+ if (!GetTokenInformation (hProcToken, TokenGroups, NULL, 0, &size)
+ && GetLastError () != ERROR_INSUFFICIENT_BUFFER)
+ debug_printf ("GetTokenInformation(hProcToken, TokenGroups), %E");
+ else if (!(my_tok_gsids = (PTOKEN_GROUPS) malloc (size)))
+ debug_printf ("malloc (my_tok_gsids) failed.");
+ else if (!GetTokenInformation (hProcToken, TokenGroups, my_tok_gsids,
+ size, &size))
+ {
+ debug_printf ("GetTokenInformation(hProcToken, TokenGroups), %E");
+ free (my_tok_gsids);
+ my_tok_gsids = NULL;
+ }
+ }
+
+ /* Create list of groups, the user is member in. */
+ int auth_pos;
+ if (new_groups.issetgroups ())
+ get_setgroups_sidlist (tmp_gsids, usersid, pw, my_tok_gsids, new_groups,
+ auth_luid, auth_pos);
+ else if (!get_initgroups_sidlist (tmp_gsids, usersid, new_groups.pgsid, pw,
+ my_tok_gsids, auth_luid, auth_pos))
+ goto out;
+
+ /* Primary group. */
+ pgrp.PrimaryGroup = new_groups.pgsid;
+
+ /* Create a TOKEN_GROUPS list from the above retrieved list of sids. */
+ new_tok_gsids = (PTOKEN_GROUPS)
+ alloca (sizeof (DWORD) + (tmp_gsids.count () + 1)
+ * sizeof (SID_AND_ATTRIBUTES));
+ new_tok_gsids->GroupCount = tmp_gsids.count ();
+ for (DWORD i = 0; i < new_tok_gsids->GroupCount; ++i)
+ {
+ new_tok_gsids->Groups[i].Sid = tmp_gsids.sids[i];
+ new_tok_gsids->Groups[i].Attributes = SE_GROUP_MANDATORY
+ | SE_GROUP_ENABLED_BY_DEFAULT
+ | SE_GROUP_ENABLED;
+ }
+ if (auth_pos >= 0)
+ new_tok_gsids->Groups[auth_pos].Attributes |= SE_GROUP_LOGON_ID;
+
+ /* On systems supporting Mandatory Integrity Control, add a MIC SID. */
+ if (wincap.has_mandatory_integrity_control ())
+ {
+ new_tok_gsids->Groups[new_tok_gsids->GroupCount].Attributes =
+ SE_GROUP_INTEGRITY | SE_GROUP_INTEGRITY_ENABLED;
+ if (usersid == well_known_system_sid)
+ new_tok_gsids->Groups[new_tok_gsids->GroupCount++].Sid
+ = mandatory_system_integrity_sid;
+ else if (tmp_gsids.contains (well_known_admins_sid))
+ new_tok_gsids->Groups[new_tok_gsids->GroupCount++].Sid
+ = mandatory_high_integrity_sid;
+ else
+ new_tok_gsids->Groups[new_tok_gsids->GroupCount++].Sid
+ = mandatory_medium_integrity_sid;
+ }
+
+ /* Retrieve list of privileges of that user. */
+ if (!(privs = get_priv_list (lsa, usersid, tmp_gsids, psize)))
+ goto out;
+
+ /* Let's be heroic... */
+ ret = NtCreateToken (&token, TOKEN_ALL_ACCESS, &oa, TokenImpersonation,
+ &auth_luid, &exp, &user, new_tok_gsids, privs, &owner,
+ &pgrp, &dacl, &source);
+ if (ret)
+ __seterrno_from_nt_status (ret);
+ else
+ {
+ /* Convert to primary token. */
+ if (!DuplicateTokenEx (token, MAXIMUM_ALLOWED, &sec_none,
+ SecurityImpersonation, TokenPrimary,
+ &primary_token))
+ {
+ __seterrno ();
+ debug_printf ("DuplicateTokenEx %E");
+ }
+ }
+
+out:
+ pop_self_privilege ();
+ if (token != INVALID_HANDLE_VALUE)
+ CloseHandle (token);
+ if (privs)
+ free (privs);
+ if (my_tok_gsids)
+ free (my_tok_gsids);
+ close_local_policy (lsa);
+
+ debug_printf ("%p = create_token ()", primary_token);
+ return primary_token;
+}
+
+HANDLE
+lsaauth (cygsid &usersid, user_groups &new_groups, struct passwd *pw)
+{
+ cygsidlist tmp_gsids (cygsidlist_auto, 12);
+ cygpsid pgrpsid;
+ LSA_STRING name;
+ HANDLE lsa_hdl = NULL, lsa = INVALID_HANDLE_VALUE;
+ LSA_OPERATIONAL_MODE sec_mode;
+ NTSTATUS ret, ret2;
+ ULONG package_id, size;
+ LUID auth_luid = SYSTEM_LUID;
+ struct {
+ LSA_STRING str;
+ CHAR buf[16];
+ } origin;
+ DWORD ulen = UNLEN + 1;
+ DWORD dlen = MAX_DOMAIN_NAME_LEN + 1;
+ SID_NAME_USE use;
+ cyglsa_t *authinf = NULL;
+ ULONG authinf_size;
+ TOKEN_SOURCE ts;
+ PCYG_TOKEN_GROUPS gsids = NULL;
+ PTOKEN_PRIVILEGES privs = NULL;
+ PACL dacl = NULL;
+ PVOID profile = NULL;
+ LUID luid;
+ QUOTA_LIMITS quota;
+ size_t psize = 0, gsize = 0, dsize = 0;
+ OFFSET offset, sids_offset;
+ int tmpidx, non_well_known_cnt;
+
+ HANDLE user_token = NULL;
+
+ push_self_privilege (SE_TCB_PRIVILEGE, true);
+
+ /* Register as logon process. */
+ str2lsa (name, "Cygwin");
+ SetLastError (0);
+ ret = LsaRegisterLogonProcess (&name, &lsa_hdl, &sec_mode);
+ if (ret != STATUS_SUCCESS)
+ {
+ debug_printf ("LsaRegisterLogonProcess: %p", ret);
+ __seterrno_from_win_error (LsaNtStatusToWinError (ret));
+ goto out;
+ }
+ else if (GetLastError () == ERROR_PROC_NOT_FOUND)
+ {
+ debug_printf ("Couldn't load Secur32.dll");
+ goto out;
+ }
+ /* Get handle to our own LSA package. */
+ str2lsa (name, CYG_LSA_PKGNAME);
+ ret = LsaLookupAuthenticationPackage (lsa_hdl, &name, &package_id);
+ if (ret != STATUS_SUCCESS)
+ {
+ debug_printf ("LsaLookupAuthenticationPackage: %p", ret);
+ __seterrno_from_win_error (LsaNtStatusToWinError (ret));
+ goto out;
+ }
+
+ /* Open policy object. */
+ if ((lsa = open_local_policy ()) == INVALID_HANDLE_VALUE)
+ goto out;
+
+ /* Create origin. */
+ str2buf2lsa (origin.str, origin.buf, "Cygwin");
+ /* Create token source. */
+ memcpy (ts.SourceName, "Cygwin.1", 8);
+ ts.SourceIdentifier.HighPart = 0;
+ ts.SourceIdentifier.LowPart = 0x0103;
+
+ /* Create list of groups, the user is member in. */
+ int auth_pos;
+ if (new_groups.issetgroups ())
+ get_setgroups_sidlist (tmp_gsids, usersid, pw, NULL, new_groups, auth_luid,
+ auth_pos);
+ else if (!get_initgroups_sidlist (tmp_gsids, usersid, new_groups.pgsid, pw,
+ NULL, auth_luid, auth_pos))
+ goto out;
+ /* The logon SID entry is not generated automatically on Windows 2000
+ and earlier for some reason. So add fake logon sid here, which is
+ filled with logon id values in the authentication package. */
+ if (wincap.needs_logon_sid_in_sid_list ())
+ tmp_gsids += fake_logon_sid;
+
+ tmp_gsids.debug_print ("tmp_gsids");
+
+ /* Evaluate size of TOKEN_GROUPS list */
+ non_well_known_cnt = tmp_gsids.non_well_known_count ();
+ gsize = sizeof (DWORD) + non_well_known_cnt * sizeof (SID_AND_ATTRIBUTES);
+ tmpidx = -1;
+ for (int i = 0; i < non_well_known_cnt; ++i)
+ if ((tmpidx = tmp_gsids.next_non_well_known_sid (tmpidx)) >= 0)
+ gsize += GetLengthSid (tmp_gsids.sids[tmpidx]);
+
+ /* Retrieve list of privileges of that user. */
+ if (!(privs = get_priv_list (lsa, usersid, tmp_gsids, psize)))
+ goto out;
+
+ /* Create DefaultDacl. */
+ dsize = sizeof (ACL) + 3 * sizeof (ACCESS_ALLOWED_ACE)
+ + GetLengthSid (usersid)
+ + GetLengthSid (well_known_admins_sid)
+ + GetLengthSid (well_known_system_sid);
+ dacl = (PACL) alloca (dsize);
+ if (!InitializeAcl (dacl, dsize, ACL_REVISION))
+ goto out;
+ if (!AddAccessAllowedAce (dacl, ACL_REVISION, GENERIC_ALL, usersid))
+ goto out;
+ if (!AddAccessAllowedAce (dacl, ACL_REVISION, GENERIC_ALL,
+ well_known_admins_sid))
+ goto out;
+ if (!AddAccessAllowedAce (dacl, ACL_REVISION, GENERIC_ALL,
+ well_known_system_sid))
+ goto out;
+
+ /* Evaluate authinf size and allocate authinf. */
+ authinf_size = (authinf->data - (PBYTE) authinf);
+ authinf_size += GetLengthSid (usersid); /* User SID */
+ authinf_size += gsize; /* Groups + Group SIDs */
+ /* When trying to define the admins group as primary group on Vista,
+ LsaLogonUser fails with error STATUS_INVALID_OWNER. As workaround
+ we define "Local" as primary group here. Seteuid32 sets the primary
+ group to the group set in /etc/passwd anyway. */
+ if (new_groups.pgsid == well_known_admins_sid)
+ pgrpsid = well_known_local_sid;
+ else
+ pgrpsid = new_groups.pgsid;
+
+ authinf_size += GetLengthSid (pgrpsid); /* Primary Group SID */
+
+ authinf_size += psize; /* Privileges */
+ authinf_size += 0; /* Owner SID */
+ authinf_size += dsize; /* Default DACL */
+
+ authinf = (cyglsa_t *) alloca (authinf_size);
+ authinf->inf_size = authinf_size - ((PBYTE) &authinf->inf - (PBYTE) authinf);
+
+ authinf->magic = CYG_LSA_MAGIC;
+
+ if (!LookupAccountSidW (NULL, usersid, authinf->username, &ulen,
+ authinf->domain, &dlen, &use))
+ {
+ __seterrno ();
+ goto out;
+ }
+
+ /* Store stuff in authinf with offset relative to start of "inf" member,
+ instead of using pointers. */
+ offset = authinf->data - (PBYTE) &authinf->inf;
+
+ authinf->inf.ExpirationTime.LowPart = 0xffffffffL;
+ authinf->inf.ExpirationTime.HighPart = 0x7fffffffL;
+ /* User SID */
+ authinf->inf.User.User.Sid = offset;
+ authinf->inf.User.User.Attributes = 0;
+ CopySid (GetLengthSid (usersid), (PSID) ((PBYTE) &authinf->inf + offset),
+ usersid);
+ offset += GetLengthSid (usersid);
+ /* Groups */
+ authinf->inf.Groups = offset;
+ gsids = (PCYG_TOKEN_GROUPS) ((PBYTE) &authinf->inf + offset);
+ sids_offset = offset + sizeof (ULONG) + non_well_known_cnt
+ * sizeof (SID_AND_ATTRIBUTES);
+ gsids->GroupCount = non_well_known_cnt;
+ /* Group SIDs */
+ tmpidx = -1;
+ for (int i = 0; i < non_well_known_cnt; ++i)
+ {
+ if ((tmpidx = tmp_gsids.next_non_well_known_sid (tmpidx)) < 0)
+ break;
+ gsids->Groups[i].Sid = sids_offset;
+ gsids->Groups[i].Attributes = SE_GROUP_MANDATORY
+ | SE_GROUP_ENABLED_BY_DEFAULT
+ | SE_GROUP_ENABLED;
+ /* Mark logon SID as logon SID :) */
+ if (wincap.needs_logon_sid_in_sid_list ()
+ && tmp_gsids.sids[tmpidx] == fake_logon_sid)
+ gsids->Groups[i].Attributes += SE_GROUP_LOGON_ID;
+ CopySid (GetLengthSid (tmp_gsids.sids[tmpidx]),
+ (PSID) ((PBYTE) &authinf->inf + sids_offset),
+ tmp_gsids.sids[tmpidx]);
+ sids_offset += GetLengthSid (tmp_gsids.sids[tmpidx]);
+ }
+ offset += gsize;
+ /* Primary Group SID */
+ authinf->inf.PrimaryGroup.PrimaryGroup = offset;
+ CopySid (GetLengthSid (pgrpsid), (PSID) ((PBYTE) &authinf->inf + offset),
+ pgrpsid);
+ offset += GetLengthSid (pgrpsid);
+ /* Privileges */
+ authinf->inf.Privileges = offset;
+ memcpy ((PBYTE) &authinf->inf + offset, privs, psize);
+ offset += psize;
+ /* Owner */
+ authinf->inf.Owner.Owner = 0;
+ /* Default DACL */
+ authinf->inf.DefaultDacl.DefaultDacl = offset;
+ memcpy ((PBYTE) &authinf->inf + offset, dacl, dsize);
+
+ authinf->checksum = CYGWIN_VERSION_MAGIC (CYGWIN_VERSION_DLL_MAJOR,
+ CYGWIN_VERSION_DLL_MINOR);
+ PDWORD csp = (PDWORD) &authinf->username;
+ PDWORD csp_end = (PDWORD) ((PBYTE) authinf + authinf_size);
+ while (csp < csp_end)
+ authinf->checksum += *csp++;
+
+ /* Try to logon... */
+ ret = LsaLogonUser (lsa_hdl, (PLSA_STRING) &origin, Interactive, package_id,
+ authinf, authinf_size, NULL, &ts, &profile, &size, &luid,
+ &user_token, "a, &ret2);
+ if (ret != STATUS_SUCCESS)
+ {
+ debug_printf ("LsaLogonUser: %p", ret);
+ __seterrno_from_win_error (LsaNtStatusToWinError (ret));
+ goto out;
+ }
+ if (profile)
+ {
+#ifdef JUST_ANOTHER_NONWORKING_SOLUTION
+ /* See ../lsaauth/cyglsa.c. */
+ cygprf_t *prf = (cygprf_t *) profile;
+ if (prf->magic_pre == MAGIC_PRE && prf->magic_post == MAGIC_POST
+ && prf->token)
+ {
+ CloseHandle (user_token);
+ user_token = prf->token;
+ system_printf ("Got token through profile: %p", user_token);
+ }
+#endif /* JUST_ANOTHER_NONWORKING_SOLUTION */
+ LsaFreeReturnBuffer (profile);
+ }
+
+ if (wincap.has_mandatory_integrity_control ())
+ {
+ typedef struct _TOKEN_LINKED_TOKEN
+ {
+ HANDLE LinkedToken;
+ } TOKEN_LINKED_TOKEN, *PTOKEN_LINKED_TOKEN;
+# define TokenLinkedToken ((TOKEN_INFORMATION_CLASS) 19)
+
+ TOKEN_LINKED_TOKEN linked;
+
+ if (GetTokenInformation (user_token, TokenLinkedToken,
+ (PVOID) &linked, sizeof linked, &size))
+ {
+ debug_printf ("Linked Token: %p", linked.LinkedToken);
+ if (linked.LinkedToken)
+ user_token = linked.LinkedToken;
+ }
+ }
+
+ /* The token returned by LsaLogonUser is not inheritable. Make it so. */
+ if (!SetHandleInformation (user_token, HANDLE_FLAG_INHERIT,
+ HANDLE_FLAG_INHERIT))
+ system_printf ("SetHandleInformation %E");
+
+out:
+ if (privs)
+ free (privs);
+ close_local_policy (lsa);
+ if (lsa_hdl)
+ LsaDeregisterLogonProcess (lsa_hdl);
+ pop_self_privilege ();
+
+ debug_printf ("%p = lsaauth ()", user_token);
+ return user_token;
+}
--- /dev/null
+/* security.h: security declarations
+
+ Copyright 2000, 2001, 2002, 2003, 2004, 2005, 2006, 2007, 2008 Red Hat, Inc.
+
+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. */
+
+#ifndef _SECURITY_H
+#define _SECURITY_H
+
+#include <accctrl.h>
+
+#define DEFAULT_UID DOMAIN_USER_RID_ADMIN
+#define UNKNOWN_UID 400 /* Non conflicting number */
+#define UNKNOWN_GID 401
+
+#define MAX_SID_LEN 40
+#define MAX_DACL_LEN(n) (sizeof (ACL) \
+ + (n) * (sizeof (ACCESS_ALLOWED_ACE) - sizeof (DWORD) + MAX_SID_LEN))
+#define SD_MIN_SIZE (sizeof (SECURITY_DESCRIPTOR) + MAX_DACL_LEN (1))
+#define ACL_DEFAULT_SIZE 3072
+#define NO_SID ((PSID)NULL)
+
+#ifndef SE_CREATE_TOKEN_PRIVILEGE
+#define SE_CREATE_TOKEN_PRIVILEGE 2UL
+#define SE_ASSIGNPRIMARYTOKEN_PRIVILEGE 3UL
+#define SE_LOCK_MEMORY_PRIVILEGE 4UL
+#define SE_INCREASE_QUOTA_PRIVILEGE 5UL
+#define SE_MACHINE_ACCOUNT_PRIVILEGE 6UL
+#define SE_TCB_PRIVILEGE 7UL
+#define SE_SECURITY_PRIVILEGE 8UL
+#define SE_TAKE_OWNERSHIP_PRIVILEGE 9UL
+#define SE_LOAD_DRIVER_PRIVILEGE 10UL
+#define SE_SYSTEM_PROFILE_PRIVILEGE 11UL
+#define SE_SYSTEMTIME_PRIVILEGE 12UL
+#define SE_PROF_SINGLE_PROCESS_PRIVILEGE 13UL
+#define SE_INC_BASE_PRIORITY_PRIVILEGE 14UL
+#define SE_CREATE_PAGEFILE_PRIVILEGE 15UL
+#define SE_CREATE_PERMANENT_PRIVILEGE 16UL
+#define SE_BACKUP_PRIVILEGE 17UL
+#define SE_RESTORE_PRIVILEGE 18UL
+#define SE_SHUTDOWN_PRIVILEGE 19UL
+#define SE_DEBUG_PRIVILEGE 20UL
+#define SE_AUDIT_PRIVILEGE 21UL
+#define SE_SYSTEM_ENVIRONMENT_PRIVILEGE 22UL
+#define SE_CHANGE_NOTIFY_PRIVILEGE 23UL
+#define SE_REMOTE_SHUTDOWN_PRIVILEGE 24UL
+/* Starting with Windows 2000 */
+#define SE_UNDOCK_PRIVILEGE 25UL
+#define SE_SYNC_AGENT_PRIVILEGE 26UL
+#define SE_ENABLE_DELEGATION_PRIVILEGE 27UL
+#define SE_MANAGE_VOLUME_PRIVILEGE 28UL
+/* Starting with Windows 2000 SP4, XP SP2, 2003 Server */
+#define SE_IMPERSONATE_PRIVILEGE 29UL
+#define SE_CREATE_GLOBAL_PRIVILEGE 30UL
+/* Starting with Vista */
+#define SE_TRUSTED_CREDMAN_ACCESS_PRIVILEGE 31UL
+#define SE_RELABEL_PRIVILEGE 32UL
+#define SE_INCREASE_WORKING_SET_PRIVILEGE 33UL
+#define SE_TIME_ZONE_PRIVILEGE 34UL
+#define SE_CREATE_SYMBOLIC_LINK_PRIVILEGE 35UL
+
+#define SE_MAX_WELL_KNOWN_PRIVILEGE SE_CREATE_SYMBOLIC_LINK_PRIVILEGE
+
+#endif /* ! SE_CREATE_TOKEN_PRIVILEGE */
+
+/* Added for debugging purposes. */
+typedef struct {
+ BYTE Revision;
+ BYTE SubAuthorityCount;
+ SID_IDENTIFIER_AUTHORITY IdentifierAuthority;
+ DWORD SubAuthority[8];
+} DBGSID, *PDBGSID;
+
+/* Macro to define variable length SID structures */
+#define MKSID(name, comment, authority, count, rid...) \
+static NO_COPY struct { \
+ BYTE Revision; \
+ BYTE SubAuthorityCount; \
+ SID_IDENTIFIER_AUTHORITY IdentifierAuthority; \
+ DWORD SubAuthority[count]; \
+} name##_struct = { SID_REVISION, count, {authority}, {rid}}; \
+cygpsid NO_COPY name = (PSID) &name##_struct;
+
+#define FILE_READ_BITS (FILE_READ_DATA | GENERIC_READ | GENERIC_ALL)
+#define FILE_WRITE_BITS (FILE_WRITE_DATA | GENERIC_WRITE | GENERIC_ALL)
+#define FILE_EXEC_BITS (FILE_EXECUTE | GENERIC_EXECUTE | GENERIC_ALL)
+
+class cygpsid {
+protected:
+ PSID psid;
+public:
+ cygpsid () {}
+ cygpsid (PSID nsid) { psid = nsid; }
+ operator PSID () const { return psid; }
+ const PSID operator= (PSID nsid) { return psid = nsid;}
+ __uid32_t get_id (BOOL search_grp, int *type = NULL);
+ int get_uid () { return get_id (FALSE); }
+ int get_gid () { return get_id (TRUE); }
+
+ PWCHAR string (PWCHAR nsidstr) const;
+ char *string (char *nsidstr) const;
+
+ bool operator== (const PSID nsid) const
+ {
+ if (!psid || !nsid)
+ return nsid == psid;
+ return EqualSid (psid, nsid);
+ }
+ bool operator!= (const PSID nsid) const
+ { return !(*this == nsid); }
+ bool operator== (const char *nsidstr) const;
+ bool operator!= (const char *nsidstr) const
+ { return !(*this == nsidstr); }
+
+ void debug_print (const char *prefix = NULL) const
+ {
+ char buf[256] __attribute__ ((unused));
+ debug_printf ("%s %s", prefix ?: "", string (buf) ?: "NULL");
+ }
+};
+
+class cygsid : public cygpsid {
+ char sbuf[MAX_SID_LEN];
+ bool well_known_sid;
+
+ const PSID getfromstr (const char *nsidstr, bool well_known);
+ PSID get_sid (DWORD s, DWORD cnt, DWORD *r, bool well_known);
+
+ inline const PSID assign (const PSID nsid, bool well_known)
+ {
+ if (!nsid)
+ psid = NO_SID;
+ else
+ {
+ psid = (PSID) sbuf;
+ CopySid (MAX_SID_LEN, psid, nsid);
+ well_known_sid = well_known;
+ }
+ return psid;
+ }
+
+public:
+ inline operator const PSID () { return psid; }
+ inline bool is_well_known_sid () { return well_known_sid; }
+
+ /* Both, = and *= are assignment operators. = creates a "normal" SID,
+ *= marks the SID as being a well-known SID. This difference is
+ important when creating a SID list for LSA authentication. */
+ inline const PSID operator= (cygsid &nsid)
+ { return assign (nsid, nsid.well_known_sid); }
+ inline const PSID operator= (const PSID nsid)
+ { return assign (nsid, false); }
+ inline const PSID operator= (const char *nsidstr)
+ { return getfromstr (nsidstr, false); }
+ inline const PSID operator*= (cygsid &nsid)
+ { return assign (nsid, true); }
+ inline const PSID operator*= (const PSID nsid)
+ { return assign (nsid, true); }
+ inline const PSID operator*= (const char *nsidstr)
+ { return getfromstr (nsidstr, true); }
+
+ inline cygsid () : cygpsid ((PSID) sbuf), well_known_sid (false) {}
+ inline cygsid (const PSID nsid) { *this = nsid; }
+ inline cygsid (const char *nstrsid) { *this = nstrsid; }
+
+ inline PSID set () { return psid = (PSID) sbuf; }
+
+ BOOL getfrompw (const struct passwd *pw);
+ BOOL getfromgr (const struct __group32 *gr);
+
+ void debug_print (const char *prefix = NULL) const
+ {
+ char buf[256] __attribute__ ((unused));
+ debug_printf ("%s %s%s", prefix ?: "", string (buf) ?: "NULL", well_known_sid ? " (*)" : " (+)");
+ }
+};
+
+typedef enum { cygsidlist_empty, cygsidlist_alloc, cygsidlist_auto } cygsidlist_type;
+class cygsidlist {
+ int maxcnt;
+ int cnt;
+
+ BOOL add (const PSID nsi, bool well_known); /* Only with auto for now */
+
+public:
+ cygsid *sids;
+ cygsidlist_type type;
+
+ cygsidlist (cygsidlist_type t, int m)
+ : maxcnt (m), cnt (0), type (t)
+ {
+ if (t == cygsidlist_alloc)
+ sids = alloc_sids (m);
+ else
+ sids = new cygsid [m];
+ }
+ ~cygsidlist () { if (type == cygsidlist_auto) delete [] sids; }
+
+ BOOL addfromgr (struct __group32 *gr) /* Only with alloc */
+ { return sids[cnt].getfromgr (gr) && ++cnt; }
+
+ /* += adds a "normal" SID, *= adds a well-known SID. See comment in class
+ cygsid above. */
+ BOOL operator+= (cygsid &si) { return add ((PSID) si, false); }
+ BOOL operator+= (const char *sidstr) { cygsid nsi (sidstr);
+ return add ((PSID) nsi, false); }
+ BOOL operator+= (const PSID psid) { return add (psid, false); }
+ BOOL operator*= (cygsid &si) { return add ((PSID) si, true); }
+ BOOL operator*= (const char *sidstr) { cygsid nsi (sidstr);
+ return add ((PSID) nsi, true); }
+ BOOL operator*= (const PSID psid) { return add (psid, true); }
+
+ void count (int ncnt)
+ { cnt = ncnt; }
+ int count () const { return cnt; }
+ int non_well_known_count () const
+ {
+ int wcnt = 0;
+ for (int i = 0; i < cnt; ++i)
+ if (!sids[i].is_well_known_sid ())
+ ++wcnt;
+ return wcnt;
+ }
+
+ int position (const PSID sid) const
+ {
+ for (int i = 0; i < cnt; ++i)
+ if (sids[i] == sid)
+ return i;
+ return -1;
+ }
+
+ int next_non_well_known_sid (int idx)
+ {
+ while (++idx < cnt)
+ if (!sids[idx].is_well_known_sid ())
+ return idx;
+ return -1;
+ }
+ BOOL contains (const PSID sid) const { return position (sid) >= 0; }
+ cygsid *alloc_sids (int n);
+ void free_sids ();
+ void debug_print (const char *prefix = NULL) const
+ {
+ debug_printf ("-- begin sidlist ---");
+ if (!cnt)
+ debug_printf ("No elements");
+ for (int i = 0; i < cnt; ++i)
+ sids[i].debug_print (prefix);
+ debug_printf ("-- ende sidlist ---");
+ }
+};
+
+/* Wrapper class to allow simple deleting of buffer space allocated
+ by read_sd() */
+class security_descriptor {
+protected:
+ PSECURITY_DESCRIPTOR psd;
+ DWORD sd_size;
+public:
+ security_descriptor () : psd (NULL), sd_size (0) {}
+ ~security_descriptor () { free (); }
+
+ PSECURITY_DESCRIPTOR malloc (size_t nsize);
+ PSECURITY_DESCRIPTOR realloc (size_t nsize);
+ void free ();
+
+ inline DWORD size () const { return sd_size; }
+ inline DWORD copy (void *buf, DWORD buf_size) const {
+ if (buf_size < size ())
+ return sd_size;
+ memcpy (buf, psd, sd_size);
+ return 0;
+ }
+ inline operator const PSECURITY_DESCRIPTOR () { return psd; }
+ inline operator PSECURITY_DESCRIPTOR *() { return &psd; }
+};
+
+class user_groups {
+public:
+ cygsid pgsid;
+ cygsidlist sgsids;
+ BOOL ischanged;
+
+ BOOL issetgroups () const { return (sgsids.type == cygsidlist_alloc); }
+ void update_supp (const cygsidlist &newsids)
+ {
+ sgsids.free_sids ();
+ sgsids = newsids;
+ ischanged = TRUE;
+ }
+ void clear_supp ()
+ {
+ if (issetgroups ())
+ {
+ sgsids.free_sids ();
+ ischanged = TRUE;
+ }
+ }
+ void update_pgrp (const PSID sid)
+ {
+ pgsid = sid;
+ ischanged = TRUE;
+ }
+};
+
+extern cygpsid well_known_null_sid;
+extern cygpsid well_known_world_sid;
+extern cygpsid well_known_local_sid;
+extern cygpsid well_known_creator_owner_sid;
+extern cygpsid well_known_creator_group_sid;
+extern cygpsid well_known_dialup_sid;
+extern cygpsid well_known_network_sid;
+extern cygpsid well_known_batch_sid;
+extern cygpsid well_known_interactive_sid;
+extern cygpsid well_known_service_sid;
+extern cygpsid well_known_authenticated_users_sid;
+extern cygpsid well_known_this_org_sid;
+extern cygpsid well_known_system_sid;
+extern cygpsid well_known_admins_sid;
+extern cygpsid fake_logon_sid;
+extern cygpsid mandatory_medium_integrity_sid;
+extern cygpsid mandatory_high_integrity_sid;
+extern cygpsid mandatory_system_integrity_sid;
+
+bool privilege_luid (const PWCHAR pname, LUID *luid);
+
+inline BOOL
+legal_sid_type (SID_NAME_USE type)
+{
+ return type == SidTypeUser || type == SidTypeGroup
+ || type == SidTypeAlias || type == SidTypeWellKnownGroup;
+}
+
+/* File manipulation */
+int __stdcall get_file_attribute (HANDLE, path_conv &, mode_t *,
+ __uid32_t *, __gid32_t *);
+int __stdcall set_file_attribute (HANDLE, path_conv &,
+ __uid32_t, __gid32_t, int);
+int __stdcall get_reg_attribute (HKEY hkey, mode_t *, __uid32_t *, __gid32_t *);
+LONG __stdcall get_file_sd (HANDLE fh, path_conv &, security_descriptor &sd);
+LONG __stdcall set_file_sd (HANDLE fh, path_conv &, security_descriptor &sd);
+bool __stdcall add_access_allowed_ace (PACL acl, int offset, DWORD attributes, PSID sid, size_t &len_add, DWORD inherit);
+bool __stdcall add_access_denied_ace (PACL acl, int offset, DWORD attributes, PSID sid, size_t &len_add, DWORD inherit);
+int __stdcall check_file_access (path_conv &, int);
+int __stdcall check_registry_access (HANDLE, int);
+
+void set_security_attribute (int attribute, PSECURITY_ATTRIBUTES psa,
+ security_descriptor &sd_buf);
+
+bool get_sids_info (cygpsid, cygpsid, __uid32_t * , __gid32_t *);
+
+/* sec_acl.cc */
+struct __acl32;
+extern "C" int aclsort32 (int, int, __acl32 *);
+extern "C" int acl32 (const char *, int, int, __acl32 *);
+int getacl (HANDLE, path_conv &, int, __acl32 *);
+int setacl (HANDLE, path_conv &, int, __acl32 *, bool &);
+
+struct _UNICODE_STRING;
+void __stdcall str2buf2uni (_UNICODE_STRING &, WCHAR *, const char *) __attribute__ ((regparm (3)));
+void __stdcall str2uni_cat (_UNICODE_STRING &, const char *) __attribute__ ((regparm (2)));
+
+/* Function creating a token by calling NtCreateToken. */
+HANDLE create_token (cygsid &usersid, user_groups &groups, struct passwd * pw);
+/* LSA authentication function. */
+HANDLE lsaauth (cygsid &, user_groups &, struct passwd *);
+/* Verify an existing token */
+bool verify_token (HANDLE token, cygsid &usersid, user_groups &groups, bool *pintern = NULL);
+/* Get groups of a user */
+bool get_server_groups (cygsidlist &grp_list, PSID usersid, struct passwd *pw);
+
+/* Extract U-domain\user field from passwd entry. */
+void extract_nt_dom_user (const struct passwd *pw, PWCHAR domain, PWCHAR user);
+/* Get default logonserver for a domain. */
+bool get_logon_server (PWCHAR domain, PWCHAR wserver, bool rediscovery);
+
+/* sec_helper.cc: Security helper functions. */
+int set_privilege (HANDLE token, DWORD privilege, bool enable);
+void set_cygwin_privileges (HANDLE token);
+
+#define _push_thread_privilege(_priv, _val, _check) { \
+ HANDLE _dup_token = NULL; \
+ HANDLE _token = (cygheap->user.issetuid () && (_check)) \
+ ? cygheap->user.primary_token () : hProcToken; \
+ if (!DuplicateTokenEx (_token, MAXIMUM_ALLOWED, NULL, \
+ SecurityImpersonation, TokenImpersonation, \
+ &_dup_token)) \
+ debug_printf ("DuplicateTokenEx: %E"); \
+ else if (!ImpersonateLoggedOnUser (_dup_token)) \
+ debug_printf ("ImpersonateLoggedOnUser: %E"); \
+ else \
+ set_privilege (_dup_token, (_priv), (_val));
+
+#define push_thread_privilege(_priv, _val) _push_thread_privilege(_priv,_val,1)
+#define push_self_privilege(_priv, _val) _push_thread_privilege(_priv,_val,0)
+
+#define pop_thread_privilege() \
+ if (_dup_token) \
+ { \
+ if (!cygheap->user.issetuid ()) \
+ RevertToSelf (); \
+ else \
+ cygheap->user.reimpersonate (); \
+ CloseHandle (_dup_token); \
+ } \
+ }
+
+#define pop_self_privilege() pop_thread_privilege()
+
+/* shared.cc: */
+/* Retrieve a security descriptor that allows all access */
+SECURITY_DESCRIPTOR *__stdcall get_null_sd ();
+
+/* Various types of security attributes for use in Create* functions. */
+extern SECURITY_ATTRIBUTES sec_none, sec_none_nih, sec_all, sec_all_nih;
+extern SECURITY_ATTRIBUTES *__stdcall __sec_user (PVOID sa_buf, PSID sid1, PSID sid2,
+ DWORD access2, BOOL inherit)
+ __attribute__ ((regparm (3)));
+extern PSECURITY_DESCRIPTOR _everyone_sd (void *buf, ACCESS_MASK access);
+#define everyone_sd(access) (_everyone_sd (alloca (SD_MIN_SIZE), (access)))
+
+extern bool sec_acl (PACL acl, bool original, bool admins, PSID sid1 = NO_SID,
+ PSID sid2 = NO_SID, DWORD access2 = 0);
+
+ssize_t __stdcall read_ea (HANDLE hdl, path_conv &pc, const char *name,
+ char *value, size_t size);
+int __stdcall write_ea (HANDLE hdl, path_conv &pc, const char *name,
+ const char *value, size_t size, int flags);
+
+/* Note: sid1 is usually (read: currently always) the current user's
+ effective sid (cygheap->user.sid ()). */
+extern inline SECURITY_ATTRIBUTES *
+sec_user_nih (SECURITY_ATTRIBUTES *sa_buf, PSID sid1, PSID sid2 = NULL,
+ DWORD access2 = 0)
+{
+ return __sec_user (sa_buf, sid1, sid2, access2, FALSE);
+}
+
+extern inline SECURITY_ATTRIBUTES *
+sec_user (SECURITY_ATTRIBUTES *sa_buf, PSID sid1, PSID sid2 = NULL,
+ DWORD access2 = 0)
+{
+ return __sec_user (sa_buf, sid1, sid2, access2, TRUE);
+}
+#endif /*_SECURITY_H*/