1 /**
2 * Licensed to the Apache Software Foundation (ASF) under one or more
3 * contributor license agreements. See the NOTICE file distributed with this
4 * work for additional information regarding copyright ownership. The ASF
5 * licenses this file to you under the Apache License, Version 2.0 (the
6 * "License"); you may not use this file except in compliance with the License.
7 * You may obtain a copy of the License at
8 *
9 * http://www.apache.org/licenses/LICENSE-2.0
10 *
11 * Unless required by applicable law or agreed to in writing, software
12 * distributed under the License is distributed on an "AS IS" BASIS, WITHOUT
13 * WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the
14 * License for the specific language governing permissions and limitations under
15 * the License.
16 */
17 
18 #include "winutils.h"
19 #include "winutils_msg.h"
20 #include <Winsvc.h>
21 #include <errno.h>
22 #include <malloc.h>
23 #include <strsafe.h>
24 #include <authz.h>
25 #include <sddl.h>
26 #include "hadoopwinutilsvc_h.h"
27 
28 #pragma comment(lib, "Rpcrt4.lib")
29 #pragma comment(lib, "advapi32.lib")
30 #pragma comment(lib, "authz.lib")
31 
32 LPCWSTR NM_WSCE_ALLOWED     = L"yarn.nodemanager.windows-secure-container-executor.allowed";
33 LPCWSTR NM_WSCE_JOB_NAME    = L"yarn.nodemanager.windows-secure-container-executor.job-name";
34 LPCWSTR NM_WSCE_LOCAL_DIRS  = L"yarn.nodemanager.windows-secure-container-executor.local-dirs";
35 
36 #define SERVICE_ACCESS_MASK 0x00000001
37 
38 SERVICE_STATUS          gSvcStatus;
39 SERVICE_STATUS_HANDLE   gSvcStatusHandle;
40 HANDLE                  ghSvcStopEvent = INVALID_HANDLE_VALUE;
41 HANDLE                  ghWaitObject = INVALID_HANDLE_VALUE;
42 HANDLE                  ghEventLog = INVALID_HANDLE_VALUE;
43 BOOL                    isListenning = FALSE;
44 PSECURITY_DESCRIPTOR    pAllowedSD = NULL;
45 LPWSTR*                 gLocalDirs = NULL;
46 size_t                  gLocalDirsCount = 0;
47 int*                    gCchLocalDir = NULL;
48 LPCWSTR                 gJobName = NULL;
49 
50 VOID SvcError(DWORD dwError);
51 VOID WINAPI SvcMain(DWORD dwArg, LPTSTR* lpszArgv);
52 DWORD SvcInit();
53 DWORD RpcInit();
54 DWORD AuthInit();
55 VOID ReportSvcStatus( DWORD dwCurrentState,
56                       DWORD dwWin32ExitCode,
57                       DWORD dwWaitHint);
58 VOID WINAPI SvcCtrlHandler( DWORD dwCtrl );
59 VOID CALLBACK SvcShutdown(
60   _In_  PVOID lpParameter,
61   _In_  BOOLEAN TimerOrWaitFired);
62 
63 #define CHECK_ERROR_DONE(status, expected, category, message)       \
64   if (status != expected) {                                         \
65     ReportSvcCheckError(                                            \
66       EVENTLOG_ERROR_TYPE,                                          \
67       category,                                                     \
68       status,                                                       \
69       message);                                                     \
70     goto done;                                                      \
71   } else {                                                          \
72     LogDebugMessage(L"%s: OK\n", message);                          \
73   }
74 
75 
76 #define CHECK_RPC_STATUS_DONE(status, message)                      \
77  CHECK_ERROR_DONE(status, RPC_S_OK, SERVICE_CATEGORY, message)
78 
79 #define CHECK_SVC_STATUS_DONE(status, message)                      \
80  CHECK_ERROR_DONE(status, ERROR_SUCCESS, SERVICE_CATEGORY, message)
81 
82 #define CHECK_UNWIND_RPC(rpcCall) {                                 \
83     unwindStatus = rpcCall;                                         \
84     if (RPC_S_OK != unwindStatus) {                                 \
85       ReportSvcCheckError(                                          \
86           EVENTLOG_WARNING_TYPE,                                    \
87           SERVICE_CATEGORY,                                         \
88           unwindStatus,                                             \
89           L#rpcCall);                                               \
90       }                                                             \
91     }
92 
93 //----------------------------------------------------------------------------
94 // Function: ReportSvcCheckError
95 //
96 // Description:
97 //  Reports an error with the system event log and to debugger console (if present)
98 //
ReportSvcCheckError(WORD type,WORD category,DWORD dwError,LPCWSTR message)99 void ReportSvcCheckError(WORD type, WORD category, DWORD dwError, LPCWSTR message) {
100     int       len;
101     LPWSTR    systemMsg = NULL;
102     LPWSTR    appMsg = NULL;
103     DWORD     dwReportError;
104     LPWSTR    reportMsg = NULL;
105     WCHAR     hexError[32];
106     LPCWSTR   inserts[] = {message, NULL, NULL, NULL};
107     HRESULT   hr;
108 
109     hr = StringCbPrintf(hexError, sizeof(hexError), TEXT("%x"), dwError);
110     if (SUCCEEDED(hr)) {
111       inserts[1] = hexError;
112     }
113     else {
114       inserts[1] = L"(Failed to format dwError as string)";
115     }
116 
117     len = FormatMessageW(
118       FORMAT_MESSAGE_ALLOCATE_BUFFER | FORMAT_MESSAGE_FROM_SYSTEM,
119       NULL, dwError,
120       MAKELANGID(LANG_NEUTRAL, SUBLANG_DEFAULT),
121       (LPWSTR)&systemMsg, 0, NULL);
122 
123     if (len) {
124       inserts[2] = systemMsg;
125     }
126     else {
127       inserts[2] = L"(Failed to get the system error message)";
128     }
129 
130     LogDebugMessage(L"%s:%d %.*s\n", message, dwError, len, systemMsg);
131 
132     if (INVALID_HANDLE_VALUE != ghEventLog) {
133       if (!ReportEvent(ghEventLog, type, category, MSG_CHECK_ERROR,
134         NULL,         // lpUserSid
135         (WORD) 3,     // wNumStrings
136         (DWORD) 0,    // dwDataSize
137         inserts,      // *lpStrings
138         NULL          // lpRawData
139         )) {
140           // We tried to report and failed. Send to dbg.
141           dwReportError = GetLastError();
142           len = FormatMessageW(
143             FORMAT_MESSAGE_ALLOCATE_BUFFER | FORMAT_MESSAGE_FROM_SYSTEM,
144             NULL, dwReportError,
145             MAKELANGID(LANG_NEUTRAL, SUBLANG_DEFAULT),
146             (LPWSTR)&reportMsg, 0, NULL);
147           LogDebugMessage(L"ReportEvent: Error:%d %.*s\n", dwReportError, reportMsg);
148       }
149     };
150 
151     if (NULL != systemMsg) LocalFree(systemMsg);
152     if (NULL != reportMsg) LocalFree(reportMsg);
153 }
154 
155 
ReportSvcMessage(WORD type,WORD category,DWORD msgId)156 VOID ReportSvcMessage(WORD type, WORD category, DWORD msgId) {
157   DWORD dwError;
158 
159   if (INVALID_HANDLE_VALUE != ghEventLog) {
160     if (!ReportEvent(ghEventLog, type, category, msgId,
161       NULL,         // lpUserSid
162       (WORD) 0,     // wNumStrings
163       (DWORD) 0,    // dwDataSize
164       NULL,         // *lpStrings
165       NULL          // lpRawData
166       )) {
167         // We tried to report and failed but debugger is attached. Send to dbg.
168         dwError = GetLastError();
169         LogDebugMessage(L"ReportEvent: error %d\n", dwError);
170     }
171   }
172 }
173 
174 //----------------------------------------------------------------------------
175 // Function: IsSidInList
176 //
177 // Description:
178 //  Finds a SID in an array of SID*
179 //
IsSidInList(__in PSID trustee,__in size_t cAllowedSids,__in_ecount (cAllowedSids)PSID * allowedSids)180 BOOL IsSidInList(
181   __in PSID trustee,
182   __in size_t cAllowedSids,
183   __in_ecount(cAllowedSids) PSID* allowedSids) {
184 
185   size_t crtSid = 0;
186 
187   for (crtSid = 0; crtSid < cAllowedSids; ++crtSid) {
188     if (EqualSid(trustee, allowedSids[crtSid])) {
189       return TRUE;
190     }
191   }
192   return FALSE;
193 }
194 
195 
196 //----------------------------------------------------------------------------
197 // Function: InitLocalDirs
198 //
199 // Description:
200 //  Validates that the wsceConfigRelativePath file is only writable by Administrators
201 //
ValidateConfigurationFile()202 DWORD ValidateConfigurationFile() {
203   DWORD dwError = ERROR_SUCCESS;
204   WCHAR xmlPath[MAX_PATH];
205   PSECURITY_DESCRIPTOR pSd = NULL;
206   BOOL daclPresent = FALSE;
207   BOOL daclDefaulted = FALSE;
208   PACL pDacl = NULL;
209   DWORD crt = 0;
210   WELL_KNOWN_SID_TYPE allowedSidTypes[] = {
211     WinLocalSystemSid,
212     WinBuiltinAdministratorsSid};
213   ACL_SIZE_INFORMATION aclInfo;
214   DWORD cbSid = SECURITY_MAX_SID_SIZE;
215   PSID* allowedSids = NULL;
216   int cAllowedSids = 0;
217   PSID sidOwner = NULL;
218   PSID sidGroup = NULL;
219 
220   allowedSids = (PSID*) LocalAlloc(
221     LPTR,
222     sizeof(PSID) * sizeof(allowedSidTypes) / sizeof(WELL_KNOWN_SID_TYPE));
223   if (NULL == allowedSids) {
224     dwError = ERROR_OUTOFMEMORY;
225     CHECK_SVC_STATUS_DONE(dwError, L"LocalAlloc");
226   }
227 
228   for(crt = 0; crt < sizeof(allowedSidTypes) / sizeof(WELL_KNOWN_SID_TYPE); ++crt) {
229     allowedSids[crt] = LocalAlloc(LPTR, SECURITY_MAX_SID_SIZE);
230     if (NULL == allowedSids[crt]) {
231       dwError = ERROR_OUTOFMEMORY;
232       CHECK_SVC_STATUS_DONE(dwError, L"LocalAlloc");
233     }
234 
235     cbSid = SECURITY_MAX_SID_SIZE;
236 
237     if (!CreateWellKnownSid(
238       allowedSidTypes[crt], NULL, allowedSids[crt], &cbSid)) {
239       dwError = GetLastError();
240       CHECK_SVC_STATUS_DONE(dwError, L"CreateWellKnownSid");
241     }
242     ++cAllowedSids;
243   }
244 
245   dwError = BuildPathRelativeToModule(
246     wsceConfigRelativePath,
247     sizeof(xmlPath)/sizeof(WCHAR),
248     xmlPath);
249   CHECK_SVC_STATUS_DONE(dwError, L"BuildPathRelativeToModule");
250 
251   dwError = GetNamedSecurityInfo(
252     xmlPath,
253     SE_FILE_OBJECT,
254     DACL_SECURITY_INFORMATION,
255     NULL, NULL, NULL, NULL, &pSd);
256   CHECK_SVC_STATUS_DONE(dwError, L"GetNamedSecurityInfo");
257 
258   if (!GetSecurityDescriptorDacl(
259     pSd,
260     &daclPresent,
261     &pDacl,
262     &daclDefaulted)) {
263     dwError = GetLastError();
264     CHECK_SVC_STATUS_DONE(dwError, L"GetSecurityDescriptorDacl");
265   }
266 
267   if (!pDacl) {
268     dwError = ERROR_BAD_CONFIGURATION;
269     CHECK_SVC_STATUS_DONE(dwError, L"pDacl");
270   }
271 
272   ZeroMemory(&aclInfo, sizeof(aclInfo));
273   if (!GetAclInformation(pDacl, &aclInfo, sizeof(aclInfo), AclSizeInformation)) {
274     dwError = GetLastError();
275     CHECK_SVC_STATUS_DONE(dwError, L"GetAclInformation");
276   }
277 
278   // Inspect all ACEs in the file DACL.
279   // Look at all WRITE GRANTs. Make sure the trustee Sid is one of the approved Sid
280   //
281   for(crt = 0; crt < aclInfo.AceCount; ++crt) {
282 
283     ACE_HEADER* aceHdr = NULL;
284     if (!GetAce(pDacl, crt, &aceHdr)) {
285       dwError = GetLastError();
286       CHECK_SVC_STATUS_DONE(dwError, L"GetAce");
287     }
288 
289     if (ACCESS_ALLOWED_ACE_TYPE == aceHdr->AceType) {
290       ACCESS_ALLOWED_ACE* pAce = (ACCESS_ALLOWED_ACE*) aceHdr;
291       if (WinMasks[WIN_WRITE] & pAce->Mask) {
292          if (!IsSidInList((PSID) &pAce->SidStart, cAllowedSids, allowedSids)) {
293             dwError = ERROR_BAD_CONFIGURATION;
294             CHECK_SVC_STATUS_DONE(dwError, L"!validSidFound");
295          }
296       }
297     }
298   }
299 
300 done:
301   if (pSd) LocalFree(pSd);
302 
303   if (allowedSids) {
304     while (cAllowedSids) {
305       LocalFree(allowedSids[cAllowedSids--]);
306       }
307     LocalFree(allowedSids);
308     }
309 
310   return dwError;
311 }
312 
313 //----------------------------------------------------------------------------
314 // Function: InitJobName
315 //
316 // Description:
317 //  Loads the job name to be used for created processes
318 //
InitJobName()319 DWORD InitJobName() {
320   DWORD     dwError = ERROR_SUCCESS;
321   size_t    len = 0;
322   LPCWSTR   value = NULL;
323   int       crt = 0;
324 
325   // Services can be restarted
326   if (gJobName) LocalFree((HLOCAL)gJobName);
327   gJobName = NULL;
328 
329   dwError = GetConfigValue(
330     wsceConfigRelativePath,
331     NM_WSCE_JOB_NAME, &len, &value);
332   CHECK_SVC_STATUS_DONE(dwError, L"GetConfigValue");
333 
334   if (len) {
335     gJobName = value;
336   }
337 done:
338   return dwError;
339 }
340 
341 
342 //----------------------------------------------------------------------------
343 // Function: InitLocalDirs
344 //
345 // Description:
346 //  Loads the configured local dirs
347 //
InitLocalDirs()348 DWORD InitLocalDirs() {
349   DWORD     dwError = ERROR_SUCCESS;
350   size_t    len = 0;
351   LPCWSTR   value = NULL;
352   size_t    crt = 0;
353 
354 
355   dwError = GetConfigValue(
356     wsceConfigRelativePath,
357     NM_WSCE_LOCAL_DIRS, &len, &value);
358   CHECK_SVC_STATUS_DONE(dwError, L"GetConfigValue");
359 
360   if (0 == len) {
361     dwError = ERROR_BAD_CONFIGURATION;
362     CHECK_SVC_STATUS_DONE(dwError, NM_WSCE_LOCAL_DIRS);
363   }
364 
365   dwError = SplitStringIgnoreSpaceW(len, value, L',', &gLocalDirsCount, &gLocalDirs);
366   CHECK_SVC_STATUS_DONE(dwError, L"SplitStringIgnoreSpaceW");
367 
368   if (0 == gLocalDirsCount) {
369     dwError = ERROR_BAD_CONFIGURATION;
370     CHECK_SVC_STATUS_DONE(dwError, NM_WSCE_LOCAL_DIRS);
371   }
372 
373   gCchLocalDir = (int*) LocalAlloc(LPTR, sizeof(int) * gLocalDirsCount);
374   if (NULL == gCchLocalDir) {
375     dwError = ERROR_OUTOFMEMORY;
376     CHECK_SVC_STATUS_DONE(dwError, L"LocalAlloc");
377   }
378 
379   for (crt = 0; crt < gLocalDirsCount; ++crt) {
380     gCchLocalDir[crt] = (int) wcsnlen(gLocalDirs[crt], MAX_PATH);
381   }
382 
383 done:
384   if (value) LocalFree((HLOCAL)value);
385 
386   return dwError;
387 }
388 
389 //----------------------------------------------------------------------------
390 // Function: ValidateLocalPath
391 //
392 // Description:
393 //  Validates that a path is within the contained local dirs
394 //
ValidateLocalPath(LPCWSTR lpszPath)395 DWORD ValidateLocalPath(LPCWSTR lpszPath) {
396   DWORD   dwError = ERROR_SUCCESS;
397   int     compareResult = 0;
398   unsigned int  crt = 0;
399   int     cchLocalBuffer = 0;
400   WCHAR   localBuffer[MAX_PATH+1];
401   BOOLEAN nullFound = FALSE;
402 
403   // Make a copy of the path and replace / with \ in the process
404   while(crt < MAX_PATH && !nullFound) {
405     switch(lpszPath[crt]) {
406     case L'/':
407       localBuffer[crt] = L'\\';
408       ++crt;
409       break;
410     case L'\0':
411       // NULL terminator
412       nullFound = TRUE;
413       break;
414     default:
415       localBuffer[crt] = lpszPath[crt];
416       ++crt;
417       break;
418     }
419   }
420 
421   if (FALSE == nullFound) {
422     dwError = ERROR_BUFFER_OVERFLOW;
423     CHECK_SVC_STATUS_DONE(dwError, L"localBuffer");
424   }
425 
426   localBuffer[crt] = 0;
427   cchLocalBuffer = crt;
428 
429   for(crt = 0; crt < gLocalDirsCount; ++crt) {
430 
431     // use max len gCchLocalDir[crt] to see if it starts with this local dir
432     compareResult = CompareStringEx(
433       LOCALE_NAME_INVARIANT,
434       NORM_IGNORECASE,
435       localBuffer, gCchLocalDir[crt] <= cchLocalBuffer ? gCchLocalDir[crt] : cchLocalBuffer,
436       gLocalDirs[crt], gCchLocalDir[crt],
437       NULL, // lpVersionInformation
438       NULL, // lpReserved
439       (LPARAM) NULL); // lParam
440 
441     if (0 == compareResult) {
442       dwError = GetLastError();
443       CHECK_SVC_STATUS_DONE(dwError, L"CompareStringEx");
444     }
445 
446     if (CSTR_EQUAL == compareResult) {
447       break;
448     }
449   }
450 
451   if (CSTR_EQUAL != compareResult) {
452     LogDebugMessage(L"ValidateLocalPath bad path: %s\n", lpszPath);
453     dwError = ERROR_BAD_PATHNAME;
454   }
455 
456 done:
457   return dwError;
458 }
459 
460 
461 
462 //----------------------------------------------------------------------------
463 // Function: RunService
464 //
465 // Description:
466 //  Registers with NT SCM and starts the service
467 //
468 // Returns:
469 // ERROR_SUCCESS: On success
470 // Error code otherwise: otherwise
RunService(__in int argc,__in_ecount (argc)wchar_t * argv[])471 DWORD RunService(__in int argc, __in_ecount(argc) wchar_t *argv[])
472 {
473   DWORD dwError= ERROR_SUCCESS;
474   int argStart = 1;
475 
476   static const SERVICE_TABLE_ENTRY serviceTable[] = {
477     { SVCNAME, (LPSERVICE_MAIN_FUNCTION) SvcMain },
478     { NULL, NULL }
479     };
480 
481   ghEventLog = RegisterEventSource(NULL, SVCNAME);
482   if (NULL == ghEventLog) {
483     dwError = GetLastError();
484     CHECK_SVC_STATUS_DONE(dwError, L"RegisterEventSource")
485   }
486 
487   if (!StartServiceCtrlDispatcher(serviceTable)) {
488     dwError = GetLastError();
489     CHECK_SVC_STATUS_DONE(dwError, L"StartServiceCtrlDispatcher")
490   }
491 
492 done:
493   return dwError;
494 }
495 
496 //----------------------------------------------------------------------------
497 // Function: SvcMain
498 //
499 // Description:
500 //  Service main entry point.
501 //
SvcMain(DWORD dwArg,LPTSTR * lpszArgv)502 VOID WINAPI SvcMain(DWORD dwArg, LPTSTR* lpszArgv) {
503   DWORD dwError = ERROR_SUCCESS;
504 
505   gSvcStatusHandle = RegisterServiceCtrlHandler(
506         SVCNAME,
507         SvcCtrlHandler);
508   if( !gSvcStatusHandle ) {
509     dwError = GetLastError();
510     CHECK_SVC_STATUS_DONE(dwError, L"RegisterServiceCtrlHandler")
511   }
512 
513   // These SERVICE_STATUS members remain as set here
514   gSvcStatus.dwServiceType = SERVICE_WIN32_OWN_PROCESS;
515   gSvcStatus.dwServiceSpecificExitCode = 0;
516 
517   // Report initial status to the SCM
518   ReportSvcStatus( SERVICE_START_PENDING, NO_ERROR, 3000 );
519 
520   // Perform service-specific initialization and work.
521   dwError = SvcInit();
522 
523 done:
524   return;
525 }
526 
527 //----------------------------------------------------------------------------
528 // Function: SvcInit
529 //
530 // Description:
531 //  Initializes the service.
532 //
SvcInit()533 DWORD SvcInit() {
534   DWORD dwError = ERROR_SUCCESS;
535 
536   dwError = EnableImpersonatePrivileges();
537   if( dwError != ERROR_SUCCESS ) {
538     ReportErrorCode(L"EnableImpersonatePrivileges", dwError);
539     goto done;
540   }
541 
542   // The recommended way to shutdown the service is to use an event
543   //  and attach a callback with RegisterWaitForSingleObject
544   //
545   ghSvcStopEvent = CreateEvent(
546                            NULL,    // default security attributes
547                            TRUE,    // manual reset event
548                            FALSE,   // not signaled
549                            NULL);   // no name
550 
551   if ( ghSvcStopEvent == NULL)
552   {
553       dwError = GetLastError();
554       ReportSvcCheckError(EVENTLOG_ERROR_TYPE, SERVICE_CATEGORY,
555         dwError, L"CreateEvent");
556       ReportSvcStatus( SERVICE_STOPPED, dwError, 0 );
557       goto done;
558   }
559 
560   if (!RegisterWaitForSingleObject (&ghWaitObject,
561                             ghSvcStopEvent,
562                             SvcShutdown,
563                             NULL,
564                             INFINITE,
565                             WT_EXECUTEONLYONCE)) {
566     dwError = GetLastError();
567     ReportSvcCheckError(EVENTLOG_ERROR_TYPE, SERVICE_CATEGORY,
568       dwError, L"RegisterWaitForSingleObject");
569     CloseHandle(ghSvcStopEvent);
570     ReportSvcStatus( SERVICE_STOPPED, dwError, 0 );
571     goto done;
572   }
573 
574   dwError = ValidateConfigurationFile();
575   if (ERROR_SUCCESS != dwError) {
576     LogDebugMessage(L"ValidateConfigurationFile failed: %d", dwError);
577     SvcError(dwError);
578     goto done;
579   }
580 
581   dwError = AuthInit();
582   if (ERROR_SUCCESS != dwError) {
583     LogDebugMessage(L"AuthInit failed: %d", dwError);
584     SvcError(dwError);
585     goto done;
586   }
587 
588   dwError = InitLocalDirs();
589   if (ERROR_SUCCESS != dwError) {
590     LogDebugMessage(L"InitLocalDirs failed: %d", dwError);
591     SvcError(dwError);
592     goto done;
593   }
594 
595   dwError = InitJobName();
596   if (ERROR_SUCCESS != dwError) {
597     LogDebugMessage(L"InitJobName failed: %d", dwError);
598     SvcError(dwError);
599     goto done;
600   }
601 
602   // Report running status when initialization is  complete.
603   ReportSvcStatus( SERVICE_RUNNING, NO_ERROR, 0 );
604 
605   dwError = RpcInit();
606 
607 done:
608   return dwError;
609 }
610 
611 //----------------------------------------------------------------------------
612 // Function: RpcAuthorizeCallback
613 //
614 // Description:
615 //  RPC Authorization callback.
616 //
617 // Returns:
618 //  RPC_S_OK for access authorized
619 //  RPC_S_ACCESS_DENIED for access denied
620 //
RpcAuthorizeCallback(RPC_IF_HANDLE hInterface,void * pContext)621 RPC_STATUS CALLBACK RpcAuthorizeCallback (
622   RPC_IF_HANDLE  hInterface,
623   void* pContext)
624 {
625   RPC_STATUS                status,
626                             unwindStatus,
627                             authStatus = RPC_S_ACCESS_DENIED;
628   DWORD                     dwError;
629   LUID                      luidReserved2;
630   AUTHZ_ACCESS_REQUEST      request;
631   AUTHZ_ACCESS_REPLY        reply;
632   AUTHZ_CLIENT_CONTEXT_HANDLE hClientContext = NULL;
633   DWORD                     authError = ERROR_SUCCESS;
634   DWORD                     saclResult = 0;
635   ACCESS_MASK               grantedMask = 0;
636 
637   ZeroMemory(&luidReserved2, sizeof(luidReserved2));
638   ZeroMemory(&request, sizeof(request));
639   ZeroMemory(&reply, sizeof(reply));
640 
641   status = RpcGetAuthorizationContextForClient(NULL,
642         FALSE,         // ImpersonateOnReturn
643         NULL,          // Reserved1
644         NULL,          // pExpirationTime
645         luidReserved2, // Reserved2
646         0,             // Reserved3
647         NULL,          // Reserved4
648         &hClientContext);
649   CHECK_RPC_STATUS_DONE(status, L"RpcGetAuthorizationContextForClient");
650 
651   request.DesiredAccess = MAXIMUM_ALLOWED;
652   reply.Error = &authError;
653   reply.SaclEvaluationResults = &saclResult;
654   reply.ResultListLength = 1;
655   reply.GrantedAccessMask = &grantedMask;
656 
657   if (!AuthzAccessCheck(
658     0,
659     hClientContext,
660     &request,
661     NULL,   // AuditEvent
662     pAllowedSD,
663     NULL,  // OptionalSecurityDescriptorArray
664     0,     // OptionalSecurityDescriptorCount
665     &reply,
666     NULL  // phAccessCheckResults
667     )) {
668     dwError = GetLastError();
669     CHECK_SVC_STATUS_DONE(dwError, L"AuthzAccessCheck");
670   }
671 
672   LogDebugMessage(L"AutzAccessCheck: Error:%d sacl:%d access:%d\n",
673     authError, saclResult, grantedMask);
674   if (authError == ERROR_SUCCESS && (grantedMask & SERVICE_ACCESS_MASK)) {
675     authStatus = RPC_S_OK;
676   }
677 
678 done:
679   if (NULL != hClientContext) CHECK_UNWIND_RPC(RpcFreeAuthorizationContext(&hClientContext));
680   return authStatus;
681 }
682 
683 //----------------------------------------------------------------------------
684 // Function: AuthInit
685 //
686 // Description:
687 //  Initializes the authorization structures (security descriptor).
688 //
689 // Notes:
690 //  This is called from RunService solely for debugging purposed
691 //   so that it can be tested by wimply running winutil service from CLI (no SCM)
692 //
AuthInit()693 DWORD AuthInit() {
694   DWORD       dwError = ERROR_SUCCESS;
695   size_t      count = 0;
696   size_t      crt  = 0;
697   size_t      len = 0;
698   LPCWSTR     value = NULL;
699   WCHAR**     tokens = NULL;
700   LPWSTR      lpszSD = NULL;
701   ULONG       cchSD = 0;
702   DWORD       dwBufferSize = 0;
703   size_t      allowedCount = 0;
704   PSID*       allowedSids = NULL;
705 
706 
707   dwError = GetConfigValue(
708     wsceConfigRelativePath,
709     NM_WSCE_ALLOWED, &len, &value);
710   CHECK_SVC_STATUS_DONE(dwError, L"GetConfigValue");
711 
712   if (0 == len) {
713     dwError = ERROR_BAD_CONFIGURATION;
714     CHECK_SVC_STATUS_DONE(dwError, NM_WSCE_ALLOWED);
715   }
716 
717   dwError = SplitStringIgnoreSpaceW(len, value, L',', &count, &tokens);
718   CHECK_SVC_STATUS_DONE(dwError, L"SplitStringIgnoreSpaceW");
719 
720   allowedSids = (PSID*) LocalAlloc(LPTR, sizeof(PSID) * count);
721   if (NULL == allowedSids) {
722     dwError = ERROR_OUTOFMEMORY;
723     CHECK_SVC_STATUS_DONE(dwError, L"LocalAlloc");
724   }
725 
726   for (crt = 0; crt < count; ++crt) {
727     dwError = GetSidFromAcctNameW(tokens[crt], &allowedSids[crt]);
728     CHECK_SVC_STATUS_DONE(dwError, L"GetSidFromAcctNameW");
729   }
730 
731   allowedCount = count;
732 
733   dwError = BuildServiceSecurityDescriptor(SERVICE_ACCESS_MASK,
734     allowedCount, allowedSids, 0, NULL, NULL, &pAllowedSD);
735   CHECK_SVC_STATUS_DONE(dwError, L"BuildServiceSecurityDescriptor");
736 
737 done:
738   if (lpszSD) LocalFree(lpszSD);
739   if (value) LocalFree((HLOCAL)value);
740   if (tokens) LocalFree(tokens);
741   return dwError;
742 }
743 
744 //----------------------------------------------------------------------------
745 // Function: RpcInit
746 //
747 // Description:
748 //  Initializes the RPC infrastructure and starts the RPC listenner.
749 //
RpcInit()750 DWORD RpcInit() {
751   RPC_STATUS  status;
752   DWORD       dwError;
753 
754   status = RpcServerUseProtseqIf(SVCBINDING,
755                  RPC_C_LISTEN_MAX_CALLS_DEFAULT,
756                  HadoopWinutilSvc_v1_0_s_ifspec,
757                  NULL);
758   if (RPC_S_OK != status) {
759     ReportSvcCheckError(EVENTLOG_ERROR_TYPE, SERVICE_CATEGORY,
760       status, L"RpcServerUseProtseqIf");
761     SvcError(status);
762     dwError = status;
763     goto done;
764   }
765 
766   status = RpcServerRegisterIfEx(HadoopWinutilSvc_v1_0_s_ifspec,
767                  NULL,                                          // MgrTypeUuid
768                  NULL,                                          // MgrEpv
769                  RPC_IF_ALLOW_LOCAL_ONLY,                       // Flags
770                  RPC_C_LISTEN_MAX_CALLS_DEFAULT,                // Max calls
771                  RpcAuthorizeCallback);                         // Auth callback
772 
773   if (RPC_S_OK != status) {
774     ReportSvcCheckError(EVENTLOG_ERROR_TYPE, SERVICE_CATEGORY,
775       status, L"RpcServerRegisterIfEx");
776     SvcError(status);
777     dwError = status;
778     goto done;
779   }
780 
781   status = RpcServerListen(1, RPC_C_LISTEN_MAX_CALLS_DEFAULT, TRUE);
782   if (RPC_S_ALREADY_LISTENING == status) {
783     ReportSvcCheckError(EVENTLOG_WARNING_TYPE, SERVICE_CATEGORY,
784       status, L"RpcServerListen");
785   }
786   else if (RPC_S_OK != status) {
787     ReportSvcCheckError(EVENTLOG_ERROR_TYPE, SERVICE_CATEGORY,
788       status, L"RpcServerListen");
789     SvcError(status);
790     dwError = status;
791     goto done;
792   }
793 
794   isListenning = TRUE;
795 
796   ReportSvcMessage(EVENTLOG_INFORMATION_TYPE, SERVICE_CATEGORY,
797       MSG_RPC_SERVICE_HAS_STARTED);
798 
799 done:
800   return dwError;
801 }
802 
803 //----------------------------------------------------------------------------
804 // Function: RpcStop
805 //
806 // Description:
807 //  Tears down the RPC infrastructure and stops the RPC listenner.
808 //
RpcStop()809 VOID RpcStop() {
810   RPC_STATUS  status;
811 
812   if (isListenning) {
813 
814     status = RpcMgmtStopServerListening(NULL);
815     isListenning = FALSE;
816 
817     if (RPC_S_OK != status) {
818       ReportSvcCheckError(EVENTLOG_WARNING_TYPE, SERVICE_CATEGORY,
819         status, L"RpcMgmtStopServerListening");
820     }
821 
822     ReportSvcMessage(EVENTLOG_INFORMATION_TYPE, SERVICE_CATEGORY,
823         MSG_RPC_SERVICE_HAS_STOPPED);
824   }
825 }
826 
827 //----------------------------------------------------------------------------
828 // Function: CleanupHandles
829 //
830 // Description:
831 //  Cleans up the global service handles.
832 //
CleanupHandles()833 VOID CleanupHandles() {
834   if (INVALID_HANDLE_VALUE != ghWaitObject) {
835     UnregisterWait(ghWaitObject);
836     ghWaitObject = INVALID_HANDLE_VALUE;
837   }
838   if (INVALID_HANDLE_VALUE != ghSvcStopEvent) {
839     CloseHandle(ghSvcStopEvent);
840     ghSvcStopEvent = INVALID_HANDLE_VALUE;
841   }
842   if (INVALID_HANDLE_VALUE != ghEventLog) {
843     DeregisterEventSource(ghEventLog);
844     ghEventLog = INVALID_HANDLE_VALUE;
845   }
846 }
847 
848 //----------------------------------------------------------------------------
849 // Function: SvcError
850 //
851 // Description:
852 //  Aborts the startup sequence. Reports error, stops RPC, cleans up globals.
853 //
SvcError(DWORD dwError)854 VOID SvcError(DWORD dwError) {
855   RpcStop();
856   CleanupHandles();
857   ReportSvcStatus( SERVICE_STOPPED, dwError, 0 );
858 }
859 
860 //----------------------------------------------------------------------------
861 // Function: SvcShutdown
862 //
863 // Description:
864 //  Callback when the shutdown event is signaled. Stops RPC, cleans up globals.
865 //
SvcShutdown(_In_ PVOID lpParameter,_In_ BOOLEAN TimerOrWaitFired)866 VOID CALLBACK SvcShutdown(
867   _In_  PVOID lpParameter,
868   _In_  BOOLEAN TimerOrWaitFired) {
869   RpcStop();
870   CleanupHandles();
871   ReportSvcStatus( SERVICE_STOPPED, NO_ERROR, 0 );
872 }
873 
874 //----------------------------------------------------------------------------
875 // Function: SvcCtrlHandler
876 //
877 // Description:
878 //  Callback from SCM for for service events (signals).
879 //
880 // Notes:
881 //   Shutdown is indirect, we set her the STOP_PENDING state and signal the stop event.
882 //   Signaling the event invokes SvcShutdown which completes the shutdown.
883 //   This two staged approach allows the SCM handler to complete fast,
884 //   not blocking the SCM big fat global lock.
885 //
SvcCtrlHandler(DWORD dwCtrl)886 VOID WINAPI SvcCtrlHandler( DWORD dwCtrl )
887 {
888    // Handle the requested control code.
889 
890    switch(dwCtrl)
891    {
892       case SERVICE_CONTROL_STOP:
893          ReportSvcStatus(SERVICE_STOP_PENDING, NO_ERROR, 0);
894 
895          // Signal the service to stop.
896          SetEvent(ghSvcStopEvent);
897 
898          return;
899 
900       default:
901          break;
902    }
903 
904 }
905 
906 //----------------------------------------------------------------------------
907 // Function: ReportSvcStatus
908 //
909 // Description:
910 //  Updates the service status with the SCM.
911 //
ReportSvcStatus(DWORD dwCurrentState,DWORD dwWin32ExitCode,DWORD dwWaitHint)912 VOID ReportSvcStatus( DWORD dwCurrentState,
913                       DWORD dwWin32ExitCode,
914                       DWORD dwWaitHint)
915 {
916     static DWORD dwCheckPoint = 1;
917     DWORD dwError;
918 
919     // Fill in the SERVICE_STATUS structure.
920 
921     gSvcStatus.dwCurrentState = dwCurrentState;
922     gSvcStatus.dwWin32ExitCode = dwWin32ExitCode;
923     gSvcStatus.dwWaitHint = dwWaitHint;
924 
925     if (dwCurrentState == SERVICE_START_PENDING)
926         gSvcStatus.dwControlsAccepted = 0;
927     else gSvcStatus.dwControlsAccepted = SERVICE_ACCEPT_STOP;
928 
929     if ( (dwCurrentState == SERVICE_RUNNING) ||
930            (dwCurrentState == SERVICE_STOPPED) )
931         gSvcStatus.dwCheckPoint = 0;
932     else gSvcStatus.dwCheckPoint = dwCheckPoint++;
933 
934     // Report the status of the service to the SCM.
935     if (!SetServiceStatus( gSvcStatusHandle, &gSvcStatus)) {
936       dwError = GetLastError();
937       ReportSvcCheckError(EVENTLOG_WARNING_TYPE, SERVICE_CATEGORY,
938         dwError, L"SetServiceStatus");
939     };
940 }
941 
942 //----------------------------------------------------------------------------
943 // Function: WinutilsCreateProcessAsUser
944 //
945 // Description:
946 //  The RPC midl declared function implementation
947 //
948 // Returns:
949 // ERROR_SUCCESS: On success
950 // Error code otherwise: otherwise
951 //
952 // Notes:
953 //  This is the entry point when the NodeManager does the RPC call
954 //  Note that the RPC call does not do any S4U work. Is simply spawns (suspended) wintutils
955 //  using the right command line and the handles over the spwaned process to the NM
956 //  The actual S4U work occurs in the spawned process, run and monitored by the NM
957 //
WinutilsCreateProcessAsUser(handle_t IDL_handle,int nmPid,CREATE_PROCESS_REQUEST * request,CREATE_PROCESS_RESPONSE ** response)958 error_status_t WinutilsCreateProcessAsUser(
959     /* [in] */ handle_t IDL_handle,
960     /* [in] */ int nmPid,
961     /* [in] */ CREATE_PROCESS_REQUEST *request,
962     /* [out] */ CREATE_PROCESS_RESPONSE **response) {
963 
964   DWORD dwError = ERROR_SUCCESS;
965   LPCWSTR inserts[] = {request->cwd, request->jobName, request->user, request->pidFile, request->cmdLine, NULL};
966   WCHAR winutilsPath[MAX_PATH];
967   WCHAR fullCmdLine[32768];
968   HANDLE taskStdInRd = INVALID_HANDLE_VALUE, taskStdInWr = INVALID_HANDLE_VALUE,
969     taskStdOutRd = INVALID_HANDLE_VALUE, taskStdOutWr = INVALID_HANDLE_VALUE,
970     taskStdErrRd = INVALID_HANDLE_VALUE, taskStdErrWr = INVALID_HANDLE_VALUE,
971     hNmProcess = INVALID_HANDLE_VALUE,
972     hDuplicateProcess = INVALID_HANDLE_VALUE,
973     hDuplicateThread = INVALID_HANDLE_VALUE,
974     hDuplicateStdIn  = INVALID_HANDLE_VALUE,
975     hDuplicateStdOut = INVALID_HANDLE_VALUE,
976     hDuplicateStdErr = INVALID_HANDLE_VALUE,
977     hSelfProcess = INVALID_HANDLE_VALUE,
978     hJob = INVALID_HANDLE_VALUE;
979   BOOL fMustCleanupProcess = FALSE;
980 
981   HRESULT hr;
982   STARTUPINFO si;
983   PROCESS_INFORMATION pi;
984   SECURITY_ATTRIBUTES saTaskStdInOutErr;
985 
986   ZeroMemory( &si, sizeof(si) );
987   si.cb = sizeof(si);
988   ZeroMemory( &pi, sizeof(pi) );
989   pi.hProcess = INVALID_HANDLE_VALUE;
990   pi.hThread = INVALID_HANDLE_VALUE;
991   ZeroMemory( &saTaskStdInOutErr, sizeof(saTaskStdInOutErr));
992 
993 
994   if (gJobName) {
995     hJob = OpenJobObject(JOB_OBJECT_ASSIGN_PROCESS, FALSE, gJobName);
996     if (!hJob) {
997       dwError = GetLastError();
998       ReportSvcCheckError(EVENTLOG_ERROR_TYPE, SERVICE_CATEGORY,
999         dwError, L"OpenJobObject");
1000       goto done;
1001     }
1002   }
1003 
1004 
1005   // NB: GetCurrentProcess returns a pseudo-handle that just so happens
1006   // has the value -1, ie. INVALID_HANDLE_VALUE. It cannot fail.
1007   //
1008   hSelfProcess = GetCurrentProcess();
1009 
1010   hNmProcess = OpenProcess(PROCESS_DUP_HANDLE, FALSE, nmPid);
1011   if (NULL == hNmProcess) {
1012     dwError = GetLastError();
1013     ReportSvcCheckError(EVENTLOG_ERROR_TYPE, SERVICE_CATEGORY,
1014       dwError, L"OpenProcess");
1015     goto done;
1016   }
1017 
1018   GetModuleFileName(NULL, winutilsPath, sizeof(winutilsPath)/sizeof(WCHAR));
1019   dwError = GetLastError(); // Always check after GetModuleFileName for ERROR_INSSUFICIENT_BUFFER
1020   if (dwError) {
1021     ReportSvcCheckError(EVENTLOG_ERROR_TYPE, SERVICE_CATEGORY,
1022       dwError, L"GetModuleFileName");
1023     goto done;
1024   }
1025 
1026   // NB. We can call CreateProcess("wintuls","task create ...") or we can call
1027   // CreateProcess(NULL, "winutils task create"). Only the second form passes "task" as
1028   // argv[1], as expected by main. First form passes "task" as argv[0] and main fails.
1029 
1030   hr = StringCbPrintf(fullCmdLine, sizeof(fullCmdLine), L"\"%s\" task createAsUser %ls %ls %ls %ls",
1031     winutilsPath,
1032     request->jobName, request->user, request->pidFile, request->cmdLine);
1033   if (FAILED(hr)) {
1034     ReportSvcCheckError(EVENTLOG_ERROR_TYPE, SERVICE_CATEGORY,
1035       hr, L"StringCbPrintf:fullCmdLine");
1036     goto done;
1037   }
1038 
1039   LogDebugMessage(L"[%ls]: %ls %ls\n", request->cwd, winutilsPath, fullCmdLine);
1040 
1041   // stdin/stdout/stderr redirection is handled here
1042   // We create 3 anonymous named pipes.
1043   // Security attributes are required so that the handles can be inherited.
1044   // We assign one end of the pipe to the process (stdin gets a read end, stdout gets a write end)
1045   // We then duplicate the other end in the NM process, and we close our own handle
1046   // Finally we return the duplicate handle values to the NM
1047   // The NM will attach Java file dscriptors to the duplicated handles and
1048   // read/write them as ordinary Java InputStream/OutputStream objects
1049 
1050   si.dwFlags |= STARTF_USESTDHANDLES;
1051 
1052   saTaskStdInOutErr.nLength = sizeof(SECURITY_ATTRIBUTES);
1053   saTaskStdInOutErr.bInheritHandle = TRUE;
1054   saTaskStdInOutErr.lpSecurityDescriptor = NULL;
1055 
1056   if (!CreatePipe(&taskStdInRd, &taskStdInWr, &saTaskStdInOutErr, 0)) {
1057     dwError = GetLastError();
1058     goto done;
1059   }
1060   if (!SetHandleInformation(taskStdInWr, HANDLE_FLAG_INHERIT, FALSE)) {
1061     dwError = GetLastError();
1062     goto done;
1063   }
1064   si.hStdInput  = taskStdInRd;
1065 
1066   if (!CreatePipe(&taskStdOutRd, &taskStdOutWr, &saTaskStdInOutErr, 0)) {
1067     dwError = GetLastError();
1068     goto done;
1069   }
1070   if (!SetHandleInformation(taskStdOutRd, HANDLE_FLAG_INHERIT, FALSE)) {
1071     dwError = GetLastError();
1072     goto done;
1073   }
1074   si.hStdOutput  = taskStdOutWr;
1075 
1076   if (!CreatePipe(&taskStdErrRd, &taskStdErrWr, &saTaskStdInOutErr, 0)) {
1077     dwError = GetLastError();
1078     goto done;
1079   }
1080   if (!SetHandleInformation(taskStdErrRd, HANDLE_FLAG_INHERIT, FALSE)) {
1081     dwError = GetLastError();
1082     goto done;
1083   }
1084   si.hStdError  = taskStdErrWr;
1085 
1086   if (!CreateProcess(
1087     NULL,                     // lpApplicationName,
1088     fullCmdLine,              // lpCommandLine,
1089     NULL,                     // lpProcessAttributes,
1090     NULL,                     // lpThreadAttributes,
1091     TRUE,                     // bInheritHandles,
1092     CREATE_SUSPENDED,         // dwCreationFlags,
1093     NULL,                     // lpEnvironment,
1094     request->cwd,             // lpCurrentDirectory,
1095     &si,                      // lpStartupInfo
1096     &pi)) {                   // lpProcessInformation
1097 
1098     dwError = GetLastError();
1099     ReportSvcCheckError(EVENTLOG_ERROR_TYPE, SERVICE_CATEGORY,
1100       dwError, L"CreateProcess");
1101     goto done;
1102   }
1103 
1104   fMustCleanupProcess = TRUE;
1105 
1106   LogDebugMessage(L"CreateProcess: pid:%x\n", pi.dwProcessId);
1107 
1108   if (INVALID_HANDLE_VALUE != hJob) {
1109     if (!AssignProcessToJobObject(hJob, pi.hProcess)) {
1110       dwError = GetLastError();
1111       goto done;
1112     }
1113   }
1114 
1115   // Grant full access to the container user on the 'winutils task createAsUser ...' helper process
1116   dwError = AddNodeManagerAndUserACEsToObject(pi.hProcess, request->user, PROCESS_ALL_ACCESS);
1117   if (dwError) {
1118     LogDebugMessage(L"failed: AddNodeManagerAndUserACEsToObject\n");
1119     goto done;
1120   }
1121 
1122   if (!DuplicateHandle(hSelfProcess, pi.hProcess, hNmProcess,
1123     &hDuplicateProcess, 0, FALSE, DUPLICATE_SAME_ACCESS)) {
1124     dwError = GetLastError();
1125     LogDebugMessage(L"failed: pi.hProcess\n");
1126     goto done;
1127   }
1128 
1129   if (!DuplicateHandle(hSelfProcess, pi.hThread, hNmProcess,
1130     &hDuplicateThread, 0, FALSE, DUPLICATE_SAME_ACCESS)) {
1131     dwError = GetLastError();
1132     LogDebugMessage(L"failed: pi.hThread\n");
1133     goto done;
1134   }
1135 
1136   if (!DuplicateHandle(hSelfProcess, taskStdInWr, hNmProcess,
1137     &hDuplicateStdIn, 0, FALSE, DUPLICATE_SAME_ACCESS)) {
1138     dwError = GetLastError();
1139     LogDebugMessage(L"failed: taskStdInWr\n");
1140     goto done;
1141   }
1142 
1143   if (!DuplicateHandle(hSelfProcess, taskStdOutRd, hNmProcess,
1144     &hDuplicateStdOut, 0, FALSE, DUPLICATE_SAME_ACCESS)) {
1145     dwError = GetLastError();
1146     LogDebugMessage(L"failed: taskStdOutRd\n");
1147     goto done;
1148   }
1149 
1150   if (!DuplicateHandle(hSelfProcess, taskStdErrRd, hNmProcess,
1151     &hDuplicateStdErr, 0, FALSE, DUPLICATE_SAME_ACCESS)) {
1152     dwError = GetLastError();
1153     LogDebugMessage(L"failed: taskStdErrRd\n");
1154     goto done;
1155   }
1156 
1157   *response = (CREATE_PROCESS_RESPONSE*) MIDL_user_allocate(sizeof(CREATE_PROCESS_RESPONSE));
1158   if (NULL == *response) {
1159     dwError = ERROR_OUTOFMEMORY;
1160     LogDebugMessage(L"Failed to allocate CREATE_PROCESS_RESPONSE* response\n");
1161     goto done;
1162   }
1163 
1164   // We're now transfering ownership of the duplicated handles to the caller
1165   // If the RPC call fails *after* this point the handles are leaked inside the NM process
1166   // Note that there are no more API calls, only assignments. A failure could occur only if
1167   // foced (process kill) or hardware error (faulty memory, processort bit flip etc).
1168 
1169   // as MIDL has no 'HANDLE' type, the (LONG_PTR) is used instead
1170   (*response)->hProcess = (LONG_PTR)hDuplicateProcess;
1171   (*response)->hThread = (LONG_PTR)hDuplicateThread;
1172   (*response)->hStdIn = (LONG_PTR)hDuplicateStdIn;
1173   (*response)->hStdOut = (LONG_PTR)hDuplicateStdOut;
1174   (*response)->hStdErr = (LONG_PTR)hDuplicateStdErr;
1175 
1176   fMustCleanupProcess = FALSE;
1177 
1178 done:
1179 
1180   if (fMustCleanupProcess) {
1181     LogDebugMessage(L"Cleaning process: %d due to error:%d\n", pi.dwProcessId, dwError);
1182     TerminateProcess(pi.hProcess, EXIT_FAILURE);
1183 
1184     // cleanup the duplicate handles inside the NM.
1185 
1186     if (INVALID_HANDLE_VALUE != hDuplicateProcess) {
1187       DuplicateHandle(hNmProcess, hDuplicateProcess, NULL, NULL, 0, FALSE, DUPLICATE_CLOSE_SOURCE);
1188     }
1189     if (INVALID_HANDLE_VALUE != hDuplicateThread) {
1190       DuplicateHandle(hNmProcess, hDuplicateThread, NULL, NULL, 0, FALSE, DUPLICATE_CLOSE_SOURCE);
1191     }
1192     if (INVALID_HANDLE_VALUE != hDuplicateStdIn) {
1193       DuplicateHandle(hNmProcess, hDuplicateStdIn, NULL, NULL, 0, FALSE, DUPLICATE_CLOSE_SOURCE);
1194     }
1195     if (INVALID_HANDLE_VALUE != hDuplicateStdOut) {
1196       DuplicateHandle(hNmProcess, hDuplicateStdOut, NULL, NULL, 0, FALSE, DUPLICATE_CLOSE_SOURCE);
1197     }
1198     if (INVALID_HANDLE_VALUE != hDuplicateStdErr) {
1199       DuplicateHandle(hNmProcess, hDuplicateStdErr, NULL, NULL, 0, FALSE, DUPLICATE_CLOSE_SOURCE);
1200     }
1201   }
1202 
1203   if (INVALID_HANDLE_VALUE != hSelfProcess) CloseHandle(hSelfProcess);
1204   if (INVALID_HANDLE_VALUE != hNmProcess) CloseHandle(hNmProcess);
1205   if (INVALID_HANDLE_VALUE != taskStdInRd) CloseHandle(taskStdInRd);
1206   if (INVALID_HANDLE_VALUE != taskStdInWr) CloseHandle(taskStdInWr);
1207   if (INVALID_HANDLE_VALUE != taskStdOutRd) CloseHandle(taskStdOutRd);
1208   if (INVALID_HANDLE_VALUE != taskStdOutWr) CloseHandle(taskStdOutWr);
1209   if (INVALID_HANDLE_VALUE != taskStdErrRd) CloseHandle(taskStdErrRd);
1210   if (INVALID_HANDLE_VALUE != taskStdErrWr) CloseHandle(taskStdErrWr);
1211 
1212 
1213   // This is closing our own process/thread handles.
1214   // If the transfer was succesfull the NM has its own duplicates (if any)
1215   if (INVALID_HANDLE_VALUE != pi.hThread) CloseHandle(pi.hThread);
1216   if (INVALID_HANDLE_VALUE != pi.hProcess) CloseHandle(pi.hProcess);
1217 
1218   if (hJob) CloseHandle(hJob);
1219 
1220   return dwError;
1221 }
1222 
WinutilsCreateFile(handle_t IDL_handle,int nm_pid,CREATEFILE_REQUEST * request,CREATEFILE_RESPONSE ** response)1223 error_status_t WinutilsCreateFile(
1224   /* [in] */ handle_t IDL_handle,
1225   /* [in] */ int nm_pid,
1226   /* [in] */ CREATEFILE_REQUEST *request,
1227   /* [out] */ CREATEFILE_RESPONSE **response) {
1228 
1229   DWORD dwError = ERROR_SUCCESS;
1230 
1231   HANDLE hNmProcess = INVALID_HANDLE_VALUE,
1232     hFile = INVALID_HANDLE_VALUE,
1233     hDuplicateFile = INVALID_HANDLE_VALUE,
1234     hSelfProcess = GetCurrentProcess();
1235 
1236   SECURITY_ATTRIBUTES saFile;
1237 
1238   ZeroMemory( &saFile, sizeof(saFile));
1239 
1240   dwError = ValidateLocalPath(request->path);
1241   CHECK_SVC_STATUS_DONE(dwError,L"ValidateLocalPath request->path");
1242 
1243   saFile.nLength = sizeof(SECURITY_ATTRIBUTES);
1244   saFile.bInheritHandle = TRUE;
1245   saFile.lpSecurityDescriptor = NULL;
1246 
1247   hFile = CreateFile(
1248     request->path,
1249     request->desiredAccess,
1250     request->shareMode,
1251     &saFile,
1252     request->creationDisposition,
1253     request->flags,
1254     NULL); // hTemplate
1255   if (INVALID_HANDLE_VALUE == hFile) {
1256     dwError = GetLastError();
1257     goto done;
1258   }
1259 
1260   hNmProcess = OpenProcess(PROCESS_DUP_HANDLE, FALSE, nm_pid);
1261   if (NULL == hNmProcess) {
1262     dwError = GetLastError();
1263     goto done;
1264   }
1265 
1266   if (!DuplicateHandle(hSelfProcess, hFile,
1267     hNmProcess, &hDuplicateFile,
1268     0, FALSE, DUPLICATE_SAME_ACCESS)) {
1269     dwError = GetLastError();
1270     goto done;
1271   }
1272 
1273   *response = (CREATEFILE_RESPONSE*) MIDL_user_allocate(sizeof(CREATEFILE_RESPONSE));
1274   if (NULL == *response) {
1275     dwError = ERROR_OUTOFMEMORY;
1276     goto done;
1277   }
1278 
1279   // As MIDL has no 'HANDLE' type, (LONG_PTR) is used instead
1280   (*response)->hFile = (LONG_PTR)hDuplicateFile;
1281   hDuplicateFile = INVALID_HANDLE_VALUE;
1282 
1283 done:
1284 
1285   if (INVALID_HANDLE_VALUE != hFile) CloseHandle(hFile);
1286   if (INVALID_HANDLE_VALUE != hDuplicateFile) {
1287     DuplicateHandle(hNmProcess, hDuplicateFile, NULL, NULL, 0, FALSE, DUPLICATE_CLOSE_SOURCE);
1288   }
1289   if (INVALID_HANDLE_VALUE != hNmProcess) CloseHandle(hNmProcess);
1290 
1291   LogDebugMessage(L"WinutilsCreateFile: %s %d, %d, %d, %d: %d",
1292     request->path,
1293     request->desiredAccess,
1294     request->shareMode,
1295     request->creationDisposition,
1296     request->flags,
1297     dwError);
1298 
1299   return dwError;
1300 }
1301 
WinutilsKillTask(handle_t IDL_handle,KILLTASK_REQUEST * request)1302 error_status_t WinutilsKillTask(
1303     /* [in] */ handle_t IDL_handle,
1304     /* [in] */ KILLTASK_REQUEST *request) {
1305   DWORD dwError = ERROR_SUCCESS;
1306   WCHAR bufferName[MAX_PATH];
1307 
1308   dwError = GetSecureJobObjectName(request->taskName, MAX_PATH, bufferName);
1309   CHECK_SVC_STATUS_DONE(dwError, L"GetSecureJobObjectName");
1310 
1311   dwError = KillTask(bufferName);
1312 
1313   if (ERROR_ACCESS_DENIED == dwError) {
1314     // This process runs as LocalSystem with debug privilege enabled
1315     // The job has a security descriptor that explictly grants JOB_OBJECT_ALL_ACCESS to us
1316     // If we get ACCESS DENIED it means the job is being unwound
1317     dwError = ERROR_SUCCESS;
1318   }
1319 
1320 done:
1321   LogDebugMessage(L"WinutilsKillTask: %s :%d\n", bufferName, dwError);
1322   return dwError;
1323 }
1324 
1325 
WinutilsDeletePath(handle_t IDL_handle,DELETEPATH_REQUEST * request,DELETEPATH_RESPONSE ** response)1326 error_status_t WinutilsDeletePath(
1327   /* [in] */ handle_t IDL_handle,
1328   /* [in] */ DELETEPATH_REQUEST *request,
1329   /* [out] */ DELETEPATH_RESPONSE **response) {
1330 
1331   DWORD dwError = ERROR_SUCCESS;
1332   BOOL deleted = FALSE;
1333 
1334   dwError = ValidateLocalPath(request->path);
1335   CHECK_SVC_STATUS_DONE(dwError,L"ValidateLocalPath request->path");
1336 
1337   switch(request->type) {
1338   case PATH_IS_DIR:
1339     deleted = RemoveDirectory(request->path);
1340     if (!deleted) {
1341       LogDebugMessage(L"Error %d deleting directory %s\n", GetLastError(), request->path);
1342     }
1343     break;
1344   case PATH_IS_FILE:
1345     deleted = DeleteFile(request->path);
1346     if (!deleted) {
1347       LogDebugMessage(L"Error %d deleting file %s\n", GetLastError(), request->path);
1348     }
1349     break;
1350   default:
1351     dwError = ERROR_BAD_ARGUMENTS;
1352     CHECK_SVC_STATUS_DONE(dwError, L"request->operation");
1353   }
1354 
1355   *response = (DELETEPATH_RESPONSE*) MIDL_user_allocate(sizeof(DELETEPATH_RESPONSE));
1356   if (NULL == *response) {
1357     dwError = ERROR_OUTOFMEMORY;
1358     CHECK_SVC_STATUS_DONE(dwError, L"MIDL_user_allocate");
1359   }
1360 
1361   (*response)->deleted = deleted;
1362 
1363 done:
1364 
1365   LogDebugMessage(L"WinutilsDeletePath: %s %d: %d %d",
1366     request->path,
1367     request->type,
1368     deleted,
1369     dwError);
1370 
1371   return dwError;
1372 }
1373 
WinutilsMkDir(handle_t IDL_handle,MKDIR_REQUEST * request)1374 error_status_t WinutilsMkDir(
1375     /* [in] */ handle_t IDL_handle,
1376     /* [in] */ MKDIR_REQUEST *request) {
1377   DWORD dwError = ERROR_SUCCESS;
1378 
1379   dwError = ValidateLocalPath(request->filePath);
1380   CHECK_SVC_STATUS_DONE(dwError,L"ValidateLocalPath request->filePath");
1381 
1382   if (!CreateDirectory(request->filePath, NULL)) {
1383     dwError = GetLastError();
1384     CHECK_SVC_STATUS_DONE(dwError, L"CreateDirectory");
1385   }
1386 
1387 done:
1388   LogDebugMessage(L"WinutilsMkDir: %s :%d\n", request->filePath, dwError);
1389   return dwError;
1390 }
1391 
WinutilsChown(handle_t IDL_handle,CHOWN_REQUEST * request)1392 error_status_t WinutilsChown(
1393     /* [in] */ handle_t IDL_handle,
1394     /* [in] */ CHOWN_REQUEST *request) {
1395   DWORD dwError = ERROR_SUCCESS;
1396 
1397   dwError = ValidateLocalPath(request->filePath);
1398   CHECK_SVC_STATUS_DONE(dwError,L"ValidateLocalPath request->filePath");
1399 
1400   dwError = ChownImpl(request->ownerName, request->groupName, request->filePath);
1401   CHECK_SVC_STATUS_DONE(dwError, L"ChownImpl");
1402 
1403 done:
1404   LogDebugMessage(L"WinutilsChown: %s %s %s :%d\n",
1405     request->ownerName, request->groupName, request->filePath, dwError);
1406   return dwError;
1407 }
1408 
WinutilsChmod(handle_t IDL_handle,CHMOD_REQUEST * request)1409 error_status_t WinutilsChmod(
1410     /* [in] */ handle_t IDL_handle,
1411     /* [in] */ CHMOD_REQUEST *request) {
1412   DWORD dwError = ERROR_SUCCESS;
1413 
1414   dwError = ValidateLocalPath(request->filePath);
1415   CHECK_SVC_STATUS_DONE(dwError,L"ValidateLocalPath request->filePath");
1416 
1417   dwError = ChangeFileModeByMask(request->filePath, request->mode);
1418   CHECK_SVC_STATUS_DONE(dwError, L"ChangeFileModeByMask");
1419 
1420 done:
1421   LogDebugMessage(L"WinutilsChmod: %s %o :%d\n",
1422    request->filePath, request->mode, dwError);
1423   return dwError;
1424 }
1425 
WinutilsMoveFile(handle_t IDL_handle,MOVEFILE_REQUEST * request)1426 error_status_t WinutilsMoveFile(
1427     /* [in] */ handle_t IDL_handle,
1428     /* [in] */ MOVEFILE_REQUEST *request) {
1429   DWORD dwError = ERROR_SUCCESS;
1430   DWORD flags = 0;
1431 
1432   dwError = ValidateLocalPath(request->sourcePath);
1433   CHECK_SVC_STATUS_DONE(dwError,L"ValidateLocalPath request->sourcePath");
1434 
1435   dwError = ValidateLocalPath(request->destinationPath);
1436   CHECK_SVC_STATUS_DONE(dwError,L"ValidateLocalPath request->destinationPath");
1437 
1438   switch (request->operation) {
1439   case MOVE_FILE:
1440     flags |= MOVEFILE_COPY_ALLOWED;
1441     if (request->replaceExisting) flags |= MOVEFILE_REPLACE_EXISTING;
1442     if (!MoveFileEx(request->sourcePath, request->destinationPath, flags)) {
1443       dwError = GetLastError();
1444       CHECK_SVC_STATUS_DONE(dwError, L"MoveFileEx");
1445     }
1446     break;
1447   case COPY_FILE:
1448     if (!request->replaceExisting) flags |= COPY_FILE_FAIL_IF_EXISTS;
1449     if (!CopyFileEx(request->sourcePath, request->destinationPath,
1450           NULL, // lpProgressRoutine
1451           NULL, // lpData
1452           NULL, // pbCancel
1453           flags)) {
1454       dwError = GetLastError();
1455       CHECK_SVC_STATUS_DONE(dwError, L"CopyFileEx");
1456     }
1457     break;
1458   default:
1459     dwError = ERROR_BAD_ARGUMENTS;
1460     CHECK_SVC_STATUS_DONE(dwError, L"request->operation");
1461   }
1462 
1463 done:
1464   LogDebugMessage(L"WinutilsMoveFile: %d: %s %s :%d\n",
1465     request->operation, request->sourcePath, request->destinationPath, dwError);
1466   return dwError;
1467 }
1468 
1469 
1470 //----------------------------------------------------------------------------
1471 // Function: ServiceUsage
1472 //
1473 // Description:
1474 //  Prints the CLI arguments for service command.
1475 //
ServiceUsage()1476 void ServiceUsage()
1477 {
1478   fwprintf(stdout, L"\
1479     Usage: service\n\
1480     Starts the nodemanager Windows Secure Container Executor helper service.\n\
1481     The service must run as a high privileged account (LocalSystem)\n\
1482     and is used by the nodemanager WSCE to spawn secure containers on Windows.\n");
1483 }
1484 
1485 
1486