Platform SDK: Active Directory, ADSI, and Directory Services |
This sample code shows the basic steps of installing a directory-enabled service on a host computer. It performs the following operations:
This sample works correctly regardless of whether the logon account is a local or domain user account or the LocalSystem account. For a domain user account, the szServiceAccountSAM parameter contains the domain\username name of the account, and the szServiceAccountDN parameter contains the distinguished name of the user account object in the directory. For the LocalSystem account, szServiceAccountSAM and szPassword are NULL, and szServiceAccountSN is the distinguished name of the local computer's account object in the directory. If szServiceAccountSAM specifies a local user account (name format is ".\username"), the sample code skips the SPN registration because mutual authentication is not supported for local user accounts.
Note that the default security configuration allows only domain administrators to execute this code.
Note that this code as written must be executed on the computer where the service is being installed. Consequently, it is typically in a separate installation executable from your service installation code, if any, that extends the schema, extends the UI, or sets up group policy. Those operations install service components for an entire a forest, whereas this code installs the service on a single computer.
void InstallServiceOnLocalComputer( LPTSTR szServiceAccountDN, // distinguished name of logon account. LPTSTR szServiceAccountSAM, // SAM name of logon account. LPTSTR szPassword) // Password of logon account. { SC_HANDLE schService = NULL; SC_HANDLE schSCManager = NULL; TCHAR szPath[512]; LPTSTR lpFilePart; TCHAR szDNofSCP[MAX_PATH]; TCHAR szServiceClass[]=TEXT("ADSockAuth"); DWORD dwStatus; TCHAR **pspn=NULL; ULONG ulSpn=1; // Get the full path of the service's executable. // The sample assumes that the executable is in the current directory. dwStatus = GetFullPathName(TEXT("service.exe"), 512, szPath, &lpFilePart); if (dwStatus == 0) { _tprintf(TEXT("Unable to install %s - %s\n"), TEXT(SZSERVICEDISPLAYNAME), GetLastErrorText(szErr, 256)); return; } _tprintf(TEXT("path of service.exe: %s\n"), szPath); // Open the Service Control Manager on the local computer. schSCManager = OpenSCManager( NULL, // computer (NULL == local) NULL, // database (NULL == default) SC_MANAGER_ALL_ACCESS // access required ); if (! schSCManager) { _tprintf(TEXT("OpenSCManager failed - %s\n"), GetLastErrorText(szErr,256)); goto cleanup; } // Install the service in the SCM database. schService = CreateService( schSCManager, // SCManager database TEXT(SZSERVICENAME), // name of service TEXT(SZSERVICEDISPLAYNAME), // name to display SERVICE_ALL_ACCESS, // desired access SERVICE_WIN32_OWN_PROCESS, // service type SERVICE_DEMAND_START, // start type SERVICE_ERROR_NORMAL, // error control type szPath, // service's binary NULL, // no load ordering group NULL, // no tag identifier TEXT(SZDEPENDENCIES), // dependencies szServiceAccountSAM, // service account szPassword); // account password if (! schService) { _tprintf(TEXT("CreateService failed - %s\n"), GetLastErrorText(szErr,256)); goto cleanup; } _tprintf(TEXT("%s installed.\n"), TEXT(SZSERVICEDISPLAYNAME) ); // Create the service's Service Connection Point (SCP). dwStatus = ScpCreate( 2000, // Service's default port number szServiceClass, // Specifies the service class string. szServiceAccountSAM, // SAM name of logon account for ACE szDNofSCP // Buffer returns the DN of the SCP. ); if (dwStatus != 0) { _tprintf(TEXT("ScpCreate failed: %d\n"), dwStatus ); DeleteService(schService); goto cleanup; } // Compose and register a service principal name for this service. // We do this on the install path because this requires elevated // privileges for updating the directory. // If a local account of the format ".\username", skip the SPN. if ( szServiceAccountSAM[0] == '.' ) { _tprintf(TEXT("Don't register SPN for a local account.\n")); goto cleanup; } dwStatus = SpnCompose( &pspn, // Receives pointer to the SPN array. &ulSpn, // Receives number of SPNs returned. szDNofSCP, // Input: DN of the SCP. szServiceClass); // Input: the service's class string. if (dwStatus == NO_ERROR) dwStatus = SpnRegister( szServiceAccountDN, // Account on which SPNs are registered. pspn, // Array of SPNs to register. ulSpn, // Number of SPNs in array. DS_SPN_ADD_SPN_OP); // Operation code: Add SPNs. if (dwStatus != NO_ERROR) { _tprintf(TEXT("Failed to compose SPN: Error was %X\n"), dwStatus); DeleteService(schService); ScpDelete(szDNofSCP, szServiceClass, szServiceAccountDN); goto cleanup; } cleanup: if (schSCManager) CloseServiceHandle(schSCManager); if (schService) CloseServiceHandle(schService); DsFreeSpnArray(ulSpn, pspn); return; }
For more information about operations performed in the previous example, see Composing the SPNs for a Service with an SCP and Registering the SPNs for a Service.