OSDN Git Service

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