http://git-wip-us.apache.org/repos/asf/hadoop/blob/29d0164e/hadoop-common-project/hadoop-common/src/main/winutils/libwinutils.c ---------------------------------------------------------------------- diff --git a/hadoop-common-project/hadoop-common/src/main/winutils/libwinutils.c b/hadoop-common-project/hadoop-common/src/main/winutils/libwinutils.c index f275111..933c177 100644 --- a/hadoop-common-project/hadoop-common/src/main/winutils/libwinutils.c +++ b/hadoop-common-project/hadoop-common/src/main/winutils/libwinutils.c @@ -19,9 +19,24 @@ #pragma comment(lib, "netapi32.lib") #pragma comment(lib, "Secur32.lib") #pragma comment(lib, "Userenv.lib") +#pragma comment(lib, "Ntdsapi.lib") + #include "winutils.h" +#include <ctype.h> +#include <Winsvc.h> #include <authz.h> #include <sddl.h> +#include <Ntdsapi.h> +#include <malloc.h> + +#define WIDEN_STRING(x) WIDEN_STRING_(x) +#define WIDEN_STRING_(x) L ## x +#define STRINGIFY(x) STRINGIFY_(x) +#define STRINGIFY_(x) #x + + +#pragma message("WSCE config is " STRINGIFY(WSCE_CONFIG_DIR) "\\" STRINGIFY(WSCE_CONFIG_FILE)) +const WCHAR* wsceConfigRelativePath = WIDEN_STRING(STRINGIFY(WSCE_CONFIG_DIR)) L"\\" WIDEN_STRING(STRINGIFY(WSCE_CONFIG_FILE)); /* * The array of 12 months' three-letter abbreviations @@ -237,10 +252,10 @@ ConvertToLongPathExit: // Function: IsDirFileInfo // // Description: -// Test if the given file information is a directory +// Test if the given file information is a directory // // Returns: -// TRUE if it is a directory +// TRUE if it is a directory // FALSE otherwise // // Notes: @@ -257,10 +272,10 @@ BOOL IsDirFileInfo(const BY_HANDLE_FILE_INFORMATION *fileInformation) // Function: CheckFileAttributes // // Description: -// Check if the given file has all the given attribute(s) +// Check if the given file has all the given attribute(s) // // Returns: -// ERROR_SUCCESS on success +// ERROR_SUCCESS on success // error code otherwise // // Notes: @@ -281,10 +296,10 @@ static DWORD FileAttributesCheck( // Function: IsDirectory // // Description: -// Check if the given file is a directory +// Check if the given file is a directory // // Returns: -// ERROR_SUCCESS on success +// ERROR_SUCCESS on success // error code otherwise // // Notes: @@ -298,10 +313,10 @@ DWORD DirectoryCheck(__in LPCWSTR pathName, __out PBOOL res) // Function: IsReparsePoint // // Description: -// Check if the given file is a reparse point +// Check if the given file is a reparse point // // Returns: -// ERROR_SUCCESS on success +// ERROR_SUCCESS on success // error code otherwise // // Notes: @@ -315,10 +330,10 @@ static DWORD ReparsePointCheck(__in LPCWSTR pathName, __out PBOOL res) // Function: CheckReparseTag // // Description: -// Check if the given file is a reparse point of the given tag. +// Check if the given file is a reparse point of the given tag. // // Returns: -// ERROR_SUCCESS on success +// ERROR_SUCCESS on success // error code otherwise // // Notes: @@ -356,10 +371,10 @@ static DWORD ReparseTagCheck(__in LPCWSTR path, __in DWORD tag, __out PBOOL res) // Function: IsSymbolicLink // // Description: -// Check if the given file is a symbolic link. +// Check if the given file is a symbolic link. // // Returns: -// ERROR_SUCCESS on success +// ERROR_SUCCESS on success // error code otherwise // // Notes: @@ -373,10 +388,10 @@ DWORD SymbolicLinkCheck(__in LPCWSTR pathName, __out PBOOL res) // Function: IsJunctionPoint // // Description: -// Check if the given file is a junction point. +// Check if the given file is a junction point. // // Returns: -// ERROR_SUCCESS on success +// ERROR_SUCCESS on success // error code otherwise // // Notes: @@ -390,14 +405,14 @@ DWORD JunctionPointCheck(__in LPCWSTR pathName, __out PBOOL res) // Function: GetSidFromAcctNameW // // Description: -// To retrieve the SID for a user account +// To retrieve the SID for a user account // // Returns: -// ERROR_SUCCESS: on success +// ERROR_SUCCESS: on success // Other error code: otherwise // // Notes: -// Caller needs to destroy the memory of Sid by calling LocalFree() +// Caller needs to destroy the memory of Sid by calling LocalFree() // DWORD GetSidFromAcctNameW(__in PCWSTR acctName, __out PSID *ppSid) { @@ -479,10 +494,10 @@ DWORD GetSidFromAcctNameW(__in PCWSTR acctName, __out PSID *ppSid) // Function: GetUnixAccessMask // // Description: -// Compute the 3 bit Unix mask for the owner, group, or, others +// Compute the 3 bit Unix mask for the owner, group, or, others // // Returns: -// The 3 bit Unix mask in INT +// The 3 bit Unix mask in INT // // Notes: // @@ -506,10 +521,10 @@ static INT GetUnixAccessMask(ACCESS_MASK Mask) // Function: GetAccess // // Description: -// Get Windows acces mask by AuthZ methods +// Get Windows acces mask by AuthZ methods // // Returns: -// ERROR_SUCCESS: on success +// ERROR_SUCCESS: on success // // Notes: // @@ -554,10 +569,10 @@ static DWORD GetAccess(AUTHZ_CLIENT_CONTEXT_HANDLE hAuthzClient, // Function: GetEffectiveRightsForSid // // Description: -// Get Windows acces mask by AuthZ methods +// Get Windows acces mask by AuthZ methods // // Returns: -// ERROR_SUCCESS: on success +// ERROR_SUCCESS: on success // // Notes: // We run into problems for local user accounts when using the method @@ -714,11 +729,11 @@ CheckAccessEnd: // Function: FindFileOwnerAndPermissionByHandle // // Description: -// Find the owner, primary group and permissions of a file object given the +// Find the owner, primary group and permissions of a file object given the // the file object handle. The function will always follow symbolic links. // // Returns: -// ERROR_SUCCESS: on success +// ERROR_SUCCESS: on success // Error code otherwise // // Notes: @@ -778,10 +793,10 @@ FindFileOwnerAndPermissionByHandleEnd: // Function: FindFileOwnerAndPermission // // Description: -// Find the owner, primary group and permissions of a file object +// Find the owner, primary group and permissions of a file object // // Returns: -// ERROR_SUCCESS: on success +// ERROR_SUCCESS: on success // Error code otherwise // // Notes: @@ -1207,14 +1222,14 @@ static DWORD GetWindowsDACLs(__in INT unixMask, if (winUserAccessDenyMask && !AddAccessDeniedAceEx(pNewDACL, ACL_REVISION, - NO_PROPAGATE_INHERIT_ACE, + CONTAINER_INHERIT_ACE | OBJECT_INHERIT_ACE, winUserAccessDenyMask, pOwnerSid)) { ret = GetLastError(); goto GetWindowsDACLsEnd; } if (!AddAccessAllowedAceEx(pNewDACL, ACL_REVISION, - NO_PROPAGATE_INHERIT_ACE, + CONTAINER_INHERIT_ACE | OBJECT_INHERIT_ACE, winUserAccessAllowMask, pOwnerSid)) { ret = GetLastError(); @@ -1222,21 +1237,21 @@ static DWORD GetWindowsDACLs(__in INT unixMask, } if (winGroupAccessDenyMask && !AddAccessDeniedAceEx(pNewDACL, ACL_REVISION, - NO_PROPAGATE_INHERIT_ACE, + CONTAINER_INHERIT_ACE | OBJECT_INHERIT_ACE, winGroupAccessDenyMask, pGroupSid)) { ret = GetLastError(); goto GetWindowsDACLsEnd; } if (!AddAccessAllowedAceEx(pNewDACL, ACL_REVISION, - NO_PROPAGATE_INHERIT_ACE, + CONTAINER_INHERIT_ACE | OBJECT_INHERIT_ACE, winGroupAccessAllowMask, pGroupSid)) { ret = GetLastError(); goto GetWindowsDACLsEnd; } if (!AddAccessAllowedAceEx(pNewDACL, ACL_REVISION, - NO_PROPAGATE_INHERIT_ACE, + CONTAINER_INHERIT_ACE | OBJECT_INHERIT_ACE, winOtherAccessAllowMask, pEveryoneSid)) { ret = GetLastError(); @@ -1440,14 +1455,14 @@ ChangeFileModeByMaskEnd: // Function: GetAccntNameFromSid // // Description: -// To retrieve an account name given the SID +// To retrieve an account name given the SID // // Returns: -// ERROR_SUCCESS: on success +// ERROR_SUCCESS: on success // Other error code: otherwise // // Notes: -// Caller needs to destroy the memory of account name by calling LocalFree() +// Caller needs to destroy the memory of account name by calling LocalFree() // DWORD GetAccntNameFromSid(__in PSID pSid, __out PWSTR *ppAcctName) { @@ -1536,10 +1551,10 @@ GetAccntNameFromSidEnd: // Function: GetLocalGroupsForUser // // Description: -// Get an array of groups for the given user. +// Get an array of groups for the given user. // // Returns: -// ERROR_SUCCESS on success +// ERROR_SUCCESS on success // Other error code on failure // // Notes: @@ -1631,11 +1646,12 @@ GetLocalGroupsForUserEnd: return ret; } + //---------------------------------------------------------------------------- // Function: EnablePrivilege // // Description: -// Check if the process has the given privilege. If yes, enable the privilege +// Check if the process has the given privilege. If yes, enable the privilege // to the process's access token. // // Returns: @@ -1672,8 +1688,8 @@ DWORD EnablePrivilege(__in LPCWSTR privilegeName) // As stated on MSDN, we need to use GetLastError() to check if // AdjustTokenPrivileges() adjusted all of the specified privileges. // - if( !AdjustTokenPrivileges(hToken, FALSE, &tp, 0, NULL, NULL) ) { - dwErrCode = GetLastError(); + if (!AdjustTokenPrivileges(hToken, FALSE, &tp, 0, NULL, NULL)) { + dwErrCode = GetLastError(); } CloseHandle(hToken); @@ -1706,12 +1722,15 @@ void ReportErrorCode(LPCWSTR func, DWORD err) (LPWSTR)&msg, 0, NULL); if (len > 0) { + LogDebugMessage(L"%s error (%d): %s\n", func, err, msg); fwprintf(stderr, L"%s error (%d): %s\n", func, err, msg); } else { + LogDebugMessage(L"%s error code: %d.\n", func, err); fwprintf(stderr, L"%s error code: %d.\n", func, err); } + if (msg != NULL) LocalFree(msg); } @@ -1843,7 +1862,7 @@ DWORD LookupKerberosAuthenticationPackageId(__in HANDLE lsaHandle, __out ULONG * } //---------------------------------------------------------------------------- -// Function: CreateLogonForUser +// Function: CreateLogonTokenForUser // // Description: // Contacts the local LSA and performs a logon without credential for the @@ -1858,7 +1877,7 @@ DWORD LookupKerberosAuthenticationPackageId(__in HANDLE lsaHandle, __out ULONG * // This call assumes that all required privileges have already been enabled (TCB etc). // IMPORTANT **** tokenOriginName must be immutable! // -DWORD CreateLogonForUser(__in HANDLE lsaHandle, +DWORD CreateLogonTokenForUser(__in HANDLE lsaHandle, __in const char * tokenSourceName, __in const char * tokenOriginName, // must be immutable, will not be copied! __in ULONG authnPkgId, @@ -2026,6 +2045,8 @@ done: return loadProfileStatus; } + + DWORD UnloadProfileForLogon(__in HANDLE logonHandle, __in PROFILEINFO * pi) { DWORD touchProfileStatus = ERROR_ASSERTION_FAILURE; // Failure to set status should trigger error @@ -2046,3 +2067,646 @@ DWORD UnloadProfileForLogon(__in HANDLE logonHandle, __in PROFILEINFO * pi) done: return touchProfileStatus; } + + +//---------------------------------------------------------------------------- +// Function: ChangeFileOwnerBySid +// +// Description: +// Change a file or directory ownership by giving new owner and group SIDs +// +// Returns: +// ERROR_SUCCESS: on success +// Error code: otherwise +// +// Notes: +// This function is long path safe, i.e. the path will be converted to long +// path format if not already converted. So the caller does not need to do +// the converstion before calling the method. +// +DWORD ChangeFileOwnerBySid(__in LPCWSTR path, + __in_opt PSID pNewOwnerSid, __in_opt PSID pNewGroupSid) +{ + LPWSTR longPathName = NULL; + INT oldMode = 0; + + SECURITY_INFORMATION securityInformation = 0; + + DWORD dwRtnCode = ERROR_SUCCESS; + + // Convert the path the the long path + // + dwRtnCode = ConvertToLongPath(path, &longPathName); + if (dwRtnCode != ERROR_SUCCESS) + { + goto ChangeFileOwnerByNameEnd; + } + + // Get a pointer to the existing owner information and DACL + // + dwRtnCode = FindFileOwnerAndPermission(longPathName, FALSE, NULL, NULL, &oldMode); + if (dwRtnCode != ERROR_SUCCESS) + { + goto ChangeFileOwnerByNameEnd; + } + + // We need SeTakeOwnershipPrivilege to set the owner if the caller does not + // have WRITE_OWNER access to the object; we need SeRestorePrivilege if the + // SID is not contained in the caller's token, and have the SE_GROUP_OWNER + // permission enabled. + // + if (EnablePrivilege(L"SeTakeOwnershipPrivilege") != ERROR_SUCCESS) + { + fwprintf(stdout, L"INFO: The user does not have SeTakeOwnershipPrivilege.\n"); + } + if (EnablePrivilege(L"SeRestorePrivilege") != ERROR_SUCCESS) + { + fwprintf(stdout, L"INFO: The user does not have SeRestorePrivilege.\n"); + } + + assert(pNewOwnerSid != NULL || pNewGroupSid != NULL); + + // Set the owners of the file. + // + if (pNewOwnerSid != NULL) securityInformation |= OWNER_SECURITY_INFORMATION; + if (pNewGroupSid != NULL) securityInformation |= GROUP_SECURITY_INFORMATION; + dwRtnCode = SetNamedSecurityInfoW( + longPathName, + SE_FILE_OBJECT, + securityInformation, + pNewOwnerSid, + pNewGroupSid, + NULL, + NULL); + if (dwRtnCode != ERROR_SUCCESS) + { + goto ChangeFileOwnerByNameEnd; + } + + // Set the permission on the file for the new owner. + // + dwRtnCode = ChangeFileModeByMask(longPathName, oldMode); + if (dwRtnCode != ERROR_SUCCESS) + { + goto ChangeFileOwnerByNameEnd; + } + +ChangeFileOwnerByNameEnd: + LocalFree(longPathName); + return dwRtnCode; +} + + +//----------------------------------------------------------------------------- +// Function: GetSecureJobObjectName +// +// Description: +// Creates a job object name usable in a secure environment: adds the Golbal\ +// + +DWORD GetSecureJobObjectName( + __in LPCWSTR jobName, + __in size_t cchSecureJobName, + __out_ecount(cchSecureJobName) LPWSTR secureJobName) { + + HRESULT hr = StringCchPrintf(secureJobName, cchSecureJobName, + L"Global\\%s", jobName); + + if (FAILED(hr)) { + return HRESULT_CODE(hr); + } + + return ERROR_SUCCESS; +} + +//----------------------------------------------------------------------------- +// Function: EnableImpersonatePrivileges +// +// Description: +// Enables the required privileges for S4U impersonation +// +// Returns: +// ERROR_SUCCESS: On success +// +DWORD EnableImpersonatePrivileges() { + DWORD dwError = ERROR_SUCCESS; + LPCWSTR privilege = NULL; + int crt = 0; + + LPCWSTR privileges[] = { + SE_IMPERSONATE_NAME, + SE_TCB_NAME, + SE_ASSIGNPRIMARYTOKEN_NAME, + SE_INCREASE_QUOTA_NAME, + SE_RESTORE_NAME, + SE_DEBUG_NAME, + SE_SECURITY_NAME, + }; + + for (crt = 0; crt < sizeof(privileges)/sizeof(LPCWSTR); ++crt) { + LPCWSTR privilege = privileges[crt]; + dwError = EnablePrivilege(privilege); + if( dwError != ERROR_SUCCESS ) { + LogDebugMessage(L"Failed to enable privilege: %s\n", privilege); + ReportErrorCode(L"EnablePrivilege", dwError); + goto done; + } + } + +done: + return dwError; +} + + +//----------------------------------------------------------------------------- +// Function: KillTask +// +// Description: +// Kills a task via a jobobject. Outputs the +// appropriate information to stdout on success, or stderr on failure. +// +// Returns: +// ERROR_SUCCESS: On success +// GetLastError: otherwise +DWORD KillTask(PCWSTR jobObjName) +{ + DWORD dwError = ERROR_SUCCESS; + + HANDLE jobObject = OpenJobObject(JOB_OBJECT_TERMINATE, FALSE, jobObjName); + if(jobObject == NULL) + { + dwError = GetLastError(); + if(dwError == ERROR_FILE_NOT_FOUND) + { + // job object does not exist. assume its not alive + dwError = ERROR_SUCCESS; + } + goto done; + } + + if(TerminateJobObject(jobObject, KILLED_PROCESS_EXIT_CODE) == 0) + { + dwError = GetLastError(); + } + +done: + CloseHandle(jobObject); + + return dwError; +} + +DWORD ChownImpl( + __in_opt LPCWSTR userName, + __in_opt LPCWSTR groupName, + __in LPCWSTR pathName) { + + DWORD dwError; + + PSID pNewOwnerSid = NULL; + PSID pNewGroupSid = NULL; + + if (userName != NULL) + { + dwError = GetSidFromAcctNameW(userName, &pNewOwnerSid); + if (dwError != ERROR_SUCCESS) + { + ReportErrorCode(L"GetSidFromAcctName", dwError); + fwprintf(stderr, L"Invalid user name: %s\n", userName); + goto done; + } + } + + if (groupName != NULL) + { + dwError = GetSidFromAcctNameW(groupName, &pNewGroupSid); + if (dwError != ERROR_SUCCESS) + { + ReportErrorCode(L"GetSidFromAcctName", dwError); + fwprintf(stderr, L"Invalid group name: %s\n", groupName); + goto done; + } + } + + if (wcslen(pathName) == 0 || wcsspn(pathName, L"/?|><:*\"") != 0) + { + fwprintf(stderr, L"Incorrect file name format: %s\n", pathName); + goto done; + } + + dwError = ChangeFileOwnerBySid(pathName, pNewOwnerSid, pNewGroupSid); + if (dwError != ERROR_SUCCESS) + { + ReportErrorCode(L"ChangeFileOwnerBySid", dwError); + goto done; + } +done: + LocalFree(pNewOwnerSid); + LocalFree(pNewGroupSid); + + return dwError; +} + + + +LPCWSTR GetSystemTimeString() { + __declspec(thread) static WCHAR buffer[1024]; + DWORD dwError; + FILETIME ftime; + SYSTEMTIME systime; + LARGE_INTEGER counter, frequency; + int subSec; + double qpc; + HRESULT hr; + buffer[0] = L'\0'; + + // GetSystemTimePreciseAsFileTime is only available in Win8+ and our libs do not link against it + + GetSystemTimeAsFileTime(&ftime); + + if (!FileTimeToSystemTime(&ftime, &systime)) { + dwError = GetLastError(); + LogDebugMessage(L"FileTimeToSystemTime error:%d\n", dwError); + goto done; + } + + // Get the ms from QPC. GetSystemTimeAdjustment is ignored... + + QueryPerformanceCounter(&counter); + QueryPerformanceFrequency(&frequency); + + qpc = (double) counter.QuadPart / (double) frequency.QuadPart; + subSec = ((qpc - (long)qpc) * 1000000); + + hr = StringCbPrintf(buffer, sizeof(buffer), L"%02d:%02d:%02d.%06d", + (int)systime.wHour, (int)systime.wMinute, (int)systime.wSecond, (int)subSec); + + if (FAILED(hr)) { + LogDebugMessage(L"StringCbPrintf error:%d\n", hr); + } +done: + return buffer; +} + + +//---------------------------------------------------------------------------- +// Function: LogDebugMessage +// +// Description: +// Sends a message to the debugger console, if one is attached +// +// Notes: +// Native debugger: windbg, ntsd, cdb, visual studio +// +VOID LogDebugMessage(LPCWSTR format, ...) { + LPWSTR buffer[8192]; + va_list args; + HRESULT hr; + + if (!IsDebuggerPresent()) return; + + va_start(args, format); + hr = StringCbVPrintf(buffer, sizeof(buffer), format, args); + if (SUCCEEDED(hr)) { + OutputDebugString(buffer); + } + va_end(args); +} + +//---------------------------------------------------------------------------- +// Function: SplitStringIgnoreSpaceW +// +// Description: +// splits a null-terminated string based on a delimiter +// +// Returns: +// ERROR_SUCCESS: on success +// error code: otherwise +// +// Notes: +// The tokes are also null-terminated +// Caller should use LocalFree to clear outTokens +// +DWORD SplitStringIgnoreSpaceW( + __in size_t len, + __in_ecount(len) LPCWSTR source, + __in WCHAR deli, + __out size_t* count, + __out_ecount(count) WCHAR*** outTokens) { + + size_t tokenCount = 0; + size_t crtSource; + size_t crtToken = 0; + WCHAR* lpwszTokenStart = NULL; + WCHAR* lpwszTokenEnd = NULL; + WCHAR* lpwszBuffer = NULL; + size_t tokenLength = 0; + size_t cchBufferLength = 0; + WCHAR crt; + WCHAR** tokens = NULL; + enum {BLANK, TOKEN, DELIMITER} State = BLANK; + + for(crtSource = 0; crtSource < len; ++crtSource) { + crt = source[crtSource]; + switch(State) { + case BLANK: // intentional fallthrough + case DELIMITER: + if (crt == deli) { + State = DELIMITER; + } + else if (!iswspace(crt)) { + ++tokenCount; + lpwszTokenEnd = lpwszTokenStart = source + crtSource; + State = TOKEN; + } + else { + State = BLANK; + } + break; + case TOKEN: + if (crt == deli) { + State = DELIMITER; + cchBufferLength += lpwszTokenEnd - lpwszTokenStart + 2; + } + else if (!iswspace(crt)) { + lpwszTokenEnd = source + crtSource; + } + break; + } + } + + if (State == TOKEN) { + cchBufferLength += lpwszTokenEnd - lpwszTokenStart + 2; + } + + LogDebugMessage(L"counted %d [buffer:%d] tokens in %s\n", tokenCount, cchBufferLength, source); + + #define COPY_CURRENT_TOKEN \ + tokenLength = lpwszTokenEnd - lpwszTokenStart + 1; \ + tokens[crtToken] = lpwszBuffer; \ + memcpy(tokens[crtToken], lpwszTokenStart, tokenLength*sizeof(WCHAR)); \ + tokens[crtToken][tokenLength] = L'\0'; \ + lpwszBuffer += (tokenLength+1); \ + ++crtToken; + + if (tokenCount) { + + // We use one contigous memory for both the pointer arrays and the data copy buffers + // We cannot use in-place references (zero-copy) because the function users + // need null-terminated strings for the tokens + + tokens = (WCHAR**) LocalAlloc(LPTR, + sizeof(WCHAR*) * tokenCount + // for the pointers + sizeof(WCHAR) * cchBufferLength); // for the data + + // Data will be copied after the array + lpwszBuffer = (WCHAR*)(((BYTE*)tokens) + (sizeof(WCHAR*) * tokenCount)); + + State = BLANK; + + for(crtSource = 0; crtSource < len; ++crtSource) { + crt = source[crtSource]; + switch(State) { + case DELIMITER: // intentional fallthrough + case BLANK: + if (crt == deli) { + State = DELIMITER; + } + else if (!iswspace(crt)) { + lpwszTokenEnd = lpwszTokenStart = source + crtSource; + State = TOKEN; + } + else { + State = BLANK; + } + break; + case TOKEN: + if (crt == deli) { + COPY_CURRENT_TOKEN; + State = DELIMITER; + } + else if (!iswspace(crt)) { + lpwszTokenEnd = source + crtSource; + } + break; + } + } + + // Copy out last token, if any + if (TOKEN == State) { + COPY_CURRENT_TOKEN; + } + } + + *count = tokenCount; + *outTokens = tokens; + + return ERROR_SUCCESS; +} + +//---------------------------------------------------------------------------- +// Function: BuildServiceSecurityDescriptor +// +// Description: +// Builds a security descriptor for an arbitrary object +// +// Returns: +// ERROR_SUCCESS: on success +// error code: otherwise +// +// Notes: +// The SD is a of the self-contained flavor (offsets, not pointers) +// Caller should use LocalFree to clear allocated pSD +// +DWORD BuildServiceSecurityDescriptor( + __in ACCESS_MASK accessMask, + __in size_t grantSidCount, + __in_ecount(grantSidCount) PSID* pGrantSids, + __in size_t denySidCount, + __in_ecount(denySidCount) PSID* pDenySids, + __in_opt PSID pOwner, + __out PSECURITY_DESCRIPTOR* pSD) { + + DWORD dwError = ERROR_SUCCESS; + int crt = 0; + int len = 0; + EXPLICIT_ACCESS* eas = NULL; + LPWSTR lpszSD = NULL; + ULONG cchSD = 0; + HANDLE hToken = INVALID_HANDLE_VALUE; + DWORD dwBufferSize = 0; + PTOKEN_USER pTokenUser = NULL; + PTOKEN_PRIMARY_GROUP pTokenGroup = NULL; + PSECURITY_DESCRIPTOR pTempSD = NULL; + ULONG cbSD = 0; + TRUSTEE owner, group; + + ZeroMemory(&owner, sizeof(owner)); + + // We'll need our own SID to add as SD owner + if (!OpenProcessToken(GetCurrentProcess(), TOKEN_QUERY, &hToken)) { + dwError = GetLastError(); + LogDebugMessage(L"OpenProcessToken: %d\n", dwError); + goto done; + } + + if (NULL == pOwner) { + if (!GetTokenInformation(hToken, TokenUser, NULL, 0, &dwBufferSize)) { + dwError = GetLastError(); + if (ERROR_INSUFFICIENT_BUFFER != dwError) { + LogDebugMessage(L"GetTokenInformation: %d\n", dwError); + goto done; + } + } + + pTokenUser = (PTOKEN_USER) LocalAlloc(LPTR, dwBufferSize); + if (NULL == pTokenUser) { + dwError = GetLastError(); + LogDebugMessage(L"LocalAlloc:pTokenUser: %d\n", dwError); + goto done; + } + + if (!GetTokenInformation(hToken, TokenUser, pTokenUser, dwBufferSize, &dwBufferSize)) { + dwError = GetLastError(); + LogDebugMessage(L"GetTokenInformation: %d\n", dwError); + goto done; + } + + if (!IsValidSid(pTokenUser->User.Sid)) { + dwError = ERROR_INVALID_PARAMETER; + LogDebugMessage(L"IsValidSid: %d\n", dwError); + goto done; + } + pOwner = pTokenUser->User.Sid; + } + + dwBufferSize = 0; + if (!GetTokenInformation(hToken, TokenPrimaryGroup, NULL, 0, &dwBufferSize)) { + dwError = GetLastError(); + if (ERROR_INSUFFICIENT_BUFFER != dwError) { + LogDebugMessage(L"GetTokenInformation: %d\n", dwError); + goto done; + } + } + + pTokenGroup = (PTOKEN_USER) LocalAlloc(LPTR, dwBufferSize); + if (NULL == pTokenGroup) { + dwError = GetLastError(); + LogDebugMessage(L"LocalAlloc:pTokenGroup: %d\n", dwError); + goto done; + } + + if (!GetTokenInformation(hToken, TokenPrimaryGroup, pTokenGroup, dwBufferSize, &dwBufferSize)) { + dwError = GetLastError(); + LogDebugMessage(L"GetTokenInformation: %d\n", dwError); + goto done; + } + + if (!IsValidSid(pTokenGroup->PrimaryGroup)) { + dwError = ERROR_INVALID_PARAMETER; + LogDebugMessage(L"IsValidSid: %d\n", dwError); + goto done; + } + + owner.TrusteeForm = TRUSTEE_IS_SID; + owner.TrusteeType = TRUSTEE_IS_UNKNOWN; + owner.ptstrName = (LPCWSTR) pOwner; + + group.TrusteeForm = TRUSTEE_IS_SID; + group.TrusteeType = TRUSTEE_IS_UNKNOWN; + group.ptstrName = (LPCWSTR) pTokenGroup->PrimaryGroup; + + eas = (EXPLICIT_ACCESS*) LocalAlloc(LPTR, sizeof(EXPLICIT_ACCESS) * (grantSidCount + denySidCount)); + if (NULL == eas) { + dwError = ERROR_OUTOFMEMORY; + LogDebugMessage(L"LocalAlloc: %d\n", dwError); + goto done; + } + + // Build the granted list + for (crt = 0; crt < grantSidCount; ++crt) { + eas[crt].grfAccessPermissions = accessMask; + eas[crt].grfAccessMode = GRANT_ACCESS; + eas[crt].grfInheritance = NO_INHERITANCE; + eas[crt].Trustee.TrusteeForm = TRUSTEE_IS_SID; + eas[crt].Trustee.TrusteeType = TRUSTEE_IS_UNKNOWN; + eas[crt].Trustee.ptstrName = (LPCWSTR) pGrantSids[crt]; + eas[crt].Trustee.pMultipleTrustee = NULL; + eas[crt].Trustee.MultipleTrusteeOperation = NO_MULTIPLE_TRUSTEE; + } + + // Build the deny list + for (; crt < grantSidCount + denySidCount; ++crt) { + eas[crt].grfAccessPermissions = accessMask; + eas[crt].grfAccessMode = DENY_ACCESS; + eas[crt].grfInheritance = NO_INHERITANCE; + eas[crt].Trustee.TrusteeForm = TRUSTEE_IS_SID; + eas[crt].Trustee.TrusteeType = TRUSTEE_IS_UNKNOWN; + eas[crt].Trustee.ptstrName = (LPCWSTR) pDenySids[crt - grantSidCount]; + eas[crt].Trustee.pMultipleTrustee = NULL; + eas[crt].Trustee.MultipleTrusteeOperation = NO_MULTIPLE_TRUSTEE; + } + + dwError = BuildSecurityDescriptor( + &owner, + &group, + crt, + eas, + 0, // cCountOfAuditEntries + NULL, // pListOfAuditEntries + NULL, // pOldSD + &cbSD, + &pTempSD); + if (ERROR_SUCCESS != dwError) { + LogDebugMessage(L"BuildSecurityDescriptor: %d\n", dwError); + goto done; + } + + *pSD = pTempSD; + pTempSD = NULL; + + if (IsDebuggerPresent()) { + ConvertSecurityDescriptorToStringSecurityDescriptor(*pSD, + SDDL_REVISION_1, + DACL_SECURITY_INFORMATION, + &lpszSD, + &cchSD); + LogDebugMessage(L"pSD: %.*s\n", cchSD, lpszSD); + } + +done: + if (eas) LocalFree(eas); + if (pTokenUser) LocalFree(pTokenUser); + if (INVALID_HANDLE_VALUE != hToken) CloseHandle(hToken); + if (lpszSD) LocalFree(lpszSD); + if (pTempSD) LocalFree(pTempSD); + return dwError; +} + +//---------------------------------------------------------------------------- +// Function: MIDL_user_allocate +// +// Description: +// Hard-coded function name used by RPC midl code for allocations +// +// Notes: +// Must match the de-allocation mechanism used in MIDL_user_free +// +void __RPC_FAR * __RPC_USER MIDL_user_allocate(size_t len) +{ + return LocalAlloc(LPTR, len); +} + + //---------------------------------------------------------------------------- + // Function: MIDL_user_free + // + // Description: + // Hard-coded function name used by RPC midl code for deallocations + // + // NoteS: + // Must match the allocation mechanism used in MIDL_user_allocate + // +void __RPC_USER MIDL_user_free(void __RPC_FAR * ptr) +{ + LocalFree(ptr); +} +
http://git-wip-us.apache.org/repos/asf/hadoop/blob/29d0164e/hadoop-common-project/hadoop-common/src/main/winutils/libwinutils.vcxproj ---------------------------------------------------------------------- diff --git a/hadoop-common-project/hadoop-common/src/main/winutils/libwinutils.vcxproj b/hadoop-common-project/hadoop-common/src/main/winutils/libwinutils.vcxproj index fc0519d..37b7f31 100644 --- a/hadoop-common-project/hadoop-common/src/main/winutils/libwinutils.vcxproj +++ b/hadoop-common-project/hadoop-common/src/main/winutils/libwinutils.vcxproj @@ -19,18 +19,10 @@ <Project DefaultTargets="Build" ToolsVersion="4.0" xmlns="http://schemas.microsoft.com/developer/msbuild/2003"> <ItemGroup Label="ProjectConfigurations"> - <ProjectConfiguration Include="Debug|Win32"> - <Configuration>Debug</Configuration> - <Platform>Win32</Platform> - </ProjectConfiguration> <ProjectConfiguration Include="Debug|x64"> <Configuration>Debug</Configuration> <Platform>x64</Platform> </ProjectConfiguration> - <ProjectConfiguration Include="Release|Win32"> - <Configuration>Release</Configuration> - <Platform>Win32</Platform> - </ProjectConfiguration> <ProjectConfiguration Include="Release|x64"> <Configuration>Release</Configuration> <Platform>x64</Platform> @@ -42,22 +34,11 @@ <RootNamespace>winutils</RootNamespace> </PropertyGroup> <Import Project="$(VCTargetsPath)\Microsoft.Cpp.Default.props" /> - <PropertyGroup Condition="'$(Configuration)|$(Platform)'=='Debug|Win32'" Label="Configuration"> - <ConfigurationType>StaticLibrary</ConfigurationType> - <UseDebugLibraries>true</UseDebugLibraries> - <CharacterSet>Unicode</CharacterSet> - </PropertyGroup> <PropertyGroup Condition="'$(Configuration)|$(Platform)'=='Debug|x64'" Label="Configuration"> <ConfigurationType>StaticLibrary</ConfigurationType> <UseDebugLibraries>true</UseDebugLibraries> <CharacterSet>Unicode</CharacterSet> </PropertyGroup> - <PropertyGroup Condition="'$(Configuration)|$(Platform)'=='Release|Win32'" Label="Configuration"> - <ConfigurationType>StaticLibrary</ConfigurationType> - <UseDebugLibraries>false</UseDebugLibraries> - <WholeProgramOptimization>true</WholeProgramOptimization> - <CharacterSet>Unicode</CharacterSet> - </PropertyGroup> <PropertyGroup Condition="'$(Configuration)|$(Platform)'=='Release|x64'" Label="Configuration"> <ConfigurationType>StaticLibrary</ConfigurationType> <UseDebugLibraries>false</UseDebugLibraries> @@ -67,15 +48,9 @@ <Import Project="$(VCTargetsPath)\Microsoft.Cpp.props" /> <ImportGroup Label="ExtensionSettings"> </ImportGroup> - <ImportGroup Label="PropertySheets" Condition="'$(Configuration)|$(Platform)'=='Debug|Win32'"> - <Import Project="$(UserRootDir)\Microsoft.Cpp.$(Platform).user.props" Condition="exists('$(UserRootDir)\Microsoft.Cpp.$(Platform).user.props')" Label="LocalAppDataPlatform" /> - </ImportGroup> <ImportGroup Condition="'$(Configuration)|$(Platform)'=='Debug|x64'" Label="PropertySheets"> <Import Project="$(UserRootDir)\Microsoft.Cpp.$(Platform).user.props" Condition="exists('$(UserRootDir)\Microsoft.Cpp.$(Platform).user.props')" Label="LocalAppDataPlatform" /> </ImportGroup> - <ImportGroup Label="PropertySheets" Condition="'$(Configuration)|$(Platform)'=='Release|Win32'"> - <Import Project="$(UserRootDir)\Microsoft.Cpp.$(Platform).user.props" Condition="exists('$(UserRootDir)\Microsoft.Cpp.$(Platform).user.props')" Label="LocalAppDataPlatform" /> - </ImportGroup> <ImportGroup Condition="'$(Configuration)|$(Platform)'=='Release|x64'" Label="PropertySheets"> <Import Project="$(UserRootDir)\Microsoft.Cpp.$(Platform).user.props" Condition="exists('$(UserRootDir)\Microsoft.Cpp.$(Platform).user.props')" Label="LocalAppDataPlatform" /> </ImportGroup> @@ -83,57 +58,35 @@ <PropertyGroup> <IncludePath>include;$(IncludePath)</IncludePath> </PropertyGroup> - <PropertyGroup Condition="'$(Configuration)|$(Platform)'=='Debug|Win32'"> - <LinkIncremental>true</LinkIncremental> - </PropertyGroup> <PropertyGroup Condition="'$(Configuration)|$(Platform)'=='Debug|x64'"> <LinkIncremental>true</LinkIncremental> - <OutDir /> - <IntDir>..\..\..\target\winutils\$(Configuration)\</IntDir> - </PropertyGroup> - <PropertyGroup Condition="'$(Configuration)|$(Platform)'=='Release|Win32'"> - <LinkIncremental>false</LinkIncremental> </PropertyGroup> <PropertyGroup Condition="'$(Configuration)|$(Platform)'=='Release|x64'"> <LinkIncremental>false</LinkIncremental> - <OutDir>..\..\..\target\bin\</OutDir> - <IntDir>..\..\..\target\winutils\$(Platform)\$(Configuration)\</IntDir> </PropertyGroup> - <ItemDefinitionGroup Condition="'$(Configuration)|$(Platform)'=='Debug|Win32'"> - <ClCompile> - <PrecompiledHeader> - </PrecompiledHeader> - <WarningLevel>Level3</WarningLevel> - <Optimization>Disabled</Optimization> - <PreprocessorDefinitions>WIN32;_DEBUG;_CONSOLE;%(PreprocessorDefinitions)</PreprocessorDefinitions> - </ClCompile> - <Link> - <SubSystem>Console</SubSystem> - <GenerateDebugInformation>true</GenerateDebugInformation> - </Link> - </ItemDefinitionGroup> <ItemDefinitionGroup Condition="'$(Configuration)|$(Platform)'=='Debug|x64'"> <ClCompile> <PrecompiledHeader> </PrecompiledHeader> <WarningLevel>Level4</WarningLevel> <Optimization>Disabled</Optimization> - <PreprocessorDefinitions>WIN32;_DEBUG;_CONSOLE;%(PreprocessorDefinitions)</PreprocessorDefinitions> + <PreprocessorDefinitions>WIN32;_DEBUG;_UNICODE;UNICODE;WSCE_CONFIG_DIR=$(WsceConfigDir);WSCE_CONFIG_FILE=$(WsceConfigFile);%(PreprocessorDefinitions)</PreprocessorDefinitions> </ClCompile> <Link> <SubSystem>Console</SubSystem> <GenerateDebugInformation>true</GenerateDebugInformation> </Link> </ItemDefinitionGroup> - <ItemDefinitionGroup Condition="'$(Configuration)|$(Platform)'=='Release|Win32'"> + <ItemDefinitionGroup Condition="'$(Configuration)|$(Platform)'=='Release|x64'"> <ClCompile> <WarningLevel>Level3</WarningLevel> <PrecompiledHeader> </PrecompiledHeader> - <Optimization>MaxSpeed</Optimization> + <!-- <Optimization>MaxSpeed</Optimization> --> + <Optimization>Disabled</Optimization> <FunctionLevelLinking>true</FunctionLevelLinking> <IntrinsicFunctions>true</IntrinsicFunctions> - <PreprocessorDefinitions>WIN32;NDEBUG;_CONSOLE;%(PreprocessorDefinitions)</PreprocessorDefinitions> + <PreprocessorDefinitions>WIN32;NDEBUG;_UNICODE;UNICODE;WSCE_CONFIG_DIR=$(WsceConfigDir);WSCE_CONFIG_FILE=$(WsceConfigFile);%(PreprocessorDefinitions)</PreprocessorDefinitions> </ClCompile> <Link> <SubSystem>Console</SubSystem> @@ -142,29 +95,34 @@ <OptimizeReferences>true</OptimizeReferences> </Link> </ItemDefinitionGroup> - <ItemDefinitionGroup Condition="'$(Configuration)|$(Platform)'=='Release|x64'"> + <ItemDefinitionGroup> <ClCompile> - <WarningLevel>Level3</WarningLevel> - <PrecompiledHeader> - </PrecompiledHeader> - <Optimization>MaxSpeed</Optimization> - <FunctionLevelLinking>true</FunctionLevelLinking> - <IntrinsicFunctions>true</IntrinsicFunctions> - <PreprocessorDefinitions>WIN32;NDEBUG;_CONSOLE;%(PreprocessorDefinitions)</PreprocessorDefinitions> + <AdditionalIncludeDirectories>$(IntermediateOutputPath)</AdditionalIncludeDirectories> </ClCompile> - <Link> - <SubSystem>Console</SubSystem> - <GenerateDebugInformation>true</GenerateDebugInformation> - <EnableCOMDATFolding>true</EnableCOMDATFolding> - <OptimizeReferences>true</OptimizeReferences> - </Link> + <Midl> + <ApplicationConfigurationMode>true</ApplicationConfigurationMode> + <TargetEnvironment>X64</TargetEnvironment> + <OutputDirectory>$(IntermediateOutputPath)</OutputDirectory> + <GenerateStublessProxies>true</GenerateStublessProxies> + <ValidateAllParameters>true</ValidateAllParameters> + <WarnAsError>true</WarnAsError> + <WarningLevel>2</WarningLevel> + </Midl> </ItemDefinitionGroup> <ItemGroup> + <ClCompile Include="client.c" /> + <ClCompile Include="$(IntermediateOutputPath)\hadoopwinutilsvc_c.c" /> <ClCompile Include="libwinutils.c" /> </ItemGroup> <ItemGroup> + <ClCompile Include="config.cpp" /> + </ItemGroup> + <ItemGroup> <ClInclude Include="include/winutils.h" /> </ItemGroup> + <ItemGroup> + <Midl Include="hadoopwinutilsvc.idl" /> + </ItemGroup> <Import Project="$(VCTargetsPath)\Microsoft.Cpp.targets" /> <ImportGroup Label="ExtensionTargets"> </ImportGroup> http://git-wip-us.apache.org/repos/asf/hadoop/blob/29d0164e/hadoop-common-project/hadoop-common/src/main/winutils/main.c ---------------------------------------------------------------------- diff --git a/hadoop-common-project/hadoop-common/src/main/winutils/main.c b/hadoop-common-project/hadoop-common/src/main/winutils/main.c index 0f40774..ac73aec 100644 --- a/hadoop-common-project/hadoop-common/src/main/winutils/main.c +++ b/hadoop-common-project/hadoop-common/src/main/winutils/main.c @@ -17,12 +17,27 @@ #include "winutils.h" +#include <winbase.h> + static void Usage(LPCWSTR program); +LONG WINAPI WinutilsSehUnhandled(_In_ struct _EXCEPTION_POINTERS *ecxr) { + LogDebugMessage(L"unhandled SEH: code:%x flags:%d\n", + ecxr->ExceptionRecord->ExceptionCode, + ecxr->ExceptionRecord->ExceptionFlags); + fwprintf(stderr, L"Unhandled exception code:%x at address:%p", + ecxr->ExceptionRecord->ExceptionCode, + ecxr->ExceptionRecord->ExceptionAddress); + ExitProcess(ERROR_UNHANDLED_EXCEPTION); + return EXCEPTION_EXECUTE_HANDLER; // not that it matters... +} + int wmain(__in int argc, __in_ecount(argc) wchar_t* argv[]) { LPCWSTR cmd = NULL; + SetUnhandledExceptionFilter(WinutilsSehUnhandled); + if (argc < 2) { Usage(argv[0]); @@ -67,6 +82,10 @@ int wmain(__in int argc, __in_ecount(argc) wchar_t* argv[]) { return SystemInfo(); } + else if (wcscmp(L"service", cmd) == 0) + { + return RunService(argc - 1, argv + 1); + } else if (wcscmp(L"help", cmd) == 0) { Usage(argv[0]); @@ -119,5 +138,9 @@ The available commands and their usages are:\n\n", program); fwprintf(stdout, L"%-15s%s\n\n", L"task", L"Task operations."); TaskUsage(); + + fwprintf(stdout, L"%-15s%s\n\n", L"service", L"Service operations."); + ServiceUsage(); + fwprintf(stdout, L"\n\n"); } http://git-wip-us.apache.org/repos/asf/hadoop/blob/29d0164e/hadoop-common-project/hadoop-common/src/main/winutils/service.c ---------------------------------------------------------------------- diff --git a/hadoop-common-project/hadoop-common/src/main/winutils/service.c b/hadoop-common-project/hadoop-common/src/main/winutils/service.c new file mode 100644 index 0000000..a0f8a66 --- /dev/null +++ b/hadoop-common-project/hadoop-common/src/main/winutils/service.c @@ -0,0 +1,1485 @@ +/** +* Licensed to the Apache Software Foundation (ASF) under one or more +* contributor license agreements. See the NOTICE file distributed with this +* work for additional information regarding copyright ownership. The ASF +* licenses this file to you under the Apache License, Version 2.0 (the +* "License"); you may not use this file except in compliance with the License. +* You may obtain a copy of the License at +* +* http://www.apache.org/licenses/LICENSE-2.0 +* +* Unless required by applicable law or agreed to in writing, software +* distributed under the License is distributed on an "AS IS" BASIS, WITHOUT +* WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the +* License for the specific language governing permissions and limitations under +* the License. +*/ + +#include "winutils.h" +#include "winutils_msg.h" +#include <Winsvc.h> +#include <errno.h> +#include <malloc.h> +#include <strsafe.h> +#include <authz.h> +#include <sddl.h> +#include "hadoopwinutilsvc_h.h" + +#pragma comment(lib, "Rpcrt4.lib") +#pragma comment(lib, "advapi32.lib") +#pragma comment(lib, "authz.lib") + +LPCWSTR NM_WSCE_ALLOWED = L"yarn.nodemanager.windows-secure-container-executor.allowed"; +LPCWSTR NM_WSCE_JOB_NAME = L"yarn.nodemanager.windows-secure-container-executor.job-name"; +LPCWSTR NM_WSCE_LOCAL_DIRS = L"yarn.nodemanager.windows-secure-container-executor.local-dirs"; + +#define SERVICE_ACCESS_MASK 0x00000001 + +SERVICE_STATUS gSvcStatus; +SERVICE_STATUS_HANDLE gSvcStatusHandle; +HANDLE ghSvcStopEvent = INVALID_HANDLE_VALUE; +HANDLE ghWaitObject = INVALID_HANDLE_VALUE; +HANDLE ghEventLog = INVALID_HANDLE_VALUE; +BOOL isListenning = FALSE; +PSECURITY_DESCRIPTOR pAllowedSD = NULL; +LPWSTR* gLocalDirs = NULL; +size_t gLocalDirsCount = 0; +int* gCchLocalDir = NULL; +LPCWSTR gJobName = NULL; + +VOID SvcError(DWORD dwError); +VOID WINAPI SvcMain(DWORD dwArg, LPTSTR* lpszArgv); +DWORD SvcInit(); +DWORD RpcInit(); +DWORD AuthInit(); +VOID ReportSvcStatus( DWORD dwCurrentState, + DWORD dwWin32ExitCode, + DWORD dwWaitHint); +VOID WINAPI SvcCtrlHandler( DWORD dwCtrl ); +VOID CALLBACK SvcShutdown( + _In_ PVOID lpParameter, + _In_ BOOLEAN TimerOrWaitFired); + +#define CHECK_ERROR_DONE(status, expected, category, message) \ + if (status != expected) { \ + ReportSvcCheckError( \ + EVENTLOG_ERROR_TYPE, \ + category, \ + status, \ + message); \ + goto done; \ + } else { \ + LogDebugMessage(L"%s: OK\n", message); \ + } + + +#define CHECK_RPC_STATUS_DONE(status, message) \ + CHECK_ERROR_DONE(status, RPC_S_OK, SERVICE_CATEGORY, message) + +#define CHECK_SVC_STATUS_DONE(status, message) \ + CHECK_ERROR_DONE(status, ERROR_SUCCESS, SERVICE_CATEGORY, message) + +#define CHECK_UNWIND_RPC(rpcCall) { \ + unwindStatus = rpcCall; \ + if (RPC_S_OK != unwindStatus) { \ + ReportSvcCheckError( \ + EVENTLOG_WARNING_TYPE, \ + SERVICE_CATEGORY, \ + unwindStatus, \ + L#rpcCall); \ + } \ + } + +//---------------------------------------------------------------------------- +// Function: ReportSvcCheckError +// +// Description: +// Reports an error with the system event log and to debugger console (if present) +// +void ReportSvcCheckError(WORD type, WORD category, DWORD dwError, LPCWSTR message) { + int len; + LPWSTR systemMsg = NULL; + LPWSTR appMsg = NULL; + DWORD dwReportError; + LPWSTR reportMsg = NULL; + WCHAR hexError[32]; + LPCWSTR inserts[] = {message, NULL, NULL, NULL}; + HRESULT hr; + + hr = StringCbPrintf(hexError, sizeof(hexError), TEXT("%x"), dwError); + if (SUCCEEDED(hr)) { + inserts[1] = hexError; + } + else { + inserts[1] = L"(Failed to format dwError as string)"; + } + + len = FormatMessageW( + FORMAT_MESSAGE_ALLOCATE_BUFFER | FORMAT_MESSAGE_FROM_SYSTEM, + NULL, dwError, + MAKELANGID(LANG_NEUTRAL, SUBLANG_DEFAULT), + (LPWSTR)&systemMsg, 0, NULL); + + if (len) { + inserts[2] = systemMsg; + } + else { + inserts[2] = L"(Failed to get the system error message)"; + } + + LogDebugMessage(L"%s:%d %.*s\n", message, dwError, len, systemMsg); + + if (INVALID_HANDLE_VALUE != ghEventLog) { + if (!ReportEvent(ghEventLog, type, category, MSG_CHECK_ERROR, + NULL, // lpUserSid + (WORD) 3, // wNumStrings + (DWORD) 0, // dwDataSize + inserts, // *lpStrings + NULL // lpRawData + )) { + // We tried to report and failed. Send to dbg. + dwReportError = GetLastError(); + len = FormatMessageW( + FORMAT_MESSAGE_ALLOCATE_BUFFER | FORMAT_MESSAGE_FROM_SYSTEM, + NULL, dwReportError, + MAKELANGID(LANG_NEUTRAL, SUBLANG_DEFAULT), + (LPWSTR)&reportMsg, 0, NULL); + LogDebugMessage(L"ReportEvent: Error:%d %.*s\n", dwReportError, reportMsg); + } + }; + + if (NULL != systemMsg) LocalFree(systemMsg); + if (NULL != reportMsg) LocalFree(reportMsg); +} + + +VOID ReportSvcMessage(WORD type, WORD category, DWORD msgId) { + DWORD dwError; + + if (INVALID_HANDLE_VALUE != ghEventLog) { + if (!ReportEvent(ghEventLog, type, category, msgId, + NULL, // lpUserSid + (WORD) 0, // wNumStrings + (DWORD) 0, // dwDataSize + NULL, // *lpStrings + NULL // lpRawData + )) { + // We tried to report and failed but debugger is attached. Send to dbg. + dwError = GetLastError(); + LogDebugMessage(L"ReportEvent: error %d\n", dwError); + } + } +} + +//---------------------------------------------------------------------------- +// Function: IsSidInList +// +// Description: +// Finds a SID in an array of SID* +// +BOOL IsSidInList( + __in PSID trustee, + __in size_t cAllowedSids, + __in_ecount(cAllowedSids) PSID* allowedSids) { + + int crtSid = 0; + + for (crtSid = 0; crtSid < cAllowedSids; ++crtSid) { + if (EqualSid(trustee, allowedSids[crtSid])) { + return TRUE; + } + } + return FALSE; +} + + +//---------------------------------------------------------------------------- +// Function: InitLocalDirs +// +// Description: +// Validates that the wsceConfigRelativePath file is only writable by Administrators +// +DWORD ValidateConfigurationFile() { + DWORD dwError = ERROR_SUCCESS; + WCHAR xmlPath[MAX_PATH]; + PSECURITY_DESCRIPTOR pSd = NULL; + BOOL daclPresent = FALSE; + BOOL daclDefaulted = FALSE; + PACL pDacl = NULL; + int crt = 0, crtSid = 0; + WELL_KNOWN_SID_TYPE allowedSidTypes[] = { + WinLocalSystemSid, + WinBuiltinAdministratorsSid}; + ACL_SIZE_INFORMATION aclInfo; + DWORD cbSid = SECURITY_MAX_SID_SIZE; + PSID* allowedSids = NULL; + int cAllowedSids = 0; + BOOL isSidDefaulted; + PSID sidOwner = NULL; + PSID sidGroup = NULL; + + allowedSids = (PSID*) LocalAlloc( + LPTR, + sizeof(PSID) * sizeof(allowedSidTypes) / sizeof(WELL_KNOWN_SID_TYPE)); + if (NULL == allowedSids) { + dwError = ERROR_OUTOFMEMORY; + CHECK_SVC_STATUS_DONE(dwError, L"LocalAlloc"); + } + + for(crt = 0; crt < sizeof(allowedSidTypes) / sizeof(WELL_KNOWN_SID_TYPE); ++crt) { + allowedSids[crt] = LocalAlloc(LPTR, SECURITY_MAX_SID_SIZE); + if (NULL == allowedSids[crt]) { + dwError = ERROR_OUTOFMEMORY; + CHECK_SVC_STATUS_DONE(dwError, L"LocalAlloc"); + } + + cbSid = SECURITY_MAX_SID_SIZE; + + if (!CreateWellKnownSid( + allowedSidTypes[crt], NULL, allowedSids[crt], &cbSid)) { + dwError = GetLastError(); + CHECK_SVC_STATUS_DONE(dwError, L"CreateWellKnownSid"); + } + ++cAllowedSids; + } + + dwError = BuildPathRelativeToModule( + wsceConfigRelativePath, + sizeof(xmlPath)/sizeof(WCHAR), + xmlPath); + CHECK_SVC_STATUS_DONE(dwError, L"BuildPathRelativeToModule"); + + dwError = GetNamedSecurityInfo( + xmlPath, + SE_FILE_OBJECT, + DACL_SECURITY_INFORMATION, + NULL, NULL, NULL, NULL, &pSd); + CHECK_SVC_STATUS_DONE(dwError, L"GetNamedSecurityInfo"); + + if (!GetSecurityDescriptorDacl( + pSd, + &daclPresent, + &pDacl, + &daclDefaulted)) { + dwError = GetLastError(); + CHECK_SVC_STATUS_DONE(dwError, L"GetSecurityDescriptorDacl"); + } + + if (!pDacl) { + dwError = ERROR_BAD_CONFIGURATION; + CHECK_SVC_STATUS_DONE(dwError, L"pDacl"); + } + + ZeroMemory(&aclInfo, sizeof(aclInfo)); + if (!GetAclInformation(pDacl, &aclInfo, sizeof(aclInfo), AclSizeInformation)) { + dwError = GetLastError(); + CHECK_SVC_STATUS_DONE(dwError, L"GetAclInformation"); + } + + // Inspect all ACEs in the file DACL. + // Look at all WRITE GRANTs. Make sure the trustee Sid is one of the approved Sid + // + for(crt = 0; crt < aclInfo.AceCount; ++crt) { + + ACE_HEADER* aceHdr = NULL; + if (!GetAce(pDacl, crt, &aceHdr)) { + dwError = GetLastError(); + CHECK_SVC_STATUS_DONE(dwError, L"GetAce"); + } + + if (ACCESS_ALLOWED_ACE_TYPE == aceHdr->AceType) { + ACCESS_ALLOWED_ACE* pAce = (ACCESS_ALLOWED_ACE*) aceHdr; + if (WinMasks[WIN_WRITE] & pAce->Mask) { + if (!IsSidInList((PSID) &pAce->SidStart, cAllowedSids, allowedSids)) { + dwError = ERROR_BAD_CONFIGURATION; + CHECK_SVC_STATUS_DONE(dwError, L"!validSidFound"); + } + } + } + } + +done: + if (pSd) LocalFree(pSd); + + if (allowedSids) { + while (cAllowedSids) { + LocalFree(allowedSids[cAllowedSids--]); + } + LocalFree(allowedSids); + } + + return dwError; +} + +//---------------------------------------------------------------------------- +// Function: InitJobName +// +// Description: +// Loads the job name to be used for created processes +// +DWORD InitJobName() { + DWORD dwError = ERROR_SUCCESS; + size_t len = 0; + LPCWSTR value = NULL; + int crt = 0; + + // Services can be restarted + if (gJobName) LocalFree(gJobName); + gJobName = NULL; + + dwError = GetConfigValue( + wsceConfigRelativePath, + NM_WSCE_JOB_NAME, &len, &value); + CHECK_SVC_STATUS_DONE(dwError, L"GetConfigValue"); + + if (len) { + gJobName = value; + } +done: + return dwError; +} + + +//---------------------------------------------------------------------------- +// Function: InitLocalDirs +// +// Description: +// Loads the configured local dirs +// +DWORD InitLocalDirs() { + DWORD dwError = ERROR_SUCCESS; + size_t len = 0; + LPCWSTR value = NULL; + int crt = 0; + + + dwError = GetConfigValue( + wsceConfigRelativePath, + NM_WSCE_LOCAL_DIRS, &len, &value); + CHECK_SVC_STATUS_DONE(dwError, L"GetConfigValue"); + + if (0 == len) { + dwError = ERROR_BAD_CONFIGURATION; + CHECK_SVC_STATUS_DONE(dwError, NM_WSCE_LOCAL_DIRS); + } + + dwError = SplitStringIgnoreSpaceW(len, value, L',', &gLocalDirsCount, &gLocalDirs); + CHECK_SVC_STATUS_DONE(dwError, L"SplitStringIgnoreSpaceW"); + + if (0 == gLocalDirsCount) { + dwError = ERROR_BAD_CONFIGURATION; + CHECK_SVC_STATUS_DONE(dwError, NM_WSCE_LOCAL_DIRS); + } + + gCchLocalDir = (int*) LocalAlloc(LPTR, sizeof(int) * gLocalDirsCount); + if (NULL == gCchLocalDir) { + dwError = ERROR_OUTOFMEMORY; + CHECK_SVC_STATUS_DONE(dwError, L"LocalAlloc"); + } + + for (crt = 0; crt < gLocalDirsCount; ++crt) { + gCchLocalDir[crt] = (int) wcsnlen(gLocalDirs[crt], MAX_PATH); + } + +done: + if (value) LocalFree(value); + + return dwError; +} + +//---------------------------------------------------------------------------- +// Function: ValidateLocalPath +// +// Description: +// Validates that a path is within the contained local dirs +// +DWORD ValidateLocalPath(LPCWSTR lpszPath) { + DWORD dwError = ERROR_SUCCESS; + int compareResult = 0; + int crt = 0; + int cchLocalBuffer = 0; + WCHAR localBuffer[MAX_PATH+1]; + BOOLEAN nullFound = FALSE; + + // Make a copy of the path and replace / with \ in the process + while(crt < MAX_PATH && !nullFound) { + switch(lpszPath[crt]) { + case L'/': + localBuffer[crt] = L'\\'; + ++crt; + break; + case L'\0': + // NULL terminator + nullFound = TRUE; + break; + default: + localBuffer[crt] = lpszPath[crt]; + ++crt; + break; + } + } + + if (FALSE == nullFound) { + dwError = ERROR_BUFFER_OVERFLOW; + CHECK_SVC_STATUS_DONE(dwError, L"localBuffer"); + } + + localBuffer[crt] = 0; + cchLocalBuffer = crt; + + for(crt = 0; crt < gLocalDirsCount; ++crt) { + + // use max len gCchLocalDir[crt] to see if it starts with this local dir + compareResult = CompareStringEx( + LOCALE_NAME_INVARIANT, + NORM_IGNORECASE, + localBuffer, gCchLocalDir[crt] <= cchLocalBuffer ? gCchLocalDir[crt] : cchLocalBuffer, + gLocalDirs[crt], gCchLocalDir[crt], + NULL, // lpVersionInformation + NULL, // lpReserved + NULL); // lParam + + if (0 == compareResult) { + dwError = GetLastError(); + CHECK_SVC_STATUS_DONE(dwError, L"CompareStringEx"); + } + + if (CSTR_EQUAL == compareResult) { + break; + } + } + + if (CSTR_EQUAL != compareResult) { + LogDebugMessage(L"ValidateLocalPath bad path: %s\n", lpszPath); + dwError = ERROR_BAD_PATHNAME; + } + +done: + return dwError; +} + + + +//---------------------------------------------------------------------------- +// Function: RunService +// +// Description: +// Registers with NT SCM and starts the service +// +// Returns: +// ERROR_SUCCESS: On success +// Error code otherwise: otherwise +DWORD RunService(__in int argc, __in_ecount(argc) wchar_t *argv[]) +{ + DWORD dwError= ERROR_SUCCESS; + int argStart = 1; + + static const SERVICE_TABLE_ENTRY serviceTable[] = { + { SVCNAME, (LPSERVICE_MAIN_FUNCTION) SvcMain }, + { NULL, NULL } + }; + + ghEventLog = RegisterEventSource(NULL, SVCNAME); + if (NULL == ghEventLog) { + dwError = GetLastError(); + CHECK_SVC_STATUS_DONE(dwError, L"RegisterEventSource") + } + + if (!StartServiceCtrlDispatcher(serviceTable)) { + dwError = GetLastError(); + CHECK_SVC_STATUS_DONE(dwError, L"StartServiceCtrlDispatcher") + } + +done: + return dwError; +} + +//---------------------------------------------------------------------------- +// Function: SvcMain +// +// Description: +// Service main entry point. +// +VOID WINAPI SvcMain() { + DWORD dwError = ERROR_SUCCESS; + + gSvcStatusHandle = RegisterServiceCtrlHandler( + SVCNAME, + SvcCtrlHandler); + if( !gSvcStatusHandle ) { + dwError = GetLastError(); + CHECK_SVC_STATUS_DONE(dwError, L"RegisterServiceCtrlHandler") + } + + // These SERVICE_STATUS members remain as set here + gSvcStatus.dwServiceType = SERVICE_WIN32_OWN_PROCESS; + gSvcStatus.dwServiceSpecificExitCode = 0; + + // Report initial status to the SCM + ReportSvcStatus( SERVICE_START_PENDING, NO_ERROR, 3000 ); + + // Perform service-specific initialization and work. + dwError = SvcInit(); + +done: + return; +} + +//---------------------------------------------------------------------------- +// Function: SvcInit +// +// Description: +// Initializes the service. +// +DWORD SvcInit() { + DWORD dwError = ERROR_SUCCESS; + + dwError = EnableImpersonatePrivileges(); + if( dwError != ERROR_SUCCESS ) { + ReportErrorCode(L"EnableImpersonatePrivileges", dwError); + goto done; + } + + // The recommended way to shutdown the service is to use an event + // and attach a callback with RegisterWaitForSingleObject + // + ghSvcStopEvent = CreateEvent( + NULL, // default security attributes + TRUE, // manual reset event + FALSE, // not signaled + NULL); // no name + + if ( ghSvcStopEvent == NULL) + { + dwError = GetLastError(); + ReportSvcCheckError(EVENTLOG_ERROR_TYPE, SERVICE_CATEGORY, + dwError, L"CreateEvent"); + ReportSvcStatus( SERVICE_STOPPED, dwError, 0 ); + goto done; + } + + if (!RegisterWaitForSingleObject (&ghWaitObject, + ghSvcStopEvent, + SvcShutdown, + NULL, + INFINITE, + WT_EXECUTEONLYONCE)) { + dwError = GetLastError(); + ReportSvcCheckError(EVENTLOG_ERROR_TYPE, SERVICE_CATEGORY, + dwError, L"RegisterWaitForSingleObject"); + CloseHandle(ghSvcStopEvent); + ReportSvcStatus( SERVICE_STOPPED, dwError, 0 ); + goto done; + } + + dwError = ValidateConfigurationFile(); + if (ERROR_SUCCESS != dwError) { + LogDebugMessage(L"ValidateConfigurationFile failed: %d", dwError); + SvcError(dwError); + goto done; + } + + dwError = AuthInit(); + if (ERROR_SUCCESS != dwError) { + LogDebugMessage(L"AuthInit failed: %d", dwError); + SvcError(dwError); + goto done; + } + + dwError = InitLocalDirs(); + if (ERROR_SUCCESS != dwError) { + LogDebugMessage(L"InitLocalDirs failed: %d", dwError); + SvcError(dwError); + goto done; + } + + dwError = InitJobName(); + if (ERROR_SUCCESS != dwError) { + LogDebugMessage(L"InitJobName failed: %d", dwError); + SvcError(dwError); + goto done; + } + + // Report running status when initialization is complete. + ReportSvcStatus( SERVICE_RUNNING, NO_ERROR, 0 ); + + dwError = RpcInit(); + +done: + return dwError; +} + +//---------------------------------------------------------------------------- +// Function: RpcAuthorizeCallback +// +// Description: +// RPC Authorization callback. +// +// Returns: +// RPC_S_OK for access authorized +// RPC_S_ACCESS_DENIED for access denied +// +RPC_STATUS CALLBACK RpcAuthorizeCallback ( + RPC_IF_HANDLE hInterface, + void* pContext) +{ + RPC_STATUS status, + unwindStatus, + authStatus = RPC_S_ACCESS_DENIED; + DWORD dwError; + LUID luidReserved2; + AUTHZ_ACCESS_REQUEST request; + AUTHZ_ACCESS_REPLY reply; + AUTHZ_CLIENT_CONTEXT_HANDLE hClientContext = NULL; + DWORD authError = ERROR_SUCCESS; + DWORD saclResult = 0; + ACCESS_MASK grantedMask = 0; + + ZeroMemory(&luidReserved2, sizeof(luidReserved2)); + ZeroMemory(&request, sizeof(request)); + ZeroMemory(&reply, sizeof(reply)); + + status = RpcGetAuthorizationContextForClient(NULL, + FALSE, // ImpersonateOnReturn + NULL, // Reserved1 + NULL, // pExpirationTime + luidReserved2, // Reserved2 + 0, // Reserved3 + NULL, // Reserved4 + &hClientContext); + CHECK_RPC_STATUS_DONE(status, L"RpcGetAuthorizationContextForClient"); + + request.DesiredAccess = MAXIMUM_ALLOWED; + reply.Error = &authError; + reply.SaclEvaluationResults = &saclResult; + reply.ResultListLength = 1; + reply.GrantedAccessMask = &grantedMask; + + if (!AuthzAccessCheck( + 0, + hClientContext, + &request, + NULL, // AuditEvent + pAllowedSD, + NULL, // OptionalSecurityDescriptorArray + 0, // OptionalSecurityDescriptorCount + &reply, + NULL // phAccessCheckResults + )) { + dwError = GetLastError(); + CHECK_SVC_STATUS_DONE(dwError, L"AuthzAccessCheck"); + } + + LogDebugMessage(L"AutzAccessCheck: Error:%d sacl:%d access:%d\n", + authError, saclResult, grantedMask); + if (authError == ERROR_SUCCESS && (grantedMask & SERVICE_ACCESS_MASK)) { + authStatus = RPC_S_OK; + } + +done: + if (NULL != hClientContext) CHECK_UNWIND_RPC(RpcFreeAuthorizationContext(&hClientContext)); + return authStatus; +} + +//---------------------------------------------------------------------------- +// Function: AuthInit +// +// Description: +// Initializes the authorization structures (security descriptor). +// +// Notes: +// This is called from RunService solely for debugging purposed +// so that it can be tested by wimply running winutil service from CLI (no SCM) +// +DWORD AuthInit() { + DWORD dwError = ERROR_SUCCESS; + int count = 0; + int crt = 0; + size_t len = 0; + LPCWSTR value = NULL; + WCHAR** tokens = NULL; + LPWSTR lpszSD = NULL; + ULONG cchSD = 0; + DWORD dwBufferSize = 0; + int allowedCount = 0; + PSID* allowedSids = NULL; + + + dwError = GetConfigValue( + wsceConfigRelativePath, + NM_WSCE_ALLOWED, &len, &value); + CHECK_SVC_STATUS_DONE(dwError, L"GetConfigValue"); + + if (0 == len) { + dwError = ERROR_BAD_CONFIGURATION; + CHECK_SVC_STATUS_DONE(dwError, NM_WSCE_ALLOWED); + } + + dwError = SplitStringIgnoreSpaceW(len, value, L',', &count, &tokens); + CHECK_SVC_STATUS_DONE(dwError, L"SplitStringIgnoreSpaceW"); + + allowedSids = (PSID*) LocalAlloc(LPTR, sizeof(PSID) * count); + if (NULL == allowedSids) { + dwError = ERROR_OUTOFMEMORY; + CHECK_SVC_STATUS_DONE(dwError, L"LocalAlloc"); + } + + for (crt = 0; crt < count; ++crt) { + dwError = GetSidFromAcctNameW(tokens[crt], &allowedSids[crt]); + CHECK_SVC_STATUS_DONE(dwError, L"GetSidFromAcctNameW"); + } + + allowedCount = count; + + dwError = BuildServiceSecurityDescriptor(SERVICE_ACCESS_MASK, + allowedCount, allowedSids, 0, NULL, NULL, &pAllowedSD); + CHECK_SVC_STATUS_DONE(dwError, L"BuildServiceSecurityDescriptor"); + +done: + if (lpszSD) LocalFree(lpszSD); + if (value) LocalFree(value); + if (tokens) LocalFree(tokens); + return dwError; +} + +//---------------------------------------------------------------------------- +// Function: RpcInit +// +// Description: +// Initializes the RPC infrastructure and starts the RPC listenner. +// +DWORD RpcInit() { + RPC_STATUS status; + DWORD dwError; + + status = RpcServerUseProtseqIf(SVCBINDING, + RPC_C_LISTEN_MAX_CALLS_DEFAULT, + HadoopWinutilSvc_v1_0_s_ifspec, + NULL); + if (RPC_S_OK != status) { + ReportSvcCheckError(EVENTLOG_ERROR_TYPE, SERVICE_CATEGORY, + status, L"RpcServerUseProtseqIf"); + SvcError(status); + dwError = status; + goto done; + } + + status = RpcServerRegisterIfEx(HadoopWinutilSvc_v1_0_s_ifspec, + NULL, // MgrTypeUuid + NULL, // MgrEpv + RPC_IF_ALLOW_LOCAL_ONLY, // Flags + RPC_C_LISTEN_MAX_CALLS_DEFAULT, // Max calls + RpcAuthorizeCallback); // Auth callback + + if (RPC_S_OK != status) { + ReportSvcCheckError(EVENTLOG_ERROR_TYPE, SERVICE_CATEGORY, + status, L"RpcServerRegisterIfEx"); + SvcError(status); + dwError = status; + goto done; + } + + status = RpcServerListen(1, RPC_C_LISTEN_MAX_CALLS_DEFAULT, TRUE); + if (RPC_S_ALREADY_LISTENING == status) { + ReportSvcCheckError(EVENTLOG_WARNING_TYPE, SERVICE_CATEGORY, + status, L"RpcServerListen"); + } + else if (RPC_S_OK != status) { + ReportSvcCheckError(EVENTLOG_ERROR_TYPE, SERVICE_CATEGORY, + status, L"RpcServerListen"); + SvcError(status); + dwError = status; + goto done; + } + + isListenning = TRUE; + + ReportSvcMessage(EVENTLOG_INFORMATION_TYPE, SERVICE_CATEGORY, + MSG_RPC_SERVICE_HAS_STARTED); + +done: + return dwError; +} + +//---------------------------------------------------------------------------- +// Function: RpcStop +// +// Description: +// Tears down the RPC infrastructure and stops the RPC listenner. +// +VOID RpcStop() { + RPC_STATUS status; + + if (isListenning) { + + status = RpcMgmtStopServerListening(NULL); + isListenning = FALSE; + + if (RPC_S_OK != status) { + ReportSvcCheckError(EVENTLOG_WARNING_TYPE, SERVICE_CATEGORY, + status, L"RpcMgmtStopServerListening"); + } + + ReportSvcMessage(EVENTLOG_INFORMATION_TYPE, SERVICE_CATEGORY, + MSG_RPC_SERVICE_HAS_STOPPED); + } +} + +//---------------------------------------------------------------------------- +// Function: CleanupHandles +// +// Description: +// Cleans up the global service handles. +// +VOID CleanupHandles() { + if (INVALID_HANDLE_VALUE != ghWaitObject) { + UnregisterWait(ghWaitObject); + ghWaitObject = INVALID_HANDLE_VALUE; + } + if (INVALID_HANDLE_VALUE != ghSvcStopEvent) { + CloseHandle(ghSvcStopEvent); + ghSvcStopEvent = INVALID_HANDLE_VALUE; + } + if (INVALID_HANDLE_VALUE != ghEventLog) { + DeregisterEventSource(ghEventLog); + ghEventLog = INVALID_HANDLE_VALUE; + } +} + +//---------------------------------------------------------------------------- +// Function: SvcError +// +// Description: +// Aborts the startup sequence. Reports error, stops RPC, cleans up globals. +// +VOID SvcError(DWORD dwError) { + RpcStop(); + CleanupHandles(); + ReportSvcStatus( SERVICE_STOPPED, dwError, 0 ); +} + +//---------------------------------------------------------------------------- +// Function: SvcShutdown +// +// Description: +// Callback when the shutdown event is signaled. Stops RPC, cleans up globals. +// +VOID CALLBACK SvcShutdown( + _In_ PVOID lpParameter, + _In_ BOOLEAN TimerOrWaitFired) { + RpcStop(); + CleanupHandles(); + ReportSvcStatus( SERVICE_STOPPED, NO_ERROR, 0 ); +} + +//---------------------------------------------------------------------------- +// Function: SvcCtrlHandler +// +// Description: +// Callback from SCM for for service events (signals). +// +// Notes: +// Shutdown is indirect, we set her the STOP_PENDING state and signal the stop event. +// Signaling the event invokes SvcShutdown which completes the shutdown. +// This two staged approach allows the SCM handler to complete fast, +// not blocking the SCM big fat global lock. +// +VOID WINAPI SvcCtrlHandler( DWORD dwCtrl ) +{ + // Handle the requested control code. + + switch(dwCtrl) + { + case SERVICE_CONTROL_STOP: + ReportSvcStatus(SERVICE_STOP_PENDING, NO_ERROR, 0); + + // Signal the service to stop. + SetEvent(ghSvcStopEvent); + + return; + + default: + break; + } + +} + +//---------------------------------------------------------------------------- +// Function: ReportSvcStatus +// +// Description: +// Updates the service status with the SCM. +// +VOID ReportSvcStatus( DWORD dwCurrentState, + DWORD dwWin32ExitCode, + DWORD dwWaitHint) +{ + static DWORD dwCheckPoint = 1; + DWORD dwError; + + // Fill in the SERVICE_STATUS structure. + + gSvcStatus.dwCurrentState = dwCurrentState; + gSvcStatus.dwWin32ExitCode = dwWin32ExitCode; + gSvcStatus.dwWaitHint = dwWaitHint; + + if (dwCurrentState == SERVICE_START_PENDING) + gSvcStatus.dwControlsAccepted = 0; + else gSvcStatus.dwControlsAccepted = SERVICE_ACCEPT_STOP; + + if ( (dwCurrentState == SERVICE_RUNNING) || + (dwCurrentState == SERVICE_STOPPED) ) + gSvcStatus.dwCheckPoint = 0; + else gSvcStatus.dwCheckPoint = dwCheckPoint++; + + // Report the status of the service to the SCM. + if (!SetServiceStatus( gSvcStatusHandle, &gSvcStatus)) { + dwError = GetLastError(); + ReportSvcCheckError(EVENTLOG_WARNING_TYPE, SERVICE_CATEGORY, + dwError, L"SetServiceStatus"); + }; +} + +//---------------------------------------------------------------------------- +// Function: WinutilsCreateProcessAsUser +// +// Description: +// The RPC midl declared function implementation +// +// Returns: +// ERROR_SUCCESS: On success +// Error code otherwise: otherwise +// +// Notes: +// This is the entry point when the NodeManager does the RPC call +// Note that the RPC call does not do any S4U work. Is simply spawns (suspended) wintutils +// using the right command line and the handles over the spwaned process to the NM +// The actual S4U work occurs in the spawned process, run and monitored by the NM +// +error_status_t WinutilsCreateProcessAsUser( + /* [in] */ handle_t IDL_handle, + /* [in] */ int nmPid, + /* [in] */ CREATE_PROCESS_REQUEST *request, + /* [out] */ CREATE_PROCESS_RESPONSE **response) { + + DWORD dwError = ERROR_SUCCESS; + LPCWSTR inserts[] = {request->cwd, request->jobName, request->user, request->pidFile, request->cmdLine, NULL}; + WCHAR winutilsPath[MAX_PATH]; + WCHAR fullCmdLine[32768]; + HANDLE taskStdInRd = INVALID_HANDLE_VALUE, taskStdInWr = INVALID_HANDLE_VALUE, + taskStdOutRd = INVALID_HANDLE_VALUE, taskStdOutWr = INVALID_HANDLE_VALUE, + taskStdErrRd = INVALID_HANDLE_VALUE, taskStdErrWr = INVALID_HANDLE_VALUE, + hNmProcess = INVALID_HANDLE_VALUE, + hDuplicateProcess = INVALID_HANDLE_VALUE, + hDuplicateThread = INVALID_HANDLE_VALUE, + hDuplicateStdIn = INVALID_HANDLE_VALUE, + hDuplicateStdOut = INVALID_HANDLE_VALUE, + hDuplicateStdErr = INVALID_HANDLE_VALUE, + hSelfProcess = INVALID_HANDLE_VALUE, + hJob = INVALID_HANDLE_VALUE; + BOOL fMustCleanupProcess = FALSE; + + HRESULT hr; + STARTUPINFO si; + PROCESS_INFORMATION pi; + SECURITY_ATTRIBUTES saTaskStdInOutErr; + + ZeroMemory( &si, sizeof(si) ); + si.cb = sizeof(si); + ZeroMemory( &pi, sizeof(pi) ); + pi.hProcess = INVALID_HANDLE_VALUE; + pi.hThread = INVALID_HANDLE_VALUE; + ZeroMemory( &saTaskStdInOutErr, sizeof(saTaskStdInOutErr)); + + + if (gJobName) { + hJob = OpenJobObject(JOB_OBJECT_ASSIGN_PROCESS, FALSE, gJobName); + if (!hJob) { + dwError = GetLastError(); + ReportSvcCheckError(EVENTLOG_ERROR_TYPE, SERVICE_CATEGORY, + dwError, L"OpenJobObject"); + goto done; + } + } + + + // NB: GetCurrentProcess returns a pseudo-handle that just so happens + // has the value -1, ie. INVALID_HANDLE_VALUE. It cannot fail. + // + hSelfProcess = GetCurrentProcess(); + + hNmProcess = OpenProcess(PROCESS_DUP_HANDLE, FALSE, nmPid); + if (NULL == hNmProcess) { + dwError = GetLastError(); + ReportSvcCheckError(EVENTLOG_ERROR_TYPE, SERVICE_CATEGORY, + dwError, L"OpenProcess"); + goto done; + } + + GetModuleFileName(NULL, winutilsPath, sizeof(winutilsPath)/sizeof(WCHAR)); + dwError = GetLastError(); // Always check after GetModuleFileName for ERROR_INSSUFICIENT_BUFFER + if (dwError) { + ReportSvcCheckError(EVENTLOG_ERROR_TYPE, SERVICE_CATEGORY, + dwError, L"GetModuleFileName"); + goto done; + } + + // NB. We can call CreateProcess("wintuls","task create ...") or we can call + // CreateProcess(NULL, "winutils task create"). Only the second form passes "task" as + // argv[1], as expected by main. First form passes "task" as argv[0] and main fails. + + hr = StringCbPrintf(fullCmdLine, sizeof(fullCmdLine), L"\"%s\" task createAsUser %ls %ls %ls %ls", + winutilsPath, + request->jobName, request->user, request->pidFile, request->cmdLine); + if (FAILED(hr)) { + ReportSvcCheckError(EVENTLOG_ERROR_TYPE, SERVICE_CATEGORY, + hr, L"StringCbPrintf:fullCmdLine"); + goto done; + } + + LogDebugMessage(L"[%ls]: %ls %ls\n", request->cwd, winutilsPath, fullCmdLine); + + // stdin/stdout/stderr redirection is handled here + // We create 3 anonymous named pipes. + // Security attributes are required so that the handles can be inherited. + // We assign one end of the pipe to the process (stdin gets a read end, stdout gets a write end) + // We then duplicate the other end in the NM process, and we close our own handle + // Finally we return the duplicate handle values to the NM + // The NM will attach Java file dscriptors to the duplicated handles and + // read/write them as ordinary Java InputStream/OutputStream objects + + si.dwFlags |= STARTF_USESTDHANDLES; + + saTaskStdInOutErr.nLength = sizeof(SECURITY_ATTRIBUTES); + saTaskStdInOutErr.bInheritHandle = TRUE; + saTaskStdInOutErr.lpSecurityDescriptor = NULL; + + if (!CreatePipe(&taskStdInRd, &taskStdInWr, &saTaskStdInOutErr, 0)) { + dwError = GetLastError(); + goto done; + } + if (!SetHandleInformation(taskStdInWr, HANDLE_FLAG_INHERIT, FALSE)) { + dwError = GetLastError(); + goto done; + } + si.hStdInput = taskStdInRd; + + if (!CreatePipe(&taskStdOutRd, &taskStdOutWr, &saTaskStdInOutErr, 0)) { + dwError = GetLastError(); + goto done; + } + if (!SetHandleInformation(taskStdOutRd, HANDLE_FLAG_INHERIT, FALSE)) { + dwError = GetLastError(); + goto done; + } + si.hStdOutput = taskStdOutWr; + + if (!CreatePipe(&taskStdErrRd, &taskStdErrWr, &saTaskStdInOutErr, 0)) { + dwError = GetLastError(); + goto done; + } + if (!SetHandleInformation(taskStdErrRd, HANDLE_FLAG_INHERIT, FALSE)) { + dwError = GetLastError(); + goto done; + } + si.hStdError = taskStdErrWr; + + if (!CreateProcess( + NULL, // lpApplicationName, + fullCmdLine, // lpCommandLine, + NULL, // lpProcessAttributes, + NULL, // lpThreadAttributes, + TRUE, // bInheritHandles, + CREATE_SUSPENDED, // dwCreationFlags, + NULL, // lpEnvironment, + request->cwd, // lpCurrentDirectory, + &si, // lpStartupInfo + &pi)) { // lpProcessInformation + + dwError = GetLastError(); + ReportSvcCheckError(EVENTLOG_ERROR_TYPE, SERVICE_CATEGORY, + dwError, L"CreateProcess"); + goto done; + } + + fMustCleanupProcess = TRUE; + + LogDebugMessage(L"CreateProcess: pid:%x\n", pi.dwProcessId); + + if (INVALID_HANDLE_VALUE != hJob) { + if (!AssignProcessToJobObject(hJob, pi.hProcess)) { + dwError = GetLastError(); + goto done; + } + } + + // Grant full access to the container user on the 'winutils task createAsUser ...' helper process + dwError = AddNodeManagerAndUserACEsToObject(pi.hProcess, request->user, PROCESS_ALL_ACCESS); + if (dwError) { + LogDebugMessage(L"failed: AddNodeManagerAndUserACEsToObject\n"); + goto done; + } + + if (!DuplicateHandle(hSelfProcess, pi.hProcess, hNmProcess, + &hDuplicateProcess, 0, FALSE, DUPLICATE_SAME_ACCESS)) { + dwError = GetLastError(); + LogDebugMessage(L"failed: pi.hProcess\n"); + goto done; + } + + if (!DuplicateHandle(hSelfProcess, pi.hThread, hNmProcess, + &hDuplicateThread, 0, FALSE, DUPLICATE_SAME_ACCESS)) { + dwError = GetLastError(); + LogDebugMessage(L"failed: pi.hThread\n"); + goto done; + } + + if (!DuplicateHandle(hSelfProcess, taskStdInWr, hNmProcess, + &hDuplicateStdIn, 0, FALSE, DUPLICATE_SAME_ACCESS)) { + dwError = GetLastError(); + LogDebugMessage(L"failed: taskStdInWr\n"); + goto done; + } + + if (!DuplicateHandle(hSelfProcess, taskStdOutRd, hNmProcess, + &hDuplicateStdOut, 0, FALSE, DUPLICATE_SAME_ACCESS)) { + dwError = GetLastError(); + LogDebugMessage(L"failed: taskStdOutRd\n"); + goto done; + } + + if (!DuplicateHandle(hSelfProcess, taskStdErrRd, hNmProcess, + &hDuplicateStdErr, 0, FALSE, DUPLICATE_SAME_ACCESS)) { + dwError = GetLastError(); + LogDebugMessage(L"failed: taskStdErrRd\n"); + goto done; + } + + *response = (CREATE_PROCESS_RESPONSE*) MIDL_user_allocate(sizeof(CREATE_PROCESS_RESPONSE)); + if (NULL == *response) { + dwError = ERROR_OUTOFMEMORY; + LogDebugMessage(L"Failed to allocate CREATE_PROCESS_RESPONSE* response\n"); + goto done; + } + + // We're now transfering ownership of the duplicated handles to the caller + // If the RPC call fails *after* this point the handles are leaked inside the NM process + // Note that there are no more API calls, only assignments. A failure could occur only if + // foced (process kill) or hardware error (faulty memory, processort bit flip etc). + + (*response)->hProcess = hDuplicateProcess; + (*response)->hThread = hDuplicateThread; + (*response)->hStdIn = hDuplicateStdIn; + (*response)->hStdOut = hDuplicateStdOut; + (*response)->hStdErr = hDuplicateStdErr; + + fMustCleanupProcess = FALSE; + +done: + + if (fMustCleanupProcess) { + LogDebugMessage(L"Cleaning process: %d due to error:%d\n", pi.dwProcessId, dwError); + TerminateProcess(pi.hProcess, EXIT_FAILURE); + + // cleanup the duplicate handles inside the NM. + + if (INVALID_HANDLE_VALUE != hDuplicateProcess) { + DuplicateHandle(hNmProcess, hDuplicateProcess, NULL, NULL, 0, FALSE, DUPLICATE_CLOSE_SOURCE); + } + if (INVALID_HANDLE_VALUE != hDuplicateThread) { + DuplicateHandle(hNmProcess, hDuplicateThread, NULL, NULL, 0, FALSE, DUPLICATE_CLOSE_SOURCE); + } + if (INVALID_HANDLE_VALUE != hDuplicateStdIn) { + DuplicateHandle(hNmProcess, hDuplicateStdIn, NULL, NULL, 0, FALSE, DUPLICATE_CLOSE_SOURCE); + } + if (INVALID_HANDLE_VALUE != hDuplicateStdOut) { + DuplicateHandle(hNmProcess, hDuplicateStdOut, NULL, NULL, 0, FALSE, DUPLICATE_CLOSE_SOURCE); + } + if (INVALID_HANDLE_VALUE != hDuplicateStdErr) { + DuplicateHandle(hNmProcess, hDuplicateStdErr, NULL, NULL, 0, FALSE, DUPLICATE_CLOSE_SOURCE); + } + } + + if (INVALID_HANDLE_VALUE != hSelfProcess) CloseHandle(hSelfProcess); + if (INVALID_HANDLE_VALUE != hNmProcess) CloseHandle(hNmProcess); + if (INVALID_HANDLE_VALUE != taskStdInRd) CloseHandle(taskStdInRd); + if (INVALID_HANDLE_VALUE != taskStdInWr) CloseHandle(taskStdInWr); + if (INVALID_HANDLE_VALUE != taskStdOutRd) CloseHandle(taskStdOutRd); + if (INVALID_HANDLE_VALUE != taskStdOutWr) CloseHandle(taskStdOutWr); + if (INVALID_HANDLE_VALUE != taskStdErrRd) CloseHandle(taskStdErrRd); + if (INVALID_HANDLE_VALUE != taskStdErrWr) CloseHandle(taskStdErrWr); + + + // This is closing our own process/thread handles. + // If the transfer was succesfull the NM has its own duplicates (if any) + if (INVALID_HANDLE_VALUE != pi.hThread) CloseHandle(pi.hThread); + if (INVALID_HANDLE_VALUE != pi.hProcess) CloseHandle(pi.hProcess); + + if (hJob) CloseHandle(hJob); + + return dwError; +} + +error_status_t WinutilsCreateFile( + /* [in] */ handle_t IDL_handle, + /* [in] */ int nm_pid, + /* [in] */ CREATEFILE_REQUEST *request, + /* [out] */ CREATEFILE_RESPONSE **response) { + + DWORD dwError = ERROR_SUCCESS; + + HANDLE hNmProcess = INVALID_HANDLE_VALUE, + hFile = INVALID_HANDLE_VALUE, + hDuplicateFile = INVALID_HANDLE_VALUE, + hSelfProcess = GetCurrentProcess(); + + SECURITY_ATTRIBUTES saFile; + + ZeroMemory( &saFile, sizeof(saFile)); + + dwError = ValidateLocalPath(request->path); + CHECK_SVC_STATUS_DONE(dwError,L"ValidateLocalPath request->path"); + + saFile.nLength = sizeof(SECURITY_ATTRIBUTES); + saFile.bInheritHandle = TRUE; + saFile.lpSecurityDescriptor = NULL; + + hFile = CreateFile( + request->path, + request->desiredAccess, + request->shareMode, + &saFile, + request->creationDisposition, + request->flags, + NULL); // hTemplate + if (INVALID_HANDLE_VALUE == hFile) { + dwError = GetLastError(); + goto done; + } + + hNmProcess = OpenProcess(PROCESS_DUP_HANDLE, FALSE, nm_pid); + if (NULL == hNmProcess) { + dwError = GetLastError(); + goto done; + } + + if (!DuplicateHandle(hSelfProcess, hFile, + hNmProcess, &hDuplicateFile, + 0, FALSE, DUPLICATE_SAME_ACCESS)) { + dwError = GetLastError(); + goto done; + } + + *response = (CREATEFILE_RESPONSE*) MIDL_user_allocate(sizeof(CREATEFILE_RESPONSE)); + if (NULL == *response) { + dwError = ERROR_OUTOFMEMORY; + goto done; + } + + (*response)->hFile = hDuplicateFile; + hDuplicateFile = INVALID_HANDLE_VALUE; + +done: + + if (INVALID_HANDLE_VALUE != hFile) CloseHandle(hFile); + if (INVALID_HANDLE_VALUE != hDuplicateFile) { + DuplicateHandle(hNmProcess, hDuplicateFile, NULL, NULL, 0, FALSE, DUPLICATE_CLOSE_SOURCE); + } + if (INVALID_HANDLE_VALUE != hNmProcess) CloseHandle(hNmProcess); + + LogDebugMessage(L"WinutilsCreateFile: %s %d, %d, %d, %d: %d", + request->path, + request->desiredAccess, + request->shareMode, + request->creationDisposition, + request->flags, + dwError); + + return dwError; +} + +error_status_t WinutilsKillTask( + /* [in] */ handle_t IDL_handle, + /* [in] */ KILLTASK_REQUEST *request) { + DWORD dwError = ERROR_SUCCESS; + HRESULT hr; + WCHAR bufferName[MAX_PATH]; + + dwError = GetSecureJobObjectName(request->taskName, MAX_PATH, bufferName); + CHECK_SVC_STATUS_DONE(dwError, L"GetSecureJobObjectName"); + + dwError = KillTask(bufferName); + + if (ERROR_ACCESS_DENIED == dwError) { + // This process runs as LocalSystem with debug privilege enabled + // The job has a security descriptor that explictly grants JOB_OBJECT_ALL_ACCESS to us + // If we get ACCESS DENIED it means the job is being unwound + dwError = ERROR_SUCCESS; + } + +done: + LogDebugMessage(L"WinutilsKillTask: %s :%d\n", bufferName, dwError); + return dwError; +} + + +error_status_t WinutilsDeletePath( + /* [in] */ handle_t IDL_handle, + /* [in] */ DELETEPATH_REQUEST *request, + /* [out] */ DELETEPATH_RESPONSE **response) { + + DWORD dwError = ERROR_SUCCESS; + BOOL deleted = FALSE; + + dwError = ValidateLocalPath(request->path); + CHECK_SVC_STATUS_DONE(dwError,L"ValidateLocalPath request->path"); + + switch(request->type) { + case PATH_IS_DIR: + deleted = RemoveDirectory(request->path); + if (!deleted) { + LogDebugMessage(L"Error %d deleting directory %s\n", GetLastError(), request->path); + } + break; + case PATH_IS_FILE: + deleted = DeleteFile(request->path); + if (!deleted) { + LogDebugMessage(L"Error %d deleting file %s\n", GetLastError(), request->path); + } + break; + default: + dwError = ERROR_BAD_ARGUMENTS; + CHECK_SVC_STATUS_DONE(dwError, L"request->operation"); + } + + *response = (DELETEPATH_RESPONSE*) MIDL_user_allocate(sizeof(DELETEPATH_RESPONSE)); + if (NULL == *response) { + dwError = ERROR_OUTOFMEMORY; + CHECK_SVC_STATUS_DONE(dwError, L"MIDL_user_allocate"); + } + + (*response)->deleted = deleted; + +done: + + LogDebugMessage(L"WinutilsDeletePath: %s %d: %d %d", + request->path, + request->type, + deleted, + dwError); + + return dwError; +} + +error_status_t WinutilsMkDir( + /* [in] */ handle_t IDL_handle, + /* [in] */ MKDIR_REQUEST *request) { + DWORD dwError = ERROR_SUCCESS; + + dwError = ValidateLocalPath(request->filePath); + CHECK_SVC_STATUS_DONE(dwError,L"ValidateLocalPath request->filePath"); + + if (!CreateDirectory(request->filePath, NULL)) { + dwError = GetLastError(); + CHECK_SVC_STATUS_DONE(dwError, L"CreateDirectory"); + } + +done: + LogDebugMessage(L"WinutilsMkDir: %s :%d\n", request->filePath, dwError); + return dwError; +} + +error_status_t WinutilsChown( + /* [in] */ handle_t IDL_handle, + /* [in] */ CHOWN_REQUEST *request) { + DWORD dwError = ERROR_SUCCESS; + + dwError = ValidateLocalPath(request->filePath); + CHECK_SVC_STATUS_DONE(dwError,L"ValidateLocalPath request->filePath"); + + dwError = ChownImpl(request->ownerName, request->groupName, request->filePath); + CHECK_SVC_STATUS_DONE(dwError, L"ChownImpl"); + +done: + LogDebugMessage(L"WinutilsChown: %s %s %s :%d\n", + request->ownerName, request->groupName, request->filePath, dwError); + return dwError; +} + +error_status_t WinutilsChmod( + /* [in] */ handle_t IDL_handle, + /* [in] */ CHMOD_REQUEST *request) { + DWORD dwError = ERROR_SUCCESS; + + dwError = ValidateLocalPath(request->filePath); + CHECK_SVC_STATUS_DONE(dwError,L"ValidateLocalPath request->filePath"); + + dwError = ChangeFileModeByMask(request->filePath, request->mode); + CHECK_SVC_STATUS_DONE(dwError, L"ChangeFileModeByMask"); + +done: + LogDebugMessage(L"WinutilsChmod: %s %o :%d\n", + request->filePath, request->mode, dwError); + return dwError; +} + +error_status_t WinutilsMoveFile( + /* [in] */ handle_t IDL_handle, + /* [in] */ MOVEFILE_REQUEST *request) { + DWORD dwError = ERROR_SUCCESS; + DWORD flags = 0; + + dwError = ValidateLocalPath(request->sourcePath); + CHECK_SVC_STATUS_DONE(dwError,L"ValidateLocalPath request->sourcePath"); + + dwError = ValidateLocalPath(request->destinationPath); + CHECK_SVC_STATUS_DONE(dwError,L"ValidateLocalPath request->destinationPath"); + + switch (request->operation) { + case MOVE_FILE: + flags |= MOVEFILE_COPY_ALLOWED; + if (request->replaceExisting) flags |= MOVEFILE_REPLACE_EXISTING; + if (!MoveFileEx(request->sourcePath, request->destinationPath, flags)) { + dwError = GetLastError(); + CHECK_SVC_STATUS_DONE(dwError, L"MoveFileEx"); + } + break; + case COPY_FILE: + if (!request->replaceExisting) flags |= COPY_FILE_FAIL_IF_EXISTS; + if (!CopyFileEx(request->sourcePath, request->destinationPath, + NULL, // lpProgressRoutine + NULL, // lpData + NULL, // pbCancel + flags)) { + dwError = GetLastError(); + CHECK_SVC_STATUS_DONE(dwError, L"CopyFileEx"); + } + break; + default: + dwError = ERROR_BAD_ARGUMENTS; + CHECK_SVC_STATUS_DONE(dwError, L"request->operation"); + } + +done: + LogDebugMessage(L"WinutilsMoveFile: %d: %s %s :%d\n", + request->operation, request->sourcePath, request->destinationPath, dwError); + return dwError; +} + + +//---------------------------------------------------------------------------- +// Function: ServiceUsage +// +// Description: +// Prints the CLI arguments for service command. +// +void ServiceUsage() +{ + fwprintf(stdout, L"\ + Usage: service\n\ + Starts the nodemanager Windows Secure Container Executor helper service.\n\ + The service must run as a high privileged account (LocalSystem)\n\ + and is used by the nodemanager WSCE to spawn secure containers on Windows.\n"); +} + +
