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