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