OSDN Git Service

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