windows-logon-20050323
[openafs.git] / src / WINNT / afsd / afslogon.c
1 /*
2  * Copyright 2000, International Business Machines Corporation and others.
3  * All Rights Reserved.
4  * 
5  * This software has been released under the terms of the IBM Public
6  * License.  For details, see the LICENSE file in the top-level source
7  * directory or online at http://www.openafs.org/dl/license10.html
8  */
9
10 #include "afslogon.h"
11
12 #include <io.h>
13 #include <sys/stat.h>
14 #include <sys/types.h>
15 #include <fcntl.h>
16
17 #include <winsock2.h>
18 #include <lm.h>
19
20 #include <afs/param.h>
21 #include <afs/stds.h>
22 #include <afs/pioctl_nt.h>
23 #include <afs/kautils.h>
24
25 #include "afsd.h"
26 #include "cm_config.h"
27 #include "krb.h"
28 #include "afskfw.h"
29
30 #include <WINNT\afsreg.h>
31
32 DWORD TraceOption = 0;
33
34 HANDLE hDLL;
35
36 WSADATA WSAjunk;
37 #define AFS_LOGON_EVENT_NAME TEXT("AFS Logon")
38
39 void DebugEvent0(char *a) 
40 {
41     HANDLE h; char *ptbuf[1];
42     if (!ISLOGONTRACE(TraceOption))
43         return;
44     h = RegisterEventSource(NULL, AFS_LOGON_EVENT_NAME);
45     ptbuf[0] = a;
46     ReportEvent(h, EVENTLOG_INFORMATION_TYPE, 0, 0, NULL, 1, 0, (const char **)ptbuf, NULL);
47     DeregisterEventSource(h);
48 }
49
50 #define MAXBUF_ 512
51 void DebugEvent(char *b,...) 
52 {
53     HANDLE h; char *ptbuf[1],buf[MAXBUF_+1];
54     va_list marker;
55
56     if (!ISLOGONTRACE(TraceOption))
57         return;
58
59     h = RegisterEventSource(NULL, AFS_LOGON_EVENT_NAME);
60     va_start(marker,b);
61     StringCbVPrintf(buf, MAXBUF_+1,b,marker);
62     buf[MAXBUF_] = '\0';
63     ptbuf[0] = buf;
64     ReportEvent(h, EVENTLOG_INFORMATION_TYPE, 0, 0, NULL, 1, 0, (const char **)ptbuf, NULL);
65     DeregisterEventSource(h);
66     va_end(marker);
67 }
68
69 static HANDLE hInitMutex = NULL;
70 static BOOL bInit = FALSE;
71
72 BOOLEAN APIENTRY DllEntryPoint(HANDLE dll, DWORD reason, PVOID reserved)
73 {
74     hDLL = dll;
75     switch (reason) {
76     case DLL_PROCESS_ATTACH:
77         /* Initialization Mutex */
78         hInitMutex = CreateMutex(NULL, FALSE, NULL);
79         break;
80
81     case DLL_PROCESS_DETACH:
82         CloseHandle(hInitMutex);
83         break;
84
85     case DLL_THREAD_ATTACH:
86     case DLL_THREAD_DETACH:
87     default:
88         /* Everything else succeeds but does nothing. */
89         break;
90     }
91
92     return TRUE;
93 }
94
95 void AfsLogonInit(void)
96 {
97     if ( bInit == FALSE ) {
98         if ( WaitForSingleObject( hInitMutex, INFINITE ) == WAIT_OBJECT_0 ) {
99             if ( bInit == FALSE ) {
100                 rx_Init(0);
101                 initAFSDirPath();
102                 ka_Init(0);
103                 bInit = TRUE;
104             }
105             ReleaseMutex(hInitMutex);
106         }
107     }
108 }
109
110 CHAR *GenRandomName(CHAR *pbuf)
111 {
112     int i;
113     srand( (unsigned)time( NULL ) );
114     for (i=0;i<MAXRANDOMNAMELEN-1;i++)
115         pbuf[i]='a'+(rand() % 26);
116     pbuf[MAXRANDOMNAMELEN-1]=0;
117     return pbuf;
118 }
119
120 BOOLEAN AFSWillAutoStart(void)
121 {
122     SC_HANDLE scm;
123     SC_HANDLE svc;
124     BOOLEAN flag;
125     BOOLEAN result = FALSE;
126     LPQUERY_SERVICE_CONFIG pConfig = NULL;
127     DWORD BufSize;
128     LONG status;
129
130     /* Open services manager */
131     scm = OpenSCManager(NULL, NULL, GENERIC_READ);
132     if (!scm) return FALSE;
133
134     /* Open AFSD service */
135     svc = OpenService(scm, "TransarcAFSDaemon", SERVICE_QUERY_CONFIG);
136     if (!svc)
137         goto close_scm;
138
139     /* Query AFSD service config, first just to get buffer size */
140     /* Expected to fail, so don't test return value */
141     (void) QueryServiceConfig(svc, NULL, 0, &BufSize);
142     status = GetLastError();
143     if (status != ERROR_INSUFFICIENT_BUFFER)
144         goto close_svc;
145
146     /* Allocate buffer */
147     pConfig = (LPQUERY_SERVICE_CONFIG)GlobalAlloc(GMEM_FIXED,BufSize);
148     if (!pConfig)
149         goto close_svc;
150
151     /* Query AFSD service config, this time for real */
152     flag = QueryServiceConfig(svc, pConfig, BufSize, &BufSize);
153     if (!flag)
154         goto free_pConfig;
155
156     /* Is it autostart? */
157     if (pConfig->dwStartType < SERVICE_DEMAND_START)
158         result = TRUE;
159
160   free_pConfig:
161     GlobalFree(pConfig);
162   close_svc:
163     CloseServiceHandle(svc);
164   close_scm:
165     CloseServiceHandle(scm);
166
167     return result;
168 }
169
170 DWORD MapAuthError(DWORD code)
171 {
172     switch (code) {
173         /* Unfortunately, returning WN_NO_NETWORK results in the MPR abandoning
174          * logon scripts for all credential managers, although they will still
175          * receive logon notifications.  Since we don't want this, we return
176          * WN_SUCCESS.  This is highly undesirable, but we also don't want to
177          * break other network providers.
178          */
179  /* case KTC_NOCM:
180     case KTC_NOCMRPC:
181     return WN_NO_NETWORK; */
182     default: return WN_SUCCESS;
183   }
184 }
185
186 DWORD APIENTRY NPGetCaps(DWORD index)
187 {
188     switch (index) {
189     case WNNC_NET_TYPE:
190         /* Don't have our own type; use somebody else's. */
191         return WNNC_NET_SUN_PC_NFS;
192
193     case WNNC_START:
194         /* Say we are already started, even though we might wait after we receive NPLogonNotify */
195         return 1;
196
197     default:
198         return 0;
199     }
200 }       
201
202 NET_API_STATUS 
203 NetUserGetProfilePath( LPCWSTR Domain, LPCWSTR UserName, char * profilePath, 
204                        DWORD profilePathLen )
205 {
206     NET_API_STATUS code;
207     LPWSTR ServerName = NULL;
208     LPUSER_INFO_3 p3 = NULL;
209
210     NetGetAnyDCName(NULL, Domain, (LPBYTE *)&ServerName);
211     /* if NetGetAnyDCName fails, ServerName == NULL
212      * NetUserGetInfo will obtain local user information 
213      */
214     code = NetUserGetInfo(ServerName, UserName, 3, (LPBYTE *)&p3);
215     if (code == NERR_Success)
216     {
217         code = NERR_UserNotFound;
218         if (p3) {
219             if (p3->usri3_profile) {
220                 DWORD len = lstrlenW(p3->usri3_profile);
221                 if (len > 0) {
222                     /* Convert From Unicode to ANSI (UTF-8 for future) */
223                     len = len < profilePathLen ? len : profilePathLen - 1;
224                     WideCharToMultiByte(CP_UTF8, 0, p3->usri3_profile, len, profilePath, len, NULL, NULL);
225                     profilePath[len] = '\0';
226                     code = NERR_Success;
227                 }
228             }
229             NetApiBufferFree(p3);
230         }
231     }
232     if (ServerName) 
233         NetApiBufferFree(ServerName);
234     return code;
235 }
236
237 BOOL IsServiceRunning (void)
238 {
239     SERVICE_STATUS Status;
240     SC_HANDLE hManager;
241     memset (&Status, 0x00, sizeof(Status));
242     Status.dwCurrentState = SERVICE_STOPPED;
243
244     if ((hManager = OpenSCManager (NULL, NULL, GENERIC_READ)) != NULL)
245     {
246         SC_HANDLE hService;
247         if ((hService = OpenService (hManager, TEXT("TransarcAFSDaemon"), GENERIC_READ)) != NULL)
248         {
249             QueryServiceStatus (hService, &Status);
250             CloseServiceHandle (hService);
251         }
252
253         CloseServiceHandle (hManager);
254     }
255     DebugEvent("AFS AfsLogon - Test Service Running","Return Code[%x] ?Running[%d]",Status.dwCurrentState,(Status.dwCurrentState == SERVICE_RUNNING));
256     return (Status.dwCurrentState == SERVICE_RUNNING);
257 }   
258
259 BOOL IsServiceStartPending (void)
260 {
261     SERVICE_STATUS Status;
262     SC_HANDLE hManager;
263     memset (&Status, 0x00, sizeof(Status));
264     Status.dwCurrentState = SERVICE_STOPPED;
265
266     if ((hManager = OpenSCManager (NULL, NULL, GENERIC_READ)) != NULL)
267     {
268         SC_HANDLE hService;
269         if ((hService = OpenService (hManager, TEXT("TransarcAFSDaemon"), GENERIC_READ)) != NULL)
270         {
271             QueryServiceStatus (hService, &Status);
272             CloseServiceHandle (hService);
273         }
274
275         CloseServiceHandle (hManager);
276     }
277     DebugEvent("AFS AfsLogon - Test Service Start Pending","Return Code[%x] ?Start Pending[%d]",Status.dwCurrentState,(Status.dwCurrentState == SERVICE_START_PENDING));
278     return (Status.dwCurrentState == SERVICE_RUNNING);
279 }   
280
281 /* LOOKUPKEYCHAIN: macro to look up the value in the list of keys in order until it's found
282    v:variable to receive value (reference type)
283    t:type
284    d:default, in case the value isn't on any of the keys
285    n:name of value */
286 #define LOOKUPKEYCHAIN(v,t,d,n) \
287         do { \
288                 rv = ~ERROR_SUCCESS; \
289                 dwType = t; \
290                 if(hkDom) { \
291                         dwSize = sizeof(v); \
292                         rv = RegQueryValueEx(hkDom, n, 0, &dwType, (LPBYTE) &(v), &dwSize); \
293                         if(rv == ERROR_SUCCESS) DebugEvent(#v " found in hkDom with type [%d]", dwType); \
294                 } \
295                 if(hkDoms && (rv != ERROR_SUCCESS || dwType != t)) { \
296                         dwSize = sizeof(v); \
297                         rv = RegQueryValueEx(hkDoms, n, 0, &dwType, (LPBYTE) &(v), &dwSize); \
298                         if(rv == ERROR_SUCCESS) DebugEvent(#v " found in hkDoms with type [%d]", dwType); \
299                 } \
300                 if(hkNp && (rv != ERROR_SUCCESS || dwType != t)) { \
301                         dwSize = sizeof(v); \
302                         rv = RegQueryValueEx(hkNp, n, 0, &dwType, (LPBYTE) &(v), &dwSize); \
303                         if(rv == ERROR_SUCCESS) DebugEvent(#v " found in hkNp with type [%d]", dwType); \
304                 } \
305                 if(rv != ERROR_SUCCESS || dwType != t) { \
306                         v = d; \
307                         DebugEvent(#v " being set to default"); \
308                 } \
309         } while(0)
310
311 /* Get domain specific configuration info.  We are returning void because if anything goes wrong
312    we just return defaults.
313  */
314 void 
315 GetDomainLogonOptions( PLUID lpLogonId, char * username, char * domain, LogonOptions_t *opt ) {
316     HKEY hkParm = NULL; /* Service parameter */
317     HKEY hkNp = NULL;   /* network provider key */
318     HKEY hkDoms = NULL; /* domains key */
319     HKEY hkDom = NULL;  /* DOMAINS/domain key */
320     HKEY hkTemp = NULL;
321     LONG rv;
322     DWORD dwSize;
323     DWORD dwType;
324     DWORD dwDummy;
325     char computerName[MAX_COMPUTERNAME_LENGTH + 1];
326     char *effDomain;
327
328     memset(opt, 0, sizeof(LogonOptions_t));
329
330     DebugEvent("In GetDomainLogonOptions for user [%s] in domain [%s]", username, domain);
331     /* If the domain is the same as the Netbios computer name, we use the LOCALHOST domain name*/
332     opt->flags = LOGON_FLAG_REMOTE;
333     if(domain) {
334         dwSize = MAX_COMPUTERNAME_LENGTH;
335         if(GetComputerName(computerName, &dwSize)) {
336             if(!stricmp(computerName, domain)) {
337                 effDomain = "LOCALHOST";
338                 opt->flags = LOGON_FLAG_LOCAL;
339             }
340             else
341                 effDomain = domain;
342         }
343     } else
344         effDomain = NULL;
345
346     rv = RegOpenKeyEx( HKEY_LOCAL_MACHINE, AFSREG_CLT_SVC_PARAM_SUBKEY, 0, KEY_READ, &hkParm );
347     if(rv != ERROR_SUCCESS) {
348         hkParm = NULL;
349         DebugEvent("GetDomainLogonOption: Can't open parms key [%d]", rv);
350     }
351
352     rv = RegOpenKeyEx( HKEY_LOCAL_MACHINE, AFSREG_CLT_SVC_PROVIDER_SUBKEY, 0, KEY_READ, &hkNp );
353     if(rv != ERROR_SUCCESS) {
354         hkNp = NULL;
355         DebugEvent("GetDomainLogonOptions: Can't open NP key [%d]", rv);
356     }
357
358     if(hkNp) {
359         rv = RegOpenKeyEx( hkNp, REG_CLIENT_DOMAINS_SUBKEY, 0, KEY_READ, &hkDoms );
360         if( rv != ERROR_SUCCESS ) {
361             hkDoms = NULL;
362             DebugEvent("GetDomainLogonOptions: Can't open Domains key [%d]", rv);
363         }
364     }
365
366     if(hkDoms && effDomain) {
367         rv = RegOpenKeyEx( hkDoms, effDomain, 0, KEY_READ, &hkDom );
368         if( rv != ERROR_SUCCESS ) {
369             hkDom = NULL;
370             DebugEvent("GetDomainLogonOptions: Can't open domain key for [%s] [%d]", effDomain, rv);
371             /* If none of the domains match, we shouldn't use the domain key either */
372             RegCloseKey(hkDoms);
373             hkDoms = NULL;
374         }
375     } else
376         DebugEvent("Not opening domain key for [%s]", effDomain);
377
378     /* Each individual can either be specified on the domain key, the domains key or in the
379        net provider key.  They fail over in that order.  If none is found, we just use the 
380        defaults. */
381
382     /* LogonOption */
383     LOOKUPKEYCHAIN(opt->LogonOption, REG_DWORD, DEFAULT_LOGON_OPTION, REG_CLIENT_LOGON_OPTION_PARM);
384
385     /* FailLoginsSilently */
386     dwSize = sizeof(dwDummy);
387     rv = RegQueryValueEx(hkParm, REG_CLIENT_FAIL_SILENTLY_PARM, 0, &dwType, (LPBYTE) &dwDummy, &dwSize);
388     if (rv != ERROR_SUCCESS)
389         LOOKUPKEYCHAIN(dwDummy, REG_DWORD, DEFAULT_FAIL_SILENTLY, REG_CLIENT_FAIL_SILENTLY_PARM);
390     opt->failSilently = !!dwDummy;
391
392     /* Retry interval */
393     LOOKUPKEYCHAIN(opt->retryInterval, REG_DWORD, DEFAULT_RETRY_INTERVAL, REG_CLIENT_RETRY_INTERVAL_PARM);
394
395     /* Sleep interval */
396     LOOKUPKEYCHAIN(opt->sleepInterval, REG_DWORD, DEFAULT_SLEEP_INTERVAL, REG_CLIENT_SLEEP_INTERVAL_PARM);
397
398     opt->logonScript = NULL;
399     opt->smbName = NULL;
400
401     if(!ISLOGONINTEGRATED(opt->LogonOption)) {
402         goto cleanup; /* no need to lookup the logon script */
403     }
404
405     /* come up with SMB username */
406     if(ISHIGHSECURITY(opt->LogonOption)) {
407         opt->smbName = malloc( MAXRANDOMNAMELEN );
408         GenRandomName(opt->smbName);
409     } else {
410         /* username and domain for logon session is not necessarily the same as
411            username and domain passed into network provider. */
412         PSECURITY_LOGON_SESSION_DATA plsd;
413         char lsaUsername[MAX_USERNAME_LENGTH];
414         char lsaDomain[MAX_DOMAIN_LENGTH];
415         size_t len, tlen;
416
417         LsaGetLogonSessionData(lpLogonId, &plsd);
418         
419         UnicodeStringToANSI(plsd->UserName, lsaUsername, MAX_USERNAME_LENGTH);
420         UnicodeStringToANSI(plsd->LogonDomain, lsaDomain, MAX_DOMAIN_LENGTH);
421
422         DebugEvent("PLSD username[%s] domain[%s]",lsaUsername,lsaDomain);
423
424         if(SUCCEEDED(StringCbLength(lsaUsername, MAX_USERNAME_LENGTH, &tlen)))
425             len = tlen;
426         else
427             goto bad_strings;
428
429         if(SUCCEEDED(StringCbLength(lsaDomain, MAX_DOMAIN_LENGTH, &tlen)))
430             len += tlen;
431         else
432             goto bad_strings;
433
434         len += 2;
435
436         opt->smbName = malloc(len);
437
438         StringCbCopy(opt->smbName, len, lsaDomain);
439         StringCbCat(opt->smbName, len, "\\");
440         StringCbCat(opt->smbName, len, lsaUsername);
441
442         strlwr(opt->smbName);
443
444       bad_strings:
445         LsaFreeReturnBuffer(plsd);
446     }
447
448     DebugEvent("Looking up logon script");
449     /* Logon script */
450     /* First find out where the key is */
451     hkTemp = NULL;
452     rv = ~ERROR_SUCCESS;
453     dwType = 0;
454     if(hkDom)
455         rv = RegQueryValueExW(hkDom, REG_CLIENT_LOGON_SCRIPT_PARMW, 0, &dwType, NULL, &dwSize);
456     if(rv == ERROR_SUCCESS && (dwType == REG_SZ || dwType == REG_EXPAND_SZ)) {
457         hkTemp = hkDom;
458         DebugEvent("Located logon script in hkDom");
459     }
460     else if(hkDoms)
461         rv = RegQueryValueExW(hkDoms, REG_CLIENT_LOGON_SCRIPT_PARMW, 0, &dwType, NULL, &dwSize);
462     if(rv == ERROR_SUCCESS && !hkTemp && (dwType == REG_SZ || dwType == REG_EXPAND_SZ)) {
463         hkTemp = hkDoms;
464         DebugEvent("Located logon script in hkDoms");
465     }
466     /* Note that the LogonScript in the NP key is only used if we are doing high security. */
467     else if(hkNp && ISHIGHSECURITY(opt->LogonOption))
468         rv = RegQueryValueExW(hkNp, REG_CLIENT_LOGON_SCRIPT_PARMW, 0, &dwType, NULL, &dwSize);
469     if(rv == ERROR_SUCCESS && !hkTemp && (dwType == REG_SZ || dwType == REG_EXPAND_SZ)) {
470         hkTemp = hkNp;
471         DebugEvent("Located logon script in hkNp");
472     }
473
474     if(hkTemp) {
475         WCHAR *regscript        = NULL;
476         WCHAR *regexscript      = NULL;
477         WCHAR *regexuscript     = NULL;
478         WCHAR *wuname           = NULL;
479         HRESULT hr;
480
481         size_t len;
482
483         StringCbLength(opt->smbName, MAX_USERNAME_LENGTH, &len);
484         len ++;
485
486         wuname = malloc(len * sizeof(WCHAR));
487         MultiByteToWideChar(CP_ACP,0,opt->smbName,-1,wuname,len*sizeof(WCHAR));
488
489         DebugEvent("Username is set for [%S]", wuname);
490
491         /* dwSize still has the size of the required buffer in bytes. */
492         regscript = malloc(dwSize);
493         rv = RegQueryValueExW(hkTemp, REG_CLIENT_LOGON_SCRIPT_PARMW, 0, &dwType, (LPBYTE) regscript, &dwSize);
494         if(rv != ERROR_SUCCESS) {/* what the ..? */
495             DebugEvent("Can't look up logon script [%d]",rv);
496             goto doneLogonScript;
497         }
498
499         DebugEvent("Found logon script [%S]", regscript);
500
501         if(dwType == REG_EXPAND_SZ) {
502             DWORD dwReq;
503
504             dwSize += MAX_PATH * sizeof(WCHAR);  /* make room for environment expansion. */
505             regexscript = malloc(dwSize);
506             dwReq = ExpandEnvironmentStringsW(regscript, regexscript, dwSize / sizeof(WCHAR));
507             free(regscript);
508             regscript = regexscript;
509             regexscript = NULL;
510             if(dwReq > (dwSize / sizeof(WCHAR))) {
511                 DebugEvent("Overflow while expanding environment strings.");
512                 goto doneLogonScript;
513             }
514         }
515
516         DebugEvent("After expanding env strings [%S]", regscript);
517
518         if(wcsstr(regscript, L"%s")) {
519             dwSize += len * sizeof(WCHAR); /* make room for username expansion */
520             regexuscript = (WCHAR *) LocalAlloc(LMEM_FIXED, dwSize);
521             hr = StringCbPrintfW(regexuscript, dwSize, regscript, wuname);
522         } else {
523             regexuscript = (WCHAR *) LocalAlloc(LMEM_FIXED, dwSize);
524             hr = StringCbCopyW(regexuscript, dwSize, regscript);
525         }
526
527         DebugEvent("After expanding username [%S]", regexuscript);
528
529         if(hr == S_OK)
530             opt->logonScript = regexuscript;
531         else
532             LocalFree(regexuscript);
533
534       doneLogonScript:
535         if(wuname) free(wuname);
536         if(regscript) free(regscript);
537         if(regexscript) free(regexscript);
538     }
539
540     DebugEvent("Looking up TheseCells");
541     /* Logon script */
542     /* First find out where the key is */
543     hkTemp = NULL;
544     rv = ~ERROR_SUCCESS;
545     dwType = 0;
546     if (hkDom)
547         rv = RegQueryValueEx(hkDom, REG_CLIENT_THESE_CELLS_PARM, 0, &dwType, NULL, &dwSize);
548     if (rv == ERROR_SUCCESS && dwType == REG_MULTI_SZ) {
549         hkTemp = hkDom;
550         DebugEvent("Located TheseCells in hkDom");
551     } else if (hkDoms)
552         rv = RegQueryValueEx(hkDoms, REG_CLIENT_THESE_CELLS_PARM, 0, &dwType, NULL, &dwSize);
553     if (rv == ERROR_SUCCESS && !hkTemp && dwType == REG_MULTI_SZ) {
554         hkTemp = hkDoms;
555         DebugEvent("Located TheseCells in hkDoms");
556     } else if (hkNp)
557         rv = RegQueryValueEx(hkNp, REG_CLIENT_THESE_CELLS_PARM, 0, &dwType, NULL, &dwSize);
558     if (rv == ERROR_SUCCESS && !hkTemp && dwType == REG_MULTI_SZ) {
559         hkTemp = hkNp;
560         DebugEvent("Located TheseCells in hkNp");
561     }
562
563     if (hkTemp) {
564         HRESULT hr;
565         size_t len;
566         CHAR * thesecells;
567
568         /* dwSize still has the size of the required buffer in bytes. */
569         thesecells = malloc(dwSize);
570         rv = RegQueryValueEx(hkTemp, REG_CLIENT_THESE_CELLS_PARM, 0, &dwType, (LPBYTE) thesecells, &dwSize);
571         if(rv != ERROR_SUCCESS) {/* what the ..? */
572             DebugEvent("Can't look up TheseCells [%d]",rv);
573             goto doneTheseCells;
574         }
575
576         DebugEvent("Found TheseCells [%s]", thesecells);
577         opt->theseCells = thesecells;
578
579       doneTheseCells:;
580     }
581
582   cleanup:
583     if(hkNp) RegCloseKey(hkNp);
584     if(hkDom) RegCloseKey(hkDom);
585     if(hkDoms) RegCloseKey(hkDoms);
586     if(hkParm) RegCloseKey(hkParm);
587 }       
588
589 #undef LOOKUPKEYCHAIN
590
591 /* Try to find out which cell the given path is in.  We must retain
592    the contents of *cell in case of failure. *cell is assumed to be
593    at least cellLen chars */
594 DWORD GetFileCellName(char * path, char * cell, size_t cellLen) {
595     struct ViceIoctl blob;
596     char tcell[MAX_PATH];
597     DWORD code;
598
599     blob.in_size = 0;
600     blob.out_size = MAX_PATH;
601     blob.out = tcell;
602
603     code = pioctl(path, VIOC_FILE_CELL_NAME, &blob, 1);
604
605     if(!code) {
606         strncpy(cell, tcell, cellLen);
607         cell[cellLen - 1] = '\0';
608     }
609     return code;
610 }       
611
612
613 static BOOL
614 WINAPI
615 UnicodeStringToANSI(UNICODE_STRING uInputString, LPSTR lpszOutputString, int nOutStringLen)
616 {
617     CPINFO CodePageInfo;
618
619     GetCPInfo(CP_ACP, &CodePageInfo);
620
621     if (CodePageInfo.MaxCharSize > 1)
622         // Only supporting non-Unicode strings
623         return FALSE;
624     
625     if (uInputString.Buffer && ((LPBYTE) uInputString.Buffer)[1] == '\0')
626     {
627         // Looks like unicode, better translate it
628         // UNICODE_STRING specifies the length of the buffer string in Bytes not WCHARS
629         WideCharToMultiByte(CP_ACP, 0, (LPCWSTR) uInputString.Buffer, uInputString.Length/2,
630                             lpszOutputString, nOutStringLen-1, NULL, NULL);
631         lpszOutputString[min(uInputString.Length/2,nOutStringLen-1)] = '\0';
632         return TRUE;
633     }
634     else
635         lpszOutputString[0] = '\0';
636     return FALSE;
637 }  // UnicodeStringToANSI
638
639 DWORD APIENTRY NPLogonNotify(
640         PLUID lpLogonId,
641         LPCWSTR lpAuthentInfoType,
642         LPVOID lpAuthentInfo,
643         LPCWSTR lpPreviousAuthentInfoType,
644         LPVOID lpPreviousAuthentInfo,
645         LPWSTR lpStationName,
646         LPVOID StationHandle,
647         LPWSTR *lpLogonScript)
648 {
649     char uname[MAX_USERNAME_LENGTH]="";
650     char password[MAX_PASSWORD_LENGTH]="";
651     char logonDomain[MAX_DOMAIN_LENGTH]="";
652     char cell[256]="<non-integrated logon>";
653     char homePath[MAX_PATH]="";
654
655     MSV1_0_INTERACTIVE_LOGON *IL;
656
657     DWORD code;
658
659     int pw_exp;
660     char *reason;
661     char *ctemp;
662
663     BOOLEAN interactive;
664     BOOLEAN flag;
665     DWORD LSPtype, LSPsize;
666     HKEY NPKey;
667
668     HWND hwndOwner = (HWND)StationHandle;
669
670     BOOLEAN afsWillAutoStart;
671
672     BOOLEAN lowercased_name = TRUE;
673
674     LogonOptions_t opt; /* domain specific logon options */
675     int retryInterval;
676     int sleepInterval;
677
678     /* Make sure the AFS Libraries are initialized */
679     AfsLogonInit();
680
681     /* Initialize Logon Script to none */
682     *lpLogonScript=NULL;
683     
684     /* TODO: We should check the value of lpAuthentInfoType before assuming that it is
685        MSV1_0_INTERACTIVE_LOGON though for our purposes KERB_INTERACTIVE_LOGON is
686        co-incidentally equivalent. */
687     IL = (MSV1_0_INTERACTIVE_LOGON *) lpAuthentInfo;
688
689     /* Are we interactive? */
690     interactive = (wcscmp(lpStationName, L"WinSta0") == 0);
691
692     /* Convert from Unicode to ANSI */
693
694     /*TODO: Use SecureZeroMemory to erase passwords */
695     UnicodeStringToANSI(IL->UserName, uname, MAX_USERNAME_LENGTH);
696     UnicodeStringToANSI(IL->Password, password, MAX_PASSWORD_LENGTH);
697     UnicodeStringToANSI(IL->LogonDomainName, logonDomain, MAX_DOMAIN_LENGTH);
698
699     /* Make sure AD-DOMANS sent from login that is sent to us is striped */
700     ctemp = strchr(uname, '@');
701     if (ctemp) *ctemp = 0;
702
703     /* is the name all lowercase? */
704     for ( ctemp = uname; *ctemp ; ctemp++) {
705         if ( !islower(*ctemp) ) {
706             lowercased_name = FALSE;
707             break;
708         }
709     }
710
711     (void) RegOpenKeyEx(HKEY_LOCAL_MACHINE, AFSREG_CLT_SVC_PARAM_SUBKEY,
712                          0, KEY_QUERY_VALUE, &NPKey);
713     LSPsize=sizeof(TraceOption);
714     RegQueryValueEx(NPKey, REG_CLIENT_TRACE_OPTION_PARM, NULL,
715                      &LSPtype, (LPBYTE)&TraceOption, &LSPsize);
716
717     RegCloseKey (NPKey);
718
719     /*
720      * Get Logon options
721      */
722
723     GetDomainLogonOptions( lpLogonId, uname, logonDomain, &opt );
724     retryInterval = opt.retryInterval;
725     sleepInterval = opt.sleepInterval;
726     *lpLogonScript = opt.logonScript;
727
728     if (retryInterval < sleepInterval)
729         sleepInterval = retryInterval;
730
731     DebugEvent("Got logon script: %S",opt.logonScript);
732
733     afsWillAutoStart = AFSWillAutoStart();
734
735     DebugEvent("LogonOption[%x], Service AutoStart[%d]",
736                 opt.LogonOption,afsWillAutoStart);
737     
738     /* Check for zero length password if integrated logon*/
739     if ( ISLOGONINTEGRATED(opt.LogonOption) )  {
740         if ( password[0] == 0 ) {
741             DebugEvent("Password is the empty string");
742             code = GT_PW_NULL;
743             reason = "zero length password is illegal";
744             code=0;
745         }       
746
747         /* Get cell name if doing integrated logon.  
748            We might overwrite this if we are logging into an AD realm and we find out that
749            the user's home dir is in some other cell. */
750         DebugEvent("About to call cm_GetRootCellName(%s)",cell);
751         code = cm_GetRootCellName(cell);
752         if (code < 0) { 
753             DebugEvent("Unable to obtain Root Cell");
754             code = KTC_NOCELL;
755             reason = "unknown cell";
756             code=0;
757         } else {
758             DebugEvent("Cell is %s",cell);
759         }       
760
761         /* We get the user's home directory path, if applicable, though we can't lookup the
762            cell right away because the client service may not have started yet. This call
763            also sets the AD_REALM flag in opt.flags if applicable. */
764         if (ISREMOTE(opt.flags)) {
765             DebugEvent("Is Remote");
766             GetAdHomePath(homePath,MAX_PATH,lpLogonId,&opt);
767         }       
768     }
769
770     /* loop until AFS is started. */
771     while (TRUE) {
772         DebugEvent("while(TRUE) LogonOption[%x], Service AutoStart[%d]",
773                     opt.LogonOption,afsWillAutoStart);
774
775         if (ISADREALM(opt.flags)) {
776             code = GetFileCellName(homePath,cell,256);
777             if (!code) {
778                 DebugEvent("profile path [%s] is in cell [%s]",homePath,cell);
779             }
780             /* Don't bail out if GetFileCellName failed.
781              * The home dir may not be in AFS after all. 
782              */
783         } else
784             code=0;
785                 
786         /* if Integrated Logon  */
787         if (ISLOGONINTEGRATED(opt.LogonOption))
788         {                       
789             if ( KFW_is_available() ) {
790                 code = KFW_AFS_get_cred(uname, cell, password, 0, opt.smbName, &reason);
791                 DebugEvent("KFW_AFS_get_cred  uname=[%s] smbname=[%s] cell=[%s] code=[%d]",uname,opt.smbName,cell,code);
792                 if (code == 0 && opt.theseCells) { 
793                     char * principal, *p;
794
795                     principal = (char *)malloc(strlen(uname) + strlen(cell) + 2);
796                     if ( principal ) {
797                         strcpy(principal, uname);
798                         p = principal + strlen(uname);
799                         *p++ = '@';
800                         strcpy(p, cell);
801                         for ( ;*p; p++) {
802                             *p = toupper(*p);
803                         }
804
805                         p = opt.theseCells;
806                         while ( *p ) {
807                             code = KFW_AFS_get_cred(principal, p, 0, 0, opt.smbName, &reason);
808                             DebugEvent("KFW_AFS_get_cred  uname=[%s] smbname=[%s] cell=[%s] code=[%d]",
809                                         principal,opt.smbName,p,code);
810                             p += strlen(p) + 1;
811                         }
812                         
813                         free(principal);
814                     }
815                 }
816             } else {
817                 code = ka_UserAuthenticateGeneral2(KA_USERAUTH_VERSION+KA_USERAUTH_AUTHENT_LOGON,
818                                                     uname, "", cell, password, opt.smbName, 0, &pw_exp, 0,
819                                                     &reason);
820                 DebugEvent("AFS AfsLogon - (INTEGRATED only)ka_UserAuthenticateGeneral2","Code[%x] uname[%s] Cell[%s]",
821                             code,uname,cell);
822             }       
823             if ( code && code != KTC_NOCM && code != KTC_NOCMRPC && !lowercased_name ) {
824                 for ( ctemp = uname; *ctemp ; ctemp++) {
825                     *ctemp = tolower(*ctemp);
826                 }
827                 lowercased_name = TRUE;
828                 goto sleeping;
829             }
830
831             /* is service started yet?*/
832
833             /* If we've failed because the client isn't running yet and the
834             * client is set to autostart (and therefore it makes sense for
835             * us to wait for it to start) then sleep a while and try again. 
836             * If the error was something else, then give up. */
837             if (code != KTC_NOCM && code != KTC_NOCMRPC || !afsWillAutoStart)
838                 break;
839         }
840         else {  
841             /*JUST check to see if its running*/
842             if (IsServiceRunning())
843                 break;
844             if (afsWillAutoStart && !IsServiceStartPending()) {
845                 code = KTC_NOCMRPC;
846                 reason = "AFS Service start failed";
847                 break;
848             }
849         }
850
851         /* If the retry interval has expired and we still aren't
852          * logged in, then just give up if we are not in interactive
853          * mode or the failSilently flag is set, otherwise let the
854          * user know we failed and give them a chance to try again. */
855         if (retryInterval <= 0) {
856             reason = "AFS not running";
857             if (!interactive || opt.failSilently)
858                 break;
859             flag = MessageBox(hwndOwner,
860                                "AFS is still starting.  Retry?",
861                                "AFS Logon",
862                                MB_ICONQUESTION | MB_RETRYCANCEL);
863             if (flag == IDCANCEL)
864                 break;
865
866             /* Wait just a little while and try again */
867             retryInterval = opt.retryInterval;
868         }
869
870       sleeping:
871         Sleep(sleepInterval * 1000);
872         retryInterval -= sleepInterval;
873     }
874
875     DebugEvent("while loop exited");
876     /* remove any kerberos 5 tickets currently held by the SYSTEM account */
877     if ( KFW_is_available() )
878         KFW_AFS_destroy_tickets_for_cell(cell);
879
880     if (code) {
881         char msg[128];
882         HANDLE h;
883         char *ptbuf[1];
884
885         StringCbPrintf(msg, sizeof(msg), "Integrated login failed: %s", reason);
886
887         if (ISLOGONINTEGRATED(opt.LogonOption) && interactive && !opt.failSilently)
888             MessageBox(hwndOwner, msg, "AFS Logon", MB_OK);
889
890         h = RegisterEventSource(NULL, AFS_LOGON_EVENT_NAME);
891         ptbuf[0] = msg;
892         ReportEvent(h, EVENTLOG_WARNING_TYPE, 0, 1008, NULL,
893                      1, 0, ptbuf, NULL);
894         DeregisterEventSource(h);
895             
896         code = MapAuthError(code);
897         SetLastError(code);
898
899         if (ISLOGONINTEGRATED(opt.LogonOption) && (code!=0))
900         {
901             if (*lpLogonScript)
902                 LocalFree(*lpLogonScript);
903             *lpLogonScript = NULL;
904             if (!afsWillAutoStart)      // its not running, so if not autostart or integrated logon then just skip
905                 code = 0;
906         }
907     }
908
909     if (opt.theseCells) free(opt.theseCells);
910     if (opt.smbName) free(opt.smbName);
911
912     DebugEvent("AFS AfsLogon - Exit","Return Code[%x]",code);
913     return code;
914 }       
915
916 DWORD APIENTRY NPPasswordChangeNotify(
917         LPCWSTR lpAuthentInfoType,
918         LPVOID lpAuthentInfo,
919         LPCWSTR lpPreviousAuthentInfoType,
920         LPVOID lpPreviousAuthentInfo,
921         LPWSTR lpStationName,
922         LPVOID StationHandle,
923         DWORD dwChangeInfo)
924 {
925     /* Make sure the AFS Libraries are initialized */
926     AfsLogonInit();
927
928     DebugEvent0("AFS AfsLogon - NPPasswordChangeNotify");
929     return 0;
930 }
931
932 #include <userenv.h>
933 #include <Winwlx.h>
934 #include <afs/vice.h>
935 #include <afs/fs_utils.h>
936
937 BOOL IsPathInAfs(const CHAR *strPath)
938 {
939     char space[2048];
940     struct ViceIoctl blob;
941     int code;
942
943     blob.in_size = 0;
944     blob.out_size = 2048;
945     blob.out = space;
946
947     code = pioctl((LPTSTR)((LPCTSTR)strPath), VIOC_FILE_CELL_NAME, &blob, 1);
948     if (code)
949         return FALSE;
950     return TRUE;
951 }
952
953 #ifdef COMMENT
954 typedef struct _WLX_NOTIFICATION_INFO {  
955     ULONG Size;  
956     ULONG Flags;  
957     PWSTR UserName;  
958     PWSTR Domain;  
959     PWSTR WindowStation;  
960     HANDLE hToken;  
961     HDESK hDesktop;  
962     PFNMSGECALLBACK pStatusCallback;
963 } WLX_NOTIFICATION_INFO, *PWLX_NOTIFICATION_INFO;
964 #endif
965
966 VOID AFS_Startup_Event( PWLX_NOTIFICATION_INFO pInfo )
967 {
968     DWORD LSPtype, LSPsize;
969     HKEY NPKey;
970
971     /* Make sure the AFS Libraries are initialized */
972     AfsLogonInit();
973
974     (void) RegOpenKeyEx(HKEY_LOCAL_MACHINE, AFSREG_CLT_SVC_PARAM_SUBKEY,
975                         0, KEY_QUERY_VALUE, &NPKey);
976     LSPsize=sizeof(TraceOption);
977     RegQueryValueEx(NPKey, REG_CLIENT_TRACE_OPTION_PARM, NULL,
978                      &LSPtype, (LPBYTE)&TraceOption, &LSPsize);
979
980     RegCloseKey (NPKey);
981     DebugEvent0("AFS_Startup_Event");
982 }
983
984 VOID AFS_Logoff_Event( PWLX_NOTIFICATION_INFO pInfo )
985 {
986     DWORD code;
987     TCHAR profileDir[1024] = TEXT("");
988     DWORD  len = 1024;
989     PTOKEN_USER  tokenUser = NULL;
990     DWORD  retLen;
991     HANDLE hToken;
992
993     /* Make sure the AFS Libraries are initialized */
994     AfsLogonInit();
995
996     DebugEvent0("AFS_Logoff_Event - Starting");
997
998     if (!GetTokenInformation(pInfo->hToken, TokenUser, NULL, 0, &retLen))
999     {
1000         if ( GetLastError() == ERROR_INSUFFICIENT_BUFFER ) {
1001             tokenUser = (PTOKEN_USER) LocalAlloc(LPTR, retLen);
1002
1003             if (!GetTokenInformation(pInfo->hToken, TokenUser, tokenUser, retLen, &retLen))
1004             {
1005                 DebugEvent("GetTokenInformation failed: GLE = %lX", GetLastError());
1006             }
1007         }
1008     }
1009
1010     /* We can't use pInfo->Domain for the domain since in the cross realm case 
1011      * this is source domain and not the destination domain.
1012      */
1013     if (QueryAdHomePathFromSid( profileDir, sizeof(profileDir), tokenUser->User.Sid, pInfo->Domain)) {
1014         WCHAR Domain[64]=L"";
1015         GetLocalShortDomain(Domain, sizeof(Domain));
1016         if (QueryAdHomePathFromSid( profileDir, sizeof(profileDir), tokenUser->User.Sid, Domain)) {
1017             if (NetUserGetProfilePath(pInfo->Domain, pInfo->UserName, profileDir, len))
1018                 GetUserProfileDirectory(pInfo->hToken, profileDir, &len);
1019         }
1020     }
1021     
1022     if (strlen(profileDir)) {
1023         DebugEvent("Profile Directory: %s", profileDir);
1024         if (!IsPathInAfs(profileDir)) {
1025             if (code = ktc_ForgetAllTokens())
1026                 DebugEvent("AFS_Logoff_Event - ForgetAllTokens failed [%lX]",code);
1027             else
1028                 DebugEvent0("AFS_Logoff_Event - ForgetAllTokens succeeded");
1029         } else {
1030             DebugEvent0("AFS_Logoff_Event - Tokens left in place; profile in AFS");
1031         }
1032     } else {
1033         DebugEvent0("AFS_Logoff_Event - Unable to load profile");
1034     }
1035
1036     if ( tokenUser )
1037         LocalFree(tokenUser);
1038 }   
1039