34833aa800a53f51a7068c63286426764ecc9a41
[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 #include <nb30.h>
20
21 #include <afs/param.h>
22 #include <afs/stds.h>
23 #include <afs/pioctl_nt.h>
24 #include <afs/kautils.h>
25
26 #include "afsd.h"
27 #include "cm_config.h"
28 #include "krb.h"
29 #include "afskfw.h"
30 #include "lanahelper.h"
31
32 #include <WINNT\afsreg.h>
33
34 DWORD TraceOption = 0;
35
36 HANDLE hDLL;
37
38 WSADATA WSAjunk;
39 #define AFS_LOGON_EVENT_NAME TEXT("AFS Logon")
40
41 void DebugEvent0(char *a) 
42 {
43     HANDLE h; char *ptbuf[1];
44     if (!ISLOGONTRACE(TraceOption))
45         return;
46     h = RegisterEventSource(NULL, AFS_LOGON_EVENT_NAME);
47     ptbuf[0] = a;
48     ReportEvent(h, EVENTLOG_INFORMATION_TYPE, 0, 0, NULL, 1, 0, (const char **)ptbuf, NULL);
49     DeregisterEventSource(h);
50 }
51
52 #define MAXBUF_ 512
53 void DebugEvent(char *b,...) 
54 {
55     HANDLE h; char *ptbuf[1],buf[MAXBUF_+1];
56     va_list marker;
57
58     if (!ISLOGONTRACE(TraceOption))
59         return;
60
61     h = RegisterEventSource(NULL, AFS_LOGON_EVENT_NAME);
62     va_start(marker,b);
63     StringCbVPrintf(buf, MAXBUF_+1,b,marker);
64     buf[MAXBUF_] = '\0';
65     ptbuf[0] = buf;
66     ReportEvent(h, EVENTLOG_INFORMATION_TYPE, 0, 0, NULL, 1, 0, (const char **)ptbuf, NULL);
67     DeregisterEventSource(h);
68     va_end(marker);
69 }
70
71 static HANDLE hInitMutex = NULL;
72 static BOOL bInit = FALSE;
73
74 BOOLEAN APIENTRY DllEntryPoint(HANDLE dll, DWORD reason, PVOID reserved)
75 {
76     hDLL = dll;
77     switch (reason) {
78     case DLL_PROCESS_ATTACH:
79         /* Initialization Mutex */
80         hInitMutex = CreateMutex(NULL, FALSE, NULL);
81         break;
82
83     case DLL_PROCESS_DETACH:
84         CloseHandle(hInitMutex);
85         break;
86
87     case DLL_THREAD_ATTACH:
88     case DLL_THREAD_DETACH:
89     default:
90         /* Everything else succeeds but does nothing. */
91         break;
92     }
93
94     return TRUE;
95 }
96
97 void AfsLogonInit(void)
98 {
99     if ( bInit == FALSE ) {
100         if ( WaitForSingleObject( hInitMutex, INFINITE ) == WAIT_OBJECT_0 ) {
101             if ( bInit == FALSE ) {
102                 rx_Init(0);
103                 initAFSDirPath();
104                 ka_Init(0);
105                 bInit = TRUE;
106             }
107             ReleaseMutex(hInitMutex);
108         }
109     }
110 }
111
112 CHAR *GenRandomName(CHAR *pbuf)
113 {
114     int i;
115     srand( (unsigned)time( NULL ) );
116     for (i=0;i<MAXRANDOMNAMELEN-1;i++)
117         pbuf[i]='a'+(rand() % 26);
118     pbuf[MAXRANDOMNAMELEN-1]=0;
119     return pbuf;
120 }
121
122 BOOLEAN AFSWillAutoStart(void)
123 {
124     SC_HANDLE scm;
125     SC_HANDLE svc;
126     BOOLEAN flag;
127     BOOLEAN result = FALSE;
128     LPQUERY_SERVICE_CONFIG pConfig = NULL;
129     DWORD BufSize;
130     LONG status;
131
132     /* Open services manager */
133     scm = OpenSCManager(NULL, NULL, GENERIC_READ);
134     if (!scm) return FALSE;
135
136     /* Open AFSD service */
137     svc = OpenService(scm, "TransarcAFSDaemon", SERVICE_QUERY_CONFIG);
138     if (!svc)
139         goto close_scm;
140
141     /* Query AFSD service config, first just to get buffer size */
142     /* Expected to fail, so don't test return value */
143     (void) QueryServiceConfig(svc, NULL, 0, &BufSize);
144     status = GetLastError();
145     if (status != ERROR_INSUFFICIENT_BUFFER)
146         goto close_svc;
147
148     /* Allocate buffer */
149     pConfig = (LPQUERY_SERVICE_CONFIG)GlobalAlloc(GMEM_FIXED,BufSize);
150     if (!pConfig)
151         goto close_svc;
152
153     /* Query AFSD service config, this time for real */
154     flag = QueryServiceConfig(svc, pConfig, BufSize, &BufSize);
155     if (!flag)
156         goto free_pConfig;
157
158     /* Is it autostart? */
159     if (pConfig->dwStartType < SERVICE_DEMAND_START)
160         result = TRUE;
161
162   free_pConfig:
163     GlobalFree(pConfig);
164   close_svc:
165     CloseServiceHandle(svc);
166   close_scm:
167     CloseServiceHandle(scm);
168
169     return result;
170 }
171
172 DWORD MapAuthError(DWORD code)
173 {
174     switch (code) {
175         /* Unfortunately, returning WN_NO_NETWORK results in the MPR abandoning
176          * logon scripts for all credential managers, although they will still
177          * receive logon notifications.  Since we don't want this, we return
178          * WN_SUCCESS.  This is highly undesirable, but we also don't want to
179          * break other network providers.
180          */
181  /* case KTC_NOCM:
182     case KTC_NOCMRPC:
183     return WN_NO_NETWORK; */
184     default: return WN_SUCCESS;
185   }
186 }
187
188 DWORD APIENTRY NPGetCaps(DWORD index)
189 {
190     switch (index) {
191     case WNNC_NET_TYPE:
192         /* Don't have our own type; use somebody else's. */
193         return WNNC_NET_SUN_PC_NFS;
194
195     case WNNC_START:
196         /* Say we are already started, even though we might wait after we receive NPLogonNotify */
197         return 1;
198
199     default:
200         return 0;
201     }
202 }       
203
204 NET_API_STATUS 
205 NetUserGetProfilePath( LPCWSTR Domain, LPCWSTR UserName, char * profilePath, 
206                        DWORD profilePathLen )
207 {
208     NET_API_STATUS code;
209     LPWSTR ServerName = NULL;
210     LPUSER_INFO_3 p3 = NULL;
211
212     NetGetAnyDCName(NULL, Domain, (LPBYTE *)&ServerName);
213     /* if NetGetAnyDCName fails, ServerName == NULL
214      * NetUserGetInfo will obtain local user information 
215      */
216     code = NetUserGetInfo(ServerName, UserName, 3, (LPBYTE *)&p3);
217     if (code == NERR_Success)
218     {
219         code = NERR_UserNotFound;
220         if (p3) {
221             if (p3->usri3_profile) {
222                 DWORD len = lstrlenW(p3->usri3_profile);
223                 if (len > 0) {
224                     /* Convert From Unicode to ANSI (UTF-8 for future) */
225                     len = len < profilePathLen ? len : profilePathLen - 1;
226                     WideCharToMultiByte(CP_UTF8, 0, p3->usri3_profile, len, profilePath, len, NULL, NULL);
227                     profilePath[len] = '\0';
228                     code = NERR_Success;
229                 }
230             }
231             NetApiBufferFree(p3);
232         }
233     }
234     if (ServerName) 
235         NetApiBufferFree(ServerName);
236     return code;
237 }
238
239 BOOL IsServiceRunning (void)
240 {
241     SERVICE_STATUS Status;
242     SC_HANDLE hManager;
243     memset (&Status, 0x00, sizeof(Status));
244     Status.dwCurrentState = SERVICE_STOPPED;
245
246     if ((hManager = OpenSCManager (NULL, NULL, GENERIC_READ)) != NULL)
247     {
248         SC_HANDLE hService;
249         if ((hService = OpenService (hManager, TEXT("TransarcAFSDaemon"), GENERIC_READ)) != NULL)
250         {
251             QueryServiceStatus (hService, &Status);
252             CloseServiceHandle (hService);
253         }
254
255         CloseServiceHandle (hManager);
256     }
257     DebugEvent("AFS AfsLogon - Test Service Running","Return Code[%x] ?Running[%d]",Status.dwCurrentState,(Status.dwCurrentState == SERVICE_RUNNING));
258     return (Status.dwCurrentState == SERVICE_RUNNING);
259 }   
260
261 BOOL IsServiceStartPending (void)
262 {
263     SERVICE_STATUS Status;
264     SC_HANDLE hManager;
265     memset (&Status, 0x00, sizeof(Status));
266     Status.dwCurrentState = SERVICE_STOPPED;
267
268     if ((hManager = OpenSCManager (NULL, NULL, GENERIC_READ)) != NULL)
269     {
270         SC_HANDLE hService;
271         if ((hService = OpenService (hManager, TEXT("TransarcAFSDaemon"), GENERIC_READ)) != NULL)
272         {
273             QueryServiceStatus (hService, &Status);
274             CloseServiceHandle (hService);
275         }
276
277         CloseServiceHandle (hManager);
278     }
279     DebugEvent("AFS AfsLogon - Test Service Start Pending","Return Code[%x] ?Start Pending[%d]",Status.dwCurrentState,(Status.dwCurrentState == SERVICE_START_PENDING));
280     return (Status.dwCurrentState == SERVICE_RUNNING);
281 }   
282
283 /* LOOKUPKEYCHAIN: macro to look up the value in the list of keys in order until it's found
284    v:variable to receive value (reference type)
285    t:type
286    d:default, in case the value isn't on any of the keys
287    n:name of value */
288 #define LOOKUPKEYCHAIN(v,t,d,n) \
289         do { \
290                 rv = ~ERROR_SUCCESS; \
291                 dwType = t; \
292                 if(hkDom) { \
293                         dwSize = sizeof(v); \
294                         rv = RegQueryValueEx(hkDom, n, 0, &dwType, (LPBYTE) &(v), &dwSize); \
295                         if(rv == ERROR_SUCCESS) DebugEvent(#v " found in hkDom with type [%d]", dwType); \
296                 } \
297                 if(hkDoms && (rv != ERROR_SUCCESS || dwType != t)) { \
298                         dwSize = sizeof(v); \
299                         rv = RegQueryValueEx(hkDoms, n, 0, &dwType, (LPBYTE) &(v), &dwSize); \
300                         if(rv == ERROR_SUCCESS) DebugEvent(#v " found in hkDoms with type [%d]", dwType); \
301                 } \
302                 if(hkNp && (rv != ERROR_SUCCESS || dwType != t)) { \
303                         dwSize = sizeof(v); \
304                         rv = RegQueryValueEx(hkNp, n, 0, &dwType, (LPBYTE) &(v), &dwSize); \
305                         if(rv == ERROR_SUCCESS) DebugEvent(#v " found in hkNp with type [%d]", dwType); \
306                 } \
307                 if(rv != ERROR_SUCCESS || dwType != t) { \
308                         v = d; \
309                         DebugEvent(#v " being set to default"); \
310                 } \
311         } while(0)
312
313 /* Get domain specific configuration info.  We are returning void because if anything goes wrong
314    we just return defaults.
315  */
316 void 
317 GetDomainLogonOptions( PLUID lpLogonId, char * username, char * domain, LogonOptions_t *opt ) {
318     HKEY hkParm = NULL; /* Service parameter */
319     HKEY hkNp = NULL;   /* network provider key */
320     HKEY hkDoms = NULL; /* domains key */
321     HKEY hkDom = NULL;  /* DOMAINS/domain key */
322     HKEY hkTemp = NULL;
323     LONG rv;
324     DWORD dwSize;
325     DWORD dwType;
326     DWORD dwDummy;
327     char computerName[MAX_COMPUTERNAME_LENGTH + 1];
328     char *effDomain;
329
330     memset(opt, 0, sizeof(LogonOptions_t));
331
332     DebugEvent("In GetDomainLogonOptions for user [%s] in domain [%s]", username, domain);
333     /* If the domain is the same as the Netbios computer name, we use the LOCALHOST domain name*/
334     opt->flags = LOGON_FLAG_REMOTE;
335     if(domain) {
336         dwSize = MAX_COMPUTERNAME_LENGTH;
337         if(GetComputerName(computerName, &dwSize)) {
338             if(!stricmp(computerName, domain)) {
339                 effDomain = "LOCALHOST";
340                 opt->flags = LOGON_FLAG_LOCAL;
341             }
342             else
343                 effDomain = domain;
344         }
345     } else
346         effDomain = NULL;
347
348     rv = RegOpenKeyEx( HKEY_LOCAL_MACHINE, AFSREG_CLT_SVC_PARAM_SUBKEY, 0, KEY_READ, &hkParm );
349     if(rv != ERROR_SUCCESS) {
350         hkParm = NULL;
351         DebugEvent("GetDomainLogonOption: Can't open parms key [%d]", rv);
352     }
353
354     rv = RegOpenKeyEx( HKEY_LOCAL_MACHINE, AFSREG_CLT_SVC_PROVIDER_SUBKEY, 0, KEY_READ, &hkNp );
355     if(rv != ERROR_SUCCESS) {
356         hkNp = NULL;
357         DebugEvent("GetDomainLogonOptions: Can't open NP key [%d]", rv);
358     }
359
360     if(hkNp) {
361         rv = RegOpenKeyEx( hkNp, REG_CLIENT_DOMAINS_SUBKEY, 0, KEY_READ, &hkDoms );
362         if( rv != ERROR_SUCCESS ) {
363             hkDoms = NULL;
364             DebugEvent("GetDomainLogonOptions: Can't open Domains key [%d]", rv);
365         }
366     }
367
368     if(hkDoms && effDomain) {
369         rv = RegOpenKeyEx( hkDoms, effDomain, 0, KEY_READ, &hkDom );
370         if( rv != ERROR_SUCCESS ) {
371             hkDom = NULL;
372             DebugEvent("GetDomainLogonOptions: Can't open domain key for [%s] [%d]", effDomain, rv);
373             /* If none of the domains match, we shouldn't use the domain key either */
374             RegCloseKey(hkDoms);
375             hkDoms = NULL;
376         }
377     } else
378         DebugEvent("Not opening domain key for [%s]", effDomain);
379
380     /* Each individual can either be specified on the domain key, the domains key or in the
381        net provider key.  They fail over in that order.  If none is found, we just use the 
382        defaults. */
383
384     /* LogonOption */
385     LOOKUPKEYCHAIN(opt->LogonOption, REG_DWORD, DEFAULT_LOGON_OPTION, REG_CLIENT_LOGON_OPTION_PARM);
386
387     /* FailLoginsSilently */
388     dwSize = sizeof(dwDummy);
389     rv = RegQueryValueEx(hkParm, REG_CLIENT_FAIL_SILENTLY_PARM, 0, &dwType, (LPBYTE) &dwDummy, &dwSize);
390     if (rv != ERROR_SUCCESS)
391         LOOKUPKEYCHAIN(dwDummy, REG_DWORD, DEFAULT_FAIL_SILENTLY, REG_CLIENT_FAIL_SILENTLY_PARM);
392     opt->failSilently = !!dwDummy;
393
394     /* Retry interval */
395     LOOKUPKEYCHAIN(opt->retryInterval, REG_DWORD, DEFAULT_RETRY_INTERVAL, REG_CLIENT_RETRY_INTERVAL_PARM);
396
397     /* Sleep interval */
398     LOOKUPKEYCHAIN(opt->sleepInterval, REG_DWORD, DEFAULT_SLEEP_INTERVAL, REG_CLIENT_SLEEP_INTERVAL_PARM);
399
400     opt->logonScript = NULL;
401     opt->smbName = NULL;
402
403     if(!ISLOGONINTEGRATED(opt->LogonOption)) {
404         goto cleanup; /* no need to lookup the logon script */
405     }
406
407     /* come up with SMB username */
408     if(ISHIGHSECURITY(opt->LogonOption)) {
409         opt->smbName = malloc( MAXRANDOMNAMELEN );
410         GenRandomName(opt->smbName);
411     } else {
412         /* username and domain for logon session is not necessarily the same as
413            username and domain passed into network provider. */
414         PSECURITY_LOGON_SESSION_DATA plsd;
415         char lsaUsername[MAX_USERNAME_LENGTH];
416         char lsaDomain[MAX_DOMAIN_LENGTH];
417         size_t len, tlen;
418
419         LsaGetLogonSessionData(lpLogonId, &plsd);
420         
421         UnicodeStringToANSI(plsd->UserName, lsaUsername, MAX_USERNAME_LENGTH);
422         UnicodeStringToANSI(plsd->LogonDomain, lsaDomain, MAX_DOMAIN_LENGTH);
423
424         DebugEvent("PLSD username[%s] domain[%s]",lsaUsername,lsaDomain);
425
426         if(SUCCEEDED(StringCbLength(lsaUsername, MAX_USERNAME_LENGTH, &tlen)))
427             len = tlen;
428         else
429             goto bad_strings;
430
431         if(SUCCEEDED(StringCbLength(lsaDomain, MAX_DOMAIN_LENGTH, &tlen)))
432             len += tlen;
433         else
434             goto bad_strings;
435
436         len += 2;
437
438         opt->smbName = malloc(len);
439
440         StringCbCopy(opt->smbName, len, lsaDomain);
441         StringCbCat(opt->smbName, len, "\\");
442         StringCbCat(opt->smbName, len, lsaUsername);
443
444         strlwr(opt->smbName);
445
446       bad_strings:
447         LsaFreeReturnBuffer(plsd);
448     }
449
450     DebugEvent("Looking up logon script");
451     /* Logon script */
452     /* First find out where the key is */
453     hkTemp = NULL;
454     rv = ~ERROR_SUCCESS;
455     dwType = 0;
456     if(hkDom)
457         rv = RegQueryValueExW(hkDom, REG_CLIENT_LOGON_SCRIPT_PARMW, 0, &dwType, NULL, &dwSize);
458     if(rv == ERROR_SUCCESS && (dwType == REG_SZ || dwType == REG_EXPAND_SZ)) {
459         hkTemp = hkDom;
460         DebugEvent("Located logon script in hkDom");
461     }
462     else if(hkDoms)
463         rv = RegQueryValueExW(hkDoms, REG_CLIENT_LOGON_SCRIPT_PARMW, 0, &dwType, NULL, &dwSize);
464     if(rv == ERROR_SUCCESS && !hkTemp && (dwType == REG_SZ || dwType == REG_EXPAND_SZ)) {
465         hkTemp = hkDoms;
466         DebugEvent("Located logon script in hkDoms");
467     }
468     /* Note that the LogonScript in the NP key is only used if we are doing high security. */
469     else if(hkNp && ISHIGHSECURITY(opt->LogonOption))
470         rv = RegQueryValueExW(hkNp, REG_CLIENT_LOGON_SCRIPT_PARMW, 0, &dwType, NULL, &dwSize);
471     if(rv == ERROR_SUCCESS && !hkTemp && (dwType == REG_SZ || dwType == REG_EXPAND_SZ)) {
472         hkTemp = hkNp;
473         DebugEvent("Located logon script in hkNp");
474     }
475
476     if(hkTemp) {
477         WCHAR *regscript        = NULL;
478         WCHAR *regexscript      = NULL;
479         WCHAR *regexuscript     = NULL;
480         WCHAR *wuname           = NULL;
481         HRESULT hr;
482
483         size_t len;
484
485         StringCbLength(opt->smbName, MAX_USERNAME_LENGTH, &len);
486         len ++;
487
488         wuname = malloc(len * sizeof(WCHAR));
489         MultiByteToWideChar(CP_ACP,0,opt->smbName,-1,wuname,len*sizeof(WCHAR));
490
491         DebugEvent("Username is set for [%S]", wuname);
492
493         /* dwSize still has the size of the required buffer in bytes. */
494         regscript = malloc(dwSize);
495         rv = RegQueryValueExW(hkTemp, REG_CLIENT_LOGON_SCRIPT_PARMW, 0, &dwType, (LPBYTE) regscript, &dwSize);
496         if(rv != ERROR_SUCCESS) {/* what the ..? */
497             DebugEvent("Can't look up logon script [%d]",rv);
498             goto doneLogonScript;
499         }
500
501         DebugEvent("Found logon script [%S]", regscript);
502
503         if(dwType == REG_EXPAND_SZ) {
504             DWORD dwReq;
505
506             dwSize += MAX_PATH * sizeof(WCHAR);  /* make room for environment expansion. */
507             regexscript = malloc(dwSize);
508             dwReq = ExpandEnvironmentStringsW(regscript, regexscript, dwSize / sizeof(WCHAR));
509             free(regscript);
510             regscript = regexscript;
511             regexscript = NULL;
512             if(dwReq > (dwSize / sizeof(WCHAR))) {
513                 DebugEvent("Overflow while expanding environment strings.");
514                 goto doneLogonScript;
515             }
516         }
517
518         DebugEvent("After expanding env strings [%S]", regscript);
519
520         if(wcsstr(regscript, L"%s")) {
521             dwSize += len * sizeof(WCHAR); /* make room for username expansion */
522             regexuscript = (WCHAR *) LocalAlloc(LMEM_FIXED, dwSize);
523             hr = StringCbPrintfW(regexuscript, dwSize, regscript, wuname);
524         } else {
525             regexuscript = (WCHAR *) LocalAlloc(LMEM_FIXED, dwSize);
526             hr = StringCbCopyW(regexuscript, dwSize, regscript);
527         }
528
529         DebugEvent("After expanding username [%S]", regexuscript);
530
531         if(hr == S_OK)
532             opt->logonScript = regexuscript;
533         else
534             LocalFree(regexuscript);
535
536       doneLogonScript:
537         if(wuname) free(wuname);
538         if(regscript) free(regscript);
539         if(regexscript) free(regexscript);
540     }
541
542     DebugEvent("Looking up TheseCells");
543     /* Logon script */
544     /* First find out where the key is */
545     hkTemp = NULL;
546     rv = ~ERROR_SUCCESS;
547     dwType = 0;
548     if (hkDom)
549         rv = RegQueryValueEx(hkDom, REG_CLIENT_THESE_CELLS_PARM, 0, &dwType, NULL, &dwSize);
550     if (rv == ERROR_SUCCESS && dwType == REG_MULTI_SZ) {
551         hkTemp = hkDom;
552         DebugEvent("Located TheseCells in hkDom");
553     } else if (hkDoms)
554         rv = RegQueryValueEx(hkDoms, REG_CLIENT_THESE_CELLS_PARM, 0, &dwType, NULL, &dwSize);
555     if (rv == ERROR_SUCCESS && !hkTemp && dwType == REG_MULTI_SZ) {
556         hkTemp = hkDoms;
557         DebugEvent("Located TheseCells in hkDoms");
558     } else if (hkNp)
559         rv = RegQueryValueEx(hkNp, REG_CLIENT_THESE_CELLS_PARM, 0, &dwType, NULL, &dwSize);
560     if (rv == ERROR_SUCCESS && !hkTemp && dwType == REG_MULTI_SZ) {
561         hkTemp = hkNp;
562         DebugEvent("Located TheseCells in hkNp");
563     }
564
565     if (hkTemp) {
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
992     /* Make sure the AFS Libraries are initialized */
993     AfsLogonInit();
994
995     DebugEvent0("AFS_Logoff_Event - Start");
996
997     if (!GetTokenInformation(pInfo->hToken, TokenUser, NULL, 0, &retLen))
998     {
999         if ( GetLastError() == ERROR_INSUFFICIENT_BUFFER ) {
1000             tokenUser = (PTOKEN_USER) LocalAlloc(LPTR, retLen);
1001
1002             if (!GetTokenInformation(pInfo->hToken, TokenUser, tokenUser, retLen, &retLen))
1003             {
1004                 DebugEvent("AFS_Logoff_Event - GetTokenInformation failed: GLE = %lX", GetLastError());
1005             }
1006         }
1007     }
1008
1009     /* We can't use pInfo->Domain for the domain since in the cross realm case 
1010      * this is source domain and not the destination domain.
1011      */
1012     if (QueryAdHomePathFromSid( profileDir, sizeof(profileDir), tokenUser->User.Sid, pInfo->Domain)) {
1013         WCHAR Domain[64]=L"";
1014         GetLocalShortDomain(Domain, sizeof(Domain));
1015         if (QueryAdHomePathFromSid( profileDir, sizeof(profileDir), tokenUser->User.Sid, Domain)) {
1016             if (NetUserGetProfilePath(pInfo->Domain, pInfo->UserName, profileDir, len))
1017                 GetUserProfileDirectory(pInfo->hToken, profileDir, &len);
1018         }
1019     }
1020     
1021     if (strlen(profileDir)) {
1022         DebugEvent("AFS_Logoff_Event - Profile Directory: %s", profileDir);
1023         if (!IsPathInAfs(profileDir)) {
1024             if (code = ktc_ForgetAllTokens())
1025                 DebugEvent("AFS_Logoff_Event - ForgetAllTokens failed [%lX]",code);
1026             else
1027                 DebugEvent0("AFS_Logoff_Event - ForgetAllTokens succeeded");
1028         } else {
1029             DebugEvent0("AFS_Logoff_Event - Tokens left in place; profile in AFS");
1030         }
1031     } else {
1032         DebugEvent0("AFS_Logoff_Event - Unable to load profile");
1033     }
1034
1035     if ( tokenUser )
1036         LocalFree(tokenUser);
1037
1038     DebugEvent0("AFS_Logoff_Event - End");
1039 }   
1040
1041 VOID AFS_Logon_Event( PWLX_NOTIFICATION_INFO pInfo )
1042 {
1043     DWORD code;
1044     TCHAR profileDir[1024] = TEXT("");
1045     DWORD  len = 1024;
1046     PTOKEN_USER  tokenUser = NULL;
1047     DWORD  retLen;
1048     HANDLE hToken;
1049
1050     WCHAR szUserW[128] = L"";
1051     char  szUserA[128] = "";
1052     char  szClient[MAX_PATH];
1053     char szPath[MAX_PATH] = "";
1054     NETRESOURCE nr;
1055     DWORD res;
1056     DWORD gle;
1057     DWORD dwSize;
1058
1059     /* Make sure the AFS Libraries are initialized */
1060     AfsLogonInit();
1061
1062     DebugEvent0("AFS_Logon_Event - Start");
1063
1064     if (!GetTokenInformation(pInfo->hToken, TokenUser, NULL, 0, &retLen))
1065     {
1066         if ( GetLastError() == ERROR_INSUFFICIENT_BUFFER ) {
1067             tokenUser = (PTOKEN_USER) LocalAlloc(LPTR, retLen);
1068
1069             if (!GetTokenInformation(pInfo->hToken, TokenUser, tokenUser, retLen, &retLen))
1070             {
1071                 DebugEvent("AFS_Logon_Event - GetTokenInformation failed: GLE = %lX", GetLastError());
1072             }
1073         }
1074     }
1075
1076     /* We can't use pInfo->Domain for the domain since in the cross realm case 
1077      * this is source domain and not the destination domain.
1078      */
1079     if (QueryAdHomePathFromSid( profileDir, sizeof(profileDir), tokenUser->User.Sid, pInfo->Domain)) {
1080         WCHAR Domain[64]=L"";
1081         GetLocalShortDomain(Domain, sizeof(Domain));
1082         if (QueryAdHomePathFromSid( profileDir, sizeof(profileDir), tokenUser->User.Sid, Domain)) {
1083             if (NetUserGetProfilePath(pInfo->Domain, pInfo->UserName, profileDir, len))
1084                 GetUserProfileDirectory(pInfo->hToken, profileDir, &len);
1085         }
1086     }
1087     
1088     if (strlen(profileDir)) {
1089         DebugEvent("AFS_Logon_Event - Profile Directory: %s", profileDir);
1090     } else {
1091         DebugEvent0("AFS_Logon_Event - Unable to load profile");
1092     }
1093
1094     dwSize = sizeof(szUserA);
1095     if (!KFW_AFS_get_lsa_principal(szUserA, &dwSize)) {
1096         StringCbPrintfW(szUserW, sizeof(szUserW), L"%s\\%s", pInfo->Domain, pInfo->UserName);
1097         WideCharToMultiByte(CP_ACP, 0, szUserW, -1, szUserA, MAX_PATH, NULL, NULL);
1098     }
1099
1100     if (szUserA[0])
1101     {
1102         lana_GetNetbiosName(szClient, LANA_NETBIOS_NAME_FULL);
1103         StringCbPrintf(szPath, sizeof(szPath), "\\\\%s", szClient);
1104
1105         DebugEvent("AFS_Logon_Event - Logon Name: %s", szUserA);
1106
1107         memset (&nr, 0x00, sizeof(NETRESOURCE));
1108         nr.dwType=RESOURCETYPE_DISK;
1109         nr.lpLocalName=0;
1110         nr.lpRemoteName=szPath;
1111         res = WNetAddConnection2(&nr,NULL,szUserA,0);
1112         if (res)
1113             DebugEvent("AFS_Logon_Event - WNetAddConnection2(%s,%s) failed: 0x%X",
1114                         szPath, szUserA,res);
1115         else
1116             DebugEvent0("AFS_Logon_Event - WNetAddConnection2() succeeded");
1117     } else 
1118         DebugEvent("AFS_Logon_Event - User name conversion failed: GLE = 0x%X",GetLastError());
1119
1120     if ( tokenUser )
1121         LocalFree(tokenUser);
1122
1123     DebugEvent0("AFS_Logon_Event - End");
1124 }
1125