a8e8af5849ab9f46e379a675091998fcc0d8d6f9
[openafs.git] / src / WINNT / afsd / afsd_service.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 <softpub.h>
15 #include <psapi.h>
16 #include <winerror.h>
17 #include <string.h>
18 #include <setjmp.h>
19 #include "afsd.h"
20 #include "afsd_init.h"
21 #include <stdio.h>
22 #include <stdlib.h>
23 #include <winsock2.h>
24
25 #include <osi.h>
26
27 #ifdef DEBUG
28 //#define NOTSERVICE
29 #endif
30 #ifdef _DEBUG
31 #include <crtdbg.h>
32 #endif
33
34 /*
35 // The following is defined if you want to receive Power notifications,
36 // including Hibernation, and also subsequent flushing of AFS volumes
37 //
38 #define REGISTER_POWER_NOTIFICATIONS 1
39 #define FLUSH_VOLUME                 1
40 //
41 // Check
42 */
43 #include "afsd_flushvol.h"
44
45 extern void afsi_log(char *pattern, ...);
46
47 static SERVICE_STATUS           ServiceStatus;
48 static SERVICE_STATUS_HANDLE    StatusHandle;
49
50 HANDLE hAFSDMainThread = NULL;
51
52 HANDLE WaitToTerminate;
53
54 int GlobalStatus;
55
56 #ifdef JUMP
57 unsigned int MainThreadId;
58 jmp_buf notifier_jmp;
59 #endif /* JUMP */
60
61 extern int traceOnPanic;
62 extern HANDLE afsi_file;
63
64 int powerEventsRegistered = 0;
65
66 /*
67  * Notifier function for use by osi_panic
68  */
69 static void afsd_notifier(char *msgp, char *filep, long line)
70 {
71     char tbuffer[512];
72     char *ptbuf[1];
73     HANDLE h;
74
75     if (filep)
76         sprintf(tbuffer, "Error at file %s, line %d: %s",
77                  filep, line, msgp);
78     else
79         sprintf(tbuffer, "Error at unknown location: %s", msgp);
80
81     h = RegisterEventSource(NULL, AFS_DAEMON_EVENT_NAME);
82     ptbuf[0] = tbuffer;
83     ReportEvent(h, EVENTLOG_ERROR_TYPE, 0, line, NULL, 1, 0, ptbuf, NULL);
84     DeregisterEventSource(h);
85
86     GlobalStatus = line;
87
88     osi_LogEnable(afsd_logp);
89
90     afsd_ForceTrace(TRUE);
91     buf_ForceTrace(TRUE);
92
93     afsi_log("--- begin dump ---");
94     cm_DumpSCache(afsi_file, "a");
95 #ifdef keisa
96     cm_dnlcDump(afsi_file, "a");
97 #endif
98     cm_DumpBufHashTable(afsi_file, "a");
99     smb_DumpVCP(afsi_file, "a");                        
100     afsi_log("--- end   dump ---");
101     
102 #ifdef DEBUG
103     DebugBreak();       
104 #endif
105
106     SetEvent(WaitToTerminate);
107
108 #ifdef JUMP
109     if (GetCurrentThreadId() == MainThreadId)
110         longjmp(notifier_jmp, 1);
111 #endif /* JUMP */
112
113     ServiceStatus.dwCurrentState = SERVICE_STOPPED;
114     ServiceStatus.dwWin32ExitCode = NO_ERROR;
115     ServiceStatus.dwCheckPoint = 0;
116     ServiceStatus.dwWaitHint = 0;
117     ServiceStatus.dwControlsAccepted = 0;
118     SetServiceStatus(StatusHandle, &ServiceStatus);
119
120     exit(1);
121 }
122
123 /*
124  * For use miscellaneously in smb.c; need to do better
125  */
126 static int _stdcall DummyMessageBox(HWND h, LPCTSTR l1, LPCTSTR l2, UINT ui)
127 {
128     return 0;
129 }
130
131 DWORD
132 afsd_ServiceFlushVolume(DWORD dwlpEventData)
133 {
134     DWORD   dwRet = ERROR_NETWORK_BUSY; /* or NO_ERROR */
135
136     /*
137     **  If UI bit is not set, user interaction is not possible
138     **      BUT, since we are a NON-interactive service, and therefore
139     **  have NO user I/O, it doesn't much matter.
140     **  This benign code left here as example of how to find this out
141     */
142     BOOL bUI = (dwlpEventData & 1);
143
144     /* flush volume */
145     if ( PowerNotificationThreadNotify() )
146     {
147         dwRet = NO_ERROR;
148     }
149     else
150     {
151         /* flush was unsuccessful, or timeout - deny shutdown */
152         dwRet = ERROR_NETWORK_BUSY;
153     }
154
155     /*      to deny hibernate, simply return
156     //      any value besides NO_ERROR.
157     //      For example:
158     //      dwRet = ERROR_NETWORK_BUSY;
159     */
160
161     return dwRet;
162 }
163
164
165 /* service control handler used in nt4 only for backward compat. */
166 VOID WINAPI 
167 afsd_ServiceControlHandler(DWORD ctrlCode)
168 {
169     HKEY parmKey;
170     DWORD dummyLen, doTrace;
171     long code;
172
173     switch (ctrlCode) {
174     case SERVICE_CONTROL_STOP:
175         /* Shutdown RPC */
176         RpcMgmtStopServerListening(NULL);
177
178         /* Force trace if requested */
179         code = RegOpenKeyEx(HKEY_LOCAL_MACHINE,
180                              AFSConfigKeyName,
181                              0, KEY_QUERY_VALUE, &parmKey);
182         if (code != ERROR_SUCCESS)
183             goto doneTrace;
184
185         dummyLen = sizeof(doTrace);
186         code = RegQueryValueEx(parmKey, "TraceOnShutdown",
187                                 NULL, NULL,
188                                 (BYTE *) &doTrace, &dummyLen);
189         RegCloseKey (parmKey);
190         if (code != ERROR_SUCCESS)
191             doTrace = 0;
192         if (doTrace) {
193             afsd_ForceTrace(FALSE);
194             buf_ForceTrace(FALSE);
195         }
196
197       doneTrace:
198         ServiceStatus.dwCurrentState = SERVICE_STOP_PENDING;
199         ServiceStatus.dwWin32ExitCode = NO_ERROR;
200         ServiceStatus.dwCheckPoint = 1;
201         ServiceStatus.dwWaitHint = 10000;
202         ServiceStatus.dwControlsAccepted = SERVICE_ACCEPT_STOP;
203         SetServiceStatus(StatusHandle, &ServiceStatus);
204         SetEvent(WaitToTerminate);
205         break;
206     case SERVICE_CONTROL_INTERROGATE:
207         ServiceStatus.dwCurrentState = SERVICE_RUNNING;
208         ServiceStatus.dwWin32ExitCode = NO_ERROR;
209         ServiceStatus.dwCheckPoint = 0;
210         ServiceStatus.dwWaitHint = 0;
211         ServiceStatus.dwControlsAccepted = SERVICE_ACCEPT_STOP;
212         SetServiceStatus(StatusHandle, &ServiceStatus);
213         break;
214         /* XXX handle system shutdown */
215         /* XXX handle pause & continue */
216     }
217 }       
218
219
220 /*
221 **    Extended ServiceControlHandler that provides Event types
222 **    for monitoring Power events, for example.
223 */
224 DWORD WINAPI
225 afsd_ServiceControlHandlerEx(
226               DWORD  ctrlCode,
227               DWORD  dwEventType,
228               LPVOID lpEventData,
229               LPVOID lpContext
230               )
231 {
232     HKEY parmKey;
233     DWORD dummyLen, doTrace;
234     long code;
235     DWORD dwRet = ERROR_CALL_NOT_IMPLEMENTED;
236
237     switch (ctrlCode) 
238     {
239     case SERVICE_CONTROL_STOP:
240         /* Shutdown RPC */
241         RpcMgmtStopServerListening(NULL);
242
243         /* Force trace if requested */
244         code = RegOpenKeyEx(HKEY_LOCAL_MACHINE,
245                             AFSConfigKeyName,
246                             0, KEY_QUERY_VALUE, &parmKey);
247         if (code != ERROR_SUCCESS)
248             goto doneTrace;
249
250         dummyLen = sizeof(doTrace);
251         code = RegQueryValueEx(parmKey, "TraceOnShutdown",
252                                NULL, NULL,
253                                (BYTE *) &doTrace, &dummyLen);
254         RegCloseKey (parmKey);
255         if (code != ERROR_SUCCESS)
256             doTrace = 0;
257         if (doTrace) {
258             afsd_ForceTrace(FALSE);
259             buf_ForceTrace(FALSE);
260         }
261
262       doneTrace:
263         ServiceStatus.dwCurrentState = SERVICE_STOP_PENDING;
264         ServiceStatus.dwWin32ExitCode = NO_ERROR;
265         ServiceStatus.dwCheckPoint = 1;
266         ServiceStatus.dwWaitHint = 10000;
267         ServiceStatus.dwControlsAccepted = 0;
268         SetServiceStatus(StatusHandle, &ServiceStatus);
269         SetEvent(WaitToTerminate);
270         dwRet = NO_ERROR;
271         break;
272
273     case SERVICE_CONTROL_INTERROGATE:
274         ServiceStatus.dwCurrentState = SERVICE_RUNNING;
275         ServiceStatus.dwWin32ExitCode = NO_ERROR;
276         ServiceStatus.dwCheckPoint = 0;
277         ServiceStatus.dwWaitHint = 0;
278         ServiceStatus.dwControlsAccepted = SERVICE_ACCEPT_STOP | SERVICE_ACCEPT_POWEREVENT;
279         SetServiceStatus(StatusHandle, &ServiceStatus);
280         dwRet = NO_ERROR;
281         break;
282
283         /* XXX handle system shutdown */
284         /* XXX handle pause & continue */
285     case SERVICE_CONTROL_POWEREVENT:                                              
286         {                                                                                     
287             /*                                                                                
288             **  dwEventType of this notification == WPARAM of WM_POWERBROADCAST               
289             **  Return NO_ERROR == return TRUE for that message, i.e. accept request          
290             **  Return any error code to deny request,                                        
291             **  i.e. as if returning BROADCAST_QUERY_DENY                                     
292             */                                                                                
293             if (powerEventsRegistered) {
294                 switch((int) dwEventType)                                                         
295                 {                                                                               
296                 case PBT_APMQUERYSUSPEND:                                                         
297                 case PBT_APMQUERYSTANDBY:                                                         
298
299 #ifdef  FLUSH_VOLUME
300                     /* handle event */                                                            
301                     dwRet = afsd_ServiceFlushVolume((DWORD) lpEventData);                         
302 #else                                                                                       
303                     dwRet = NO_ERROR;                                                             
304 #endif                                                                                      
305                     break;                                                                        
306                                                                                                                           
307                     /* allow remaining case PBT_WhatEver */                                           
308                 case PBT_APMSUSPEND:                                                              
309                 case PBT_APMSTANDBY:                                                              
310                 case PBT_APMRESUMECRITICAL:                                                       
311                 case PBT_APMRESUMESUSPEND:                                                        
312                 case PBT_APMRESUMESTANDBY:                                                        
313                 case PBT_APMBATTERYLOW:                                                           
314                 case PBT_APMPOWERSTATUSCHANGE:                                                    
315                 case PBT_APMOEMEVENT:                                                             
316                 case PBT_APMRESUMEAUTOMATIC:                                                      
317                 default:                                                                          
318                     dwRet = NO_ERROR;                                                             
319                 }   
320             }
321         }
322     }           /* end switch(ctrlCode) */                                                        
323     return dwRet;   
324 }
325
326 /* There is similar code in client_config\drivemap.cpp GlobalMountDrive()
327  * 
328  * Mount a drive into AFS if there global mapping
329  */
330 /* DEE Could check first if we are run as SYSTEM */
331 #define MAX_RETRIES 30
332 static void MountGlobalDrives(void)
333 {
334     char szAfsPath[_MAX_PATH];
335     char szDriveToMapTo[5];
336     DWORD dwResult;
337     char szKeyName[256];
338     HKEY hKey;
339     DWORD dwIndex = 0, dwRetry = 0;
340     DWORD dwDriveSize;
341     DWORD dwSubMountSize;
342     char szSubMount[256];
343     DWORD dwType;
344
345     sprintf(szKeyName, "%s\\GlobalAutoMapper", AFSConfigKeyName);
346
347     dwResult = RegOpenKeyEx(HKEY_LOCAL_MACHINE, szKeyName, 0, KEY_QUERY_VALUE, &hKey);
348     if (dwResult != ERROR_SUCCESS)
349         return;
350
351     while (dwRetry < MAX_RETRIES) {
352         dwDriveSize = sizeof(szDriveToMapTo);
353         dwSubMountSize = sizeof(szSubMount);
354         dwResult = RegEnumValue(hKey, dwIndex++, szDriveToMapTo, &dwDriveSize, 0, &dwType, szSubMount, &dwSubMountSize);
355         if (dwResult != ERROR_MORE_DATA) {
356             if (dwResult != ERROR_SUCCESS) {
357                 if (dwResult != ERROR_NO_MORE_ITEMS)
358                     afsi_log("Failed to read GlobalAutoMapper values: %d\n", dwResult);
359                 break;
360             }
361         }
362
363         for ( ; dwRetry < MAX_RETRIES; dwRetry++)
364                 {
365                     NETRESOURCE nr;
366                     memset (&nr, 0x00, sizeof(NETRESOURCE));
367  
368                     sprintf(szAfsPath,"\\\\%s\\%s",cm_NetbiosName,szSubMount);
369                     
370                     nr.dwScope = RESOURCE_GLOBALNET;              /* ignored parameter */
371                     nr.dwType=RESOURCETYPE_DISK;
372                     nr.lpLocalName=szDriveToMapTo;
373                     nr.lpRemoteName=szAfsPath;
374                     nr.dwDisplayType = RESOURCEDISPLAYTYPE_SHARE; /* ignored parameter */
375                     nr.dwUsage = RESOURCEUSAGE_CONNECTABLE;       /* ignored parameter */
376
377                     dwResult = WNetAddConnection2(&nr,NULL,NULL,0);
378             afsi_log("GlobalAutoMap of %s to %s %s (%d)", szDriveToMapTo, szSubMount, 
379                      (dwResult == NO_ERROR) ? "succeeded" : "failed", dwResult);
380             if (dwResult == NO_ERROR) {
381                 break;
382             }
383             /* wait for smb server to come up */
384             Sleep((DWORD)1000 /* miliseconds */);               
385
386             /* Disconnect any previous mappings */
387             dwResult = WNetCancelConnection2(szDriveToMapTo, 0, TRUE);
388         }
389     }        
390
391     RegCloseKey(hKey);
392 }
393
394 static void DismountGlobalDrives()
395 {
396     char szAfsPath[_MAX_PATH];
397     char szDriveToMapTo[5];
398     DWORD dwResult;
399     char szKeyName[256];
400     HKEY hKey;
401     DWORD dwIndex = 0;
402     DWORD dwDriveSize;
403     DWORD dwSubMountSize;
404     char szSubMount[256];
405     DWORD dwType;
406
407     sprintf(szKeyName, "%s\\GlobalAutoMapper", AFSConfigKeyName);
408
409     dwResult = RegOpenKeyEx(HKEY_LOCAL_MACHINE, szKeyName, 0, KEY_QUERY_VALUE, &hKey);
410     if (dwResult != ERROR_SUCCESS)
411         return;
412
413     while (1) {
414         dwDriveSize = sizeof(szDriveToMapTo);
415         dwSubMountSize = sizeof(szSubMount);
416         dwResult = RegEnumValue(hKey, dwIndex++, szDriveToMapTo, &dwDriveSize, 0, &dwType, szSubMount, &dwSubMountSize);
417         if (dwResult != ERROR_MORE_DATA) {
418             if (dwResult != ERROR_SUCCESS) {
419                 if (dwResult != ERROR_NO_MORE_ITEMS)
420                     afsi_log("Failed to read GlobalAutoMapper values: %d\n", dwResult);
421                 break;
422             }
423         }
424
425         sprintf(szAfsPath,"\\\\%s\\%s",cm_NetbiosName,szSubMount);
426                     
427         dwResult = WNetCancelConnection2(szDriveToMapTo, 0, TRUE);
428         dwResult = WNetCancelConnection(szAfsPath, TRUE);
429         
430         afsi_log("Disconnect from GlobalAutoMap of %s to %s %s", szDriveToMapTo, szSubMount, dwResult ? "succeeded" : "failed");
431     }        
432
433     RegCloseKey(hKey);
434 }
435
436 DWORD
437 GetVersionInfo( CHAR * filename, CHAR * szOutput, DWORD dwOutput )
438 {
439     DWORD dwVersionHandle;
440     LPVOID pVersionInfo = 0;
441     DWORD retval = 0;
442     LPDWORD pLangInfo = 0;
443     LPTSTR szVersion = 0;
444     UINT len = 0;
445     TCHAR szVerQ[] = TEXT("\\StringFileInfo\\12345678\\FileVersion");
446     DWORD size = GetFileVersionInfoSize(filename, &dwVersionHandle);
447
448     if (!size) {
449         afsi_log("GetFileVersionInfoSize failed");
450         return GetLastError();
451     }
452
453     pVersionInfo = malloc(size);
454     if (!pVersionInfo) {
455         afsi_log("out of memory 1");
456         return ERROR_NOT_ENOUGH_MEMORY;
457     }
458
459     GetFileVersionInfo(filename, dwVersionHandle, size, pVersionInfo);
460     if (retval = GetLastError())
461     {
462         afsi_log("GetFileVersionInfo failed: %d", retval);
463         goto cleanup;
464     }
465
466     VerQueryValue(pVersionInfo, TEXT("\\VarFileInfo\\Translation"),
467                        (LPVOID*)&pLangInfo, &len);
468     if (retval = GetLastError())
469     {
470         afsi_log("VerQueryValue 1 failed: %d", retval);
471         goto cleanup;
472     }
473
474     wsprintf(szVerQ,
475              TEXT("\\StringFileInfo\\%04x%04x\\FileVersion"),
476              LOWORD(*pLangInfo), HIWORD(*pLangInfo));
477
478     VerQueryValue(pVersionInfo, szVerQ, (LPVOID*)&szVersion, &len);
479     if (retval = GetLastError())
480     {
481         /* try again with language 409 since the old binaries were tagged wrong */
482         wsprintf(szVerQ,
483                   TEXT("\\StringFileInfo\\0409%04x\\FileVersion"),
484                   HIWORD(*pLangInfo));
485
486         VerQueryValue(pVersionInfo, szVerQ, (LPVOID*)&szVersion, &len);
487         if (retval = GetLastError()) {
488             afsi_log("VerQueryValue 2 failed: [%s] %d", szVerQ, retval);
489             goto cleanup;
490         }
491     }
492     snprintf(szOutput, dwOutput, TEXT("%s"), szVersion);
493     szOutput[dwOutput - 1] = 0;
494
495  cleanup:
496     if (pVersionInfo)
497         free(pVersionInfo);
498
499     return retval;
500 }
501
502 static HINSTANCE hCrypt32;
503 static DWORD (WINAPI *pCertGetNameString)(PCCERT_CONTEXT pCertContext,  DWORD dwType,  DWORD dwFlags,
504                                           void* pvTypePara, LPTSTR pszNameString, DWORD cchNameString);
505 static BOOL (WINAPI *pCryptQueryObject)(DWORD dwObjectType, const void* pvObject, DWORD dwExpectedContentTypeFlags,
506                                         DWORD dwExpectedFormatTypeFlags, DWORD dwFlags,
507                                         DWORD* pdwMsgAndCertEncodingType, DWORD* pdwContentType,
508                                         DWORD* pdwFormatType, HCERTSTORE* phCertStore,
509                                         HCRYPTMSG* phMsg, const void** ppvContext);
510 static BOOL (WINAPI *pCryptMsgGetParam)(HCRYPTMSG hCryptMsg, DWORD dwParamType, DWORD dwIndex,
511                                         void* pvData, DWORD* pcbData);
512 static PCCERT_CONTEXT (WINAPI *pCertFindCertificateInStore)(HCERTSTORE hCertStore, DWORD dwCertEncodingType,
513                                                             DWORD dwFindFlags, DWORD dwFindType,
514                                                             const void* pvFindPara,
515                                                             PCCERT_CONTEXT pPrevCertContext);
516 static BOOL (WINAPI *pCertCloseStore)(HCERTSTORE hCertStore, DWORD dwFlags);
517 static BOOL (WINAPI *pCryptMsgClose)(HCRYPTMSG hCryptMsg);
518 static BOOL (WINAPI *pCertCompareCertificate)(DWORD dwCertEncodingType, PCERT_INFO pCertId1,
519                                               PCERT_INFO pCertId2);
520 static BOOL (WINAPI *pCertFreeCertificateContext)(PCCERT_CONTEXT pCertContext);
521
522 void LoadCrypt32(void)
523 {
524     hCrypt32 = LoadLibrary("crypt32");
525     if ( !hCrypt32 )
526         return;
527
528     (FARPROC) pCertGetNameString = GetProcAddress( hCrypt32, "CertGetNameString" );
529     (FARPROC) pCryptQueryObject = GetProcAddress( hCrypt32, "CryptQueryObject" );
530     (FARPROC) pCryptMsgGetParam = GetProcAddress( hCrypt32, "CryptMsgGetParam" );
531     (FARPROC) pCertFindCertificateInStore = GetProcAddress( hCrypt32, "CertFindCertificateInStore" );
532     (FARPROC) pCertCloseStore = GetProcAddress( hCrypt32, "CertCloseStore" );
533     (FARPROC) pCryptMsgClose = GetProcAddress( hCrypt32, "CryptMsgClose" );
534     (FARPROC) pCertCompareCertificate = GetProcAddress( hCrypt32, "CertCompareCertificate" );
535     (FARPROC) pCertFreeCertificateContext = GetProcAddress( hCrypt32, "CertFreeCertificateContext" );
536     
537     if ( !pCertGetNameString ||
538          !pCryptQueryObject ||
539          !pCryptMsgGetParam ||
540          !pCertFindCertificateInStore ||
541          !pCertCloseStore ||
542          !pCryptMsgClose ||
543          !pCertCompareCertificate ||
544          !pCertFreeCertificateContext)
545     {
546         FreeLibrary(hCrypt32);
547         hCrypt32 = NULL;
548     }
549 }
550
551 void UnloadCrypt32(void)
552 {
553     FreeLibrary(hCrypt32);
554 }
555
556 #define ENCODING (X509_ASN_ENCODING | PKCS_7_ASN_ENCODING)
557
558 PCCERT_CONTEXT GetCertCtx(CHAR * filename)
559 {
560     wchar_t wfilename[260];
561     BOOL fResult;
562     DWORD dwEncoding;
563     DWORD dwContentType;
564     DWORD dwFormatType;
565     DWORD dwSignerInfo;
566     HCERTSTORE hStore = NULL;
567     HCRYPTMSG hMsg = NULL;
568     PCMSG_SIGNER_INFO pSignerInfo = NULL;
569     PCCERT_CONTEXT pCertContext = NULL;
570     CERT_INFO CertInfo;
571
572     if ( hCrypt32 == NULL )
573         return NULL;
574
575     ZeroMemory(&CertInfo, sizeof(CertInfo));
576     mbstowcs(wfilename, filename, 260);
577
578     fResult = pCryptQueryObject(CERT_QUERY_OBJECT_FILE,
579                                 wfilename,
580                                 CERT_QUERY_CONTENT_FLAG_PKCS7_SIGNED_EMBED,
581                                 CERT_QUERY_FORMAT_FLAG_BINARY,
582                                 0,
583                                 &dwEncoding,
584                                 &dwContentType,
585                                 &dwFormatType,
586                                 &hStore,
587                                 &hMsg,
588                                 NULL);
589
590     if (!fResult) {
591         afsi_log("CryptQueryObject failed for [%s] with error 0x%x",
592                  filename,
593                  GetLastError());
594         goto __exit;
595     }
596
597     fResult = pCryptMsgGetParam(hMsg,
598                                 CMSG_SIGNER_INFO_PARAM,
599                                 0,
600                                 NULL,
601                                 &dwSignerInfo);
602
603     if (!fResult) {
604         afsi_log("CryptMsgGetParam failed for [%s] with error 0x%x",
605                  filename,
606                  GetLastError());
607         goto __exit;
608     }
609
610     pSignerInfo = (PCMSG_SIGNER_INFO)LocalAlloc(LPTR, dwSignerInfo);
611
612     fResult = pCryptMsgGetParam(hMsg,
613                                 CMSG_SIGNER_INFO_PARAM,
614                                 0,
615                                 (PVOID)pSignerInfo,
616                                 &dwSignerInfo);
617     
618     if (!fResult) {
619         afsi_log("CryptMsgGetParam failed for [%s] with error 0x%x",
620                  filename,
621                  GetLastError());
622         goto __exit;
623     }
624
625     CertInfo.Issuer = pSignerInfo->Issuer;
626     CertInfo.SerialNumber = pSignerInfo->SerialNumber;
627
628     pCertContext = pCertFindCertificateInStore(hStore,
629                                               ENCODING,
630                                               0,
631                                               CERT_FIND_SUBJECT_CERT,
632                                               (PVOID) &CertInfo,
633                                               NULL);
634
635     if (!pCertContext) {
636       afsi_log("CertFindCertificateInStore for file [%s] failed with 0x%x",
637                filename,
638                GetLastError());
639       goto __exit;
640     }
641
642   __exit:
643     if (pSignerInfo)
644       LocalFree(pSignerInfo);
645
646     /*    if (pCertContext)
647           CertFreeCertificateContext(pCertContext);*/
648
649     if (hStore)
650       pCertCloseStore(hStore,0);
651
652     if (hMsg)
653       pCryptMsgClose(hMsg);
654
655     return pCertContext;
656 }
657
658 BOOL VerifyTrust(CHAR * filename)
659 {
660     WIN_TRUST_ACTDATA_CONTEXT_WITH_SUBJECT fContextWSubject;
661     WIN_TRUST_SUBJECT_FILE fSubjectFile;
662     GUID trustAction = WIN_SPUB_ACTION_PUBLISHED_SOFTWARE;
663     GUID subject = WIN_TRUST_SUBJTYPE_PE_IMAGE;
664     wchar_t wfilename[260];
665     LONG ret;
666     BOOL success = FALSE;
667
668     LONG (WINAPI *pWinVerifyTrust)(HWND hWnd, GUID* pgActionID, WINTRUST_DATA* pWinTrustData) = NULL;
669     HINSTANCE hWinTrust;
670
671     if (filename == NULL ) 
672         return FALSE;
673
674     hWinTrust = LoadLibrary("wintrust");
675     if ( !hWinTrust )
676         return FALSE;
677
678     if (((FARPROC) pWinVerifyTrust =
679           GetProcAddress( hWinTrust, "WinVerifyTrust" )) == NULL )
680     {
681         FreeLibrary(hWinTrust);
682         return FALSE;
683     }
684
685     mbstowcs(wfilename, filename, 260);
686
687     fSubjectFile.hFile = CreateFile(filename, GENERIC_READ, FILE_SHARE_READ, NULL, OPEN_EXISTING,
688                                     0, NULL);
689     fSubjectFile.lpPath = wfilename;
690     fContextWSubject.hClientToken = OpenProcess(PROCESS_QUERY_INFORMATION | PROCESS_VM_READ,
691                                                 FALSE, GetCurrentProcessId());
692     fContextWSubject.SubjectType = &subject;
693     fContextWSubject.Subject = &fSubjectFile;
694
695     ret = pWinVerifyTrust(INVALID_HANDLE_VALUE, &trustAction, (WINTRUST_DATA *)&fContextWSubject);
696
697     if ( fSubjectFile.hFile != INVALID_HANDLE_VALUE )
698         CloseHandle( fSubjectFile.hFile );
699     if ( fContextWSubject.hClientToken != INVALID_HANDLE_VALUE )
700         CloseHandle( fContextWSubject.hClientToken );
701
702     if (ret == ERROR_SUCCESS) {
703         success = TRUE;
704     } else {
705         DWORD gle = GetLastError();
706         switch (gle) {
707         case TRUST_E_PROVIDER_UNKNOWN:
708             afsi_log("VerifyTrust failed: \"Generic Verify V2\" Provider Unknown");
709             break;  
710         case TRUST_E_NOSIGNATURE:
711             afsi_log("VerifyTrust failed: Unsigned executable");
712             break;
713         case TRUST_E_EXPLICIT_DISTRUST:
714             afsi_log("VerifyTrust failed: Certificate Marked as Untrusted by the user");
715             break;
716         case TRUST_E_SUBJECT_NOT_TRUSTED:
717             afsi_log("VerifyTrust failed: File is not trusted");
718             break;
719         case TRUST_E_BAD_DIGEST:
720             afsi_log("VerifyTrust failed: Executable has been modified");
721             break;
722         case CRYPT_E_SECURITY_SETTINGS:
723             afsi_log("VerifyTrust failed: local security options prevent verification");
724             break;
725         default:
726             afsi_log("VerifyTrust failed: 0x%X", GetLastError());
727         }
728         success = FALSE;
729     }
730     FreeLibrary(hWinTrust);
731     return success;
732 }
733
734 void LogCertCtx(PCCERT_CONTEXT pCtx) {
735     DWORD dwData;
736     LPTSTR szName = NULL;
737
738     if ( hCrypt32 == NULL )
739         return;
740
741     // Get Issuer name size.
742     if (!(dwData = pCertGetNameString(pCtx,
743                                       CERT_NAME_SIMPLE_DISPLAY_TYPE,
744                                       CERT_NAME_ISSUER_FLAG,
745                                       NULL,
746                                       NULL,
747                                       0))) {
748         afsi_log("CertGetNameString failed: 0x%x", GetLastError());
749         goto __exit;
750     }
751
752     // Allocate memory for Issuer name.
753     szName = (LPTSTR)LocalAlloc(LPTR, dwData * sizeof(TCHAR));
754
755     // Get Issuer name.
756     if (!(pCertGetNameString(pCtx,
757                              CERT_NAME_SIMPLE_DISPLAY_TYPE,
758                              CERT_NAME_ISSUER_FLAG,
759                              NULL,
760                              szName,
761                              dwData))) {
762         afsi_log("CertGetNameString failed: 0x%x", GetLastError());
763         goto __exit;
764     }
765
766     // print Issuer name.
767     afsi_log("Issuer Name: %s", szName);
768     LocalFree(szName);
769     szName = NULL;
770
771     // Get Subject name size.
772     if (!(dwData = pCertGetNameString(pCtx,
773                                       CERT_NAME_SIMPLE_DISPLAY_TYPE,
774                                       0,
775                                       NULL,
776                                       NULL,
777                                       0))) {
778         afsi_log("CertGetNameString failed: 0x%x", GetLastError());
779         goto __exit;
780     }
781
782     // Allocate memory for subject name.
783     szName = (LPTSTR)LocalAlloc(LPTR, dwData * sizeof(TCHAR));
784
785     // Get subject name.
786     if (!(pCertGetNameString(pCtx,
787                              CERT_NAME_SIMPLE_DISPLAY_TYPE,
788                              0,
789                              NULL,
790                              szName,
791                              dwData))) {
792         afsi_log("CertGetNameString failed: 0x%x", GetLastError());
793         goto __exit;
794     }
795
796     // Print Subject Name.
797     afsi_log("Subject Name: %s", szName);
798
799   __exit:
800
801     if (szName)
802         LocalFree(szName);
803 }
804
805 BOOL AFSModulesVerify(void)
806 {
807     CHAR filename[1024];
808     CHAR afsdVersion[128];
809     CHAR modVersion[128];
810     CHAR checkName[1024];
811     BOOL trustVerified = FALSE;
812     HMODULE hMods[1024];
813     HANDLE hProcess;
814     DWORD cbNeeded;
815     unsigned int i;
816     BOOL success = TRUE;
817     PCCERT_CONTEXT pCtxService = NULL;
818     HINSTANCE hPSAPI;
819     DWORD (WINAPI *pGetModuleFileNameExA)(HANDLE hProcess, HMODULE hModule, LPTSTR lpFilename, DWORD nSize);
820     BOOL (WINAPI *pEnumProcessModules)(HANDLE hProcess, HMODULE* lphModule, DWORD cb, LPDWORD lpcbNeeded);
821     DWORD dummyLen, code;
822     DWORD verifyServiceSig = TRUE;
823     HKEY parmKey;
824
825     hPSAPI = LoadLibrary("psapi");
826
827     if ( hPSAPI == NULL )
828         return FALSE;
829
830     if (!GetModuleFileName(NULL, filename, sizeof(filename)))
831         return FALSE;
832
833     if (GetVersionInfo(filename, afsdVersion, sizeof(afsdVersion)))
834         return FALSE;
835
836     afsi_log("%s version %s", filename, afsdVersion);
837
838     if (((FARPROC) pGetModuleFileNameExA =
839           GetProcAddress( hPSAPI, "GetModuleFileNameExA" )) == NULL ||
840          ((FARPROC) pEnumProcessModules =
841            GetProcAddress( hPSAPI, "EnumProcessModules" )) == NULL)
842     {
843         FreeLibrary(hPSAPI);
844         return FALSE;
845     }
846
847     code = RegOpenKeyEx(HKEY_LOCAL_MACHINE, "SOFTWARE\\OpenAFS\\Client",
848                          0, KEY_QUERY_VALUE, &parmKey);
849     if (code == ERROR_SUCCESS) {
850         dummyLen = sizeof(verifyServiceSig);
851         code = RegQueryValueEx(parmKey, "VerifyServiceSignature", NULL, NULL,
852                                 (BYTE *) &verifyServiceSig, &dummyLen);
853         RegCloseKey (parmKey);
854     }
855
856     if (verifyServiceSig)
857         trustVerified = VerifyTrust(filename);
858
859     if (trustVerified) {
860         LoadCrypt32();
861
862         // get a certificate context for the signer of afsd_service.
863         pCtxService = GetCertCtx(filename);
864         if (pCtxService)
865             LogCertCtx(pCtxService);
866     }
867
868     // Get a list of all the modules in this process.
869     hProcess = OpenProcess(PROCESS_QUERY_INFORMATION | PROCESS_VM_READ,
870                            FALSE, GetCurrentProcessId());
871
872     if (pEnumProcessModules(hProcess, hMods, sizeof(hMods), &cbNeeded))
873     {
874         afsi_log("Num of Process Modules: %d", (cbNeeded / sizeof(HMODULE)));
875
876         for (i = 0; i < (cbNeeded / sizeof(HMODULE)); i++)
877         {
878             char szModName[2048];
879
880             // Get the full path to the module's file.
881             if (pGetModuleFileNameExA(hProcess, hMods[i], szModName, sizeof(szModName)))
882             {
883                 lstrcpy(checkName, szModName);
884                 strlwr(checkName);
885
886                 if ( strstr(checkName, "afspthread.dll") ||
887                      strstr(checkName, "afsauthent.dll") ||
888                      strstr(checkName, "afsrpc.dll") ||
889                      strstr(checkName, "libafsconf.dll") ||
890                      strstr(checkName, "libosi.dll") )
891                 {
892                     if (GetVersionInfo(szModName, modVersion, sizeof(modVersion))) {
893                         success = FALSE;
894                         continue;
895                     }
896
897                     afsi_log("%s version %s", szModName, modVersion);
898                     if (strcmp(afsdVersion,modVersion)) {
899                         afsi_log("Version mismatch: %s", szModName);
900                         success = FALSE;
901                     }
902                     if ( trustVerified ) {
903                         if ( !VerifyTrust(szModName) ) {
904                             afsi_log("Signature Verification failed: %s", szModName);
905                             success = FALSE;
906                         } 
907                         else if (pCtxService) {
908                             PCCERT_CONTEXT pCtx = GetCertCtx(szModName);
909
910                             if (!pCtx || !pCertCompareCertificate(X509_ASN_ENCODING | PKCS_7_ASN_ENCODING,
911                                                                   pCtxService->pCertInfo,
912                                                                   pCtx->pCertInfo)) {
913                                 afsi_log("Certificate mismatch: %s", szModName);
914                                 if (pCtx)
915                                     LogCertCtx(pCtx);
916                                 
917                                 success = FALSE;
918                             }
919                             
920                             if (pCtx)
921                                 pCertFreeCertificateContext(pCtx);
922                         }
923                     }
924                 }
925             }
926         }
927     }
928
929     if (pCtxService) {
930         pCertFreeCertificateContext(pCtxService);
931         UnloadCrypt32();
932     }
933
934     FreeLibrary(hPSAPI);
935
936     CloseHandle(hProcess);
937     return success;
938 }
939
940 typedef BOOL ( APIENTRY * AfsdInitHook )(void);
941 #define AFSD_INIT_HOOK "AfsdInitHook"
942 #define AFSD_HOOK_DLL  "afsdhook.dll"
943
944 /*
945 control serviceex exists only on 2000/xp. These functions will be loaded dynamically.
946 */
947
948 typedef SERVICE_STATUS_HANDLE ( * RegisterServiceCtrlHandlerExFunc )(  LPCTSTR , LPHANDLER_FUNCTION_EX , LPVOID );
949 typedef SERVICE_STATUS_HANDLE ( * RegisterServiceCtrlHandlerFunc   )(  LPCTSTR ,  LPHANDLER_FUNCTION );
950
951 RegisterServiceCtrlHandlerExFunc pRegisterServiceCtrlHandlerEx = NULL;
952 RegisterServiceCtrlHandlerFunc   pRegisterServiceCtrlHandler   = NULL; 
953
954 void afsd_Main(DWORD argc, LPTSTR *argv)
955 {
956     long code;
957     char *reason;
958 #ifdef JUMP
959     int jmpret;
960 #endif /* JUMP */
961     HANDLE hInitHookDll;
962     HANDLE hAdvApi32;
963     AfsdInitHook initHook;
964
965 #ifdef _DEBUG
966     _CrtSetDbgFlag(_CRTDBG_ALLOC_MEM_DF /*| _CRTDBG_CHECK_ALWAYS_DF*/ | 
967                    _CRTDBG_CHECK_CRT_DF /* | _CRTDBG_DELAY_FREE_MEM_DF */ );
968 #endif 
969
970     osi_InitPanic(afsd_notifier);
971     osi_InitTraceOption();
972
973     GlobalStatus = 0;
974
975     afsi_start();
976
977     WaitToTerminate = CreateEvent(NULL, TRUE, FALSE, TEXT("afsd_service_WaitToTerminate"));
978     if ( GetLastError() == ERROR_ALREADY_EXISTS )
979         afsi_log("Event Object Already Exists: %s", TEXT("afsd_service_WaitToTerminate"));
980
981 #ifndef NOTSERVICE
982     hAdvApi32 = LoadLibrary("advapi32.dll");
983     if (hAdvApi32 == NULL)
984     {
985         afsi_log("Fatal: cannot load advapi32.dll");
986         return;
987     }
988
989     pRegisterServiceCtrlHandlerEx = (RegisterServiceCtrlHandlerExFunc)GetProcAddress(hAdvApi32, "RegisterServiceCtrlHandlerExA");
990     if (pRegisterServiceCtrlHandlerEx)
991     {
992         afsi_log("running on 2000+ - using RegisterServiceCtrlHandlerEx");
993         StatusHandle = RegisterServiceCtrlHandlerEx(AFS_DAEMON_SERVICE_NAME, afsd_ServiceControlHandlerEx, NULL );
994     }
995     else
996     {
997         StatusHandle = RegisterServiceCtrlHandler(AFS_DAEMON_SERVICE_NAME, afsd_ServiceControlHandler);
998     }
999
1000     ServiceStatus.dwServiceType = SERVICE_WIN32_OWN_PROCESS;
1001     ServiceStatus.dwServiceSpecificExitCode = 0;
1002     ServiceStatus.dwCurrentState = SERVICE_START_PENDING;
1003     ServiceStatus.dwWin32ExitCode = NO_ERROR;
1004     ServiceStatus.dwCheckPoint = 1;
1005     ServiceStatus.dwWaitHint = 30000;
1006     /* accept Power Events */
1007     ServiceStatus.dwControlsAccepted = SERVICE_ACCEPT_POWEREVENT;
1008     SetServiceStatus(StatusHandle, &ServiceStatus);
1009 #endif
1010
1011     {       
1012     HANDLE h; char *ptbuf[1];
1013     h = RegisterEventSource(NULL, AFS_DAEMON_EVENT_NAME);
1014     ptbuf[0] = "AFS start pending";
1015     ReportEvent(h, EVENTLOG_INFORMATION_TYPE, 0, 0, NULL, 1, 0, ptbuf, NULL);
1016     DeregisterEventSource(h);
1017     }
1018
1019 #ifdef REGISTER_POWER_NOTIFICATIONS
1020     {
1021         HKEY hkParm;
1022         DWORD code;
1023         DWORD dummyLen;
1024         int bpower = TRUE;
1025
1026         /* see if we should handle power notifications */
1027         code = RegOpenKeyEx(HKEY_LOCAL_MACHINE, AFSConfigKeyName, 0, KEY_QUERY_VALUE, &hkParm);
1028         if (code == ERROR_SUCCESS) {
1029             dummyLen = sizeof(bpower);
1030             code = RegQueryValueEx(hkParm, "FlushOnHibernate", NULL, NULL,
1031                 (BYTE *) &bpower, &dummyLen);      
1032
1033             if(code != ERROR_SUCCESS)
1034                 bpower = TRUE;
1035
1036             RegCloseKey(hkParm);
1037         }
1038         /* create thread used to flush cache */
1039         if (bpower) {
1040             PowerNotificationThreadCreate();
1041             powerEventsRegistered = 1;
1042         }
1043     }
1044 #endif
1045
1046     /* Verify the versions of the DLLs which were loaded */
1047     if (!AFSModulesVerify()) {
1048         ServiceStatus.dwCurrentState = SERVICE_STOPPED;
1049         ServiceStatus.dwWin32ExitCode = NO_ERROR;
1050         ServiceStatus.dwCheckPoint = 0;
1051         ServiceStatus.dwWaitHint = 0;
1052         ServiceStatus.dwControlsAccepted = 0;
1053         SetServiceStatus(StatusHandle, &ServiceStatus);
1054                        
1055         /* exit if initialization failed */
1056         return;
1057     }
1058
1059     /* allow an exit to be called prior to any initialization */
1060     hInitHookDll = LoadLibrary(AFSD_HOOK_DLL);
1061     if (hInitHookDll)
1062     {
1063         BOOL hookRc = FALSE;
1064         initHook = ( AfsdInitHook ) GetProcAddress(hInitHookDll, AFSD_INIT_HOOK);
1065         if (initHook)
1066         {
1067             hookRc = initHook();
1068         }
1069         FreeLibrary(hInitHookDll);
1070         hInitHookDll = NULL;
1071
1072         if (hookRc == FALSE)
1073         {
1074             ServiceStatus.dwCurrentState = SERVICE_STOPPED;
1075             ServiceStatus.dwWin32ExitCode = NO_ERROR;
1076             ServiceStatus.dwCheckPoint = 0;
1077             ServiceStatus.dwWaitHint = 0;
1078             ServiceStatus.dwControlsAccepted = 0;
1079             SetServiceStatus(StatusHandle, &ServiceStatus);
1080                        
1081             /* exit if initialization failed */
1082             return;
1083         }
1084         else
1085         {
1086             /* allow another 15 seconds to start */
1087             ServiceStatus.dwServiceType = SERVICE_WIN32_OWN_PROCESS;
1088             ServiceStatus.dwServiceSpecificExitCode = 0;
1089             ServiceStatus.dwCurrentState = SERVICE_START_PENDING;
1090             ServiceStatus.dwWin32ExitCode = NO_ERROR;
1091             ServiceStatus.dwCheckPoint = 2;
1092             ServiceStatus.dwWaitHint = 20000;
1093             /* accept Power Events */
1094             ServiceStatus.dwControlsAccepted = SERVICE_ACCEPT_POWEREVENT;
1095             SetServiceStatus(StatusHandle, &ServiceStatus);
1096         }
1097     }
1098
1099 #ifdef JUMP
1100     MainThreadId = GetCurrentThreadId();
1101     jmpret = setjmp(notifier_jmp);
1102
1103     if (jmpret == 0) 
1104 #endif /* JUMP */
1105     {
1106         code = afsd_InitCM(&reason);
1107         if (code != 0) {
1108             afsi_log("afsd_InitCM failed: %s (code = %d)", reason, code);
1109             osi_panic(reason, __FILE__, __LINE__);
1110         }
1111
1112 #ifndef NOTSERVICE
1113         ServiceStatus.dwCheckPoint++;
1114         ServiceStatus.dwWaitHint -= 5000;
1115         SetServiceStatus(StatusHandle, &ServiceStatus);
1116 #endif
1117         code = afsd_InitDaemons(&reason);
1118         if (code != 0) {
1119             afsi_log("afsd_InitDaemons failed: %s (code = %d)", reason, code);
1120                         osi_panic(reason, __FILE__, __LINE__);
1121         }
1122
1123 #ifndef NOTSERVICE
1124         ServiceStatus.dwCheckPoint++;
1125         ServiceStatus.dwWaitHint -= 5000;
1126         SetServiceStatus(StatusHandle, &ServiceStatus);
1127 #endif
1128         code = afsd_InitSMB(&reason, MessageBox);
1129         if (code != 0) {
1130             afsi_log("afsd_InitSMB failed: %s (code = %d)", reason, code);
1131             osi_panic(reason, __FILE__, __LINE__);
1132         }
1133
1134         MountGlobalDrives();
1135
1136 #ifndef NOTSERVICE
1137         ServiceStatus.dwCurrentState = SERVICE_RUNNING;
1138         ServiceStatus.dwWin32ExitCode = NO_ERROR;
1139         ServiceStatus.dwCheckPoint = 0;
1140         ServiceStatus.dwWaitHint = 0;
1141
1142         /* accept Power events */
1143         ServiceStatus.dwControlsAccepted = SERVICE_ACCEPT_STOP | SERVICE_ACCEPT_POWEREVENT;
1144         SetServiceStatus(StatusHandle, &ServiceStatus);
1145 #endif  
1146         {
1147             HANDLE h; char *ptbuf[1];
1148             h = RegisterEventSource(NULL, AFS_DAEMON_EVENT_NAME);
1149             ptbuf[0] = "AFS running";
1150             ReportEvent(h, EVENTLOG_INFORMATION_TYPE, 0, 0, NULL, 1, 0, ptbuf, NULL);
1151             DeregisterEventSource(h);
1152         }
1153     }
1154
1155     WaitForSingleObject(WaitToTerminate, INFINITE);
1156
1157     {   
1158         HANDLE h; char *ptbuf[1];
1159         h = RegisterEventSource(NULL, AFS_DAEMON_EVENT_NAME);
1160         ptbuf[0] = "AFS quitting";
1161         ReportEvent(h, GlobalStatus ? EVENTLOG_ERROR_TYPE : EVENTLOG_INFORMATION_TYPE,
1162                 0, 0, NULL, 1, 0, ptbuf, NULL);
1163         DeregisterEventSource(h);
1164     }
1165
1166     DismountGlobalDrives();
1167     smb_Shutdown();
1168     rx_Finalize();
1169
1170 #ifdef  REGISTER_POWER_NOTIFICATIONS
1171     /* terminate thread used to flush cache */
1172     if (powerEventsRegistered)
1173         PowerNotificationThreadExit();
1174 #endif
1175
1176     /* Remove the ExceptionFilter */
1177     SetUnhandledExceptionFilter(NULL);
1178
1179     ServiceStatus.dwCurrentState = SERVICE_STOPPED;
1180     ServiceStatus.dwWin32ExitCode = GlobalStatus ? ERROR_EXCEPTION_IN_SERVICE : NO_ERROR;
1181     ServiceStatus.dwCheckPoint = 0;
1182     ServiceStatus.dwWaitHint = 0;
1183     ServiceStatus.dwControlsAccepted = 0;
1184     SetServiceStatus(StatusHandle, &ServiceStatus);
1185 }       
1186
1187 DWORD __stdcall afsdMain_thread(void* notUsed)
1188 {
1189     char * argv[2] = {AFS_DAEMON_SERVICE_NAME, NULL};
1190     afsd_Main(1, (LPTSTR*)argv);
1191     return(0);
1192 }
1193
1194 int
1195 main(void)
1196 {
1197     static SERVICE_TABLE_ENTRY dispatchTable[] = {
1198         {AFS_DAEMON_SERVICE_NAME, (LPSERVICE_MAIN_FUNCTION) afsd_Main},
1199         {NULL, NULL}
1200     };
1201
1202     if (!StartServiceCtrlDispatcher(dispatchTable))
1203     {
1204         LONG status = GetLastError();
1205         if (status == ERROR_FAILED_SERVICE_CONTROLLER_CONNECT)
1206         {
1207             DWORD tid;
1208             hAFSDMainThread = CreateThread(NULL, 0, afsdMain_thread, 0, 0, &tid);
1209                 
1210             printf("Hit <Enter> to terminate OpenAFS Client Service\n");
1211             getchar();  
1212             SetEvent(WaitToTerminate);
1213         }
1214     }
1215
1216     if ( hAFSDMainThread ) {
1217         WaitForSingleObject( hAFSDMainThread, INFINITE );
1218         CloseHandle( hAFSDMainThread );
1219     }
1220     return(0);
1221 }