//+-------------------------------------------------------------------------- // File: callback.cpp // Contents: access check callback //--------------------------------------------------------------------------- #include #pragma hdrstop #include "csext.h" #include "certsd.h" #include #include #include "csprop.h" #include "sid.h" #include namespace CertSrv { HRESULT GetAccountSid( IN LPWSTR pwszName, PSID *ppSid) { HRESULT hr = S_OK; DWORD cbSid = 0; DWORD cbDomainName = 0; SID_NAME_USE use; LPWSTR pwszDomainName = NULL; *ppSid = NULL; if(!pwszName || L'\0'== pwszName[0]) { hr = GetEveryoneSID(ppSid); _JumpIfError(hr, error, "GetEveryoneSID"); } else { LookupAccountName( NULL, pwszName, NULL, &cbSid, NULL, &cbDomainName, &use); if(ERROR_INSUFFICIENT_BUFFER != GetLastError()) { hr = myHError(GetLastError()); _JumpError(hr, error, "LookupAccountName"); } *ppSid = (PSID)LocalAlloc(LMEM_FIXED, cbSid); if(!*ppSid) { hr = E_OUTOFMEMORY; _JumpError(hr, error, "LocalAlloc"); } pwszDomainName = (LPWSTR)LocalAlloc(LMEM_FIXED, cbDomainName*sizeof(WCHAR)); if(!pwszDomainName) { hr = E_OUTOFMEMORY; _JumpError(hr, error, "LocalAlloc"); } if(!LookupAccountName( NULL, pwszName, *ppSid, &cbSid, pwszDomainName, &cbDomainName, &use)) { hr = myHError(GetLastError()); _JumpError(hr, error, "LookupAccountName"); } } hr = S_OK; error: if(S_OK!=hr) { if(*ppSid) { LocalFree(*ppSid); } if(pwszDomainName) { LocalFree(pwszDomainName); } } return hr; } BOOL CallbackAccessCheck( IN AUTHZ_CLIENT_CONTEXT_HANDLE pAuthzClientContext, IN PACE_HEADER pAce, IN PVOID pArgs OPTIONAL, IN OUT PBOOL pbAceApplicable) { HRESULT hr = S_OK; LPWSTR pwszSamName = (LPWSTR)pArgs;// requester name is passed in to // AuthzAccessCheck in NT4 style form // "DomainNetbiosName\RequesterSAMName" PSID pSid = NULL, pClientSid = NULL, pCallerSid = NULL; PSID pEveryoneSid = NULL; PTOKEN_GROUPS pGroups = NULL; ACCESS_ALLOWED_CALLBACK_ACE* pCallbackAce = (ACCESS_ALLOWED_CALLBACK_ACE*)pAce; PSID_LIST pSidList = (PSID_LIST) (((BYTE*)&pCallbackAce->SidStart)+ GetLengthSid(&pCallbackAce->SidStart)); DWORD cSids, cClientSids; CSASSERT( ACCESS_ALLOWED_CALLBACK_ACE_TYPE == pAce->AceType || ACCESS_DENIED_CALLBACK_ACE_TYPE == pAce->AceType); CSASSERT(HeapValidate(GetProcessHeap(),0,NULL)); SetLastError(ERROR_SUCCESS); // get the SID for the requester hr = GetAccountSid(pwszSamName, &pCallerSid); CSASSERT(HeapValidate(GetProcessHeap(),0,NULL)); if(HRESULT_FROM_WIN32(ERROR_NONE_MAPPED)==hr) { // if name cannot be resolved, default to Everyone DWORD dwSize = sizeof(TOKEN_GROUPS); pGroups = (PTOKEN_GROUPS)LocalAlloc(LMEM_FIXED, sizeof(TOKEN_GROUPS)); if(!pGroups) { hr = E_OUTOFMEMORY; _JumpError(hr, error, "LocalAlloc"); } hr = GetEveryoneSID(&pEveryoneSid); _JumpIfError(hr, error, "GetEveryoneSID"); CSASSERT(HeapValidate(GetProcessHeap(),0,NULL)); pGroups->GroupCount=1; pGroups->Groups[0].Sid = pEveryoneSid; pGroups->Groups[0].Attributes = 0; } else { _JumpIfError(hr, error, "GetAccountSid"); // get the list of groups this SID is member of hr = GetMembership(g_AuthzCertSrvRM, pCallerSid, &pGroups); _JumpIfError(hr, error, "GetMembership"); CSASSERT(HeapValidate(GetProcessHeap(),0,NULL)); } CSASSERT(HeapValidate(GetProcessHeap(),0,NULL)); // traverse the SID list stored in the ACE and compare with the // client's membership for(pSid=(PSID)&pSidList->SidListStart, cSids=0; cSidsdwSidCount; cSids++, pSid = (PSID)(((BYTE*)pSid)+GetLengthSid(pSid))) { CSASSERT(IsValidSid(pSid)); // group membership doesn't include the user itself, so // compare with the user first if(pCallerSid && EqualSid(pSid, pCallerSid)) { *pbAceApplicable = TRUE; goto error; } for(cClientSids=0; cClientSidsGroupCount; cClientSids++) { pClientSid = pGroups->Groups[cClientSids].Sid; CSASSERT(IsValidSid(pClientSid)); if(EqualSid(pSid, pClientSid)) { *pbAceApplicable = TRUE; goto error; } } } *pbAceApplicable = FALSE; error: CSASSERT(HeapValidate(GetProcessHeap(),0,NULL)); if(pEveryoneSid) { LocalFree(pEveryoneSid); } if(pCallerSid) { LocalFree(pCallerSid); } if(pGroups) { LocalFree(pGroups); } if(S_OK==hr) { return TRUE; } else { SetLastError(HRESULT_CODE(hr)); return FALSE; } } HRESULT GetMembership( IN AUTHZ_RESOURCE_MANAGER_HANDLE AuthzRM, IN PSID pSid, PTOKEN_GROUPS *ppGroups) { HRESULT hr = S_OK; static LUID luid = {0,0}; AUTHZ_CLIENT_CONTEXT_HANDLE AuthzCC = NULL; DWORD dwSizeRequired; *ppGroups = NULL; if(!AuthzInitializeContextFromSid( 0, pSid, AuthzRM, NULL, luid, //ignored NULL, &AuthzCC)) { hr = myHError(GetLastError()); _JumpError(hr, error, "AuthzInitializeContextFromSid"); } if(!AuthzGetInformationFromContext( AuthzCC, AuthzContextInfoGroupsSids, 0, &dwSizeRequired, NULL)) { if(ERROR_INSUFFICIENT_BUFFER!=GetLastError()) { hr = myHError(GetLastError()); _JumpError(hr, error, "AuthzGetContextInformation"); } } *ppGroups = (PTOKEN_GROUPS)LocalAlloc(LMEM_FIXED, dwSizeRequired); if(!*ppGroups) { hr = E_OUTOFMEMORY; _JumpError(hr, error, "LocalAlloc"); } if(!AuthzGetInformationFromContext( AuthzCC, AuthzContextInfoGroupsSids, dwSizeRequired, &dwSizeRequired, *ppGroups)) { hr = myHError(GetLastError()); _JumpError(hr, error, "AuthzGetContextInformation"); } error: if(AuthzCC) { AuthzFreeContext(AuthzCC); } if(S_OK!=hr && *ppGroups) { LocalFree(*ppGroups); } return hr; } HRESULT GetRequesterName(DWORD dwRequestId, LPWSTR *ppwszName) { HRESULT hr = S_OK; ICertDBRow *prow = NULL; 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 **) ppwszName); _JumpIfError(hr, error, "PKCSGetProperty"); error: if(prow) { prow->Release(); } return hr; } HRESULT CheckOfficerRights(DWORD dwRequestID, CAuditEvent& event) { HRESULT hr = S_OK; LPWSTR pwszRequesterName = NULL; // officer rights disabled means every officer is allowed to manage requests // for everyone, so return ok if(!g_OfficerRightsSD.IsEnabled()) return S_OK; hr = GetRequesterName(dwRequestID, &pwszRequesterName); if(CERTSRV_E_PROPERTY_EMPTY!=hr && S_OK != hr) { _JumpError(hr, error, "GetRequesterName"); } hr = CheckOfficerRights(pwszRequesterName, event); error: if(pwszRequesterName) { LocalFree(pwszRequesterName); } return hr; } // Verify if impersonated user has the rights over the specified request, // based on the officer rights defined in the global officer SD and the // requester name stored in the request // Return S_OK if allowed or if the officer rights feature is disabled // E_ACCESSDENIED if not allowed // E_* if failed to check the rights HRESULT CheckOfficerRights(LPCWSTR pwszRequesterName, CAuditEvent& event) { HRESULT hr = S_OK; IServerSecurity *pISS = NULL; HANDLE hThread = NULL; HANDLE hToken = NULL; PSECURITY_DESCRIPTOR pOfficerSD = NULL; static LUID luid = {0,0}; AUTHZ_CLIENT_CONTEXT_HANDLE AuthzCC = NULL; AUTHZ_ACCESS_REQUEST AuthzRequest; AUTHZ_ACCESS_REPLY AuthzReply; ACCESS_MASK GrantedMask; DWORD dwError = 0; DWORD dwSaclEval = 0; bool fImpersonating = false; // officer rights disabled means every officer is allowed to manage requests // for everyone, so return ok if(!g_OfficerRightsSD.IsEnabled()) return S_OK; hr = CoGetCallContext(IID_IServerSecurity, (void**)&pISS); _JumpIfError(hr, error, "CoGetCallContext"); if (!pISS->IsImpersonating()) { hr = pISS->ImpersonateClient(); _JumpIfError(hr, error, "ImpersonateClient"); } else { pISS->Release(); pISS = NULL; } hThread = GetCurrentThread(); if (NULL == hThread) { hr = myHLastError(); _JumpIfError(hr, error, "GetCurrentThread"); } if (!OpenThreadToken(hThread, TOKEN_QUERY, FALSE, // client impersonation &hToken)) { hr = myHLastError(); _JumpIfError(hr, error, "OpenThreadToken"); } if(!AuthzInitializeContextFromToken( 0, hToken, g_AuthzCertSrvRM, NULL, luid, NULL, &AuthzCC)) { hr = myHLastError(); _JumpError(hr, error, "AuthzInitializeContextFromToken"); } CloseHandle(hToken); hToken = NULL; if(pISS) // impersonating { pISS->RevertToSelf(); pISS->Release(); pISS = NULL; } AuthzRequest.DesiredAccess = DELETE; AuthzRequest.PrincipalSelfSid = NULL; AuthzRequest.ObjectTypeList = NULL; AuthzRequest.ObjectTypeListLength = 0; AuthzRequest.OptionalArguments = (void*)pwszRequesterName; AuthzReply.ResultListLength = 1; AuthzReply.GrantedAccessMask = &GrantedMask; AuthzReply.Error = &dwError; AuthzReply.SaclEvaluationResults = &dwSaclEval; hr = g_OfficerRightsSD.LockGet(&pOfficerSD); _JumpIfError(hr, error, "CProtectedSecurityDescriptor::LockGet"); CSASSERT(IsValidSecurityDescriptor(pOfficerSD)); if(!AuthzAccessCheck( 0, AuthzCC, &AuthzRequest, NULL, //no audit pOfficerSD, NULL, 0, &AuthzReply, NULL)) { hr = myHLastError(); _JumpError(hr, error, "AuthzAccessCheck"); } hr = AuthzReply.Error[0]==ERROR_SUCCESS?S_OK:CERTSRV_E_RESTRICTEDOFFICER; error: if(AuthzCC) { AuthzFreeContext(AuthzCC); } if(pOfficerSD) { g_OfficerRightsSD.Unlock(); } if (NULL != hThread) { CloseHandle(hThread); } if (NULL != hToken) { CloseHandle(hToken); } if (NULL != pISS) { pISS->RevertToSelf(); pISS->Release(); } // generate a failure audit event if restricted officer if(CERTSRV_E_RESTRICTEDOFFICER==hr) { HRESULT hrtemp = event.AccessCheck( CA_ACCESS_DENIED, event.m_gcNoAuditSuccess); if(S_OK!=hrtemp && E_ACCESSDENIED!=hrtemp) hr = hrtemp; } return hr; } } // namespace CertSrv