1 /* sec/auth.cc: NT authentication functions
3 This file is part of Cygwin.
5 This software is a copyrighted work licensed under the terms of the
6 Cygwin license. Please consult the file "CYGWIN_LICENSE" for
26 #define SECURITY_WIN32
28 #include "cygserver_setpwd.h"
29 #include <cygwin/version.h>
31 /* OpenBSD 2.0 and later. */
36 return cygheap
->user
.issetuid () ? 1 : 0;
39 /* The token returned by system functions is a restricted token. The full
40 admin token is linked to it and can be fetched with NtQueryInformationToken.
41 This function returns the elevated token if available, the original token
42 otherwise. The token handle is also made inheritable since that's necessary
45 get_full_privileged_inheritable_token (HANDLE token
)
47 TOKEN_LINKED_TOKEN linked
;
50 /* When fetching the linked token without TCB privs, then the linked
51 token is not a primary token, only an impersonation token, which is
52 not suitable for CreateProcessAsUser. Converting it to a primary
53 token using DuplicateTokenEx does NOT work for the linked token in
54 this case. So we have to switch on TCB privs to get a primary token.
55 This is generally performed in the calling functions. */
56 if (NT_SUCCESS (NtQueryInformationToken (token
, TokenLinkedToken
,
57 (PVOID
) &linked
, sizeof linked
,
60 debug_printf ("Linked Token: %p", linked
.LinkedToken
);
61 if (linked
.LinkedToken
)
65 /* At this point we don't know if the user actually had TCB
66 privileges. Check if the linked token is a primary token.
67 If not, just return the original token. */
68 if (NT_SUCCESS (NtQueryInformationToken (linked
.LinkedToken
,
69 TokenType
, (PVOID
) &type
,
71 && type
!= TokenPrimary
)
72 debug_printf ("Linked Token is not a primary token!");
76 token
= linked
.LinkedToken
;
80 if (!SetHandleInformation (token
, HANDLE_FLAG_INHERIT
, HANDLE_FLAG_INHERIT
))
90 set_imp_token (HANDLE token
, int type
)
92 debug_printf ("set_imp_token (%p, %d)", token
, type
);
93 cygheap
->user
.external_token
= (token
== INVALID_HANDLE_VALUE
94 ? NO_IMPERSONATION
: token
);
95 cygheap
->user
.ext_token_is_restricted
= (type
== CW_TOKEN_RESTRICTED
);
99 cygwin_set_impersonation_token (const HANDLE hToken
)
101 set_imp_token (hToken
, CW_TOKEN_IMPERSONATION
);
105 extract_nt_dom_user (const struct passwd
*pw
, PWCHAR domain
, PWCHAR user
)
109 DWORD ulen
= UNLEN
+ 1;
110 DWORD dlen
= MAX_DOMAIN_NAME_LEN
+ 1;
113 debug_printf ("pw_gecos %p (%s)", pw
->pw_gecos
, pw
->pw_gecos
);
115 /* The incoming passwd entry is not necessarily a pointer to the
116 internal passwd buffers, thus we must not rely on being able to
117 cast it to pg_pwd. */
118 if (psid
.getfrompw_gecos (pw
)
119 && LookupAccountSidW (NULL
, psid
, user
, &ulen
, domain
, &dlen
, &use
))
124 sys_mbstowcs (user
, UNLEN
+ 1, pw
->pw_name
);
125 if ((d
= strstr (pw
->pw_gecos
, "U-")) != NULL
&&
126 (d
== pw
->pw_gecos
|| d
[-1] == ','))
128 c
= strchrnul (d
+ 2, ',');
129 if ((u
= strchrnul (d
+ 2, '\\')) >= c
)
131 else if (u
- d
<= MAX_DOMAIN_NAME_LEN
+ 2)
132 sys_mbstowcs (domain
, MAX_DOMAIN_NAME_LEN
+ 1, d
+ 2, u
- d
- 1);
133 if (c
- u
<= UNLEN
+ 1)
134 sys_mbstowcs (user
, UNLEN
+ 1, u
+ 1, c
- u
);
139 cygwin_logon_user (const struct passwd
*pw
, const char *password
)
141 if (!pw
|| !password
)
144 return INVALID_HANDLE_VALUE
;
147 WCHAR nt_domain
[MAX_DOMAIN_NAME_LEN
+ 1];
148 WCHAR nt_user
[UNLEN
+ 1];
153 extract_nt_dom_user (pw
, nt_domain
, nt_user
);
154 debug_printf ("LogonUserW (%W, %W, ...)", nt_user
, nt_domain
);
155 sys_mbstowcs (passwd
= tp
.w_get (), NT_MAX_PATH
, password
);
156 /* CV 2005-06-08: LogonUser should run under the primary process token,
157 otherwise it returns with ERROR_ACCESS_DENIED. */
158 cygheap
->user
.deimpersonate ();
159 if (!LogonUserW (nt_user
, *nt_domain
? nt_domain
: NULL
, passwd
,
160 LOGON32_LOGON_INTERACTIVE
, LOGON32_PROVIDER_DEFAULT
,
164 hToken
= INVALID_HANDLE_VALUE
;
168 HANDLE hPrivToken
= NULL
;
170 /* See the comment in get_full_privileged_inheritable_token for a
171 description why we enable TCB privileges here. */
172 push_self_privilege (SE_TCB_PRIVILEGE
, true);
173 hPrivToken
= get_full_privileged_inheritable_token (hToken
);
174 pop_self_privilege ();
176 debug_printf ("Can't fetch linked token (%E), use standard token");
180 RtlSecureZeroMemory (passwd
, NT_MAX_PATH
);
181 cygheap
->user
.reimpersonate ();
182 debug_printf ("%R = logon_user(%s,...)", hToken
, pw
->pw_name
);
186 /* The buffer path points to should be at least MAX_PATH bytes. */
188 get_user_profile_directory (PCWSTR sidstr
, PWCHAR path
, SIZE_T path_len
)
190 if (!sidstr
|| !path
)
198 RTL_QUERY_REGISTRY_TABLE tab
[2] = {
199 { NULL
, RTL_QUERY_REGISTRY_NOEXPAND
| RTL_QUERY_REGISTRY_DIRECT
200 | RTL_QUERY_REGISTRY_REQUIRED
,
201 L
"ProfileImagePath", &buf
, REG_NONE
, NULL
, 0 },
202 { NULL
, 0, NULL
, NULL
, 0, NULL
, 0 }
205 WCHAR key
[wcslen (sidstr
) + 16];
206 wcpcpy (wcpcpy (key
, L
"ProfileList\\"), sidstr
);
207 status
= RtlQueryRegistryValues (RTL_REGISTRY_WINDOWS_NT
, key
, tab
,
209 if (!NT_SUCCESS (status
) || buf
.Length
== 0)
211 debug_printf ("ProfileImagePath for %W not found, status %y", sidstr
,
215 ExpandEnvironmentStringsW (buf
.Buffer
, path
, path_len
);
216 debug_printf ("ProfileImagePath for %W: %W", sidstr
, path
);
220 /* Load user profile if it's not already loaded. If the user profile doesn't
221 exist on the machine try to create it.
223 Return a handle to the loaded user registry hive only if it got actually
224 loaded here, not if it already existed. There's no reliable way to know
225 when to unload the hive yet, so we're leaking this registry handle for now.
226 TODO: Try to find a way to reliably unload the user profile again. */
228 load_user_profile (HANDLE token
, struct passwd
*pw
, cygpsid
&usersid
)
230 WCHAR domain
[DNLEN
+ 1];
231 WCHAR username
[UNLEN
+ 1];
233 WCHAR userpath
[MAX_PATH
];
237 if (!cygheap
->dom
.init ())
240 extract_nt_dom_user (pw
, domain
, username
);
241 usersid
.string (sid
);
242 debug_printf ("user: <%W> <%W> <%W>", username
, domain
, sid
);
243 /* Check if the local profile dir has already been created. */
244 if (!get_user_profile_directory (sid
, userpath
, MAX_PATH
))
246 /* No, try to create it. */
247 HRESULT res
= CreateProfile (sid
, username
, userpath
, MAX_PATH
);
250 debug_printf ("CreateProfile, HRESULT %x", res
);
254 /* Fill PROFILEINFO */
255 memset (&pi
, 0, sizeof pi
);
256 pi
.dwSize
= sizeof pi
;
257 pi
.dwFlags
= PI_NOUI
;
258 pi
.lpUserName
= username
;
259 /* Check if user has a roaming profile and fill in lpProfilePath, if so.
260 Call NetUserGetInfo only for local machine accounts, use LDAP otherwise. */
261 if (!wcscasecmp (domain
, cygheap
->dom
.account_flat_name ()))
266 nas
= NetUserGetInfo (NULL
, username
, 3, (PBYTE
*) &ui
);
267 if (nas
!= NERR_Success
)
268 debug_printf ("NetUserGetInfo, %u", nas
);
271 if (ui
->usri3_profile
&& *ui
->usri3_profile
)
273 wcsncpy (userpath
, ui
->usri3_profile
, MAX_PATH
- 1);
274 userpath
[MAX_PATH
- 1] = L
'\0';
275 pi
.lpProfilePath
= userpath
;
277 NetApiBufferFree (ui
);
283 PCWSTR dnsdomain
= NULL
;
285 if (wcscasecmp (domain
, cygheap
->dom
.primary_flat_name ()))
287 PDS_DOMAIN_TRUSTSW td
= NULL
;
289 for (ULONG idx
= 0; (td
= cygheap
->dom
.trusted_domain (idx
)); ++idx
)
290 if (!wcscasecmp (domain
, td
->NetbiosDomainName
))
292 dnsdomain
= td
->DnsDomainName
;
296 if (cldap
.fetch_ad_account (usersid
, false, dnsdomain
))
298 PWCHAR val
= cldap
.get_profile_path ();
301 wcsncpy (userpath
, val
, MAX_PATH
- 1);
302 userpath
[MAX_PATH
- 1] = L
'\0';
303 pi
.lpProfilePath
= userpath
;
308 if (!LoadUserProfileW (token
, &pi
))
309 debug_printf ("LoadUserProfileW, %E");
314 lsa_open_policy (PWCHAR server
, ACCESS_MASK access
)
316 LSA_UNICODE_STRING srvbuf
;
317 PLSA_UNICODE_STRING srv
= NULL
;
318 static LSA_OBJECT_ATTRIBUTES oa
= { 0, 0, 0, 0, 0, 0 };
324 RtlInitUnicodeString (srv
, server
);
326 NTSTATUS status
= LsaOpenPolicy (srv
, &oa
, access
, &lsa
);
327 if (!NT_SUCCESS (status
))
329 __seterrno_from_nt_status (status
);
336 lsa_close_policy (HANDLE lsa
)
343 get_logon_server (PCWSTR domain
, PWCHAR server
, ULONG flags
)
346 PDOMAIN_CONTROLLER_INFOW pci
;
348 /* Empty domain is interpreted as local system */
349 if (cygheap
->dom
.init ()
351 || !wcscasecmp (domain
, cygheap
->dom
.account_flat_name ())))
353 wcpcpy (wcpcpy (server
, L
"\\\\"), cygheap
->dom
.account_flat_name ());
357 /* Try to get any available domain controller for this domain */
358 ret
= DsGetDcNameW (NULL
, domain
, NULL
, NULL
, flags
, &pci
);
359 if (ret
== ERROR_SUCCESS
)
361 wcscpy (server
, pci
->DomainControllerName
);
362 NetApiBufferFree (pci
);
363 debug_printf ("DC: server: %W", server
);
366 __seterrno_from_win_error (ret
);
371 sid_in_token_groups (PTOKEN_GROUPS grps
, cygpsid sid
)
375 for (DWORD i
= 0; i
< grps
->GroupCount
; ++i
)
376 if (sid
== grps
->Groups
[i
].Sid
)
382 get_server_groups (cygsidlist
&grp_list
, PSID usersid
,
383 acct_disabled_chk_t check_account_disabled
)
385 WCHAR user
[UNLEN
+ 1];
386 WCHAR domain
[MAX_DOMAIN_NAME_LEN
+ 1];
387 DWORD ulen
= UNLEN
+ 1;
388 DWORD dlen
= MAX_DOMAIN_NAME_LEN
+ 1;
391 if (well_known_system_sid
== usersid
)
393 grp_list
*= well_known_admins_sid
;
397 if (!LookupAccountSidW (NULL
, usersid
, user
, &ulen
, domain
, &dlen
, &use
))
402 /* If the SID does NOT start with S-1-5-21, the domain is some builtin
403 domain. We don't fetch a group list then. */
404 if (sid_id_auth (usersid
) == 5 /* SECURITY_NT_AUTHORITY */
405 && sid_sub_auth (usersid
, 0) == SECURITY_NT_NON_UNIQUE
)
410 PTOKEN_GROUPS groups
;
413 token
= s4uauth (false, domain
, user
, status
);
417 groups
= (PTOKEN_GROUPS
) tp
.w_get ();
418 status
= NtQueryInformationToken (token
, TokenGroups
, groups
,
419 2 * NT_MAX_PATH
, &size
);
420 if (NT_SUCCESS (status
))
421 for (DWORD pg
= 0; pg
< groups
->GroupCount
; ++pg
)
423 if (groups
->Groups
[pg
].Attributes
& SE_GROUP_USE_FOR_DENY_ONLY
)
425 cygpsid grpsid
= groups
->Groups
[pg
].Sid
;
426 if (sid_id_auth (grpsid
) == 5 /* SECURITY_NT_AUTHORITY */
427 && sid_sub_auth (grpsid
, 0) == SECURITY_NT_NON_UNIQUE
)
438 - the requested usersid matches the TokenUser and
439 - if setgroups has been called:
440 the token groups that are listed in /etc/group match the union of
441 the requested primary and supplementary groups in gsids.
442 - else the (unknown) implicitly requested supplementary groups and those
443 in the token are the groups associated with the usersid. We assume
444 they match and verify only the primary groups.
445 The requested primary group must appear in the token.
446 The primary group in the token is a group associated with the usersid,
447 except if the token is internal and the group is in the token SD. In
448 that latter case that group must match the requested primary group. */
450 verify_token (HANDLE token
, cygsid
&usersid
, user_groups
&groups
, bool *pintern
)
460 status
= NtQueryInformationToken (token
, TokenSource
, &ts
, sizeof ts
,
462 if (!NT_SUCCESS (status
))
463 debug_printf ("NtQueryInformationToken(), %y", status
);
465 *pintern
= intern
= !memcmp (ts
.SourceName
, "Cygwin.1", 8);
468 cygsid
tok_usersid (NO_SID
);
469 status
= NtQueryInformationToken (token
, TokenUser
, &tok_usersid
,
470 sizeof tok_usersid
, &size
);
471 if (!NT_SUCCESS (status
))
472 debug_printf ("NtQueryInformationToken(), %y", status
);
473 if (usersid
!= tok_usersid
)
476 /* For an internal token, if setgroups was not called and if the sd group
477 is not well_known_null_sid, it must match pgrpsid */
478 if (intern
&& !groups
.issetgroups ())
480 const DWORD sd_buf_siz
= SECURITY_MAX_SID_SIZE
481 + sizeof (SECURITY_DESCRIPTOR
);
482 PSECURITY_DESCRIPTOR sd_buf
= (PSECURITY_DESCRIPTOR
) alloca (sd_buf_siz
);
483 cygpsid
gsid (NO_SID
);
485 status
= NtQuerySecurityObject (token
, GROUP_SECURITY_INFORMATION
,
486 sd_buf
, sd_buf_siz
, &size
);
487 if (!NT_SUCCESS (status
))
488 debug_printf ("NtQuerySecurityObject(), %y", status
);
492 status
= RtlGetGroupSecurityDescriptor (sd_buf
, (PSID
*) &gsid
,
494 if (!NT_SUCCESS (status
))
495 debug_printf ("RtlGetGroupSecurityDescriptor(), %y", status
);
497 if (well_known_null_sid
!= gsid
)
498 return gsid
== groups
.pgsid
;
501 PTOKEN_GROUPS my_grps
= (PTOKEN_GROUPS
) tp
.w_get ();
503 status
= NtQueryInformationToken (token
, TokenGroups
, my_grps
,
504 2 * NT_MAX_PATH
, &size
);
505 if (!NT_SUCCESS (status
))
507 debug_printf ("NtQueryInformationToken(my_token, TokenGroups), %y",
514 if (groups
.issetgroups ()) /* setgroups was called */
517 bool saw
[groups
.sgsids
.count ()];
519 /* Check that all groups in the setgroups () list are in the token.
520 A token created through ADVAPI should be allowed to contain more
521 groups than requested through setgroups(), especially since the
522 addition of integrity groups. */
523 memset (saw
, 0, sizeof(saw
));
524 for (int gidx
= 0; gidx
< groups
.sgsids
.count (); gidx
++)
526 gsid
= groups
.sgsids
.sids
[gidx
];
527 if (sid_in_token_groups (my_grps
, gsid
))
529 int pos
= groups
.sgsids
.position (gsid
);
532 else if (groups
.pgsid
== gsid
)
536 /* user.sgsids groups must be in the token, except for builtin groups.
537 These can be different on domain member machines compared to
538 domain controllers, so these builtin groups may be validly missing
539 from a token created through password or lsaauth logon. */
540 for (int gidx
= 0; gidx
< groups
.sgsids
.count (); gidx
++)
542 && !groups
.sgsids
.sids
[gidx
].is_well_known_sid ()
543 && !sid_in_token_groups (my_grps
, groups
.sgsids
.sids
[gidx
]))
546 /* The primary group must be in the token */
548 || sid_in_token_groups (my_grps
, groups
.pgsid
)
549 || groups
.pgsid
== usersid
;
553 account_restriction (NTSTATUS status
)
559 case STATUS_INVALID_LOGON_HOURS
:
560 type
= "Logon outside allowed hours";
562 case STATUS_INVALID_WORKSTATION
:
563 type
= "Logon at this machine not allowed";
565 case STATUS_PASSWORD_EXPIRED
:
566 type
= "Password expired";
568 case STATUS_ACCOUNT_DISABLED
:
569 type
= "Account disabled";
578 #define SFU_LSA_KEY_SUFFIX L"_microsoft_sfu_utility"
581 lsaprivkeyauth (struct passwd
*pw
)
587 WCHAR domain
[MAX_DOMAIN_NAME_LEN
+ 1];
588 WCHAR user
[UNLEN
+ 1];
589 WCHAR key_name
[MAX_DOMAIN_NAME_LEN
+ UNLEN
+ wcslen (SFU_LSA_KEY_SUFFIX
) + 2];
591 PUNICODE_STRING data
= NULL
;
595 push_self_privilege (SE_TCB_PRIVILEGE
, true);
597 /* Open policy object. */
598 if (!(lsa
= lsa_open_policy (NULL
, POLICY_GET_PRIVATE_INFORMATION
)))
601 /* Needed for Interix key and LogonUser. */
602 extract_nt_dom_user (pw
, domain
, user
);
604 /* First test for a Cygwin entry. */
605 if (psid
.getfrompw (pw
) && psid
.string (sid
))
607 wcpcpy (wcpcpy (key_name
, CYGWIN_LSA_KEY_PREFIX
), sid
);
608 RtlInitUnicodeString (&key
, key_name
);
609 status
= LsaRetrievePrivateData (lsa
, &key
, &data
);
610 if (!NT_SUCCESS (status
))
613 /* No Cygwin key, try Interix key. */
614 if (!data
&& *domain
)
616 __small_swprintf (key_name
, L
"%W_%W%W",
617 domain
, user
, SFU_LSA_KEY_SUFFIX
);
618 RtlInitUnicodeString (&key
, key_name
);
619 status
= LsaRetrievePrivateData (lsa
, &key
, &data
);
620 if (!NT_SUCCESS (status
))
623 /* Found an entry? Try to logon. */
626 /* The key is not 0-terminated. */
628 size_t pwdsize
= data
->Length
+ sizeof (WCHAR
);
630 passwd
= (PWCHAR
) alloca (pwdsize
);
631 *wcpncpy (passwd
, data
->Buffer
, data
->Length
/ sizeof (WCHAR
)) = L
'\0';
632 /* Weird: LsaFreeMemory invalidates the content of the UNICODE_STRING
633 structure, but it does not invalidate the Buffer content. */
634 RtlSecureZeroMemory (data
->Buffer
, data
->Length
);
635 LsaFreeMemory (data
);
636 debug_printf ("Try logon for %W\\%W", domain
, user
);
637 ret
= LogonUserW (user
, domain
, passwd
, LOGON32_LOGON_INTERACTIVE
,
638 LOGON32_PROVIDER_DEFAULT
, &token
);
639 RtlSecureZeroMemory (passwd
, pwdsize
);
646 token
= get_full_privileged_inheritable_token (token
);
648 lsa_close_policy (lsa
);
651 pop_self_privilege ();
655 /* The following code is inspired by the generate_s4u_user_token
656 and lookup_principal_name functions from
657 https://github.com/PowerShell/openssh-portable
659 Thanks guys! For courtesy here's the original copyright disclaimer: */
662 * Author: Manoj Ampalam <manoj.ampalam@microsoft.com>
663 * Utilities to generate user tokens
665 * Author: Bryan Berns <berns@uwalumni.com>
666 * Updated s4u, logon, and profile loading routines to use
667 * normalized login names.
669 * Copyright (c) 2015 Microsoft Corp.
670 * All rights reserved
672 * Microsoft openssh win32 port
674 * Redistribution and use in source and binary forms, with or without
675 * modification, are permitted provided that the following conditions
678 * 1. Redistributions of source code must retain the above copyright
679 * notice, this list of conditions and the following disclaimer.
680 * 2. Redistributions in binary form must reproduce the above copyright
681 * notice, this list of conditions and the following disclaimer in the
682 * documentation and/or other materials provided with the distribution.
684 * THIS SOFTWARE IS PROVIDED BY THE AUTHOR ``AS IS'' AND ANY EXPRESS OR
685 * IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES
686 * OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED.
687 * IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR ANY DIRECT, INDIRECT,
688 * INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT
689 * NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE,
690 * DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY
691 * THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
692 * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF
693 * THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
696 /* In w32api prior to 10.0.0, MsV1_0S4ULogon and MSV1_0_S4U_LOGON are only
697 defined in ddk/ntifs.h, which we can't include. */
698 #if (__MINGW64_VERSION_MAJOR < 10)
700 #define MsV1_0S4ULogon ((MSV1_0_LOGON_SUBMIT_TYPE) 12)
702 typedef struct _MSV1_0_S4U_LOGON
704 MSV1_0_LOGON_SUBMIT_TYPE MessageType
;
706 UNICODE_STRING UserPrincipalName
;
707 UNICODE_STRING DomainName
;
708 } MSV1_0_S4U_LOGON
, *PMSV1_0_S4U_LOGON
;
710 /* Missing in Mingw-w64 */
711 #define KERB_S4U_LOGON_FLAG_IDENTIFY 0x08
715 /* If logon is true we need an impersonation token. Otherwise we just
716 need an identification token, e. g. to fetch the group list. */
718 s4uauth (bool logon
, PCWSTR domain
, PCWSTR user
, NTSTATUS
&ret_status
)
721 HANDLE lsa_hdl
= NULL
;
722 LSA_OPERATIONAL_MODE sec_mode
;
723 NTSTATUS status
, sub_status
;
725 ULONG package_id
, size
;
732 PVOID authinf
= NULL
;
735 PKERB_INTERACTIVE_PROFILE profile
= NULL
;
741 if (!cygheap
->dom
.init ())
744 push_self_privilege (SE_TCB_PRIVILEGE
, true);
748 /* Register as logon process. */
749 debug_printf ("Impersonation requested");
750 RtlInitAnsiString (&name
, "Cygwin");
751 status
= LsaRegisterLogonProcess (&name
, &lsa_hdl
, &sec_mode
);
755 /* Connect untrusted to just create a identification token */
756 debug_printf ("Identification requested");
757 status
= LsaConnectUntrusted (&lsa_hdl
);
759 if (status
!= STATUS_SUCCESS
)
761 debug_printf ("%s: %y", logon
? "LsaRegisterLogonProcess"
762 : "LsaConnectUntrusted", status
);
763 /* If the privilege is not held, set the proper error code. */
764 if (status
== STATUS_PORT_CONNECTION_REFUSED
)
765 status
= STATUS_PRIVILEGE_NOT_HELD
;
766 __seterrno_from_nt_status (status
);
770 /* Check if this is a domain user. If so, use Kerberos. */
771 kerberos_auth
= cygheap
->dom
.member_machine ()
772 && wcscasecmp (domain
, cygheap
->dom
.account_flat_name ());
773 debug_printf ("kerb %d, domain member %d, user domain <%W>, machine <%W>",
774 kerberos_auth
, cygheap
->dom
.member_machine (), domain
,
775 cygheap
->dom
.account_flat_name ());
777 /* Connect to authentication package. */
778 RtlInitAnsiString (&name
, kerberos_auth
? MICROSOFT_KERBEROS_NAME_A
779 : MSV1_0_PACKAGE_NAME
);
780 status
= LsaLookupAuthenticationPackage (lsa_hdl
, &name
, &package_id
);
781 if (status
!= STATUS_SUCCESS
)
783 debug_printf ("LsaLookupAuthenticationPackage: %y", status
);
784 __seterrno_from_nt_status (status
);
789 stpcpy (origin
.buf
, "Cygwin");
790 RtlInitAnsiString (&origin
.str
, origin
.buf
);
792 /* Create token source. */
793 memcpy (ts
.SourceName
, "Cygwin.1", 8);
794 ts
.SourceIdentifier
.HighPart
= 0;
795 ts
.SourceIdentifier
.LowPart
= kerberos_auth
? 0x0105 : 0x0106;
799 PWCHAR sam_name
= tp
.w_get ();
800 PWCHAR upn_name
= tp
.w_get ();
802 KERB_S4U_LOGON
*s4u_logon
;
805 wcpcpy (wcpcpy (wcpcpy (sam_name
, domain
), L
"\\"), user
);
806 if (TranslateNameW (sam_name
, NameSamCompatible
, NameUserPrincipal
,
807 upn_name
, &size
) == 0)
809 PWCHAR translated_name
= tp
.w_get ();
812 debug_printf ("TranslateNameW(%W, NameUserPrincipal) %E", sam_name
);
814 if (TranslateNameW (sam_name
, NameSamCompatible
, NameCanonical
,
815 translated_name
, &size
) == 0)
817 debug_printf ("TranslateNameW(%W, NameCanonical) %E", sam_name
);
820 p
= wcschr (translated_name
, L
'/');
823 wcpcpy (wcpcpy (wcpcpy (upn_name
, user
), L
"@"), translated_name
);
826 name_len
= wcslen (upn_name
) * sizeof (WCHAR
);
827 authinf_size
= sizeof (KERB_S4U_LOGON
) + name_len
;
828 authinf
= tp
.c_get ();
829 RtlSecureZeroMemory (authinf
, authinf_size
);
830 s4u_logon
= (KERB_S4U_LOGON
*) authinf
;
831 s4u_logon
->MessageType
= KerbS4ULogon
;
832 s4u_logon
->Flags
= logon
? 0 : KERB_S4U_LOGON_FLAG_IDENTIFY
;
833 /* Append user to login info */
834 RtlInitEmptyUnicodeString (&s4u_logon
->ClientUpn
,
835 (PWCHAR
) (s4u_logon
+ 1),
837 RtlAppendUnicodeToString (&s4u_logon
->ClientUpn
, upn_name
);
838 debug_printf ("KerbS4ULogon: ClientUpn: <%S>", &s4u_logon
->ClientUpn
);
842 MSV1_0_S4U_LOGON
*s4u_logon
;
843 USHORT user_len
, domain_len
;
845 user_len
= wcslen (user
) * sizeof (WCHAR
);
846 domain_len
= wcslen (domain
) * sizeof (WCHAR
); /* Local machine */
847 authinf_size
= sizeof (MSV1_0_S4U_LOGON
) + user_len
+ domain_len
;
849 authinf
= tp
.c_get ();
850 RtlSecureZeroMemory (authinf
, authinf_size
);
851 s4u_logon
= (MSV1_0_S4U_LOGON
*) authinf
;
852 s4u_logon
->MessageType
= MsV1_0S4ULogon
;
853 s4u_logon
->Flags
= 0;
854 /* Append user and domain to login info */
855 RtlInitEmptyUnicodeString (&s4u_logon
->UserPrincipalName
,
856 (PWCHAR
) (s4u_logon
+ 1),
858 RtlInitEmptyUnicodeString (&s4u_logon
->DomainName
,
859 (PWCHAR
) ((PBYTE
) (s4u_logon
+ 1) + user_len
),
861 RtlAppendUnicodeToString (&s4u_logon
->UserPrincipalName
, user
);
862 RtlAppendUnicodeToString (&s4u_logon
->DomainName
, domain
);
863 debug_printf ("MsV1_0S4ULogon: DomainName: <%S> UserPrincipalName: <%S>",
864 &s4u_logon
->DomainName
, &s4u_logon
->UserPrincipalName
);
868 status
= LsaLogonUser (lsa_hdl
, (PLSA_STRING
) &origin
, Network
, package_id
,
869 authinf
, authinf_size
, NULL
, &ts
, (PVOID
*) &profile
,
870 &size
, &luid
, &token
, "a
, &sub_status
);
875 case STATUS_ACCOUNT_RESTRICTION
:
876 debug_printf ("%s S4U LsaLogonUser failed: %y (%s)",
877 kerberos_auth
? "Kerberos" : "MsV1_0", status
,
878 account_restriction (sub_status
));
881 debug_printf ("%s S4U LsaLogonUser failed: %y",
882 kerberos_auth
? "Kerberos" : "MsV1_0", status
);
888 LsaDeregisterLogonProcess (lsa_hdl
);
890 LsaFreeReturnBuffer (profile
);
894 /* Convert to primary token. CreateProcessAsUser takes impersonation
895 tokens since Windows 7 but MSDN still claims a primary token is
896 required. Better safe than sorry. */
899 if (DuplicateTokenEx (token
, MAXIMUM_ALLOWED
, &sec_none
,
900 SecurityImpersonation
, TokenPrimary
, &tmp_token
))
908 debug_printf ("DuplicateTokenEx %E");
909 /* Make sure not to allow create_token. */
910 status
= STATUS_INVALID_HANDLE
;
916 pop_self_privilege ();