//+-------------------------------------------------------------------------- // // Microsoft Windows // Copyright (C) Microsoft Corporation, 1999 - 2000 // // File: event.cpp // // Contents: Cert CAuditEvent class implementation // //--------------------------------------------------------------------------- #include #pragma hdrstop #include #include #include using namespace CertSrv; CAuditEvent::AUDIT_CATEGORIES cat[] = { // event ID // event category no of check role separation //args for this event {SE_AUDITID_CERTSRV_SHUTDOWN, AUDIT_FILTER_STARTSTOP, 0, TRUE}, {SE_AUDITID_CERTSRV_SERVICESTART, AUDIT_FILTER_STARTSTOP, 2, TRUE}, {SE_AUDITID_CERTSRV_SERVICESTOP, AUDIT_FILTER_STARTSTOP, 2, TRUE}, {SE_AUDITID_CERTSRV_BACKUPSTART, AUDIT_FILTER_BACKUPRESTORE,1, TRUE}, {SE_AUDITID_CERTSRV_BACKUPEND, AUDIT_FILTER_BACKUPRESTORE,0, TRUE}, {SE_AUDITID_CERTSRV_RESTORESTART, AUDIT_FILTER_BACKUPRESTORE,0, FALSE}, {SE_AUDITID_CERTSRV_RESTOREEND, AUDIT_FILTER_BACKUPRESTORE,0, FALSE}, {SE_AUDITID_CERTSRV_DENYREQUEST, AUDIT_FILTER_CERTIFICATE, 1, TRUE}, {SE_AUDITID_CERTSRV_RESUBMITREQUEST, AUDIT_FILTER_CERTIFICATE, 1, TRUE}, {SE_AUDITID_CERTSRV_SETEXTENSION, AUDIT_FILTER_CERTIFICATE, 5, TRUE}, {SE_AUDITID_CERTSRV_SETATTRIBUTES, AUDIT_FILTER_CERTIFICATE, 2, TRUE}, {SE_AUDITID_CERTSRV_IMPORTCERT, AUDIT_FILTER_CERTIFICATE, 2, TRUE}, {SE_AUDITID_CERTSRV_NEWREQUEST, AUDIT_FILTER_CERTIFICATE, 3, FALSE}, {SE_AUDITID_CERTSRV_REQUESTAPPROVED, AUDIT_FILTER_CERTIFICATE, 6, FALSE}, {SE_AUDITID_CERTSRV_REQUESTDENIED, AUDIT_FILTER_CERTIFICATE, 6, FALSE}, {SE_AUDITID_CERTSRV_REQUESTPENDING, AUDIT_FILTER_CERTIFICATE, 6, FALSE}, {SE_AUDITID_CERTSRV_DELETEROW, AUDIT_FILTER_CERTIFICATE, 3, TRUE}, {SE_AUDITID_CERTSRV_PUBLISHCACERT, AUDIT_FILTER_CERTIFICATE, 3, FALSE}, {SE_AUDITID_CERTSRV_REVOKECERT, AUDIT_FILTER_CERTREVOCATION, 2, TRUE}, {SE_AUDITID_CERTSRV_PUBLISHCRL, AUDIT_FILTER_CERTREVOCATION, 3, TRUE}, {SE_AUDITID_CERTSRV_AUTOPUBLISHCRL, AUDIT_FILTER_CERTREVOCATION, 5, FALSE}, {SE_AUDITID_CERTSRV_SETSECURITY, AUDIT_FILTER_CASECURITY, 1, TRUE}, {SE_AUDITID_CERTSRV_SETAUDITFILTER, AUDIT_FILTER_CASECURITY, 1, TRUE}, {SE_AUDITID_CERTSRV_SETOFFICERRIGHTS, AUDIT_FILTER_CASECURITY, 2, TRUE}, {SE_AUDITID_CERTSRV_ROLESEPARATIONSTATE,AUDIT_FILTER_CASECURITY, 1, FALSE}, {SE_AUDITID_CERTSRV_GETARCHIVEDKEY, AUDIT_FILTER_KEYAARCHIVAL, 1, TRUE}, {SE_AUDITID_CERTSRV_KEYARCHIVED, AUDIT_FILTER_KEYAARCHIVAL, 3, FALSE}, {SE_AUDITID_CERTSRV_IMPORTKEY, AUDIT_FILTER_KEYAARCHIVAL, 1, TRUE}, {SE_AUDITID_CERTSRV_SETCONFIGENTRY, AUDIT_FILTER_CACONFIG, 3, TRUE}, {SE_AUDITID_CERTSRV_SETCAPROPERTY, AUDIT_FILTER_CACONFIG, 4, TRUE}, }; CAuditEvent::AUDIT_CATEGORIES *CAuditEvent::m_gAuditCategories = cat; DWORD CAuditEvent::m_gdwAuditCategoriesSize = sizeof(cat)/sizeof(cat[0]); bool CAuditEvent::m_gfRoleSeparationEnabled = false; CAuditEvent::CAuditEvent(ULONG ulEventID, DWORD dwFilter) : m_cEventData(0), m_cRequiredEventData(0), m_dwFilter(dwFilter), m_fRoleSeparationEnabled(false), m_pISS(NULL), m_hClientToken(NULL), m_pCASD(NULL), m_ClientContext(NULL), m_pSDPrivileges(NULL), m_pDaclPrivileges(NULL), m_hRpc(NULL), m_Error(0), m_MaskAllowed(0), m_crtGUID(0), m_pUserSid(NULL), m_hAuditEventType(NULL) { m_AuthzHandle = NULL; m_Request.ObjectTypeList = NULL; m_Request.PrincipalSelfSid = NULL; m_Request.ObjectTypeListLength = 0; m_Request.OptionalArguments = NULL; m_Reply.ResultListLength = 1; m_Reply.GrantedAccessMask = &m_MaskAllowed; m_Reply.Error = &m_Error; m_Reply.SaclEvaluationResults = &m_SaclEval; SetEventID(ulEventID); }; // initializes internal data associated with a particular audit event void CAuditEvent::SetEventID(ULONG ulEventID) { m_ulEventID = ulEventID; for(DWORD c=0; c=m_cRequiredEventData); if(!m_gAuditCategories[c].hAuditEventType) { AuthziInitializeAuditEventType( 0, SE_CATEGID_OBJECT_ACCESS, (USHORT)m_ulEventID, (USHORT)m_gAuditCategories[c].dwParamCount, &m_gAuditCategories[c].hAuditEventType); } m_hAuditEventType = m_gAuditCategories[c].hAuditEventType; break; } } } CAuditEvent::~CAuditEvent() { for(DWORD cData=0;cDatablob.cbSize = dwDataLen; pvtData->blob.pBlobData = (BYTE*)CoTaskMemAlloc(dwDataLen); if(!pvtData->blob.pBlobData) { return E_OUTOFMEMORY; } memcpy(pvtData->blob.pBlobData, pData, dwDataLen); return S_OK; } HRESULT CAuditEvent::AddData(bool fData) { PROPVARIANT *pvtData = CreateNewEventData(); if(!pvtData) { return E_OUTOFMEMORY; } V_VT(pvtData) = VT_BOOL; V_BOOL(pvtData) = fData?VARIANT_TRUE:VARIANT_FALSE; return S_OK; } HRESULT CAuditEvent::AddData(LPCWSTR pcwszData) { if(!pcwszData) pcwszData = L""; PROPVARIANT *pvtData = CreateNewEventData(); if(!pvtData) { return E_OUTOFMEMORY; } V_VT(pvtData) = VT_LPWSTR; pvtData->pwszVal = (LPWSTR)CoTaskMemAlloc((wcslen(pcwszData)+1)*sizeof(WCHAR)); if(!pvtData->pwszVal) { return E_OUTOFMEMORY; } wcscpy(pvtData->pwszVal, pcwszData); return S_OK; } HRESULT CAuditEvent::AddData(LPCWSTR *ppcwszData) { CSASSERT(ppcwszData); PROPVARIANT *pvtData = CreateNewEventData(); if(!pvtData) { return E_OUTOFMEMORY; } V_VT(pvtData) = VT_LPWSTR; DWORD dwTextLen = 1; for(LPCWSTR *ppcwszStr=ppcwszData; *ppcwszStr; ppcwszStr++) { dwTextLen += wcslen(*ppcwszStr)+2; } pvtData->pwszVal = (LPWSTR)CoTaskMemAlloc(dwTextLen*sizeof(WCHAR)); if(!pvtData->pwszVal) { return E_OUTOFMEMORY; } wcscpy(pvtData->pwszVal, L""); for(ppcwszStr=ppcwszData; *ppcwszStr; ppcwszStr++) { wcscat(pvtData->pwszVal, *ppcwszStr); wcscat(pvtData->pwszVal, L"; "); } return S_OK; } HRESULT CAuditEvent::AddData(FILETIME time) { PROPVARIANT *pvtData = CreateNewEventData(); if(!pvtData) { return E_OUTOFMEMORY; } V_VT(pvtData) = VT_FILETIME; pvtData->filetime = time; return S_OK; } HRESULT CAuditEvent::AddData(const VARIANT *pvar, bool fDoublePercentInStrings=false) { CSASSERT(pvar); EventData *pData = CreateNewEventData1(); if(!pData) { return E_OUTOFMEMORY; } pData->m_fDoublePercentsInStrings = fDoublePercentInStrings; HRESULT hr = VariantCopy((VARIANT*)&pData->m_vtData, (VARIANT*)pvar); return hr; } PROPVARIANT *CAuditEvent::CreateNewEventData() { EventData *pData = CreateNewEventData1(); return &pData->m_vtData; } CAuditEvent::EventData *CAuditEvent::CreateNewEventData1() { EventData *pData = new EventData; if(!pData) { return NULL; } m_pEventDataList[m_cEventData++] = pData; return pData; } HRESULT CAuditEvent::EventData::ConvertToStringI2I4( LONG lVal, LPWSTR *ppwszOut) { WCHAR wszVal[100]; // big enough to hold a LONG as string _itow(lVal, wszVal, 10); *ppwszOut = new WCHAR[wcslen(wszVal)+1]; if(!*ppwszOut) { return E_OUTOFMEMORY; } wcscpy(*ppwszOut, wszVal); return S_OK; } HRESULT CAuditEvent::EventData::ConvertToStringUI2UI4( ULONG ulVal, LPWSTR *ppwszOut) { WCHAR wszVal[100]; // big enough to hold a LONG as string _itow(ulVal, wszVal, 10); *ppwszOut = new WCHAR[wcslen(wszVal)+1]; if(!*ppwszOut) { return E_OUTOFMEMORY; } wcscpy(*ppwszOut, wszVal); return S_OK; } HRESULT CAuditEvent::EventData::DoublePercentsInString( LPCWSTR pcwszIn, LPWSTR *ppwszOut) { const WCHAR *pchSrc; WCHAR *pchDest; int cPercentChars = 0; wprintf(L"********Found %d percents\n", cPercentChars); *ppwszOut = new WCHAR[2*wcslen(pcwszIn)+1]; if(!*ppwszOut) return E_OUTOFMEMORY; for(pchSrc = pcwszIn, pchDest = *ppwszOut; L'\0'!=*pchSrc; pchSrc++, pchDest++) { *pchDest = *pchSrc; if(L'%'==*pchSrc) *(++pchDest) = L'%'; } *pchDest = L'\0'; wprintf(L"****************************"); wprintf(L"%s%", *ppwszOut); wprintf(L"****************************"); return S_OK; } HRESULT CAuditEvent::EventData::ConvertToStringWSZ( LPCWSTR pcwszVal, LPWSTR *ppwszOut) { if(m_fDoublePercentsInStrings) { // replace each occurence of % with %% return DoublePercentsInString( pcwszVal, ppwszOut); } else { *ppwszOut = new WCHAR[(wcslen(pcwszVal)+1)]; if(!*ppwszOut) { return E_OUTOFMEMORY; } wcscpy(*ppwszOut, pcwszVal); } return S_OK; } HRESULT CAuditEvent::EventData::ConvertToStringBOOL( BOOL fVal, LPWSTR *ppwszOut) { LPCWSTR pwszBoolVal = fVal==VARIANT_TRUE? g_pwszYes: g_pwszNo; *ppwszOut = new WCHAR[wcslen(pwszBoolVal)+1]; if(!*ppwszOut) { return E_OUTOFMEMORY; } wcscpy(*ppwszOut, pwszBoolVal); return S_OK; } HRESULT CAuditEvent::EventData::ConvertToStringArrayUI1( LPSAFEARRAY psa, LPWSTR *ppwszOut) { SafeArrayEnum saenum(psa); if(!saenum.IsValid()) { return E_INVALIDARG; } BYTE b; // byte array is formated as "0x00 0x00..." ie 5 // chars per byte *ppwszOut = new WCHAR[saenum.GetCount()*5]; if(!*ppwszOut) return E_OUTOFMEMORY; LPWSTR pwszCrt = *ppwszOut; while(S_OK==saenum.Next(b)) { wsprintf(pwszCrt, L"0x%02X ", b); // eg "0x0f" or "0xa4" pwszCrt+=5; } return S_OK; } HRESULT CAuditEvent::EventData::ConvertToStringArrayBSTR( LPSAFEARRAY psa, LPWSTR *ppwszOut) { SafeArrayEnum saenum(psa); if(!saenum.IsValid()) { return E_INVALIDARG; } DWORD dwLen = 1; BSTR bstr; while(S_OK==saenum.Next(bstr)) { dwLen+=2*wcslen(bstr)+10; } *ppwszOut = new WCHAR[dwLen]; if(!*ppwszOut) return E_OUTOFMEMORY; **ppwszOut = L'\0'; saenum.Reset(); while(S_OK==saenum.Next(bstr)) { if(m_fDoublePercentsInStrings) { CAutoLPWSTR pwszTemp; if(S_OK != DoublePercentsInString(bstr, &pwszTemp)) { delete[] *ppwszOut; *ppwszOut = NULL; return E_OUTOFMEMORY; } wcscat(*ppwszOut, pwszTemp); } else { wcscat(*ppwszOut, bstr); } wcscat(*ppwszOut, L"\n"); } return S_OK; } HRESULT CAuditEvent::EventData::ConvertToString(LPWSTR *ppwszValue) { LPWSTR pwszVal = NULL; HRESULT hr = S_OK; switch(V_VT(&m_vtData)) { case VT_I2: hr = ConvertToStringI2I4(V_I2(&m_vtData), ppwszValue); break; case VT_BYREF|VT_I2: hr = ConvertToStringI2I4(*V_I2REF(&m_vtData), ppwszValue); break; case VT_I4: hr = ConvertToStringI2I4(V_I4(&m_vtData), ppwszValue); break; case VT_BYREF|VT_I4: hr = ConvertToStringI2I4(*V_I4REF(&m_vtData), ppwszValue); break; case VT_UI2: hr = ConvertToStringUI2UI4(V_UI2(&m_vtData), ppwszValue); break; case VT_BYREF|VT_UI2: hr = ConvertToStringUI2UI4(*V_UI2REF(&m_vtData), ppwszValue); break; case VT_UI4: hr = ConvertToStringUI2UI4(V_UI4(&m_vtData), ppwszValue); break; case VT_BYREF|VT_UI4: hr = ConvertToStringUI2UI4(*V_UI4REF(&m_vtData), ppwszValue); break; case VT_BLOB: // We don't call CryptBinaryToString directly anywhere in the CA tree. // This avoids errors when linking in the CryptBinaryToString code // for NT 4 reskit builds. WCHAR *pwszLocalAlloc; hr = myCryptBinaryToString( m_vtData.blob.pBlobData, m_vtData.blob.cbSize, CRYPT_STRING_BASE64, &pwszLocalAlloc); if (S_OK != hr) { return(hr); } pwszVal = new WCHAR[(wcslen(pwszLocalAlloc) + 1)]; if (NULL == pwszVal) { LocalFree(pwszLocalAlloc); return E_OUTOFMEMORY; } wcscpy(pwszVal, pwszLocalAlloc); LocalFree(pwszLocalAlloc); *ppwszValue = pwszVal; // pwszVal[cOut-2] = L'\0'; \\ Base64Encode adds a new line break; case VT_BOOL: hr = ConvertToStringBOOL(V_BOOL(&m_vtData), ppwszValue); break; case VT_BOOL|VT_BYREF: hr = ConvertToStringBOOL(*V_BOOLREF(&m_vtData), ppwszValue); break; case VT_LPWSTR: hr = ConvertToStringWSZ(m_vtData.pwszVal, ppwszValue); break; case VT_BSTR: hr = ConvertToStringWSZ(V_BSTR(&m_vtData), ppwszValue); break; case VT_BSTR|VT_BYREF: hr = ConvertToStringWSZ(*V_BSTRREF(&m_vtData), ppwszValue); break; case VT_FILETIME: { LPWSTR pwszTime = NULL; hr = myFileTimeToWszTime( &m_vtData.filetime, TRUE, &pwszTime); if(FAILED(hr)) return hr; pwszVal = new WCHAR[wcslen(pwszTime)+1]; if(!pwszVal) { return E_OUTOFMEMORY; } wcscpy(pwszVal, pwszTime); LocalFree(pwszTime); *ppwszValue = pwszVal; } break; case VT_ARRAY|VT_UI1: hr = ConvertToStringArrayUI1(V_ARRAY(&m_vtData), ppwszValue); break; case VT_ARRAY|VT_UI1|VT_BYREF: hr = ConvertToStringArrayUI1(*V_ARRAYREF(&m_vtData), ppwszValue); break; case VT_ARRAY|VT_BSTR: hr = ConvertToStringArrayBSTR(V_ARRAY(&m_vtData), ppwszValue); break; case VT_ARRAY|VT_BSTR|VT_BYREF: hr = ConvertToStringArrayBSTR(*V_ARRAYREF(&m_vtData), ppwszValue); break; default: { LPCWSTR pwszValOut = cAuditString_UnknownDataType; VARIANT varOut; VariantInit(&varOut); hr = VariantChangeType(&varOut, (VARIANT*)&m_vtData, 0, VT_BSTR); if(S_OK==hr) { pwszValOut = V_BSTR(&varOut); } pwszVal = new WCHAR[wcslen(pwszValOut)+1]; if(!pwszVal) { return E_OUTOFMEMORY; } wcscpy(pwszVal, pwszValOut); VariantClear(&varOut); *ppwszValue = pwszVal; hr = S_OK; } break; } return hr; } HRESULT CAuditEvent::Report(bool fSuccess /* = true */) { HRESULT hr; AUTHZ_AUDIT_EVENT_HANDLE AuthzAIH = NULL; PAUDIT_PARAMS pAuditParams = NULL; PAUDIT_PARAM pParamArray = NULL; if(!IsEventEnabled()) { return S_OK; } hr = BuildAuditParamArray(pParamArray); _JumpIfError(hr, error, "GetAuditText"); if(!AuthziAllocateAuditParams( &pAuditParams, (USHORT)(m_cEventData+2))) // authz adds 2 { // extra params hr = myHLastError(); // internally _JumpError(hr, error, "AuthziAllocateAuditParams"); } #ifndef _DISABLE_AUTHZ_ if(!AuthziInitializeAuditParamsFromArray( fSuccess?APF_AuditSuccess:APF_AuditFailure, g_AuthzCertSrvRM, (USHORT)m_cEventData, pParamArray, pAuditParams)) #else SetLastError(E_INVALIDARG); #endif { hr = myHLastError(); _JumpError(hr, error, "AuthziInitializeAuditParamsFromArray"); } if (!AuthziInitializeAuditEvent(0, g_AuthzCertSrvRM, m_hAuditEventType, pAuditParams, NULL, INFINITE, L"", L"", L"", L"", &AuthzAIH)) { hr = myHLastError(); _JumpIfError(hr, error, "AuthzInitializeAuditInfo"); } if(!AuthziLogAuditEvent( 0, AuthzAIH, NULL )) { hr = myHLastError(); _JumpIfError(hr, error, "AuthzGenAuditEvent"); } DBGPRINT(( DBG_SS_AUDIT, "Audit event ID=%d\n", m_ulEventID)); error: if(AuthzAIH) { AuthzFreeAuditEvent(AuthzAIH); } if(pAuditParams) { AuthziFreeAuditParams(pAuditParams); } FreeAuditParamArray(pParamArray); return hr; } HRESULT CAuditEvent::SaveFilter(LPCWSTR pcwszSanitizedName) { return mySetCertRegDWValue( pcwszSanitizedName, NULL, NULL, wszREGAUDITFILTER, m_dwFilter); } HRESULT CAuditEvent::LoadFilter(LPCWSTR pcwszSanitizedName) { return myGetCertRegDWValue( pcwszSanitizedName, NULL, NULL, wszREGAUDITFILTER, &m_dwFilter); } HRESULT CAuditEvent::Impersonate() { HRESULT hr; HANDLE hThread = NULL; CSASSERT(NULL==m_pISS); CSASSERT(NULL==m_hClientToken); if (NULL == m_hRpc) { // dcom impersonate hr = CoGetCallContext(IID_IServerSecurity, (void**)&m_pISS); _JumpIfError(hr, error, "CoGetCallContext"); hr = m_pISS->ImpersonateClient(); _JumpIfError(hr, error, "ImpersonateClient"); } else { // rpc impersonate hr = RpcImpersonateClient((RPC_BINDING_HANDLE) m_hRpc); if (S_OK != hr) { hr = myHError(hr); _JumpError(hr, error, "RpcImpersonateClient"); } } hThread = GetCurrentThread(); if (NULL == hThread) { hr = myHLastError(); _JumpIfError(hr, error, "GetCurrentThread"); } if (!OpenThreadToken(hThread, TOKEN_QUERY | TOKEN_DUPLICATE, FALSE, // client impersonation &m_hClientToken)) { hr = myHLastError(); _JumpIfError(hr, error, "OpenThreadToken"); } error: if(S_OK!=hr) { if(NULL!=m_pISS) { m_pISS->Release(); m_pISS = NULL; } } if (NULL != hThread) { CloseHandle(hThread); } return hr; } HRESULT CAuditEvent::RevertToSelf() { HRESULT hr = S_OK; // CSASSERT(m_pISS||m_hRpc); if (NULL != m_hRpc) // rpc { hr = RpcRevertToSelf(); if (S_OK != hr) { hr = myHError(hr); _JumpError(hr, error, "RpcRevertToSelf"); } m_hRpc = NULL; } else if(m_pISS) // dcom { hr = m_pISS->RevertToSelf(); _JumpIfError(hr, error, "IServerSecurity::RpcRevertToSelf"); m_pISS->Release(); m_pISS = NULL; } error: return hr; } HANDLE CAuditEvent::GetClientToken() { CSASSERT(m_hClientToken); HANDLE hSave = m_hClientToken; m_hClientToken = NULL; return hSave; } // dwAuditFlags - not asking for both success and failure implicitely // means the handles will be cached for future audit HRESULT CAuditEvent::AccessCheck( ACCESS_MASK Mask, DWORD dwAuditFlags, handle_t hRpc, HANDLE *phToken) { HRESULT hr = S_OK; LUID luid = {0,0}; bool fAccessAllowed = false; DWORD dwRoles = 0; DWORD dwRolesChecked = 0; PACL pDacl = NULL; FreeCachedHandles(); m_hRpc = hRpc; if (!g_CASD.IsInitialized()) { hr = HRESULT_FROM_WIN32(ERROR_NOT_READY); _JumpError(hr, error, "Security not enabled"); } hr = g_CASD.LockGet(&m_pCASD); _JumpIfError(hr, error, "CProtectedSecurityDescriptor::LockGet"); hr = Impersonate(); _JumpIfError(hr, error, "CAuditEvent::Impersonate"); if(!AuthzInitializeContextFromToken( 0, m_hClientToken, g_AuthzCertSrvRM, NULL, luid, NULL, &m_ClientContext)) { hr = myHLastError(); _PrintError(hr, "AuthzInitializeContextFromToken"); if (E_INVALIDARG == hr && !IsWhistler()) { hr = S_OK; fAccessAllowed = TRUE; } goto error; } if(Mask & CA_ACCESS_LOCALADMIN) { bool fLocalAdmin; hr = IsCurrentUserBuiltinAdmin(&fLocalAdmin); _JumpIfError(hr, error, "IsCurrentUserBuiltinAdmin"); if(fLocalAdmin) { dwRoles |= CA_ACCESS_LOCALADMIN; } } RevertToSelf(); // Get privilege based roles if checking access on a privilege role // or if role separation is enabled when we have to know all roles if(IsEventRoleSeparationEnabled() || Mask & (CA_ACCESS_OPERATOR|CA_ACCESS_AUDITOR|CA_ACCESS_LOCALADMIN)) { hr = GetPrivilegeRoles(&dwRoles); _JumpIfError(hr, error, "CAuditEvent::GetPrivilegeRolesCount"); hr = BuildPrivilegeSecurityDescriptor(dwRoles); _JumpIfError(hr, error, "CAuditEvent::BuildPrivilegeSecurityDescriptor"); } // Get security descriptor based roles m_Request.DesiredAccess = MAXIMUM_ALLOWED; CSASSERT(!m_AuthzHandle); if(!AuthzAccessCheck( 0, m_ClientContext, &m_Request, NULL, //no audit m_pCASD, m_pSDPrivileges?(&m_pSDPrivileges):NULL, m_pSDPrivileges?1:0, &m_Reply, IsEventEnabled()?&m_AuthzHandle:NULL)) // no caching if no audit // event will be generated { hr = myHLastError(); _JumpError(hr, error, "AuthzAccessCheck"); } dwRoles |= m_Reply.GrantedAccessMask[0]; if(m_Reply.Error[0]==ERROR_SUCCESS && m_Reply.GrantedAccessMask[0]&Mask) { fAccessAllowed = true; } if(IsEventRoleSeparationEnabled() && GetBitCount(dwRoles&CA_ACCESS_MASKROLES)>1) { hr = CERTSRV_E_ROLECONFLICT; fAccessAllowed = false; // don't return yet, we need to generate an audit } // Next is a fake access check to generate an audit. // Access is denied if: // - role separation is enabled and user has more than one role // - none of the roles requested is allowed // Generate audit if event is enabled and if(IsEventEnabled() && (!fAccessAllowed && !(dwAuditFlags&m_gcNoAuditFailure) || fAccessAllowed && !(dwAuditFlags&m_gcNoAuditSuccess))) { m_Request.DesiredAccess = fAccessAllowed? m_Reply.GrantedAccessMask[0]&Mask: Mask; if(CERTSRV_E_ROLECONFLICT==hr) m_Request.DesiredAccess = 0x0000ffff; //force a failure audit HRESULT hr2 = CachedGenerateAudit(); if(S_OK != hr2) { hr = hr2; _JumpIfError(hr, error, "CAuditEvent::CachedGenerateAudit"); } } if(phToken) { *phToken = GetClientToken(); } error: #ifdef DBG_CERTSRV_DEBUG_PRINT if(IsEventRoleSeparationEnabled()) { DBGPRINT((DBG_SS_AUDIT, "EVENT %d ROLES: 0x%x %s%s%s%s%s%s\n", m_ulEventID, dwRoles, (dwRoles&CA_ACCESS_ADMIN)?"CAADMIN ":"", (dwRoles&CA_ACCESS_OFFICER)?"OFFICER ":"", (dwRoles&CA_ACCESS_AUDITOR)?"AUDITOR ":"", (dwRoles&CA_ACCESS_OPERATOR)?"OPERATOR ":"", (dwRoles&CA_ACCESS_ENROLL)?"ENROLL ":"", (dwRoles&CA_ACCESS_READ)?"READ ":"")); } #endif if(!IsEventEnabled()) { FreeCachedHandles(); } if(S_OK==hr) { hr = fAccessAllowed?S_OK:E_ACCESSDENIED; } return(hr); } HRESULT CAuditEvent::CachedGenerateAudit() { HRESULT hr = S_OK; AUTHZ_AUDIT_EVENT_HANDLE AuditInfo = NULL; PAUDIT_PARAMS pAuditParams = NULL; PAUDIT_PARAM pParamArray = NULL; if(!IsEventEnabled()) { FreeCachedHandles(); return S_OK; } CSASSERT(m_AuthzHandle); hr = BuildAuditParamArray(pParamArray); _JumpIfError(hr, error, "GetAuditText"); if(!AuthziAllocateAuditParams( &pAuditParams, (USHORT)(m_cEventData+2))) // authz adds 2 { // extra params hr = myHLastError(); // internally _JumpError(hr, error, "AuthziAllocateAuditParams"); } #ifndef _DISABLE_AUTHZ_ if(!AuthziInitializeAuditParamsFromArray( APF_AuditSuccess|APF_AuditFailure, g_AuthzCertSrvRM, (USHORT)m_cEventData, pParamArray, pAuditParams)) #else SetLastError(E_INVALIDARG); #endif { hr = myHLastError(); _JumpError(hr, error, "AuthzInitAuditParams"); } if(!AuthziInitializeAuditEvent( 0, g_AuthzCertSrvRM, m_hAuditEventType, pAuditParams, NULL, INFINITE, L"", L"", L"", L"", &AuditInfo)) { hr = myHLastError(); _JumpError(hr, error, "AuthzInitAuditInfoHandle"); } if(!AuthzCachedAccessCheck( 0, m_AuthzHandle, &m_Request, AuditInfo, &m_Reply)) { hr = myHLastError(); _JumpError(hr, error, "AuthzCachedAccessCheck"); } error: if(AuditInfo) { AuthzFreeAuditEvent(AuditInfo); } if(pAuditParams) { AuthziFreeAuditParams(pAuditParams); } FreeCachedHandles(); FreeAuditParamArray(pParamArray); return hr; } void CAuditEvent::FreeCachedHandles() { if(m_hClientToken) { CloseHandle(m_hClientToken); m_hClientToken = NULL; } if(m_AuthzHandle) { AuthzFreeHandle(m_AuthzHandle); m_AuthzHandle = NULL; } if(m_pCASD) { g_CASD.Unlock(); m_pCASD = NULL; } if(m_ClientContext) { AuthzFreeContext(m_ClientContext); m_ClientContext = NULL; } if(m_pUserSid) { LocalFree(m_pUserSid); m_pUserSid = NULL; } if(m_pSDPrivileges) { LocalFree(m_pSDPrivileges); m_pSDPrivileges = NULL; } if(m_pDaclPrivileges) { LocalFree(m_pDaclPrivileges); m_pDaclPrivileges = NULL; } } HRESULT CAuditEvent::RoleSeparationFlagSave(LPCWSTR pcwszSanitizedName) { return mySetCertRegDWValue( pcwszSanitizedName, NULL, NULL, wszREGROLESEPARATIONENABLED, RoleSeparationIsEnabled()?1:0); } HRESULT CAuditEvent::RoleSeparationFlagLoad(LPCWSTR pcwszSanitizedName) { DWORD dwFlags = 0; HRESULT hr = myGetCertRegDWValue( pcwszSanitizedName, NULL, NULL, wszREGROLESEPARATIONENABLED, &dwFlags); if(S_OK==hr) { RoleSeparationEnable(dwFlags?true:false); } return hr; } HRESULT CAuditEvent::GetPrivilegeRoles(PDWORD pdwRoles) { HRESULT hr = S_OK; PTOKEN_USER pTokenUser = NULL; DWORD cbTokenUser = 0; PTOKEN_GROUPS pTokenGroups = NULL; DWORD cbTokenGroups = 0; DWORD dwRoles = 0; LSA_HANDLE lsahPolicyHandle = NULL; LSA_OBJECT_ATTRIBUTES ObjectAttributes; NTSTATUS NTStatus; // first get roles for the user itself AuthzGetInformationFromContext( m_ClientContext, AuthzContextInfoUserSid, 0, &cbTokenUser, NULL); if(GetLastError()!=ERROR_INSUFFICIENT_BUFFER) { hr = myHLastError(); _JumpError(hr, error, "AuthzGetContextInformation"); } pTokenUser = (PTOKEN_USER)LocalAlloc(LMEM_FIXED, cbTokenUser); _JumpIfAllocFailed(pTokenUser, error); if(!AuthzGetInformationFromContext( m_ClientContext, AuthzContextInfoUserSid, cbTokenUser, &cbTokenUser, pTokenUser)) { hr = myHLastError(); _JumpError(hr, error, "AuthzGetContextInformation"); } // Object attributes are reserved, so initalize to zeroes. ZeroMemory(&ObjectAttributes, sizeof(ObjectAttributes)); NTStatus = LsaOpenPolicy( NULL, &ObjectAttributes, POLICY_LOOKUP_NAMES, &lsahPolicyHandle); if(STATUS_SUCCESS!=NTStatus) { hr = HRESULT_FROM_WIN32(LsaNtStatusToWinError(NTStatus)); _JumpError(hr, error, "LsaOpenPolicy"); } CSASSERT(!m_pUserSid); m_pUserSid = (PSID)LocalAlloc(LMEM_FIXED, GetLengthSid(pTokenUser->User.Sid)); _JumpIfAllocFailed(m_pUserSid, error); CopySid( GetLengthSid(pTokenUser->User.Sid), m_pUserSid, pTokenUser->User.Sid); hr = GetUserPrivilegeRoles(lsahPolicyHandle, &pTokenUser->User, &dwRoles); if(HRESULT_FROM_WIN32(ERROR_FILE_NOT_FOUND)==hr) { hr =S_OK; } _JumpIfError(hr, error, "CAuditEvent::GetUserPrivilegeRoles"); *pdwRoles |= dwRoles; // then find the roles assigned to the groups the user is member of AuthzGetInformationFromContext( m_ClientContext, AuthzContextInfoGroupsSids, 0, &cbTokenGroups, NULL); if(GetLastError()!=ERROR_INSUFFICIENT_BUFFER) { hr = myHLastError(); _JumpError(hr, error, "AuthzGetContextInformation"); } pTokenGroups = (PTOKEN_GROUPS)LocalAlloc(LMEM_FIXED, cbTokenGroups); _JumpIfAllocFailed(pTokenGroups, error); if(!AuthzGetInformationFromContext( m_ClientContext, AuthzContextInfoGroupsSids, cbTokenGroups, &cbTokenGroups, pTokenGroups)) { hr = myHLastError(); _JumpError(hr, error, "AuthzGetContextInformation"); } for(DWORD cGroups = 0; cGroupsGroupCount; cGroups++) { dwRoles = 0; hr = GetUserPrivilegeRoles( lsahPolicyHandle, &pTokenGroups->Groups[cGroups], &dwRoles); if(HRESULT_FROM_WIN32(ERROR_FILE_NOT_FOUND)==hr) { hr =S_OK; } _JumpIfError(hr, error, "CAuditEvent::GetUserPrivilegeRoles"); *pdwRoles |= dwRoles; } error: if(pTokenUser) { LocalFree(pTokenUser); } if(pTokenGroups) { LocalFree(pTokenGroups); } if(lsahPolicyHandle) { LsaClose(lsahPolicyHandle); } return hr; } HRESULT CAuditEvent::GetUserPrivilegeRoles( LSA_HANDLE lsah, PSID_AND_ATTRIBUTES pSA, PDWORD pdwRoles) { NTSTATUS NTStatus; PLSA_UNICODE_STRING pLSAString = NULL; ULONG cRights, c; NTStatus = LsaEnumerateAccountRights( lsah, pSA->Sid, &pLSAString, &cRights); if(STATUS_SUCCESS!=NTStatus) { return HRESULT_FROM_WIN32(LsaNtStatusToWinError(NTStatus)); } for(c=0; cConvertToString( &rpParamArray[c].String); _JumpIfError(hr, error, "ConvertToString"); } error: return hr; } void CAuditEvent::FreeAuditParamArray(PAUDIT_PARAM pParamArray) { if(pParamArray) { for(USHORT c=0;c