OSDN Git Service

0eb77acccfccc6a5eef307c59bc2dc2ccefed827
[pf3gnuchains/pf3gnuchains3x.git] / winsup / cygwin / sec_auth.cc
1 /* sec_auth.cc: NT authentication functions
2
3    Copyright 1997, 1998, 1999, 2000, 2001, 2002, 2003, 2004, 2005,
4    2006, 2007, 2008 Red Hat, Inc.
5
6 This file is part of Cygwin.
7
8 This software is a copyrighted work licensed under the terms of the
9 Cygwin license.  Please consult the file "CYGWIN_LICENSE" for
10 details. */
11
12 #include "winsup.h"
13 #include <stdlib.h>
14 #include <wininet.h>
15 #include <ntsecapi.h>
16 #include <dsgetdc.h>
17 #include "cygerrno.h"
18 #include "security.h"
19 #include "path.h"
20 #include "fhandler.h"
21 #include "dtable.h"
22 #include "pinfo.h"
23 #include "cygheap.h"
24 #include "ntdll.h"
25 #include "lm.h"
26 #include "pwdgrp.h"
27 #include "cyglsa.h"
28 #include <cygwin/version.h>
29
30 extern "C" void
31 cygwin_set_impersonation_token (const HANDLE hToken)
32 {
33   debug_printf ("set_impersonation_token (%d)", hToken);
34   cygheap->user.external_token = hToken == INVALID_HANDLE_VALUE ? NO_IMPERSONATION : hToken;
35 }
36
37 void
38 extract_nt_dom_user (const struct passwd *pw, char *domain, char *user)
39 {
40   char *d, *u, *c;
41
42   domain[0] = 0;
43   strlcpy (user, pw->pw_name, UNLEN + 1);
44   debug_printf ("pw_gecos %x (%s)", pw->pw_gecos, pw->pw_gecos);
45
46   if ((d = strstr (pw->pw_gecos, "U-")) != NULL &&
47       (d == pw->pw_gecos || d[-1] == ','))
48     {
49       c = strechr (d + 2, ',');
50       if ((u = strechr (d + 2, '\\')) >= c)
51         u = d + 1;
52       else if (u - d <= INTERNET_MAX_HOST_NAME_LENGTH + 2)
53         strlcpy (domain, d + 2, u - d - 1);
54       if (c - u <= UNLEN + 1)
55         strlcpy (user, u + 1, c - u);
56     }
57   if (domain[0])
58     return;
59
60   cygsid psid;
61   DWORD ulen = UNLEN + 1;
62   DWORD dlen = INTERNET_MAX_HOST_NAME_LENGTH + 1;
63   SID_NAME_USE use;
64   if (psid.getfrompw (pw))
65     LookupAccountSid (NULL, psid, user, &ulen, domain, &dlen, &use);
66 }
67
68 extern "C" HANDLE
69 cygwin_logon_user (const struct passwd *pw, const char *password)
70 {
71   if (!pw)
72     {
73       set_errno (EINVAL);
74       return INVALID_HANDLE_VALUE;
75     }
76
77   char nt_domain[INTERNET_MAX_HOST_NAME_LENGTH + 1];
78   char nt_user[UNLEN + 1];
79   HANDLE hToken;
80
81   extract_nt_dom_user (pw, nt_domain, nt_user);
82   debug_printf ("LogonUserA (%s, %s, %s, ...)", nt_user, nt_domain, password);
83   /* CV 2005-06-08: LogonUser should run under the primary process token,
84      otherwise it returns with ERROR_ACCESS_DENIED on W2K. Don't ask me why. */
85   RevertToSelf ();
86   if (!LogonUserA (nt_user, *nt_domain ? nt_domain : NULL, (char *) password,
87                    LOGON32_LOGON_INTERACTIVE,
88                    LOGON32_PROVIDER_DEFAULT,
89                    &hToken))
90     {
91       __seterrno ();
92       hToken = INVALID_HANDLE_VALUE;
93     }
94   else if (!SetHandleInformation (hToken,
95                                   HANDLE_FLAG_INHERIT,
96                                   HANDLE_FLAG_INHERIT))
97     {
98       __seterrno ();
99       CloseHandle (hToken);
100       hToken = INVALID_HANDLE_VALUE;
101     }
102   cygheap->user.reimpersonate ();
103   debug_printf ("%d = logon_user(%s,...)", hToken, pw->pw_name);
104   return hToken;
105 }
106
107 static void
108 str2lsa (LSA_STRING &tgt, const char *srcstr)
109 {
110   tgt.Length = strlen (srcstr);
111   tgt.MaximumLength = tgt.Length + 1;
112   tgt.Buffer = (PCHAR) srcstr;
113 }
114
115 static void
116 str2buf2lsa (LSA_STRING &tgt, char *buf, const char *srcstr)
117 {
118   tgt.Length = strlen (srcstr);
119   tgt.MaximumLength = tgt.Length + 1;
120   tgt.Buffer = (PCHAR) buf;
121   memcpy (buf, srcstr, tgt.MaximumLength);
122 }
123
124 /* The dimension of buf is assumed to be at least strlen(srcstr) + 1,
125    The result will be shorter if the input has multibyte chars */
126 void
127 str2buf2uni (UNICODE_STRING &tgt, WCHAR *buf, const char *srcstr)
128 {
129   tgt.Buffer = (PWCHAR) buf;
130   tgt.MaximumLength = (strlen (srcstr) + 1) * sizeof (WCHAR);
131   tgt.Length = sys_mbstowcs (buf, tgt.MaximumLength / sizeof (WCHAR), srcstr)
132                * sizeof (WCHAR);
133   if (tgt.Length)
134     tgt.Length -= sizeof (WCHAR);
135 }
136
137 void
138 str2uni_cat (UNICODE_STRING &tgt, const char *srcstr)
139 {
140   int len = sys_mbstowcs (tgt.Buffer + tgt.Length / sizeof (WCHAR),
141                           (tgt.MaximumLength - tgt.Length) / sizeof (WCHAR),
142                           srcstr);
143   if (len)
144     tgt.Length += (len - 1) * sizeof (WCHAR);
145   else
146     tgt.Length = tgt.MaximumLength = 0;
147 }
148
149 static LSA_HANDLE
150 open_local_policy ()
151 {
152   LSA_OBJECT_ATTRIBUTES oa = { 0, 0, 0, 0, 0, 0 };
153   LSA_HANDLE lsa = INVALID_HANDLE_VALUE;
154
155   NTSTATUS ret = LsaOpenPolicy (NULL, &oa, POLICY_EXECUTE, &lsa);
156   if (ret != STATUS_SUCCESS)
157     __seterrno_from_win_error (LsaNtStatusToWinError (ret));
158   return lsa;
159 }
160
161 static void
162 close_local_policy (LSA_HANDLE &lsa)
163 {
164   if (lsa != INVALID_HANDLE_VALUE)
165     LsaClose (lsa);
166   lsa = INVALID_HANDLE_VALUE;
167 }
168
169 /* CV, 2006-07-06: Missing in w32api. */
170 extern "C" DWORD WINAPI DsGetDcNameA (LPCSTR, LPCSTR, GUID *, LPCSTR, ULONG,
171                                       PDOMAIN_CONTROLLER_INFOA *);
172 #define DS_FORCE_REDISCOVERY    1
173
174 bool
175 get_logon_server (const char *domain, char *server, WCHAR *wserver,
176                   bool rediscovery)
177 {
178   DWORD dret;
179   PDOMAIN_CONTROLLER_INFOA pci;
180   WCHAR *buf;
181   DWORD size = INTERNET_MAX_HOST_NAME_LENGTH + 1;
182   WCHAR wdomain[size];
183
184   /* Empty domain is interpreted as local system */
185   if ((GetComputerName (server + 2, &size)) &&
186       (strcasematch (domain, server + 2) || !domain[0]))
187     {
188       server[0] = server[1] = '\\';
189       if (wserver)
190         sys_mbstowcs (wserver, INTERNET_MAX_HOST_NAME_LENGTH + 1, server);
191       return true;
192     }
193
194   /* Try to get any available domain controller for this domain */
195   dret = DsGetDcNameA (NULL, domain, NULL, NULL,
196                        rediscovery ? DS_FORCE_REDISCOVERY : 0, &pci);
197   if (dret == ERROR_SUCCESS)
198     {
199       strcpy (server, pci->DomainControllerName);
200       sys_mbstowcs (wserver, INTERNET_MAX_HOST_NAME_LENGTH + 1, server);
201       NetApiBufferFree (pci);
202       debug_printf ("DC: rediscovery: %d, server: %s", rediscovery, server);
203       return true;
204     }
205   else if (dret == ERROR_PROC_NOT_FOUND)
206     {
207       /* NT4 w/o DSClient */
208       sys_mbstowcs (wdomain, INTERNET_MAX_HOST_NAME_LENGTH + 1, domain);
209       if (rediscovery)
210         dret = NetGetAnyDCName (NULL, wdomain, (LPBYTE *) &buf);
211       else
212         dret = NetGetDCName (NULL, wdomain, (LPBYTE *) &buf);
213       if (dret == NERR_Success)
214         {
215           sys_wcstombs (server, INTERNET_MAX_HOST_NAME_LENGTH + 1, buf);
216           if (wserver)
217             for (WCHAR *ptr1 = buf; (*wserver++ = *ptr1++);)
218               ;
219           NetApiBufferFree (buf);
220           debug_printf ("NT: rediscovery: %d, server: %s", rediscovery, server);
221           return true;
222         }
223     }
224   __seterrno_from_win_error (dret);
225   return false;
226 }
227
228 static bool
229 get_user_groups (WCHAR *wlogonserver, cygsidlist &grp_list, char *user,
230                  char *domain)
231 {
232   char dgroup[INTERNET_MAX_HOST_NAME_LENGTH + GNLEN + 2];
233   WCHAR wuser[UNLEN + 1];
234   sys_mbstowcs (wuser, UNLEN + 1, user);
235   LPGROUP_USERS_INFO_0 buf;
236   DWORD cnt, tot, len;
237   NET_API_STATUS ret;
238
239   /* Look only on logonserver */
240   ret = NetUserGetGroups (wlogonserver, wuser, 0, (LPBYTE *) &buf,
241                           MAX_PREFERRED_LENGTH, &cnt, &tot);
242   if (ret)
243     {
244       __seterrno_from_win_error (ret);
245       /* It's no error when the user name can't be found. */
246       return ret == NERR_UserNotFound;
247     }
248
249   len = strlen (domain);
250   strcpy (dgroup, domain);
251   dgroup[len++] = '\\';
252
253   for (DWORD i = 0; i < cnt; ++i)
254     {
255       cygsid gsid;
256       DWORD glen = MAX_SID_LEN;
257       char domain[INTERNET_MAX_HOST_NAME_LENGTH + 1];
258       DWORD dlen = sizeof (domain);
259       SID_NAME_USE use = SidTypeInvalid;
260
261       sys_wcstombs (dgroup + len, GNLEN + 1, buf[i].grui0_name);
262       if (!LookupAccountName (NULL, dgroup, gsid, &glen, domain, &dlen, &use))
263         debug_printf ("LookupAccountName(%s), %E", dgroup);
264       else if (legal_sid_type (use))
265         grp_list += gsid;
266       else
267         debug_printf ("Global group %s invalid. Domain: %s Use: %d",
268                       dgroup, domain, use);
269     }
270
271   NetApiBufferFree (buf);
272   return true;
273 }
274
275 static bool
276 is_group_member (WCHAR *wgroup, PSID pusersid, cygsidlist &grp_list)
277 {
278   LPLOCALGROUP_MEMBERS_INFO_1 buf;
279   DWORD cnt, tot;
280   NET_API_STATUS ret;
281
282   /* Members can be users or global groups */
283   ret = NetLocalGroupGetMembers (NULL, wgroup, 1, (LPBYTE *) &buf,
284                                  MAX_PREFERRED_LENGTH, &cnt, &tot, NULL);
285   if (ret)
286     return false;
287
288   bool retval = true;
289   for (DWORD bidx = 0; bidx < cnt; ++bidx)
290     if (EqualSid (pusersid, buf[bidx].lgrmi1_sid))
291       goto done;
292     else
293       {
294         /* The extra test for the group being a global group or a well-known
295            group is necessary, since apparently also aliases (for instance
296            Administrators or Users) can be members of local groups, even
297            though MSDN states otherwise.  The GUI refuses to put aliases into
298            local groups, but the CLI interface allows it.  However, a normal
299            logon token does not contain groups, in which the user is only
300            indirectly a member by being a member of an alias in this group.
301            So we also should not put them into the token group list.
302            Note: Allowing those groups in our group list renders external
303            tokens invalid, so that it becomes impossible to logon with
304            password and valid logon token. */
305         for (int glidx = 0; glidx < grp_list.count (); ++glidx)
306           if ((buf[bidx].lgrmi1_sidusage == SidTypeGroup
307                || buf[bidx].lgrmi1_sidusage == SidTypeWellKnownGroup)
308               && EqualSid (grp_list.sids[glidx], buf[bidx].lgrmi1_sid))
309             goto done;
310       }
311
312   retval = false;
313  done:
314   NetApiBufferFree (buf);
315   return retval;
316 }
317
318 static bool
319 get_user_local_groups (cygsidlist &grp_list, PSID pusersid)
320 {
321   LPLOCALGROUP_INFO_0 buf;
322   DWORD cnt, tot;
323   NET_API_STATUS ret;
324
325   ret = NetLocalGroupEnum (NULL, 0, (LPBYTE *) &buf,
326                            MAX_PREFERRED_LENGTH, &cnt, &tot, NULL);
327   if (ret)
328     {
329       __seterrno_from_win_error (ret);
330       return false;
331     }
332
333   char bgroup[INTERNET_MAX_HOST_NAME_LENGTH + GNLEN + 2];
334   char lgroup[INTERNET_MAX_HOST_NAME_LENGTH + GNLEN + 2];
335   DWORD blen, llen;
336   SID_NAME_USE use;
337
338   blen = llen = INTERNET_MAX_HOST_NAME_LENGTH + 1;
339   if (!LookupAccountSid (NULL, well_known_admins_sid, lgroup, &llen, bgroup, &blen, &use)
340       || !GetComputerNameA (lgroup, &(llen = INTERNET_MAX_HOST_NAME_LENGTH + 1)))
341     {
342       __seterrno ();
343       return false;
344     }
345   bgroup[blen++] = lgroup[llen++] = '\\';
346
347   for (DWORD i = 0; i < cnt; ++i)
348     if (is_group_member (buf[i].lgrpi0_name, pusersid, grp_list))
349       {
350         cygsid gsid;
351         DWORD glen = MAX_SID_LEN;
352         char domain[INTERNET_MAX_HOST_NAME_LENGTH + 1];
353         DWORD dlen = sizeof (domain);
354
355         use = SidTypeInvalid;
356         sys_wcstombs (bgroup + blen, GNLEN + 1, buf[i].lgrpi0_name);
357         if (!LookupAccountName (NULL, bgroup, gsid, &glen, domain, &dlen, &use))
358           {
359             if (GetLastError () != ERROR_NONE_MAPPED)
360               debug_printf ("LookupAccountName(%s), %E", bgroup);
361             strcpy (lgroup + llen, bgroup + blen);
362             if (!LookupAccountName (NULL, lgroup, gsid, &glen,
363                                     domain, &dlen, &use))
364               debug_printf ("LookupAccountName(%s), %E", lgroup);
365           }
366         if (!legal_sid_type (use))
367           debug_printf ("Rejecting local %s. use: %d", bgroup + blen, use);
368         grp_list *= gsid;
369       }
370   NetApiBufferFree (buf);
371   return true;
372 }
373
374 static bool
375 sid_in_token_groups (PTOKEN_GROUPS grps, cygpsid sid)
376 {
377   if (!grps)
378     return false;
379   for (DWORD i = 0; i < grps->GroupCount; ++i)
380     if (sid == grps->Groups[i].Sid)
381       return true;
382   return false;
383 }
384
385 static void
386 get_unix_group_sidlist (struct passwd *pw, cygsidlist &grp_list)
387 {
388   struct __group32 *gr;
389   cygsid gsid;
390
391   for (int gidx = 0; (gr = internal_getgrent (gidx)); ++gidx)
392     {
393       if (gr->gr_gid == (__gid32_t) pw->pw_gid)
394         goto found;
395       else if (gr->gr_mem)
396         for (int gi = 0; gr->gr_mem[gi]; ++gi)
397           if (strcasematch (pw->pw_name, gr->gr_mem[gi]))
398             goto found;
399       continue;
400     found:
401       if (gsid.getfromgr (gr))
402         grp_list += gsid;
403
404     }
405 }
406
407 static void
408 get_token_group_sidlist (cygsidlist &grp_list, PTOKEN_GROUPS my_grps,
409                          LUID auth_luid, int &auth_pos)
410 {
411   auth_pos = -1;
412   if (my_grps)
413     {
414       grp_list += well_known_local_sid;
415       if (sid_in_token_groups (my_grps, well_known_dialup_sid))
416         grp_list *= well_known_dialup_sid;
417       if (sid_in_token_groups (my_grps, well_known_network_sid))
418         grp_list *= well_known_network_sid;
419       if (sid_in_token_groups (my_grps, well_known_batch_sid))
420         grp_list *= well_known_batch_sid;
421       grp_list *= well_known_interactive_sid;
422       if (sid_in_token_groups (my_grps, well_known_service_sid))
423         grp_list *= well_known_service_sid;
424       if (sid_in_token_groups (my_grps, well_known_this_org_sid))
425         grp_list *= well_known_this_org_sid;
426     }
427   else
428     {
429       grp_list += well_known_local_sid;
430       grp_list *= well_known_interactive_sid;
431     }
432   if (get_ll (auth_luid) != 999LL) /* != SYSTEM_LUID */
433     {
434       for (DWORD i = 0; i < my_grps->GroupCount; ++i)
435         if (my_grps->Groups[i].Attributes & SE_GROUP_LOGON_ID)
436           {
437             grp_list += my_grps->Groups[i].Sid;
438             auth_pos = grp_list.count () - 1;
439             break;
440           }
441     }
442 }
443
444 bool
445 get_server_groups (cygsidlist &grp_list, PSID usersid, struct passwd *pw)
446 {
447   char user[UNLEN + 1];
448   char domain[INTERNET_MAX_HOST_NAME_LENGTH + 1];
449   WCHAR wserver[INTERNET_MAX_HOST_NAME_LENGTH + 3];
450   char server[INTERNET_MAX_HOST_NAME_LENGTH + 3];
451
452   if (well_known_system_sid == usersid)
453     {
454       grp_list *= well_known_admins_sid;
455       get_unix_group_sidlist (pw, grp_list);
456       return true;
457     }
458
459   grp_list *= well_known_world_sid;
460   grp_list *= well_known_authenticated_users_sid;
461   extract_nt_dom_user (pw, domain, user);
462   if (get_logon_server (domain, server, wserver, false)
463       && !get_user_groups (wserver, grp_list, user, domain)
464       && get_logon_server (domain, server, wserver, true))
465     get_user_groups (wserver, grp_list, user, domain);
466   if (get_user_local_groups (grp_list, usersid))
467     {
468       get_unix_group_sidlist (pw, grp_list);
469       return true;
470     }
471   return false;
472 }
473
474 static bool
475 get_initgroups_sidlist (cygsidlist &grp_list,
476                         PSID usersid, PSID pgrpsid, struct passwd *pw,
477                         PTOKEN_GROUPS my_grps, LUID auth_luid, int &auth_pos)
478 {
479   grp_list *= well_known_world_sid;
480   grp_list *= well_known_authenticated_users_sid;
481   if (well_known_system_sid == usersid)
482     auth_pos = -1;
483   else
484     get_token_group_sidlist (grp_list, my_grps, auth_luid, auth_pos);
485   if (!get_server_groups (grp_list, usersid, pw))
486     return false;
487
488   /* special_pgrp true if pgrpsid is not in normal groups */
489   grp_list += pgrpsid;
490   return true;
491 }
492
493 static void
494 get_setgroups_sidlist (cygsidlist &tmp_list, PSID usersid, struct passwd *pw,
495                        PTOKEN_GROUPS my_grps, user_groups &groups,
496                        LUID auth_luid, int &auth_pos)
497 {
498   tmp_list *= well_known_world_sid;
499   tmp_list *= well_known_authenticated_users_sid;
500   get_token_group_sidlist (tmp_list, my_grps, auth_luid, auth_pos);
501   get_server_groups (tmp_list, usersid, pw);
502   for (int gidx = 0; gidx < groups.sgsids.count (); gidx++)
503     tmp_list += groups.sgsids.sids[gidx];
504   tmp_list += groups.pgsid;
505 }
506
507 static ULONG sys_privs[] = {
508   SE_CREATE_TOKEN_PRIVILEGE,
509   SE_ASSIGNPRIMARYTOKEN_PRIVILEGE,
510   SE_LOCK_MEMORY_PRIVILEGE,
511   SE_INCREASE_QUOTA_PRIVILEGE,
512   SE_TCB_PRIVILEGE,
513   SE_SECURITY_PRIVILEGE,
514   SE_TAKE_OWNERSHIP_PRIVILEGE,
515   SE_LOAD_DRIVER_PRIVILEGE,
516   SE_SYSTEM_PROFILE_PRIVILEGE,          /* Vista ONLY */
517   SE_SYSTEMTIME_PRIVILEGE,
518   SE_PROF_SINGLE_PROCESS_PRIVILEGE,
519   SE_INC_BASE_PRIORITY_PRIVILEGE,
520   SE_CREATE_PAGEFILE_PRIVILEGE,
521   SE_CREATE_PERMANENT_PRIVILEGE,
522   SE_BACKUP_PRIVILEGE,
523   SE_RESTORE_PRIVILEGE,
524   SE_SHUTDOWN_PRIVILEGE,
525   SE_DEBUG_PRIVILEGE,
526   SE_AUDIT_PRIVILEGE,
527   SE_SYSTEM_ENVIRONMENT_PRIVILEGE,
528   SE_CHANGE_NOTIFY_PRIVILEGE,
529   SE_UNDOCK_PRIVILEGE,
530   SE_MANAGE_VOLUME_PRIVILEGE,
531   SE_IMPERSONATE_PRIVILEGE,
532   SE_CREATE_GLOBAL_PRIVILEGE,
533   SE_INCREASE_WORKING_SET_PRIVILEGE,
534   SE_TIME_ZONE_PRIVILEGE,
535   SE_CREATE_SYMBOLIC_LINK_PRIVILEGE
536 };
537
538 #define SYSTEM_PRIVILEGES_COUNT (sizeof sys_privs / sizeof *sys_privs)
539
540 static PTOKEN_PRIVILEGES
541 get_system_priv_list (size_t &size)
542 {
543   ULONG max_idx = 0;
544   while (max_idx < SYSTEM_PRIVILEGES_COUNT
545          && sys_privs[max_idx] != wincap.max_sys_priv ())
546     ++max_idx;
547   if (max_idx >= SYSTEM_PRIVILEGES_COUNT)
548     api_fatal ("Coding error: wincap privilege %u doesn't exist in sys_privs",
549                wincap.max_sys_priv ());
550   size = sizeof (ULONG) + (max_idx + 1) * sizeof (LUID_AND_ATTRIBUTES);
551   PTOKEN_PRIVILEGES privs = (PTOKEN_PRIVILEGES) malloc (size);
552   if (!privs)
553     {
554       debug_printf ("malloc (system_privs) failed.");
555       return NULL;
556     }
557   privs->PrivilegeCount = 0;
558   for (ULONG i = 0; i <= max_idx; ++i)
559     {
560       privs->Privileges[privs->PrivilegeCount].Luid.HighPart = 0L;
561       privs->Privileges[privs->PrivilegeCount].Luid.LowPart = sys_privs[i];
562       privs->Privileges[privs->PrivilegeCount].Attributes =
563         SE_PRIVILEGE_ENABLED | SE_PRIVILEGE_ENABLED_BY_DEFAULT;
564       ++privs->PrivilegeCount;
565     }
566   return privs;
567 }
568
569 static PTOKEN_PRIVILEGES
570 get_priv_list (LSA_HANDLE lsa, cygsid &usersid, cygsidlist &grp_list,
571                size_t &size)
572 {
573   PLSA_UNICODE_STRING privstrs;
574   ULONG cnt;
575   PTOKEN_PRIVILEGES privs = NULL;
576   NTSTATUS ret;
577   char buf[INTERNET_MAX_HOST_NAME_LENGTH + 1];
578
579   if (usersid == well_known_system_sid)
580     return get_system_priv_list (size);
581
582   for (int grp = -1; grp < grp_list.count (); ++grp)
583     {
584       if (grp == -1)
585         {
586           if ((ret = LsaEnumerateAccountRights (lsa, usersid, &privstrs,
587                                                 &cnt)) != STATUS_SUCCESS)
588             continue;
589         }
590       else if ((ret = LsaEnumerateAccountRights (lsa, grp_list.sids[grp],
591                                                  &privstrs, &cnt))
592                != STATUS_SUCCESS)
593         continue;
594       for (ULONG i = 0; i < cnt; ++i)
595         {
596           LUID priv;
597           PTOKEN_PRIVILEGES tmp;
598           DWORD tmp_count;
599
600           sys_wcstombs (buf, sizeof (buf),
601                         privstrs[i].Buffer, privstrs[i].Length / 2);
602           if (!privilege_luid (buf, &priv))
603             continue;
604
605           if (privs)
606             {
607               DWORD pcnt = privs->PrivilegeCount;
608               LUID_AND_ATTRIBUTES *p = privs->Privileges;
609               for (; pcnt > 0; --pcnt, ++p)
610                 if (priv.HighPart == p->Luid.HighPart
611                     && priv.LowPart == p->Luid.LowPart)
612                   goto next_account_right;
613             }
614
615           tmp_count = privs ? privs->PrivilegeCount : 0;
616           size = sizeof (DWORD)
617                  + (tmp_count + 1) * sizeof (LUID_AND_ATTRIBUTES);
618           tmp = (PTOKEN_PRIVILEGES) realloc (privs, size);
619           if (!tmp)
620             {
621               if (privs)
622                 free (privs);
623               LsaFreeMemory (privstrs);
624               debug_printf ("realloc (privs) failed.");
625               return NULL;
626             }
627           tmp->PrivilegeCount = tmp_count;
628           privs = tmp;
629           privs->Privileges[privs->PrivilegeCount].Luid = priv;
630           privs->Privileges[privs->PrivilegeCount].Attributes =
631             SE_PRIVILEGE_ENABLED | SE_PRIVILEGE_ENABLED_BY_DEFAULT;
632           ++privs->PrivilegeCount;
633
634         next_account_right:
635           ;
636         }
637       LsaFreeMemory (privstrs);
638     }
639   return privs;
640 }
641
642 /* Accept a token if
643    - the requested usersid matches the TokenUser and
644    - if setgroups has been called:
645         the token groups that are listed in /etc/group match the union of
646         the requested primary and supplementary groups in gsids.
647    - else the (unknown) implicitly requested supplementary groups and those
648         in the token are the groups associated with the usersid. We assume
649         they match and verify only the primary groups.
650         The requested primary group must appear in the token.
651         The primary group in the token is a group associated with the usersid,
652         except if the token is internal and the group is in the token SD
653         (see create_token). In that latter case that group must match the
654         requested primary group.  */
655 bool
656 verify_token (HANDLE token, cygsid &usersid, user_groups &groups, bool *pintern)
657 {
658   DWORD size;
659   bool intern = false;
660
661   if (pintern)
662     {
663       TOKEN_SOURCE ts;
664       if (!GetTokenInformation (token, TokenSource,
665                                 &ts, sizeof ts, &size))
666         debug_printf ("GetTokenInformation(), %E");
667       else
668         *pintern = intern = !memcmp (ts.SourceName, "Cygwin.1", 8);
669     }
670   /* Verify usersid */
671   cygsid tok_usersid = NO_SID;
672   if (!GetTokenInformation (token, TokenUser,
673                             &tok_usersid, sizeof tok_usersid, &size))
674     debug_printf ("GetTokenInformation(), %E");
675   if (usersid != tok_usersid)
676     return false;
677
678   /* For an internal token, if setgroups was not called and if the sd group
679      is not well_known_null_sid, it must match pgrpsid */
680   if (intern && !groups.issetgroups ())
681     {
682       const DWORD sd_buf_siz = MAX_SID_LEN + sizeof (SECURITY_DESCRIPTOR);
683       PSECURITY_DESCRIPTOR sd_buf = (PSECURITY_DESCRIPTOR) alloca (sd_buf_siz);
684       cygpsid gsid (NO_SID);
685       if (!GetKernelObjectSecurity (token, GROUP_SECURITY_INFORMATION,
686                                     sd_buf, sd_buf_siz, &size))
687         debug_printf ("GetKernelObjectSecurity(), %E");
688       else if (!GetSecurityDescriptorGroup (sd_buf, (PSID *) &gsid,
689                                             (BOOL *) &size))
690         debug_printf ("GetSecurityDescriptorGroup(), %E");
691       if (well_known_null_sid != gsid)
692         return gsid == groups.pgsid;
693     }
694
695   PTOKEN_GROUPS my_grps;
696   bool sawpg = false, ret = false;
697
698   if (!GetTokenInformation (token, TokenGroups, NULL, 0, &size) &&
699       GetLastError () != ERROR_INSUFFICIENT_BUFFER)
700     debug_printf ("GetTokenInformation(token, TokenGroups), %E");
701   else if (!(my_grps = (PTOKEN_GROUPS) alloca (size)))
702     debug_printf ("alloca (my_grps) failed.");
703   else if (!GetTokenInformation (token, TokenGroups, my_grps, size, &size))
704     debug_printf ("GetTokenInformation(my_token, TokenGroups), %E");
705   else
706     {
707       if (groups.issetgroups ()) /* setgroups was called */
708         {
709           cygsid gsid;
710           struct __group32 *gr;
711           bool saw[groups.sgsids.count ()];
712           memset (saw, 0, sizeof(saw));
713
714           /* token groups found in /etc/group match the user.gsids ? */
715           for (int gidx = 0; (gr = internal_getgrent (gidx)); ++gidx)
716             if (gsid.getfromgr (gr) && sid_in_token_groups (my_grps, gsid))
717               {
718                 int pos = groups.sgsids.position (gsid);
719                 if (pos >= 0)
720                   saw[pos] = true;
721                 else if (groups.pgsid == gsid)
722                   sawpg = true;
723                 else if (gsid != well_known_world_sid
724                          && gsid != usersid)
725                   goto done;
726               }
727           /* user.sgsids groups must be in the token */
728           for (int gidx = 0; gidx < groups.sgsids.count (); gidx++)
729             if (!saw[gidx] && !sid_in_token_groups (my_grps, groups.sgsids.sids[gidx]))
730               goto done;
731         }
732       /* The primary group must be in the token */
733       ret = sawpg
734         || sid_in_token_groups (my_grps, groups.pgsid)
735         || groups.pgsid == usersid;
736     }
737 done:
738   return ret;
739 }
740
741 HANDLE
742 create_token (cygsid &usersid, user_groups &new_groups, struct passwd *pw)
743 {
744   NTSTATUS ret;
745   LSA_HANDLE lsa = INVALID_HANDLE_VALUE;
746
747   cygsidlist tmp_gsids (cygsidlist_auto, 12);
748
749   SECURITY_QUALITY_OF_SERVICE sqos =
750     { sizeof sqos, SecurityImpersonation, SECURITY_STATIC_TRACKING, FALSE };
751   OBJECT_ATTRIBUTES oa = { sizeof oa, 0, 0, 0, 0, &sqos };
752   LUID auth_luid = SYSTEM_LUID;
753   LARGE_INTEGER exp = { QuadPart:INT64_MAX };
754
755   TOKEN_USER user;
756   PTOKEN_GROUPS new_tok_gsids = NULL;
757   PTOKEN_PRIVILEGES privs = NULL;
758   TOKEN_OWNER owner;
759   TOKEN_PRIMARY_GROUP pgrp;
760   TOKEN_DEFAULT_DACL dacl = {};
761   TOKEN_SOURCE source;
762   TOKEN_STATISTICS stats;
763   memcpy (source.SourceName, "Cygwin.1", 8);
764   source.SourceIdentifier.HighPart = 0;
765   source.SourceIdentifier.LowPart = 0x0101;
766
767   HANDLE token = INVALID_HANDLE_VALUE;
768   HANDLE primary_token = INVALID_HANDLE_VALUE;
769
770   PTOKEN_GROUPS my_tok_gsids = NULL;
771   DWORD size;
772   size_t psize = 0;
773
774   /* SE_CREATE_TOKEN_NAME privilege needed to call NtCreateToken. */
775   push_self_privilege (SE_CREATE_TOKEN_PRIVILEGE, true);
776
777   /* Open policy object. */
778   if ((lsa = open_local_policy ()) == INVALID_HANDLE_VALUE)
779     goto out;
780
781   /* User, owner, primary group. */
782   user.User.Sid = usersid;
783   user.User.Attributes = 0;
784   owner.Owner = usersid;
785
786   /* Retrieve authentication id and group list from own process. */
787   if (hProcToken)
788     {
789       /* Switching user context to SYSTEM doesn't inherit the authentication
790          id of the user account running current process. */
791       if (usersid != well_known_system_sid)
792         if (!GetTokenInformation (hProcToken, TokenStatistics,
793                                   &stats, sizeof stats, &size))
794           debug_printf
795             ("GetTokenInformation(hProcToken, TokenStatistics), %E");
796         else
797           auth_luid = stats.AuthenticationId;
798
799       /* Retrieving current processes group list to be able to inherit
800          some important well known group sids. */
801       if (!GetTokenInformation (hProcToken, TokenGroups, NULL, 0, &size)
802           && GetLastError () != ERROR_INSUFFICIENT_BUFFER)
803         debug_printf ("GetTokenInformation(hProcToken, TokenGroups), %E");
804       else if (!(my_tok_gsids = (PTOKEN_GROUPS) malloc (size)))
805         debug_printf ("malloc (my_tok_gsids) failed.");
806       else if (!GetTokenInformation (hProcToken, TokenGroups, my_tok_gsids,
807                                      size, &size))
808         {
809           debug_printf ("GetTokenInformation(hProcToken, TokenGroups), %E");
810           free (my_tok_gsids);
811           my_tok_gsids = NULL;
812         }
813     }
814
815   /* Create list of groups, the user is member in. */
816   int auth_pos;
817   if (new_groups.issetgroups ())
818     get_setgroups_sidlist (tmp_gsids, usersid, pw, my_tok_gsids, new_groups,
819                            auth_luid, auth_pos);
820   else if (!get_initgroups_sidlist (tmp_gsids, usersid, new_groups.pgsid, pw,
821                                     my_tok_gsids, auth_luid, auth_pos))
822     goto out;
823
824   /* Primary group. */
825   pgrp.PrimaryGroup = new_groups.pgsid;
826
827   /* Create a TOKEN_GROUPS list from the above retrieved list of sids. */
828   new_tok_gsids = (PTOKEN_GROUPS)
829                   alloca (sizeof (DWORD) + tmp_gsids.count ()
830                                            * sizeof (SID_AND_ATTRIBUTES));
831   new_tok_gsids->GroupCount = tmp_gsids.count ();
832   for (DWORD i = 0; i < new_tok_gsids->GroupCount; ++i)
833     {
834       new_tok_gsids->Groups[i].Sid = tmp_gsids.sids[i];
835       new_tok_gsids->Groups[i].Attributes = SE_GROUP_MANDATORY
836                                             | SE_GROUP_ENABLED_BY_DEFAULT
837                                             | SE_GROUP_ENABLED;
838     }
839   if (auth_pos >= 0)
840     new_tok_gsids->Groups[auth_pos].Attributes |= SE_GROUP_LOGON_ID;
841   /* Retrieve list of privileges of that user. */
842   if (!(privs = get_priv_list (lsa, usersid, tmp_gsids, psize)))
843     goto out;
844
845   /* Let's be heroic... */
846   ret = NtCreateToken (&token, TOKEN_ALL_ACCESS, &oa, TokenImpersonation,
847                        &auth_luid, &exp, &user, new_tok_gsids, privs, &owner,
848                        &pgrp, &dacl, &source);
849   if (ret)
850     __seterrno_from_nt_status (ret);
851   else if (GetLastError () == ERROR_PROC_NOT_FOUND)
852     {
853       __seterrno ();
854       debug_printf ("Loading NtCreateToken failed.");
855     }
856   else
857     {
858       /* Convert to primary token. */
859       if (!DuplicateTokenEx (token, MAXIMUM_ALLOWED, &sec_none,
860                              SecurityImpersonation, TokenPrimary,
861                              &primary_token))
862         {
863           __seterrno ();
864           debug_printf ("DuplicateTokenEx %E");
865         }
866     }
867
868 out:
869   pop_self_privilege ();
870   if (token != INVALID_HANDLE_VALUE)
871     CloseHandle (token);
872   if (privs)
873     free (privs);
874   if (my_tok_gsids)
875     free (my_tok_gsids);
876   close_local_policy (lsa);
877
878   debug_printf ("0x%x = create_token ()", primary_token);
879   return primary_token;
880 }
881
882 HANDLE
883 lsaauth (cygsid &usersid, user_groups &new_groups, struct passwd *pw)
884 {
885   cygsidlist tmp_gsids (cygsidlist_auto, 12);
886   cygpsid pgrpsid;
887   LSA_STRING name;
888   HANDLE lsa_hdl = NULL, lsa = INVALID_HANDLE_VALUE;
889   LSA_OPERATIONAL_MODE sec_mode;
890   NTSTATUS ret, ret2;
891   ULONG package_id, size;
892   LUID auth_luid = SYSTEM_LUID;
893   struct {
894     LSA_STRING str;
895     CHAR buf[16];
896   } origin;
897   cyglsa_t *authinf = NULL;
898   ULONG authinf_size;
899   TOKEN_SOURCE ts;
900   PCYG_TOKEN_GROUPS gsids = NULL;
901   PTOKEN_PRIVILEGES privs = NULL;
902   PACL dacl = NULL;
903   PVOID profile = NULL;
904   LUID luid;
905   QUOTA_LIMITS quota;
906   size_t psize = 0, gsize = 0, dsize = 0;
907   OFFSET offset, sids_offset;
908   int tmpidx, non_well_known_cnt;
909
910   HANDLE user_token = NULL;
911
912   push_self_privilege (SE_TCB_PRIVILEGE, true);
913
914   /* Register as logon process. */
915   str2lsa (name, "Cygwin");
916   SetLastError (0);
917   ret = LsaRegisterLogonProcess (&name, &lsa_hdl, &sec_mode);
918   if (ret != STATUS_SUCCESS)
919     {
920       debug_printf ("LsaRegisterLogonProcess: %p", ret);
921       __seterrno_from_win_error (LsaNtStatusToWinError (ret));
922       goto out;
923     }
924   else if (GetLastError () == ERROR_PROC_NOT_FOUND)
925     {
926       debug_printf ("Couldn't load Secur32.dll");
927       goto out;
928     }
929   /* Get handle to our own LSA package. */
930   str2lsa (name, CYG_LSA_PKGNAME);
931   ret = LsaLookupAuthenticationPackage (lsa_hdl, &name, &package_id);
932   if (ret != STATUS_SUCCESS)
933     {
934       debug_printf ("LsaLookupAuthenticationPackage: %p", ret);
935       __seterrno_from_win_error (LsaNtStatusToWinError (ret));
936       goto out;
937     }
938
939   /* Open policy object. */
940   if ((lsa = open_local_policy ()) == INVALID_HANDLE_VALUE)
941     goto out;
942
943   /* Create origin. */
944   str2buf2lsa (origin.str, origin.buf, "Cygwin");
945   /* Create token source. */
946   memcpy (ts.SourceName, "Cygwin.1", 8);
947   ts.SourceIdentifier.HighPart = 0;
948   ts.SourceIdentifier.LowPart = 0x0103;
949
950   /* Create list of groups, the user is member in. */
951   int auth_pos;
952   if (new_groups.issetgroups ())
953     get_setgroups_sidlist (tmp_gsids, usersid, pw, NULL, new_groups, auth_luid,
954                            auth_pos);
955   else if (!get_initgroups_sidlist (tmp_gsids, usersid, new_groups.pgsid, pw,
956                                     NULL, auth_luid, auth_pos))
957     goto out;
958   /* The logon SID entry is not generated automatically on Windows 2000
959      and earlier for some reason.  So add fake logon sid here, which is
960      filled with logon id values in the authentication package. */
961   if (wincap.needs_logon_sid_in_sid_list ())
962     tmp_gsids += fake_logon_sid;
963
964   tmp_gsids.debug_print ("tmp_gsids");
965
966   /* Evaluate size of TOKEN_GROUPS list */
967   non_well_known_cnt =  tmp_gsids.non_well_known_count ();
968   gsize = sizeof (DWORD) + non_well_known_cnt * sizeof (SID_AND_ATTRIBUTES);
969   tmpidx = -1;
970   for (int i = 0; i < non_well_known_cnt; ++i)
971     if ((tmpidx = tmp_gsids.next_non_well_known_sid (tmpidx)) >= 0)
972       gsize += GetLengthSid (tmp_gsids.sids[tmpidx]);
973
974   /* Retrieve list of privileges of that user. */
975   if (!(privs = get_priv_list (lsa, usersid, tmp_gsids, psize)))
976     goto out;
977
978   /* Create DefaultDacl. */
979   dsize = sizeof (ACL) + 3 * sizeof (ACCESS_ALLOWED_ACE)
980           + GetLengthSid (usersid)
981           + GetLengthSid (well_known_admins_sid)
982           + GetLengthSid (well_known_system_sid);
983   dacl = (PACL) alloca (dsize);
984   if (!InitializeAcl (dacl, dsize, ACL_REVISION))
985     goto out;
986   if (!AddAccessAllowedAce (dacl, ACL_REVISION, GENERIC_ALL, usersid))
987     goto out;
988   if (!AddAccessAllowedAce (dacl, ACL_REVISION, GENERIC_ALL,
989                             well_known_admins_sid))
990     goto out;
991   if (!AddAccessAllowedAce (dacl, ACL_REVISION, GENERIC_ALL,
992                             well_known_system_sid))
993     goto out;
994
995   /* Evaluate authinf size and allocate authinf. */
996   authinf_size = (authinf->data - (PBYTE) authinf);
997   authinf_size += GetLengthSid (usersid);           /* User SID */
998   authinf_size += gsize;                            /* Groups + Group SIDs */
999   /* When trying to define the admins group as primary group on Vista,
1000      LsaLogonUser fails with error STATUS_INVALID_OWNER.  As workaround
1001      we define "Local" as primary group here.  First, this adds the otherwise
1002      missing "Local" group to the group list and second, seteuid32
1003      sets the primary group to the group set in /etc/passwd anyway. */
1004   pgrpsid = well_known_local_sid;
1005   authinf_size += GetLengthSid (pgrpsid);           /* Primary Group SID */
1006
1007   authinf_size += psize;                            /* Privileges */
1008   authinf_size += 0;                                /* Owner SID */
1009   authinf_size += dsize;                            /* Default DACL */
1010
1011   authinf = (cyglsa_t *) alloca (authinf_size);
1012   authinf->inf_size = authinf_size - ((PBYTE) &authinf->inf - (PBYTE) authinf);
1013
1014   authinf->magic = CYG_LSA_MAGIC;
1015
1016   extract_nt_dom_user (pw, authinf->domain, authinf->username);
1017
1018   /* Store stuff in authinf with offset relative to start of "inf" member,
1019      instead of using pointers. */
1020   offset = authinf->data - (PBYTE) &authinf->inf;
1021
1022   authinf->inf.ExpirationTime.LowPart = 0xffffffffL;
1023   authinf->inf.ExpirationTime.HighPart = 0x7fffffffL;
1024   /* User SID */
1025   authinf->inf.User.User.Sid = offset;
1026   authinf->inf.User.User.Attributes = 0;
1027   CopySid (GetLengthSid (usersid), (PSID) ((PBYTE) &authinf->inf + offset),
1028            usersid);
1029   offset += GetLengthSid (usersid);
1030   /* Groups */
1031   authinf->inf.Groups = offset;
1032   gsids = (PCYG_TOKEN_GROUPS) ((PBYTE) &authinf->inf + offset);
1033   sids_offset = offset + sizeof (ULONG) + non_well_known_cnt
1034                                           * sizeof (SID_AND_ATTRIBUTES);
1035   gsids->GroupCount = non_well_known_cnt;
1036   /* Group SIDs */
1037   tmpidx = -1;
1038   for (int i = 0; i < non_well_known_cnt; ++i)
1039     {
1040       if ((tmpidx = tmp_gsids.next_non_well_known_sid (tmpidx)) < 0)
1041         break;
1042       gsids->Groups[i].Sid = sids_offset;
1043       gsids->Groups[i].Attributes = SE_GROUP_MANDATORY
1044                                     | SE_GROUP_ENABLED_BY_DEFAULT
1045                                     | SE_GROUP_ENABLED;
1046       /* Mark logon SID as logon SID :) */
1047       if (wincap.needs_logon_sid_in_sid_list ()
1048           && tmp_gsids.sids[tmpidx] == fake_logon_sid)
1049         gsids->Groups[i].Attributes += SE_GROUP_LOGON_ID;
1050       CopySid (GetLengthSid (tmp_gsids.sids[tmpidx]),
1051                (PSID) ((PBYTE) &authinf->inf + sids_offset),
1052                tmp_gsids.sids[tmpidx]);
1053       sids_offset += GetLengthSid (tmp_gsids.sids[tmpidx]);
1054     }
1055   offset += gsize;
1056   /* Primary Group SID */
1057   authinf->inf.PrimaryGroup.PrimaryGroup = offset;
1058   CopySid (GetLengthSid (pgrpsid), (PSID) ((PBYTE) &authinf->inf + offset),
1059            pgrpsid);
1060   offset += GetLengthSid (pgrpsid);
1061   /* Privileges */
1062   authinf->inf.Privileges = offset;
1063   memcpy ((PBYTE) &authinf->inf + offset, privs, psize);
1064   offset += psize;
1065   /* Owner */
1066   authinf->inf.Owner.Owner = 0;
1067   /* Default DACL */
1068   authinf->inf.DefaultDacl.DefaultDacl = offset;
1069   memcpy ((PBYTE) &authinf->inf + offset, dacl, dsize);
1070
1071   authinf->checksum = CYGWIN_VERSION_MAGIC (CYGWIN_VERSION_DLL_MAJOR,
1072                                             CYGWIN_VERSION_DLL_MINOR);
1073   PDWORD csp = (PDWORD) &authinf->username;
1074   PDWORD csp_end = (PDWORD) ((PBYTE) authinf + authinf_size);
1075   while (csp < csp_end)
1076     authinf->checksum += *csp++;
1077
1078   /* Try to logon... */
1079   ret = LsaLogonUser (lsa_hdl, (PLSA_STRING) &origin, Interactive, package_id,
1080                       authinf, authinf_size, NULL, &ts, &profile, &size, &luid,
1081                       &user_token, &quota, &ret2);
1082   if (ret != STATUS_SUCCESS)
1083     {
1084       debug_printf ("LsaLogonUser: %p", ret);
1085       __seterrno_from_win_error (LsaNtStatusToWinError (ret));
1086       goto out;
1087     }
1088   if (profile)
1089     LsaFreeReturnBuffer (profile);
1090
1091   if (wincap.has_mandatory_integrity_control ())
1092     {
1093       typedef struct _TOKEN_LINKED_TOKEN
1094       {
1095         HANDLE LinkedToken;
1096       } TOKEN_LINKED_TOKEN, *PTOKEN_LINKED_TOKEN;
1097 #     define TokenLinkedToken ((TOKEN_INFORMATION_CLASS) 19)
1098
1099       TOKEN_LINKED_TOKEN linked;
1100
1101       if (GetTokenInformation (user_token, TokenLinkedToken,
1102                                (PVOID) &linked, sizeof linked, &size))
1103         {
1104           debug_printf ("Linked Token: %lu", linked.LinkedToken);
1105           if (linked.LinkedToken)
1106             user_token = linked.LinkedToken;
1107         }
1108     }
1109
1110 out:
1111   if (privs)
1112     free (privs);
1113   close_local_policy (lsa);
1114   if (lsa_hdl)
1115     LsaDeregisterLogonProcess (lsa_hdl);
1116   pop_self_privilege ();
1117
1118   debug_printf ("0x%x = lsaauth ()", user_token);
1119   return user_token;
1120 }