/*++ Copyright (c) 1992 Microsoft Corporation Module Name: scopen.cxx Abstract: Functions for handling opening and closing of Service and ServiceController handles. ROpenSCManagerW ROpenServiceW RCloseServiceHandle SC_RPC_HANDLE_rundown ScCreateScManagerHandle ScCreateServiceHandle ScIsValidScManagerHandle ScIsValidServiceHandle ScIsValidScManagerOrServiceHandle Author: Dan Lafferty (danl) 20-Jan-1992 Environment: User Mode - Win32 Revision History: 20-Jan-1992 danl Created 10-Apr-1992 JohnRo Added ScIsValidServiceHandle(). Export ScCreateServiceHandle() for RCreateService() too. 14-Apr-1992 JohnRo Added ScIsValidScManagerHandle(). 22-Feb-1995 AnirudhS RCloseServiceHandle: Pass the handle, rather than the address of the handle, to the auditing routine. --*/ #include "precomp.hxx" #include // wide character c runtimes. #include // Unicode string macros #include "scsec.h" // ScAccessValidate #include "sclib.h" // ScIsValidServiceName //-------------------------------------------------------------------// // // // Local function prototypes // // // //-------------------------------------------------------------------// DWORD ScCreateScManagerHandle( IN LPWSTR DatabaseName, OUT LPSC_HANDLE_STRUCT *ContextHandle ); //-------------------------------------------------------------------// // // // Functions // // // //-------------------------------------------------------------------// DWORD ROpenSCManagerW( IN LPWSTR lpMachineName, IN LPWSTR lpDatabaseName, IN DWORD dwDesiredAccess OPTIONAL, OUT LPSC_RPC_HANDLE lpScHandle ) /*++ Routine Description: Arguments: lpMachineName - lpDatabaseName - dwDesiredAccess - lpScHandle - Return Value: NO_ERROR - The operation was successful. ERROR_INVALID_NAME - lpDatabaseName is invalid ERROR_DATABASE_DOES_NOT_EXIST - Valid database name but database does not exist. ERROR_ACCESS_DENIED - dwDesiredAccess specifies accesses that are not granted to the client, or contains invalid bits. ERROR_NOT_ENOUGH_MEMORY - Could not allocated memory for context handle. --*/ { LPSC_HANDLE_STRUCT scManagerHandle; DWORD error; LPWSTR RequestedDatabase = SERVICES_ACTIVE_DATABASEW; if (ScShutdownInProgress) { return(ERROR_SHUTDOWN_IN_PROGRESS); } // // This parameter got us to the server side and is uninteresting // once we get here. // UNREFERENCED_PARAMETER(lpMachineName); // // Validate specified database name // if (ARGUMENT_PRESENT(lpDatabaseName)) { if ((_wcsicmp(lpDatabaseName, SERVICES_ACTIVE_DATABASEW) != 0) && (_wcsicmp(lpDatabaseName, SERVICES_FAILED_DATABASEW) != 0)) { return ERROR_INVALID_NAME; } else if ((_wcsicmp(lpDatabaseName, SERVICES_FAILED_DATABASEW) == 0) && (TRUE)) { // // CODEWORK: Actually implement a ServicesFailed database // at some point in the future and check for it // in place of the (TRUE) above. // // // ServicesFailed database does not exist // return ERROR_DATABASE_DOES_NOT_EXIST; } else { RequestedDatabase = lpDatabaseName; } } // // Allocate context handle structure and save the database name in it // if ((error = ScCreateScManagerHandle( RequestedDatabase, &scManagerHandle )) != NO_ERROR) { return error; } // // Make sure the desired access specified is valid and allowed to // the client. Save away the desired access in the handle structure. // if ((error = ScAccessValidate( scManagerHandle, (dwDesiredAccess | SC_MANAGER_CONNECT) )) != NO_ERROR) { SC_LOG(ERROR,"ROpenSCManagerW:ScAccessValidate Failed %u\n", error); (void) LocalFree(scManagerHandle); return error; } // // return the pointer to the handle struct as the context handle for // this open. // *lpScHandle = (SC_RPC_HANDLE)scManagerHandle; SC_LOG(HANDLE,"SC Manager Handle Opened 0x%08lx\n",*lpScHandle); return(NO_ERROR); } DWORD ROpenServiceW( IN SC_RPC_HANDLE hSCManager, IN LPWSTR lpServiceName, IN DWORD dwDesiredAccess, OUT LPSC_RPC_HANDLE phService ) /*++ Routine Description: Returns a handle to the service. This handle is actually a pointer to a data structure that contains a pointer to the service record. Arguments: hSCManager - This is a handle to this service controller. It is an RPC context handle, and has allowed the request to get this far. lpServiceName - This is a pointer to a string containing the name of the service dwDesiredAccess - This is an access mask that contains a description of the access that is desired for this service. phService - This is a pointer to the location where the handle to the service is to be placed. Return Value: NO_ERROR - The operation was successful. ERROR_INVALID_HANDLE - The specified ScManager handle is invalid. ERROR_SERVICE_DOES_NOT_EXIST - The specified service does not exist in the database. ERROR_NOT_ENOUGH_MEMORY - The memory allocation for the handle structure failed. ERROR_INVALID_NAME - Service name contains invalid character or name is too long. Note: --*/ { LPSC_HANDLE_STRUCT serviceHandle; DWORD status; LPSERVICE_RECORD serviceRecord; if (ScShutdownInProgress) { return(ERROR_SHUTDOWN_IN_PROGRESS); } // // Check the handle. // if (!ScIsValidScManagerHandle(hSCManager)) { return ERROR_INVALID_HANDLE; } // // Validate the format of the service name. // if (! ScIsValidServiceName(lpServiceName)) { return(ERROR_INVALID_NAME); } // // Find the service record in the database. // CServiceListSharedLock LLock; CServiceRecordExclusiveLock RLock; status = ScGetNamedServiceRecord( lpServiceName, &serviceRecord); if (status != NO_ERROR) { return(status); } // // Allocate context handle structure and save the service record // pointer in it. // if ((status = ScCreateServiceHandle( serviceRecord, &serviceHandle )) != NO_ERROR) { return(status); } // // Make sure the desired access specified is valid and allowed to // the client. Save away the desired access in the handle structure. // if ((status = ScAccessValidate( serviceHandle, dwDesiredAccess )) != NO_ERROR) { SC_LOG(ERROR,"ROpenServiceW:ScAccessValidate Failed %u\n", status); (void) LocalFree(serviceHandle); return(status); } // // Additional check is required if the SCManager points to a database // other than the active one. Execute accesses are not allowed. // if (_wcsicmp( ((LPSC_HANDLE_STRUCT)hSCManager)->Type.ScManagerObject.DatabaseName, SERVICES_ACTIVE_DATABASEW ) != 0) { if (dwDesiredAccess & MAXIMUM_ALLOWED) { // // MAXIMUM_ALLOWED is requested. Remove bits for execute accesses. // serviceHandle->AccessGranted &= ~(SERVICE_STOP | SERVICE_START | SERVICE_PAUSE_CONTINUE | SERVICE_INTERROGATE | SERVICE_USER_DEFINED_CONTROL); } else if ((serviceHandle->AccessGranted & (SERVICE_STOP | SERVICE_START | SERVICE_PAUSE_CONTINUE | SERVICE_INTERROGATE | SERVICE_USER_DEFINED_CONTROL)) != 0) { // // Deny access if any execute access is requested. // SC_LOG( SECURITY, "ROpenServiceW:Non-active database, execute accesses not allowed\n", 0 ); (void) LocalFree(serviceHandle); return(ERROR_ACCESS_DENIED); } } // // Increment the UseCount. The service record cannot be deleted // as long as the UseCount is greater than zero. // serviceRecord->UseCount++; SC_LOG2(USECOUNT, "ROpenServiceW: " FORMAT_LPWSTR " increment USECOUNT=%lu\n", serviceRecord->ServiceName, serviceRecord->UseCount); // // return the pointer to the handle struct as the handle for this // open. // *phService = (SC_RPC_HANDLE)serviceHandle; SC_LOG(HANDLE,"Service Handle Opened 0x%lx\n",*phService); return (NO_ERROR); } DWORD RCloseServiceHandle( IN OUT SC_RPC_HANDLE *phSCObject ) /*++ Routine Description: This function closes a handle to a service or to the service controller by freeing the data structure that the handle points to. Arguments: phSCObject - This is a pointer to a pointer to the context handle structure. Return Value: NO_ERROR - The operation was successful. ERROR_INVALID_HANDLE - The handle is invalid. It does not point to a recognizable structure. Note: --*/ { NTSTATUS status; HLOCAL FreeStatus; UNICODE_STRING Subsystem; ULONG privileges[1]; SC_HANDLE_TYPE HandleType; // // Check the handle // if (!ScIsValidScManagerOrServiceHandle(*phSCObject, &HandleType)) { return ERROR_INVALID_HANDLE; } // // If it is a service handle being closed, decrement the use count. // If the count goes to zero, and the service is marked for deletion, // it will get deleted. // if (HandleType == SC_HANDLE_TYPE_SERVICE) { RPC_STATUS RpcStatus; BOOL fImpersonated = TRUE; { CServiceRecordExclusiveLock RLock; ScDecrementUseCountAndDelete( ((LPSC_HANDLE_STRUCT)*phSCObject)->Type.ScServiceObject.ServiceRecord); } // // Get Audit Privilege // privileges[0] = SE_AUDIT_PRIVILEGE; status = ScGetPrivilege( 1, privileges); if (status != NO_ERROR) { SC_LOG1(ERROR, "RCloseServiceHandle: ScGetPrivilege (Enable) failed: %d\n", status); } // // Generate the audit -- must be done as the user as per C2 requirements. // RpcStatus = RpcImpersonateClient(NULL); if (RpcStatus != RPC_S_OK) { // // Can't impersonate the user -- either RCloseServiceHandle was called // internally by the SCM or the impersonate failed for some other reason. // Do the audit as System instead. // fImpersonated = FALSE; } RtlInitUnicodeString(&Subsystem, SC_MANAGER_AUDIT_NAME); status = NtCloseObjectAuditAlarm( &Subsystem, *phSCObject, (BOOLEAN)((((LPSC_HANDLE_STRUCT)*phSCObject)->Flags & SC_HANDLE_GENERATE_ON_CLOSE) != 0)); if (!NT_SUCCESS(status)) { SC_LOG1(ERROR, "RCloseServiceHandle: NtCloseObjectAuditAlarm failed: %#lx\n",status); } if (fImpersonated) { RpcStatus = RpcRevertToSelf(); if (RpcStatus != RPC_S_OK) { SC_LOG(ERROR, "RCloseServiceHandle: Fail to revert to self %08lx\n", RpcStatus); ScLogEvent( NEVENT_CALL_TO_FUNCTION_FAILED, SC_RPC_REVERT, RpcStatus); ASSERT(FALSE); // // Not much else we can do at this point -- keep on going. // } } ScReleasePrivilege(); } // // Attempt to free the memory that the handle points to. // FreeStatus = LocalFree(*phSCObject); if (FreeStatus != NULL) { // // For some reason, the handle couldn't be freed. Therefore, the // best we can do to disable it is to remove the signature. // SC_LOG(ERROR,"RCloseServiceHandle:LocalFree Failed %d\n",GetLastError()); ((LPSC_HANDLE_STRUCT)*phSCObject)->Signature = 0; } // // Tell RPC we are done with the context handle. // SC_LOG(HANDLE,"Handle Closed 0x%08lx\n",*phSCObject); *phSCObject = NULL; return(NO_ERROR); } VOID SC_RPC_HANDLE_rundown( SC_RPC_HANDLE scHandle ) /*++ Routine Description: This function is called by RPC when a connection is broken that had an outstanding context handle. The value of the context handle is passed in here so that we have an opportunity to clean up. Arguments: scHandle - This is the handle value of the context handle that is broken. Return Value: none. --*/ { // // Close the handle. // RCloseServiceHandle(&scHandle); } DWORD ScCreateScManagerHandle( IN LPWSTR DatabaseName, OUT LPSC_HANDLE_STRUCT *ContextHandle ) /*++ Routine Description: This function allocates the memory for an SC Manager context handle structure, and initializes it. Arguments: DatabaseName - Supplies the name of the SC Manager database which the returned structure is a context of. ContextHandle - Returns a pointer to the context handle structure created. Return Value: NO_ERROR - The operation was successful. ERROR_NOT_ENOUGH_MEMORY - Memory allocation for the context handle structure failed. Note: The memory allocated by this routine should be freed with LocalFree. --*/ { // // Allocate memory for the context handle structure, and database name. // *ContextHandle = (LPSC_HANDLE_STRUCT)LocalAlloc( LMEM_ZEROINIT, sizeof(SC_HANDLE_STRUCT) + WCSSIZE(DatabaseName)); if (*ContextHandle == NULL) { SC_LOG(ERROR,"ScCreateScManagerHandle:LocalAlloc Failed %d\n", GetLastError()); return ERROR_NOT_ENOUGH_MEMORY; } // // Initialize contents of the context handle structure, except for the // granted access which is figured out when the desired access is validated // later. // (*ContextHandle)->Signature = SC_SIGNATURE; (*ContextHandle)->Flags = 0; (*ContextHandle)->Type.ScManagerObject.DatabaseName = (LPWSTR) ((DWORD_PTR) *ContextHandle + sizeof(SC_HANDLE_STRUCT)); wcscpy((*ContextHandle)->Type.ScManagerObject.DatabaseName, DatabaseName); return NO_ERROR; } BOOL ScIsValidScManagerHandle( IN SC_RPC_HANDLE hScManager ) { LPSC_HANDLE_STRUCT serviceHandleStruct = (LPSC_HANDLE_STRUCT) hScManager; if (serviceHandleStruct == NULL) { return (FALSE); // Not valid. } if (serviceHandleStruct->Signature != SC_SIGNATURE) { return (FALSE); // Not valid. } return (TRUE); } // ScIsValidScManagerHandle DWORD ScCreateServiceHandle( IN LPSERVICE_RECORD ServiceRecord, OUT LPSC_HANDLE_STRUCT *ContextHandle ) /*++ Routine Description: This function allocates the memory for a service context handle structure, and initializes it. Arguments: ServiceRecord - Supplies a pointer to the service record which the returned structure is a context of. ContextHandle - Returns a pointer to the context handle structure created. Return Value: NO_ERROR - The operation was successful. ERROR_NOT_ENOUGH_MEMORY - Memory allocation for the context handle structure failed. Note: The memory allocated by this routine should be freed with LocalFree. --*/ { // // Allocate memory for the context handle structure. // *ContextHandle = (LPSC_HANDLE_STRUCT)LocalAlloc( LMEM_ZEROINIT, sizeof(SC_HANDLE_STRUCT) ); if (*ContextHandle == NULL) { SC_LOG(ERROR,"ScCreateServiceHandle:LocalAlloc Failed %d\n", GetLastError()); return ERROR_NOT_ENOUGH_MEMORY; } // // Initialize contents of the context handle structure, except for the // granted access which is figured out when the desired access is validated // later. // (*ContextHandle)->Signature = SERVICE_SIGNATURE; (*ContextHandle)->Flags = 0; (*ContextHandle)->Type.ScServiceObject.ServiceRecord = ServiceRecord; SC_ASSERT( ScIsValidServiceHandle( *ContextHandle ) ); return NO_ERROR; } BOOL ScIsValidServiceHandle( IN SC_RPC_HANDLE hService ) { LPSC_HANDLE_STRUCT serviceHandleStruct = (LPSC_HANDLE_STRUCT) hService; if (serviceHandleStruct == NULL) { return (FALSE); // Not valid. } if (serviceHandleStruct->Signature != SERVICE_SIGNATURE) { return (FALSE); // Not valid. } return (TRUE); } // ScIsValidServiceHandle BOOL ScIsValidScManagerOrServiceHandle( IN SC_RPC_HANDLE ContextHandle, OUT PSC_HANDLE_TYPE phType ) /*++ Routine Description: Function to check a handle that may be either a service handle or an SC Manager handle without having to check vs. NULL twice by calling both ScIsValidScManagerHandle and ScIsValidServiceHandle Arguments: ContextHandle -- The handle to check phType -- The type of the handle (SCManager vs. Service) if valid Return Value: TRUE -- The handle is valid FALSE -- The handle is not valid --*/ { LPSC_HANDLE_STRUCT pHandle = (LPSC_HANDLE_STRUCT) ContextHandle; SC_ASSERT(phType != NULL); if (pHandle == NULL) { return FALSE; // Not valid. } if (pHandle->Signature == SERVICE_SIGNATURE) { *phType = SC_HANDLE_TYPE_SERVICE; return TRUE; } else if (pHandle->Signature == SC_SIGNATURE) { *phType = SC_HANDLE_TYPE_MANAGER; return TRUE; } return FALSE; } // ScIsValidScManagerOrServiceHandle