1d24875bd3ab50d49a1469d00754e64f21359782
[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 <afs/param.h>
11 #include <afs/stds.h>
12
13 #include <windows.h>
14 #include <npapi.h>
15 #include <winsock2.h>
16 #include "afsd.h"
17 #include <afs/pioctl_nt.h>
18 #include <afs/kautils.h>
19 #include "cm_config.h"
20 #include "krb.h"
21
22 #include <io.h>
23 #include <sys/stat.h>
24 #include <sys/types.h>
25 #include <fcntl.h>
26
27 DWORD LogonOption,TraceOption;
28
29 HANDLE hDLL;
30
31 WSADATA WSAjunk;
32
33 #define REG_CLIENT_PARMS_KEY            "SYSTEM\\CurrentControlSet\\Services\\TransarcAFSDaemon\\Parameters"
34 #define REG_CLIENT_PROVIDER_KEY                 "SYSTEM\\CurrentControlSet\\Services\\TransarcAFSDaemon\\NetworkProvider"
35 #define REG_CLIENT_RETRY_INTERVAL_PARM  "LoginRetryInterval"
36 #define REG_CLIENT_FAIL_SILENTLY_PARM   "FailLoginsSilently"
37 #define DEFAULT_RETRY_INTERVAL          30                        /* seconds*/
38 #define DEFAULT_FAIL_SILENTLY           FALSE
39 #define DEFAULT_SLEEP_INTERVAL          5                         /* seconds*/
40
41 #define ISLOGONINTEGRATED(v) ( ((v) & LOGON_OPTION_INTEGRATED)==LOGON_OPTION_INTEGRATED)
42 #define ISHIGHSECURITY(v) ( ((v) & LOGON_OPTION_HIGHSECURITY)==LOGON_OPTION_HIGHSECURITY)
43
44 #define TRACE_OPTION_EVENT 1
45 #define ISLOGONTRACE(v) ( ((v) & TRACE_OPTION_EVENT)==TRACE_OPTION_EVENT)
46
47 #ifdef COMMENT
48 /* Structure def copied from DDK (NTDEF.H) */
49 typedef struct UNICODE_STRING {
50         USHORT Length;          /* number of bytes of Buffer actually used */
51         USHORT MaximumLength;   /* sizeof buffer in bytes */
52         WCHAR *Buffer;          /* 16 bit characters */
53 } UNICODE_STRING;
54
55 /* Structure def copied from NP API documentation */
56 typedef struct _MSV1_0_INTERACTIVE_LOGON {
57         DWORD           MessageType;    /* Actually this is an enum; ignored */
58         UNICODE_STRING  LogonDomainName;
59         UNICODE_STRING  UserName;
60         UNICODE_STRING  Password;
61 } MSV1_0_INTERACTIVE_LOGON;
62 #endif
63
64 /*
65  * GetLogonScript
66  *
67  * We get a logon script pathname from the HKEY_LOCAL_MACHINE registry.
68  * I don't know what good this does; I just copied it from DFS.
69  *
70  * Returns NULL on failure.
71  */
72
73
74 void DebugEvent0(char *a) 
75 {
76         HANDLE h; char *ptbuf[1];
77         if (!ISLOGONTRACE(TraceOption))
78                 return;
79         h = RegisterEventSource(NULL, AFS_DAEMON_EVENT_NAME);
80         ptbuf[0] = a;
81         ReportEvent(h, EVENTLOG_INFORMATION_TYPE, 0, 0, NULL, 1, 0, (const char **)ptbuf, NULL);
82         DeregisterEventSource(h);
83 }
84
85 #define MAXBUF_ 131
86 void DebugEvent(char *a,char *b,...) 
87 {
88         HANDLE h; char *ptbuf[1],buf[MAXBUF_+1];
89         va_list marker;
90         if (!ISLOGONTRACE(TraceOption))
91                 return;
92         h = RegisterEventSource(NULL, AFS_DAEMON_EVENT_NAME);
93         va_start(marker,b);
94         _vsnprintf(buf,MAXBUF_,b,marker);
95     buf[MAXBUF_] = '\0';
96         ptbuf[0] = buf;
97         ReportEvent(h, EVENTLOG_INFORMATION_TYPE, 0, 0, NULL, 1, 0, (const char **)ptbuf, NULL);\
98         DeregisterEventSource(h);
99         va_end(marker);
100 }
101
102 CHAR *GenRandomName(CHAR *pbuf)
103 {
104         int i;
105         srand( (unsigned)time( NULL ) );
106         for (i=0;i<MAXRANDOMNAMELEN-1;i++)
107                 pbuf[i]='a'+(rand() % 26);
108         pbuf[MAXRANDOMNAMELEN-1]=0;
109         return pbuf;
110 }
111
112 WCHAR *GetLogonScript(CHAR *pname)
113 {
114         WCHAR *script,*buf;
115         DWORD code;
116         DWORD LSPtype, LSPsize;
117         HKEY NPKey;
118         WCHAR randomName[MAXRANDOMNAMELEN];
119
120         /*
121          * Get Network Provider key.
122          * Assume this works or we wouldn't be here.
123          */
124         (void) RegOpenKeyEx(HKEY_LOCAL_MACHINE, REG_CLIENT_PROVIDER_KEY,
125                             0, KEY_QUERY_VALUE, &NPKey);
126
127         /*
128          * Get Logon Script pathname length
129          */
130
131         code = RegQueryValueExW(NPKey, L"LogonScript", NULL,
132                                 &LSPtype, NULL, &LSPsize);
133
134         if (code) {
135                 RegCloseKey (NPKey);
136                 return NULL;
137         }
138
139         if (LSPtype != REG_SZ) {        /* Maybe handle REG_EXPAND_SZ? */
140                 RegCloseKey (NPKey);
141                 return NULL;
142         }
143
144         buf=(WCHAR *)LocalAlloc(LMEM_FIXED,LSPsize);
145         script=(WCHAR *)LocalAlloc(LMEM_FIXED,LSPsize+(MAXRANDOMNAMELEN)*sizeof(WCHAR));
146         /*
147          * Explicitly call UNICODE version
148          * Assume it will succeed since it did before
149          */
150         (void) RegQueryValueExW(NPKey, L"LogonScript", NULL,
151                                 &LSPtype, (LPBYTE)buf, &LSPsize);
152         MultiByteToWideChar(CP_ACP,0,pname,strlen(pname)+1,randomName,(strlen(pname)+1)*sizeof(WCHAR));
153         swprintf(script,buf,randomName);
154         LocalFree(buf);
155
156 #ifdef DEBUG_VERBOSE
157     {
158         HANDLE h; char *ptbuf[1],buf[132],tbuf[255];
159                 WideCharToMultiByte(CP_ACP,0,script,LSPsize,tbuf,255,NULL,NULL);
160         h = RegisterEventSource(NULL, AFS_DAEMON_EVENT_NAME);
161         sprintf(buf, "Script[%s,%d] Return Code[%x]",tbuf,LSPsize,code);
162         ptbuf[0] = buf;
163         ReportEvent(h, EVENTLOG_INFORMATION_TYPE, 0, 0, NULL, 1, 0, ptbuf, NULL);
164         DeregisterEventSource(h);
165     }
166 #endif
167
168         RegCloseKey (NPKey);
169         return script;
170 }
171
172 BOOLEAN AFSWillAutoStart(void)
173 {
174         SC_HANDLE scm;
175         SC_HANDLE svc;
176         BOOLEAN flag;
177         BOOLEAN result = FALSE;
178         LPQUERY_SERVICE_CONFIG pConfig = NULL;
179         DWORD BufSize;
180         LONG status;
181
182         /* Open services manager */
183         scm = OpenSCManager(NULL, NULL, SC_MANAGER_ALL_ACCESS);
184         if (!scm) return FALSE;
185
186         /* Open AFSD service */
187         svc = OpenService(scm, "TransarcAFSDaemon", SERVICE_QUERY_CONFIG);
188         if (!svc)
189                 goto close_scm;
190
191         /* Query AFSD service config, first just to get buffer size */
192         /* Expected to fail, so don't test return value */
193         (void) QueryServiceConfig(svc, NULL, 0, &BufSize);
194         status = GetLastError();
195         if (status != ERROR_INSUFFICIENT_BUFFER)
196                 goto close_svc;
197
198         /* Allocate buffer */
199         pConfig = (LPQUERY_SERVICE_CONFIG)GlobalAlloc(GMEM_FIXED,BufSize);
200         if (!pConfig)
201                 goto close_svc;
202
203         /* Query AFSD service config, this time for real */
204         flag = QueryServiceConfig(svc, pConfig, BufSize, &BufSize);
205         if (!flag)
206                 goto free_pConfig;
207
208         /* Is it autostart? */
209         if (pConfig->dwStartType < SERVICE_DEMAND_START)
210                 result = TRUE;
211
212 free_pConfig:
213         GlobalFree(pConfig);
214 close_svc:
215         CloseServiceHandle(svc);
216 close_scm:
217         CloseServiceHandle(scm);
218
219         return result;
220 }
221
222 DWORD MapAuthError(DWORD code)
223 {
224         switch (code) {
225         case KTC_NOCM:
226         case KTC_NOCMRPC:
227                 return WN_NO_NETWORK;
228 /*      case INTK_BADPW: return WN_BAD_PASSWORD;*/
229 /*      case KERB_ERR_PRINCIPAL_UNKNOWN: return WN_BAD_USER;*/
230         default: return WN_SUCCESS;
231         }
232 }
233
234 BOOLEAN APIENTRY DllEntryPoint(HANDLE dll, DWORD reason, PVOID reserved)
235 {
236         hDLL = dll;
237         switch (reason) {
238                 case DLL_PROCESS_ATTACH:
239                         /* Initialize AFS libraries */
240                         rx_Init(0);
241             initAFSDirPath();
242                         ka_Init(0);
243                         break;
244
245                 /* Everything else succeeds but does nothing. */
246                 case DLL_PROCESS_DETACH:
247                 case DLL_THREAD_ATTACH:
248                 case DLL_THREAD_DETACH:
249                 default:
250                         break;
251         }
252
253         return TRUE;
254 }
255
256 DWORD APIENTRY NPGetCaps(DWORD index)
257 {
258         switch (index) {
259                 case WNNC_NET_TYPE:
260                         /* Don't have our own type; use somebody else's. */
261                         return WNNC_NET_SUN_PC_NFS;
262                 default:
263                         return 0;
264         }
265 }
266
267 static void GetLoginBehavior(int *pRetryInterval, BOOLEAN *pFailSilently)
268 {
269     long result;
270     HKEY hKey;
271     DWORD dummyLen;
272                 
273         result = RegOpenKeyEx(HKEY_LOCAL_MACHINE, REG_CLIENT_PARMS_KEY, 0, KEY_QUERY_VALUE, &hKey);
274     if (result != ERROR_SUCCESS) {
275         *pRetryInterval = DEFAULT_RETRY_INTERVAL;
276         *pFailSilently = DEFAULT_FAIL_SILENTLY;
277         return;
278     }
279         
280     result = RegQueryValueEx(hKey, REG_CLIENT_RETRY_INTERVAL_PARM, 0, 0, (BYTE *)pRetryInterval, &dummyLen);
281     if (result != ERROR_SUCCESS)
282         *pRetryInterval = DEFAULT_RETRY_INTERVAL;
283                 
284     result = RegQueryValueEx(hKey, REG_CLIENT_FAIL_SILENTLY_PARM, 0, 0, (BYTE *)pFailSilently, &dummyLen);
285     if (result != ERROR_SUCCESS)
286         *pFailSilently = DEFAULT_FAIL_SILENTLY;
287
288     /* Make sure this is really a bool value in the strict sense*/
289     *pFailSilently = !!*pFailSilently;
290
291     RegCloseKey(hKey);
292 }   
293
294 BOOL IsServiceRunning (void)
295 {
296     SERVICE_STATUS Status;
297     SC_HANDLE hManager;
298     memset (&Status, 0x00, sizeof(Status));
299     Status.dwCurrentState = SERVICE_STOPPED;
300
301     if ((hManager = OpenSCManager (NULL, NULL, GENERIC_READ)) != NULL)
302     {
303         SC_HANDLE hService;
304         if ((hService = OpenService (hManager, TEXT("TransarcAFSDaemon"), GENERIC_READ)) != NULL)
305         {
306             QueryServiceStatus (hService, &Status);
307             CloseServiceHandle (hService);
308         }
309
310         CloseServiceHandle (hManager);
311     }
312     DebugEvent("AFS AfsLogon - Test Service Running","Return Code[%x] ?Running[%d]",Status.dwCurrentState,(Status.dwCurrentState == SERVICE_RUNNING));
313     return (Status.dwCurrentState == SERVICE_RUNNING);
314 }   
315
316
317 static BOOL
318 WINAPI
319 UnicodeStringToANSI(UNICODE_STRING uInputString, LPSTR lpszOutputString, int nOutStringLen)
320 {
321     CPINFO CodePageInfo;
322
323     GetCPInfo(CP_ACP, &CodePageInfo);
324
325     if (CodePageInfo.MaxCharSize > 1)
326         // Only supporting non-Unicode strings
327         return FALSE;
328     
329     if (((LPBYTE) uInputString.Buffer)[1] == '\0')
330     {
331         // Looks like unicode, better translate it
332         // UNICODE_STRING specifies the length of the buffer string in Bytes not WCHARS
333         WideCharToMultiByte(CP_ACP, 0, (LPCWSTR) uInputString.Buffer, uInputString.Length/2,
334                             lpszOutputString, nOutStringLen-1, NULL, NULL);
335         lpszOutputString[max(uInputString.Length/2,nOutStringLen-1)] = '\0';
336         return TRUE;
337     }
338     return FALSE;
339 }  // UnicodeStringToANSI
340
341 DWORD APIENTRY NPLogonNotify(
342         PLUID lpLogonId,
343         LPCWSTR lpAuthentInfoType,
344         LPVOID lpAuthentInfo,
345         LPCWSTR lpPreviousAuthentInfoType,
346         LPVOID lpPreviousAuthentInfo,
347         LPWSTR lpStationName,
348         LPVOID StationHandle,
349         LPWSTR *lpLogonScript)
350 {
351         char uname[256]="";
352         char *ctemp;
353         char password[256]="";
354         char cell[256]="<non-integrated logon>";
355         MSV1_0_INTERACTIVE_LOGON *IL;
356         DWORD code;
357         int pw_exp;
358         char *reason;
359         BOOLEAN interactive;
360         BOOLEAN flag;
361         DWORD LSPtype, LSPsize;
362         HKEY NPKey;
363         HWND hwndOwner = (HWND)StationHandle;
364     BOOLEAN failSilently;
365     int retryInterval;
366     int sleepInterval = DEFAULT_SLEEP_INTERVAL;        /* seconds        */
367     BOOLEAN afsWillAutoStart;
368         CHAR RandomName[MAXRANDOMNAMELEN];
369     BOOLEAN uppercased_name = TRUE;
370
371     /* Initialize Logon Script to none */
372         *lpLogonScript=NULL;
373         
374         IL = (MSV1_0_INTERACTIVE_LOGON *) lpAuthentInfo;
375
376         /* Are we interactive? */
377         interactive = (wcscmp(lpStationName, L"WinSta0") == 0);
378
379         /* Convert from Unicode to ANSI */
380         UnicodeStringToANSI(IL->UserName, uname, 256);
381         UnicodeStringToANSI(IL->Password, password, 256);
382
383         /* Make sure AD-DOMANS sent from login that is sent to us is striped */
384     ctemp = strchr(uname, '@');
385     if (ctemp) *ctemp = 0;
386
387     /* is the name all uppercase? */
388     for ( ctemp = uname; *ctemp ; ctemp++) {
389         if ( islower(*ctemp) ) {
390             uppercased_name = FALSE;
391             break;
392         }
393     }
394
395         (void) RegOpenKeyEx(HKEY_LOCAL_MACHINE, REG_CLIENT_PARMS_KEY,
396                         0, KEY_QUERY_VALUE, &NPKey);
397         LSPsize=sizeof(TraceOption);
398         RegQueryValueEx(NPKey, "TraceOption", NULL,
399                      &LSPtype, (LPBYTE)&TraceOption, &LSPsize);
400     RegCloseKey (NPKey);
401         
402         /*
403          * Get Logon OPTIONS
404          */
405
406         (void) RegOpenKeyEx(HKEY_LOCAL_MACHINE, REG_CLIENT_PROVIDER_KEY,
407                          0, KEY_QUERY_VALUE, &NPKey);
408
409         LSPsize=sizeof(LogonOption);
410         code = RegQueryValueEx(NPKey, "LogonOptions", NULL,
411                             &LSPtype, (LPBYTE)&LogonOption, &LSPsize);
412
413         RegCloseKey (NPKey);
414         if ((code!=0) || (LSPtype!=REG_DWORD))
415                 LogonOption=LOGON_OPTION_INTEGRATED;    /*default to integrated logon only*/
416
417         afsWillAutoStart = AFSWillAutoStart();
418         
419         DebugEvent("AFS AfsLogon - NPLogonNotify","LogonOption[%x], Service AutoStart[%d]",
420                 LogonOption,afsWillAutoStart);
421     
422     /* Get local machine specified login behavior (or defaults) */
423     GetLoginBehavior(&retryInterval, &failSilently);
424         
425     /* Check for zero length password if integrated logon*/
426         if ( ISLOGONINTEGRATED(LogonOption) )  {
427         if ( password[0] == 0 ) {
428             code = GT_PW_NULL;
429             reason = "zero length password is illegal";
430             code=0;
431         }
432
433         /* Get cell name if doing integrated logon */
434                 code = cm_GetRootCellName(cell);
435                 if (code < 0) { 
436                         code = KTC_NOCELL;
437                         reason = "unknown cell";
438                         code=0;
439                 }
440
441         /*only do if high security option is on*/
442         if (ISHIGHSECURITY(LogonOption))
443             *lpLogonScript = GetLogonScript(GenRandomName(RandomName)); 
444     }
445
446     /* loop until AFS is started. */
447     while (TRUE) {
448         code=0;
449                 
450         /* is service started yet?*/
451         DebugEvent("AFS AfsLogon - ka_UserAuthenticateGeneral2","Code[%x] uname[%s] Cell[%s]",
452                    code,uname,cell);
453
454         /* if Integrated Logon only */
455         if (ISLOGONINTEGRATED(LogonOption) && !ISHIGHSECURITY(LogonOption))
456                 {                       
457             if ( KFW_is_available() )
458                 code = KFW_AFS_get_cred(uname, cell, password, 0, uname, &reason);
459             else
460                 code = ka_UserAuthenticateGeneral2(KA_USERAUTH_VERSION+KA_USERAUTH_AUTHENT_LOGON,
461                                                 uname, "", cell, password, uname, 0, &pw_exp, 0,
462                                                 &reason);
463                         DebugEvent("AFS AfsLogon - (INTEGRATED only)ka_UserAuthenticateGeneral2","Code[%x]",
464                         code);
465             if ( code && code != KTC_NOCM && code != KTC_NOCMRPC && uppercased_name ) {
466                 for ( ctemp = uname; *ctemp ; ctemp++) {
467                     *ctemp = tolower(*ctemp);
468                 }
469                 uppercased_name = FALSE;
470                 continue;
471             }
472                 } 
473         /* if Integrated Logon and High Security pass random generated name*/
474         else if (ISLOGONINTEGRATED(LogonOption) && ISHIGHSECURITY(LogonOption))
475                 {
476             if ( KFW_is_available() )
477                 code = KFW_AFS_get_cred(uname, cell, password, 0, RandomName, &reason);
478             else
479                 code = ka_UserAuthenticateGeneral2(KA_USERAUTH_VERSION+KA_USERAUTH_AUTHENT_LOGON,
480                                                 uname, "", cell, password,RandomName, 0, &pw_exp, 0,
481                                                 &reason);
482                         DebugEvent("AFS AfsLogon - (Both)ka_UserAuthenticateGeneral2","Code[%x] RandomName[%s]",
483                        code, RandomName);
484
485             if ( code && code != KTC_NOCM && code != KTC_NOCMRPC && uppercased_name ) {
486                 for ( ctemp = uname; *ctemp ; ctemp++) {
487                     *ctemp = tolower(*ctemp);
488                 }
489                 uppercased_name = FALSE;
490                 continue;
491             }
492                 } else {  
493             /*JUST check to see if its running*/
494                     if (IsServiceRunning())
495                 break;
496                     code = KTC_NOCM;
497                     if (!afsWillAutoStart)
498                 break;
499                 }
500
501                 /* If we've failed because the client isn't running yet and the
502          * client is set to autostart (and therefore it makes sense for
503          * us to wait for it to start) then sleep a while and try again. 
504          * If the error was something else, then give up. */
505                 if (code != KTC_NOCM && code != KTC_NOCMRPC || !afsWillAutoStart)
506                         break;
507                 
508         /* If the retry interval has expired and we still aren't
509          * logged in, then just give up if we are not in interactive
510          * mode or the failSilently flag is set, otherwise let the
511          * user know we failed and give them a chance to try again. */
512         if (retryInterval <= 0) {
513             reason = "AFS not running";
514             if (!interactive || failSilently)
515                 break;
516                         flag = MessageBox(hwndOwner,
517                                "AFS is still starting.  Retry?",
518                                "AFS Logon",
519                                MB_ICONQUESTION | MB_RETRYCANCEL);
520                         if (flag == IDCANCEL)
521                 break;
522
523             /* Wait just a little while and try again */
524             retryInterval = sleepInterval = DEFAULT_SLEEP_INTERVAL;
525         }
526
527         if (retryInterval < sleepInterval)
528                         sleepInterval = retryInterval;
529
530                 Sleep(sleepInterval * 1000);
531
532         retryInterval -= sleepInterval;
533     }
534
535     /* remove any kerberos 5 tickets currently held by the SYSTEM account */
536     if ( KFW_is_available() )
537         KFW_AFS_destroy_tickets_for_cell(cell);
538
539         if (code) {
540         char msg[128];
541         sprintf(msg, "Integrated login failed: %s", reason);
542
543                 if (interactive && !failSilently)
544                         MessageBox(hwndOwner, msg, "AFS Logon", MB_OK);
545                 else {
546             HANDLE h;
547             char *ptbuf[1];
548
549             h = RegisterEventSource(NULL, AFS_DAEMON_EVENT_NAME);
550             ptbuf[0] = msg;
551             ReportEvent(h, EVENTLOG_WARNING_TYPE, 0, 1008, NULL,
552                          1, 0, ptbuf, NULL);
553             DeregisterEventSource(h);
554         }
555             code = MapAuthError(code);
556                 SetLastError(code);
557
558                 if (ISLOGONINTEGRATED(LogonOption) && (code!=0))
559                 {
560                         if (*lpLogonScript)
561                                 LocalFree(*lpLogonScript);
562                         *lpLogonScript = NULL;
563                         if (!afsWillAutoStart)  // its not running, so if not autostart or integrated logon then just skip
564                                 code = 0;
565
566                 }
567         }
568
569         DebugEvent("AFS AfsLogon - Exit","Return Code[%x]",code);
570         return code;
571 }
572
573 DWORD APIENTRY NPPasswordChangeNotify(
574         LPCWSTR lpAuthentInfoType,
575         LPVOID lpAuthentInfo,
576         LPCWSTR lpPreviousAuthentInfoType,
577         LPVOID lpPreviousAuthentInfo,
578         LPWSTR lpStationName,
579         LPVOID StationHandle,
580         DWORD dwChangeInfo)
581 {
582         DebugEvent0("AFS AfsLogon - NPPasswordChangeNotify");
583         return 0;
584 }
585