//+-------------------------------------------------------------------------- // // Microsoft Windows // Copyright (C) Microsoft Corporation, 1996 - 1999 // // File: core.cpp // // Contents: Cert Server Core implementation // // History: 25-Jul-96 vich created // //--------------------------------------------------------------------------- #include #pragma hdrstop #include #include #include #include #include #include "cscom.h" #include "csprop.h" #include "cspolicy.h" #include "ciinit.h" #include "csdisp.h" #include "csldap.h" #include "cainfop.h" #include "elog.h" #include "certlog.h" #include "resource.h" #define __dwFILE__ __dwFILE_CERTSRV_CORE_CPP__ #if DBG_COMTEST #define DBG_COMTEST_CONST #else #define DBG_COMTEST_CONST const #endif DBG_COMTEST_CONST BOOL fComTest = FALSE; SERVERCALLBACKS ServerCallBacks = { PropCIGetProperty, PropCISetProperty, PropCIGetExtension, PropCISetExtension, PropCIEnumSetup, PropCIEnumNext, PropCIEnumClose, }; HINSTANCE g_hInstance; WCHAR g_wszSharedFolder[MAX_PATH]; WCHAR g_wszSanitizedName[MAX_PATH]; WCHAR *g_pwszSanitizedDSName; WCHAR g_wszCommonName[MAX_PATH]; WCHAR g_wszParentConfig[MAX_PATH]; WCHAR *g_pwszzSubjectTemplate = NULL; WCHAR *g_pwszServerName = NULL; DWORD g_dwClockSkewMinutes = CCLOCKSKEWMINUTESDEFAULT; DWORD g_dwLogLevel = CERTLOG_WARNING; DWORD g_dwHighSerial = 0; DWORD g_cbMaxIncomingMessageSize = MAXINCOMINGMESSAGESIZEDEFAULT; WCHAR const g_wszRegValidityPeriodString[] = wszREGVALIDITYPERIODSTRING; WCHAR const g_wszRegValidityPeriodCount[] = wszREGVALIDITYPERIODCOUNT; WCHAR const g_wszRegCAXchgValidityPeriodString[] = wszREGCAXCHGVALIDITYPERIODSTRING; WCHAR const g_wszRegCAXchgValidityPeriodCount[] = wszREGCAXCHGVALIDITYPERIODCOUNT; WCHAR const g_wszRegCAXchgOverlapPeriodString[] = wszREGCAXCHGOVERLAPPERIODSTRING; WCHAR const g_wszRegCAXchgOverlapPeriodCount[] = wszREGCAXCHGOVERLAPPERIODCOUNT; WCHAR const g_wszRegCAXchgCertHash[] = wszREGCAXCHGCERTHASH; WCHAR const g_wszRegSubjectTemplate[] = wszREGSUBJECTTEMPLATE; WCHAR const g_wszRegKeyConfigPath[] = wszREGKEYCONFIGPATH; WCHAR const g_wszRegDirectory[] = wszREGDIRECTORY; WCHAR const g_wszRegActive[] = wszREGACTIVE; WCHAR const g_wszRegEnabled[] = wszREGENABLED; WCHAR const g_wszRegPolicyFlags[] = wszREGPOLICYFLAGS; WCHAR const g_wszCertSrvServiceName[] = wszSERVICE_NAME; WCHAR const g_wszRegCertEnrollCompatible[] = wszREGCERTENROLLCOMPATIBLE; WCHAR const g_wszRegEnforceX500NameLengths[] = wszREGENFORCEX500NAMELENGTHS; WCHAR const g_wszRegForceTeletex[] = wszREGFORCETELETEX; WCHAR const g_wszRegClockSkewMinutes[] = wszREGCLOCKSKEWMINUTES; WCHAR const g_wszRegLogLevel[] = wszREGLOGLEVEL; WCHAR const g_wszRegHighSerial[] = wszREGHIGHSERIAL; WCHAR const g_wszRegMaxIncomingMessageSize[] = wszREGMAXINCOMINGMESSAGESIZE; BOOL g_fCertEnrollCompatible = TRUE; BOOL g_fEnforceRDNNameLengths = TRUE; DWORD g_KRAFlags = 0; DWORD g_CRLEditFlags = EDITF_ENABLEAKIKEYID | EDITF_ENABLEAKIISSUERNAME | EDITF_ENABLEAKIISSUERSERIAL | EDITF_ENABLEAKICRITICAL; ENUM_FORCETELETEX g_fForceTeletex = ENUM_TELETEX_AUTO; ENUM_CATYPES g_CAType = ENUM_UNKNOWN_CA; BOOL g_fUseDS = FALSE; BOOL g_fcritsecDSCache = FALSE; BOOL g_fServerUpgraded = FALSE; CRITICAL_SECTION g_critsecDSCache; BOOL g_fLockICertRequest = FALSE; //+-------------------------------------------------------------------------- // Name properties: WCHAR const g_wszPropDistinguishedName[] = wszPROPDISTINGUISHEDNAME; WCHAR const g_wszPropRawName[] = wszPROPRAWNAME; WCHAR const g_wszPropCountry[] = wszPROPCOUNTRY; WCHAR const g_wszPropOrganization[] = wszPROPORGANIZATION; WCHAR const g_wszPropOrgUnit[] = wszPROPORGUNIT; WCHAR const g_wszPropCommonName[] = wszPROPCOMMONNAME; WCHAR const g_wszPropLocality[] = wszPROPLOCALITY; WCHAR const g_wszPropState[] = wszPROPSTATE; WCHAR const g_wszPropTitle[] = wszPROPTITLE; WCHAR const g_wszPropGivenName[] = wszPROPGIVENNAME; WCHAR const g_wszPropInitials[] = wszPROPINITIALS; WCHAR const g_wszPropSurName[] = wszPROPSURNAME; WCHAR const g_wszPropDomainComponent[] = wszPROPDOMAINCOMPONENT; WCHAR const g_wszPropEMail[] = wszPROPEMAIL; WCHAR const g_wszPropStreetAddress[] = wszPROPSTREETADDRESS; WCHAR const g_wszPropUnstructuredAddress[] = wszPROPUNSTRUCTUREDADDRESS; WCHAR const g_wszPropUnstructuredName[] = wszPROPUNSTRUCTUREDNAME; WCHAR const g_wszPropDeviceSerialNumber[] = wszPROPDEVICESERIALNUMBER; //+-------------------------------------------------------------------------- // Subject Name properties: WCHAR const g_wszPropSubjectDot[] = wszPROPSUBJECTDOT; WCHAR const g_wszPropSubjectDistinguishedName[] = wszPROPSUBJECTDISTINGUISHEDNAME; WCHAR const g_wszPropSubjectRawName[] = wszPROPSUBJECTRAWNAME; WCHAR const g_wszPropSubjectCountry[] = wszPROPSUBJECTCOUNTRY; WCHAR const g_wszPropSubjectOrganization[] = wszPROPSUBJECTORGANIZATION; WCHAR const g_wszPropSubjectOrgUnit[] = wszPROPSUBJECTORGUNIT; WCHAR const g_wszPropSubjectCommonName[] = wszPROPSUBJECTCOMMONNAME; WCHAR const g_wszPropSubjectLocality[] = wszPROPSUBJECTLOCALITY; WCHAR const g_wszPropSubjectState[] = wszPROPSUBJECTSTATE; WCHAR const g_wszPropSubjectTitle[] = wszPROPSUBJECTTITLE; WCHAR const g_wszPropSubjectGivenName[] = wszPROPSUBJECTGIVENNAME; WCHAR const g_wszPropSubjectInitials[] = wszPROPSUBJECTINITIALS; WCHAR const g_wszPropSubjectSurName[] = wszPROPSUBJECTSURNAME; WCHAR const g_wszPropSubjectDomainComponent[] = wszPROPSUBJECTDOMAINCOMPONENT; WCHAR const g_wszPropSubjectEMail[] = wszPROPSUBJECTEMAIL; WCHAR const g_wszPropSubjectStreetAddress[] = wszPROPSUBJECTSTREETADDRESS; WCHAR const g_wszPropSubjectUnstructuredAddress[] = wszPROPSUBJECTUNSTRUCTUREDADDRESS; WCHAR const g_wszPropSubjectUnstructuredName[] = wszPROPSUBJECTUNSTRUCTUREDNAME; WCHAR const g_wszPropSubjectDeviceSerialNumber[] = wszPROPSUBJECTDEVICESERIALNUMBER; //+-------------------------------------------------------------------------- // Issuer Name properties: WCHAR const g_wszPropIssuerDot[] = wszPROPISSUERDOT; WCHAR const g_wszPropIssuerDistinguishedName[] = wszPROPISSUERDISTINGUISHEDNAME; WCHAR const g_wszPropIssuerRawName[] = wszPROPISSUERRAWNAME; WCHAR const g_wszPropIssuerCountry[] = wszPROPISSUERCOUNTRY; WCHAR const g_wszPropIssuerOrganization[] = wszPROPISSUERORGANIZATION; WCHAR const g_wszPropIssuerOrgUnit[] = wszPROPISSUERORGUNIT; WCHAR const g_wszPropIssuerCommonName[] = wszPROPISSUERCOMMONNAME; WCHAR const g_wszPropIssuerLocality[] = wszPROPISSUERLOCALITY; WCHAR const g_wszPropIssuerState[] = wszPROPISSUERSTATE; WCHAR const g_wszPropIssuerTitle[] = wszPROPISSUERTITLE; WCHAR const g_wszPropIssuerGivenName[] = wszPROPISSUERGIVENNAME; WCHAR const g_wszPropIssuerInitials[] = wszPROPISSUERINITIALS; WCHAR const g_wszPropIssuerSurName[] = wszPROPISSUERSURNAME; WCHAR const g_wszPropIssuerDomainComponent[] = wszPROPISSUERDOMAINCOMPONENT; WCHAR const g_wszPropIssuerEMail[] = wszPROPISSUEREMAIL; WCHAR const g_wszPropIssuerStreetAddress[] = wszPROPISSUERSTREETADDRESS; WCHAR const g_wszPropIssuerUnstructuredAddress[] = wszPROPISSUERUNSTRUCTUREDADDRESS; WCHAR const g_wszPropIssuerUnstructuredName[] = wszPROPISSUERUNSTRUCTUREDNAME; WCHAR const g_wszPropIssuerDeviceSerialNumber[] = wszPROPISSUERDEVICESERIALNUMBER; //+-------------------------------------------------------------------------- // Request properties: WCHAR const g_wszPropRequestRequestID[] = wszPROPREQUESTREQUESTID; WCHAR const g_wszPropRequestRawRequest[] = wszPROPREQUESTRAWREQUEST; WCHAR const g_wszPropRequestRawArchivedKey[] = wszPROPREQUESTRAWARCHIVEDKEY; WCHAR const g_wszPropRequestKeyRecoveryHashes[] = wszPROPREQUESTKEYRECOVERYHASHES; WCHAR const g_wszPropRequestRawOldCertificate[] = wszPROPREQUESTRAWOLDCERTIFICATE; WCHAR const g_wszPropRequestAttributes[] = wszPROPREQUESTATTRIBUTES; WCHAR const g_wszPropRequestType[] = wszPROPREQUESTTYPE; WCHAR const g_wszPropRequestFlags[] = wszPROPREQUESTFLAGS; WCHAR const g_wszPropRequestStatusCode[] = wszPROPREQUESTSTATUSCODE; WCHAR const g_wszPropRequestDisposition[] = wszPROPREQUESTDISPOSITION; WCHAR const g_wszPropRequestDispositionMessage[] = wszPROPREQUESTDISPOSITIONMESSAGE; WCHAR const g_wszPropRequestSubmittedWhen[] = wszPROPREQUESTSUBMITTEDWHEN; WCHAR const g_wszPropRequestResolvedWhen[] = wszPROPREQUESTRESOLVEDWHEN; WCHAR const g_wszPropRequestRevokedWhen[] = wszPROPREQUESTREVOKEDWHEN; WCHAR const g_wszPropRequestRevokedEffectiveWhen[] = wszPROPREQUESTREVOKEDEFFECTIVEWHEN; WCHAR const g_wszPropRequestRevokedReason[] = wszPROPREQUESTREVOKEDREASON; WCHAR const g_wszPropRequesterName[] = wszPROPREQUESTERNAME; WCHAR const g_wszPropCallerName[] = wszPROPCALLERNAME; WCHAR const g_wszPropRequestOSVersion[] = wszPROPREQUESTOSVERSION; WCHAR const g_wszPropRequestCSPProvider[] = wszPROPREQUESTCSPPROVIDER; //+-------------------------------------------------------------------------- // Request attribute properties: WCHAR const g_wszPropChallenge[] = wszPROPCHALLENGE; WCHAR const g_wszPropExpectedChallenge[] = wszPROPEXPECTEDCHALLENGE; //+-------------------------------------------------------------------------- // Certificate properties: WCHAR const g_wszPropCertificateRequestID[] = wszPROPCERTIFICATEREQUESTID; WCHAR const g_wszPropRawCertificate[] = wszPROPRAWCERTIFICATE; WCHAR const g_wszPropCertificateHash[] = wszPROPCERTIFICATEHASH; WCHAR const g_wszPropCertificateSerialNumber[] = wszPROPCERTIFICATESERIALNUMBER; WCHAR const g_wszPropCertificateIssuerNameID[] = wszPROPCERTIFICATEISSUERNAMEID; WCHAR const g_wszPropCertificateNotBeforeDate[] = wszPROPCERTIFICATENOTBEFOREDATE; WCHAR const g_wszPropCertificateNotAfterDate[] = wszPROPCERTIFICATENOTAFTERDATE; WCHAR const g_wszPropCertificateSubjectKeyIdentifier[] = wszPROPCERTIFICATESUBJECTKEYIDENTIFIER; WCHAR const g_wszPropCertificateRawPublicKey[] = wszPROPCERTIFICATERAWPUBLICKEY; WCHAR const g_wszPropCertificatePublicKeyLength[] = wszPROPCERTIFICATEPUBLICKEYLENGTH; WCHAR const g_wszPropCertificatePublicKeyAlgorithm[] = wszPROPCERTIFICATEPUBLICKEYALGORITHM; WCHAR const g_wszPropCertificateRawPublicKeyAlgorithmParameters[] = wszPROPCERTIFICATERAWPUBLICKEYALGORITHMPARAMETERS; // Strings loaded from the resource file: WCHAR const *g_pwszRequestedBy; WCHAR const *g_pwszDeniedBy; WCHAR const *g_pwszPublishedBy; WCHAR const *g_pwszPolicyDeniedRequest; WCHAR const *g_pwszIssued; WCHAR const *g_pwszUnderSubmission; WCHAR const *g_pwszRequestProcessingError; WCHAR const *g_pwszRequestParsingError; WCHAR const *g_pwszRevokedBy; WCHAR const *g_pwszUnrevokedBy; WCHAR const *g_pwszResubmittedBy; WCHAR const *g_pwszPrintfCertRequestDisposition; WCHAR const *g_pwszUnknownSubject; WCHAR const *g_pwszIntermediateCAStore; WCHAR const *g_pwszPublishError; WCHAR const *g_pwszYes; WCHAR const *g_pwszNo; LPWSTR g_wszzSecuredAttributes = NULL; LPCWSTR g_wszzSecuredAttributesDefault = wszzDEFAULTSIGNEDATTRIBUTES; typedef struct _STRINGINITMAP { int idResource; WCHAR const **ppwszResource; } STRINGINITMAP; STRINGINITMAP g_aStringInitStrings[] = { { IDS_REVOKEDBY, &g_pwszRevokedBy }, { IDS_UNREVOKEDBY, &g_pwszUnrevokedBy }, { IDS_RESUBMITTEDBY, &g_pwszResubmittedBy }, { IDS_REQUESTEDBY, &g_pwszRequestedBy }, { IDS_DENIEDBY, &g_pwszDeniedBy }, { IDS_PUBLISHEDBY, &g_pwszPublishedBy }, { IDS_POLICYDENIED, &g_pwszPolicyDeniedRequest }, { IDS_ISSUED, &g_pwszIssued }, { IDS_REQUESTPROCESSERROR, &g_pwszRequestProcessingError }, { IDS_REQUESTPARSEERROR, &g_pwszRequestParsingError }, { IDS_UNDERSUBMISSION, &g_pwszUnderSubmission }, { IDS_PRINTFCERTREQUESTDISPOSITION, &g_pwszPrintfCertRequestDisposition }, { IDS_UNKNOWNSUBJECT, &g_pwszUnknownSubject }, { IDS_INTERMEDIATECASTORE, &g_pwszIntermediateCAStore }, { IDS_PUBLISHERROR, &g_pwszPublishError }, { IDS_YES, &g_pwszYes}, { IDS_NO, &g_pwszNo}, { IDS_ALLOW,&g_pwszAuditResources[0]}, { IDS_DENY, &g_pwszAuditResources[1]}, { IDS_CAADMIN, &g_pwszAuditResources[2]}, { IDS_OFFICER, &g_pwszAuditResources[3]}, { IDS_READ, &g_pwszAuditResources[4]}, { IDS_ENROLL, &g_pwszAuditResources[5]}, }; typedef struct _DSCACHE { _DSCACHE *pdscNext; WCHAR *pwszDomain; LDAP *pld; } DSCACHE; DSCACHE *g_DSCache = NULL; HANDLE g_hDS = NULL; DWORD coreDSUnbindWorker( OPTIONAL IN OUT VOID *pvparms) { HANDLE hDS = (HANDLE) pvparms; DsUnBind(&hDS); return(0); } VOID coreDSUnBind( IN BOOL fSynchronous) { HRESULT hr; HANDLE hDS = g_hDS; HANDLE hThread = NULL; DWORD ThreadId; if (NULL != hDS) { g_hDS = NULL; if (fSynchronous) { coreDSUnbindWorker(hDS); } else { hThread = CreateThread( NULL, // lpThreadAttributes (Security Attr) 0, // dwStackSize coreDSUnbindWorker, hDS, // lpParameter 0, // dwCreationFlags &ThreadId); if (NULL == hThread) { hr = myHLastError(); _JumpError(hr, error, "CreateThread"); } } } error: if (NULL != hThread) { CloseHandle(hThread); } } HRESULT myAddDomainName( IN WCHAR const *pwszSamName, OUT WCHAR **ppwszSamName, // *ppwszSamName is NULL if unchanged OUT WCHAR const **ppwszUserName) { HRESULT hr; WCHAR const *pwszUserName; WCHAR wszDomain[MAX_PATH]; *ppwszSamName = NULL; *ppwszUserName = NULL; if (L'\0' == *pwszSamName) { hr = E_ACCESSDENIED; // can't have a zero length name _JumpError(hr, error, "zero length name"); } // See if it includes a domain name. pwszUserName = wcschr(pwszSamName, L'\\'); if (NULL == pwszUserName) { DWORD cwc = ARRAYSIZE(wszDomain); WCHAR *pwsz; // There was no domain portion, so assume part of the current domain. if (GetUserNameEx(NameSamCompatible, wszDomain, &cwc)) { // Fix NULL termination bug if (0 != cwc) { cwc--; } wszDomain[cwc] = L'\0'; pwsz = wcschr(wszDomain, L'\\'); if (NULL != pwsz) { pwsz++; wcsncpy(pwsz, pwszSamName, ARRAYSIZE(wszDomain) - cwc); hr = myDupString(wszDomain, ppwszSamName); _JumpIfError(hr, error, "myDupString"); pwszSamName = *ppwszSamName; } } } pwszUserName = wcschr(pwszSamName, L'\\'); if (NULL == pwszUserName) { pwszUserName = pwszSamName; } else { pwszUserName++; } *ppwszUserName = pwszUserName; hr = S_OK; error: return(hr); } HRESULT coreGetDNFromSamName( IN WCHAR const *pwszSamName, OUT WCHAR **ppwszDN) { HRESULT hr; BOOL fRediscover = FALSE; DS_NAME_RESULTW *pNameResults = NULL; CSASSERT(NULL != ppwszDN); *ppwszDN = NULL; while (TRUE) { if (fRediscover) { coreDSUnBind(FALSE); } if (NULL == g_hDS) { hr = DsBind(NULL, NULL, &g_hDS); if (S_OK != hr) { hr = myHError(hr); _JumpError(hr, error, "DsBind"); } } // Got a connection. Crack the name: hr = DsCrackNames( g_hDS, DS_NAME_NO_FLAGS, DS_NT4_ACCOUNT_NAME, DS_FQDN_1779_NAME, 1, // one name &pwszSamName, // one name (IN) &pNameResults); // OUT if (S_OK != hr) { hr = myHError(hr); if (!fRediscover) { _PrintError(hr, "DsCrackNames"); fRediscover = TRUE; continue; } _JumpError(hr, error, "DsCrackNames"); } if (1 > pNameResults->cItems || DS_NAME_NO_ERROR != pNameResults->rItems[0].status) { hr = HRESULT_FROM_WIN32(ERROR_CANT_ACCESS_DOMAIN_INFO); if (!fRediscover) { _PrintError(hr, "DsCrackNames result"); fRediscover = TRUE; continue; } _JumpError(hr, error, "DsCrackNames result"); } break; } hr = myDupString(pNameResults->rItems[0].pName, ppwszDN); _JumpIfError(hr, error, "myDupString"); error: if (NULL != pNameResults) { DsFreeNameResult(pNameResults); } return(hr); } HRESULT coreGetComContextUserDNFromSamName( IN BOOL fDeleteUserDNOnly, OPTIONAL IN WCHAR const *pwszSamName, IN LONG Context, IN DWORD dwComContextIndex, OPTIONAL OUT WCHAR const **ppwszDN) // do NOT free! { HRESULT hr; CERTSRV_COM_CONTEXT *pComContext; hr = ComGetClientInfo(Context, dwComContextIndex, &pComContext); _JumpIfError(hr, error, "ComGetClientInfo"); if (fDeleteUserDNOnly) { if (NULL != pComContext->pwszUserDN) { LocalFree(pComContext->pwszUserDN); pComContext->pwszUserDN = NULL; } } else { if (NULL == pComContext->pwszUserDN) { hr = coreGetDNFromSamName(pwszSamName, &pComContext->pwszUserDN); _JumpIfError(hr, error, "coreGetDNFromSamName"); } } if (NULL != ppwszDN) { *ppwszDN = pComContext->pwszUserDN; } hr = S_OK; error: return(hr); } HRESULT CoreSetComContextUserDN( IN DWORD dwRequestId, IN LONG Context, IN DWORD dwComContextIndex, OPTIONAL OUT WCHAR const **ppwszDN) // do NOT free! { HRESULT hr; ICertDBRow *prow = NULL; WCHAR *pwszSamName = NULL; WCHAR *pwszSamNamePatched = NULL; WCHAR const *pwszUserName; hr = g_pCertDB->OpenRow( PROPOPEN_READONLY | PROPTABLE_REQCERT, dwRequestId, NULL, &prow); _JumpIfError(hr, error, "OpenRow"); hr = PKCSGetProperty( prow, g_wszPropRequesterName, PROPTYPE_STRING | PROPCALLER_SERVER | PROPTABLE_REQUEST, NULL, (BYTE **) &pwszSamName); _JumpIfError(hr, error, "PKCSGetProperty"); hr = myAddDomainName(pwszSamName, &pwszSamNamePatched, &pwszUserName); _JumpIfError(hr, error, "myAddDomainName"); hr = coreGetComContextUserDNFromSamName( FALSE, // fDeleteUserDNOnly NULL != pwszSamNamePatched? pwszSamNamePatched : pwszSamName, Context, dwComContextIndex, ppwszDN); _JumpIfError(hr, error, "coreGetComContextUserDNFromSamName"); error: if (NULL != pwszSamName) { LocalFree(pwszSamName); } if (NULL != pwszSamNamePatched) { LocalFree(pwszSamNamePatched); } if (NULL != prow) { prow->Release(); } return(hr); } VOID CoreTerminate(VOID) { VOID coreFreeDSCache(); coreFreeDSCache(); DBShutDown(FALSE); ComShutDown(); PKCSTerminate(); CRLTerminate(); if (NULL != g_pwszServerName) { LocalFree(g_pwszServerName); g_pwszServerName = NULL; } if (NULL != g_pwszzSubjectTemplate) { LocalFree(g_pwszzSubjectTemplate); g_pwszzSubjectTemplate = NULL; } if (NULL != g_pwszSanitizedDSName) { LocalFree(g_pwszSanitizedDSName); g_pwszSanitizedDSName = NULL; } // free only if it points to memory that isn't the default static buffer if (NULL != g_wszzSecuredAttributes && g_wszzSecuredAttributes != g_wszzSecuredAttributesDefault) { LocalFree(g_wszzSecuredAttributes); g_wszzSecuredAttributes = NULL; } if (g_fcritsecDSCache) { DeleteCriticalSection(&g_critsecDSCache); g_fcritsecDSCache = FALSE; } } DWORD g_PolicyFlags; HRESULT CoreSetDisposition( IN ICertDBRow *prow, IN DWORD Disposition) { HRESULT hr; hr = prow->SetProperty( g_wszPropRequestDisposition, PROPTYPE_LONG | PROPCALLER_SERVER | PROPTABLE_REQUEST, sizeof(Disposition), (BYTE const *) &Disposition); _JumpIfError(hr, error, "SetProperty(disposition)"); error: return(hr); } DWORD coreRegGetTimePeriod( IN HKEY hkeyCN, IN WCHAR const *pwszRegPeriodCount, IN WCHAR const *pwszRegPeriodString, OUT enum ENUM_PERIOD *penumPeriod, OUT LONG *plCount) { HRESULT hr; LONG lCount; DWORD dwType; DWORD cbValue; cbValue = sizeof(lCount); hr = RegQueryValueEx( hkeyCN, pwszRegPeriodCount, NULL, // lpdwReserved &dwType, (BYTE *) &lCount, &cbValue); if (S_OK == hr && REG_DWORD == dwType && sizeof(lCount) == cbValue) { WCHAR awcPeriod[10]; cbValue = sizeof(awcPeriod); hr = RegQueryValueEx( hkeyCN, pwszRegPeriodString, NULL, // lpdwReserved &dwType, (BYTE *) &awcPeriod, &cbValue); if (S_OK != hr) { hr = myHError(hr); if (HRESULT_FROM_WIN32(ERROR_MORE_DATA) == hr) { hr = HRESULT_FROM_WIN32(ERROR_INVALID_DATA); } else if (HRESULT_FROM_WIN32(ERROR_FILE_NOT_FOUND) == hr) { hr = S_OK; } _JumpIfError(hr, error, "RegQueryValueEx"); } else { if (REG_SZ != dwType || sizeof(awcPeriod) <= cbValue) { hr = HRESULT_FROM_WIN32(ERROR_INVALID_DATA); _JumpIfErrorStr(hr, error, "time period string", pwszRegPeriodString); } hr = myTranslatePeriodUnits( awcPeriod, lCount, penumPeriod, plCount); _JumpIfError(hr, error, "myTranslatePeriodUnits"); } } error: return(hr); } // Converts a REG_SZ Subject template into a double null terminated REG_MULTI_SZ type string DWORD coreConvertSubjectTemplate( OUT WCHAR* pwszz, IN WCHAR* pwszTemplate, IN DWORD cwc) { HRESULT hr; WCHAR *pwszToken; WCHAR *pwszRemain = pwszTemplate; WCHAR *pwszzNew = pwszz; DWORD cwszzNew = 0; BOOL fSplit; while (TRUE) { pwszToken = PKCSSplitToken(&pwszRemain, wszNAMESEPARATORDEFAULT, &fSplit); if (NULL == pwszToken) { *pwszzNew = L'\0'; break; } cwszzNew += (1 + wcslen(pwszToken)) * sizeof(WCHAR); if (cwszzNew > cwc) { hr = HRESULT_FROM_WIN32(ERROR_BUFFER_OVERFLOW); _JumpError(hr, error, "overflow"); } wcscpy(pwszzNew, pwszToken); pwszzNew = wcschr(pwszzNew, L'\0'); pwszzNew++; } hr = S_OK; error: return(hr); } HRESULT CoreInit(VOID) { HRESULT hr; HKEY hkeyConfig = NULL; HKEY hkeyCN = NULL; BYTE abbuf[MAX_PATH * sizeof(TCHAR)]; WCHAR awcTemplate[MAX_PATH]; DWORD cbbuf; DWORD cchbuf; DWORD dwType; WCHAR *pwsz; DWORD dw; BOOL fLogError = TRUE; DWORD LogMsg = MSG_BAD_REGISTRY; WCHAR const *pwszLog = NULL; int i; DWORD cbValue; DWORD dwEnabled; CAuditEvent AuditSettings; __try { InitializeCriticalSection(&g_critsecDSCache); g_fcritsecDSCache = TRUE; hr = S_OK; } __except(hr = myHEXCEPTIONCODE(), EXCEPTION_EXECUTE_HANDLER) { } _JumpIfError(hr, error, "InitializeCriticalSection"); hr = myGetMachineDnsName(&g_pwszServerName); _JumpIfError(hr, error, "myGetMachineDnsName"); for (i = 0; i < ARRAYSIZE(g_aStringInitStrings); i++) { WCHAR const *pwszT; pwszT = myLoadResourceString(g_aStringInitStrings[i].idResource); if (NULL == pwszT) { hr = myHLastError(); _JumpError(hr, error, "myLoadResourceString"); } *g_aStringInitStrings[i].ppwszResource = pwszT; } hr = RegOpenKeyEx( HKEY_LOCAL_MACHINE, g_wszRegKeyConfigPath, 0, // dwReserved KEY_ENUMERATE_SUB_KEYS | KEY_EXECUTE | KEY_QUERY_VALUE, &hkeyConfig); _JumpIfError(hr, error, "RegOpenKeyEx(Config)"); cbbuf = sizeof(abbuf); hr = RegQueryValueEx( hkeyConfig, g_wszRegDirectory, NULL, // lpdwReserved &dwType, abbuf, &cbbuf); if (S_OK != hr) { hr = myHError(hr); } if (HRESULT_FROM_WIN32(ERROR_FILE_NOT_FOUND) != hr) { _JumpIfError(hr, error, "RegQueryValueEx(Base)"); if (REG_SZ != dwType) { hr = HRESULT_FROM_WIN32(ERROR_INVALID_DATA); _JumpError(hr, error, "RegQueryValueEx(Base)"); } if (sizeof(abbuf) < cbbuf) { hr = HRESULT_FROM_WIN32(ERROR_BUFFER_OVERFLOW); _JumpError(hr, error, "RegQueryValueEx(Base)"); } CopyMemory(g_wszSharedFolder, abbuf, cbbuf); } DBGPRINT((DBG_SS_CERTSRVI, "Shared Folder = '%ws'\n", g_wszSharedFolder)); // Find out the name of the active CA(s) g_wszSanitizedName[0] = L'\0'; cbbuf = sizeof(g_wszSanitizedName); hr = RegQueryValueEx( hkeyConfig, g_wszRegActive, NULL, // lpdwReserved &dwType, (BYTE *) g_wszSanitizedName, &cbbuf); if ((HRESULT) ERROR_FILE_NOT_FOUND == hr) { #define szForgotSetup "\n\nDid you forget to setup the Cert Server?\n\n\n" CONSOLEPRINT0((MAXDWORD, szForgotSetup)); } _JumpIfError(hr, error, "RegQueryValueEx(Base)"); if (REG_SZ != dwType && REG_MULTI_SZ != dwType) { hr = HRESULT_FROM_WIN32(ERROR_INVALID_DATA); _JumpError(hr, error, "RegQueryValueEx: value type"); } g_wszSanitizedName[cbbuf / sizeof(WCHAR)] = L'\0'; if (REG_MULTI_SZ == dwType) { i = wcslen(g_wszSanitizedName); if (L'\0' != g_wszSanitizedName[i + 1]) { hr = HRESULT_FROM_WIN32(ERROR_INVALID_DATA); _JumpError(hr, error, "RegQueryValueEx: multiple Active CAs"); } } DBGPRINT((DBG_SS_CERTSRVI, "Active CA (Sanitized Name) = '%ws'\n", g_wszSanitizedName)); pwszLog = g_wszSanitizedName; hr = mySanitizedNameToDSName(g_wszSanitizedName, &g_pwszSanitizedDSName); _JumpIfError(hr, error, "mySanitizedNameToDSName"); hr = RegOpenKeyEx( hkeyConfig, g_wszSanitizedName, 0, // dwReserved KEY_ENUMERATE_SUB_KEYS | KEY_EXECUTE | KEY_QUERY_VALUE, &hkeyCN); if (S_OK != hr) { hr = myHError(hr); _JumpError(hr, error, "RegOpenKeyEx"); } cbValue = sizeof(g_wszCommonName) - 2 * sizeof(WCHAR); hr = RegQueryValueEx( hkeyCN, wszREGCOMMONNAME, NULL, &dwType, (BYTE *)g_wszCommonName, &cbValue); if (S_OK != hr) { hr = myHError(hr); _JumpError(hr, error, "RegOpenKeyEx"); } if (REG_SZ != dwType) { hr = HRESULT_FROM_WIN32(ERROR_INVALID_DATA); _JumpError(hr, error, "Couldn't find CA common name"); } g_wszCommonName[cbValue / sizeof(WCHAR)] = L'\0'; pwszLog = g_wszCommonName; cbValue = sizeof(dwEnabled); hr = RegQueryValueEx( hkeyCN, g_wszRegEnabled, NULL, // lpdwReserved &dwType, (BYTE *) &dwEnabled, &cbValue); if (S_OK != hr) { hr = myHError(hr); _JumpError(hr, error, "RegQueryValueEx"); } if (REG_DWORD == dwType && sizeof(dwEnabled) == cbValue && 0 == dwEnabled) { DBGPRINT((DBG_SS_CERTSRVI, "CN = '%ws' DISABLED!\n", g_wszSanitizedName)); hr = HRESULT_FROM_WIN32(ERROR_INVALID_DATA); _JumpError(hr, error, "RegQueryValueEx: Active CA DISABLED!"); } DBGPRINT((DBG_SS_CERTSRVI, "CN = '%ws': Enabled\n", g_wszSanitizedName)); // to check machine setup status hr = GetSetupStatus(NULL, &dw); _JumpIfError(hr, error, "GetSetupStatus"); if (!(SETUP_SERVER_FLAG & dw)) { hr = HRESULT_FROM_WIN32(ERROR_CAN_NOT_COMPLETE); _JumpError(hr, error, "Server installation was not complete"); } if (SETUP_SERVER_UPGRADED_FLAG & dw) { g_fServerUpgraded = TRUE; DBGPRINT(( DBG_SS_CERTSRV, "CoreInit: read SETUP_SERVER_UPGRADED_FLAG\n")); } // check per ca hr = GetSetupStatus(g_wszSanitizedName, &dw); _JumpIfError(hr, error, "GetSetupStatus"); if (SETUP_SUSPEND_FLAG & dw) { LogMsg = MSG_E_INCOMPLETE_HIERARCHY; hr = HRESULT_FROM_WIN32(ERROR_INSTALL_SUSPEND); _JumpError(hr, error, "Hierarchy setup incomplete"); } if (!(SETUP_SERVER_FLAG & dw)) { hr = HRESULT_FROM_WIN32(ERROR_CAN_NOT_COMPLETE); _JumpError(hr, error, "Server installation was not complete"); } if (SETUP_FORCECRL_FLAG & dw) { // Don't clear SETUP_FORCECRL_FLAG until CRLs successfully generated hr = myDeleteCertRegValue( g_wszSanitizedName, NULL, NULL, wszREGCRLNEXTPUBLISH); _PrintIfErrorStr2( hr, "myDeleteCertRegValue", wszREGCRLNEXTPUBLISH, HRESULT_FROM_WIN32(ERROR_FILE_NOT_FOUND)); } // update the CA DS object with the server type flags if (SETUP_UPDATE_CAOBJECT_SVRTYPE & dw) { hr = SetCAObjectFlags( g_fAdvancedServer? CA_FLAG_CA_SERVERTYPE_ADVANCED : 0); _PrintIfError(hr, "SetCAObjectFlags"); if (S_OK == hr) { hr = SetSetupStatus( g_wszSanitizedName, SETUP_UPDATE_CAOBJECT_SVRTYPE, FALSE); _PrintIfError(hr, "SetSetupStatus"); } } cbValue = sizeof(g_PolicyFlags); hr = RegQueryValueEx( hkeyCN, g_wszRegPolicyFlags, NULL, // lpdwReserved &dwType, (BYTE *) &g_PolicyFlags, &cbValue); if (S_OK != hr || REG_DWORD != dwType || sizeof(g_PolicyFlags) != cbValue) { g_PolicyFlags = 0; } cbValue = sizeof(awcTemplate); hr = RegQueryValueEx( hkeyCN, g_wszRegSubjectTemplate, NULL, // lpdwReserved &dwType, (BYTE *) awcTemplate, &cbValue); if (S_OK == hr && (REG_SZ == dwType || REG_MULTI_SZ == dwType) && sizeof(WCHAR) < cbValue && L'\0' != awcTemplate[0]) { if (L'\0' != awcTemplate[cbValue/sizeof(WCHAR) - 1] || (REG_MULTI_SZ == dwType && L'\0' != awcTemplate[cbValue/sizeof(WCHAR) - 2]) || sizeof(awcTemplate) < cbValue) { LogMsg = MSG_E_REG_BAD_SUBJECT_TEMPLATE; hr = HRESULT_FROM_WIN32(ERROR_INVALID_DATA); _JumpError(hr, error, "Bad Subject Template length/termination"); } pwsz = (WCHAR *) LocalAlloc(LMEM_FIXED, cbValue + sizeof(WCHAR)); if (NULL != pwsz) { if (dwType == REG_MULTI_SZ) { CopyMemory(pwsz, awcTemplate, cbValue); } else { hr = coreConvertSubjectTemplate(pwsz, awcTemplate, cbValue); if (S_OK != hr) { LocalFree(pwsz); } _JumpIfError(hr, error, "coreConvertSubjectTemplate"); } g_pwszzSubjectTemplate = pwsz; } } cbValue = sizeof(dw); hr = RegQueryValueEx( hkeyCN, g_wszRegCertEnrollCompatible, NULL, // lpdwReserved &dwType, (BYTE *) &dw, &cbValue); if (S_OK == hr && REG_DWORD == dwType && sizeof(dw) == cbValue) { g_fCertEnrollCompatible = dw? TRUE : FALSE; } cbValue = sizeof(dw); hr = RegQueryValueEx( hkeyCN, g_wszRegEnforceX500NameLengths, NULL, // lpdwReserved &dwType, (BYTE *) &dw, &cbValue); if (S_OK == hr && REG_DWORD == dwType && sizeof(dw) == cbValue) { g_fEnforceRDNNameLengths = dw? TRUE : FALSE; } cbValue = sizeof(dw); hr = RegQueryValueEx( hkeyCN, wszREGCRLEDITFLAGS, NULL, // lpdwReserved &dwType, (BYTE *) &dw, &cbValue); if (S_OK == hr && REG_DWORD == dwType && sizeof(dw) == cbValue) { g_CRLEditFlags = dw; } cbValue = sizeof(dw); hr = RegQueryValueEx( hkeyCN, wszREGKRAFLAGS, NULL, // lpdwReserved &dwType, (BYTE *) &dw, &cbValue); if (S_OK == hr && REG_DWORD == dwType && sizeof(dw) == cbValue) { g_KRAFlags = dw; } cbValue = sizeof(dw); hr = RegQueryValueEx( hkeyCN, wszREGKRACERTCOUNT, NULL, // lpdwReserved &dwType, (BYTE *) &dw, &cbValue); if (S_OK == hr && REG_DWORD == dwType && sizeof(dw) == cbValue) { g_cKRACertsRoundRobin = dw; } hr = coreRegGetTimePeriod( hkeyCN, g_wszRegValidityPeriodCount, g_wszRegValidityPeriodString, &g_enumValidityPeriod, &g_lValidityPeriodCount); if (S_OK != hr) { LogMsg = MSG_E_REG_BAD_CERT_PERIOD; _JumpError(hr, error, "Bad Registry ValidityPeriod"); } hr = coreRegGetTimePeriod( hkeyCN, g_wszRegCAXchgValidityPeriodCount, g_wszRegCAXchgValidityPeriodString, &g_enumCAXchgValidityPeriod, &g_lCAXchgValidityPeriodCount); _PrintIfError(hr, "Bad Registry CA Xchg Validity Period"); hr = coreRegGetTimePeriod( hkeyCN, g_wszRegCAXchgOverlapPeriodCount, g_wszRegCAXchgOverlapPeriodString, &g_enumCAXchgOverlapPeriod, &g_lCAXchgOverlapPeriodCount); _PrintIfError(hr, "Bad Registry CA Xchg Overlap Period"); cbValue = sizeof(dw); hr = RegQueryValueEx( hkeyCN, g_wszRegForceTeletex, NULL, // lpdwReserved &dwType, (BYTE *) &dw, &cbValue); if (S_OK == hr && REG_DWORD == dwType && sizeof(dw) == cbValue) { switch (ENUM_TELETEX_MASK & dw) { case ENUM_TELETEX_OFF: case ENUM_TELETEX_ON: g_fForceTeletex = (enum ENUM_FORCETELETEX) (ENUM_TELETEX_MASK & dw); break; default: g_fForceTeletex = ENUM_TELETEX_AUTO; break; } if (ENUM_TELETEX_UTF8 & dw) { *(DWORD *) &g_fForceTeletex |= ENUM_TELETEX_UTF8; } } cbValue = sizeof(g_CAType); hr = RegQueryValueEx( hkeyCN, wszREGCATYPE, NULL, &dwType, (BYTE *) &g_CAType, &cbValue); _JumpIfError(hr, error, "RegQueryValueEx"); cbValue = sizeof(g_fUseDS); hr = RegQueryValueEx( hkeyCN, wszREGCAUSEDS, NULL, &dwType, (BYTE *) &g_fUseDS, &cbValue); _JumpIfError(hr, error, "RegQueryValueEx"); cbValue = sizeof(g_wszParentConfig) - 2 * sizeof(WCHAR); hr = RegQueryValueEx( hkeyCN, wszREGPARENTCAMACHINE, NULL, &dwType, (BYTE *) g_wszParentConfig, &cbValue); if (S_OK == hr && REG_SZ == dwType) { g_wszParentConfig[cbValue / sizeof(WCHAR)] = L'\0'; pwsz = &g_wszParentConfig[wcslen(g_wszParentConfig)]; *pwsz++ = L'\\'; *pwsz = L'\0'; cbValue = sizeof(g_wszParentConfig) - (SAFE_SUBTRACT_POINTERS(pwsz, g_wszParentConfig) + 1) * sizeof(WCHAR); hr = RegQueryValueEx( hkeyCN, wszREGPARENTCANAME, NULL, &dwType, (BYTE *) pwsz, &cbValue); if (S_OK == hr && REG_SZ == dwType) { pwsz[cbValue / sizeof(WCHAR)] = L'\0'; } else { g_wszParentConfig[0] = L'\0'; } } else { g_wszParentConfig[0] = L'\0'; } cbValue = sizeof(dw); hr = RegQueryValueEx( hkeyCN, g_wszRegClockSkewMinutes, NULL, // lpdwReserved &dwType, (BYTE *) &dw, &cbValue); if (S_OK == hr && REG_DWORD == dwType && sizeof(dw) == cbValue) { g_dwClockSkewMinutes = dw; } cbValue = sizeof(dw); hr = RegQueryValueEx( hkeyCN, g_wszRegMaxIncomingMessageSize, NULL, // lpdwReserved &dwType, (BYTE *) &dw, &cbValue); if (S_OK == hr && REG_DWORD == dwType && sizeof(dw) == cbValue) { g_cbMaxIncomingMessageSize = dw; } // load CRL globals hr = CRLInit(g_wszSanitizedName); _JumpIfError(hr, error, "CRLInitializeGlobals"); cbValue = sizeof(dw); hr = RegQueryValueEx( hkeyCN, g_wszRegLogLevel, NULL, // lpdwReserved &dwType, (BYTE *) &dw, &cbValue); if (S_OK == hr && REG_DWORD == dwType && sizeof(dw) == cbValue) { g_dwLogLevel = dw; } cbValue = sizeof(dw); hr = RegQueryValueEx( hkeyCN, g_wszRegHighSerial, NULL, // lpdwReserved &dwType, (BYTE *) &dw, &cbValue); if (S_OK == hr && REG_DWORD == dwType && sizeof(dw) == cbValue) { g_dwHighSerial = dw; } cbValue = sizeof(dw); hr = RegQueryValueEx( hkeyCN, wszLOCKICERTREQUEST, NULL, // lpdwReserved &dwType, (BYTE *) &dw, &cbValue); if (S_OK == hr && REG_DWORD == dwType && sizeof(dw) == cbValue) { g_fLockICertRequest = dw; } hr = g_CASD.Initialize(g_wszSanitizedName); _JumpIfError(hr, error, "CProtectedSecurityDescriptor::Initialize"); g_CASD.ImportResourceStrings(g_pwszAuditResources); // Functionality available only on advanced server: // - auditing // - restricted officers // - enforce role separation if (g_fAdvancedServer) { hr = g_OfficerRightsSD.Initialize(g_wszSanitizedName); _JumpIfError(hr, error, "CProtectedSecurityDescriptor::Initialize"); g_OfficerRightsSD.ImportResourceStrings(g_pwszAuditResources); hr = AuditSettings.LoadFilter(g_wszSanitizedName); if (HRESULT_FROM_WIN32(ERROR_FILE_NOT_FOUND) != hr) { _JumpIfError(hr, error, "CAuditEvent::LoadFilter"); } hr = AuditSettings.RoleSeparationFlagLoad(g_wszSanitizedName); if (HRESULT_FROM_WIN32(ERROR_FILE_NOT_FOUND) != hr) { _JumpIfError(hr, error, "CAuditEvent::RoleSeparationFlagLoad"); } } g_dwAuditFilter = AuditSettings.GetFilter(); hr = PKCSSetup(g_wszCommonName, g_wszSanitizedName); if (S_OK != hr) { fLogError = FALSE; // PKCSSetup logs a specific error _JumpError(hr, error, "PKCSSetup"); } hr = CertificateInterfaceInit( &ServerCallBacks, sizeof(ServerCallBacks)); if (S_OK != hr) { LogMsg = MSG_CERTIF_MISMATCH; _JumpError(hr, error, "CertificateInterfaceInit"); } hr = ComInit(); _JumpIfError(hr, error, "ComInit"); hr = RequestInitCAPropertyInfo(); _JumpIfError(hr, error, "RequestInitCAPropertyInfo"); // We must have a policy module to continue. hr = PolicyInit(g_wszCommonName, g_wszSanitizedName); if (S_OK != hr) { LogMsg = MSG_NO_POLICY; _JumpError(hr, error, "PolicyInit"); } CSASSERT(g_fEnablePolicy); // On error, silently leave exit module(s) disabled. hr = ExitInit(g_wszCommonName, g_wszSanitizedName); _PrintIfError(hr, "ExitInit"); if (NULL != g_pwszzSubjectTemplate) { hr = PKCSSetSubjectTemplate(g_pwszzSubjectTemplate); if (S_OK != hr) { LogMsg = MSG_E_REG_BAD_SUBJECT_TEMPLATE; pwszLog = g_wszSanitizedName; _JumpError(hr, error, "PKCSSetSubjectTemplate"); } } hr = myGetCertRegMultiStrValue( g_wszSanitizedName, NULL, NULL, wszSECUREDATTRIBUTES, &g_wszzSecuredAttributes); if (S_OK != hr) { // Force defaults g_wszzSecuredAttributes = (LPWSTR)g_wszzSecuredAttributesDefault; } if (g_fServerUpgraded) { DBGPRINT(( DBG_SS_CERTSRV, "CoreInit: clearing SETUP_SERVER_UPGRADED_FLAG\n")); hr = SetSetupStatus(NULL, SETUP_SERVER_UPGRADED_FLAG, FALSE); _PrintIfError(hr, "SetSetupStatus"); } fLogError = FALSE; error: if (fLogError) { LogEventString(EVENTLOG_ERROR_TYPE, LogMsg, pwszLog); } if (NULL != hkeyCN) { RegCloseKey(hkeyCN); } if (NULL != hkeyConfig) { RegCloseKey(hkeyConfig); } if (S_OK != hr) { CoreTerminate(); } return(hr); } HRESULT CoreSetRequestDispositionFields( IN ICertDBRow *prow, IN DWORD ErrCode, IN DWORD Disposition, IN WCHAR const *pwszDisposition) { HRESULT hr; hr = CoreSetDisposition(prow, Disposition); _JumpIfError(hr, error, "CoreSetDisposition"); hr = prow->SetProperty( g_wszPropRequestStatusCode, PROPTYPE_LONG | PROPCALLER_SERVER | PROPTABLE_REQUEST, sizeof(ErrCode), (BYTE const *) &ErrCode); _JumpIfError(hr, error, "SetProperty(status code)"); if (NULL != pwszDisposition) { hr = prow->SetProperty( g_wszPropRequestDispositionMessage, PROPTYPE_STRING | PROPCALLER_SERVER | PROPTABLE_REQUEST, MAXDWORD, (BYTE const *) pwszDisposition); _JumpIfError(hr, error, "SetProperty(disposition message)"); } error: return(hr); } HRESULT coreCreateRequest( IN DWORD dwFlags, IN WCHAR const *pwszUserName, IN DWORD cbRequest, IN BYTE const *pbRequest, IN WCHAR const *pwszAttributes, IN DWORD dwComContextIndex, OUT ICertDBRow **pprow, // may return non-NULL on error IN OUT CERTSRV_RESULT_CONTEXT *pResult) { HRESULT hr; DWORD dwRequestFlags; DWORD cb; ICertDBRow *prow = NULL; hr = g_pCertDB->OpenRow(PROPTABLE_REQCERT, 0, NULL, pprow); _JumpIfError(hr, error, "OpenRow"); prow = *pprow; hr = PropSetRequestTimeProperty(prow, g_wszPropRequestSubmittedWhen); _JumpIfError(hr, error, "PropSetRequestTimeProperty"); hr = CoreSetDisposition(prow, DB_DISP_ACTIVE); _JumpIfError(hr, error, "CoreSetDisposition"); hr = prow->SetProperty( g_wszPropRequestType, PROPTYPE_LONG | PROPCALLER_SERVER | PROPTABLE_REQUEST, sizeof(dwFlags), (BYTE const *) &dwFlags); _JumpIfError(hr, error, "SetProperty(type)"); if (L'\0' != *pwszUserName) { hr = prow->SetProperty( g_wszPropRequesterName, PROPTYPE_STRING | PROPCALLER_SERVER | PROPTABLE_REQUEST, MAXDWORD, (BYTE const *) pwszUserName); _JumpIfError(hr, error, "SetProperty(requester)"); hr = prow->SetProperty( g_wszPropCallerName, PROPTYPE_STRING | PROPCALLER_SERVER | PROPTABLE_REQUEST, MAXDWORD, (BYTE const *) pwszUserName); _JumpIfError(hr, error, "SetProperty(caller)"); } if (NULL != pwszAttributes && L'\0' != *pwszAttributes) { WCHAR awcAttributes[CCH_DBMAXTEXT_ATTRSTRING + 1]; wcsncpy(awcAttributes, pwszAttributes, CCH_DBMAXTEXT_ATTRSTRING); awcAttributes[CCH_DBMAXTEXT_ATTRSTRING] = L'\0'; if (wcslen(pwszAttributes) > CCH_DBMAXTEXT_ATTRSTRING) { DBGPRINT(( DBG_SS_CERTSRV, "coreCreateRequest: truncating Attributes %u -> %u chars\n", wcslen(pwszAttributes), CCH_DBMAXTEXT_ATTRSTRING)); } hr = prow->SetProperty( g_wszPropRequestAttributes, PROPTYPE_STRING | PROPCALLER_SERVER | PROPTABLE_REQUEST, MAXDWORD, (BYTE const *) awcAttributes); _JumpIfError(hr, error, "SetProperty(attrib)"); } hr = PropParseRequest(prow, dwFlags, cbRequest, pbRequest, pResult); _JumpIfError(hr, error, "PropParseRequest"); hr = PKCSParseAttributes( prow, pwszAttributes, FALSE, PROPTABLE_REQUEST, NULL); _JumpIfError(hr, error, "PKCSParseAttributes"); hr = prow->CopyRequestNames(); // after parsing request attributes! _JumpIfError(hr, error, "CopyRequestNames"); hr = PKCSVerifyChallengeString(prow); _JumpIfError(hr, error, "PKCSVerifyChallengeString"); cb = sizeof(dwRequestFlags); hr = prow->GetProperty( g_wszPropRequestFlags, PROPTYPE_LONG | PROPCALLER_SERVER | PROPTABLE_REQUEST, &cb, (BYTE *) &dwRequestFlags); _JumpIfError(hr, error, "GetProperty"); if (CR_FLG_ENROLLONBEHALFOF & dwRequestFlags) { hr = coreGetComContextUserDNFromSamName( TRUE, // fDeleteUserDNOnly NULL, // pwszSamName 0, // Context dwComContextIndex, NULL); // pwszDN _JumpIfError(hr, error, "coreGetComContextUserDNFromSamName"); } hr = S_OK; error: return(hr); } HRESULT coreFetchCertificate( IN ICertDBRow *prow, OUT CERTTRANSBLOB *pctbCert) // CoTaskMem* { HRESULT hr; DWORD cbProp; pctbCert->pb = NULL; cbProp = 0; hr = prow->GetProperty( g_wszPropRawCertificate, PROPTYPE_BINARY | PROPCALLER_SERVER | PROPTABLE_CERTIFICATE, &cbProp, NULL); _JumpIfError(hr, error, "GetProperty(raw cert size)"); pctbCert->pb = (BYTE *) CoTaskMemAlloc(cbProp); if (NULL == pctbCert->pb) { hr = E_OUTOFMEMORY; _JumpError(hr, error, "CoTaskMemAlloc(raw cert)"); } hr = prow->GetProperty( g_wszPropRawCertificate, PROPTYPE_BINARY | PROPCALLER_SERVER | PROPTABLE_CERTIFICATE, &cbProp, pctbCert->pb); _JumpIfError(hr, error, "GetProperty(raw cert)"); error: if (S_OK != hr && NULL != pctbCert->pb) { CoTaskMemFree(pctbCert->pb); pctbCert->pb = NULL; } pctbCert->cb = cbProp; return(hr); } HRESULT coreRetrievePending( IN ICertDBRow *prow, IN BOOL fIncludeCRLs, OUT WCHAR **ppwszDisposition, // LocalAlloc OUT CACTX **ppCAContext, IN OUT CERTSRV_RESULT_CONTEXT *pResult) // CoTaskMem* { HRESULT hr; DWORD cbProp; WCHAR *pwszDisposition = NULL; DWORD Disposition; HRESULT hrRequest; BOOL fIssued; *ppwszDisposition = NULL; *ppCAContext = NULL; cbProp = sizeof(Disposition); hr = prow->GetProperty( g_wszPropRequestDisposition, PROPTYPE_LONG | PROPCALLER_SERVER | PROPTABLE_REQUEST, &cbProp, (BYTE *) &Disposition); _JumpIfError(hr, error, "GetProperty(disposition)"); cbProp = sizeof(hrRequest); hr = prow->GetProperty( g_wszPropRequestStatusCode, PROPTYPE_LONG | PROPCALLER_SERVER | PROPTABLE_REQUEST, &cbProp, (BYTE *) &hrRequest); _JumpIfError(hr, error, "GetProperty(status code)"); hr = PKCSGetProperty( prow, g_wszPropRequestDispositionMessage, PROPTYPE_STRING | PROPCALLER_SERVER | PROPTABLE_REQUEST, NULL, (BYTE **) &pwszDisposition); _PrintIfError2(hr, "PKCSGetProperty", CERTSRV_E_PROPERTY_EMPTY); fIssued = FALSE; switch (Disposition) { FILETIME FileTime; case DB_DISP_ACTIVE: case DB_DISP_PENDING: *pResult->pdwDisposition = CR_DISP_UNDER_SUBMISSION; break; case DB_DISP_ISSUED: case DB_DISP_CA_CERT: case DB_DISP_CA_CERT_CHAIN: hr = CERTSRV_E_PROPERTY_EMPTY; if (DB_DISP_CA_CERT == Disposition && IsRootCA(g_CAType)) { cbProp = sizeof(FileTime); hr = prow->GetProperty( g_wszPropRequestRevokedEffectiveWhen, PROPTYPE_DATE | PROPCALLER_SERVER | PROPTABLE_REQUEST, &cbProp, (BYTE *) &FileTime); } if (CERTSRV_E_PROPERTY_EMPTY == hr) { *pResult->pdwDisposition = CR_DISP_ISSUED; fIssued = TRUE; break; } // FALLTHROUGH case DB_DISP_REVOKED: *pResult->pdwDisposition = CR_DISP_REVOKED; fIssued = TRUE; break; case DB_DISP_ERROR: *pResult->pdwDisposition = CR_DISP_ERROR; break; case DB_DISP_DENIED: *pResult->pdwDisposition = CR_DISP_DENIED; if (FAILED(hrRequest)) { *pResult->pdwDisposition = hrRequest; } break; default: *pResult->pdwDisposition = CR_DISP_INCOMPLETE; break; } if (fIssued) { BOOL fErrorLogged = FALSE; hr = coreFetchCertificate(prow, pResult->pctbCert); _JumpIfError(hr, error, "coreFetchCertificate"); CSASSERT(NULL != pResult->pctbCert && NULL != pResult->pctbCert->pb); hr = PKCSCreateCertificate( prow, Disposition, fIncludeCRLs, &fErrorLogged, ppCAContext, pResult); CSASSERT(!fErrorLogged); if (S_OK != hr) { if (CERTLOG_ERROR <= g_dwLogLevel) { LogEventHResult( EVENTLOG_ERROR_TYPE, MSG_E_CANNOT_BUILD_CERT_OR_CHAIN, hr); } _JumpError(hr, error, "PKCSCreateCertificate"); } } *ppwszDisposition = pwszDisposition; pwszDisposition = NULL; hr = S_OK; error: if (S_OK != hr && NULL != pResult->pctbCert->pb) { CoTaskMemFree(pResult->pctbCert->pb); pResult->pctbCert->pb = NULL; } if (NULL != pwszDisposition) { LocalFree(pwszDisposition); } return(hr); } VOID CoreLogRequestStatus( IN ICertDBRow *prow, IN DWORD LogMsg, IN DWORD ErrCode, OPTIONAL IN WCHAR const *pwszDisposition) { HRESULT hr; DWORD cbProp; WCHAR awcSubject[1024]; WCHAR const *pwszSubject; WCHAR wszRequestId[11 + 1]; WCHAR awchr[cwcHRESULTSTRING]; WORD cString = 0; WCHAR const *apwsz[4]; DWORD ReqId; DWORD infotype = EVENTLOG_INFORMATION_TYPE; WCHAR const *pwszMessageText = NULL; DWORD LogMsg2; prow->GetRowId(&ReqId); wsprintf(wszRequestId, L"%u", ReqId); apwsz[cString++] = wszRequestId; LogMsg2 = LogMsg; switch (LogMsg) { case MSG_DN_CERT_ISSUED: LogMsg2 = MSG_DN_CERT_ISSUED_WITH_INFO; break; case MSG_DN_CERT_PENDING: LogMsg2 = MSG_DN_CERT_PENDING_WITH_INFO; break; case MSG_DN_CERT_ADMIN_DENIED: LogMsg2 = MSG_DN_CERT_ADMIN_DENIED_WITH_INFO; break; case MSG_DN_CERT_DENIED: LogMsg2 = MSG_DN_CERT_DENIED_WITH_INFO; infotype = EVENTLOG_WARNING_TYPE; break; case MSG_E_PROCESS_REQUEST_FAILED: LogMsg2 = MSG_E_PROCESS_REQUEST_FAILED_WITH_INFO; infotype = EVENTLOG_ERROR_TYPE; break; } if (EVENTLOG_INFORMATION_TYPE != infotype) { if (S_OK == ErrCode) { ErrCode = SEC_E_CERT_UNKNOWN; // unknown error } pwszMessageText = myGetErrorMessageText(ErrCode, TRUE); if (NULL == pwszMessageText) { pwszMessageText = myHResultToStringRaw(awchr, ErrCode); } apwsz[cString++] = pwszMessageText; } cbProp = sizeof(awcSubject); hr = prow->GetProperty( g_wszPropSubjectDistinguishedName, PROPTYPE_STRING | PROPCALLER_SERVER | PROPTABLE_CERTIFICATE, &cbProp, (BYTE *) awcSubject); if (CERTSRV_E_PROPERTY_EMPTY == hr) { cbProp = sizeof(awcSubject); hr = prow->GetProperty( g_wszPropSubjectDistinguishedName, PROPTYPE_STRING | PROPCALLER_SERVER | PROPTABLE_REQUEST, &cbProp, (BYTE *) awcSubject); } if (CERTSRV_E_PROPERTY_EMPTY == hr) { cbProp = sizeof(awcSubject); hr = prow->GetProperty( g_wszPropRequesterName, PROPTYPE_STRING | PROPCALLER_SERVER | PROPTABLE_REQUEST, &cbProp, (BYTE *) awcSubject); } pwszSubject = awcSubject; if (S_OK != hr) { _PrintError(hr, "GetProperty(DN/Requester)"); pwszSubject = g_pwszUnknownSubject; } apwsz[cString++] = pwszSubject; if (NULL != pwszDisposition) { LogMsg = LogMsg2; apwsz[cString++] = pwszDisposition; } if (CERTLOG_VERBOSE <= g_dwLogLevel || (EVENTLOG_WARNING_TYPE == infotype && CERTLOG_WARNING <= g_dwLogLevel) || (EVENTLOG_ERROR_TYPE == infotype && CERTLOG_ERROR <= g_dwLogLevel)) { LogEvent(infotype, LogMsg, cString, apwsz); } #if 0 == i386 # define IOBUNALIGNED(pf) ((sizeof(WCHAR) - 1) & (DWORD) (ULONG_PTR) (pf)->_ptr) # define ALIGNIOB(pf) \ { \ if (IOBUNALIGNED(pf)) \ { \ fflush(pf); /* fails when running as a service */ \ } \ if (IOBUNALIGNED(pf)) \ { \ fprintf(pf, " "); \ fflush(pf); \ } \ } #else # define IOBUNALIGNED(pf) FALSE # define ALIGNIOB(pf) #endif { BOOL fRetried = FALSE; while (TRUE) { ALIGNIOB(stdout); __try { wprintf( // L"\nCertSrv Request %u: rc=%x: %ws: %ws '%ws'\n" g_pwszPrintfCertRequestDisposition, ReqId, ErrCode, NULL != pwszMessageText? pwszMessageText : L"", NULL != pwszDisposition? pwszDisposition : L"", pwszSubject); hr = S_OK; } __except(hr = myHEXCEPTIONCODE(), EXCEPTION_EXECUTE_HANDLER) { } if (S_OK == hr || fRetried || !IOBUNALIGNED(stdout)) { break; } ALIGNIOB(stdout); fRetried = TRUE; } } if (NULL != pwszMessageText && awchr != pwszMessageText) { LocalFree(const_cast(pwszMessageText)); } } WCHAR * CoreBuildDispositionString( OPTIONAL IN WCHAR const *pwszDispositionBase, OPTIONAL IN WCHAR const *pwszUserName, OPTIONAL IN WCHAR const *pwszDispositionDetail, OPTIONAL IN WCHAR const *pwszBy, IN HRESULT hrFail, IN BOOL fPublishError) { DWORD cwc = 0; WCHAR *pwsz = NULL; WCHAR const *pwszMessageText = NULL; WCHAR awchr[cwcHRESULTSTRING]; if (NULL == pwszUserName) { pwszUserName = L""; } if (NULL != pwszDispositionBase) { cwc += wcslen(pwszDispositionBase) + wcslen(pwszUserName); } if (NULL != pwszDispositionDetail) { if (0 != cwc) { cwc += 2; // spaces } cwc += wcslen(pwszDispositionDetail); } if (NULL != pwszBy) { if (0 != cwc) { cwc += 2; // spaces } cwc += wcslen(pwszBy) + wcslen(pwszUserName); } if (S_OK != hrFail) { pwszMessageText = myGetErrorMessageText(hrFail, TRUE); if (NULL == pwszMessageText) { pwszMessageText = myHResultToStringRaw(awchr, hrFail); } if (0 != cwc) { cwc += 2; // spaces } if (fPublishError) { cwc += wcslen(g_pwszPublishError); cwc += 2; // spaces } cwc += wcslen(pwszMessageText); } if (0 != cwc) { pwsz = (WCHAR *) LocalAlloc(LMEM_FIXED, (cwc + 1) * sizeof(WCHAR)); if (NULL != pwsz) { pwsz[0] = L'\0'; if (NULL != pwszDispositionBase) { wsprintf(pwsz, pwszDispositionBase, pwszUserName); } if (NULL != pwszDispositionDetail) { if (L'\0' != pwsz[0]) { wcscat(pwsz, L" "); } wcscat(pwsz, pwszDispositionDetail); } if (NULL != pwszBy) { if (L'\0' != pwsz[0]) { wcscat(pwsz, L" "); } wsprintf(&pwsz[wcslen(pwsz)], pwszBy, pwszUserName); } if (S_OK != hrFail) { if (L'\0' != pwsz[0] && L'\n' != pwsz[wcslen(pwsz) - 1]) { wcscat(pwsz, L" "); } if (fPublishError) { wcscat(pwsz, g_pwszPublishError); wcscat(pwsz, L" "); } wcscat(pwsz, pwszMessageText); } } CSASSERT(wcslen(pwsz) <= cwc); } //error: if (NULL != pwszMessageText && awchr != pwszMessageText) { LocalFree(const_cast(pwszMessageText)); } return(pwsz); } VOID coreLogPublishError( IN DWORD RequestId, IN WCHAR const *pwszSamName, IN LDAP *pld, IN WCHAR const *pwszDN, OPTIONAL IN WCHAR const *pwszError, IN HRESULT hrPublish) { HRESULT hr; WCHAR const *apwsz[6]; WORD cpwsz; WCHAR wszRequestId[11 + 1]; WCHAR awchr[cwcHRESULTSTRING]; WCHAR const *pwszMessageText = NULL; WCHAR *pwszHostName = NULL; DWORD LogMsg; wsprintf(wszRequestId, L"%u", RequestId); if (NULL != pld) { myLdapGetDSHostName(pld, &pwszHostName); } pwszMessageText = myGetErrorMessageText(hrPublish, TRUE); if (NULL == pwszMessageText) { pwszMessageText = myHResultToStringRaw(awchr, hrPublish); } cpwsz = 0; apwsz[cpwsz++] = wszRequestId; apwsz[cpwsz++] = pwszDN; apwsz[cpwsz++] = pwszMessageText; LogMsg = MSG_E_CERT_PUBLICATION; if (NULL != pwszHostName) { LogMsg = MSG_E_CERT_PUBLICATION_HOST_NAME; } else { pwszHostName = L""; } apwsz[cpwsz++] = pwszHostName; apwsz[cpwsz++] = NULL != pwszError? L"\n" : L""; apwsz[cpwsz++] = NULL != pwszError? pwszError : L""; CSASSERT(ARRAYSIZE(apwsz) >= cpwsz); if (CERTLOG_WARNING <= g_dwLogLevel) { hr = LogEvent(EVENTLOG_WARNING_TYPE, LogMsg, cpwsz, apwsz); _PrintIfError(hr, "LogEvent"); } //error: if (NULL != pwszMessageText && awchr != pwszMessageText) { LocalFree(const_cast(pwszMessageText)); } } HRESULT corePublishKRACertificate( IN DWORD RequestId, IN WCHAR const *pwszSamName, IN CERT_CONTEXT const *pcc) { HRESULT hr; LDAP *pld = NULL; HCERTSTORE hStore = NULL; DWORD dwDisposition; WCHAR *pwszError = NULL; hr = myRobustLdapBind(&pld, FALSE); _JumpIfError(hr, error, "myRobustLdapBind"); hStore = CertOpenStore( CERT_STORE_PROV_SYSTEM_W, X509_ASN_ENCODING, NULL, // hProv CERT_SYSTEM_STORE_LOCAL_MACHINE, wszKRA_CERTSTORE); if (NULL == hStore) { hr = myHLastError(); _JumpErrorStr(hr, error, "CertOpenStore", wszKRA_CERTSTORE); } // It's a new cert. CERT_STORE_ADD_ALWAYS is faster. if (!CertAddCertificateContextToStore( hStore, pcc, CERT_STORE_ADD_ALWAYS, NULL)) { hr = myHLastError(); _JumpError(hr, error, "CertAddCertificateContextToStore"); } hr = myLdapPublishCertToDS( pld, pcc, g_pwszKRAPublishURL, wszDSKRACERTATTRIBUTE, LPC_KRAOBJECT, &dwDisposition, &pwszError); _JumpIfError(hr, error, "myLdapPublishCertToDS"); error: if (S_OK != hr) { coreLogPublishError( RequestId, pwszSamName, pld, g_pwszKRAPublishURL, pwszError, hr); } if (NULL != pwszError) { LocalFree(pwszError); } if (NULL != hStore) { CertCloseStore(hStore, CERT_CLOSE_STORE_CHECK_FLAG); } if (NULL != pld) { ldap_unbind(pld); } return(hr); } HRESULT corePublishCrossCertificate( IN DWORD RequestId, IN WCHAR const *pwszSamName, IN CERT_CONTEXT const *pcc) { HRESULT hr; HRESULT hr2; LDAP *pld = NULL; DWORD dwDisposition; WCHAR *pwszError = NULL; WCHAR const *pwszDN = g_pwszAIACrossCertPublishURL; CAutoLPWSTR pwszSubjectHash; CAutoLPWSTR pwszSubjectDN, pwszSubject; LPCWSTR pcwszFormatDN = L"LDAP:///CN=%s%s"; DWORD dwFlags = LPC_CAOBJECT; bool fPublishToSubject = false; hr = myRobustLdapBind(&pld, FALSE); _JumpIfError(hr, error, "myRobustLdapBind"); // Attempt to build the location based on subject name: // // LDAP:///CN=, // // In case of errors, fall back to publishing to CA AIA object hr = CertNameToHashString( &pcc->pCertInfo->Subject, &pwszSubjectHash); if(S_OK==hr) { WCHAR *pchRelDN = wcschr(pwszDN, L','); if(pchRelDN) { pwszSubjectDN = (LPWSTR)LocalAlloc(LMEM_FIXED, (wcslen(pwszSubjectHash)+ wcslen(pchRelDN)+ wcslen(pcwszFormatDN))*sizeof(WCHAR)); if(pwszSubjectDN) { wsprintf(pwszSubjectDN, pcwszFormatDN, (LPCWSTR)pwszSubjectHash, pchRelDN); pwszDN = pwszSubjectDN; // we'll publish to subject's DS object which might not exist dwFlags |= LPC_CREATEOBJECT; fPublishToSubject = true; } } } hr = myLdapPublishCertToDS( pld, pcc, pwszDN, wszDSCROSSCERTPAIRATTRIBUTE, dwFlags, &dwDisposition, &pwszError); if (S_OK != hr) { _PrintErrorStr(hr, "myLdapPublishCertToDS", pwszDN); if (HRESULT_FROM_WIN32(ERROR_FILE_NOT_FOUND) == hr && IsRootCA(g_CAType)) { hr = S_OK; } } _JumpIfErrorStr(hr, error, "myLdapPublishCertToDS", pwszDN); if(fPublishToSubject) { hr = myCertNameToStr( X509_ASN_ENCODING, &pcc->pCertInfo->Subject, CERT_X500_NAME_STR, //| CERT_NAME_STR_REVERSE_FLAG, &pwszSubject); _JumpIfError(hr, error, "myCertNameToStr"); hr = myLDAPSetStringAttribute( pld, pwszDN, CA_PROP_CERT_DN, pwszSubject, &dwDisposition, &pwszError ); _JumpIfErrorStr(hr, error, "myLDAPSetStringAttribute", pwszDN); } error: if (S_OK != hr) { coreLogPublishError(RequestId, pwszSamName, pld, pwszDN, pwszError, hr); } if (NULL != pwszError) { LocalFree(pwszError); } if (NULL != pld) { ldap_unbind(pld); } return(hr); } VOID coreFreeDSCacheEntry( IN OUT DSCACHE *pdsc) { if (NULL != pdsc->pld) { ldap_unbind(pdsc->pld); pdsc->pld = NULL; } if (NULL != pdsc->pwszDomain) { LocalFree(pdsc->pwszDomain); pdsc->pwszDomain = NULL; } LocalFree(pdsc); } VOID coreFreeDSCache() { DSCACHE *pdsc; // only called during shutdown while (NULL != g_DSCache) { // remove from head of list pdsc = g_DSCache; g_DSCache = pdsc->pdscNext; coreFreeDSCacheEntry(pdsc); } coreDSUnBind(TRUE); } HRESULT coreGetCachedDS( IN WCHAR const *pwszSamName, IN BOOL fRediscover, OUT LDAP **ppld) { HRESULT hr; ULONG ldaperr; DS_NAME_RESULTW *pNameResults = NULL; WCHAR *pwszDomain = NULL; DSCACHE *pdsc = NULL; DSCACHE **ppdscPrev; DSCACHE *pdscFree = NULL; DWORD cwc; WCHAR *pwsz; CSASSERT(NULL != ppld); *ppld = NULL; // Copy domain out of the SamName pwsz = wcschr(pwszSamName, L'\\'); if (NULL != pwsz) { cwc = SAFE_SUBTRACT_POINTERS(pwsz, pwszSamName); } else { cwc = wcslen(pwszSamName); } pwszDomain = (WCHAR *) LocalAlloc(LMEM_FIXED, (cwc + 1) * sizeof(WCHAR)); if (NULL == pwszDomain) { hr = E_OUTOFMEMORY; _JumpError(hr, error, "LocalAlloc"); } CopyMemory(pwszDomain, pwszSamName, sizeof(WCHAR) * cwc); pwszDomain[cwc] = L'\0'; ppdscPrev = &g_DSCache; for (pdsc = g_DSCache; NULL != pdsc; pdsc = pdsc->pdscNext) { if (0 == lstrcmpi(pdsc->pwszDomain, pwszDomain)) { // should we toss the cached entry? if (fRediscover) { // unhook from cache list & free the entry, *ppdscPrev = pdsc->pdscNext; coreFreeDSCacheEntry(pdsc); pdsc = NULL; } break; } ppdscPrev = &pdsc->pdscNext; } if (fRediscover) { coreDSUnBind(FALSE); } if (NULL == pdsc) { pdsc = (DSCACHE *) LocalAlloc( LMEM_FIXED | LMEM_ZEROINIT, sizeof(*pdsc)); if (NULL == pdsc) { hr = E_OUTOFMEMORY; _JumpError(hr, error, "LocalAlloc"); } pdscFree = pdsc; pdsc->pwszDomain = pwszDomain; pwszDomain = NULL; } CSASSERT(NULL != pdsc->pwszDomain); // if we don't have an ldap handle yet, get a good one if (NULL == pdsc->pld) { CSASSERT(pdsc == pdscFree); hr = myRobustLdapBindEx( FALSE, // fGC FALSE, // fRediscover LDAP_VERSION2, // uVersion pwszDomain, // pwszDomainName &pdsc->pld, // ppld NULL); // ppwszForestDNSName _JumpIfError(hr, error, "Policy:myRobustLdapBindEx"); // link into list: we have all data necessary pdsc->pdscNext = g_DSCache; g_DSCache = pdsc; pdscFree = NULL; } *ppld = pdsc->pld; error: if (NULL != pwszDomain) { LocalFree(pwszDomain); } if (NULL != pNameResults) { DsFreeNameResult(pNameResults); } if (NULL != pdscFree) { coreFreeDSCacheEntry(pdscFree); } return(hr); } HRESULT corePublishIssuedCertificate( IN DWORD RequestId, IN DWORD dwComContextIndex, IN WCHAR const *pwszSamName, IN CERT_CONTEXT const *pcc, IN DWORD dwObjectType) // LPC_* { HRESULT hr; WCHAR *pwszSamNamePatched = NULL; WCHAR const *pwszUserName; DWORD cbProp; LDAP *pld = NULL; WCHAR const *pwszDN; DWORD dwDisposition; BOOL fCritSecEntered = FALSE; WCHAR *pwszError = NULL; hr = myAddDomainName(pwszSamName, &pwszSamNamePatched, &pwszUserName); _JumpIfError(hr, error, "myAddDomainName"); if (NULL != pwszSamNamePatched) { pwszSamName = pwszSamNamePatched; } EnterCriticalSection(&g_critsecDSCache); fCritSecEntered = TRUE; __try { BOOL fRediscover = FALSE; hr = coreGetComContextUserDNFromSamName( FALSE, // fDeleteUserDNOnly pwszSamName, 0, // Context dwComContextIndex, &pwszDN); _JumpIfError(hr, error, "coreGetComContextUserDNFromSamName"); while (TRUE) { if (NULL != pwszError) { LocalFree(pwszError); pwszError = NULL; } pld = NULL; hr = coreGetCachedDS(pwszSamName, fRediscover, &pld); if (S_OK != hr) { _PrintErrorStr( hr, "coreGetCachedDS", fRediscover? L"noncached" : L"cached"); if (fRediscover) { _leave; } } else { hr = myLdapPublishCertToDS( pld, pcc, pwszDN, wszDSUSERCERTATTRIBUTE, dwObjectType, // LPC_* &dwDisposition, &pwszError); _PrintIfErrorStr(hr, "myLdapPublishCertToDS", pwszDN); if (fRediscover || S_OK == hr) { break; } if (!myLdapRebindRequired(dwDisposition, pld)) { _LeaveError(hr, "myLdapPublishCertToDS"); } } fRediscover = TRUE; } } __except(hr = myHEXCEPTIONCODE(), EXCEPTION_EXECUTE_HANDLER) { _PrintError(hr, "Exception"); } error: if (S_OK != hr) { coreLogPublishError(RequestId, pwszSamName, pld, pwszDN, pwszError, hr); } if (NULL != pwszError) { LocalFree(pwszError); } if (NULL != pwszSamNamePatched) { LocalFree(pwszSamNamePatched); } if (fCritSecEntered) { LeaveCriticalSection(&g_critsecDSCache); } return(hr); } HRESULT CorePublishCertificate( IN ICertDBRow *prow, IN DWORD dwComContextIndex) { HRESULT hr; DWORD cbProp; DWORD RequestId; DWORD GeneralFlags; DWORD EnrollmentFlags; DWORD cbCert; BYTE *pbCert = NULL; CERT_CONTEXT const *pcc = NULL; WCHAR *pwszSamName = NULL; prow->GetRowId(&RequestId); cbProp = sizeof(EnrollmentFlags); hr = prow->GetProperty( wszPROPCERTIFICATEENROLLMENTFLAGS, PROPTYPE_LONG | PROPCALLER_SERVER | PROPTABLE_CERTIFICATE, &cbProp, (BYTE *) &EnrollmentFlags); _PrintIfError2(hr, "GetProperty", CERTSRV_E_PROPERTY_EMPTY); if (S_OK != hr) { EnrollmentFlags = 0; } if (0 == ((CT_FLAG_PUBLISH_TO_DS | CT_FLAG_PUBLISH_TO_KRA_CONTAINER) & EnrollmentFlags)) { hr = S_OK; goto error; } cbProp = sizeof(GeneralFlags); hr = prow->GetProperty( wszPROPCERTIFICATEGENERALFLAGS, PROPTYPE_LONG | PROPCALLER_SERVER | PROPTABLE_CERTIFICATE, &cbProp, (BYTE *) &GeneralFlags); _PrintIfError2(hr, "GetProperty", CERTSRV_E_PROPERTY_EMPTY); if (S_OK != hr) { GeneralFlags = 0; } // Get the name of the user or machine hr = PKCSGetProperty( prow, g_wszPropRequesterName, PROPTYPE_STRING | PROPCALLER_SERVER | PROPTABLE_REQUEST, NULL, (BYTE **) &pwszSamName); _JumpIfError(hr, error, "PKCSGetProperty"); hr = PKCSGetProperty( prow, g_wszPropRawCertificate, PROPTYPE_BINARY | PROPCALLER_SERVER | PROPTABLE_CERTIFICATE, &cbCert, &pbCert); _JumpIfError(hr, error, "PKCSGetProperty(raw cert)"); pcc = CertCreateCertificateContext(X509_ASN_ENCODING, pbCert, cbCert); if (NULL == pcc) { hr = HRESULT_FROM_WIN32(ERROR_INVALID_DATA); _JumpError(hr, error, "CertCreateCertificateContext"); } hr = S_OK; if (CT_FLAG_PUBLISH_TO_DS & EnrollmentFlags) { if (CT_FLAG_IS_CROSS_CA & GeneralFlags) { hr = corePublishCrossCertificate(RequestId, pwszSamName, pcc); _PrintIfError(hr, "corePublishCrossCertificate"); } else { hr = corePublishIssuedCertificate( RequestId, dwComContextIndex, pwszSamName, pcc, (CT_FLAG_MACHINE_TYPE & GeneralFlags)? LPC_MACHINEOBJECT : LPC_USEROBJECT); _PrintIfError(hr, "corePublishIssuedCertificate"); } } if (CT_FLAG_PUBLISH_TO_KRA_CONTAINER & EnrollmentFlags) { HRESULT hr2; hr2 = corePublishKRACertificate(RequestId, pwszSamName, pcc); _PrintIfError(hr2, "corePublishKRACertificate"); if (S_OK == hr) { hr = hr2; } } _JumpIfError(hr, error, "CorePublishCertificate"); error: if (NULL != pwszSamName) { LocalFree(pwszSamName); } if (NULL != pcc) { CertFreeCertificateContext(pcc); } if (NULL != pbCert) { LocalFree(pbCert); } return(hr); } HRESULT coreAcceptRequest( IN ICertDBRow *prow, IN BOOL fIncludeCRLs, IN DWORD dwComContextIndex, OUT BOOL *pfErrorLogged, OUT CACTX **ppCAContext, IN OUT CERTSRV_RESULT_CONTEXT *pResult, // CoTaskMem* OUT HRESULT *phrPublish) { HRESULT hr; *ppCAContext = NULL; *phrPublish = S_OK; // Force Cert creation: CSASSERT(NULL == pResult->pctbCert || NULL == pResult->pctbCert->pb); hr = CoreValidateRequestId(prow, DB_DISP_ACTIVE); _JumpIfError(hr, error, "CoreValidateRequestId"); hr = PKCSCreateCertificate( prow, DB_DISP_ACTIVE, fIncludeCRLs, pfErrorLogged, ppCAContext, pResult); _JumpIfError(hr, error, "PKCSCreateCertificate"); *phrPublish = CorePublishCertificate(prow, dwComContextIndex); _PrintIfError(*phrPublish, "CorePublishCertificate"); if (S_OK != *phrPublish) { hr = PKCSSetRequestFlags(prow, TRUE, CR_FLG_PUBLISHERROR); _JumpIfError(hr, error, "PKCSSetRequestFlags"); } CSASSERT(S_OK == hr); error: return(hr); } HRESULT coreVerifyRequest( IN OUT ICertDBRow **pprow, IN DWORD OpRequest, IN BOOL fIncludeCRLs, OPTIONAL IN WCHAR const *pwszUserName, IN DWORD dwComContextIndex, OUT DWORD *pReqId, OUT LONG *pExitEvent, OUT WCHAR **ppwszDisposition, // LocalAlloc OUT CACTX **ppCAContext, IN OUT CERTSRV_RESULT_CONTEXT *pResult) // CoTaskMem* { HRESULT hr; HRESULT hr2; HRESULT hrRequest = S_OK; HRESULT hrPublish = S_OK; DWORD VerifyStatus; DWORD DBDisposition; BOOL fResolved; LONG ExitEvent; BOOL fPending; BOOL fSubmit; BOOL fRetrieve; BOOL fDenied; BOOL fUpdateDisposition = FALSE; WCHAR *pwszDispositionRetrieved = NULL; WCHAR const *pwszDispositionBase = NULL; WCHAR *pwszDispositionDetail = NULL; WCHAR *pwszDisposition = NULL; WCHAR const *pwszBy = NULL; DWORD LogMsg = MSG_E_PROCESS_REQUEST_FAILED; BOOL fErrorLogged = FALSE; DWORD ReqId; ICertDBRow *prow = *pprow; prow->GetRowId(&ReqId); *ppCAContext = NULL; *pResult->pdwDisposition = CR_DISP_ERROR; DBDisposition = DB_DISP_ERROR; *ppwszDisposition = NULL; ExitEvent = EXITEVENT_INVALID; fSubmit = CR_IN_NEW == OpRequest || CR_IN_RESUBMIT == OpRequest; fPending = CR_IN_DENY == OpRequest || CR_IN_RESUBMIT == OpRequest; fRetrieve = CR_IN_RETRIEVE == OpRequest; #if DBG_COMTEST if (fSubmit && fComTest && !ComTest((LONG) ReqId)) { _PrintError(0, "ComTest"); } #endif if (fRetrieve) { hr = coreRetrievePending( prow, fIncludeCRLs, &pwszDispositionRetrieved, ppCAContext, pResult); // CoTaskMem* _JumpIfError(hr, error, "coreRetrievePending"); pwszDispositionBase = pwszDispositionRetrieved; ExitEvent = EXITEVENT_CERTRETRIEVEPENDING; } else { // If the current status is expected to be pending, verify that now, // and make the request active. // // If it was already marked active, then something went wrong last time // we processed the request (out of disk space?), and we can try to // pick up where we left off, by resubmitting or denying the request. if (fPending) { hr = CoreValidateRequestId(prow, DB_DISP_PENDING); if (CERTSRV_E_BAD_REQUESTSTATUS == hr) { hr = CoreValidateRequestId(prow, DB_DISP_ACTIVE); } if (CERTSRV_E_BAD_REQUESTSTATUS == hr && fSubmit) { hr = CoreValidateRequestId(prow, DB_DISP_DENIED); if (S_OK == hr) { DBGPRINT(( DBG_SS_CERTSRV, "Resubmit failed request %u\n", ReqId)); } } _JumpIfError(hr, error, "CoreValidateRequestId"); hr = CoreSetDisposition(prow, DB_DISP_ACTIVE); _JumpIfError(hr, error, "CoreSetDisposition"); } fUpdateDisposition = TRUE; if (fSubmit) { if (fPending) { pwszBy = g_pwszResubmittedBy; hr = PKCSSetServerProperties( prow, g_lValidityPeriodCount, g_enumValidityPeriod); _JumpIfError(hr, error, "PKCSSetServerProperties"); } hr = prow->CommitTransaction(TRUE); _JumpIfError(hr, error, "CommitTransaction"); prow->Release(); prow = NULL; *pprow = NULL; hr = PolicyVerifyRequest( g_wszCommonName, ReqId, g_PolicyFlags, CR_IN_NEW == OpRequest, dwComContextIndex, &pwszDispositionDetail, &VerifyStatus); if (S_OK != hr) { _PrintError(hr, "PolicyVerifyRequest"); if (SUCCEEDED(hr)) { if (S_FALSE == hr) { hr = E_UNEXPECTED; } else { hr = myHError(hr); } } VerifyStatus = hr; } hr = g_pCertDB->OpenRow(PROPTABLE_REQCERT, ReqId, NULL, &prow); _JumpIfError(hr, error, "OpenRow"); CSASSERT(NULL != prow); *pprow = prow; } else // else we're denying the request! { VerifyStatus = VR_INSTANT_BAD; } fResolved = FALSE; fDenied = FALSE; switch (VerifyStatus) { case VR_PENDING: hr = S_OK; DBDisposition = DB_DISP_PENDING; ExitEvent = EXITEVENT_CERTPENDING; LogMsg = MSG_DN_CERT_PENDING; *pResult->pdwDisposition = CR_DISP_UNDER_SUBMISSION; pwszDispositionBase = g_pwszUnderSubmission; break; case VR_INSTANT_OK: hr = coreAcceptRequest( prow, fIncludeCRLs, dwComContextIndex, &fErrorLogged, ppCAContext, pResult, &hrPublish); if (S_OK != hr) { CSASSERT(FAILED(hr)); _PrintError(hr, "coreAcceptRequest"); pwszDispositionBase = g_pwszRequestProcessingError; VerifyStatus = hr; hr = S_OK; fDenied = TRUE; } else { fResolved = TRUE; DBDisposition = DB_DISP_ISSUED; ExitEvent = EXITEVENT_CERTISSUED; LogMsg = MSG_DN_CERT_ISSUED; *pResult->pdwDisposition = CR_DISP_ISSUED; pwszDispositionBase = g_pwszIssued; } break; default: if (SUCCEEDED(VerifyStatus)) { CSASSERT( VerifyStatus == VR_PENDING || VerifyStatus == VR_INSTANT_OK || VerifyStatus == VR_INSTANT_BAD); hr = HRESULT_FROM_WIN32(ERROR_INVALID_DATA); _JumpError(hr, error, "VerifyStatus"); } // FALLTHROUGH case VR_INSTANT_BAD: hr = CoreValidateRequestId(prow, DB_DISP_ACTIVE); _JumpIfError(hr, error, "CoreValidateRequestId"); fDenied = TRUE; break; } if (fDenied) { fResolved = TRUE; DBDisposition = DB_DISP_DENIED; ExitEvent = EXITEVENT_CERTDENIED; *pResult->pdwDisposition = CR_DISP_DENIED; if (FAILED(VerifyStatus)) { *pResult->pdwDisposition = VerifyStatus; hrRequest = VerifyStatus; } if (fSubmit) { if (NULL == pwszDispositionBase) { pwszDispositionBase = g_pwszPolicyDeniedRequest; } LogMsg = MSG_DN_CERT_DENIED; } else { pwszDispositionBase = g_pwszDeniedBy; LogMsg = MSG_DN_CERT_ADMIN_DENIED; } } if (fResolved) { hr = PropSetRequestTimeProperty(prow, g_wszPropRequestResolvedWhen); _JumpIfError(hr, error, "PropSetRequestTimeProperty"); } } error: *pReqId = ReqId; *pExitEvent = ExitEvent; // If we verified or denied the request, set the status & disposition // Build the full disposition string pwszDisposition = CoreBuildDispositionString( pwszDispositionBase, pwszUserName, pwszDispositionDetail, pwszBy, hrPublish, TRUE); if (NULL != pwszDispositionDetail) { LocalFree(pwszDispositionDetail); } if (S_OK == hrRequest && S_OK != hr) { hrRequest = hr; } if (fUpdateDisposition && NULL != prow) { hr2 = CoreSetRequestDispositionFields( prow, hrRequest, DBDisposition, pwszDisposition); if (S_OK == hr) { hr = hr2; } } if (!fErrorLogged && NULL != prow && (fUpdateDisposition || S_OK != hr)) { CoreLogRequestStatus(prow, LogMsg, hrRequest, pwszDisposition); } if (NULL != ppwszDisposition) { *ppwszDisposition = pwszDisposition; } else if (NULL != pwszDisposition) { LocalFree(pwszDisposition); } if (NULL != pwszDispositionRetrieved) { LocalFree(pwszDispositionRetrieved); } return(hr); } HRESULT coreAuditAddStringProperty( IN ICertDBRow *prow, IN WCHAR const *pwszPropName, IN CertSrv::CAuditEvent *pevent) { HRESULT hr; WCHAR const *pwszLogValue = L""; WCHAR *pwszPropValue = NULL; hr = PKCSGetProperty( prow, pwszPropName, PROPTYPE_STRING | PROPCALLER_SERVER | PROPTABLE_CERTIFICATE, NULL, (BYTE **) &pwszPropValue); _PrintIfErrorStr(hr, "PKCSGetProperty", pwszPropName); if (S_OK == hr) { pwszLogValue = pwszPropValue; } hr = pevent->AddData(pwszLogValue); _JumpIfError(hr, error, "CAuditEvent::AddData"); error: if (NULL != pwszPropValue) { LocalFree(pwszPropValue); } return(hr); } HRESULT coreAuditRequestDisposition( OPTIONAL IN ICertDBRow *prow, IN DWORD ReqId, IN WCHAR const *pwszUserName, IN WCHAR const *pwszAttributes, IN DWORD dwDisposition) { HRESULT hr; CertSrv::CAuditEvent audit(0, g_dwAuditFilter); hr = audit.AddData(ReqId); // %1 request ID _JumpIfError(hr, error, "CAuditEvent::AddData"); hr = audit.AddData(pwszUserName); // %2 requester _JumpIfError(hr, error, "CAuditEvent::AddData"); hr = audit.AddData(pwszAttributes); // %3 attributes _JumpIfError(hr, error, "CAuditEvent::AddData"); hr = audit.AddData(dwDisposition); // %4 disposition _JumpIfError(hr, error, "CAuditEvent::AddData"); if (NULL != prow) { hr = coreAuditAddStringProperty( prow, g_wszPropCertificateSubjectKeyIdentifier, &audit); // %5 SKI _JumpIfError(hr, error, "coreAuditAddStringProperty"); hr = coreAuditAddStringProperty( prow, g_wszPropSubjectDistinguishedName, &audit); // %6 Subject _JumpIfError(hr, error, "coreAuditAddStringProperty"); } else // we need to guarantee the same number of audit params { hr = audit.AddData(L""); // %5 SKI _JumpIfError(hr, error, ""); hr = audit.AddData(L""); // %6 Subject _JumpIfError(hr, error, ""); } switch (dwDisposition) { case CR_DISP_ISSUED: audit.SetEventID(SE_AUDITID_CERTSRV_REQUESTAPPROVED); hr = audit.Report(); _JumpIfError(hr, error, "CAuditEvent::Report"); break; case CR_DISP_UNDER_SUBMISSION: audit.SetEventID(SE_AUDITID_CERTSRV_REQUESTPENDING); hr = audit.Report(); _JumpIfError(hr, error, "CAuditEvent::Report"); break; case CR_DISP_DENIED: // fail over default: audit.SetEventID(SE_AUDITID_CERTSRV_REQUESTDENIED); hr = audit.Report(false); _JumpIfError(hr, error, "CAuditEvent::Report"); break; } CSASSERT(S_OK == hr); error: return(hr); } #define LOGMSG_ATTACK_DELAY_SECONDS (CVT_MINUTES * 20) static FILETIME g_ftLogNextAttackMsg = {0}; HRESULT ValidateMessageSize(OPTIONAL LPCWSTR pwszUser, DWORD cbRequest) { HRESULT hr = S_OK; if (cbRequest > g_cbMaxIncomingMessageSize) { hr = HRESULT_FROM_WIN32(ERROR_MESSAGE_EXCEEDS_MAX_SIZE); SYSTEMTIME stNow; FILETIME ftNow; GetSystemTime(&stNow); // if VERBOSE -or- it's time to log the next msg (Next < Now) if ( (CERTLOG_VERBOSE <= g_dwLogLevel) || ( (SystemTimeToFileTime(&stNow, &ftNow)) && (0 > CompareFileTime(&g_ftLogNextAttackMsg, &ftNow)) ) ) { // g_ftLogNextAttackMsg = ftNow + DELAY; ULARGE_INTEGER ui; ui.LowPart = ftNow.dwLowDateTime; ui.HighPart = ftNow.dwHighDateTime; ui.QuadPart += ((LONGLONG)LOGMSG_ATTACK_DELAY_SECONDS) * CVT_BASE; g_ftLogNextAttackMsg.dwLowDateTime = ui.LowPart; g_ftLogNextAttackMsg.dwHighDateTime = ui.HighPart; // don't pass NULL if (NULL == pwszUser) pwszUser = L"?"; LogEventStringHResult(EVENTLOG_ERROR_TYPE, MSG_E_POSSIBLE_DENIAL_OF_SERVICE_ATTACK, pwszUser, hr); } } return hr; } HRESULT coreInitRequest( IN DWORD dwFlags, OPTIONAL IN WCHAR const *pwszUserName, IN DWORD cbRequest, OPTIONAL IN BYTE const *pbRequest, OPTIONAL IN WCHAR const *pwszAttributes, OPTIONAL IN WCHAR const *pwszSerialNumber, IN DWORD dwComContextIndex, OUT DWORD *pOpRequest, OUT ICertDBRow **pprow, // may return non-NULL on error OUT WCHAR **ppwszDisposition, IN OUT CERTSRV_RESULT_CONTEXT *pResult) { HRESULT hr; *pprow = NULL; *ppwszDisposition = NULL; // for Denial-of-Service reasons, don't do anything with a too-long message hr = ValidateMessageSize(pwszUserName, cbRequest); _JumpIfError(hr, error, "ValidateMessageSize"); // Called in several cases: // // - CR_IN_NEW: Create a new request and return error/pending/etc & // possibly the cert: // NULL != pbRequest && NULL != pResult->pctbCert, etc. // // - CR_IN_DENY: Deny a pending request: // NULL == pbRequest && NULL == pResult->pctbCert, etc. // // - CR_IN_RESUBMIT: Resubmit a pending request and return hr/pending/etc. // NULL == pbRequest && NULL == pResult->pctbCert, etc. // // - CR_IN_RETRIEVE: Retrieve a cert for a processed request and return // error/pending/etc & possibly the cert: // NULL == pbRequest && NULL != pResult->pctbCert, etc. *pOpRequest = (CR_IN_COREMASK & dwFlags); switch (*pOpRequest) { // Process a new request: case CR_IN_NEW: CSASSERT(NULL != pwszUserName); CSASSERT(0 != cbRequest); CSASSERT(NULL != pbRequest); CSASSERT(0 == *pResult->pdwRequestId); *pResult->pdwRequestId = 0; hr = coreCreateRequest( ~CR_IN_COREMASK & dwFlags, pwszUserName, cbRequest, pbRequest, pwszAttributes, dwComContextIndex, pprow, pResult); _JumpIfError(hr, error, "coreCreateRequest"); (*pprow)->GetRowId(pResult->pdwRequestId); { CertSrv::CAuditEvent audit(SE_AUDITID_CERTSRV_NEWREQUEST, g_dwAuditFilter); hr = audit.AddData(*pResult->pdwRequestId); // %1 request ID _JumpIfError(hr, error, "CAuditEvent::AddData"); hr = audit.AddData(pwszUserName); // %2 requester _JumpIfError(hr, error, "CAuditEvent::AddData"); hr = audit.AddData(pwszAttributes); // %3 attributes _JumpIfError(hr, error, "CAuditEvent::AddData"); hr = audit.Report(); _JumpIfError(hr, error, "CAuditEvent::Report"); } break; // Deny a request: // Resubmit a request: case CR_IN_DENY: case CR_IN_RESUBMIT: break; // Retrieve a cert: case CR_IN_RETRIEVE: break; default: CSASSERT(*pOpRequest != *pOpRequest); break; } if (CR_IN_NEW != *pOpRequest) { hr = E_INVALIDARG; if (0 != cbRequest || NULL != pbRequest) { _JumpError(hr, error, "unexpected request"); } if ((0 != *pResult->pdwRequestId) ^ (NULL == pwszSerialNumber)) { _JumpError(hr, error, "expected RequestId or SerialNumber"); } // RetrievePending by RequestId OR SerialNumber in pwszSerialNumber hr = g_pCertDB->OpenRow( PROPTABLE_REQCERT, *pResult->pdwRequestId, pwszSerialNumber, pprow); _JumpIfError(hr, error, "OpenRow"); } hr = S_OK; error: if (S_OK != hr) { HRESULT hr2; WCHAR const *pwszDisp = g_pwszRequestParsingError; hr2 = myDupString(pwszDisp, ppwszDisposition); _PrintIfError(hr2, "myDupString"); if (NULL != *pprow) { hr2 = CoreSetRequestDispositionFields( *pprow, hr, DB_DISP_ERROR, pwszDisp); _PrintIfError(hr2, "CoreSetRequestDispositionFields"); CoreLogRequestStatus( *pprow, MSG_E_PROCESS_REQUEST_FAILED, hr, pwszDisp); } } return(hr); } HRESULT CoreProcessRequest( IN DWORD dwFlags, OPTIONAL IN WCHAR const *pwszUserName, IN DWORD cbRequest, OPTIONAL IN BYTE const *pbRequest, OPTIONAL IN WCHAR const *pwszAttributes, OPTIONAL IN WCHAR const *pwszSerialNumber, IN DWORD dwComContextIndex, IN DWORD dwRequestId, OUT CERTSRV_RESULT_CONTEXT *pResult) { HRESULT hr; HRESULT hr2; WCHAR *pwszDisposition = NULL; DWORD OpRequest; ICertDBRow *prow = NULL; DWORD ReqId; LONG ExitEvent = EXITEVENT_INVALID; BOOL fCoInitialized = FALSE; CACTX *pCAContext; BOOL fCommitted = FALSE; CSASSERT(NULL != pResult->pdwRequestId); CSASSERT(NULL != pResult->pdwDisposition); if (MAXDWORD == dwRequestId) { dwRequestId = 0; } *pResult->pdwRequestId = dwRequestId; *pResult->pdwDisposition = CR_DISP_ERROR; hr = CoInitializeEx(NULL, GetCertsrvComThreadingModel()); if (S_OK != hr && S_FALSE != hr) { _JumpError(hr, error, "CoInitializeEx"); } fCoInitialized = TRUE; hr = coreInitRequest( dwFlags, pwszUserName, cbRequest, pbRequest, pwszAttributes, pwszSerialNumber, dwComContextIndex, &OpRequest, &prow, &pwszDisposition, pResult); _PrintIfError(hr, "coreInitRequest"); pCAContext = NULL; if (S_OK == hr) { CSASSERT(NULL == pwszDisposition); // error string only if (CR_IN_NEW != OpRequest) { DWORD cb; DWORD dw; cb = sizeof(dw); hr = prow->GetProperty( g_wszPropRequestType, PROPTYPE_LONG | PROPCALLER_SERVER | PROPTABLE_REQUEST, &cb, (BYTE *) &dw); if (S_OK == hr) { dwFlags |= (CR_IN_CRLS & dw); } } hr = coreVerifyRequest( &prow, OpRequest, 0 != (CR_IN_CRLS & dwFlags), pwszUserName, dwComContextIndex, &ReqId, &ExitEvent, &pwszDisposition, &pCAContext, pResult); // CoTaskMem _PrintIfError(hr, "coreVerifyRequest"); } else { WCHAR *pwszDisposition2 = CoreBuildDispositionString( pwszDisposition, NULL, // pwszUserName NULL, // pwszDispositionDetail NULL, // pwszBy hr, FALSE); if (NULL != pwszDisposition2) { if (NULL != pwszDisposition) { LocalFree(pwszDisposition); } pwszDisposition = pwszDisposition2; } } if (NULL != pResult->pctbFullResponse) { BYTE const *pbCert = NULL; DWORD cbCert = 0; if (NULL != pResult->pctbCert && NULL != pResult->pctbCert->pb) { pbCert = pResult->pctbCert->pb; cbCert = pResult->pctbCert->cb; } CSASSERT(NULL == pResult->pctbFullResponse->pb); hr2 = PKCSEncodeFullResponse( prow, pResult, hr, pwszDisposition, pCAContext, pbCert, // pbCertLeaf cbCert, // cbCertLeaf 0 != (CR_IN_CRLS & dwFlags), &pResult->pctbFullResponse->pb, // CoTaskMem* &pResult->pctbFullResponse->cb); _PrintIfError(hr2, "PKCSEncodeFullResponse"); if (S_OK == hr && (HRESULT_FROM_WIN32(ERROR_FILE_NOT_FOUND) != hr2 || IsWhistler())) { hr = hr2; } } hr2 = coreAuditRequestDisposition( prow, ReqId, pwszUserName, pwszAttributes, *pResult->pdwDisposition); _PrintIfError(hr2, "coreAuditRequestDisposition"); if (S_OK == hr) { hr = hr2; } if (NULL != pwszDisposition && NULL != pResult->pctbDispositionMessage) { DWORD cbAlloc = (wcslen(pwszDisposition) + 1) * sizeof(WCHAR); BYTE *pbAlloc; pbAlloc = (BYTE *) CoTaskMemAlloc(cbAlloc); if (NULL != pbAlloc) { CopyMemory(pbAlloc, pwszDisposition, cbAlloc); pResult->pctbDispositionMessage->pb = pbAlloc; pResult->pctbDispositionMessage->cb = cbAlloc; } } if (NULL != prow) { if (pResult->fKeyArchived && (KRAF_SAVEBADREQUESTKEY & g_KRAFlags)) { BOOL fSave = FALSE; switch (*pResult->pdwDisposition) { case CR_DISP_INCOMPLETE: case CR_DISP_ERROR: case CR_DISP_DENIED: fSave = TRUE; break; default: if (S_OK != hr) { fSave = TRUE; } break; } if (fSave) { hr2 = prow->SetProperty( g_wszPropRequestRawRequest, PROPTYPE_BINARY | PROPCALLER_SERVER | PROPTABLE_REQUEST, cbRequest, pbRequest); _PrintIfError(hr2, "SetProperty(request)"); if (S_OK == hr) { hr = hr2; } } } hr2 = prow->CommitTransaction(TRUE); _PrintIfError(hr2, "CommitTransaction"); fCommitted = S_OK == hr2; if (S_OK == hr) { hr = hr2; } } error: // If the request exists, clean up the DB if (NULL != prow) { if (S_OK != hr && !fCommitted) { hr2 = prow->CommitTransaction(FALSE); _PrintIfError(hr2, "CommitTransaction"); } prow->Release(); } if (EXITEVENT_INVALID != ExitEvent) { CSASSERT(fCoInitialized); ExitNotify(ExitEvent, ReqId, dwComContextIndex); } if (fCoInitialized) { CoUninitialize(); } if (S_OK != hr) { WCHAR const *pwszMsg; pwszMsg = myGetErrorMessageText(hr, TRUE); if (NULL != pwszMsg) { CONSOLEPRINT1((DBG_SS_CERTSRV, "%ws\n", pwszMsg)); LocalFree(const_cast(pwszMsg)); } } if (NULL != pwszDisposition) { LocalFree(pwszDisposition); } // Hide the failed HRESULT in the returned Disposition. // This allows the encoded Full response and disposition message to be // returned via DCOM or RPC. Returning S_OK != hr defeats this mechanism. if (FAILED(hr) && (CR_DISP_ERROR == *pResult->pdwDisposition || CR_DISP_DENIED == *pResult->pdwDisposition)) { *pResult->pdwDisposition = hr; hr = S_OK; } return(hr); } HRESULT CoreValidateRequestId( IN ICertDBRow *prow, IN DWORD ExpectedDisposition) { HRESULT hr; DWORD cbProp; DWORD Disposition; cbProp = sizeof(Disposition); hr = prow->GetProperty( g_wszPropRequestDisposition, PROPTYPE_LONG | PROPCALLER_SERVER | PROPTABLE_REQUEST, &cbProp, (BYTE *) &Disposition); _PrintIfError(hr, "GetProperty"); if (S_OK != hr || sizeof(Disposition) != cbProp) { hr = CERTSRV_E_NO_REQUEST; } else if (Disposition != ExpectedDisposition) { hr = CERTSRV_E_BAD_REQUESTSTATUS; } return(hr); } HRESULT SetCAObjectFlags( IN DWORD dwFlags) { HRESULT hr = S_OK; HCAINFO hCAInfo = NULL; DWORD dwCAFlags; hr = CAFindByName( g_wszSanitizedName, NULL, CA_FIND_LOCAL_SYSTEM | CA_FIND_INCLUDE_UNTRUSTED, &hCAInfo); _JumpIfError(hr, error, "CAFindByName"); hr = CAGetCAFlags(hCAInfo, &dwCAFlags); _JumpIfError(hr, error, "CAGetCAFlags"); dwCAFlags |= dwFlags; hr = CASetCAFlags(hCAInfo, dwCAFlags); _JumpIfError(hr, error, "CASetCAFlags"); hr = CAUpdateCA(hCAInfo); _JumpIfError(hr, error, "CAUpdateCA"); error: if (NULL != hCAInfo) { CACloseCA(hCAInfo); } return(hr); }