Book a Demo!
CoCalc Logo Icon
StoreFeaturesDocsShareSupportNewsAboutPoliciesSign UpSign In
freebsd
GitHub Repository: freebsd/freebsd-src
Path: blob/main/crypto/krb5/src/windows/kfwlogon/kfwlogon.c
34907 views
1
/*
2
Copyright 2005,2006 by the Massachusetts Institute of Technology
3
Copyright 2007 by Secure Endpoints Inc.
4
5
All rights reserved.
6
7
Permission to use, copy, modify, and distribute this software and its
8
documentation for any purpose and without fee is hereby granted,
9
provided that the above copyright notice appear in all copies and that
10
both that copyright notice and this permission notice appear in
11
supporting documentation, and that the name of the Massachusetts
12
Institute of Technology (M.I.T.) not be used in advertising or publicity
13
pertaining to distribution of the software without specific, written
14
prior permission.
15
16
M.I.T. DISCLAIMS ALL WARRANTIES WITH REGARD TO THIS SOFTWARE, INCLUDING
17
ALL IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS, IN NO EVENT SHALL
18
M.I.T. BE LIABLE FOR ANY SPECIAL, INDIRECT OR CONSEQUENTIAL DAMAGES OR
19
ANY DAMAGES WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS,
20
WHETHER IN AN ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION,
21
ARISING OUT OF OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS
22
SOFTWARE.
23
24
*/
25
26
#include "kfwlogon.h"
27
28
#include <io.h>
29
#include <stdio.h>
30
#include <sys/stat.h>
31
#include <sys/types.h>
32
#include <fcntl.h>
33
34
#include <winsock2.h>
35
#include <lm.h>
36
#include <nb30.h>
37
38
static HANDLE hDLL;
39
40
static HANDLE hInitMutex = NULL;
41
static BOOL bInit = FALSE;
42
43
44
BOOLEAN APIENTRY DllMain(HANDLE dll, DWORD reason, PVOID reserved)
45
{
46
hDLL = dll;
47
switch (reason) {
48
case DLL_PROCESS_ATTACH:
49
/* Initialization Mutex */
50
hInitMutex = CreateMutex(NULL, FALSE, NULL);
51
break;
52
53
case DLL_PROCESS_DETACH:
54
CloseHandle(hInitMutex);
55
break;
56
57
case DLL_THREAD_ATTACH:
58
case DLL_THREAD_DETACH:
59
default:
60
/* Everything else succeeds but does nothing. */
61
break;
62
}
63
64
return TRUE;
65
}
66
67
DWORD APIENTRY NPGetCaps(DWORD index)
68
{
69
switch (index) {
70
case WNNC_NET_TYPE:
71
/* We aren't a file system; We don't have our own type; use somebody else's. */
72
return WNNC_NET_SUN_PC_NFS;
73
case WNNC_START:
74
/* Say we are already started, even though we might wait after we receive NPLogonNotify */
75
return 1;
76
77
default:
78
return 0;
79
}
80
}
81
82
83
static BOOL
84
WINAPI
85
UnicodeStringToANSI(UNICODE_STRING uInputString, LPSTR lpszOutputString, int nOutStringLen)
86
{
87
CPINFO CodePageInfo;
88
89
GetCPInfo(CP_ACP, &CodePageInfo);
90
91
if (CodePageInfo.MaxCharSize > 1)
92
// Only supporting non-Unicode strings
93
return FALSE;
94
95
if (uInputString.Buffer && ((LPBYTE) uInputString.Buffer)[1] == '\0')
96
{
97
// Looks like unicode, better translate it
98
// UNICODE_STRING specifies the length of the buffer string in Bytes not WCHARS
99
WideCharToMultiByte(CP_ACP, 0, (LPCWSTR) uInputString.Buffer, uInputString.Length/2,
100
lpszOutputString, nOutStringLen-1, NULL, NULL);
101
lpszOutputString[min(uInputString.Length/2,nOutStringLen-1)] = '\0';
102
return TRUE;
103
}
104
105
lpszOutputString[0] = '\0';
106
return FALSE;
107
} // UnicodeStringToANSI
108
109
110
static BOOL
111
is_windows_vista(void)
112
{
113
static BOOL fChecked = FALSE;
114
static BOOL fIsWinVista = FALSE;
115
116
if (!fChecked)
117
{
118
OSVERSIONINFO Version;
119
120
memset (&Version, 0x00, sizeof(Version));
121
Version.dwOSVersionInfoSize = sizeof(Version);
122
123
if (GetVersionEx (&Version))
124
{
125
if (Version.dwPlatformId == VER_PLATFORM_WIN32_NT &&
126
Version.dwMajorVersion >= 6)
127
fIsWinVista = TRUE;
128
}
129
fChecked = TRUE;
130
}
131
132
return fIsWinVista;
133
}
134
135
136
/* Construct a Logon Script that will cause the LogonEventHandler to be executed
137
* under in the logon session
138
*/
139
140
#define RUNDLL32_CMDLINE "rundll32.exe kfwlogon.dll,LogonEventHandler "
141
VOID
142
ConfigureLogonScript(LPWSTR *lpLogonScript, char * filename) {
143
DWORD dwLogonScriptLen;
144
LPWSTR lpScript;
145
LPSTR lpTemp;
146
147
if (!lpLogonScript)
148
return;
149
*lpLogonScript = NULL;
150
151
if (!filename)
152
return;
153
154
dwLogonScriptLen = strlen(RUNDLL32_CMDLINE) + strlen(filename) + 2;
155
lpTemp = (LPSTR) malloc(dwLogonScriptLen);
156
if (!lpTemp)
157
return;
158
159
_snprintf(lpTemp, dwLogonScriptLen, "%s%s", RUNDLL32_CMDLINE, filename);
160
161
SetLastError(0);
162
dwLogonScriptLen = MultiByteToWideChar(CP_ACP, MB_PRECOMPOSED, lpTemp, -1, NULL, 0);
163
DebugEvent("ConfigureLogonScript %s requires %d bytes gle=0x%x", lpTemp, dwLogonScriptLen, GetLastError());
164
165
lpScript = LocalAlloc(LMEM_ZEROINIT, dwLogonScriptLen * 2);
166
if (lpScript) {
167
if (MultiByteToWideChar(CP_ACP, MB_PRECOMPOSED, lpTemp, -1, lpScript, 2 * dwLogonScriptLen))
168
*lpLogonScript = lpScript;
169
else {
170
DebugEvent("ConfigureLogonScript - MultiByteToWideChar failed gle = 0x%x", GetLastError());
171
LocalFree(lpScript);
172
}
173
} else {
174
DebugEvent("LocalAlloc failed gle=0x%x", GetLastError());
175
}
176
free(lpTemp);
177
}
178
179
180
DWORD APIENTRY NPLogonNotify(
181
PLUID lpLogonId,
182
LPCWSTR lpAuthentInfoType,
183
LPVOID lpAuthentInfo,
184
LPCWSTR lpPreviousAuthentInfoType,
185
LPVOID lpPreviousAuthentInfo,
186
LPWSTR lpStationName,
187
LPVOID StationHandle,
188
LPWSTR *lpLogonScript)
189
{
190
char uname[MAX_USERNAME_LENGTH+1]="";
191
char password[MAX_PASSWORD_LENGTH+1]="";
192
char logonDomain[MAX_DOMAIN_LENGTH+1]="";
193
194
MSV1_0_INTERACTIVE_LOGON *IL;
195
196
DWORD code = 0;
197
198
char *reason;
199
char *ctemp;
200
201
BOOLEAN interactive = TRUE;
202
HWND hwndOwner = (HWND)StationHandle;
203
BOOLEAN lowercased_name = TRUE;
204
205
/* Can we load KFW binaries? */
206
if ( !KFW_is_available() )
207
return 0;
208
209
DebugEvent0("NPLogonNotify start");
210
211
/* Remote Desktop / Terminal Server connections to existing sessions
212
* are interactive logons. Unfortunately, because the session already
213
* exists the logon script does not get executed and this prevents
214
* us from being able to execute the rundll32 entrypoint
215
* LogonEventHandlerA which would process the credential cache this
216
* routine will produce. Therefore, we must cleanup orphaned cache
217
* files from this routine. We will take care of it before doing
218
* anything else.
219
*/
220
KFW_cleanup_orphaned_caches();
221
222
/* Are we interactive? */
223
if (lpStationName)
224
interactive = (wcsicmp(lpStationName, L"WinSta0") == 0);
225
226
if ( !interactive ) {
227
char station[64]="station";
228
DWORD rv;
229
230
SetLastError(0);
231
rv = WideCharToMultiByte(CP_UTF8, 0, lpStationName, -1,
232
station, sizeof(station), NULL, NULL);
233
DebugEvent("Skipping NPLogonNotify- LoginId(%d,%d) - Interactive(%d:%s) - gle %d",
234
lpLogonId->HighPart, lpLogonId->LowPart, interactive, rv != 0 ? station : "failure", GetLastError());
235
return 0;
236
} else
237
DebugEvent("NPLogonNotify - LoginId(%d,%d)", lpLogonId->HighPart, lpLogonId->LowPart);
238
239
/* Initialize Logon Script to none */
240
*lpLogonScript=NULL;
241
242
/* MSV1_0_INTERACTIVE_LOGON and KERB_INTERACTIVE_LOGON are equivalent for
243
* our purposes */
244
245
if ( wcsicmp(lpAuthentInfoType,L"MSV1_0:Interactive") &&
246
wcsicmp(lpAuthentInfoType,L"Kerberos:Interactive") )
247
{
248
char msg[64];
249
WideCharToMultiByte(CP_ACP, 0, lpAuthentInfoType, -1,
250
msg, sizeof(msg), NULL, NULL);
251
msg[sizeof(msg)-1]='\0';
252
DebugEvent("NPLogonNotify - Unsupported Authentication Info Type: %s", msg);
253
return 0;
254
}
255
256
IL = (MSV1_0_INTERACTIVE_LOGON *) lpAuthentInfo;
257
258
/* Convert from Unicode to ANSI */
259
260
/*TODO: Use SecureZeroMemory to erase passwords */
261
if (!UnicodeStringToANSI(IL->UserName, uname, MAX_USERNAME_LENGTH) ||
262
!UnicodeStringToANSI(IL->Password, password, MAX_PASSWORD_LENGTH) ||
263
!UnicodeStringToANSI(IL->LogonDomainName, logonDomain, MAX_DOMAIN_LENGTH))
264
return 0;
265
266
/* Make sure AD-DOMAINS sent from login that is sent to us is stripped */
267
ctemp = strchr(uname, '@');
268
if (ctemp) *ctemp = 0;
269
270
/* is the name all lowercase? */
271
for ( ctemp = uname; *ctemp ; ctemp++) {
272
if ( !islower(*ctemp) ) {
273
lowercased_name = FALSE;
274
break;
275
}
276
}
277
278
code = KFW_get_cred(uname, password, 0, &reason);
279
DebugEvent("NPLogonNotify - KFW_get_cred uname=[%s] code=[%d]",uname, code);
280
281
/* remove any kerberos 5 tickets currently held by the SYSTEM account
282
* for this user
283
*/
284
if (!code) {
285
char filename[MAX_PATH+1] = "";
286
char acctname[MAX_USERNAME_LENGTH+MAX_DOMAIN_LENGTH+3]="";
287
PSID pUserSid = NULL;
288
LPTSTR pReferencedDomainName = NULL;
289
DWORD dwSidLen = 0, dwDomainLen = 0, count;
290
SID_NAME_USE eUse;
291
292
if (_snprintf(acctname, sizeof(acctname), "%s\\%s", logonDomain, uname) < 0) {
293
code = -1;
294
goto cleanup;
295
}
296
297
count = GetTempPath(sizeof(filename), filename);
298
if (count == 0 || count > (sizeof(filename)-1)) {
299
code = -1;
300
goto cleanup;
301
}
302
303
if (_snprintf(filename, sizeof(filename), "%s\\kfwlogon-%x.%x",
304
filename, lpLogonId->HighPart, lpLogonId->LowPart) < 0)
305
{
306
code = -1;
307
goto cleanup;
308
}
309
310
KFW_copy_cache_to_system_file(uname, filename);
311
312
/* Need to determine the SID */
313
314
/* First get the size of the required buffers */
315
LookupAccountName (NULL,
316
acctname,
317
pUserSid,
318
&dwSidLen,
319
pReferencedDomainName,
320
&dwDomainLen,
321
&eUse);
322
if(dwSidLen){
323
pUserSid = (PSID) malloc (dwSidLen);
324
memset(pUserSid,0,dwSidLen);
325
}
326
327
if(dwDomainLen){
328
pReferencedDomainName = (LPTSTR) malloc (dwDomainLen * sizeof(TCHAR));
329
memset(pReferencedDomainName,0,dwDomainLen * sizeof(TCHAR));
330
}
331
332
//Now get the SID and the domain name
333
if (pUserSid && LookupAccountName( NULL,
334
acctname,
335
pUserSid,
336
&dwSidLen,
337
pReferencedDomainName,
338
&dwDomainLen,
339
&eUse))
340
{
341
DebugEvent("LookupAccountName obtained user %s sid in domain %s", acctname, pReferencedDomainName);
342
code = KFW_set_ccache_dacl_with_user_sid(filename, pUserSid);
343
344
#ifdef USE_WINLOGON_EVENT
345
/* If we are on Vista, setup a LogonScript
346
* that will execute the LogonEventHandler entry point via rundll32.exe
347
*/
348
if (is_windows_vista()) {
349
ConfigureLogonScript(lpLogonScript, filename);
350
if (*lpLogonScript)
351
DebugEvent0("LogonScript assigned");
352
else
353
DebugEvent0("No Logon Script");
354
}
355
#else
356
ConfigureLogonScript(lpLogonScript, filename);
357
if (*lpLogonScript)
358
DebugEvent0("LogonScript assigned");
359
else
360
DebugEvent0("No Logon Script");
361
#endif
362
} else {
363
DebugEvent0("LookupAccountName failed");
364
DeleteFile(filename);
365
code = -1;
366
}
367
368
cleanup:
369
if (pUserSid)
370
free(pUserSid);
371
if (pReferencedDomainName)
372
free(pReferencedDomainName);
373
}
374
375
KFW_destroy_tickets_for_principal(uname);
376
377
if (code) {
378
char msg[128];
379
HANDLE h;
380
char *ptbuf[1];
381
382
StringCbPrintf(msg, sizeof(msg), "Kerberos ticket acquisition failed: %s", reason);
383
384
h = RegisterEventSource(NULL, KFW_LOGON_EVENT_NAME);
385
ptbuf[0] = msg;
386
ReportEvent(h, EVENTLOG_WARNING_TYPE, 0, 1008, NULL, 1, 0, ptbuf, NULL);
387
DeregisterEventSource(h);
388
SetLastError(code);
389
}
390
391
if (code)
392
DebugEvent0("NPLogonNotify failure");
393
else
394
DebugEvent0("NPLogonNotify success");
395
396
return code;
397
}
398
399
400
DWORD APIENTRY NPPasswordChangeNotify(
401
LPCWSTR lpAuthentInfoType,
402
LPVOID lpAuthentInfo,
403
LPCWSTR lpPreviousAuthentInfoType,
404
LPVOID lpPreviousAuthentInfo,
405
LPWSTR lpStationName,
406
LPVOID StationHandle,
407
DWORD dwChangeInfo)
408
{
409
return 0;
410
}
411
412
#include <userenv.h>
413
#include <Winwlx.h>
414
415
#ifdef COMMENT
416
typedef struct _WLX_NOTIFICATION_INFO {
417
ULONG Size;
418
ULONG Flags;
419
PWSTR UserName;
420
PWSTR Domain;
421
PWSTR WindowStation;
422
HANDLE hToken;
423
HDESK hDesktop;
424
PFNMSGECALLBACK pStatusCallback;
425
} WLX_NOTIFICATION_INFO, *PWLX_NOTIFICATION_INFO;
426
#endif
427
428
VOID KFW_Startup_Event( PWLX_NOTIFICATION_INFO pInfo )
429
{
430
DebugEvent0("KFW_Startup_Event");
431
}
432
433
static BOOL
434
GetSecurityLogonSessionData(HANDLE hToken, PSECURITY_LOGON_SESSION_DATA * ppSessionData)
435
{
436
NTSTATUS Status = 0;
437
TOKEN_STATISTICS Stats;
438
DWORD ReqLen;
439
BOOL Success;
440
441
if (!ppSessionData)
442
return FALSE;
443
*ppSessionData = NULL;
444
445
446
Success = GetTokenInformation( hToken, TokenStatistics, &Stats, sizeof(TOKEN_STATISTICS), &ReqLen );
447
if ( !Success )
448
return FALSE;
449
450
Status = LsaGetLogonSessionData( &Stats.AuthenticationId, ppSessionData );
451
if ( FAILED(Status) || !ppSessionData )
452
return FALSE;
453
454
return TRUE;
455
}
456
457
VOID KFW_Logon_Event( PWLX_NOTIFICATION_INFO pInfo )
458
{
459
#ifdef USE_WINLOGON_EVENT
460
WCHAR szUserW[128] = L"";
461
char szUserA[128] = "";
462
char szPath[MAX_PATH] = "";
463
char szLogonId[128] = "";
464
DWORD count;
465
char filename[MAX_PATH] = "";
466
char newfilename[MAX_PATH] = "";
467
char commandline[MAX_PATH+256] = "";
468
STARTUPINFO startupinfo;
469
PROCESS_INFORMATION procinfo;
470
HANDLE hf = NULL;
471
472
LUID LogonId = {0, 0};
473
PSECURITY_LOGON_SESSION_DATA pLogonSessionData = NULL;
474
475
HKEY hKey1 = NULL, hKey2 = NULL;
476
477
DebugEvent0("KFW_Logon_Event - Start");
478
479
GetSecurityLogonSessionData( pInfo->hToken, &pLogonSessionData );
480
481
if ( pLogonSessionData ) {
482
LogonId = pLogonSessionData->LogonId;
483
DebugEvent("KFW_Logon_Event - LogonId(%d,%d)", LogonId.HighPart, LogonId.LowPart);
484
485
_snprintf(szLogonId, sizeof(szLogonId), "kfwlogon-%d.%d",LogonId.HighPart, LogonId.LowPart);
486
LsaFreeReturnBuffer( pLogonSessionData );
487
} else {
488
DebugEvent0("KFW_Logon_Event - Unable to determine LogonId");
489
return;
490
}
491
492
count = GetEnvironmentVariable("TEMP", filename, sizeof(filename));
493
if ( count > sizeof(filename) || count == 0 ) {
494
GetWindowsDirectory(filename, sizeof(filename));
495
}
496
497
if ( strlen(filename) + strlen(szLogonId) + 2 > sizeof(filename) ) {
498
DebugEvent0("KFW_Logon_Event - filename too long");
499
return;
500
}
501
502
strcat(filename, "\\");
503
strcat(filename, szLogonId);
504
505
hf = CreateFile(filename, FILE_ALL_ACCESS, 0, NULL, OPEN_EXISTING,
506
FILE_ATTRIBUTE_NORMAL, NULL);
507
if (hf == INVALID_HANDLE_VALUE) {
508
DebugEvent0("KFW_Logon_Event - file cannot be opened");
509
return;
510
}
511
CloseHandle(hf);
512
513
if (KFW_set_ccache_dacl(filename, pInfo->hToken)) {
514
DebugEvent0("KFW_Logon_Event - unable to set dacl");
515
DeleteFile(filename);
516
return;
517
}
518
519
if (KFW_obtain_user_temp_directory(pInfo->hToken, newfilename, sizeof(newfilename))) {
520
DebugEvent0("KFW_Logon_Event - unable to obtain temp directory");
521
return;
522
}
523
524
if ( strlen(newfilename) + strlen(szLogonId) + 2 > sizeof(newfilename) ) {
525
DebugEvent0("KFW_Logon_Event - new filename too long");
526
return;
527
}
528
529
strcat(newfilename, "\\");
530
strcat(newfilename, szLogonId);
531
532
if (!MoveFileEx(filename, newfilename,
533
MOVEFILE_COPY_ALLOWED | MOVEFILE_REPLACE_EXISTING | MOVEFILE_WRITE_THROUGH)) {
534
DebugEvent("KFW_Logon_Event - MoveFileEx failed GLE = 0x%x", GetLastError());
535
return;
536
}
537
538
_snprintf(commandline, sizeof(commandline), "kfwcpcc.exe \"%s\"", newfilename);
539
540
GetStartupInfo(&startupinfo);
541
if (CreateProcessAsUser( pInfo->hToken,
542
"kfwcpcc.exe",
543
commandline,
544
NULL,
545
NULL,
546
FALSE,
547
CREATE_NEW_PROCESS_GROUP | DETACHED_PROCESS,
548
NULL,
549
NULL,
550
&startupinfo,
551
&procinfo))
552
{
553
DebugEvent("KFW_Logon_Event - CommandLine %s", commandline);
554
555
WaitForSingleObject(procinfo.hProcess, 30000);
556
557
CloseHandle(procinfo.hThread);
558
CloseHandle(procinfo.hProcess);
559
} else {
560
DebugEvent0("KFW_Logon_Event - CreateProcessFailed");
561
}
562
563
DeleteFile(newfilename);
564
565
DebugEvent0("KFW_Logon_Event - End");
566
#endif /* USE_WINLOGON_EVENT */
567
}
568
569
570
/* Documentation on the use of RunDll32 entrypoints can be found
571
* at https://support.microsoft.com/kb/164787
572
*/
573
void CALLBACK
574
LogonEventHandlerA(HWND hwnd, HINSTANCE hinst, LPSTR lpszCmdLine, int nCmdShow)
575
{
576
HANDLE hf = NULL;
577
char commandline[MAX_PATH+256] = "";
578
STARTUPINFO startupinfo;
579
PROCESS_INFORMATION procinfo;
580
581
DebugEvent0("LogonEventHandler - Start");
582
583
/* Validate lpszCmdLine as a file */
584
hf = CreateFile(lpszCmdLine, GENERIC_READ | DELETE, 0, NULL, OPEN_EXISTING,
585
FILE_ATTRIBUTE_NORMAL, NULL);
586
if (hf == INVALID_HANDLE_VALUE) {
587
DebugEvent("LogonEventHandler - \"%s\" cannot be opened", lpszCmdLine);
588
return;
589
}
590
CloseHandle(hf);
591
592
593
_snprintf(commandline, sizeof(commandline), "kfwcpcc.exe \"%s\"", lpszCmdLine);
594
595
GetStartupInfo(&startupinfo);
596
SetLastError(0);
597
if (CreateProcess( NULL,
598
commandline,
599
NULL,
600
NULL,
601
FALSE,
602
CREATE_NEW_PROCESS_GROUP | DETACHED_PROCESS,
603
NULL,
604
NULL,
605
&startupinfo,
606
&procinfo))
607
{
608
DebugEvent("KFW_Logon_Event - CommandLine %s", commandline);
609
610
WaitForSingleObject(procinfo.hProcess, 30000);
611
612
CloseHandle(procinfo.hThread);
613
CloseHandle(procinfo.hProcess);
614
} else {
615
DebugEvent("KFW_Logon_Event - CreateProcessFailed \"%s\" GLE 0x%x",
616
commandline, GetLastError());
617
DebugEvent("KFW_Logon_Event PATH %s", getenv("PATH"));
618
}
619
620
DeleteFile(lpszCmdLine);
621
622
DebugEvent0("KFW_Logon_Event - End");
623
}
624
625