Platform SDK: Active Directory, ADSI, and Directory Services |
The following sample code sets a pair of ACEs on a service connection point (SCP) object. The ACEs grant read/write access to the user or computer account under which the service instance will be running. Your service installation program calls this code to ensure that the service will be allowed to update its properties at run time. If you don't set ACEs like these, your service will get access-denied errors if it tries to modify the SCP's properties.
Typically, your service installation program would set these ACEs right after creating the SCP object. For sample code that creates an SCP and calls this function, see How Clients Find and Use a Service Connection Point. If the service is reconfigured to run under a different account, the ACEs would need to be updated. Note that this code needs to be run in the security context of a domain administrator.
The first parameter of the sample function specifies the name of the user account to be granted access. The function assumes the name is in domain\account format. If no account is specified, the function assumes the service uses the LocalSystem account, which means it must grant access to the computer account of the host server on which the service is running. To do this, the example calls the GetComputerObjectName function to get the domain\account name of the local computer.
Note that the code could be easily modified to grant the service full access to the SCP object. But the best practice is to grant only the specific access rights that the service might need at run time. In this case, it grants access to two properties:
Each property is identified by the schemaIDGUID of the property's attributeSchema class. Every property in the schema has its own unique schemaIDGUID. The code uses strings to specify the GUIDs. The GUID strings have the following format:
hr = pACE1->put_ObjectType( L"{28630eb8-41d5-11d1-a9c1-0000f80367c1}" ); // serviceDNSName
Refer to the Active Directory Schema reference pages for the schemaIDGUID values assigned to the properties you want to grant or deny access to.
The code uses the IADsSecurityDescriptor, IADsAccessControlList, and IADsAccessControlEntry interfaces to do the following:
HRESULT AllowAccessToScpProperties( LPOLESTR szAccountSAM, // Service account to allow access. IADs *pSCPObject) // IADs pointer to the SCP object. { TCHAR szServerSAMName[512]; DWORD dwLen; LPOLESTR szTrustee; VARIANT varSD; HRESULT hr = E_FAIL; IADsAccessControlList *pACL = NULL; IADsSecurityDescriptor *pSD = NULL; IDispatch *pDisp = NULL; IADsAccessControlEntry *pACE1 = NULL; IADsAccessControlEntry *pACE2 = NULL; IDispatch *pDispACE = NULL; long lFlags = 0L; LPOLESTR szAttribute = L"nTSecurityDescriptor"; // If no service account is specified, service runs under LocalSystem. // So allow access to the computer account of the service's host. if (szAccountSAM) szTrustee = szAccountSAM; else { // Get the SAM account name of the computer object for the server. dwLen = sizeof(szServerSAMName); if (!GetComputerObjectName(NameSamCompatible, szServerSAMName, &dwLen)) return GetLastError(); _tprintf(TEXT("GetComputerObjectName: %s\n"), szServerSAMName); szTrustee = szServerSAMName; } VariantClear(&varSD); // Get the nTSecurityDescriptor hr = pSCPObject->Get(szAttribute, &varSD); if (FAILED(hr) || (varSD.vt!=VT_DISPATCH)) { _tprintf(TEXT("Get nTSecurityDescriptor failed: 0x%x\n"), hr); goto cleanup; } // Use the V_DISPATCH macro to get the IDispatch pointer from VARIANT // structure and QueryInterface for an IADsSecurityDescriptor pointer. hr = V_DISPATCH( &varSD )->QueryInterface(IID_IADsSecurityDescriptor, (void**)&pSD); if (FAILED(hr)) { _tprintf(TEXT("Couldn't get IADsSecurityDescriptor: 0x%x\n"), hr); goto cleanup; } // Get an IADsAccessControlList pointer to the security descriptor's DACL. hr = pSD->get_DiscretionaryAcl(&pDisp); if (SUCCEEDED(hr)) hr = pDisp->QueryInterface(IID_IADsAccessControlList,(void**)&pACL); if (FAILED(hr)) { _tprintf(TEXT("Couldn't get DACL: 0x%x\n"), hr); goto cleanup; } // Create the COM object for the first ACE. hr = CoCreateInstance(CLSID_AccessControlEntry, NULL, CLSCTX_INPROC_SERVER, IID_IADsAccessControlEntry, (void **)&pACE1); // Create the COM object for the second ACE. if (SUCCEEDED(hr)) hr = CoCreateInstance(CLSID_AccessControlEntry, NULL, CLSCTX_INPROC_SERVER, IID_IADsAccessControlEntry, (void **)&pACE2); if (FAILED(hr)) { _tprintf(TEXT("Couldn't create ACEs: 0x%x\n"), hr); goto cleanup; } // Set the properties of the two ACEs. // Allow read and write access to the property. hr = pACE1->put_AccessMask( ADS_RIGHT_DS_READ_PROP | ADS_RIGHT_DS_WRITE_PROP ); hr = pACE2->put_AccessMask( ADS_RIGHT_DS_READ_PROP | ADS_RIGHT_DS_WRITE_PROP ); // Set the trustee, which is either the service account or the // host computer account. hr = pACE1->put_Trustee( szTrustee ); hr = pACE2->put_Trustee( szTrustee ); // Set the ACE type. hr = pACE1->put_AceType( ADS_ACETYPE_ACCESS_ALLOWED_OBJECT ); hr = pACE2->put_AceType( ADS_ACETYPE_ACCESS_ALLOWED_OBJECT ); // Set AceFlags to zero because ACE is not inheritable. hr = pACE1->put_AceFlags( 0 ); hr = pACE2->put_AceFlags( 0 ); // Set Flags to indicate an ACE that protects a specified object. hr = pACE1->put_Flags( ADS_FLAG_OBJECT_TYPE_PRESENT ); hr = pACE2->put_Flags( ADS_FLAG_OBJECT_TYPE_PRESENT ); // Set ObjectType to the schemaIDGUID of the attribute. hr = pACE1->put_ObjectType( L"{28630eb8-41d5-11d1-a9c1-0000f80367c1}" ); // serviceDNSName hr = pACE2->put_ObjectType( L"{b7b1311c-b82e-11d0-afee-0000f80367c1}" ); // serviceBindingInformation // Add the ACEs to the DACL. Need an IDispatch pointer for each ACE // to pass to the AddAce method. hr = pACE1->QueryInterface(IID_IDispatch,(void**)&pDispACE); if (SUCCEEDED(hr)) hr = pACL->AddAce(pDispACE); if (FAILED(hr)) { _tprintf(TEXT("Couldn't add first ACE: 0x%x\n"), hr); goto cleanup; } else { if (pDispACE) pDispACE->Release(); pDispACE = NULL; } // Do it again for the second ACE. hr = pACE2->QueryInterface(IID_IDispatch, (void**)&pDispACE); if (SUCCEEDED(hr)) hr = pACL->AddAce(pDispACE); if (FAILED(hr)) { _tprintf(TEXT("Couldn't add second ACE: 0x%x\n"), hr); goto cleanup; } // Write the modified DACL back to the security descriptor. hr = pSD->put_DiscretionaryAcl(pDisp); if (SUCCEEDED(hr)) { // Write the ntSecurityDescriptor property to the property cache. hr = pSCPObject->Put(szAttribute, varSD); if (SUCCEEDED(hr)) { // SetInfo updates the SCP object in the directory. hr = pSCPObject->SetInfo(); } } cleanup: if (pDispACE) pDispACE->Release(); if (pACE1) pACE1->Release(); if (pACE2) pACE2->Release(); if (pACL) pACL->Release(); if (pDisp) pDisp->Release(); if (pSD) pSD->Release(); VariantClear(&varSD); return hr; }