|
|
//+----------------------------------------------------------------------------
//
// Microsoft Windows
// Copyright (C) Microsoft Corporation, 1992 - 1993.
//
// File: setup.cxx
//
// Contents: Task Scheduler setup program
//
// Classes: None.
//
// Functions:
//
// History: 04-Apr-96 MarkBl Created
// 23-Sep-96 AnirudhS Added SetTaskFolderSecurity, etc.
// 30-Sep-96 AnirudhS Added /firstlogon and /logon options
// 15-Nov-96 AnirudhS Conditionally enable the service on NT too
// 01-09-97 DavidMun Add sysagent.exe path value under
// app paths, backup sysagent.exe
// 04-14-97 DavidMun Add DoMemphisSetup
// 03-03-01 JBenton Prefix BUG 333200 use of uninit memory
// 03-10-01 JBenton BUG 142333 tighten Tasks folder security
// October 01 Maxa Updated security on Tasks folder in preparation for addition of auditing.
//
//-----------------------------------------------------------------------------
#include <windows.h>
#include <tchar.h>
#include <sddl.h>
#include <StrSafe.h>
#include "security.hxx"
#include "setupids.h"
#define ARRAY_LEN(a) (sizeof(a)/sizeof((a)[0]))
#define ARG_DELIMITERS TEXT(" \t")
#define MINUTES_BEFORE_IDLE_DEFAULT 15
#define MAX_LOG_SIZE_DEFAULT (0x20)
//
// Note that the svchost registry keys to run schedule service as a part
// of netsvcs is set in hivesft.inx file.
//
#define SCHED_SETUP_SWITCH TEXT("/setup")
#define SCHED_SERVICE_EXE_PATH TEXT("%SystemRoot%\\System32\\svchost.exe -k netsvcs")
#define SCHED_SERVICE_DLL TEXT("MSTask.dll")
#define SCHED_SERVICE_NAME TEXT("Schedule")
#define SCHED_SERVICE_GROUP TEXT("SchedulerGroup")
#define MINUTESBEFOREIDLE TEXT("MinutesBeforeIdle")
#define MAXLOGSIZEKB TEXT("MaxLogSizeKB")
#define TASKSFOLDER TEXT("TasksFolder")
#define FIRSTBOOT TEXT("FirstBoot")
#define SM_SA_KEY TEXT("Software\\Microsoft\\SchedulingAgent")
//
// Entry points from mstask.dll
// Note they are used with GetProcAddress, which always wants an ANSI string.
//
#define CONVERT_AT_TASKS_API "ConvertAtJobsToTasks"
//
// Function pointer types used when loading above functions from mstask.dll
//
typedef HRESULT (__stdcall *PSTDAPI)(void); typedef BOOL (__stdcall *PBOOLAPI)(void); typedef VOID (__stdcall *PVOIDAPI)(void);
typedef struct _MYSIDINFO { PSID_IDENTIFIER_AUTHORITY pIdentifierAuthority; DWORD dwSubAuthority; DWORD dwSubSubAuthority; PSID pSid; } MYSIDINFO;
DWORD SetTaskFolderSecurity(LPCWSTR pwszFolderPath); DWORD AllocateAndInitializeDomainSid( PSID pDomainSid, MYSIDINFO * pDomainSidInfo);
void DoSetup(void); void ErrorDialog(UINT ErrorFmtStringID, TCHAR * szRoutine, DWORD ErrorCode);
HINSTANCE ghInstance = NULL;
//+----------------------------------------------------------------------------
//
// Function: WinMainCRTStartup
//
// Synopsis: entry point
//
//-----------------------------------------------------------------------------
void _cdecl main(int argc, char ** argv) { //
// Skip EXE name and find first parameter, if any
//
LPTSTR ptszStart; LPTSTR szArg1 = _tcstok(ptszStart = GetCommandLine(), ARG_DELIMITERS); szArg1 = _tcstok(NULL, ARG_DELIMITERS); //
// Switch based on the first parameter
//
if (szArg1 == NULL) { ; // Do nothing
} else if (lstrcmpi(szArg1, SCHED_SETUP_SWITCH) == 0) { DoSetup(); } }
//+----------------------------------------------------------------------------
//
// Function: DoSetup
//
// Synopsis: Performs the normal setup procedure
//
//-----------------------------------------------------------------------------
void DoSetup(void) { #define SCHED_SERVICE_DEPENDENCY L"RpcSs\0"
#define SCC_AT_SVC_KEY L"System\\CurrentControlSet\\Services\\Schedule"
#define TASKS_FOLDER_DEFAULT L"%SystemRoot%\\Tasks"
TCHAR szTasksFolder[MAX_PATH + 1] = TEXT(""); TCHAR tszDisplayName[50]; // "Task Scheduler"
DWORD dwTmp; HKEY hKey;
//
// Disable hard-error popups.
//
SetErrorMode(SEM_FAILCRITICALERRORS | SEM_NOOPENFILEERRORBOX);
ghInstance = GetModuleHandle(NULL);
//
// Load the service display name.
//
int cch = LoadString(ghInstance, IDS_SERVICE_DISPLAY_NAME, tszDisplayName, ARRAY_LEN(tszDisplayName)); if (!(0 < cch && cch < ARRAY_LEN(tszDisplayName) - 1)) { ErrorDialog(IDS_INSTALL_FAILURE, TEXT("LoadString"), GetLastError()); return; }
//
// Create/open the Scheduling Agent key in Software\Microsoft.
//
LONG lErr = RegCreateKeyEx(HKEY_LOCAL_MACHINE, SM_SA_KEY, 0, NULL, REG_OPTION_NON_VOLATILE, KEY_ALL_ACCESS, NULL, &hKey, &dwTmp);
if (ERROR_SUCCESS != lErr) { ErrorDialog(IDS_INSTALL_FAILURE, TEXT("RegCreateKeyEx"), lErr); return; }
// Creator/Owner, System, Builtin Admins: Full control,
// Authenticated users: generic read
WCHAR pwszSDDL[] = L"D:P(A;OICIIO;FA;;;CO)(A;OICI;FA;;;BA)(A;OICI;FA;;;SY)(A;OICI;GR;;;AU)"; PSECURITY_DESCRIPTOR pSD = NULL;
if (!ConvertStringSecurityDescriptorToSecurityDescriptorW(pwszSDDL, SDDL_REVISION_1, &pSD, NULL)) { lErr = (GetLastError()); ErrorDialog(IDS_INSTALL_FAILURE, TEXT("ConvertStringSecurityDescriptorToSecurityDescriptorW"), lErr); RegCloseKey(hKey); return; }
lErr = RegSetKeySecurity(hKey, DACL_SECURITY_INFORMATION, pSD); LocalFree(pSD); if (ERROR_SUCCESS != lErr) { ErrorDialog(IDS_INSTALL_FAILURE, TEXT("RegSetKeySecurity"), lErr); RegCloseKey(hKey); return; }
// Set MinutesBeforeIdle to a default value of 15 mins.
//
dwTmp = MINUTES_BEFORE_IDLE_DEFAULT; lErr = RegSetValueEx(hKey, MINUTESBEFOREIDLE, 0, REG_DWORD, (CONST BYTE *)&dwTmp, sizeof(dwTmp));
if (ERROR_SUCCESS != lErr) { ErrorDialog(IDS_INSTALL_FAILURE, TEXT("RegSetValueEx"), lErr); RegCloseKey(hKey); return; }
// Set MaxLogSizeKB to 32K or 0x7FFF.
//
dwTmp = MAX_LOG_SIZE_DEFAULT; lErr = RegSetValueEx(hKey, MAXLOGSIZEKB, 0, REG_DWORD, (CONST BYTE *)&dwTmp, sizeof(dwTmp));
if (ERROR_SUCCESS != lErr) { ErrorDialog(IDS_INSTALL_FAILURE, TEXT("RegSetValueEx"), lErr); RegCloseKey(hKey); return; }
// Read the tasks folder location. The .INF should've set this.
// If not, default.
//
dwTmp = MAX_PATH * sizeof(TCHAR); if (RegQueryValueEx(hKey, TASKSFOLDER, NULL, NULL, (LPBYTE)szTasksFolder, &dwTmp) != ERROR_SUCCESS || szTasksFolder[0] == TEXT('\0')) { StringCchCopy(szTasksFolder, MAX_PATH + 1, TASKS_FOLDER_DEFAULT); }
// Set FirstBoot to non-zero.
//
dwTmp = 1; lErr = RegSetValueEx(hKey, FIRSTBOOT, 0, REG_DWORD, (CONST BYTE *)&dwTmp, sizeof(dwTmp));
RegCloseKey(hKey);
if (ERROR_SUCCESS != lErr) { ErrorDialog(IDS_INSTALL_FAILURE, TEXT("RegSetValueEx"), lErr); return; }
//
// Set the right permissions on the job folder.
// The default permissions allow anyone to delete any job, which we
// don't want.
//
{ TCHAR szTaskFolderPath[MAX_PATH + 1]; DWORD cch = ExpandEnvironmentStrings(szTasksFolder, szTaskFolderPath, ARRAY_LEN(szTaskFolderPath)); if (cch == 0 || cch > ARRAY_LEN(szTaskFolderPath)) { //
// The job folder path is too long.
//
ErrorDialog(IDS_INSTALL_FAILURE, TEXT("ExpandEnvironmentStrings"), cch ? ERROR_BUFFER_OVERFLOW : GetLastError()); return; }
DWORD dwError = SetTaskFolderSecurity(szTaskFolderPath); if (dwError != ERROR_SUCCESS) { ErrorDialog(IDS_INSTALL_FAILURE, TEXT("SetTaskFolderSecurity"), dwError); return; } }
HINSTANCE hinstMSTask = LoadLibrary(SCHED_SERVICE_DLL); if (!hinstMSTask) { ErrorDialog(IDS_INSTALL_FAILURE, SCHED_SERVICE_DLL, GetLastError()); return; }
PVOIDAPI pfnConvertLegacyJobsToTasks = (PVOIDAPI) GetProcAddress(hinstMSTask, CONVERT_AT_TASKS_API);
if (!pfnConvertLegacyJobsToTasks) { ErrorDialog(IDS_INSTALL_FAILURE, TEXT("GetProcAddress"), GetLastError()); return; }
pfnConvertLegacyJobsToTasks();
//
// Install the Win32 service.
//
SC_HANDLE hSCMgr = OpenSCManager(NULL, NULL, SC_MANAGER_CREATE_SERVICE);
if (hSCMgr == NULL) { //
// Yow, we're hosed.
//
ErrorDialog(IDS_INSTALL_FAILURE, TEXT("OpenSCManager"), GetLastError()); return; }
//
// Is the service already installed? If so, change its parameters;
// otherwise, create it.
//
SC_HANDLE hSvc = OpenService(hSCMgr, SCHED_SERVICE_NAME, SERVICE_CHANGE_CONFIG | SERVICE_START);
if (hSvc == NULL) { hSvc = CreateService(hSCMgr, SCHED_SERVICE_NAME, tszDisplayName, SERVICE_CHANGE_CONFIG | SERVICE_START, SERVICE_WIN32_SHARE_PROCESS, SERVICE_AUTO_START, SERVICE_ERROR_NORMAL, SCHED_SERVICE_EXE_PATH, SCHED_SERVICE_GROUP, NULL, SCHED_SERVICE_DEPENDENCY, NULL, NULL);
if (hSvc == NULL) { ErrorDialog(IDS_INSTALL_FAILURE, TEXT("CreateService"), GetLastError()); CloseServiceHandle(hSCMgr); return; } } else { //
// This path will be followed when we upgrade the At service
// to the Scheduling Agent. The service name will remain the
// same, but the display name will be set to the new display
// name (the At service had no display name) and the image path
// will be changed to point to the new exe.
// (The old binary will be left on disk in order to make it easy
// to revert to it, in case of compatibility problems.)
//
if (!ChangeServiceConfig( hSvc, // hService
SERVICE_WIN32_SHARE_PROCESS, // dwServiceType
SERVICE_AUTO_START, // dwStartType
SERVICE_ERROR_NORMAL, // dwErrorControl
SCHED_SERVICE_EXE_PATH, // lpBinaryPathName
SCHED_SERVICE_GROUP, // lpLoadOrderGroup
NULL, // lpdwTagId
SCHED_SERVICE_DEPENDENCY, // lpDependencies
L".\\LocalSystem", // lpServiceStartName
L"", // lpPassword
tszDisplayName // lpDisplayName
)) { ErrorDialog(IDS_INSTALL_FAILURE, TEXT("ChangeServiceConfig"), GetLastError()); CloseServiceHandle(hSvc); CloseServiceHandle(hSCMgr); return; } }
// in either case, set the recovery options
// not checking return - it's a minor glitch, no need to upset the user
SC_ACTION actions[3] = {{SC_ACTION_RESTART, 6000},{SC_ACTION_RESTART, 60000},{SC_ACTION_NONE, 0}}; SERVICE_FAILURE_ACTIONS recoveryInfo = {24*60*60, NULL, NULL, 3, &(actions[0])}; ChangeServiceConfig2(hSvc,SERVICE_CONFIG_FAILURE_ACTIONS, (LPVOID)&recoveryInfo);
CloseServiceHandle(hSvc); CloseServiceHandle(hSCMgr); }
//+----------------------------------------------------------------------------
//
// Function: ErrorDialog
//
// Synopsis: Displays an error message.
//
//-----------------------------------------------------------------------------
void ErrorDialog(UINT ErrorFmtStringID, TCHAR * szRoutine, DWORD ErrorCode) { #define ERROR_BUFFER_SIZE (MAX_PATH * 2)
TCHAR szErrorFmt[MAX_PATH + 1] = TEXT(""); TCHAR szError[ERROR_BUFFER_SIZE + 1]; TCHAR * pszError = szError;
LoadString(ghInstance, ErrorFmtStringID, szErrorFmt, MAX_PATH);
if (*szErrorFmt) { StringCchPrintf(szError, ERROR_BUFFER_SIZE + 1, szErrorFmt, szRoutine, ErrorCode); } else { //
// Not a localizable string, but done just in case LoadString
// should fail for some reason.
//
StringCchCopy(szErrorFmt, MAX_PATH + 1, TEXT("Error installing Task Scheduler; error = 0x%x")); StringCchPrintf(szError, ERROR_BUFFER_SIZE + 1, szErrorFmt, ErrorCode); }
MessageBox(NULL, szError, NULL, MB_ICONSTOP | MB_OK); }
//+---------------------------------------------------------------------------
//
// Function: SetTaskFolderSecurity
//
// Synopsis: Grant the following permissions to the task folder:
//
// LocalSystem All Access.
// Domain Administrators All Access.
// World RWX Access (no permission to delete
// child files).
//
// Arguments: [pwszFolderPath] -- Task folder path.
//
// Notes: None.
//
//----------------------------------------------------------------------------
DWORD SetTaskFolderSecurity(LPCWSTR pwszFolderPath) { DWORD dwError = ERROR_SUCCESS; BOOL bSuccess; BOOL bWasEnabled = FALSE; BOOL bSetSacl = TRUE; PSECURITY_DESCRIPTOR pSecurityDescriptor = 0; DWORD i; SECURITY_INFORMATION dwSecInfo = 0;
const DWORD c_dwBaseSidCount = 6; const DWORD c_dwDomainSidCount = 3; const DWORD c_dwDaclAceCountSrv = 5; const DWORD c_dwDaclAceCountWks = 4; const DWORD c_dwSaclAceCount = 2;
//
// Build the SIDs that will go in the security descriptor.
//
SID_IDENTIFIER_AUTHORITY NtAuth = SECURITY_NT_AUTHORITY; SID_IDENTIFIER_AUTHORITY WorldAuth = SECURITY_WORLD_SID_AUTHORITY; SID_IDENTIFIER_AUTHORITY CreatorAuth = SECURITY_CREATOR_SID_AUTHORITY;
MYSIDINFO rgBaseSidInfo[c_dwBaseSidCount] = { { &NtAuth, // Local System.
SECURITY_LOCAL_SYSTEM_RID, NULL }, { &NtAuth, // Built in domain. (Used for
SECURITY_BUILTIN_DOMAIN_RID, // domain admins SID.)
NULL }, { &NtAuth, // Authenticated user.
SECURITY_AUTHENTICATED_USER_RID, NULL }, { &CreatorAuth, // Creator.
SECURITY_CREATOR_OWNER_RID, NULL }, { &WorldAuth, // Everyone.
SECURITY_WORLD_RID, NULL }, { &NtAuth, // Anonymous.
SECURITY_ANONYMOUS_LOGON_RID, NULL },
};
MYSIDINFO rgDomainSidInfo[c_dwDomainSidCount] = { { NULL, // Domain administrators.
DOMAIN_ALIAS_RID_ADMINS, NULL }, { NULL, // Server Operators.
DOMAIN_ALIAS_RID_SYSTEM_OPS, NULL }, { NULL, // Backup Operators.
DOMAIN_ALIAS_RID_BACKUP_OPS, NULL } };
//
// Create the base SIDs.
//
for (i = 0; i < c_dwBaseSidCount; i++) { if (!AllocateAndInitializeSid( rgBaseSidInfo[i].pIdentifierAuthority, 1, rgBaseSidInfo[i].dwSubAuthority, 0, 0, 0, 0, 0, 0, 0, &rgBaseSidInfo[i].pSid)) { dwError = GetLastError(); break; } }
if (dwError == ERROR_SUCCESS) { //
// Create the domain SIDs.
//
for (i = 0; i < c_dwDomainSidCount; i++) { dwError = AllocateAndInitializeDomainSid( rgBaseSidInfo[1].pSid, &rgDomainSidInfo[i]);
if (dwError != ERROR_SUCCESS) { break; } } }
//
// Create the security descriptor.
//
ACE_DESC rgDaclAcesSrv[c_dwDaclAceCountSrv] = { { FILE_ALL_ACCESS, ACCESS_ALLOWED_ACE_TYPE, OBJECT_INHERIT_ACE | CONTAINER_INHERIT_ACE, rgBaseSidInfo[0].pSid // Local System
}, { FILE_ALL_ACCESS, ACCESS_ALLOWED_ACE_TYPE, OBJECT_INHERIT_ACE | CONTAINER_INHERIT_ACE, rgDomainSidInfo[0].pSid // Domain admins
}, { FILE_GENERIC_READ | FILE_GENERIC_EXECUTE | FILE_WRITE_DATA, ACCESS_ALLOWED_ACE_TYPE, 0, rgDomainSidInfo[1].pSid // Backup Operators
}, { FILE_GENERIC_READ | FILE_GENERIC_EXECUTE | FILE_WRITE_DATA, ACCESS_ALLOWED_ACE_TYPE, 0, rgDomainSidInfo[2].pSid // Server Operators
}, { FILE_ALL_ACCESS, ACCESS_ALLOWED_ACE_TYPE, OBJECT_INHERIT_ACE | CONTAINER_INHERIT_ACE | INHERIT_ONLY_ACE, rgBaseSidInfo[3].pSid // Creator
} };
ACE_DESC rgDaclAcesWks[c_dwDaclAceCountWks] = { { FILE_ALL_ACCESS, ACCESS_ALLOWED_ACE_TYPE, OBJECT_INHERIT_ACE | CONTAINER_INHERIT_ACE, rgBaseSidInfo[0].pSid // Local System
}, { FILE_ALL_ACCESS, ACCESS_ALLOWED_ACE_TYPE, OBJECT_INHERIT_ACE | CONTAINER_INHERIT_ACE, rgDomainSidInfo[0].pSid // Domain admins
}, { FILE_GENERIC_READ | FILE_GENERIC_EXECUTE | FILE_WRITE_DATA, ACCESS_ALLOWED_ACE_TYPE, 0, rgBaseSidInfo[2].pSid // Authenticated user
}, { FILE_ALL_ACCESS, ACCESS_ALLOWED_ACE_TYPE, OBJECT_INHERIT_ACE | CONTAINER_INHERIT_ACE | INHERIT_ONLY_ACE, rgBaseSidInfo[3].pSid // Creator
} };
ACE_DESC rgSaclAces[c_dwSaclAceCount] = { { FILE_WRITE_DATA | FILE_APPEND_DATA | FILE_DELETE_CHILD | DELETE | WRITE_DAC | WRITE_OWNER, SYSTEM_AUDIT_ACE_TYPE, SUCCESSFUL_ACCESS_ACE_FLAG | FAILED_ACCESS_ACE_FLAG | OBJECT_INHERIT_ACE | CONTAINER_INHERIT_ACE, rgBaseSidInfo[4].pSid // Everyone
}, { FILE_WRITE_DATA | FILE_APPEND_DATA | FILE_DELETE_CHILD | DELETE | WRITE_DAC | WRITE_OWNER, SYSTEM_AUDIT_ACE_TYPE, SUCCESSFUL_ACCESS_ACE_FLAG | FAILED_ACCESS_ACE_FLAG | OBJECT_INHERIT_ACE | CONTAINER_INHERIT_ACE, rgBaseSidInfo[5].pSid // Anonymous
} };
if (dwError != ERROR_SUCCESS) { goto Cleanup; }
// different security on workstation vs server
OSVERSIONINFOEX verInfo; verInfo.dwOSVersionInfoSize = sizeof(OSVERSIONINFOEX); if (!GetVersionEx((LPOSVERSIONINFOW)&verInfo)) { dwError = ERROR_NOT_SUPPORTED; goto Cleanup; }
if (verInfo.wProductType == VER_NT_WORKSTATION) {
dwError = CreateSecurityDescriptor( &pSecurityDescriptor, c_dwDaclAceCountWks, rgDaclAcesWks, c_dwSaclAceCount, rgSaclAces);
if (dwError != ERROR_SUCCESS) { goto Cleanup; }
} else { dwError = CreateSecurityDescriptor( &pSecurityDescriptor, c_dwDaclAceCountSrv, rgDaclAcesSrv, c_dwSaclAceCount, rgSaclAces);
if (dwError != ERROR_SUCCESS) { goto Cleanup; } }
//
// Enable SecurityPrivilege in order to be able
// to set the SACL.
//
dwError = EnablePrivilege( SE_SECURITY_NAME, TRUE, &bWasEnabled);
if (dwError != ERROR_SUCCESS) { bSetSacl = FALSE; }
//
// Finally, set permissions.
//
dwSecInfo |= DACL_SECURITY_INFORMATION;
if (bSetSacl && c_dwSaclAceCount) { dwSecInfo |= SACL_SECURITY_INFORMATION; }
bSuccess = SetFileSecurity( pwszFolderPath, dwSecInfo, pSecurityDescriptor);
if (!bWasEnabled) { EnablePrivilege( SE_SECURITY_NAME, FALSE, 0); }
if (!bSuccess) { dwError = GetLastError(); goto Cleanup; }
dwError = ERROR_SUCCESS;
Cleanup:
for (i = 0; i < c_dwBaseSidCount; i++) { if (rgBaseSidInfo[i].pSid != NULL) { FreeSid(rgBaseSidInfo[i].pSid); } } for (i = 0; i < c_dwDomainSidCount; i++) { LocalFree(rgDomainSidInfo[i].pSid); } if (pSecurityDescriptor != NULL) { DeleteSecurityDescriptor(pSecurityDescriptor); }
return dwError; }
//+---------------------------------------------------------------------------
//
// Function: AllocateAndInitializeDomainSid
//
// Synopsis:
//
// Arguments: [pDomainSid] --
// [pDomainSidInfo] --
//
// Notes: None.
//
//----------------------------------------------------------------------------
DWORD AllocateAndInitializeDomainSid( PSID pDomainSid, MYSIDINFO * pDomainSidInfo) { UCHAR DomainIdSubAuthorityCount; DWORD SidLength;
//
// Allocate a Sid which has one more sub-authority than the domain ID.
//
DomainIdSubAuthorityCount = *(GetSidSubAuthorityCount(pDomainSid)); SidLength = GetSidLengthRequired(DomainIdSubAuthorityCount + 1);
pDomainSidInfo->pSid = (PSID) LocalAlloc(0, SidLength);
if (pDomainSidInfo->pSid == NULL) { return(ERROR_NOT_ENOUGH_MEMORY); }
//
// Initialize the new SID to have the same initial value as the
// domain ID.
//
if (!CopySid(SidLength, pDomainSidInfo->pSid, pDomainSid)) { LocalFree(pDomainSidInfo->pSid); pDomainSidInfo->pSid = NULL; return(GetLastError()); }
//
// Adjust the sub-authority count and add the relative Id unique
// to the newly allocated SID
//
(*(GetSidSubAuthorityCount(pDomainSidInfo->pSid)))++; *(GetSidSubAuthority(pDomainSidInfo->pSid, DomainIdSubAuthorityCount)) = pDomainSidInfo->dwSubAuthority;
return(ERROR_SUCCESS); }
|