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