OSDN Git Service

* sec_auth.cc (extract_nt_dom_user): Return domain and user name as
authorCorinna Vinschen <vinschen@redhat.com>
Wed, 30 Jul 2008 12:10:20 +0000 (12:10 +0000)
committerCorinna Vinschen <vinschen@redhat.com>
Wed, 30 Jul 2008 12:10:20 +0000 (12:10 +0000)
WCHAR.
(cygwin_logon_user): Accommodate above change.  Convert password to
WCHAR and call LogonUserW.
* external.cc (cygwin_internal): Accommodate above change.
* security.h (extract_nt_dom_user): Change prototype accordingly.

winsup/cygwin/ChangeLog
winsup/cygwin/external.cc [new file with mode: 0644]
winsup/cygwin/sec_auth.cc [new file with mode: 0644]
winsup/cygwin/security.h [new file with mode: 0644]

index cde34cf..8a84c10 100644 (file)
@@ -1,3 +1,12 @@
+2008-07-30  Corinna Vinschen  <corinna@vinschen.de>
+
+       * sec_auth.cc (extract_nt_dom_user): Return domain and user name as
+       WCHAR.
+       (cygwin_logon_user): Accommodate above change.  Convert password to
+       WCHAR and call LogonUserW.
+       * external.cc (cygwin_internal): Accommodate above change.
+       * security.h (extract_nt_dom_user): Change prototype accordingly.
+
 2008-07-30  Christopher Faylor  <me+cygwin@cgf.cx>
 
        * cygwin.din (_getutline): Remove.
diff --git a/winsup/cygwin/external.cc b/winsup/cygwin/external.cc
new file mode 100644 (file)
index 0000000..d9ab4d5
--- /dev/null
@@ -0,0 +1,365 @@
+/* external.cc: Interface to Cygwin internals from external programs.
+
+   Copyright 1997, 1998, 1999, 2000, 2001, 2002, 2003, 2004, 2005,
+   2006, 2007, 2008 Red Hat, Inc.
+
+   Written by Christopher Faylor <cgf@cygnus.com>
+
+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 "sigproc.h"
+#include "pinfo.h"
+#include "shared_info.h"
+#include "cygwin_version.h"
+#include "cygerrno.h"
+#include "path.h"
+#include "fhandler.h"
+#include "dtable.h"
+#include "cygheap.h"
+#include "heap.h"
+#include "cygtls.h"
+#include "child_info.h"
+#include "environ.h"
+#include <unistd.h>
+#include <stdlib.h>
+#include <wchar.h>
+#include <iptypes.h>
+
+child_info *get_cygwin_startup_info ();
+
+static winpids pids;
+
+static external_pinfo *
+fillout_pinfo (pid_t pid, int winpid)
+{
+  BOOL nextpid;
+  static external_pinfo ep;
+  static char ep_progname_long_buf[NT_MAX_PATH];
+
+  if ((nextpid = !!(pid & CW_NEXTPID)))
+    pid ^= CW_NEXTPID;
+
+
+  static unsigned int i;
+  if (!pids.npids || !nextpid)
+    {
+      pids.set (winpid);
+      i = 0;
+    }
+
+  if (!pid)
+    i = 0;
+
+  memset (&ep, 0, sizeof ep);
+  while (i < pids.npids)
+    {
+      DWORD thispid = pids.winpid (i);
+      _pinfo *p = pids[i];
+      i++;
+
+      if (!p)
+       {
+         if (!nextpid && thispid != (DWORD) pid)
+           continue;
+         ep.pid = cygwin_pid (thispid);
+         ep.dwProcessId = thispid;
+         ep.process_state = PID_IN_USE;
+         ep.ctty = -1;
+         break;
+       }
+      else if (nextpid || p->pid == pid || (winpid && thispid == (DWORD) pid))
+       {
+         ep.ctty = p->ctty;
+         ep.pid = p->pid;
+         ep.ppid = p->ppid;
+         ep.dwProcessId = p->dwProcessId;
+         ep.uid = p->uid;
+         ep.gid = p->gid;
+         ep.pgid = p->pgid;
+         ep.sid = p->sid;
+         ep.umask = 0;
+         ep.start_time = p->start_time;
+         ep.rusage_self = p->rusage_self;
+         ep.rusage_children = p->rusage_children;
+         ep.progname[0] = '\0';
+         strncat (ep.progname, p->progname, MAX_PATH - 1);
+         ep.strace_mask = 0;
+         ep.version = EXTERNAL_PINFO_VERSION;
+
+         ep.process_state = p->process_state;
+
+         ep.uid32 = p->uid;
+         ep.gid32 = p->gid;
+
+         ep.progname_long = ep_progname_long_buf;
+         strcpy (ep.progname_long, p->progname);
+         break;
+       }
+    }
+
+  if (!ep.pid)
+    {
+      i = 0;
+      pids.reset ();
+      return 0;
+    }
+  return &ep;
+}
+
+static inline DWORD
+get_cygdrive_info (char *user, char *system, char *user_flags,
+                  char *system_flags)
+{
+  int res = mount_table->get_cygdrive_info (user, system, user_flags,
+                                           system_flags);
+  return (res == ERROR_SUCCESS) ? 1 : 0;
+}
+
+static DWORD
+check_ntsec (const char *filename)
+{
+  if (!filename)
+    return true;
+  path_conv pc (filename);
+  return pc.has_acls ();
+}
+
+/* Copy cygwin environment variables to the Windows environment. */
+static void
+sync_winenv ()
+{
+  int unused_envc;
+  PWCHAR envblock = NULL;
+  char **envp = build_env (cur_environ (), envblock, unused_envc, false);
+  PWCHAR p = envblock;
+
+  if (envp)
+    {
+      for (char **e = envp; *e; e++)
+       cfree (*e);
+      cfree (envp);
+    }
+  if (!p)
+    return;
+  while (*p)
+    {
+      PWCHAR eq = wcschr (p, L'=');
+      if (eq)
+       {
+         *eq = L'\0';
+         SetEnvironmentVariableW (p, ++eq);
+         p = eq;
+       }
+      p = wcschr (p, L'\0') + 1;
+    }
+  free (envblock);
+}
+
+extern "C" unsigned long
+cygwin_internal (cygwin_getinfo_types t, ...)
+{
+  va_list arg;
+  va_start (arg, t);
+
+  switch (t)
+    {
+      case CW_LOCK_PINFO:
+       return 1;
+
+      case CW_UNLOCK_PINFO:
+       return 1;
+
+      case CW_GETTHREADNAME:
+       return (DWORD) cygthread::name (va_arg (arg, DWORD));
+
+      case CW_SETTHREADNAME:
+       {
+         set_errno (ENOSYS);
+         return 0;
+       }
+
+      case CW_GETPINFO:
+       return (DWORD) fillout_pinfo (va_arg (arg, DWORD), 0);
+
+      case CW_GETVERSIONINFO:
+       return (DWORD) cygwin_version_strings;
+
+      case CW_READ_V1_MOUNT_TABLES:
+       set_errno (ENOSYS);
+       return 1;
+
+      case CW_USER_DATA:
+       return (DWORD) &__cygwin_user_data;
+
+      case CW_PERFILE:
+       perfile_table = va_arg (arg, struct __cygwin_perfile *);
+       return 0;
+
+      case CW_GET_CYGDRIVE_PREFIXES:
+       {
+         char *user = va_arg (arg, char *);
+         char *system = va_arg (arg, char *);
+         return get_cygdrive_info (user, system, NULL, NULL);
+       }
+
+      case CW_GETPINFO_FULL:
+       return (DWORD) fillout_pinfo (va_arg (arg, pid_t), 1);
+
+      case CW_INIT_EXCEPTIONS:
+       /* noop */ /* init_exceptions (va_arg (arg, exception_list *)); */
+       return 0;
+
+      case CW_GET_CYGDRIVE_INFO:
+       {
+         char *user = va_arg (arg, char *);
+         char *system = va_arg (arg, char *);
+         char *user_flags = va_arg (arg, char *);
+         char *system_flags = va_arg (arg, char *);
+         return get_cygdrive_info (user, system, user_flags, system_flags);
+       }
+
+      case CW_SET_CYGWIN_REGISTRY_NAME:
+      case CW_GET_CYGWIN_REGISTRY_NAME:
+       return 0;
+
+      case CW_STRACE_TOGGLE:
+       {
+         pid_t pid = va_arg (arg, pid_t);
+         pinfo p (pid);
+         if (p)
+           {
+             sig_send (p, __SIGSTRACE);
+             return 0;
+           }
+         else
+           {
+             set_errno (ESRCH);
+             return (DWORD) -1;
+           }
+       }
+
+      case CW_STRACE_ACTIVE:
+       {
+         return strace.active ();
+       }
+
+      case CW_CYGWIN_PID_TO_WINPID:
+       {
+         pinfo p (va_arg (arg, pid_t));
+         return p ? p->dwProcessId : 0;
+       }
+      case CW_EXTRACT_DOMAIN_AND_USER:
+       {
+         WCHAR nt_domain[MAX_DOMAIN_NAME_LEN + 1];
+         WCHAR nt_user[UNLEN + 1];
+
+         struct passwd *pw = va_arg (arg, struct passwd *);
+         char *domain = va_arg (arg, char *);
+         char *user = va_arg (arg, char *);
+         extract_nt_dom_user (pw, nt_domain, nt_user);
+         if (domain)
+           sys_wcstombs (domain, MAX_DOMAIN_NAME_LEN + 1, nt_domain);
+         if (user)
+           sys_wcstombs (user, UNLEN + 1, nt_user);
+         return 0;
+       }
+      case CW_CMDLINE:
+       {
+         size_t n;
+         pid_t pid = va_arg (arg, pid_t);
+         pinfo p (pid);
+         return (DWORD) p->cmdline (n);
+       }
+      case CW_CHECK_NTSEC:
+       {
+         char *filename = va_arg (arg, char *);
+         return check_ntsec (filename);
+       }
+      case CW_GET_ERRNO_FROM_WINERROR:
+       {
+         int error = va_arg (arg, int);
+         int deferrno = va_arg (arg, int);
+         return geterrno_from_win_error (error, deferrno);
+       }
+      case CW_GET_POSIX_SECURITY_ATTRIBUTE:
+       {
+         security_descriptor sd;
+         int attribute = va_arg (arg, int);
+         PSECURITY_ATTRIBUTES psa = va_arg (arg, PSECURITY_ATTRIBUTES);
+         void *sd_buf = va_arg (arg, void *);
+         DWORD sd_buf_size = va_arg (arg, DWORD);
+         set_security_attribute (attribute, psa, sd);
+         if (!psa->lpSecurityDescriptor)
+           return sd.size ();
+         psa->lpSecurityDescriptor = sd_buf;
+         return sd.copy (sd_buf, sd_buf_size);
+       }
+      case CW_GET_SHMLBA:
+       {
+         return getpagesize ();
+       }
+      case CW_GET_UID_FROM_SID:
+       {
+         cygpsid psid = va_arg (arg, PSID);
+         return psid.get_id (false, NULL);
+       }
+      case CW_GET_GID_FROM_SID:
+       {
+         cygpsid psid = va_arg (arg, PSID);
+         return psid.get_id (true, NULL);
+       }
+      case CW_GET_BINMODE:
+       {
+         const char *path = va_arg (arg, const char *);
+         path_conv p (path, PC_SYM_FOLLOW | PC_NULLEMPTY);
+         if (p.error)
+           {
+             set_errno (p.error);
+             return (unsigned long) -1;
+           }
+         return p.binmode ();
+       }
+      case CW_HOOK:
+       {
+         const char *name = va_arg (arg, const char *);
+         const void *hookfn = va_arg (arg, const void *);
+         WORD subsys;
+         return (unsigned long) hook_or_detect_cygwin (name, hookfn, subsys);
+       }
+      case CW_ARGV:
+       {
+         child_info_spawn *ci = (child_info_spawn *) get_cygwin_startup_info ();
+         return (unsigned long) (ci ? ci->moreinfo->argv : NULL);
+       }
+      case CW_ENVP:
+       {
+         child_info_spawn *ci = (child_info_spawn *) get_cygwin_startup_info ();
+         return (unsigned long) (ci ? ci->moreinfo->envp : NULL);
+       }
+      case CW_DEBUG_SELF:
+       error_start_init (va_arg (arg, const char *));
+       try_to_debug ();
+       break;
+      case CW_SYNC_WINENV:
+       sync_winenv ();
+       return 0;
+      case CW_CYGTLS_PADSIZE:
+       return CYGTLS_PADSIZE;
+      case CW_SET_DOS_FILE_WARNING:
+       {
+         extern bool dos_file_warning;
+         dos_file_warning = va_arg (arg, int);
+       }
+       break;
+
+      default:
+       break;
+    }
+  set_errno (ENOSYS);
+  return (unsigned long) -1;
+}
diff --git a/winsup/cygwin/sec_auth.cc b/winsup/cygwin/sec_auth.cc
new file mode 100644 (file)
index 0000000..5043455
--- /dev/null
@@ -0,0 +1,1171 @@
+/* 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, &quota, &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;
+}
diff --git a/winsup/cygwin/security.h b/winsup/cygwin/security.h
new file mode 100644 (file)
index 0000000..9be7a28
--- /dev/null
@@ -0,0 +1,450 @@
+/* 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*/