Leaked source code of windows server 2003
You can not select more than 25 topics Topics must start with a letter or number, can include dashes ('-') and can be up to 35 characters long.
 
 
 
 
 
 

1205 lines
32 KiB

//+---------------------------------------------------------------------------
//
// Scheduling Agent Service
//
// Microsoft Windows
// Copyright (C) Microsoft Corporation, 1992 - 1996.
//
// File: netsch.cxx
//
// Contents: Server-side Net Scheduler RPC implementation.
//
// Classes: None.
//
// RPC: NetrJobAdd
// NetrJobDel
// NetrJobEnum
// NetrJobGetInfo
//
// Functions: CreateAtJobPath
// GetAtJobIdFromFileName
// InitializeNetScheduleApi
// UninitializeNetScheduleApi
//
// History: 11-Nov-95 MarkBl Created.
// 02-Feb-01 JBenton Fixed BUG 303146 - 64bit pointer alignment problem
//
//----------------------------------------------------------------------------
#include "..\pch\headers.hxx"
#pragma hdrstop
#include "debug.hxx"
#include <align.h>
#include <apperr.h>
#include <lmerr.h>
#include <netevent.h>
extern "C" {
#include <netlib.h>
}
#include "atsvc.h"
#include "..\inc\resource.h"
#include "globals.hxx"
#include "svc_core.hxx"
#include "atsec.hxx"
#include "proto.hxx"
//
// Manifests below taken from the existing At service. Values must *not*
// change to maintain compatibility.
//
#define MAXIMUM_COMMAND_LENGTH (MAX_PATH - 1)
#define MAXIMUM_JOB_TIME (24 * 60 * 60 * 1000 - 1)
#define DAYS_OF_WEEK 0x7F // 7 bits for 7 days.
#define DAYS_OF_MONTH 0x7FFFFFFF // 31 bits for 31 days.
// This is not localized - it is a registry key (indirectly) from the At service
#define SCHEDULE_EVENTLOG_NAME TEXT("Schedule")
//
// Converts an HRESULT to a WIN32 status code. Masks off everything but
// the error code.
//
// BUGBUG : Review.
//
#define WIN32_FROM_HRESULT(x) (HRESULT_CODE(x))
//
// Minimum and maximum buffer size returned in an enumeration.
//
// 02/05/01-jbenton : this macro is used to a unicode buffer so must be even
// to avoid alignment problems (bug 303146).
#define BUFFER_LENGTH_MINIMUM (sizeof(AT_ENUM) + (MAXIMUM_COMMAND_LENGTH+1)*sizeof(WCHAR))
#define BUFFER_LENGTH_MAXIMUM 65536
//
// Ballpark maximum command string length.
//
// BUGBUG : Review this value.
//
#define COMMAND_STRING_LENGTH_APPROX (((MAX_PATH / 4) + 1) * sizeof(WCHAR))
#define ASTERISK_STR L"*"
#define BACKSLASH_STR L"\\"
void CreateAtJobPath(DWORD, WCHAR *, size_t);
DWORD GetAtJobIdFromFileName(WCHAR *);
void GetNextAtID(LPDWORD);
WCHAR * gpwszAtJobPathTemplate = NULL;
//+---------------------------------------------------------------------------
//
// RPC: NetrJobAdd
//
// Synopsis: Add a single At job.
//
// Arguments: [ServerName] -- Unused.
// [pAtInfo] -- New job information.
// [pJobId] -- Returned job id.
//
// Returns: BUGBUG : Problem mapping a HRESULT to WIN32. Masking off the
// facility & error bits is insufficient.
//
// Notes: None.
//
//----------------------------------------------------------------------------
NET_API_STATUS
NetrJobAdd(ATSVC_HANDLE ServerName, LPAT_INFO pAtInfo, LPDWORD pJobId)
{
schDebugOut((DEB_ITRACE,
"NetrJobAdd ServerName(%ws), pAtInfo(0x%x)\n",
(ServerName != NULL) ? ServerName : L"(local)",
pAtInfo));
UNREFERENCED_PARAMETER(ServerName);
NET_API_STATUS Status = NERR_Success;
Status = AtCheckSecurity(AT_JOB_ADD);
if (Status != NERR_Success)
{
return ERROR_ACCESS_DENIED;
}
//
// Validate arguments.
//
if ( (pAtInfo->Command == NULL) ||
(wcslen(pAtInfo->Command) > MAXIMUM_COMMAND_LENGTH) ||
(pAtInfo->JobTime > MAXIMUM_JOB_TIME) ||
(pAtInfo->DaysOfWeek & ~DAYS_OF_WEEK) ||
(pAtInfo->DaysOfMonth & ~DAYS_OF_MONTH) ||
(pAtInfo->Flags & ~JOB_INPUT_FLAGS))
{
return(ERROR_INVALID_PARAMETER);
}
//
// TBD : Logic to punt the submission if the service is paused.
//
EnterCriticalSection(&gcsNetScheduleCritSection);
//
// Have the global schedule instance add the At job.
//
HRESULT hr = g_pSched->m_pSch->AddAtJobWithHash(*pAtInfo, pJobId);
if (FAILED(hr))
{
//
// Convert the HRESULT to a WIN32 status code.
//
Status = WIN32_FROM_HRESULT(hr);
}
LeaveCriticalSection(&gcsNetScheduleCritSection);
return(Status);
}
//+---------------------------------------------------------------------------
//
// RPC: NetrJobDel
//
// Synopsis: Delete the At jobs in the range specified.
//
// Arguments: [ServerName] -- Unused.
// [MinJobId] -- Range lower bound, inclusive.
// [MaxJobId] -- Range upper bound, inclusive.
//
// Returns: NERR_Sucess
// ERROR_INVALID_PARAMETER
// APE_AT_ID_NOT_FOUND
//
// Notes: None.
//
//----------------------------------------------------------------------------
NET_API_STATUS
NetrJobDel(ATSVC_HANDLE ServerName, DWORD MinJobId, DWORD MaxJobId)
{
schDebugOut((DEB_ITRACE,
"NetrJobDel ServerName(%ws), MinJobId(%d), MaxJobId(%d)\n",
(ServerName != NULL) ? ServerName : L"(local)",
MinJobId,
MaxJobId));
UNREFERENCED_PARAMETER(ServerName);
NET_API_STATUS Status;
Status = AtCheckSecurity(AT_JOB_DEL);
if (Status != NERR_Success)
{
return ERROR_ACCESS_DENIED;
}
//
// Validate range.
//
if (MinJobId > MaxJobId)
{
return(ERROR_INVALID_PARAMETER);
}
EnterCriticalSection(&gcsNetScheduleCritSection);
//
// Delete the indicated At job objects from storage.
//
// NB : To maintain compatibility with the existing AT service, if at
// least one job is deleted successfully, return success; otherwise,
// return APE_ID_NOT_FOUND.
//
WCHAR wszPath[MAX_PATH + 1];
BOOL fJobDeleted = FALSE;
HRESULT hr;
BOOL fDeleteAll = FALSE;
//
// Test for delete-all; signaled by passing a MaxJobId of 0xffffffff.
//
if (MaxJobId == 0xffffffff)
{
//
// Get the actual maximum ID value (this fixes bug 55839).
//
GetNextAtID(&MaxJobId);
fDeleteAll = TRUE;
}
CJob * pJob = CJob::Create();
if (pJob)
{
for (DWORD i = MinJobId; i <= MaxJobId; i++)
{
CreateAtJobPath(i, wszPath, MAX_PATH + 1);
//
// Make sure this is really an AT job, and not one that's just
// named like one. Just load the fixed-length data and check for
// the at flag.
//
hr = pJob->LoadP(wszPath, 0, FALSE, FALSE);
if (SUCCEEDED(hr))
{
DWORD rgFlags;
pJob->GetAllFlags(&rgFlags);
if (rgFlags & JOB_I_FLAG_NET_SCHEDULE)
{
if (DeleteFile(wszPath))
{
fJobDeleted = TRUE;
}
}
}
else
{
schDebugOut((DEB_IWARN, "LoadP(%S) hr=0x%x\n", wszPath, hr));
}
}
pJob->Release();
}
//
// If the user asked to delete all at jobs, reset the next id to 1
//
if (fDeleteAll)
{
(void) g_pSched->m_pSch->ResetAtID();
}
LeaveCriticalSection(&gcsNetScheduleCritSection);
Status = fJobDeleted ? NERR_Success : APE_AT_ID_NOT_FOUND;
return(Status);
}
//+---------------------------------------------------------------------------
//
// RPC: NetrJobEnum
//
// Synopsis: Enumerate At jobs.
//
// Arguments: [ServerName] -- Unused.
// [pEnumContainer] -- Returned enumeration (AT_JOB_INFO
// array and size).
// [PreferredMaximumLength] -- Preferred buffer size maximum. If
// -1, allocate as needed.
// [pTotalEntries] -- Returns the total number of
// entries available.
// [pResumeHandle] -- Enumeration context. Indexes the
// the At jobs directory.
//
// Returns: BUGBUG : Problem here too with HRESULTs mapped to WIN32 status
// codes.
//
// Notes: None.
//
//----------------------------------------------------------------------------
NET_API_STATUS
NetrJobEnum(
ATSVC_HANDLE ServerName,
LPAT_ENUM_CONTAINER pEnumContainer,
DWORD PreferredMaximumLength,
LPDWORD pTotalEntries,
LPDWORD pResumeHandle)
{
schDebugOut((DEB_ITRACE,
"NetrJobEnum ServerName(%ws), pEnumContainer(0x%x), " \
"PreferredMaximumLength(%d)\n",
(ServerName != NULL) ? ServerName : L"(local)",
pEnumContainer,
PreferredMaximumLength));
UNREFERENCED_PARAMETER(ServerName);
WCHAR wszCommand[MAX_PATH + 1];
WIN32_FIND_DATA fd;
NET_API_STATUS Status;
HANDLE hFileFindContext;
LPBYTE pbBuffer;
LPBYTE pbStringsOffset;
PAT_ENUM pAtEnum;
DWORD cbBufferSize;
DWORD cbCommandSize;
DWORD cJobsEnumerated;
DWORD iEnumContext;
DWORD i;
DWORD rgFlags;
HRESULT hr;
Status = NERR_Success;
pbBuffer = NULL;
cJobsEnumerated = 0;
i = 0;
//
// pEnumContainer is defined in the IDL file as [in,out] though it
// should only be [out]. This can't be changed in the IDL file for
// backwards compatibility, so check it here. Without this check,
// we'll leak memory if the user gives a non-NULL buffer.
//
if (pEnumContainer->Buffer != NULL)
{
return ERROR_INVALID_PARAMETER;
}
Status = AtCheckSecurity(AT_JOB_ENUM);
if (Status != NERR_Success)
{
return ERROR_ACCESS_DENIED;
}
if (pResumeHandle != NULL)
{
iEnumContext = *pResumeHandle;
}
else
{
iEnumContext = 0;
}
//
// Allocate one job object that will be reused.
//
CJob * pJob = CJob::Create();
if (pJob == NULL)
{
return ERROR_NOT_ENOUGH_MEMORY;
}
EnterCriticalSection(&gcsNetScheduleCritSection);
//
// Compute the total number of At jobs (i.e., the number of At jobs in
// the At subdirectory). This number is used to update the pTotalEntries
// argument, and may be used for enumeration buffer size computation.
//
DWORD cAtJobTotal = 0;
hFileFindContext = FindFirstFile(g_wszAtJobSearchPath, &fd);
if (hFileFindContext == INVALID_HANDLE_VALUE)
{
//
// Nothing to enumerate.
//
*pTotalEntries = 0;
goto EnumExit;
}
do
{
//
// If somebody renamed an At job, don't enumerate it. This is to
// prevent us from returning duplicate IDs as a result of finding jobs
// like At1.job and At01.job.
//
if (!IsValidAtFilename(fd.cFileName))
{
continue;
}
hr = LoadAtJob(pJob, fd.cFileName);
if (FAILED(hr))
{
// we don't want to return the job,
// but failing altogether is a little drastic
// ERR_OUT("NetrJobEnum: pJob->Load", hr);
// Status = WIN32_FROM_HRESULT(hr);
// FindClose(hFileFindContext);
// goto EnumExit;
continue; // skip it and go on
}
pJob->GetAllFlags(&rgFlags);
if (rgFlags & JOB_I_FLAG_NET_SCHEDULE)
{
cAtJobTotal++;
}
} while (FindNextFile(hFileFindContext, &fd));
FindClose(hFileFindContext);
if (!cAtJobTotal)
{
//
// Nothing to enumerate.
//
*pTotalEntries = 0;
goto EnumExit;
}
//
// Get buffer size.
//
if (PreferredMaximumLength != -1)
{
//
// Caller has specified a preferred buffer size.
//
// 02/05/01-jbenton : buffer size must be even to avoid
// alignment errors. (bug 303146).
cbBufferSize = ROUND_DOWN_COUNT(PreferredMaximumLength, ALIGN_WCHAR);
}
else
{
//
// Compute a "best-guess" buffer size to return all of the data.
// If we underestimate the buffer size, we'll return as much data
// as the buffer allows, plus a return code of ERROR_MORE_DATA.
//
cbBufferSize = (sizeof(AT_ENUM) + COMMAND_STRING_LENGTH_APPROX) *
cAtJobTotal;
}
//
// Restrict buffer size.
//
cbBufferSize = (DWORD)max(cbBufferSize, BUFFER_LENGTH_MINIMUM);
cbBufferSize = min(cbBufferSize, BUFFER_LENGTH_MAXIMUM);
//
// The enumeration context is utilized as an index in the find first/next
// file result. If non-zero, enumerate the directory until the number
// of AT jobs enumerated equals the caller's enumeration context.
//
// BUGBUG : This is quite a departure from the existing At service, but
// I'm confident it should not present a problem. Note for
// review.
//
// Seek to the enumeration context index.
//
hFileFindContext = FindFirstFile(g_wszAtJobSearchPath, &fd);
if (hFileFindContext == INVALID_HANDLE_VALUE)
{
//
// Nothing to enumerate.
//
*pTotalEntries = 0;
goto EnumExit;
}
i = 0;
do
{
if (!IsValidAtFilename(fd.cFileName))
{
continue;
}
hr = LoadAtJob(pJob, fd.cFileName);
if (FAILED(hr))
{
// we don't want to return the job,
// but failing altogether is a little drastic
// ERR_OUT("NetrJobEnum: pJob->Load", hr);
// Status = WIN32_FROM_HRESULT(hr);
// FindClose(hFileFindContext);
// goto EnumExit;
continue; // skip it and go on
}
pJob->GetAllFlags(&rgFlags);
if (rgFlags & JOB_I_FLAG_NET_SCHEDULE)
{
i++;
if (i > iEnumContext)
{
break;
}
}
} while (FindNextFile(hFileFindContext, &fd));
if (i <= iEnumContext)
{
//
// The above enumeration seek failed to find any more AT jobs
// beyond the Resume handle count. Thus, the enumeration is
// complete. Nothing else to enumerate.
//
FindClose(hFileFindContext);
*pTotalEntries = 0;
goto EnumExit;
}
//
// Update pTotalEntries argument. It is the difference between the total
// number of jobs and the number of jobs previously enumerated.
//
*pTotalEntries = cAtJobTotal - i + 1;
pbBuffer = (LPBYTE)MIDL_user_allocate(cbBufferSize);
if (pbBuffer == NULL)
{
Status = ERROR_NOT_ENOUGH_MEMORY;
CHECK_HRESULT(HRESULT_FROM_WIN32(ERROR_NOT_ENOUGH_MEMORY));
goto EnumExit;
}
//
// Begin the enumeration.
//
pbStringsOffset = pbBuffer + cbBufferSize;
pAtEnum = (PAT_ENUM)pbBuffer;
//
// To have arrived here, the resume handle seek above will have left us
// a valid AT job object in pJob and the corresponding rgFlags.
//
do
{
if (rgFlags & JOB_I_FLAG_NET_SCHEDULE)
{
if (pbStringsOffset <= (LPBYTE)pAtEnum + sizeof(AT_ENUM))
{
//
// Buffer full.
//
Status = ERROR_MORE_DATA;
break;
}
//
// Get At job information.
//
DWORD CommandSize = MAX_PATH + 1;
AT_INFO AtInfo;
hr = pJob->GetAtInfo(&AtInfo, wszCommand, &CommandSize);
if (SUCCEEDED(hr))
{
//
// Copy fixed portion.
//
pAtEnum->JobId = GetAtJobIdFromFileName(fd.cFileName);
pAtEnum->JobTime = AtInfo.JobTime;
pAtEnum->DaysOfMonth = AtInfo.DaysOfMonth;
pAtEnum->DaysOfWeek = AtInfo.DaysOfWeek;
pAtEnum->Flags = AtInfo.Flags;
//
// Copy variable data.
//
// whack one off of CommandSize
// because NetpCopyStringToBuffer doesn't want the NULL counted
BOOL fRet = NetpCopyStringToBuffer(
wszCommand,
CommandSize -1,
(LPBYTE)(pAtEnum + 1),
(LPWSTR *)&pbStringsOffset,
&pAtEnum->Command);
if (!fRet)
{
Status = ERROR_MORE_DATA;
break;
}
pAtEnum++; cJobsEnumerated++; iEnumContext++;
}
}
//
// Get the next filename, skipping any that have been renamed
//
BOOL fFoundAnotherAtJob = FALSE;
do
{
while (fFoundAnotherAtJob = FindNextFile(hFileFindContext, &fd))
{
if (IsValidAtFilename(fd.cFileName))
{
break;
}
}
if (!fFoundAnotherAtJob)
{
//
// No more files.
//
break;
}
hr = LoadAtJob(pJob, fd.cFileName);
}
// we wish to continue doing this until we find a good one
while (FAILED(hr));
// but if we broke out without finding a good job above, we're done
if (!fFoundAnotherAtJob)
break;
pJob->GetAllFlags(&rgFlags);
} while (TRUE);
FindClose(hFileFindContext);
//
// Reset enumeration context if everything has been read.
//
if (Status == NERR_Success)
{
iEnumContext = 0;
}
EnumExit:
LeaveCriticalSection(&gcsNetScheduleCritSection);
if (pJob)
{
pJob->Release();
}
pEnumContainer->EntriesRead = cJobsEnumerated;
if (cJobsEnumerated == 0 && pbBuffer != NULL)
{
MIDL_user_free(pbBuffer);
pbBuffer = NULL;
}
pEnumContainer->Buffer = (LPAT_ENUM)pbBuffer;
if (pResumeHandle != NULL)
{
*pResumeHandle = iEnumContext;
}
return(Status);
}
//+---------------------------------------------------------------------------
//
// RPC: NetrJobGetInfo
//
// Synopsis: Get information on an At job.
//
// Arguments: [ServerName] -- Unused.
// [JobId] -- Target At job.
// [ppAtInfo] -- Returned information.
//
// Returns: BUGBUG : Problem here too with HRESULTs mapped to WIN32 status
// codes.
//
// Notes: None.
//
//----------------------------------------------------------------------------
NET_API_STATUS
NetrJobGetInfo(ATSVC_HANDLE ServerName, DWORD JobId, LPAT_INFO * ppAtInfo)
{
schDebugOut((DEB_ITRACE,
"NetrJobGetInfo ServerName(%ws), JobId(%d)\n",
(ServerName != NULL) ? ServerName : L"(local)",
JobId));
UNREFERENCED_PARAMETER(ServerName);
AT_INFO AtInfo;
PAT_INFO pAtInfo;
NET_API_STATUS Status;
WCHAR wszPath[MAX_PATH + 1];
WCHAR wszCommand[MAX_PATH + 1];
WCHAR wszJobId[10 + 1];
DWORD CommandSize;
HRESULT hr;
Status = NERR_Success;
pAtInfo = NULL;
Status = AtCheckSecurity(AT_JOB_GET_INFO);
if (Status != NERR_Success)
{
return ERROR_ACCESS_DENIED;
}
//
// Create the file name from the ID.
//
CreateAtJobPath(JobId, wszPath, MAX_PATH + 1);
schDebugOut((DEB_ITRACE, "At job name: %S\n", wszPath));
EnterCriticalSection(&gcsNetScheduleCritSection);
//
// Ensure the job object exists in storage.
//
if (GetFileAttributes(wszPath) == -1 &&
GetLastError() == ERROR_FILE_NOT_FOUND)
{
//
// Job object does not exist.
//
Status = APE_AT_ID_NOT_FOUND;
CHECK_HRESULT(HRESULT_FROM_WIN32(Status));
goto GetInfoExit;
}
//
// Command size. A character count throughout the call to GetAtJob;
// a byte count thereafter.
//
CommandSize = MAX_PATH + 1;
//
// Get At job information.
//
hr = g_pSched->m_pSch->GetAtJob(wszPath, &AtInfo, wszCommand, &CommandSize);
if (FAILED(hr))
{
//
// Convert the HRESULT to a WIN32 status code.
//
Status = WIN32_FROM_HRESULT(hr);
if (Status == ERROR_FILE_NOT_FOUND)
{
Status = APE_AT_ID_NOT_FOUND;
}
goto GetInfoExit;
}
CommandSize *= sizeof(WCHAR); // Character count -> Byte count
pAtInfo = (PAT_INFO)MIDL_user_allocate(sizeof(AT_INFO) + CommandSize);
if (pAtInfo == NULL)
{
Status = ERROR_NOT_ENOUGH_MEMORY;
CHECK_HRESULT(HRESULT_FROM_WIN32(Status));
goto GetInfoExit;
}
*pAtInfo = AtInfo;
pAtInfo->Command = (LPWSTR)(pAtInfo + 1);
CopyMemory(pAtInfo->Command, wszCommand, CommandSize);
GetInfoExit:
LeaveCriticalSection(&gcsNetScheduleCritSection);
*ppAtInfo = pAtInfo;
return(Status);
}
//+---------------------------------------------------------------------------
//
// Function: GetAtJobIdFromFileName
//
// Synopsis: Return the DWORD At job id from an At filename. At filenames
// are named according the following convention: "At<nnnn>.Job".
// The "<nnnn>" portion is the At job id in string form.
// eg: "At132.Job"
//
// Arguments: [pwszAtFileName] -- At path/filename.
//
// Returns: Non-zero At job id.
// Zero if the filename is not recognized as an At filename.
//
// Notes: None.
//
//----------------------------------------------------------------------------
DWORD
GetAtJobIdFromFileName(WCHAR * pwszAtFileName)
{
static ULONG ccAtJobFilenamePrefix = 0;
schAssert(pwszAtFileName != NULL);
if (ccAtJobFilenamePrefix == 0)
{
ccAtJobFilenamePrefix = ARRAY_LEN(TSZ_AT_JOB_PREFIX) - 1;
}
//
// Refer to the last (right-most) path element.
//
WCHAR * pwsz = wcsrchr(pwszAtFileName, L'\\');
if (pwsz == NULL)
{
pwsz = pwszAtFileName;
}
//
// Skip past the "At" filename portion.
//
if (_wcsnicmp(pwsz, TSZ_AT_JOB_PREFIX, ccAtJobFilenamePrefix) == 0)
{
pwsz += ccAtJobFilenamePrefix;
}
else
{
//
// Unknown filename. At least, it's known if this is an At job.
// Proceed no further.
//
return(0);
}
//
// Isolate the At job Id portion of the path. Do so by temporarilly
// replacing the extension period character with a null character.
//
WCHAR * pwszExt = wcsrchr(pwsz, L'.');
if (pwszExt != NULL)
{
*pwszExt = L'\0';
}
//
// Convert the Id to integer from string form.
//
DWORD AtJobId = _wtol(pwsz);
//
// Restore period character.
//
if (pwszExt != NULL)
{
*pwszExt = L'.';
}
return(AtJobId);
}
//+---------------------------------------------------------------------------
//
// Function: CreateAtJobPath
//
// Synopsis: Constructs a path in the form:
// "...\Jobs\At_Jobs\At<nnnn>.job"
// where <nnnn> is the At job id.
//
// Arguments: [JobId] -- At job Id.
// [pwszPath] -- Returned path.
// [cchBuff] -- size of path buffer
//
// Returns: None.
//
// Notes: None.
//
//----------------------------------------------------------------------------
void
CreateAtJobPath(DWORD JobId, WCHAR* pwszPath, size_t cchBuff)
{
WCHAR wszJobId[10 + 1];
StringCchPrintf(wszJobId, 10 + 1, L"%d", JobId);
StringCchCopy(pwszPath, cchBuff, gpwszAtJobPathTemplate);
StringCchCat(pwszPath, cchBuff, wszJobId);
StringCchCat(pwszPath, cchBuff, TSZ_DOTJOB);
}
//+---------------------------------------------------------------------------
//
// Function: InitializeNetScheduleApi
//
// Synopsis: Initializes globals used by the server-side NetScheduleXXX.
// Associated globals:
//
// gpwszAtJobPathTemplate -- Used to construct full paths to
// At jobs in the At jobs directory.
// (eg: "...\At_Jobs\At")
// gcsNetScheduleCritSection -- Used to serialize thread access
// to server-side NetScheduleXXX
// RPC.
//
// Arguments: None.
//
// Returns: S_OK
// E_OUTOFMEMORY
//
// Notes: None.
//
//----------------------------------------------------------------------------
HRESULT
InitializeNetScheduleApi(void)
{
WCHAR wszBuffer[MAX_PATH + 1];
ULONG ccAtJobPathTemplate;
HRESULT hr;
NET_API_STATUS Status;
Status = AtCreateSecurityObject();
if (Status != NERR_Success)
{
hr = Status;
CHECK_HRESULT(hr);
goto InitializeError;
}
schAssert(g_TasksFolderInfo.ptszPath);
ULONG ccFolderPath;
ccFolderPath = wcslen(g_TasksFolderInfo.ptszPath);
//
// Create the At job path template. For use in NetScheduleJobAdd/Del.
// Example: "<Job Folder Path>\At". To which the Job Id (string form) +
// the ".job" extension is appended.
//
ccAtJobPathTemplate = wcslen(g_TasksFolderInfo.ptszPath) +
ARRAY_LEN(TSZ_AT_JOB_PREFIX) +
1; // '\' + null terminator
gpwszAtJobPathTemplate = new WCHAR[ccAtJobPathTemplate];
if (gpwszAtJobPathTemplate == NULL)
{
hr = E_OUTOFMEMORY;
CHECK_HRESULT(hr);
goto InitializeError;
}
StringCchCopy(gpwszAtJobPathTemplate, ccAtJobPathTemplate, g_TasksFolderInfo.ptszPath);
StringCchCat(gpwszAtJobPathTemplate, ccAtJobPathTemplate, BACKSLASH_STR TSZ_AT_JOB_PREFIX);
//
// Register the Event Source, which is used to report NetSchedule
// errors in the event log - for NT4 ATSVC compatibility
//
g_hAtEventSource = RegisterEventSource(NULL, SCHEDULE_EVENTLOG_NAME);
if (g_hAtEventSource == NULL)
{
hr = HRESULT_FROM_WIN32(GetLastError());
CHECK_HRESULT(hr);
goto InitializeError;
}
return(S_OK);
InitializeError:
if (gpwszAtJobPathTemplate != NULL)
{
delete gpwszAtJobPathTemplate;
gpwszAtJobPathTemplate = NULL;
}
return(hr);
}
//+---------------------------------------------------------------------------
//
// Function: UninitializeNetScheduleApi
//
// Synopsis: Un-does work done in InitializeNetScheduleApi.
//
// Arguments: None.
//
// Returns: None.
//
// Notes: None.
//
//----------------------------------------------------------------------------
void
UninitializeNetScheduleApi(void)
{
//
// Clean up the event logging for downlevel jobs
//
if (g_hAtEventSource != NULL)
{
DeregisterEventSource(g_hAtEventSource);
g_hAtEventSource = NULL;
}
if (gpwszAtJobPathTemplate != NULL)
{
delete gpwszAtJobPathTemplate;
gpwszAtJobPathTemplate = NULL;
}
AtDeleteSecurityObject();
}
//+---------------------------------------------------------------------------
//
// Function: IsAdminFileOwner
//
// Synopsis: Ensure the file owner is an adminstrator. Currently used to
// determine if AT jobs are owned by administrators. Local system
// ownership is allowed as well.
//
// Arguments: [pwszFile] -- Checked file.
//
// Returns: TRUE -- The owner is an admin or local system.
// FALSE -- The owner isn't an admin or local system, or the
// attempt to confirm ownership identity failed.
//
// Notes: None.
//
//----------------------------------------------------------------------------
BOOL
IsAdminFileOwner(LPCWSTR pwszFile)
{
#define SECDESCR_STACK_BUFFER_SIZE 512
BYTE rgbBuffer[SECDESCR_STACK_BUFFER_SIZE];
PSECURITY_DESCRIPTOR pOwnerSecDescr = rgbBuffer;
DWORD cbSize = SECDESCR_STACK_BUFFER_SIZE;
DWORD cbSizeNeeded = 0;
BOOL fAllocatedBuffer;
if (GetFileSecurity(pwszFile,
OWNER_SECURITY_INFORMATION,
pOwnerSecDescr,
cbSize,
&cbSizeNeeded))
{
//
// The information fit within the stack-allocated buffer.
// This should cover 90% of the cases.
//
}
else if (GetLastError() == ERROR_INSUFFICIENT_BUFFER && cbSizeNeeded)
{
//
// Too much data. We'll need to allocate memory on the heap.
//
fAllocatedBuffer = TRUE;
pOwnerSecDescr = (SECURITY_DESCRIPTOR *)new BYTE[cbSizeNeeded];
if (pOwnerSecDescr == NULL)
{
return FALSE;
}
if (!GetFileSecurity(pwszFile,
OWNER_SECURITY_INFORMATION,
pOwnerSecDescr,
cbSizeNeeded,
&cbSizeNeeded))
{
delete pOwnerSecDescr;
return FALSE;
}
}
else
{
//
// An unexpected error occurred. Disallow access.
//
return FALSE;
}
//
// Get the owner sid.
//
PSID pOwnerSid;
BOOL fOwnerDefaulted;
BOOL fRet = FALSE;
if (GetSecurityDescriptorOwner(pOwnerSecDescr, &pOwnerSid,
&fOwnerDefaulted))
{
if (IsValidSid(pOwnerSid))
{
//
// Enumerate the subauthorities to check for the admin RID.
//
for (DWORD i = *GetSidSubAuthorityCount(pOwnerSid); i; i--)
{
DWORD SubAuthority = *GetSidSubAuthority(pOwnerSid, i);
if (SubAuthority == DOMAIN_ALIAS_RID_ADMINS ||
SubAuthority == SECURITY_LOCAL_SYSTEM_RID)
{
//
// Done. Owner is an admin or local system.
//
fRet = TRUE;
break;
}
}
}
}
if (pOwnerSecDescr != rgbBuffer)
{
delete pOwnerSecDescr;
}
return fRet;
}