バグ: SQL Server 2005 がネットワーク サービス アカウントで実行されているときに証明書を使用して暗号化を有効ことはできません。


BUG #: 374329 (SQLBUDT)

現象


Microsoft SQL Server 2005 がネットワーク サービス アカウントで実行している場合、証明書を使用して暗号化を有効にすることはできません。暗号化で使用する証明書を提供する場合、SQL Server は起動しません。さらに、SQL Server エラー ログに次のメッセージが発生する可能性があります。

日付時間サーバーのサーバーは、SSL 接続を開始する必要のある証明書を読み込めませんでした。次のエラーが返されます: 0x8009030d。有効であるかどうかを確認する証明書を確認してください。
日付時間 サーバー エラー: 26014、レベル: 16、状態: 1 です。

日付時間サーバー ユーザー指定の証明書をロードできませんでした。サーバーは接続を受け付けません。証明書が正しくインストールされていることを確認する必要があります。オンライン」を構成する証明書を SSL で使用する書籍を参照してください。

日付時間サーバー エラー: 17195、重大度: 16、状態: 1 です。

日付時間サーバーでは、サーバーが暗号化を要求するように構成されていますが、ネットワーク ライブラリは、暗号化をサポートできないため起動ができません。
日付時間 サーバー エラー: 17182、レベル: 16、状態: 1 です。

原因


この問題は、ネットワーク サービス アカウントが秘密キー コンテナーの読み取りアクセス許可を持たないために発生します。

解決策


この問題を解決するには、コンパイルし、次の .cpp のコードを実行します。

注: このコードは、コンピューターのストアに、ネットワーク サービス アカウントのアクセスで実行されているすべてのサービスを付与します。このコードを慎重に使用します。
/*
MKACLS - This utility will list the machine key containers present and allow
the user to change the key container DACL.
*/

#include <stdio.h>
#include <windows.h>
#include <sddl.h>
#include <aclapi.h>
#include <lm.h>

//Get SD for retrieving DACL.
SECURITY_DESCRIPTOR* GetSecurityDescDacl(HCRYPTPROV hProv) {

SECURITY_DESCRIPTOR *sd;
unsigned long size = 0;

CryptGetProvParam(
hProv,
PP_KEYSET_SEC_DESCR,
0,
&size,
DACL_SECURITY_INFORMATION);

int ret = GetLastError();
if (ret != ERROR_INSUFFICIENT_BUFFER) {
fprintf(stderr, "Error getting file security DACL: %d\n", ret);
return 0;
}

sd = (SECURITY_DESCRIPTOR *) malloc(size);
if (! sd) {
fprintf(stderr, "Out of memory for security descriptor!\n");
return 0;
}

CryptGetProvParam(
hProv,
PP_KEYSET_SEC_DESCR,
(BYTE*)sd,
&size,
DACL_SECURITY_INFORMATION);

return sd;
}

//Get DACL from SD.
ACL* GetDacl(SECURITY_DESCRIPTOR *sd) {

ACL *acl;
int defaulted, present;

if (! sd) return 0;

if (! GetSecurityDescriptorDacl(
sd,
&present,
&acl,
&defaulted))
{
fprintf(stderr, "Error getting DACL from security descriptor: %d\n", GetLastError());
return 0;
}

if (! present) {
fprintf(stderr, "Security descriptor has no DACL present\n");
free(acl);
return 0;
}

return acl;
}


ACL *AddSidToAcl(const ACL *pOldACL, PSID pSID)
{
EXPLICIT_ACCESS ea[1] ={{0}};

// Initialize an EXPLICIT_ACCESS structure for an access control entry.
ea[0].grfAccessPermissions = FILE_READ_DATA;
ea[0].grfAccessMode = SET_ACCESS;
ea[0].grfInheritance= NO_INHERITANCE;
ea[0].Trustee.TrusteeForm = TRUSTEE_IS_SID;
ea[0].Trustee.TrusteeType = TRUSTEE_IS_USER;
ea[0].Trustee.ptstrName = (LPTSTR) pSID;

// Create a new DACL that contains the new access control entries and the old ones.

ACL *pNewACL = NULL;
DWORD dwRes = SetEntriesInAcl(1, ea, (ACL*)pOldACL, &pNewACL);
if (ERROR_SUCCESS == dwRes)
return pNewACL;

return NULL;
}


BOOL SetSecurityDescDacl(HCRYPTPROV hProv, const ACL *pACL)
{
PSECURITY_DESCRIPTOR pSD = NULL;
BOOL bRetVal = FALSE;

// Initialize a security descriptor.
pSD = (PSECURITY_DESCRIPTOR) LocalAlloc(LPTR, SECURITY_DESCRIPTOR_MIN_LENGTH);
if (NULL == pSD)
{
printf("LocalAlloc Error %u\n", GetLastError());
goto CommonReturn;
}

if (!InitializeSecurityDescriptor(pSD, SECURITY_DESCRIPTOR_REVISION))
{
printf("InitializeSecurityDescriptor Error %u\n",GetLastError());
goto CommonReturn;
}

// Add the DACL to the security descriptor.
if (!SetSecurityDescriptorDacl(pSD,
TRUE,
(ACL*)pACL,
FALSE))
{
printf("SetSecurityDescriptorDacl Error %u\n", GetLastError());
goto CommonReturn;
}

if(!CryptSetProvParam(
hProv,
PP_KEYSET_SEC_DESCR,
(BYTE*)pSD,
DACL_SECURITY_INFORMATION))
{
printf("CryptSetProvParam failed, lasterror = 0x%08x\n", GetLastError());
goto CommonReturn;
}

bRetVal = TRUE;

CommonReturn:
if (pSD)
LocalFree(pSD);

return bRetVal;
}




PSID GetSIDForAccount(LPCSTR pszAcct)
{
PSIDpSID = NULL;
DWORDcbSID = 0;
DWORD cbDomain = 0;
LPTSTR pszDomain = NULL;
SID_NAME_USE snu;


LookupAccountName(NULL, pszAcct, NULL, &cbSID, NULL, &cbDomain, &snu);

if ( cbSID )
{
pSID = (PSID)malloc(cbSID);
pszDomain = (LPTSTR)malloc(cbDomain);

if ( pSID && pszDomain)
{
if ( LookupAccountName(NULL, pszAcct, pSID, &cbSID, pszDomain, &cbDomain, &snu) )
{
// Success. Therefore, kill the domain buffer that we do not need.
free(pszDomain);
pszDomain = NULL;
}
else
{
// Failed. Therefore, kill both buffers.
free(pszDomain);
pszDomain = NULL;
free(pSID);
pSID = NULL;
}
}
}

return pSID;
}

BOOL ChangeContainerACL(LPCSTR pszContainer)
{
HCRYPTPROV hProv = 0;
BOOL bRetVal = FALSE;

if(CryptAcquireContext(&hProv,
pszContainer,
MS_DEF_PROV,
PROV_RSA_FULL,
CRYPT_MACHINE_KEYSET))
{

SECURITY_DESCRIPTOR *pSD = GetSecurityDescDacl(hProv);
if (pSD)
{
ACL *pCurACL = GetDacl(pSD);
if (pCurACL)
{
//TODO: This should be replaced with a call to RtlCreateServiceSid for LH.
PSID pSID = GetSIDForAccount("NetworkService");

if ( pSID)
{
ACL *pNewACL = AddSidToAcl(pCurACL, pSID);
if ( pNewACL )
{
bRetVal = SetSecurityDescDacl(hProv, pNewACL);
LocalFree(pNewACL);
}

free(pSID);
}

LocalFree(pCurACL);
}
free(pSD);
}

CryptReleaseContext(hProv, 0);
}
else
printf("Error opening key container %s\n", pszContainer);

return bRetVal;
}

int __cdecl main()
{
HCRYPTPROV hProv= 0;

printf("Looking for CAPI machine key containers...\n");

if(CryptAcquireContext(&hProv,
NULL,
MS_DEF_PROV,
PROV_RSA_FULL,
CRYPT_VERIFYCONTEXT | CRYPT_MACHINE_KEYSET))
{

for (int i=0 ; ; i++)
{
DWORD dwFlags = i ? 0 : CRYPT_FIRST;
const DWORD BUFLEN = 4096;
DWORD dwBufLen = BUFLEN;
BYTE szBuf[BUFLEN];

if(!CryptGetProvParam(hProv, PP_ENUMCONTAINERS, szBuf, &dwBufLen, dwFlags))
{
DWORD dwError = GetLastError();
if( dwError != ERROR_NO_MORE_ITEMS && dwError != ERROR_FILE_NOT_FOUND)
printf("Error reading container name - %08x\n", GetLastError());
break;
}
else
{
if ( !ChangeContainerACL((LPCSTR)szBuf) )
printf("Error changing ACL on container %s\n", szBuf);
else
printf("Changed ACL on container %s\n", szBuf);
}

}

CryptReleaseContext(hProv,0);
}
else
printf("Error opening CSP - %08x\n", GetLastError());

printf("Done\n");

return 0;
}
このコードは、プライベート キー コンテナーを読み取るためのネットワーク サービス アカウントのアクセス許可を付与します。このコードを実行した後、SQL Server を起動すると、SQL Server エラー ログに次のメッセージをことがあります。

日付時間サーバー証明書は暗号化のため正常に読み込まれました。

状況


マイクロソフトでは、この問題をこの資料の「対象製品」として記載されている マイクロソフト製品の問題として認識しています。