YARN-2198. Remove the need to run NodeManager as privileged account for Windows Secure Container Executor. Contributed by Remus Rusanu

(cherry picked from commit 3b12fd6cfb)
This commit is contained in:
Jian He 2014-10-22 15:57:46 -07:00
parent c8d0758656
commit 1c235a4448
37 changed files with 5555 additions and 787 deletions

3
.gitignore vendored
View File

@ -4,6 +4,9 @@
*.orig *.orig
*.rej *.rej
**/.keep **/.keep
*.sdf
*.suo
*.vcxproj.user
.idea .idea
.svn .svn
.classpath .classpath

View File

@ -33,6 +33,8 @@
<properties> <properties>
<hadoop.component>common</hadoop.component> <hadoop.component>common</hadoop.component>
<is.hadoop.component>true</is.hadoop.component> <is.hadoop.component>true</is.hadoop.component>
<wsce.config.dir>../etc/hadoop</wsce.config.dir>
<wsce.config.file>wsce-site.xml</wsce.config.file>
</properties> </properties>
<dependencies> <dependencies>
@ -735,6 +737,9 @@
<argument>/nologo</argument> <argument>/nologo</argument>
<argument>/p:Configuration=Release</argument> <argument>/p:Configuration=Release</argument>
<argument>/p:OutDir=${project.build.directory}/bin/</argument> <argument>/p:OutDir=${project.build.directory}/bin/</argument>
<argument>/p:IntermediateOutputPath=${project.build.directory}/winutils/</argument>
<argument>/p:WsceConfigDir=${wsce.config.dir}</argument>
<argument>/p:WsceConfigFile=${wsce.config.file}</argument>
</arguments> </arguments>
</configuration> </configuration>
</execution> </execution>

View File

@ -1186,6 +1186,11 @@ public static String[] list(File dir) throws IOException {
return fileNames; return fileNames;
} }
public static String[] createJarWithClassPath(String inputClassPath, Path pwd,
Map<String, String> callerEnv) throws IOException {
return createJarWithClassPath(inputClassPath, pwd, pwd, callerEnv);
}
/** /**
* Create a jar file at the given path, containing a manifest with a classpath * Create a jar file at the given path, containing a manifest with a classpath
* that references all specified entries. * that references all specified entries.
@ -1210,6 +1215,7 @@ public static String[] list(File dir) throws IOException {
* *
* @param inputClassPath String input classpath to bundle into the jar manifest * @param inputClassPath String input classpath to bundle into the jar manifest
* @param pwd Path to working directory to save jar * @param pwd Path to working directory to save jar
* @param targetDir path to where the jar execution will have its working dir
* @param callerEnv Map<String, String> caller's environment variables to use * @param callerEnv Map<String, String> caller's environment variables to use
* for expansion * for expansion
* @return String[] with absolute path to new jar in position 0 and * @return String[] with absolute path to new jar in position 0 and
@ -1217,6 +1223,7 @@ public static String[] list(File dir) throws IOException {
* @throws IOException if there is an I/O error while writing the jar file * @throws IOException if there is an I/O error while writing the jar file
*/ */
public static String[] createJarWithClassPath(String inputClassPath, Path pwd, public static String[] createJarWithClassPath(String inputClassPath, Path pwd,
Path targetDir,
Map<String, String> callerEnv) throws IOException { Map<String, String> callerEnv) throws IOException {
// Replace environment variables, case-insensitive on Windows // Replace environment variables, case-insensitive on Windows
@SuppressWarnings("unchecked") @SuppressWarnings("unchecked")
@ -1265,7 +1272,7 @@ public static String[] createJarWithClassPath(String inputClassPath, Path pwd,
// Append just this entry // Append just this entry
File fileCpEntry = null; File fileCpEntry = null;
if(!new Path(classPathEntry).isAbsolute()) { if(!new Path(classPathEntry).isAbsolute()) {
fileCpEntry = new File(workingDir, classPathEntry); fileCpEntry = new File(targetDir.toString(), classPathEntry);
} }
else { else {
fileCpEntry = new File(classPathEntry); fileCpEntry = new File(classPathEntry);

View File

@ -268,7 +268,12 @@ private FSDataOutputStream create(Path f, boolean overwrite,
throw new IOException("Mkdirs failed to create " + parent.toString()); throw new IOException("Mkdirs failed to create " + parent.toString());
} }
return new FSDataOutputStream(new BufferedOutputStream( return new FSDataOutputStream(new BufferedOutputStream(
new LocalFSFileOutputStream(f, false), bufferSize), statistics); createOutputStream(f, false), bufferSize), statistics);
}
protected OutputStream createOutputStream(Path f, boolean append)
throws IOException {
return new LocalFSFileOutputStream(f, append);
} }
@Override @Override
@ -401,6 +406,10 @@ public FileStatus[] listStatus(Path f) throws IOException {
return Arrays.copyOf(results, j); return Arrays.copyOf(results, j);
} }
protected boolean mkOneDir(File p2f) throws IOException {
return p2f.mkdir();
}
/** /**
* Creates the specified directory hierarchy. Does not * Creates the specified directory hierarchy. Does not
* treat existence as an error. * treat existence as an error.
@ -412,8 +421,9 @@ public boolean mkdirs(Path f) throws IOException {
} }
Path parent = f.getParent(); Path parent = f.getParent();
File p2f = pathToFile(f); File p2f = pathToFile(f);
File parent2f = null;
if(parent != null) { if(parent != null) {
File parent2f = pathToFile(parent); parent2f = pathToFile(parent);
if(parent2f != null && parent2f.exists() && !parent2f.isDirectory()) { if(parent2f != null && parent2f.exists() && !parent2f.isDirectory()) {
throw new ParentNotDirectoryException("Parent path is not a directory: " throw new ParentNotDirectoryException("Parent path is not a directory: "
+ parent); + parent);
@ -423,8 +433,8 @@ public boolean mkdirs(Path f) throws IOException {
throw new FileNotFoundException("Destination exists" + throw new FileNotFoundException("Destination exists" +
" and is not a directory: " + p2f.getCanonicalPath()); " and is not a directory: " + p2f.getCanonicalPath());
} }
return (parent == null || mkdirs(parent)) && return (parent == null || parent2f.exists() || mkdirs(parent)) &&
(p2f.mkdir() || p2f.isDirectory()); (mkOneDir(p2f) || p2f.isDirectory());
} }
@Override @Override

View File

@ -22,6 +22,8 @@
import java.io.FileInputStream; import java.io.FileInputStream;
import java.io.FileOutputStream; import java.io.FileOutputStream;
import java.io.IOException; import java.io.IOException;
import java.io.InputStream;
import java.io.OutputStream;
import java.io.RandomAccessFile; import java.io.RandomAccessFile;
import java.lang.reflect.Field; import java.lang.reflect.Field;
import java.nio.ByteBuffer; import java.nio.ByteBuffer;
@ -35,6 +37,7 @@
import org.apache.hadoop.conf.Configuration; import org.apache.hadoop.conf.Configuration;
import org.apache.hadoop.fs.CommonConfigurationKeys; import org.apache.hadoop.fs.CommonConfigurationKeys;
import org.apache.hadoop.fs.HardLink; import org.apache.hadoop.fs.HardLink;
import org.apache.hadoop.fs.Path;
import org.apache.hadoop.io.SecureIOUtils.AlreadyExistsException; import org.apache.hadoop.io.SecureIOUtils.AlreadyExistsException;
import org.apache.hadoop.util.NativeCodeLoader; import org.apache.hadoop.util.NativeCodeLoader;
import org.apache.hadoop.util.Shell; import org.apache.hadoop.util.Shell;
@ -505,6 +508,8 @@ public static class Windows {
public static final long FILE_CURRENT = 1; public static final long FILE_CURRENT = 1;
public static final long FILE_END = 2; public static final long FILE_END = 2;
public static final long FILE_ATTRIBUTE_NORMAL = 0x00000080L;
/** Wrapper around CreateFile() on Windows */ /** Wrapper around CreateFile() on Windows */
public static native FileDescriptor createFile(String path, public static native FileDescriptor createFile(String path,
long desiredAccess, long shareMode, long creationDisposition) long desiredAccess, long shareMode, long creationDisposition)

View File

@ -649,6 +649,18 @@ public String toString() {
} }
} }
public interface CommandExecutor {
void execute() throws IOException;
int getExitCode() throws IOException;
String getOutput() throws IOException;
void close();
}
/** /**
* A simple shell command executor. * A simple shell command executor.
* *
@ -657,7 +669,8 @@ public String toString() {
* directory and the environment remains unchanged. The output of the command * directory and the environment remains unchanged. The output of the command
* is stored as-is and is expected to be small. * is stored as-is and is expected to be small.
*/ */
public static class ShellCommandExecutor extends Shell { public static class ShellCommandExecutor extends Shell
implements CommandExecutor {
private String[] command; private String[] command;
private StringBuffer output; private StringBuffer output;
@ -749,6 +762,10 @@ public String toString() {
} }
return builder.toString(); return builder.toString();
} }
@Override
public void close() {
}
} }
/** /**

View File

@ -107,6 +107,9 @@
<AdditionalOptions Condition="'$(SnappyEnabled)' == 'true'">/D HADOOP_SNAPPY_LIBRARY=L\"snappy.dll\"</AdditionalOptions> <AdditionalOptions Condition="'$(SnappyEnabled)' == 'true'">/D HADOOP_SNAPPY_LIBRARY=L\"snappy.dll\"</AdditionalOptions>
</ClCompile> </ClCompile>
<ClCompile Include="src\org\apache\hadoop\util\NativeCrc32.c" /> <ClCompile Include="src\org\apache\hadoop\util\NativeCrc32.c" />
<ClCompile Include="src\org\apache\hadoop\yarn\server\nodemanager\windows_secure_container_executor.c">
<AdditionalIncludeDirectories>src\org\apache\hadoop\io\nativeio;%(AdditionalIncludeDirectories)</AdditionalIncludeDirectories>
</ClCompile>
</ItemGroup> </ItemGroup>
<ItemGroup> <ItemGroup>
<ClInclude Include="..\src\org\apache\hadoop\util\crc32c_tables.h" /> <ClInclude Include="..\src\org\apache\hadoop\util\crc32c_tables.h" />
@ -120,6 +123,7 @@
<ClInclude Include="src\org\apache\hadoop\util\crc32c_tables.h" /> <ClInclude Include="src\org\apache\hadoop\util\crc32c_tables.h" />
<ClInclude Include="src\org\apache\hadoop\util\crc32_zlib_polynomial_tables.h" /> <ClInclude Include="src\org\apache\hadoop\util\crc32_zlib_polynomial_tables.h" />
<ClInclude Include="src\org_apache_hadoop.h" /> <ClInclude Include="src\org_apache_hadoop.h" />
<ClInclude Include="src\org\apache\hadoop\yarn\server\nodemanager\windows_secure_container_executor.h" />
</ItemGroup> </ItemGroup>
<Import Project="$(VCTargetsPath)\Microsoft.Cpp.targets" /> <Import Project="$(VCTargetsPath)\Microsoft.Cpp.targets" />
<ImportGroup Label="ExtensionTargets"> <ImportGroup Label="ExtensionTargets">

View File

@ -71,8 +71,13 @@ static jmethodID nioe_ctor;
// Please see HADOOP-7156 for details. // Please see HADOOP-7156 for details.
jobject pw_lock_object; jobject pw_lock_object;
/*
* Throw a java.IO.IOException, generating the message from errno.
* NB. this is also used form windows_secure_container_executor.c
*/
extern void throw_ioe(JNIEnv* env, int errnum);
// Internal functions // Internal functions
static void throw_ioe(JNIEnv* env, int errnum);
#ifdef UNIX #ifdef UNIX
static ssize_t get_pw_buflen(); static ssize_t get_pw_buflen();
#endif #endif
@ -802,7 +807,7 @@ cleanup:
/* /*
* Throw a java.IO.IOException, generating the message from errno. * Throw a java.IO.IOException, generating the message from errno.
*/ */
static void throw_ioe(JNIEnv* env, int errnum) void throw_ioe(JNIEnv* env, int errnum)
{ {
#ifdef UNIX #ifdef UNIX
char message[80]; char message[80];

View File

@ -0,0 +1,569 @@
/**
* 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 <jni.h>
#include "org_apache_hadoop.h"
#include "windows_secure_container_executor.h"
#include "winutils.h"
#include "file_descriptor.h"
// class of org.apache.hadoop.yarn.server.nodemanager.WindowsSecureContainerExecutor.Native.WinutilsProcessStub
static jclass wps_class = NULL;
/*
* private static native void initWsceNative();
*
* We rely on this function rather than lazy initialization because
* the lazy approach may have a race if multiple callers try to
* init at the same time.
*/
JNIEXPORT void JNICALL
Java_org_apache_hadoop_yarn_server_nodemanager_WindowsSecureContainerExecutor_00024Native_initWsceNative(
JNIEnv *env, jclass clazz) {
#ifdef WINDOWS
winutils_process_stub_init(env);
PASS_EXCEPTIONS_GOTO(env, error);
#endif
return;
error:
// these are all idempodent and safe to call even if the
// class wasn't initted yet
#ifdef WINDOWS
winutils_process_stub_deinit(env);
#endif
}
static jmethodID wps_constructor = NULL;
static jfieldID wps_hProcess = NULL;
static jfieldID wps_hThread = NULL;
static jfieldID wps_disposed = NULL;
extern void throw_ioe(JNIEnv* env, int errnum);
void winutils_process_stub_init(JNIEnv *env) {
if (wps_class != NULL) return; // already initted
wps_class = (*env)->FindClass(env, WINUTILS_PROCESS_STUB_CLASS);
PASS_EXCEPTIONS(env);
wps_class = (*env)->NewGlobalRef(env, wps_class);
PASS_EXCEPTIONS(env);
wps_hProcess = (*env)->GetFieldID(env, wps_class, "hProcess", "J");
PASS_EXCEPTIONS(env);
wps_hThread = (*env)->GetFieldID(env, wps_class, "hThread", "J");
PASS_EXCEPTIONS(env);
wps_disposed = (*env)->GetFieldID(env, wps_class, "disposed", "Z");
PASS_EXCEPTIONS(env);
wps_constructor = (*env)->GetMethodID(env, wps_class, "<init>", "(JJJJJ)V");
PASS_EXCEPTIONS(env);
LogDebugMessage(L"winutils_process_stub_init\n");
}
void winutils_process_stub_deinit(JNIEnv *env) {
if (wps_class != NULL) {
(*env)->DeleteGlobalRef(env, wps_class);
wps_class = NULL;
}
wps_hProcess = NULL;
wps_hThread = NULL;
wps_disposed = NULL;
wps_constructor = NULL;
LogDebugMessage(L"winutils_process_stub_deinit\n");
}
jobject winutils_process_stub_create(JNIEnv *env,
jlong hProcess, jlong hThread, jlong hStdIn, jlong hStdOut, jlong hStdErr) {
jobject obj = (*env)->NewObject(env, wps_class, wps_constructor,
hProcess, hThread, hStdIn, hStdOut, hStdErr);
PASS_EXCEPTIONS_RET(env, NULL);
LogDebugMessage(L"winutils_process_stub_create: %p\n", obj);
return obj;
}
/*
* Class: org.apache.hadoop.yarn.server.nodemanager.WindowsSecureContainerExecutor$Native
* Method: createTaskAsUser
* Signature: (Ljava/lang/String;Ljava/lang/String;Ljava/lang/String;Ljava/lang/String)Lorg/apache/hadoop/io/nativeio/NativeIO$WinutilsProcessStub
*/
JNIEXPORT jobject JNICALL
Java_org_apache_hadoop_yarn_server_nodemanager_WindowsSecureContainerExecutor_00024Native_createTaskAsUser0(JNIEnv* env,
jclass clazz, jstring jcwd, jstring jjobName, jstring juser, jstring jpidFile, jstring jcmdLine) {
#ifdef UNIX
THROW(env, "java/io/IOException",
"The function createTaskAsUser is not supported on Unix");
return NULL;
#endif
#ifdef WINDOWS
LPCWSTR cwd = NULL, jobName = NULL,
user = NULL, pidFile = NULL, cmdLine = NULL;
DWORD dwError = ERROR_SUCCESS;
HANDLE hProcess = INVALID_HANDLE_VALUE,
hThread = INVALID_HANDLE_VALUE,
hStdIn = INVALID_HANDLE_VALUE,
hStdOut = INVALID_HANDLE_VALUE,
hStdErr = INVALID_HANDLE_VALUE;
jobject ret = NULL;
cwd = (LPCWSTR) (*env)->GetStringChars(env, jcwd, NULL);
if (!cwd) goto done; // exception was thrown
jobName = (LPCWSTR) (*env)->GetStringChars(env, jjobName, NULL);
if (!jobName) goto done; // exception was thrown
user = (LPCWSTR) (*env)->GetStringChars(env, juser, NULL);
if (!user) goto done; // exception was thrown
pidFile = (LPCWSTR) (*env)->GetStringChars(env, jpidFile, NULL);
if (!pidFile) goto done; // exception was thrown
cmdLine = (LPCWSTR) (*env)->GetStringChars(env, jcmdLine, NULL);
if (!cmdLine) goto done; // exception was thrown
LogDebugMessage(L"createTaskAsUser: jcwd:%s job:%s juser:%s pid:%s cmd:%s\n",
cwd, jobName, user, pidFile, cmdLine);
dwError = RpcCall_TaskCreateAsUser(cwd, jobName, user, pidFile, cmdLine,
&hProcess, &hThread, &hStdIn, &hStdOut, &hStdErr);
if (ERROR_SUCCESS == dwError) {
ret = winutils_process_stub_create(env, (jlong) hProcess, (jlong) hThread,
(jlong) hStdIn, (jlong) hStdOut, (jlong) hStdErr);
if (NULL == ret) {
TerminateProcess(hProcess, EXIT_FAILURE);
CloseHandle(hThread);
CloseHandle(hProcess);
CloseHandle(hStdIn);
CloseHandle(hStdOut);
CloseHandle(hStdErr);
}
}
if (dwError != ERROR_SUCCESS) {
throw_ioe (env, dwError);
}
done:
if (cwd) (*env)->ReleaseStringChars(env, jcwd, cwd);
if (jobName) (*env)->ReleaseStringChars(env, jjobName, jobName);
if (user) (*env)->ReleaseStringChars(env, juser, user);
if (pidFile) (*env)->ReleaseStringChars(env, jpidFile, pidFile);
if (cmdLine) (*env)->ReleaseStringChars(env, jcmdLine, cmdLine);
return ret;
#endif
}
/*
* Class: org.apache.hadoop.yarn.server.nodemanager.WindowsSecureContainerExecutor$Native$Elevated
* Method: elevatedKillTaskImpl
* Signature: (Ljava/lang/String;Ljava/lang/String;Ljava/lang/String;)V
*/
JNIEXPORT void JNICALL
Java_org_apache_hadoop_yarn_server_nodemanager_WindowsSecureContainerExecutor_00024Native_00024Elevated_elevatedKillTaskImpl(JNIEnv* env,
jclass clazz, jstring jtask_name) {
#ifdef UNIX
THROW(env, "java/io/IOException",
"The function elevatedSetOwner0 is not supported on Unix");
return;
#endif
#ifdef WINDOWS
LPCWSTR task_name = NULL;
DWORD dwError;
task_name = (LPCWSTR) (*env)->GetStringChars(env, jtask_name, NULL);
if (!task_name) goto done; // exception was thrown
dwError = RpcCall_WinutilsKillTask(task_name);
if (dwError != ERROR_SUCCESS) {
throw_ioe (env, dwError);
}
done:
if (task_name) (*env)->ReleaseStringChars(env, jtask_name, task_name);
#endif
}
/*
* Class: org.apache.hadoop.yarn.server.nodemanager.WindowsSecureContainerExecutor$Native$Elevated
* Method: elevatedChownImpl
* Signature: (Ljava/lang/String;Ljava/lang/String;Ljava/lang/String;)V
*/
JNIEXPORT void JNICALL
Java_org_apache_hadoop_yarn_server_nodemanager_WindowsSecureContainerExecutor_00024Native_00024Elevated_elevatedChownImpl(JNIEnv* env,
jclass clazz, jstring jpath, jstring juser, jstring jgroup) {
#ifdef UNIX
THROW(env, "java/io/IOException",
"The function elevatedSetOwner0 is not supported on Unix");
return;
#endif
#ifdef WINDOWS
LPCWSTR path = NULL, user = NULL, group = NULL;
DWORD dwError;
path = (LPCWSTR) (*env)->GetStringChars(env, jpath, NULL);
if (!path) goto done; // exception was thrown
if (juser) {
user = (LPCWSTR) (*env)->GetStringChars(env, juser, NULL);
if (!user) goto done; // exception was thrown
}
if (jgroup) {
group = (LPCWSTR) (*env)->GetStringChars(env, jgroup, NULL);
if (!group) goto done; // exception was thrown
}
dwError = RpcCall_WinutilsChown(path, user, group);
if (dwError != ERROR_SUCCESS) {
throw_ioe (env, dwError);
}
done:
if (path) (*env)->ReleaseStringChars(env, jpath, path);
if (user) (*env)->ReleaseStringChars(env, juser, user);
if (group) (*env)->ReleaseStringChars(env, jgroup, group);
#endif
}
/*
* Class: org.apache.hadoop.yarn.server.nodemanager.WindowsSecureContainerExecutor$Native$Elevated
* Method: elevatedMkDirImpl
* Signature: (Ljava/lang/String;)V
*/
JNIEXPORT void JNICALL
Java_org_apache_hadoop_yarn_server_nodemanager_WindowsSecureContainerExecutor_00024Native_00024Elevated_elevatedMkDirImpl(JNIEnv* env,
jclass clazz, jstring jpath) {
#ifdef UNIX
THROW(env, "java/io/IOException",
"The function elevatedMkDirImpl is not supported on Unix");
return;
#endif
#ifdef WINDOWS
LPCWSTR path = NULL, user = NULL, group = NULL;
DWORD dwError;
path = (LPCWSTR) (*env)->GetStringChars(env, jpath, NULL);
if (!path) goto done; // exception was thrown
dwError = RpcCall_WinutilsMkDir(path);
if (dwError != ERROR_SUCCESS) {
throw_ioe (env, dwError);
}
done:
if (path) (*env)->ReleaseStringChars(env, jpath, path);
#endif
}
/*
* Class: org.apache.hadoop.yarn.server.nodemanager.WindowsSecureContainerExecutor$Native$Elevated
* Method: elevatedChmodImpl
* Signature: (Ljava/lang/String;I)V
*/
JNIEXPORT void JNICALL
Java_org_apache_hadoop_yarn_server_nodemanager_WindowsSecureContainerExecutor_00024Native_00024Elevated_elevatedChmodImpl(JNIEnv* env,
jclass clazz, jstring jpath, jint jmode) {
#ifdef UNIX
THROW(env, "java/io/IOException",
"The function elevatedChmodImpl is not supported on Unix");
return;
#endif
#ifdef WINDOWS
LPCWSTR path = NULL;
DWORD dwError;
path = (LPCWSTR) (*env)->GetStringChars(env, jpath, NULL);
if (!path) goto done; // exception was thrown
dwError = RpcCall_WinutilsChmod(path, (int) jmode);
if (dwError != ERROR_SUCCESS) {
throw_ioe (env, dwError);
}
done:
if (path) (*env)->ReleaseStringChars(env, jpath, path);
#endif
}
/*
* Class: org.apache.hadoop.yarn.server.nodemanager.WindowsSecureContainerExecutor$Native$Elevated
* Method: elevatedCopyImpl
* Signature: (I;Ljava/lang/String;Ljava/lang/String;Ljava/lang/String;Z)V
*/
JNIEXPORT void JNICALL
Java_org_apache_hadoop_yarn_server_nodemanager_WindowsSecureContainerExecutor_00024Native_00024Elevated_elevatedCopyImpl(JNIEnv* env,
jclass clazz, jint joperation, jstring jsourcePath, jstring jdestinationPath, jboolean replaceExisting) {
#ifdef UNIX
THROW(env, "java/io/IOException",
"The function elevatedCopyImpl is not supported on Unix");
return;
#endif
#ifdef WINDOWS
LPCWSTR sourcePath = NULL, destinationPath = NULL;
DWORD dwError;
sourcePath = (LPCWSTR) (*env)->GetStringChars(env, jsourcePath, NULL);
if (!sourcePath) goto done; // exception was thrown
destinationPath = (LPCWSTR) (*env)->GetStringChars(env, jdestinationPath, NULL);
if (!destinationPath) goto done; // exception was thrown
dwError = RpcCall_WinutilsMoveFile((INT) joperation, sourcePath, destinationPath, (BOOL) replaceExisting);
if (dwError != ERROR_SUCCESS) {
throw_ioe (env, dwError);
}
done:
if (sourcePath) (*env)->ReleaseStringChars(env, jsourcePath, sourcePath);
if (destinationPath) (*env)->ReleaseStringChars(env, jdestinationPath, destinationPath);
#endif
}
/*
* Class: org.apache.hadoop.yarn.server.nodemanager.WindowsSecureContainerExecutor$Native$Elevated
* Method: elevatedCreateImpl
* Signature: (Ljava/lang/String;J;J;J;J)J
*/
JNIEXPORT jlong JNICALL
Java_org_apache_hadoop_yarn_server_nodemanager_WindowsSecureContainerExecutor_00024Native_00024Elevated_elevatedCreateImpl(JNIEnv* env,
jclass clazz, jstring jpath, jlong jdesired_access, jlong jshare_mode, jlong jcreation_disposition, jlong jflags) {
#ifdef UNIX
THROW(env, "java/io/IOException",
"The function elevatedCreateImpl is not supported on Unix");
return 0;
#endif
#ifdef WINDOWS
LPCWSTR path = NULL;
DWORD dwError;
HANDLE hFile = INVALID_HANDLE_VALUE;
path = (LPCWSTR) (*env)->GetStringChars(env, jpath, NULL);
if (!path) goto done; // exception was thrown
dwError = RpcCall_WinutilsCreateFile(path,
(DWORD) jdesired_access, (DWORD) jshare_mode, (DWORD) jcreation_disposition, (DWORD) jflags,
&hFile);
if (dwError != ERROR_SUCCESS) {
throw_ioe (env, dwError);
}
done:
if (path) (*env)->ReleaseStringChars(env, jpath, path);
return hFile;
#endif
}
/*
* Class: org.apache.hadoop.yarn.server.nodemanager.WindowsSecureContainerExecutor$Native$Elevated
* Method: elevatedDeletePathImpl
* Signature: (Ljava/lang/String;Z)Z
*/
JNIEXPORT jboolean JNICALL
Java_org_apache_hadoop_yarn_server_nodemanager_WindowsSecureContainerExecutor_00024Native_00024Elevated_elevatedDeletePathImpl(JNIEnv* env,
jclass clazz, jstring jpath, jboolean jIsDir) {
#ifdef UNIX
THROW(env, "java/io/IOException",
"The function elevatedDeleteFileImpl is not supported on Unix");
return JNI_FALSE;
#endif
#ifdef WINDOWS
LPCWSTR path = NULL;
DWORD dwError;
BOOL deleted = FALSE;
path = (LPCWSTR) (*env)->GetStringChars(env, jpath, NULL);
if (!path) goto done; // exception was thrown
dwError = RpcCall_WinutilsDeletePath(path, (BOOL) jIsDir, &deleted);
if (dwError != ERROR_SUCCESS) {
throw_ioe (env, dwError);
}
done:
if (path) (*env)->ReleaseStringChars(env, jpath, path);
return (jboolean) deleted;
#endif
}
/*
* native void destroy();
*
* The "00024" in the function name is an artifact of how JNI encodes
* special characters. U+0024 is '$'.
*/
JNIEXPORT void JNICALL
Java_org_apache_hadoop_yarn_server_nodemanager_WindowsSecureContainerExecutor_00024Native_00024WinutilsProcessStub_destroy(
JNIEnv *env, jobject objSelf) {
HANDLE hProcess = (HANDLE)(*env)->GetLongField(env, objSelf, wps_hProcess);
LogDebugMessage(L"TerminateProcess: %x\n", hProcess);
TerminateProcess(hProcess, EXIT_FAILURE);
}
/*
* native void waitFor();
*
* The "00024" in the function name is an artifact of how JNI encodes
* special characters. U+0024 is '$'.
*/
JNIEXPORT void JNICALL
Java_org_apache_hadoop_yarn_server_nodemanager_WindowsSecureContainerExecutor_00024Native_00024WinutilsProcessStub_waitFor(
JNIEnv *env, jobject objSelf) {
HANDLE hProcess = (HANDLE)(*env)->GetLongField(env, objSelf, wps_hProcess);
LogDebugMessage(L"WaitForSingleObject: %x\n", hProcess);
WaitForSingleObject(hProcess, INFINITE);
}
/*
* native void resume();
*
* The "00024" in the function name is an artifact of how JNI encodes
* special characters. U+0024 is '$'.
*/
JNIEXPORT void JNICALL
Java_org_apache_hadoop_yarn_server_nodemanager_WindowsSecureContainerExecutor_00024Native_00024WinutilsProcessStub_resume(
JNIEnv *env, jobject objSelf) {
DWORD dwError;
HANDLE hThread = (HANDLE)(*env)->GetLongField(env, objSelf, wps_hThread);
if (-1 == ResumeThread(hThread)) {
dwError = GetLastError();
LogDebugMessage(L"ResumeThread: %x error:%d\n", hThread, dwError);
throw_ioe(env, dwError);
}
}
/*
* native int exitValue();
*
* The "00024" in the function name is an artifact of how JNI encodes
* special characters. U+0024 is '$'.
*/
JNIEXPORT jint JNICALL
Java_org_apache_hadoop_yarn_server_nodemanager_WindowsSecureContainerExecutor_00024Native_00024WinutilsProcessStub_exitValue(
JNIEnv *env, jobject objSelf) {
DWORD exitCode;
DWORD dwError;
HANDLE hProcess = (HANDLE)(*env)->GetLongField(env, objSelf, wps_hProcess);
if (!GetExitCodeProcess(hProcess, &exitCode)) {
dwError = GetLastError();
throw_ioe(env, dwError);
return dwError; // exception was thrown, return value doesn't really matter
}
LogDebugMessage(L"GetExitCodeProcess: %x :%d\n", hProcess, exitCode);
return exitCode;
}
/*
* native void dispose();
*
* The "00024" in the function name is an artifact of how JNI encodes
* special characters. U+0024 is '$'.
*/
JNIEXPORT void JNICALL
Java_org_apache_hadoop_yarn_server_nodemanager_WindowsSecureContainerExecutor_00024Native_00024WinutilsProcessStub_dispose(
JNIEnv *env, jobject objSelf) {
HANDLE hProcess = INVALID_HANDLE_VALUE,
hThread = INVALID_HANDLE_VALUE;
jboolean disposed = (*env)->GetBooleanField(env, objSelf, wps_disposed);
if (JNI_TRUE != disposed) {
hProcess = (HANDLE)(*env)->GetLongField(env, objSelf, wps_hProcess);
hThread = (HANDLE)(*env)->GetLongField(env, objSelf, wps_hThread);
CloseHandle(hProcess);
CloseHandle(hThread);
(*env)->SetBooleanField(env, objSelf, wps_disposed, JNI_TRUE);
LogDebugMessage(L"disposed: %p\n", objSelf);
}
}
/*
* native static FileDescriptor getFileDescriptorFromHandle(long handle);
*
* The "00024" in the function name is an artifact of how JNI encodes
* special characters. U+0024 is '$'.
*/
JNIEXPORT jobject JNICALL
Java_org_apache_hadoop_yarn_server_nodemanager_WindowsSecureContainerExecutor_00024Native_00024WinutilsProcessStub_getFileDescriptorFromHandle(
JNIEnv *env, jclass klass, jlong handle) {
LogDebugMessage(L"getFileDescriptorFromHandle: %x\n", handle);
return fd_create(env, (long) handle);
}

View File

@ -0,0 +1,27 @@
/*
* 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.
*/
#pragma once
#define WINUTILS_PROCESS_STUB_CLASS "org/apache/hadoop/yarn/server/nodemanager/WindowsSecureContainerExecutor$Native$WinutilsProcessStub"
void winutils_process_stub_init(JNIEnv *env);
void winutils_process_stub_deinit(JNIEnv *env);
jobject winutils_process_stub_create(JNIEnv *env,
jlong hProcess, jlong hThread, jlong hStdIn, jlong hStdOut, jlong hStdErr);

View File

@ -17,93 +17,6 @@
#include "winutils.h" #include "winutils.h"
//----------------------------------------------------------------------------
// 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.
//
static 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: Chown // Function: Chown
// //
@ -130,9 +43,6 @@ int Chown(__in int argc, __in_ecount(argc) wchar_t *argv[])
LPWSTR groupName = NULL; LPWSTR groupName = NULL;
size_t groupNameLen = 0; size_t groupNameLen = 0;
PSID pNewOwnerSid = NULL;
PSID pNewGroupSid = NULL;
DWORD dwRtnCode = 0; DWORD dwRtnCode = 0;
int ret = EXIT_FAILURE; int ret = EXIT_FAILURE;
@ -210,38 +120,8 @@ int Chown(__in int argc, __in_ecount(argc) wchar_t *argv[])
goto ChownEnd; goto ChownEnd;
} }
if (userName != NULL) dwRtnCode = ChownImpl(userName, groupName, pathName);
{ if (dwRtnCode) {
dwRtnCode = GetSidFromAcctNameW(userName, &pNewOwnerSid);
if (dwRtnCode != ERROR_SUCCESS)
{
ReportErrorCode(L"GetSidFromAcctName", dwRtnCode);
fwprintf(stderr, L"Invalid user name: %s\n", userName);
goto ChownEnd;
}
}
if (groupName != NULL)
{
dwRtnCode = GetSidFromAcctNameW(groupName, &pNewGroupSid);
if (dwRtnCode != ERROR_SUCCESS)
{
ReportErrorCode(L"GetSidFromAcctName", dwRtnCode);
fwprintf(stderr, L"Invalid group name: %s\n", groupName);
goto ChownEnd;
}
}
if (wcslen(pathName) == 0 || wcsspn(pathName, L"/?|><:*\"") != 0)
{
fwprintf(stderr, L"Incorrect file name format: %s\n", pathName);
goto ChownEnd;
}
dwRtnCode = ChangeFileOwnerBySid(pathName, pNewOwnerSid, pNewGroupSid);
if (dwRtnCode != ERROR_SUCCESS)
{
ReportErrorCode(L"ChangeFileOwnerBySid", dwRtnCode);
goto ChownEnd; goto ChownEnd;
} }
@ -250,8 +130,6 @@ int Chown(__in int argc, __in_ecount(argc) wchar_t *argv[])
ChownEnd: ChownEnd:
LocalFree(userName); LocalFree(userName);
LocalFree(groupName); LocalFree(groupName);
LocalFree(pNewOwnerSid);
LocalFree(pNewGroupSid);
return ret; return ret;
} }

View File

@ -0,0 +1,498 @@
/**
* 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 <Winsvc.h>
#include <errno.h>
#include "hadoopwinutilsvc_h.h"
#pragma comment(lib, "Rpcrt4.lib")
#pragma comment(lib, "advapi32.lib")
static ACCESS_MASK CLIENT_MASK = 1;
VOID ReportClientError(LPWSTR lpszLocation, DWORD dwError) {
LPWSTR debugMsg = NULL;
int len;
WCHAR hexError[32];
HRESULT hr;
if (IsDebuggerPresent()) {
len = FormatMessageW(
FORMAT_MESSAGE_ALLOCATE_BUFFER | FORMAT_MESSAGE_FROM_SYSTEM,
NULL, dwError,
MAKELANGID(LANG_NEUTRAL, SUBLANG_DEFAULT),
(LPWSTR)&debugMsg, 0, NULL);
LogDebugMessage(L"%s: %s: %x: %.*s\n", GetSystemTimeString(), lpszLocation, dwError, len, debugMsg);
}
if (NULL != debugMsg) LocalFree(debugMsg);
}
DWORD PrepareRpcBindingHandle(
__out RPC_BINDING_HANDLE* pHadoopWinutilsSvcBinding) {
DWORD dwError = EXIT_FAILURE;
RPC_STATUS status;
LPWSTR lpszStringBinding = NULL;
ULONG ulCode;
RPC_SECURITY_QOS_V3 qos;
SID_IDENTIFIER_AUTHORITY authNT = SECURITY_NT_AUTHORITY;
BOOL rpcBindingInit = FALSE;
PSID pLocalSystemSid = NULL;
DWORD cbSystemSidSize = SECURITY_MAX_SID_SIZE;
pLocalSystemSid = (PSID) LocalAlloc(LPTR, cbSystemSidSize);
if (!pLocalSystemSid) {
dwError = GetLastError();
ReportClientError(L"LocalAlloc", dwError);
goto done;
}
if (!CreateWellKnownSid(WinLocalSystemSid, NULL, pLocalSystemSid, &cbSystemSidSize)) {
dwError = GetLastError();
ReportClientError(L"CreateWellKnownSid", dwError);
goto done;
}
ZeroMemory(&qos, sizeof(qos));
qos.Version = RPC_C_SECURITY_QOS_VERSION_3;
qos.Capabilities = RPC_C_QOS_CAPABILITIES_LOCAL_MA_HINT | RPC_C_QOS_CAPABILITIES_MUTUAL_AUTH;
qos.IdentityTracking = RPC_C_QOS_IDENTITY_DYNAMIC;
qos.ImpersonationType = RPC_C_IMP_LEVEL_DEFAULT;
qos.Sid = pLocalSystemSid;
status = RpcStringBindingCompose(NULL,
SVCBINDING,
NULL,
SVCNAME,
NULL,
&lpszStringBinding);
if (RPC_S_OK != status) {
ReportClientError(L"RpcStringBindingCompose", status);
dwError = status;
goto done;
}
status = RpcBindingFromStringBinding(lpszStringBinding, pHadoopWinutilsSvcBinding);
if (RPC_S_OK != status) {
ReportClientError(L"RpcBindingFromStringBinding", status);
dwError = status;
goto done;
}
rpcBindingInit = TRUE;
status = RpcBindingSetAuthInfoEx(
*pHadoopWinutilsSvcBinding,
NULL,
RPC_C_AUTHN_LEVEL_PKT_PRIVACY, // AuthnLevel
RPC_C_AUTHN_WINNT, // AuthnSvc
NULL, // AuthnIdentity (self)
RPC_C_AUTHZ_NONE, // AuthzSvc
&qos);
if (RPC_S_OK != status) {
ReportClientError(L"RpcBindingSetAuthInfoEx", status);
dwError = status;
goto done;
}
dwError = ERROR_SUCCESS;
done:
if (dwError && rpcBindingInit) RpcBindingFree(pHadoopWinutilsSvcBinding);
if (pLocalSystemSid) LocalFree(pLocalSystemSid);
if (NULL != lpszStringBinding) {
status = RpcStringFree(&lpszStringBinding);
if (RPC_S_OK != status) {
ReportClientError(L"RpcStringFree", status);
}
}
return dwError;
}
DWORD RpcCall_WinutilsKillTask(
__in LPCWSTR taskName) {
DWORD dwError = EXIT_FAILURE;
ULONG ulCode;
KILLTASK_REQUEST request;
RPC_BINDING_HANDLE hHadoopWinutilsSvcBinding;
BOOL rpcBindingInit = FALSE;
dwError = PrepareRpcBindingHandle(&hHadoopWinutilsSvcBinding);
if (dwError) {
ReportClientError(L"PrepareRpcBindingHandle", dwError);
goto done;
}
rpcBindingInit = TRUE;
ZeroMemory(&request, sizeof(request));
request.taskName = taskName;
RpcTryExcept {
dwError = WinutilsKillTask(hHadoopWinutilsSvcBinding, &request);
}
RpcExcept(1) {
ulCode = RpcExceptionCode();
ReportClientError(L"RpcExcept", ulCode);
dwError = (DWORD) ulCode;
}
RpcEndExcept;
done:
if (rpcBindingInit) RpcBindingFree(&hHadoopWinutilsSvcBinding);
LogDebugMessage(L"RpcCall_WinutilsKillTask: %s :%d\n", taskName, dwError);
return dwError;
}
DWORD RpcCall_WinutilsMkDir(
__in LPCWSTR filePath) {
DWORD dwError = EXIT_FAILURE;
ULONG ulCode;
MKDIR_REQUEST request;
RPC_BINDING_HANDLE hHadoopWinutilsSvcBinding;
BOOL rpcBindingInit = FALSE;
dwError = PrepareRpcBindingHandle(&hHadoopWinutilsSvcBinding);
if (dwError) {
ReportClientError(L"PrepareRpcBindingHandle", dwError);
goto done;
}
rpcBindingInit = TRUE;
ZeroMemory(&request, sizeof(request));
request.filePath = filePath;
RpcTryExcept {
dwError = WinutilsMkDir(hHadoopWinutilsSvcBinding, &request);
}
RpcExcept(1) {
ulCode = RpcExceptionCode();
ReportClientError(L"RpcExcept", ulCode);
dwError = (DWORD) ulCode;
}
RpcEndExcept;
done:
if (rpcBindingInit) RpcBindingFree(&hHadoopWinutilsSvcBinding);
LogDebugMessage(L"RpcCall_WinutilsMkDir: %s :%d\n", filePath, dwError);
return dwError;
}
DWORD RpcCall_WinutilsChown(
__in LPCWSTR filePath,
__in_opt LPCWSTR ownerName,
__in_opt LPCWSTR groupName) {
DWORD dwError = EXIT_FAILURE;
ULONG ulCode;
CHOWN_REQUEST request;
RPC_BINDING_HANDLE hHadoopWinutilsSvcBinding;
BOOL rpcBindingInit = FALSE;
dwError = PrepareRpcBindingHandle(&hHadoopWinutilsSvcBinding);
if (dwError) {
ReportClientError(L"PrepareRpcBindingHandle", dwError);
goto done;
}
rpcBindingInit = TRUE;
ZeroMemory(&request, sizeof(request));
request.filePath = filePath;
request.ownerName = ownerName;
request.groupName = groupName;
RpcTryExcept {
dwError = WinutilsChown(hHadoopWinutilsSvcBinding, &request);
}
RpcExcept(1) {
ulCode = RpcExceptionCode();
ReportClientError(L"RpcExcept", ulCode);
dwError = (DWORD) ulCode;
}
RpcEndExcept;
done:
if (rpcBindingInit) RpcBindingFree(&hHadoopWinutilsSvcBinding);
LogDebugMessage(L"RpcCall_WinutilsChown: %s %s %s :%d\n",
ownerName, groupName, filePath, dwError);
return dwError;
}
DWORD RpcCall_WinutilsChmod(
__in LPCWSTR filePath,
__in int mode) {
DWORD dwError = EXIT_FAILURE;
ULONG ulCode;
CHMOD_REQUEST request;
RPC_BINDING_HANDLE hHadoopWinutilsSvcBinding;
BOOL rpcBindingInit = FALSE;
dwError = PrepareRpcBindingHandle(&hHadoopWinutilsSvcBinding);
if (dwError) {
ReportClientError(L"PrepareRpcBindingHandle", dwError);
goto done;
}
rpcBindingInit = TRUE;
ZeroMemory(&request, sizeof(request));
request.filePath = filePath;
request.mode = mode;
RpcTryExcept {
dwError = WinutilsChmod(hHadoopWinutilsSvcBinding, &request);
}
RpcExcept(1) {
ulCode = RpcExceptionCode();
ReportClientError(L"RpcExcept", ulCode);
dwError = (DWORD) ulCode;
}
RpcEndExcept;
done:
if (rpcBindingInit) RpcBindingFree(&hHadoopWinutilsSvcBinding);
LogDebugMessage(L"RpcCall_WinutilsChmod: %s %o :%d\n",
filePath, mode, dwError);
return dwError;
}
DWORD RpcCall_WinutilsMoveFile(
__in int operation,
__in LPCWSTR sourcePath,
__in LPCWSTR destinationPath,
__in BOOL replaceExisting) {
DWORD dwError = EXIT_FAILURE;
ULONG ulCode;
MOVEFILE_REQUEST request;
RPC_BINDING_HANDLE hHadoopWinutilsSvcBinding;
BOOL rpcBindingInit = FALSE;
dwError = PrepareRpcBindingHandle(&hHadoopWinutilsSvcBinding);
if (dwError) {
ReportClientError(L"PrepareRpcBindingHandle", dwError);
goto done;
}
rpcBindingInit = TRUE;
ZeroMemory(&request, sizeof(request));
request.operation = operation;
request.sourcePath = sourcePath;
request.destinationPath = destinationPath;
request.replaceExisting = replaceExisting;
RpcTryExcept {
dwError = WinutilsMoveFile(hHadoopWinutilsSvcBinding, &request);
}
RpcExcept(1) {
ulCode = RpcExceptionCode();
ReportClientError(L"RpcExcept", ulCode);
dwError = (DWORD) ulCode;
}
RpcEndExcept;
done:
if (rpcBindingInit) RpcBindingFree(&hHadoopWinutilsSvcBinding);
LogDebugMessage(L"RpcCall_WinutilsMoveFile: %s %s %d :%d\n",
sourcePath, destinationPath, replaceExisting, dwError);
return dwError;
}
DWORD RpcCall_WinutilsCreateFile(
__in LPCWSTR path,
__in DWORD desiredAccess,
__in DWORD shareMode,
__in DWORD creationDisposition,
__in DWORD flags,
__out HANDLE* hFile) {
DWORD dwError = EXIT_FAILURE;
ULONG ulCode;
DWORD dwSelfPid = GetCurrentProcessId();
CREATEFILE_REQUEST request;
CREATEFILE_RESPONSE *response = NULL;
RPC_BINDING_HANDLE hHadoopWinutilsSvcBinding;
BOOL rpcBindingInit = FALSE;
dwError = PrepareRpcBindingHandle(&hHadoopWinutilsSvcBinding);
if (dwError) {
ReportClientError(L"PrepareRpcBindingHandle", dwError);
goto done;
}
rpcBindingInit = TRUE;
ZeroMemory(&request, sizeof(request));
request.path = path;
request.desiredAccess = desiredAccess;
request.shareMode = shareMode;
request.creationDisposition = creationDisposition;
request.flags = flags;
RpcTryExcept {
dwError = WinutilsCreateFile(hHadoopWinutilsSvcBinding, dwSelfPid, &request, &response);
}
RpcExcept(1) {
ulCode = RpcExceptionCode();
ReportClientError(L"RpcExcept", ulCode);
dwError = (DWORD) ulCode;
}
RpcEndExcept;
if (ERROR_SUCCESS == dwError) {
*hFile = response->hFile;
}
done:
if (rpcBindingInit) RpcBindingFree(&hHadoopWinutilsSvcBinding);
if(NULL != response) MIDL_user_free(response);
LogDebugMessage(L"RpcCall_WinutilsCreateFile: %s %d, %d, %d, %d :%d\n",
path, desiredAccess, shareMode, creationDisposition, flags, dwError);
return dwError;
}
DWORD RpcCall_WinutilsDeletePath(
__in LPCWSTR path,
__in BOOL isDir,
__out BOOL* pDeleted) {
DWORD dwError = EXIT_FAILURE;
ULONG ulCode;
DELETEPATH_REQUEST request;
DELETEPATH_RESPONSE *response = NULL;
RPC_BINDING_HANDLE hHadoopWinutilsSvcBinding;
BOOL rpcBindingInit = FALSE;
pDeleted = FALSE;
dwError = PrepareRpcBindingHandle(&hHadoopWinutilsSvcBinding);
if (dwError) {
ReportClientError(L"PrepareRpcBindingHandle", dwError);
goto done;
}
rpcBindingInit = TRUE;
ZeroMemory(&request, sizeof(request));
request.path = path;
request.type = isDir ? PATH_IS_DIR : PATH_IS_FILE;
RpcTryExcept {
dwError = WinutilsDeletePath(hHadoopWinutilsSvcBinding, &request, &response);
}
RpcExcept(1) {
ulCode = RpcExceptionCode();
ReportClientError(L"RpcExcept", ulCode);
dwError = (DWORD) ulCode;
}
RpcEndExcept;
if (ERROR_SUCCESS == dwError) {
*pDeleted = response->deleted;
}
done:
if (rpcBindingInit) RpcBindingFree(&hHadoopWinutilsSvcBinding);
if(NULL != response) MIDL_user_free(response);
LogDebugMessage(L"RpcCall_WinutilsDeletePath: %s %d: %d %d\n",
path, isDir, *pDeleted, dwError);
return dwError;
}
DWORD RpcCall_TaskCreateAsUser(
LPCWSTR cwd, LPCWSTR jobName,
LPCWSTR user, LPCWSTR pidFile, LPCWSTR cmdLine,
HANDLE* phProcess, HANDLE* phThread, HANDLE* phStdIn, HANDLE* phStdOut, HANDLE* phStdErr)
{
DWORD dwError = EXIT_FAILURE;
ULONG ulCode;
DWORD dwSelfPid = GetCurrentProcessId();
CREATE_PROCESS_REQUEST request;
CREATE_PROCESS_RESPONSE *response = NULL;
RPC_BINDING_HANDLE hHadoopWinutilsSvcBinding;
BOOL rpcBindingInit = FALSE;
dwError = PrepareRpcBindingHandle(&hHadoopWinutilsSvcBinding);
if (dwError) {
ReportClientError(L"PrepareRpcBindingHandle", dwError);
goto done;
}
rpcBindingInit = TRUE;
ZeroMemory(&request, sizeof(request));
request.cwd = cwd;
request.jobName = jobName;
request.user = user;
request.pidFile = pidFile;
request.cmdLine = cmdLine;
RpcTryExcept {
dwError = WinutilsCreateProcessAsUser(hHadoopWinutilsSvcBinding, dwSelfPid, &request, &response);
}
RpcExcept(1) {
ulCode = RpcExceptionCode();
ReportClientError(L"RpcExcept", ulCode);
dwError = (DWORD) ulCode;
}
RpcEndExcept;
if (ERROR_SUCCESS == dwError) {
*phProcess = response->hProcess;
*phThread = response->hThread;
*phStdIn = response->hStdIn;
*phStdOut = response->hStdOut;
*phStdErr = response->hStdErr;
}
done:
if (rpcBindingInit) RpcBindingFree(&hHadoopWinutilsSvcBinding);
if (NULL != response) {
MIDL_user_free(response);
}
return dwError;
}

View File

@ -0,0 +1,174 @@
/**
* 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 <string.h>
#include <stdlib.h>
#import "msxml6.dll"
#define ERROR_CHECK_HRESULT_DONE(hr, message) \
if (FAILED(hr)) { \
dwError = (DWORD) hr; \
LogDebugMessage(L"%s: %x", message, hr); \
goto done; \
}
DWORD BuildPathRelativeToModule(
__in LPCWSTR relativePath,
__in size_t len,
__out_ecount(len) LPWSTR buffer) {
DWORD dwError = ERROR_SUCCESS;
WCHAR moduleFile[MAX_PATH];
WCHAR modulePath[_MAX_DIR];
WCHAR moduleDrive[_MAX_DRIVE];
DWORD size;
HRESULT hr = S_OK;
errno_t errno;
size = GetModuleFileName(NULL, moduleFile, MAX_PATH);
dwError = GetLastError(); // Always check due to ERROR_INSUFFICIENT_BUFFER
if (dwError) {
LogDebugMessage(L"GetModuleFileName: %x\n", dwError);
goto done;
}
errno = _wsplitpath_s(moduleFile,
moduleDrive, _MAX_DRIVE,
modulePath, _MAX_DIR,
NULL, 0, // fname, not interesting
NULL, 0); // extenssion, not interesting
if (errno) {
LogDebugMessage(L"_wsplitpath_s: %x\n", errno);
dwError = ERROR_BAD_PATHNAME;
goto done;
}
hr = StringCbPrintf(buffer, len, L"%s%s%s", moduleDrive, modulePath, relativePath);
if (FAILED(hr)) {
// There is no reliable HRESULT to WIN32 mapping, use code.
// see http://blogs.msdn.com/b/oldnewthing/archive/2006/11/03/942851.aspx
//
dwError = HRESULT_CODE(hr);
goto done;
}
LogDebugMessage(L"BuildPathRelativeToModule: %s (%s)\n", buffer, relativePath);
done:
return dwError;
}
DWORD GetConfigValue(
__in LPCWSTR relativePath,
__in LPCWSTR keyName,
__out size_t* len, __out_ecount(len) LPCWSTR* value) {
DWORD dwError = ERROR_SUCCESS;
WCHAR xmlPath[MAX_PATH];
*len = 0;
*value = NULL;
dwError = BuildPathRelativeToModule(
relativePath,
sizeof(xmlPath)/sizeof(WCHAR),
xmlPath);
if (dwError) {
goto done;
}
dwError = GetConfigValueFromXmlFile(xmlPath, keyName, len, value);
done:
if (*len) {
LogDebugMessage(L"GetConfigValue:%d key:%s len:%d value:%.*s from:%s\n", dwError, keyName, *len, *len, *value, xmlPath);
}
return dwError;
}
DWORD GetConfigValueFromXmlFile(__in LPCWSTR xmlFile, __in LPCWSTR keyName,
__out size_t* outLen, __out_ecount(len) LPCWSTR* outValue) {
DWORD dwError = ERROR_SUCCESS;
HRESULT hr;
WCHAR keyXsl[8192];
size_t len = 0;
LPWSTR value = NULL;
BOOL comInitialized = FALSE;
*outLen = 0;
*outValue = NULL;
hr = CoInitialize(NULL);
ERROR_CHECK_HRESULT_DONE(hr, L"CoInitialize");
comInitialized = TRUE;
hr = StringCbPrintf(keyXsl, sizeof(keyXsl), L"//configuration/property[name='%s']/value/text()", keyName);
ERROR_CHECK_HRESULT_DONE(hr, L"StringCbPrintf");
try {
MSXML2::IXMLDOMDocument2Ptr pDoc;
hr = pDoc.CreateInstance(__uuidof(MSXML2::DOMDocument60), NULL, CLSCTX_INPROC_SERVER);
ERROR_CHECK_HRESULT_DONE(hr, L"CreateInstance");
pDoc->async = VARIANT_FALSE;
pDoc->validateOnParse = VARIANT_FALSE;
pDoc->resolveExternals = VARIANT_FALSE;
_variant_t file(xmlFile);
if (VARIANT_FALSE == pDoc->load(file)) {
dwError = pDoc->parseError->errorCode;
LogDebugMessage(L"load %s failed:%d %s\n", xmlFile, dwError,
static_cast<LPCWSTR>(pDoc->parseError->Getreason()));
goto done;
}
MSXML2::IXMLDOMElementPtr pRoot = pDoc->documentElement;
MSXML2::IXMLDOMNodePtr keyNode = pRoot->selectSingleNode(keyXsl);
if (keyNode) {
_bstr_t bstrValue = static_cast<_bstr_t>(keyNode->nodeValue);
len = bstrValue.length();
value = (LPWSTR) LocalAlloc(LPTR, (len+1) * sizeof(WCHAR));
LPCWSTR lpwszValue = static_cast<LPCWSTR>(bstrValue);
memcpy(value, lpwszValue, (len) * sizeof(WCHAR));
LogDebugMessage(L"key:%s :%.*s [%s]\n", keyName, len, value, lpwszValue);
*outLen = len;
*outValue = value;
}
else {
LogDebugMessage(L"node Xpath:%s not found in:%s\n", keyXsl, xmlFile);
}
}
catch(_com_error errorObject) {
dwError = errorObject.Error();
LogDebugMessage(L"catch _com_error:%x %s\n", dwError, errorObject.ErrorMessage());
goto done;
}
done:
if (comInitialized) {
CoUninitialize();
}
return dwError;
}

View File

@ -0,0 +1,130 @@
/*
* 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.
*/
import "oaidl.idl";
import "ocidl.idl";
[
uuid(0492311C-1718-4F53-A6EB-86AD7039988D),
version(1.0),
pointer_default(unique),
//implicit_handle(handle_t hHadoopWinutilsSvcBinding),
endpoint("ncalrpc:[hadoopwinutilsvc]"),
#ifndef __midl
explicit_handle
#endif
]
interface HadoopWinutilSvc
{
typedef struct {
[string] const wchar_t* cwd;
[string] const wchar_t* jobName;
[string] const wchar_t* user;
[string] const wchar_t* pidFile;
[string] const wchar_t* cmdLine;
} CREATE_PROCESS_REQUEST;
typedef struct {
LONG_PTR hProcess;
LONG_PTR hThread;
LONG_PTR hStdIn;
LONG_PTR hStdOut;
LONG_PTR hStdErr;
} CREATE_PROCESS_RESPONSE;
typedef struct {
[string] const wchar_t* filePath;
[string] const wchar_t* ownerName;
[string] const wchar_t* groupName;
} CHOWN_REQUEST;
typedef struct {
[string] const wchar_t* filePath;
int mode;
} CHMOD_REQUEST;
typedef struct {
[string] const wchar_t* filePath;
} MKDIR_REQUEST;
typedef enum { MOVE_FILE = 1, COPY_FILE = 2} MOVE_COPY_OPERATION;
typedef struct {
MOVE_COPY_OPERATION operation;
[string] const wchar_t* sourcePath;
[string] const wchar_t* destinationPath;
boolean replaceExisting;
} MOVEFILE_REQUEST;
typedef struct {
[string] const wchar_t* path;
int desiredAccess;
int shareMode;
int creationDisposition;
int flags;
} CREATEFILE_REQUEST;
typedef struct {
LONG_PTR hFile;
} CREATEFILE_RESPONSE;
typedef enum {PATH_IS_DIR = 1, PATH_IS_FILE = 2} DELETEPATH_TYPE;
typedef struct {
DELETEPATH_TYPE type;
[string] const wchar_t* path;
} DELETEPATH_REQUEST;
typedef struct {
boolean deleted;
} DELETEPATH_RESPONSE;
typedef struct {
[string] const wchar_t* taskName;
} KILLTASK_REQUEST;
error_status_t WinutilsKillTask(
[in] KILLTASK_REQUEST *request);
error_status_t WinutilsMkDir(
[in] MKDIR_REQUEST *request);
error_status_t WinutilsMoveFile(
[in] MOVEFILE_REQUEST *request);
error_status_t WinutilsChown(
[in] CHOWN_REQUEST *request);
error_status_t WinutilsChmod(
[in] CHMOD_REQUEST *request);
error_status_t WinutilsCreateFile(
[in] int nmPid,
[in] CREATEFILE_REQUEST *request,
[out] CREATEFILE_RESPONSE **response);
error_status_t WinutilsDeletePath(
[in] DELETEPATH_REQUEST *request,
[out] DELETEPATH_RESPONSE **response);
error_status_t WinutilsCreateProcessAsUser(
[in] int nmPid,
[in] CREATE_PROCESS_REQUEST *request,
[out] CREATE_PROCESS_RESPONSE **response);
}

View File

@ -30,6 +30,11 @@
#include <ntsecapi.h> #include <ntsecapi.h>
#include <userenv.h> #include <userenv.h>
#ifdef __cplusplus
extern "C" {
#endif
enum EXIT_CODE enum EXIT_CODE
{ {
/* Common success exit code shared among all utilities */ /* Common success exit code shared among all utilities */
@ -38,6 +43,12 @@ enum EXIT_CODE
FAILURE = EXIT_FAILURE, FAILURE = EXIT_FAILURE,
/* Failure code indicates the user does not privilege to create symlinks */ /* Failure code indicates the user does not privilege to create symlinks */
SYMLINK_NO_PRIVILEGE = 2, SYMLINK_NO_PRIVILEGE = 2,
ERROR_TASK_NOT_ALIVE = 1,
// This exit code for killed processes is compatible with Unix, where a killed
// process exits with 128 + signal. For SIGKILL, this would be 128 + 9 = 137.
KILLED_PROCESS_EXIT_CODE = 137,
}; };
@ -101,6 +112,8 @@ void GroupsUsage(LPCWSTR program);
int Hardlink(__in int argc, __in_ecount(argc) wchar_t *argv[]); int Hardlink(__in int argc, __in_ecount(argc) wchar_t *argv[]);
void HardlinkUsage(); void HardlinkUsage();
DWORD KillTask(PCWSTR jobObjName);
int Task(__in int argc, __in_ecount(argc) wchar_t *argv[]); int Task(__in int argc, __in_ecount(argc) wchar_t *argv[]);
void TaskUsage(); void TaskUsage();
@ -167,7 +180,7 @@ void UnregisterWithLsa(__in HANDLE lsaHandle);
DWORD LookupKerberosAuthenticationPackageId(__in HANDLE lsaHandle, __out ULONG * packageId); DWORD LookupKerberosAuthenticationPackageId(__in HANDLE lsaHandle, __out ULONG * packageId);
DWORD CreateLogonForUser(__in HANDLE lsaHandle, DWORD CreateLogonTokenForUser(__in HANDLE lsaHandle,
__in const char * tokenSourceName, __in const char * tokenSourceName,
__in const char * tokenOriginName, __in const char * tokenOriginName,
__in ULONG authnPkgId, __in ULONG authnPkgId,
@ -178,3 +191,102 @@ DWORD LoadUserProfileForLogon(__in HANDLE logonHandle, __out PROFILEINFO * pi);
DWORD UnloadProfileForLogon(__in HANDLE logonHandle, __in PROFILEINFO * pi); DWORD UnloadProfileForLogon(__in HANDLE logonHandle, __in PROFILEINFO * pi);
DWORD EnableImpersonatePrivileges();
DWORD RunService(__in int argc, __in_ecount(argc) wchar_t *argv[]);
void ServiceUsage();
DWORD ChangeFileOwnerBySid(__in LPCWSTR path,
__in_opt PSID pNewOwnerSid, __in_opt PSID pNewGroupSid);
DWORD ChownImpl(
__in_opt LPCWSTR userName,
__in_opt LPCWSTR groupName,
__in LPCWSTR pathName);
LPCWSTR GetSystemTimeString();
VOID LogDebugMessage(LPCWSTR format, ...);
DWORD SplitStringIgnoreSpaceW(
__in size_t len,
__in_ecount(len) LPCWSTR source,
__in WCHAR deli,
__out size_t* count, __out_ecount(count) WCHAR*** out);
DWORD BuildPathRelativeToModule(
__in LPCWSTR relativePath,
__in size_t len,
__out_ecount(len) LPWSTR buffer);
DWORD GetConfigValue(
__in LPCWSTR relativePath,
__in LPCWSTR keyName,
__out size_t* len,
__out_ecount(len) LPCWSTR* value);
DWORD GetConfigValueFromXmlFile(
__in LPCWSTR xmlFile,
__in LPCWSTR keyName,
__out size_t* len,
__out_ecount(len) LPCWSTR* value);
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 AddNodeManagerAndUserACEsToObject(
__in HANDLE hProcess,
__in LPWSTR user);
DWORD GetSecureJobObjectName(
__in LPCWSTR jobName,
__in size_t cchSecureJobName,
__out_ecount(cchSecureJobName) LPWSTR secureJobName);
extern const WCHAR* wsceConfigRelativePath;
extern LPCWSTR NM_WSCE_ALLOWED;
#define SVCNAME TEXT("hadoopwinutilsvc")
#define SVCBINDING TEXT("ncalrpc")
DWORD RpcCall_WinutilsKillTask(
__in LPCWSTR taskName);
DWORD RpcCall_TaskCreateAsUser(
LPCWSTR cwd, LPCWSTR jobName,
LPCWSTR user, LPCWSTR pidFile, LPCWSTR cmdLine,
HANDLE* phProcess, HANDLE* phThread, HANDLE* phStdIn, HANDLE* phStdOut, HANDLE* phStdErr);
DWORD RpcCall_WinutilsCreateFile(
__in LPCWSTR path,
__in DWORD desiredAccess,
__in DWORD shareMode,
__in DWORD creationDisposition,
__in DWORD flags,
__out HANDLE* hFile);
DWORD RpcCall_WinutilsMoveFile(
__in LPCWSTR sourcePath,
__in LPCWSTR destinationPath,
__in BOOL replaceExisting);
DWORD RpcCall_WinutilsDeletePath(
__in LPCWSTR path,
__in BOOL isDir,
__out BOOL* pDeleted);
#ifdef __cplusplus
}
#endif

View File

@ -19,9 +19,24 @@
#pragma comment(lib, "netapi32.lib") #pragma comment(lib, "netapi32.lib")
#pragma comment(lib, "Secur32.lib") #pragma comment(lib, "Secur32.lib")
#pragma comment(lib, "Userenv.lib") #pragma comment(lib, "Userenv.lib")
#pragma comment(lib, "Ntdsapi.lib")
#include "winutils.h" #include "winutils.h"
#include <ctype.h>
#include <Winsvc.h>
#include <authz.h> #include <authz.h>
#include <sddl.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 * The array of 12 months' three-letter abbreviations
@ -1207,14 +1222,14 @@ static DWORD GetWindowsDACLs(__in INT unixMask,
if (winUserAccessDenyMask && if (winUserAccessDenyMask &&
!AddAccessDeniedAceEx(pNewDACL, ACL_REVISION, !AddAccessDeniedAceEx(pNewDACL, ACL_REVISION,
NO_PROPAGATE_INHERIT_ACE, CONTAINER_INHERIT_ACE | OBJECT_INHERIT_ACE,
winUserAccessDenyMask, pOwnerSid)) winUserAccessDenyMask, pOwnerSid))
{ {
ret = GetLastError(); ret = GetLastError();
goto GetWindowsDACLsEnd; goto GetWindowsDACLsEnd;
} }
if (!AddAccessAllowedAceEx(pNewDACL, ACL_REVISION, if (!AddAccessAllowedAceEx(pNewDACL, ACL_REVISION,
NO_PROPAGATE_INHERIT_ACE, CONTAINER_INHERIT_ACE | OBJECT_INHERIT_ACE,
winUserAccessAllowMask, pOwnerSid)) winUserAccessAllowMask, pOwnerSid))
{ {
ret = GetLastError(); ret = GetLastError();
@ -1222,21 +1237,21 @@ static DWORD GetWindowsDACLs(__in INT unixMask,
} }
if (winGroupAccessDenyMask && if (winGroupAccessDenyMask &&
!AddAccessDeniedAceEx(pNewDACL, ACL_REVISION, !AddAccessDeniedAceEx(pNewDACL, ACL_REVISION,
NO_PROPAGATE_INHERIT_ACE, CONTAINER_INHERIT_ACE | OBJECT_INHERIT_ACE,
winGroupAccessDenyMask, pGroupSid)) winGroupAccessDenyMask, pGroupSid))
{ {
ret = GetLastError(); ret = GetLastError();
goto GetWindowsDACLsEnd; goto GetWindowsDACLsEnd;
} }
if (!AddAccessAllowedAceEx(pNewDACL, ACL_REVISION, if (!AddAccessAllowedAceEx(pNewDACL, ACL_REVISION,
NO_PROPAGATE_INHERIT_ACE, CONTAINER_INHERIT_ACE | OBJECT_INHERIT_ACE,
winGroupAccessAllowMask, pGroupSid)) winGroupAccessAllowMask, pGroupSid))
{ {
ret = GetLastError(); ret = GetLastError();
goto GetWindowsDACLsEnd; goto GetWindowsDACLsEnd;
} }
if (!AddAccessAllowedAceEx(pNewDACL, ACL_REVISION, if (!AddAccessAllowedAceEx(pNewDACL, ACL_REVISION,
NO_PROPAGATE_INHERIT_ACE, CONTAINER_INHERIT_ACE | OBJECT_INHERIT_ACE,
winOtherAccessAllowMask, pEveryoneSid)) winOtherAccessAllowMask, pEveryoneSid))
{ {
ret = GetLastError(); ret = GetLastError();
@ -1631,6 +1646,7 @@ GetLocalGroupsForUserEnd:
return ret; return ret;
} }
//---------------------------------------------------------------------------- //----------------------------------------------------------------------------
// Function: EnablePrivilege // Function: EnablePrivilege
// //
@ -1672,7 +1688,7 @@ DWORD EnablePrivilege(__in LPCWSTR privilegeName)
// As stated on MSDN, we need to use GetLastError() to check if // As stated on MSDN, we need to use GetLastError() to check if
// AdjustTokenPrivileges() adjusted all of the specified privileges. // AdjustTokenPrivileges() adjusted all of the specified privileges.
// //
if( !AdjustTokenPrivileges(hToken, FALSE, &tp, 0, NULL, NULL) ) { if (!AdjustTokenPrivileges(hToken, FALSE, &tp, 0, NULL, NULL)) {
dwErrCode = GetLastError(); dwErrCode = GetLastError();
} }
CloseHandle(hToken); CloseHandle(hToken);
@ -1706,12 +1722,15 @@ void ReportErrorCode(LPCWSTR func, DWORD err)
(LPWSTR)&msg, 0, NULL); (LPWSTR)&msg, 0, NULL);
if (len > 0) if (len > 0)
{ {
LogDebugMessage(L"%s error (%d): %s\n", func, err, msg);
fwprintf(stderr, L"%s error (%d): %s\n", func, err, msg); fwprintf(stderr, L"%s error (%d): %s\n", func, err, msg);
} }
else else
{ {
LogDebugMessage(L"%s error code: %d.\n", func, err);
fwprintf(stderr, L"%s error code: %d.\n", func, err); fwprintf(stderr, L"%s error code: %d.\n", func, err);
} }
if (msg != NULL) LocalFree(msg); if (msg != NULL) LocalFree(msg);
} }
@ -1843,7 +1862,7 @@ DWORD LookupKerberosAuthenticationPackageId(__in HANDLE lsaHandle, __out ULONG *
} }
//---------------------------------------------------------------------------- //----------------------------------------------------------------------------
// Function: CreateLogonForUser // Function: CreateLogonTokenForUser
// //
// Description: // Description:
// Contacts the local LSA and performs a logon without credential for the // 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). // This call assumes that all required privileges have already been enabled (TCB etc).
// IMPORTANT **** tokenOriginName must be immutable! // IMPORTANT **** tokenOriginName must be immutable!
// //
DWORD CreateLogonForUser(__in HANDLE lsaHandle, DWORD CreateLogonTokenForUser(__in HANDLE lsaHandle,
__in const char * tokenSourceName, __in const char * tokenSourceName,
__in const char * tokenOriginName, // must be immutable, will not be copied! __in const char * tokenOriginName, // must be immutable, will not be copied!
__in ULONG authnPkgId, __in ULONG authnPkgId,
@ -2026,6 +2045,8 @@ done:
return loadProfileStatus; return loadProfileStatus;
} }
DWORD UnloadProfileForLogon(__in HANDLE logonHandle, __in PROFILEINFO * pi) DWORD UnloadProfileForLogon(__in HANDLE logonHandle, __in PROFILEINFO * pi)
{ {
DWORD touchProfileStatus = ERROR_ASSERTION_FAILURE; // Failure to set status should trigger error 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: done:
return touchProfileStatus; 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);
}

View File

@ -19,18 +19,10 @@
<Project DefaultTargets="Build" ToolsVersion="4.0" xmlns="http://schemas.microsoft.com/developer/msbuild/2003"> <Project DefaultTargets="Build" ToolsVersion="4.0" xmlns="http://schemas.microsoft.com/developer/msbuild/2003">
<ItemGroup Label="ProjectConfigurations"> <ItemGroup Label="ProjectConfigurations">
<ProjectConfiguration Include="Debug|Win32">
<Configuration>Debug</Configuration>
<Platform>Win32</Platform>
</ProjectConfiguration>
<ProjectConfiguration Include="Debug|x64"> <ProjectConfiguration Include="Debug|x64">
<Configuration>Debug</Configuration> <Configuration>Debug</Configuration>
<Platform>x64</Platform> <Platform>x64</Platform>
</ProjectConfiguration> </ProjectConfiguration>
<ProjectConfiguration Include="Release|Win32">
<Configuration>Release</Configuration>
<Platform>Win32</Platform>
</ProjectConfiguration>
<ProjectConfiguration Include="Release|x64"> <ProjectConfiguration Include="Release|x64">
<Configuration>Release</Configuration> <Configuration>Release</Configuration>
<Platform>x64</Platform> <Platform>x64</Platform>
@ -42,22 +34,11 @@
<RootNamespace>winutils</RootNamespace> <RootNamespace>winutils</RootNamespace>
</PropertyGroup> </PropertyGroup>
<Import Project="$(VCTargetsPath)\Microsoft.Cpp.Default.props" /> <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"> <PropertyGroup Condition="'$(Configuration)|$(Platform)'=='Debug|x64'" Label="Configuration">
<ConfigurationType>StaticLibrary</ConfigurationType> <ConfigurationType>StaticLibrary</ConfigurationType>
<UseDebugLibraries>true</UseDebugLibraries> <UseDebugLibraries>true</UseDebugLibraries>
<CharacterSet>Unicode</CharacterSet> <CharacterSet>Unicode</CharacterSet>
</PropertyGroup> </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"> <PropertyGroup Condition="'$(Configuration)|$(Platform)'=='Release|x64'" Label="Configuration">
<ConfigurationType>StaticLibrary</ConfigurationType> <ConfigurationType>StaticLibrary</ConfigurationType>
<UseDebugLibraries>false</UseDebugLibraries> <UseDebugLibraries>false</UseDebugLibraries>
@ -67,15 +48,9 @@
<Import Project="$(VCTargetsPath)\Microsoft.Cpp.props" /> <Import Project="$(VCTargetsPath)\Microsoft.Cpp.props" />
<ImportGroup Label="ExtensionSettings"> <ImportGroup Label="ExtensionSettings">
</ImportGroup> </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"> <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" /> <Import Project="$(UserRootDir)\Microsoft.Cpp.$(Platform).user.props" Condition="exists('$(UserRootDir)\Microsoft.Cpp.$(Platform).user.props')" Label="LocalAppDataPlatform" />
</ImportGroup> </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"> <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" /> <Import Project="$(UserRootDir)\Microsoft.Cpp.$(Platform).user.props" Condition="exists('$(UserRootDir)\Microsoft.Cpp.$(Platform).user.props')" Label="LocalAppDataPlatform" />
</ImportGroup> </ImportGroup>
@ -83,74 +58,35 @@
<PropertyGroup> <PropertyGroup>
<IncludePath>include;$(IncludePath)</IncludePath> <IncludePath>include;$(IncludePath)</IncludePath>
</PropertyGroup> </PropertyGroup>
<PropertyGroup Condition="'$(Configuration)|$(Platform)'=='Debug|Win32'">
<LinkIncremental>true</LinkIncremental>
</PropertyGroup>
<PropertyGroup Condition="'$(Configuration)|$(Platform)'=='Debug|x64'"> <PropertyGroup Condition="'$(Configuration)|$(Platform)'=='Debug|x64'">
<LinkIncremental>true</LinkIncremental> <LinkIncremental>true</LinkIncremental>
<OutDir />
<IntDir>..\..\..\target\winutils\$(Configuration)\</IntDir>
</PropertyGroup>
<PropertyGroup Condition="'$(Configuration)|$(Platform)'=='Release|Win32'">
<LinkIncremental>false</LinkIncremental>
</PropertyGroup> </PropertyGroup>
<PropertyGroup Condition="'$(Configuration)|$(Platform)'=='Release|x64'"> <PropertyGroup Condition="'$(Configuration)|$(Platform)'=='Release|x64'">
<LinkIncremental>false</LinkIncremental> <LinkIncremental>false</LinkIncremental>
<OutDir>..\..\..\target\bin\</OutDir>
<IntDir>..\..\..\target\winutils\$(Platform)\$(Configuration)\</IntDir>
</PropertyGroup> </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'"> <ItemDefinitionGroup Condition="'$(Configuration)|$(Platform)'=='Debug|x64'">
<ClCompile> <ClCompile>
<PrecompiledHeader> <PrecompiledHeader>
</PrecompiledHeader> </PrecompiledHeader>
<WarningLevel>Level4</WarningLevel> <WarningLevel>Level4</WarningLevel>
<Optimization>Disabled</Optimization> <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> </ClCompile>
<Link> <Link>
<SubSystem>Console</SubSystem> <SubSystem>Console</SubSystem>
<GenerateDebugInformation>true</GenerateDebugInformation> <GenerateDebugInformation>true</GenerateDebugInformation>
</Link> </Link>
</ItemDefinitionGroup> </ItemDefinitionGroup>
<ItemDefinitionGroup Condition="'$(Configuration)|$(Platform)'=='Release|Win32'">
<ClCompile>
<WarningLevel>Level3</WarningLevel>
<PrecompiledHeader>
</PrecompiledHeader>
<Optimization>MaxSpeed</Optimization>
<FunctionLevelLinking>true</FunctionLevelLinking>
<IntrinsicFunctions>true</IntrinsicFunctions>
<PreprocessorDefinitions>WIN32;NDEBUG;_CONSOLE;%(PreprocessorDefinitions)</PreprocessorDefinitions>
</ClCompile>
<Link>
<SubSystem>Console</SubSystem>
<GenerateDebugInformation>true</GenerateDebugInformation>
<EnableCOMDATFolding>true</EnableCOMDATFolding>
<OptimizeReferences>true</OptimizeReferences>
</Link>
</ItemDefinitionGroup>
<ItemDefinitionGroup Condition="'$(Configuration)|$(Platform)'=='Release|x64'"> <ItemDefinitionGroup Condition="'$(Configuration)|$(Platform)'=='Release|x64'">
<ClCompile> <ClCompile>
<WarningLevel>Level3</WarningLevel> <WarningLevel>Level3</WarningLevel>
<PrecompiledHeader> <PrecompiledHeader>
</PrecompiledHeader> </PrecompiledHeader>
<Optimization>MaxSpeed</Optimization> <!-- <Optimization>MaxSpeed</Optimization> -->
<Optimization>Disabled</Optimization>
<FunctionLevelLinking>true</FunctionLevelLinking> <FunctionLevelLinking>true</FunctionLevelLinking>
<IntrinsicFunctions>true</IntrinsicFunctions> <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> </ClCompile>
<Link> <Link>
<SubSystem>Console</SubSystem> <SubSystem>Console</SubSystem>
@ -159,12 +95,34 @@
<OptimizeReferences>true</OptimizeReferences> <OptimizeReferences>true</OptimizeReferences>
</Link> </Link>
</ItemDefinitionGroup> </ItemDefinitionGroup>
<ItemDefinitionGroup>
<ClCompile>
<AdditionalIncludeDirectories>$(IntermediateOutputPath)</AdditionalIncludeDirectories>
</ClCompile>
<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> <ItemGroup>
<ClCompile Include="client.c" />
<ClCompile Include="$(IntermediateOutputPath)\hadoopwinutilsvc_c.c" />
<ClCompile Include="libwinutils.c" /> <ClCompile Include="libwinutils.c" />
</ItemGroup> </ItemGroup>
<ItemGroup>
<ClCompile Include="config.cpp" />
</ItemGroup>
<ItemGroup> <ItemGroup>
<ClInclude Include="include/winutils.h" /> <ClInclude Include="include/winutils.h" />
</ItemGroup> </ItemGroup>
<ItemGroup>
<Midl Include="hadoopwinutilsvc.idl" />
</ItemGroup>
<Import Project="$(VCTargetsPath)\Microsoft.Cpp.targets" /> <Import Project="$(VCTargetsPath)\Microsoft.Cpp.targets" />
<ImportGroup Label="ExtensionTargets"> <ImportGroup Label="ExtensionTargets">
</ImportGroup> </ImportGroup>

View File

@ -17,12 +17,27 @@
#include "winutils.h" #include "winutils.h"
#include <winbase.h>
static void Usage(LPCWSTR program); 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[]) int wmain(__in int argc, __in_ecount(argc) wchar_t* argv[])
{ {
LPCWSTR cmd = NULL; LPCWSTR cmd = NULL;
SetUnhandledExceptionFilter(WinutilsSehUnhandled);
if (argc < 2) if (argc < 2)
{ {
Usage(argv[0]); Usage(argv[0]);
@ -67,6 +82,10 @@ int wmain(__in int argc, __in_ecount(argc) wchar_t* argv[])
{ {
return SystemInfo(); return SystemInfo();
} }
else if (wcscmp(L"service", cmd) == 0)
{
return RunService(argc - 1, argv + 1);
}
else if (wcscmp(L"help", cmd) == 0) else if (wcscmp(L"help", cmd) == 0)
{ {
Usage(argv[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."); fwprintf(stdout, L"%-15s%s\n\n", L"task", L"Task operations.");
TaskUsage(); TaskUsage();
fwprintf(stdout, L"%-15s%s\n\n", L"service", L"Service operations.");
ServiceUsage();
fwprintf(stdout, L"\n\n"); fwprintf(stdout, L"\n\n");
} }

File diff suppressed because it is too large Load Diff

View File

@ -19,15 +19,18 @@
#include <errno.h> #include <errno.h>
#include <psapi.h> #include <psapi.h>
#include <malloc.h> #include <malloc.h>
#include <authz.h>
#include <sddl.h>
#define PSAPI_VERSION 1 #define PSAPI_VERSION 1
#pragma comment(lib, "psapi.lib") #pragma comment(lib, "psapi.lib")
#define ERROR_TASK_NOT_ALIVE 1 #define NM_WSCE_IMPERSONATE_ALLOWED L"yarn.nodemanager.windows-secure-container-executor.impersonate.allowed"
#define NM_WSCE_IMPERSONATE_DENIED L"yarn.nodemanager.windows-secure-container-executor.impersonate.denied"
// The S4U impersonation access check mask. Arbitrary value (we use 1 for the service access check)
#define SERVICE_IMPERSONATE_MASK 0x00000002
// This exit code for killed processes is compatible with Unix, where a killed
// process exits with 128 + signal. For SIGKILL, this would be 128 + 9 = 137.
#define KILLED_PROCESS_EXIT_CODE 137
// Name for tracking this logon process when registering with LSA // Name for tracking this logon process when registering with LSA
static const char *LOGON_PROCESS_NAME="Hadoop Container Executor"; static const char *LOGON_PROCESS_NAME="Hadoop Container Executor";
@ -104,6 +107,459 @@ static BOOL ParseCommandLine(__in int argc,
return FALSE; return FALSE;
} }
//----------------------------------------------------------------------------
// Function: BuildImpersonateSecurityDescriptor
//
// Description:
// Builds the security descriptor for the S4U impersonation permissions
// This describes what users can be impersonated and what not
//
// Returns:
// ERROR_SUCCESS: On success
// GetLastError: otherwise
//
DWORD BuildImpersonateSecurityDescriptor(__out PSECURITY_DESCRIPTOR* ppSD) {
DWORD dwError = ERROR_SUCCESS;
size_t countAllowed = 0;
PSID* allowedSids = NULL;
size_t countDenied = 0;
PSID* deniedSids = NULL;
LPCWSTR value = NULL;
WCHAR** tokens = NULL;
size_t len = 0;
size_t count = 0;
int crt = 0;
PSECURITY_DESCRIPTOR pSD = NULL;
dwError = GetConfigValue(wsceConfigRelativePath, NM_WSCE_IMPERSONATE_ALLOWED, &len, &value);
if (dwError) {
ReportErrorCode(L"GetConfigValue:1", dwError);
goto done;
}
if (0 == len) {
dwError = ERROR_BAD_CONFIGURATION;
ReportErrorCode(L"GetConfigValue:2", dwError);
goto done;
}
dwError = SplitStringIgnoreSpaceW(len, value, L',', &count, &tokens);
if (dwError) {
ReportErrorCode(L"SplitStringIgnoreSpaceW:1", dwError);
goto done;
}
allowedSids = LocalAlloc(LPTR, sizeof(PSID) * count);
if (NULL == allowedSids) {
dwError = GetLastError();
ReportErrorCode(L"LocalAlloc:1", dwError);
goto done;
}
for(crt = 0; crt < count; ++crt) {
dwError = GetSidFromAcctNameW(tokens[crt], &allowedSids[crt]);
if (dwError) {
ReportErrorCode(L"GetSidFromAcctNameW:1", dwError);
goto done;
}
}
countAllowed = count;
LocalFree(tokens);
tokens = NULL;
LocalFree(value);
value = NULL;
dwError = GetConfigValue(wsceConfigRelativePath, NM_WSCE_IMPERSONATE_DENIED, &len, &value);
if (dwError) {
ReportErrorCode(L"GetConfigValue:3", dwError);
goto done;
}
if (0 != len) {
dwError = SplitStringIgnoreSpaceW(len, value, L',', &count, &tokens);
if (dwError) {
ReportErrorCode(L"SplitStringIgnoreSpaceW:2", dwError);
goto done;
}
deniedSids = LocalAlloc(LPTR, sizeof(PSID) * count);
if (NULL == allowedSids) {
dwError = GetLastError();
ReportErrorCode(L"LocalAlloc:2", dwError);
goto done;
}
for(crt = 0; crt < count; ++crt) {
dwError = GetSidFromAcctNameW(tokens[crt], &deniedSids[crt]);
if (dwError) {
ReportErrorCode(L"GetSidFromAcctNameW:2", dwError);
goto done;
}
}
countDenied = count;
}
dwError = BuildServiceSecurityDescriptor(
SERVICE_IMPERSONATE_MASK,
countAllowed, allowedSids,
countDenied, deniedSids,
NULL,
&pSD);
if (dwError) {
ReportErrorCode(L"BuildServiceSecurityDescriptor", dwError);
goto done;
}
*ppSD = pSD;
pSD = NULL;
done:
if (pSD) LocalFree(pSD);
if (tokens) LocalFree(tokens);
if (allowedSids) LocalFree(allowedSids);
if (deniedSids) LocalFree(deniedSids);
return dwError;
}
//----------------------------------------------------------------------------
// Function: AddNodeManagerAndUserACEsToObject
//
// Description:
// Adds ACEs to grant NM and user the provided access mask over a given handle
//
// Returns:
// ERROR_SUCCESS: on success
//
DWORD AddNodeManagerAndUserACEsToObject(
__in HANDLE hObject,
__in LPWSTR user,
__in ACCESS_MASK accessMask) {
DWORD dwError = ERROR_SUCCESS;
int countTokens = 0;
size_t len = 0;
LPCWSTR value = NULL;
WCHAR** tokens = NULL;
int crt = 0;
PACL pDacl = NULL;
PSECURITY_DESCRIPTOR psdProcess = NULL;
LPSTR lpszOldDacl = NULL, lpszNewDacl = NULL;
ULONG daclLen = 0;
PACL pNewDacl = NULL;
ACL_SIZE_INFORMATION si;
DWORD dwNewAclSize = 0;
PACE_HEADER pTempAce = NULL;
BYTE sidTemp[SECURITY_MAX_SID_SIZE];
DWORD cbSid = SECURITY_MAX_SID_SIZE;
PSID tokenSid = NULL;
// These hard-coded SIDs are allways added
WELL_KNOWN_SID_TYPE forcesSidTypes[] = {
WinLocalSystemSid,
WinBuiltinAdministratorsSid};
BOOL logSDs = IsDebuggerPresent(); // Check only once to avoid attach-while-running
dwError = GetSecurityInfo(hObject,
SE_KERNEL_OBJECT,
DACL_SECURITY_INFORMATION,
NULL,
NULL,
&pDacl,
NULL,
&psdProcess);
if (dwError) {
ReportErrorCode(L"GetSecurityInfo", dwError);
goto done;
}
// This is debug only output for troubleshooting
if (logSDs) {
if (!ConvertSecurityDescriptorToStringSecurityDescriptor(
psdProcess,
SDDL_REVISION_1,
DACL_SECURITY_INFORMATION,
&lpszOldDacl,
&daclLen)) {
dwError = GetLastError();
ReportErrorCode(L"ConvertSecurityDescriptorToStringSecurityDescriptor", dwError);
goto done;
}
}
ZeroMemory(&si, sizeof(si));
if (!GetAclInformation(pDacl, &si, sizeof(si), AclSizeInformation)) {
dwError = GetLastError();
ReportErrorCode(L"GetAclInformation", dwError);
goto done;
}
dwError = GetConfigValue(wsceConfigRelativePath, NM_WSCE_ALLOWED, &len, &value);
if (ERROR_SUCCESS != dwError) {
ReportErrorCode(L"GetConfigValue", dwError);
goto done;
}
if (0 == len) {
dwError = ERROR_BAD_CONFIGURATION;
ReportErrorCode(L"GetConfigValue", dwError);
goto done;
}
dwError = SplitStringIgnoreSpaceW(len, value, L',', &countTokens, &tokens);
if (ERROR_SUCCESS != dwError) {
ReportErrorCode(L"SplitStringIgnoreSpaceW", dwError);
goto done;
}
// We're gonna add 1 ACE for each token found, +1 for user and +1 for each forcesSidTypes[]
// ACCESS_ALLOWED_ACE struct contains the first DWORD of the SID
//
dwNewAclSize = si.AclBytesInUse +
(countTokens + 1 + sizeof(forcesSidTypes)/sizeof(forcesSidTypes[0])) *
(sizeof(ACCESS_ALLOWED_ACE) + SECURITY_MAX_SID_SIZE - sizeof(DWORD));
pNewDacl = (PSID) LocalAlloc(LPTR, dwNewAclSize);
if (!pNewDacl) {
dwError = ERROR_OUTOFMEMORY;
ReportErrorCode(L"LocalAlloc", dwError);
goto done;
}
if (!InitializeAcl(pNewDacl, dwNewAclSize, ACL_REVISION)) {
dwError = ERROR_OUTOFMEMORY;
ReportErrorCode(L"InitializeAcl", dwError);
goto done;
}
// Copy over old ACEs
for (crt = 0; crt < si.AceCount; ++crt) {
if (!GetAce(pDacl, crt, &pTempAce)) {
dwError = ERROR_OUTOFMEMORY;
ReportErrorCode(L"InitializeAcl", dwError);
goto done;
}
if (!AddAce(pNewDacl, ACL_REVISION, MAXDWORD, pTempAce, pTempAce->AceSize)) {
dwError = ERROR_OUTOFMEMORY;
ReportErrorCode(L"InitializeAcl", dwError);
goto done;
}
}
// Add the configured allowed SIDs
for (crt = 0; crt < countTokens; ++crt) {
dwError = GetSidFromAcctNameW(tokens[crt], &tokenSid);
if (ERROR_SUCCESS != dwError) {
ReportErrorCode(L"GetSidFromAcctNameW", dwError);
goto done;
}
if (!AddAccessAllowedAceEx(
pNewDacl,
ACL_REVISION_DS,
CONTAINER_INHERIT_ACE | OBJECT_INHERIT_ACE,
PROCESS_ALL_ACCESS,
tokenSid)) {
dwError = GetLastError();
ReportErrorCode(L"AddAccessAllowedAceEx:1", dwError);
goto done;
}
LocalFree(tokenSid);
tokenSid = NULL;
}
// add the forced SIDs ACE
for (crt = 0; crt < sizeof(forcesSidTypes)/sizeof(forcesSidTypes[0]); ++crt) {
cbSid = SECURITY_MAX_SID_SIZE;
if (!CreateWellKnownSid(forcesSidTypes[crt], NULL, &sidTemp, &cbSid)) {
dwError = GetLastError();
ReportErrorCode(L"CreateWellKnownSid", dwError);
goto done;
}
if (!AddAccessAllowedAceEx(
pNewDacl,
ACL_REVISION_DS,
CONTAINER_INHERIT_ACE | OBJECT_INHERIT_ACE,
accessMask,
(PSID) sidTemp)) {
dwError = GetLastError();
ReportErrorCode(L"AddAccessAllowedAceEx:2", dwError);
goto done;
}
}
// add the user ACE
dwError = GetSidFromAcctNameW(user, &tokenSid);
if (ERROR_SUCCESS != dwError) {
ReportErrorCode(L"GetSidFromAcctNameW:user", dwError);
goto done;
}
if (!AddAccessAllowedAceEx(
pNewDacl,
ACL_REVISION_DS,
CONTAINER_INHERIT_ACE | OBJECT_INHERIT_ACE,
PROCESS_ALL_ACCESS,
tokenSid)) {
dwError = GetLastError();
ReportErrorCode(L"AddAccessAllowedAceEx:3", dwError);
goto done;
}
LocalFree(tokenSid);
tokenSid = NULL;
dwError = SetSecurityInfo(hObject,
SE_KERNEL_OBJECT,
DACL_SECURITY_INFORMATION,
NULL,
NULL,
pNewDacl,
NULL);
if (dwError) {
ReportErrorCode(L"SetSecurityInfo", dwError);
goto done;
}
// This is debug only output for troubleshooting
if (logSDs) {
dwError = GetSecurityInfo(hObject,
SE_KERNEL_OBJECT,
DACL_SECURITY_INFORMATION,
NULL,
NULL,
&pDacl,
NULL,
&psdProcess);
if (dwError) {
ReportErrorCode(L"GetSecurityInfo:2", dwError);
goto done;
}
if (!ConvertSecurityDescriptorToStringSecurityDescriptor(
psdProcess,
SDDL_REVISION_1,
DACL_SECURITY_INFORMATION,
&lpszNewDacl,
&daclLen)) {
dwError = GetLastError();
ReportErrorCode(L"ConvertSecurityDescriptorToStringSecurityDescriptor:2", dwError);
goto done;
}
LogDebugMessage(L"Old DACL: %s\nNew DACL: %s\n", lpszOldDacl, lpszNewDacl);
}
done:
if (tokenSid) LocalFree(tokenSid);
if (pNewDacl) LocalFree(pNewDacl);
if (lpszOldDacl) LocalFree(lpszOldDacl);
if (lpszNewDacl) LocalFree(lpszNewDacl);
if (psdProcess) LocalFree(psdProcess);
return dwError;
}
//----------------------------------------------------------------------------
// Function: ValidateImpersonateAccessCheck
//
// Description:
// Performs the access check for S4U impersonation
//
// Returns:
// ERROR_SUCCESS: On success
// ERROR_ACCESS_DENIED, GetLastError: otherwise
//
DWORD ValidateImpersonateAccessCheck(__in HANDLE logonHandle) {
DWORD dwError = ERROR_SUCCESS;
PSECURITY_DESCRIPTOR pSD = NULL;
LUID luidUnused;
AUTHZ_ACCESS_REQUEST request;
AUTHZ_ACCESS_REPLY reply;
DWORD authError = ERROR_SUCCESS;
DWORD saclResult = 0;
ACCESS_MASK grantedMask = 0;
AUTHZ_RESOURCE_MANAGER_HANDLE hManager = NULL;
AUTHZ_CLIENT_CONTEXT_HANDLE hAuthzToken = NULL;
ZeroMemory(&luidUnused, sizeof(luidUnused));
ZeroMemory(&request, sizeof(request));
ZeroMemory(&reply, sizeof(reply));
dwError = BuildImpersonateSecurityDescriptor(&pSD);
if (dwError) {
ReportErrorCode(L"BuildImpersonateSecurityDescriptor", dwError);
goto done;
}
request.DesiredAccess = MAXIMUM_ALLOWED;
reply.Error = &authError;
reply.SaclEvaluationResults = &saclResult;
reply.ResultListLength = 1;
reply.GrantedAccessMask = &grantedMask;
if (!AuthzInitializeResourceManager(
AUTHZ_RM_FLAG_NO_AUDIT,
NULL, // pfnAccessCheck
NULL, // pfnComputeDynamicGroups
NULL, // pfnFreeDynamicGroups
NULL, // szResourceManagerName
&hManager)) {
dwError = GetLastError();
ReportErrorCode(L"AuthzInitializeResourceManager", dwError);
goto done;
}
if (!AuthzInitializeContextFromToken(
0,
logonHandle,
hManager,
NULL, // expiration time
luidUnused, // not used
NULL, // callback args
&hAuthzToken)) {
dwError = GetLastError();
ReportErrorCode(L"AuthzInitializeContextFromToken", dwError);
goto done;
}
if (!AuthzAccessCheck(
0,
hAuthzToken,
&request,
NULL, // AuditEvent
pSD,
NULL, // OptionalSecurityDescriptorArray
0, // OptionalSecurityDescriptorCount
&reply,
NULL // phAccessCheckResults
)) {
dwError = GetLastError();
ReportErrorCode(L"AuthzAccessCheck", dwError);
goto done;
}
LogDebugMessage(L"AutzAccessCheck: Error:%d sacl:%d access:%d\n",
authError, saclResult, grantedMask);
if (authError != ERROR_SUCCESS) {
ReportErrorCode(L"AuthzAccessCheck:REPLY:1", authError);
dwError = authError;
}
else if (!(grantedMask & SERVICE_IMPERSONATE_MASK)) {
ReportErrorCode(L"AuthzAccessCheck:REPLY:2", ERROR_ACCESS_DENIED);
dwError = ERROR_ACCESS_DENIED;
}
done:
if (hAuthzToken) AuthzFreeContext(hAuthzToken);
if (hManager) AuthzFreeResourceManager(hManager);
if (pSD) LocalFree(pSD);
return dwError;
}
//---------------------------------------------------------------------------- //----------------------------------------------------------------------------
// Function: CreateTaskImpl // Function: CreateTaskImpl
// //
@ -116,7 +572,8 @@ static BOOL ParseCommandLine(__in int argc,
// Returns: // Returns:
// ERROR_SUCCESS: On success // ERROR_SUCCESS: On success
// GetLastError: otherwise // GetLastError: otherwise
DWORD CreateTaskImpl(__in_opt HANDLE logonHandle, __in PCWSTR jobObjName,__in PWSTR cmdLine) DWORD CreateTaskImpl(__in_opt HANDLE logonHandle, __in PCWSTR jobObjName,__in PCWSTR cmdLine,
__in LPCWSTR userName)
{ {
DWORD dwErrorCode = ERROR_SUCCESS; DWORD dwErrorCode = ERROR_SUCCESS;
DWORD exitCode = EXIT_FAILURE; DWORD exitCode = EXIT_FAILURE;
@ -126,19 +583,36 @@ DWORD CreateTaskImpl(__in_opt HANDLE logonHandle, __in PCWSTR jobObjName,__in PW
HANDLE jobObject = NULL; HANDLE jobObject = NULL;
JOBOBJECT_EXTENDED_LIMIT_INFORMATION jeli = { 0 }; JOBOBJECT_EXTENDED_LIMIT_INFORMATION jeli = { 0 };
void * envBlock = NULL; void * envBlock = NULL;
BOOL createProcessResult = FALSE; WCHAR secureJobNameBuffer[MAX_PATH];
LPCWSTR secureJobName = jobObjName;
wchar_t* curr_dir = NULL; wchar_t* curr_dir = NULL;
FILE *stream = NULL; FILE *stream = NULL;
if (NULL != logonHandle) {
dwErrorCode = ValidateImpersonateAccessCheck(logonHandle);
if (dwErrorCode) {
ReportErrorCode(L"ValidateImpersonateAccessCheck", dwErrorCode);
return dwErrorCode;
}
dwErrorCode = GetSecureJobObjectName(jobObjName, MAX_PATH, secureJobNameBuffer);
if (dwErrorCode) {
ReportErrorCode(L"GetSecureJobObjectName", dwErrorCode);
return dwErrorCode;
}
secureJobName = secureJobNameBuffer;
}
// Create un-inheritable job object handle and set job object to terminate // Create un-inheritable job object handle and set job object to terminate
// when last handle is closed. So winutils.exe invocation has the only open // when last handle is closed. So winutils.exe invocation has the only open
// job object handle. Exit of winutils.exe ensures termination of job object. // job object handle. Exit of winutils.exe ensures termination of job object.
// Either a clean exit of winutils or crash or external termination. // Either a clean exit of winutils or crash or external termination.
jobObject = CreateJobObject(NULL, jobObjName); jobObject = CreateJobObject(NULL, secureJobName);
dwErrorCode = GetLastError(); dwErrorCode = GetLastError();
if(jobObject == NULL || dwErrorCode == ERROR_ALREADY_EXISTS) if(jobObject == NULL || dwErrorCode == ERROR_ALREADY_EXISTS)
{ {
ReportErrorCode(L"CreateJobObject", dwErrorCode);
return dwErrorCode; return dwErrorCode;
} }
jeli.BasicLimitInformation.LimitFlags = JOB_OBJECT_LIMIT_KILL_ON_JOB_CLOSE; jeli.BasicLimitInformation.LimitFlags = JOB_OBJECT_LIMIT_KILL_ON_JOB_CLOSE;
@ -148,6 +622,14 @@ DWORD CreateTaskImpl(__in_opt HANDLE logonHandle, __in PCWSTR jobObjName,__in PW
sizeof(jeli)) == 0) sizeof(jeli)) == 0)
{ {
dwErrorCode = GetLastError(); dwErrorCode = GetLastError();
ReportErrorCode(L"SetInformationJobObject", dwErrorCode);
CloseHandle(jobObject);
return dwErrorCode;
}
dwErrorCode = AddNodeManagerAndUserACEsToObject(jobObject, userName, JOB_OBJECT_ALL_ACCESS);
if (dwErrorCode) {
ReportErrorCode(L"AddNodeManagerAndUserACEsToObject", dwErrorCode);
CloseHandle(jobObject); CloseHandle(jobObject);
return dwErrorCode; return dwErrorCode;
} }
@ -155,6 +637,7 @@ DWORD CreateTaskImpl(__in_opt HANDLE logonHandle, __in PCWSTR jobObjName,__in PW
if(AssignProcessToJobObject(jobObject, GetCurrentProcess()) == 0) if(AssignProcessToJobObject(jobObject, GetCurrentProcess()) == 0)
{ {
dwErrorCode = GetLastError(); dwErrorCode = GetLastError();
ReportErrorCode(L"AssignProcessToJobObject", dwErrorCode);
CloseHandle(jobObject); CloseHandle(jobObject);
return dwErrorCode; return dwErrorCode;
} }
@ -164,6 +647,7 @@ DWORD CreateTaskImpl(__in_opt HANDLE logonHandle, __in PCWSTR jobObjName,__in PW
if(SetEnvironmentVariable(L"JVM_PID", jobObjName) == 0) if(SetEnvironmentVariable(L"JVM_PID", jobObjName) == 0)
{ {
dwErrorCode = GetLastError(); dwErrorCode = GetLastError();
ReportErrorCode(L"SetEnvironmentVariable", dwErrorCode);
// We have to explictly Terminate, passing in the error code // We have to explictly Terminate, passing in the error code
// simply closing the job would kill our own process with success exit status // simply closing the job would kill our own process with success exit status
TerminateJobObject(jobObject, dwErrorCode); TerminateJobObject(jobObject, dwErrorCode);
@ -180,6 +664,7 @@ DWORD CreateTaskImpl(__in_opt HANDLE logonHandle, __in PCWSTR jobObjName,__in PW
logonHandle, logonHandle,
TRUE )) { TRUE )) {
dwErrorCode = GetLastError(); dwErrorCode = GetLastError();
ReportErrorCode(L"CreateEnvironmentBlock", dwErrorCode);
// We have to explictly Terminate, passing in the error code // We have to explictly Terminate, passing in the error code
// simply closing the job would kill our own process with success exit status // simply closing the job would kill our own process with success exit status
TerminateJobObject(jobObject, dwErrorCode); TerminateJobObject(jobObject, dwErrorCode);
@ -197,14 +682,17 @@ DWORD CreateTaskImpl(__in_opt HANDLE logonHandle, __in PCWSTR jobObjName,__in PW
if (0 == currDirCnt) { if (0 == currDirCnt) {
dwErrorCode = GetLastError(); dwErrorCode = GetLastError();
ReportErrorCode(L"GetCurrentDirectory", dwErrorCode);
// We have to explictly Terminate, passing in the error code // We have to explictly Terminate, passing in the error code
// simply closing the job would kill our own process with success exit status // simply closing the job would kill our own process with success exit status
TerminateJobObject(jobObject, dwErrorCode); TerminateJobObject(jobObject, dwErrorCode);
return dwErrorCode; return dwErrorCode;
} }
dwErrorCode = ERROR_SUCCESS;
if (logonHandle == NULL) { if (logonHandle == NULL) {
createProcessResult = CreateProcess( if (!CreateProcess(
NULL, // ApplicationName NULL, // ApplicationName
cmdLine, // command line cmdLine, // command line
NULL, // process security attributes NULL, // process security attributes
@ -214,25 +702,52 @@ DWORD CreateTaskImpl(__in_opt HANDLE logonHandle, __in PCWSTR jobObjName,__in PW
NULL, // environment NULL, // environment
curr_dir, // current directory curr_dir, // current directory
&si, // startup info &si, // startup info
&pi); // process info &pi)) { // process info
dwErrorCode = GetLastError();
ReportErrorCode(L"CreateProcess", dwErrorCode);
} }
else { goto create_process_done;
createProcessResult = CreateProcessAsUser( }
// From here on is the secure S4U implementation for CreateProcessAsUser
// We desire to grant process access to NM so that it can interogate process status
// and resource utilization. Passing in a security descriptor though results in the
// S4U privilege checks being done against that SD and CreateProcessAsUser fails.
// So instead we create the process suspended and then we add the desired ACEs.
//
if (!CreateProcessAsUser(
logonHandle, // logon token handle logonHandle, // logon token handle
NULL, // Application handle NULL, // Application handle
cmdLine, // command line cmdLine, // command line
NULL, // process security attributes NULL, // process security attributes
NULL, // thread security attributes NULL, // thread security attributes
FALSE, // inherit handles FALSE, // inherit handles
CREATE_UNICODE_ENVIRONMENT, // creation flags CREATE_UNICODE_ENVIRONMENT | CREATE_SUSPENDED, // creation flags
envBlock, // environment envBlock, // environment
curr_dir, // current directory curr_dir, // current directory
&si, // startup info &si, // startup info
&pi); // process info &pi)) { // process info
dwErrorCode = GetLastError();
ReportErrorCode(L"CreateProcessAsUser", dwErrorCode);
goto create_process_done;
} }
if (FALSE == createProcessResult) { dwErrorCode = AddNodeManagerAndUserACEsToObject(pi.hProcess, userName, PROCESS_ALL_ACCESS);
if (dwErrorCode) {
ReportErrorCode(L"AddNodeManagerAndUserACEsToObject", dwErrorCode);
goto create_process_done;
}
if (-1 == ResumeThread(pi.hThread)) {
dwErrorCode = GetLastError(); dwErrorCode = GetLastError();
ReportErrorCode(L"ResumeThread", dwErrorCode);
goto create_process_done;
}
create_process_done:
if (dwErrorCode) {
if( envBlock != NULL ) { if( envBlock != NULL ) {
DestroyEnvironmentBlock( envBlock ); DestroyEnvironmentBlock( envBlock );
envBlock = NULL; envBlock = NULL;
@ -293,10 +808,11 @@ DWORD CreateTaskImpl(__in_opt HANDLE logonHandle, __in PCWSTR jobObjName,__in PW
DWORD CreateTask(__in PCWSTR jobObjName,__in PWSTR cmdLine) DWORD CreateTask(__in PCWSTR jobObjName,__in PWSTR cmdLine)
{ {
// call with null logon in order to create tasks utilizing the current logon // call with null logon in order to create tasks utilizing the current logon
return CreateTaskImpl( NULL, jobObjName, cmdLine ); return CreateTaskImpl( NULL, jobObjName, cmdLine, NULL);
} }
//---------------------------------------------------------------------------- //----------------------------------------------------------------------------
// Function: CreateTask // Function: CreateTaskAsUser
// //
// Description: // Description:
// Creates a task via a jobobject. Outputs the // Creates a task via a jobobject. Outputs the
@ -305,7 +821,8 @@ DWORD CreateTask(__in PCWSTR jobObjName,__in PWSTR cmdLine)
// Returns: // Returns:
// ERROR_SUCCESS: On success // ERROR_SUCCESS: On success
// GetLastError: otherwise // GetLastError: otherwise
DWORD CreateTaskAsUser(__in PCWSTR jobObjName,__in PWSTR user, __in PWSTR pidFilePath, __in PWSTR cmdLine) DWORD CreateTaskAsUser(__in PCWSTR jobObjName,
__in PCWSTR user, __in PCWSTR pidFilePath, __in PCWSTR cmdLine)
{ {
DWORD err = ERROR_SUCCESS; DWORD err = ERROR_SUCCESS;
DWORD exitCode = EXIT_FAILURE; DWORD exitCode = EXIT_FAILURE;
@ -314,53 +831,50 @@ DWORD CreateTaskAsUser(__in PCWSTR jobObjName,__in PWSTR user, __in PWSTR pidFil
PROFILEINFO pi; PROFILEINFO pi;
BOOL profileIsLoaded = FALSE; BOOL profileIsLoaded = FALSE;
FILE* pidFile = NULL; FILE* pidFile = NULL;
DWORD retLen = 0; DWORD retLen = 0;
HANDLE logonHandle = NULL; HANDLE logonHandle = NULL;
err = EnablePrivilege(SE_TCB_NAME); err = EnableImpersonatePrivileges();
if( err != ERROR_SUCCESS ) { if( err != ERROR_SUCCESS ) {
fwprintf(stdout, L"INFO: The user does not have SE_TCB_NAME.\n"); ReportErrorCode(L"EnableImpersonatePrivileges", err);
goto done;
}
err = EnablePrivilege(SE_ASSIGNPRIMARYTOKEN_NAME);
if( err != ERROR_SUCCESS ) {
fwprintf(stdout, L"INFO: The user does not have SE_ASSIGNPRIMARYTOKEN_NAME.\n");
goto done;
}
err = EnablePrivilege(SE_INCREASE_QUOTA_NAME);
if( err != ERROR_SUCCESS ) {
fwprintf(stdout, L"INFO: The user does not have SE_INCREASE_QUOTA_NAME.\n");
goto done;
}
err = EnablePrivilege(SE_RESTORE_NAME);
if( err != ERROR_SUCCESS ) {
fwprintf(stdout, L"INFO: The user does not have SE_RESTORE_NAME.\n");
goto done; goto done;
} }
err = RegisterWithLsa(LOGON_PROCESS_NAME ,&lsaHandle); err = RegisterWithLsa(LOGON_PROCESS_NAME ,&lsaHandle);
if( err != ERROR_SUCCESS ) goto done; if( err != ERROR_SUCCESS ) {
ReportErrorCode(L"RegisterWithLsa", err);
goto done;
}
err = LookupKerberosAuthenticationPackageId( lsaHandle, &authnPkgId ); err = LookupKerberosAuthenticationPackageId( lsaHandle, &authnPkgId );
if( err != ERROR_SUCCESS ) goto done; if( err != ERROR_SUCCESS ) {
ReportErrorCode(L"LookupKerberosAuthenticationPackageId", err);
goto done;
}
err = CreateLogonForUser(lsaHandle, err = CreateLogonTokenForUser(lsaHandle,
LOGON_PROCESS_NAME, LOGON_PROCESS_NAME,
TOKEN_SOURCE_NAME, TOKEN_SOURCE_NAME,
authnPkgId, authnPkgId,
user, user,
&logonHandle); &logonHandle);
if( err != ERROR_SUCCESS ) goto done; if( err != ERROR_SUCCESS ) {
ReportErrorCode(L"CreateLogonTokenForUser", err);
goto done;
}
err = LoadUserProfileForLogon(logonHandle, &pi); err = LoadUserProfileForLogon(logonHandle, &pi);
if( err != ERROR_SUCCESS ) goto done; if( err != ERROR_SUCCESS ) {
ReportErrorCode(L"LoadUserProfileForLogon", err);
goto done;
}
profileIsLoaded = TRUE; profileIsLoaded = TRUE;
// Create the PID file // Create the PID file
if (!(pidFile = _wfopen(pidFilePath, "w"))) { if (!(pidFile = _wfopen(pidFilePath, "w"))) {
err = GetLastError(); err = GetLastError();
ReportErrorCode(L"_wfopen:pidFilePath", err);
goto done; goto done;
} }
@ -371,10 +885,11 @@ DWORD CreateTaskAsUser(__in PCWSTR jobObjName,__in PWSTR user, __in PWSTR pidFil
fclose(pidFile); fclose(pidFile);
if (err != ERROR_SUCCESS) { if (err != ERROR_SUCCESS) {
ReportErrorCode(L"fprintf_s:pidFilePath", err);
goto done; goto done;
} }
err = CreateTaskImpl(logonHandle, jobObjName, cmdLine); err = CreateTaskImpl(logonHandle, jobObjName, cmdLine, user);
done: done:
if( profileIsLoaded ) { if( profileIsLoaded ) {
@ -392,7 +907,6 @@ done:
return err; return err;
} }
//---------------------------------------------------------------------------- //----------------------------------------------------------------------------
// Function: IsTaskAlive // Function: IsTaskAlive
// //
@ -408,10 +922,27 @@ DWORD IsTaskAlive(const WCHAR* jobObjName, int* isAlive, int* procsInJob)
PJOBOBJECT_BASIC_PROCESS_ID_LIST procList; PJOBOBJECT_BASIC_PROCESS_ID_LIST procList;
HANDLE jobObject = NULL; HANDLE jobObject = NULL;
int numProcs = 100; int numProcs = 100;
WCHAR secureJobNameBuffer[MAX_PATH];
*isAlive = FALSE; *isAlive = FALSE;
jobObject = OpenJobObject(JOB_OBJECT_QUERY, FALSE, jobObjName); jobObject = OpenJobObject(JOB_OBJECT_QUERY, FALSE, jobObjName);
if(jobObject == NULL)
{
// Try Global\...
DWORD err = GetSecureJobObjectName(jobObjName, MAX_PATH, secureJobNameBuffer);
if (err) {
ReportErrorCode(L"GetSecureJobObjectName", err);
return err;
}
jobObject = OpenJobObject(JOB_OBJECT_QUERY, FALSE, secureJobNameBuffer);
}
if(jobObject == NULL)
{
DWORD err = GetLastError();
return err;
}
if(jobObject == NULL) if(jobObject == NULL)
{ {
@ -453,39 +984,6 @@ DWORD IsTaskAlive(const WCHAR* jobObjName, int* isAlive, int* procsInJob)
return ERROR_SUCCESS; return ERROR_SUCCESS;
} }
//----------------------------------------------------------------------------
// 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)
{
HANDLE jobObject = OpenJobObject(JOB_OBJECT_TERMINATE, FALSE, jobObjName);
if(jobObject == NULL)
{
DWORD err = GetLastError();
if(err == ERROR_FILE_NOT_FOUND)
{
// job object does not exist. assume its not alive
return ERROR_SUCCESS;
}
return err;
}
if(TerminateJobObject(jobObject, KILLED_PROCESS_EXIT_CODE) == 0)
{
return GetLastError();
}
CloseHandle(jobObject);
return ERROR_SUCCESS;
}
//---------------------------------------------------------------------------- //----------------------------------------------------------------------------
// Function: PrintTaskProcessList // Function: PrintTaskProcessList
// //
@ -500,7 +998,21 @@ DWORD PrintTaskProcessList(const WCHAR* jobObjName)
DWORD i; DWORD i;
PJOBOBJECT_BASIC_PROCESS_ID_LIST procList; PJOBOBJECT_BASIC_PROCESS_ID_LIST procList;
int numProcs = 100; int numProcs = 100;
HANDLE jobObject = OpenJobObject(JOB_OBJECT_QUERY, FALSE, jobObjName); WCHAR secureJobNameBuffer[MAX_PATH];
HANDLE jobObject = NULL;
jobObject = OpenJobObject(JOB_OBJECT_QUERY, FALSE, jobObjName);
if(jobObject == NULL)
{
// Try Global\...
DWORD err = GetSecureJobObjectName(jobObjName, MAX_PATH, secureJobNameBuffer);
if (err) {
ReportErrorCode(L"GetSecureJobObjectName", err);
return err;
}
jobObject = OpenJobObject(JOB_OBJECT_QUERY, FALSE, secureJobNameBuffer);
}
if(jobObject == NULL) if(jobObject == NULL)
{ {
DWORD err = GetLastError(); DWORD err = GetLastError();
@ -581,7 +1093,7 @@ int Task(__in int argc, __in_ecount(argc) wchar_t *argv[])
TaskCommandOption command = TaskInvalid; TaskCommandOption command = TaskInvalid;
wchar_t* cmdLine = NULL; wchar_t* cmdLine = NULL;
wchar_t buffer[16*1024] = L""; // 32K max command line wchar_t buffer[16*1024] = L""; // 32K max command line
size_t charCountBufferLeft = sizeof (buffer)/sizeof(wchar_t); size_t charCountBufferLeft = sizeof(buffer)/sizeof(wchar_t);
int crtArgIndex = 0; int crtArgIndex = 0;
size_t argLen = 0; size_t argLen = 0;
size_t wscatErr = 0; size_t wscatErr = 0;
@ -712,6 +1224,7 @@ int Task(__in int argc, __in_ecount(argc) wchar_t *argv[])
} }
TaskExit: TaskExit:
ReportErrorCode(L"TaskExit:", dwErrorCode);
return dwErrorCode; return dwErrorCode;
} }

View File

@ -0,0 +1,64 @@
;/*
; * 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.
; */
; // winutils.mc
; // EventLog messages for Hadoop winutils service.
LanguageNames=(English=0x409:MSG00409)
; // The following are the categories of events.
MessageIdTypedef=WORD
MessageId=0x1
SymbolicName=SERVICE_CATEGORY
Language=English
Service Events
.
MessageId=0x2
SymbolicName=LOG_CATEGORY
Language=English
Task Events
.
; // The following are the message definitions.
MessageIdTypedef=DWORD
MessageId=0x80
SymbolicName=MSG_CHECK_ERROR
Language=English
%1. Error %2: %3.
.
MessageId=0x100
SymbolicName=MSG_RPC_SERVICE_HAS_STARTED
Language=English
The LPC server is listenning.
.
MessageId=0x200
SymbolicName=MSG_RPC_SERVICE_HAS_STOPPED
Language=English
The LPC server has stopped listenning.
.

View File

@ -26,26 +26,16 @@ Project("{8BC9CEB8-8B4A-11D0-8D11-00A0C91BC942}") = "libwinutils", "libwinutils.
EndProject EndProject
Global Global
GlobalSection(SolutionConfigurationPlatforms) = preSolution GlobalSection(SolutionConfigurationPlatforms) = preSolution
Debug|Win32 = Debug|Win32
Debug|x64 = Debug|x64 Debug|x64 = Debug|x64
Release|Win32 = Release|Win32
Release|x64 = Release|x64 Release|x64 = Release|x64
EndGlobalSection EndGlobalSection
GlobalSection(ProjectConfigurationPlatforms) = postSolution GlobalSection(ProjectConfigurationPlatforms) = postSolution
{D94B3BD7-39CC-47A0-AE9A-353FDE506F33}.Debug|Win32.ActiveCfg = Debug|x64 {D94B3BD7-39CC-47A0-AE9A-353FDE506F33}.Debug|x64.ActiveCfg = Release|x64
{D94B3BD7-39CC-47A0-AE9A-353FDE506F33}.Debug|Win32.Build.0 = Debug|x64 {D94B3BD7-39CC-47A0-AE9A-353FDE506F33}.Debug|x64.Build.0 = Release|x64
{D94B3BD7-39CC-47A0-AE9A-353FDE506F33}.Debug|x64.ActiveCfg = Debug|x64
{D94B3BD7-39CC-47A0-AE9A-353FDE506F33}.Debug|x64.Build.0 = Debug|x64
{D94B3BD7-39CC-47A0-AE9A-353FDE506F33}.Release|Win32.ActiveCfg = Release|Win32
{D94B3BD7-39CC-47A0-AE9A-353FDE506F33}.Release|Win32.Build.0 = Release|Win32
{D94B3BD7-39CC-47A0-AE9A-353FDE506F33}.Release|x64.ActiveCfg = Release|x64 {D94B3BD7-39CC-47A0-AE9A-353FDE506F33}.Release|x64.ActiveCfg = Release|x64
{D94B3BD7-39CC-47A0-AE9A-353FDE506F33}.Release|x64.Build.0 = Release|x64 {D94B3BD7-39CC-47A0-AE9A-353FDE506F33}.Release|x64.Build.0 = Release|x64
{12131AA7-902E-4A6D-9CE3-043261D22A12}.Debug|Win32.ActiveCfg = Debug|x64 {12131AA7-902E-4A6D-9CE3-043261D22A12}.Debug|x64.ActiveCfg = Release|x64
{12131AA7-902E-4A6D-9CE3-043261D22A12}.Debug|Win32.Build.0 = Debug|x64 {12131AA7-902E-4A6D-9CE3-043261D22A12}.Debug|x64.Build.0 = Release|x64
{12131AA7-902E-4A6D-9CE3-043261D22A12}.Debug|x64.ActiveCfg = Debug|x64
{12131AA7-902E-4A6D-9CE3-043261D22A12}.Debug|x64.Build.0 = Debug|x64
{12131AA7-902E-4A6D-9CE3-043261D22A12}.Release|Win32.ActiveCfg = Release|Win32
{12131AA7-902E-4A6D-9CE3-043261D22A12}.Release|Win32.Build.0 = Release|Win32
{12131AA7-902E-4A6D-9CE3-043261D22A12}.Release|x64.ActiveCfg = Release|x64 {12131AA7-902E-4A6D-9CE3-043261D22A12}.Release|x64.ActiveCfg = Release|x64
{12131AA7-902E-4A6D-9CE3-043261D22A12}.Release|x64.Build.0 = Release|x64 {12131AA7-902E-4A6D-9CE3-043261D22A12}.Release|x64.Build.0 = Release|x64
EndGlobalSection EndGlobalSection

View File

@ -19,18 +19,10 @@
<Project DefaultTargets="Build" ToolsVersion="4.0" xmlns="http://schemas.microsoft.com/developer/msbuild/2003"> <Project DefaultTargets="Build" ToolsVersion="4.0" xmlns="http://schemas.microsoft.com/developer/msbuild/2003">
<ItemGroup Label="ProjectConfigurations"> <ItemGroup Label="ProjectConfigurations">
<ProjectConfiguration Include="Debug|Win32">
<Configuration>Debug</Configuration>
<Platform>Win32</Platform>
</ProjectConfiguration>
<ProjectConfiguration Include="Debug|x64"> <ProjectConfiguration Include="Debug|x64">
<Configuration>Debug</Configuration> <Configuration>Debug</Configuration>
<Platform>x64</Platform> <Platform>x64</Platform>
</ProjectConfiguration> </ProjectConfiguration>
<ProjectConfiguration Include="Release|Win32">
<Configuration>Release</Configuration>
<Platform>Win32</Platform>
</ProjectConfiguration>
<ProjectConfiguration Include="Release|x64"> <ProjectConfiguration Include="Release|x64">
<Configuration>Release</Configuration> <Configuration>Release</Configuration>
<Platform>x64</Platform> <Platform>x64</Platform>
@ -42,22 +34,11 @@
<RootNamespace>winutils</RootNamespace> <RootNamespace>winutils</RootNamespace>
</PropertyGroup> </PropertyGroup>
<Import Project="$(VCTargetsPath)\Microsoft.Cpp.Default.props" /> <Import Project="$(VCTargetsPath)\Microsoft.Cpp.Default.props" />
<PropertyGroup Condition="'$(Configuration)|$(Platform)'=='Debug|Win32'" Label="Configuration">
<ConfigurationType>Application</ConfigurationType>
<UseDebugLibraries>true</UseDebugLibraries>
<CharacterSet>Unicode</CharacterSet>
</PropertyGroup>
<PropertyGroup Condition="'$(Configuration)|$(Platform)'=='Debug|x64'" Label="Configuration"> <PropertyGroup Condition="'$(Configuration)|$(Platform)'=='Debug|x64'" Label="Configuration">
<ConfigurationType>Application</ConfigurationType> <ConfigurationType>Application</ConfigurationType>
<UseDebugLibraries>true</UseDebugLibraries> <UseDebugLibraries>true</UseDebugLibraries>
<CharacterSet>Unicode</CharacterSet> <CharacterSet>Unicode</CharacterSet>
</PropertyGroup> </PropertyGroup>
<PropertyGroup Condition="'$(Configuration)|$(Platform)'=='Release|Win32'" Label="Configuration">
<ConfigurationType>Application</ConfigurationType>
<UseDebugLibraries>false</UseDebugLibraries>
<WholeProgramOptimization>true</WholeProgramOptimization>
<CharacterSet>Unicode</CharacterSet>
</PropertyGroup>
<PropertyGroup Condition="'$(Configuration)|$(Platform)'=='Release|x64'" Label="Configuration"> <PropertyGroup Condition="'$(Configuration)|$(Platform)'=='Release|x64'" Label="Configuration">
<ConfigurationType>Application</ConfigurationType> <ConfigurationType>Application</ConfigurationType>
<UseDebugLibraries>false</UseDebugLibraries> <UseDebugLibraries>false</UseDebugLibraries>
@ -67,15 +48,9 @@
<Import Project="$(VCTargetsPath)\Microsoft.Cpp.props" /> <Import Project="$(VCTargetsPath)\Microsoft.Cpp.props" />
<ImportGroup Label="ExtensionSettings"> <ImportGroup Label="ExtensionSettings">
</ImportGroup> </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"> <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" /> <Import Project="$(UserRootDir)\Microsoft.Cpp.$(Platform).user.props" Condition="exists('$(UserRootDir)\Microsoft.Cpp.$(Platform).user.props')" Label="LocalAppDataPlatform" />
</ImportGroup> </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"> <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" /> <Import Project="$(UserRootDir)\Microsoft.Cpp.$(Platform).user.props" Condition="exists('$(UserRootDir)\Microsoft.Cpp.$(Platform).user.props')" Label="LocalAppDataPlatform" />
</ImportGroup> </ImportGroup>
@ -83,74 +58,32 @@
<PropertyGroup> <PropertyGroup>
<IncludePath>include;$(IncludePath)</IncludePath> <IncludePath>include;$(IncludePath)</IncludePath>
</PropertyGroup> </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'"> <PropertyGroup Condition="'$(Configuration)|$(Platform)'=='Release|x64'">
<LinkIncremental>false</LinkIncremental> <LinkIncremental>false</LinkIncremental>
<IntDir>..\..\..\target\winutils\$(Platform)\$(Configuration)\</IntDir>
<OutDir>..\..\..\target\bin\</OutDir>
</PropertyGroup> </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'"> <ItemDefinitionGroup Condition="'$(Configuration)|$(Platform)'=='Debug|x64'">
<ClCompile> <ClCompile>
<PrecompiledHeader> <PrecompiledHeader>
</PrecompiledHeader> </PrecompiledHeader>
<WarningLevel>Level4</WarningLevel> <WarningLevel>Level4</WarningLevel>
<Optimization>Disabled</Optimization> <Optimization>Disabled</Optimization>
<PreprocessorDefinitions>WIN32;_DEBUG;_CONSOLE;%(PreprocessorDefinitions)</PreprocessorDefinitions> <PreprocessorDefinitions>WIN32;_CONSOLE;_DEBUG;_UNICODE;UNICODE;WSCE_CONFIG_DIR=$(WsceConfigDir);WSCE_CONFIG_FILE=$(WsceConfigFile);%(PreprocessorDefinitions)</PreprocessorDefinitions>
</ClCompile> </ClCompile>
<Link> <Link>
<SubSystem>Console</SubSystem> <SubSystem>Console</SubSystem>
<GenerateDebugInformation>true</GenerateDebugInformation> <GenerateDebugInformation>true</GenerateDebugInformation>
</Link> </Link>
</ItemDefinitionGroup> </ItemDefinitionGroup>
<ItemDefinitionGroup Condition="'$(Configuration)|$(Platform)'=='Release|Win32'">
<ClCompile>
<WarningLevel>Level3</WarningLevel>
<PrecompiledHeader>
</PrecompiledHeader>
<Optimization>MaxSpeed</Optimization>
<FunctionLevelLinking>true</FunctionLevelLinking>
<IntrinsicFunctions>true</IntrinsicFunctions>
<PreprocessorDefinitions>WIN32;NDEBUG;_CONSOLE;%(PreprocessorDefinitions)</PreprocessorDefinitions>
</ClCompile>
<Link>
<SubSystem>Console</SubSystem>
<GenerateDebugInformation>true</GenerateDebugInformation>
<EnableCOMDATFolding>true</EnableCOMDATFolding>
<OptimizeReferences>true</OptimizeReferences>
</Link>
</ItemDefinitionGroup>
<ItemDefinitionGroup Condition="'$(Configuration)|$(Platform)'=='Release|x64'"> <ItemDefinitionGroup Condition="'$(Configuration)|$(Platform)'=='Release|x64'">
<ClCompile> <ClCompile>
<WarningLevel>Level3</WarningLevel> <WarningLevel>Level3</WarningLevel>
<PrecompiledHeader> <PrecompiledHeader>
</PrecompiledHeader> </PrecompiledHeader>
<Optimization>MaxSpeed</Optimization> <!-- <Optimization>MaxSpeed</Optimization> -->
<Optimization>Disabled</Optimization>
<FunctionLevelLinking>true</FunctionLevelLinking> <FunctionLevelLinking>true</FunctionLevelLinking>
<IntrinsicFunctions>true</IntrinsicFunctions> <IntrinsicFunctions>true</IntrinsicFunctions>
<PreprocessorDefinitions>WIN32;NDEBUG;_CONSOLE;%(PreprocessorDefinitions)</PreprocessorDefinitions> <PreprocessorDefinitions>WIN32;_CONSOLE;NDEBUG;_UNICODE;UNICODE;WSCE_CONFIG_DIR=$(WsceConfigDir);WSCE_CONFIG_FILE=$(WsceConfigFile);%(PreprocessorDefinitions)</PreprocessorDefinitions>
</ClCompile> </ClCompile>
<Link> <Link>
<SubSystem>Console</SubSystem> <SubSystem>Console</SubSystem>
@ -159,7 +92,40 @@
<OptimizeReferences>true</OptimizeReferences> <OptimizeReferences>true</OptimizeReferences>
</Link> </Link>
</ItemDefinitionGroup> </ItemDefinitionGroup>
<ItemDefinitionGroup>
<ClCompile>
<AdditionalIncludeDirectories>$(IntermediateOutputPath)</AdditionalIncludeDirectories>
</ClCompile>
<CustomBuildStep>
<Message>Compiling Messages</Message>
<Command>mc.exe $(TargetName).mc -z $(TargetName)_msg -r $(IntermediateOutputPath) -h $(IntermediateOutputPath) -U</Command>
<Outputs>$(IntermediateOutputPath)$(TargetName)_msg.rc;$(IntermediateOutputPath)$(TargetName)_msg.h</Outputs>
</CustomBuildStep>
<Midl>
<ApplicationConfigurationMode>true</ApplicationConfigurationMode>
<TargetEnvironment>X64</TargetEnvironment>
<OutputDirectory>$(IntermediateOutputPath)</OutputDirectory>
<GenerateStublessProxies>true</GenerateStublessProxies>
<ValidateAllParameters>true</ValidateAllParameters>
<WarnAsError>true</WarnAsError>
<WarningLevel>2</WarningLevel>
</Midl>
</ItemDefinitionGroup>
<PropertyGroup>
<CustomBuildAfterTargets>Midl</CustomBuildAfterTargets>
<CustomBuildBeforeTargets>ClCompile,ResourceCompile</CustomBuildBeforeTargets>
</PropertyGroup>
<ItemGroup> <ItemGroup>
<Midl Include="hadoopwinutilsvc.idl" />
</ItemGroup>
<ItemGroup>
<ResourceCompile Include="$(IntermediateOutputPath)$(TargetName)_msg.rc" />
</ItemGroup>
<ItemGroup>
<ClCompile Include="$(IntermediateOutputPath)\hadoopwinutilsvc_s.c" />
</ItemGroup>
<ItemGroup>
<ClCompile Include="service.c" />
<ClCompile Include="readlink.c" /> <ClCompile Include="readlink.c" />
<ClCompile Include="symlink.c" /> <ClCompile Include="symlink.c" />
<ClCompile Include="systeminfo.c" /> <ClCompile Include="systeminfo.c" />

View File

@ -296,7 +296,7 @@ public static boolean isAlive(String pid) {
return false; return false;
} catch (IOException ioe) { } catch (IOException ioe) {
LOG.warn("Error executing shell command " LOG.warn("Error executing shell command "
+ Arrays.toString(shexec.getExecString()) + ioe); + shexec.toString() + ioe);
return false; return false;
} }
return (shexec.getExitCode() == 0 ? true : false); return (shexec.getExitCode() == 0 ? true : false);
@ -321,7 +321,7 @@ public static boolean isProcessGroupAlive(String pgrpId) {
return false; return false;
} catch (IOException ioe) { } catch (IOException ioe) {
LOG.warn("Error executing shell command " LOG.warn("Error executing shell command "
+ Arrays.toString(shexec.getExecString()) + ioe); + shexec.toString() + ioe);
return false; return false;
} }
return (shexec.getExitCode() == 0 ? true : false); return (shexec.getExitCode() == 0 ? true : false);

View File

@ -151,6 +151,9 @@ Release 2.6.0 - UNRELEASED
YARN-2504. Enhanced RM Admin CLI to support management of node-labels. YARN-2504. Enhanced RM Admin CLI to support management of node-labels.
(Wangda Tan via vinodkv) (Wangda Tan via vinodkv)
YARN-2198. Remove the need to run NodeManager as privileged account for
Windows Secure Container Executor. (Remus Rusanu via jianhe)
IMPROVEMENTS IMPROVEMENTS
YARN-2242. Improve exception information on AM launch crashes. (Li Lu YARN-2242. Improve exception information on AM launch crashes. (Li Lu

View File

@ -79,19 +79,23 @@ public Configuration getConf() {
public abstract void init() throws IOException; public abstract void init() throws IOException;
/** /**
* On Windows the ContainerLaunch creates a temporary empty jar to workaround the CLASSPATH length * On Windows the ContainerLaunch creates a temporary special jar manifest of
* In a secure cluster this jar must be localized so that the container has access to it * other jars to workaround the CLASSPATH length. In a secure cluster this
* jar must be localized so that the container has access to it.
* This function localizes on-demand the jar. * This function localizes on-demand the jar.
* *
* @param classPathJar * @param classPathJar
* @param owner * @param owner
* @throws IOException * @throws IOException
*/ */
public void localizeClasspathJar(Path classPathJar, String owner) throws IOException { public Path localizeClasspathJar(Path classPathJar, Path pwd, String owner)
// For the default container this is a no-op throws IOException {
// The WindowsSecureContainerExecutor overrides this // Non-secure executor simply use the classpath created
// in the NM fprivate folder
return classPathJar;
} }
/** /**
* Prepare the environment for containers in this application to execute. * Prepare the environment for containers in this application to execute.
* For $x in local.dirs * For $x in local.dirs
@ -105,14 +109,13 @@ public void localizeClasspathJar(Path classPathJar, String owner) throws IOExcep
* @param appId id of the application * @param appId id of the application
* @param nmPrivateContainerTokens path to localized credentials, rsrc by NM * @param nmPrivateContainerTokens path to localized credentials, rsrc by NM
* @param nmAddr RPC address to contact NM * @param nmAddr RPC address to contact NM
* @param localDirs nm-local-dirs * @param dirsHandler NM local dirs service, for nm-local-dirs and nm-log-dirs
* @param logDirs nm-log-dirs
* @throws IOException For most application init failures * @throws IOException For most application init failures
* @throws InterruptedException If application init thread is halted by NM * @throws InterruptedException If application init thread is halted by NM
*/ */
public abstract void startLocalizer(Path nmPrivateContainerTokens, public abstract void startLocalizer(Path nmPrivateContainerTokens,
InetSocketAddress nmAddr, String user, String appId, String locId, InetSocketAddress nmAddr, String user, String appId, String locId,
List<String> localDirs, List<String> logDirs) LocalDirsHandlerService dirsHandler)
throws IOException, InterruptedException; throws IOException, InterruptedException;
@ -132,8 +135,8 @@ public abstract void startLocalizer(Path nmPrivateContainerTokens,
*/ */
public abstract int launchContainer(Container container, public abstract int launchContainer(Container container,
Path nmPrivateContainerScriptPath, Path nmPrivateTokensPath, Path nmPrivateContainerScriptPath, Path nmPrivateTokensPath,
String user, String appId, Path containerWorkDir, List<String> localDirs, String user, String appId, Path containerWorkDir,
List<String> logDirs) throws IOException; List<String> localDirs, List<String> logDirs) throws IOException;
public abstract boolean signalContainer(String user, String pid, public abstract boolean signalContainer(String user, String pid,
Signal signal) Signal signal)

View File

@ -19,6 +19,7 @@
package org.apache.hadoop.yarn.server.nodemanager; package org.apache.hadoop.yarn.server.nodemanager;
import com.google.common.base.Optional; import com.google.common.base.Optional;
import static org.apache.hadoop.fs.CreateFlag.CREATE; import static org.apache.hadoop.fs.CreateFlag.CREATE;
import static org.apache.hadoop.fs.CreateFlag.OVERWRITE; import static org.apache.hadoop.fs.CreateFlag.OVERWRITE;
@ -32,9 +33,11 @@
import java.util.EnumSet; import java.util.EnumSet;
import java.util.List; import java.util.List;
import java.util.Random; import java.util.Random;
import java.util.Map;
import org.apache.commons.logging.Log; import org.apache.commons.logging.Log;
import org.apache.commons.logging.LogFactory; import org.apache.commons.logging.LogFactory;
import org.apache.hadoop.conf.Configuration;
import org.apache.hadoop.fs.FileContext; import org.apache.hadoop.fs.FileContext;
import org.apache.hadoop.fs.Path; import org.apache.hadoop.fs.Path;
import org.apache.hadoop.fs.UnsupportedFileSystemException; import org.apache.hadoop.fs.UnsupportedFileSystemException;
@ -42,6 +45,7 @@
import org.apache.hadoop.io.IOUtils; import org.apache.hadoop.io.IOUtils;
import org.apache.hadoop.util.Shell; import org.apache.hadoop.util.Shell;
import org.apache.hadoop.util.Shell.ExitCodeException; import org.apache.hadoop.util.Shell.ExitCodeException;
import org.apache.hadoop.util.Shell.CommandExecutor;
import org.apache.hadoop.util.Shell.ShellCommandExecutor; import org.apache.hadoop.util.Shell.ShellCommandExecutor;
import org.apache.hadoop.util.StringUtils; import org.apache.hadoop.util.StringUtils;
import org.apache.hadoop.yarn.api.records.ContainerId; import org.apache.hadoop.yarn.api.records.ContainerId;
@ -92,9 +96,12 @@ public void init() throws IOException {
@Override @Override
public synchronized void startLocalizer(Path nmPrivateContainerTokensPath, public synchronized void startLocalizer(Path nmPrivateContainerTokensPath,
InetSocketAddress nmAddr, String user, String appId, String locId, InetSocketAddress nmAddr, String user, String appId, String locId,
List<String> localDirs, List<String> logDirs) LocalDirsHandlerService dirsHandler)
throws IOException, InterruptedException { throws IOException, InterruptedException {
List<String> localDirs = dirsHandler.getLocalDirs();
List<String> logDirs = dirsHandler.getLogDirs();
ContainerLocalizer localizer = ContainerLocalizer localizer =
new ContainerLocalizer(lfs, user, appId, locId, getPaths(localDirs), new ContainerLocalizer(lfs, user, appId, locId, getPaths(localDirs),
RecordFactoryProvider.getRecordFactory(getConf())); RecordFactoryProvider.getRecordFactory(getConf()));
@ -120,7 +127,7 @@ public synchronized void startLocalizer(Path nmPrivateContainerTokensPath,
@Override @Override
public int launchContainer(Container container, public int launchContainer(Container container,
Path nmPrivateContainerScriptPath, Path nmPrivateTokensPath, Path nmPrivateContainerScriptPath, Path nmPrivateTokensPath,
String userName, String appId, Path containerWorkDir, String user, String appId, Path containerWorkDir,
List<String> localDirs, List<String> logDirs) throws IOException { List<String> localDirs, List<String> logDirs) throws IOException {
FsPermission dirPerm = new FsPermission(APPDIR_PERM); FsPermission dirPerm = new FsPermission(APPDIR_PERM);
@ -134,29 +141,30 @@ public int launchContainer(Container container,
getApplicationId()); getApplicationId());
for (String sLocalDir : localDirs) { for (String sLocalDir : localDirs) {
Path usersdir = new Path(sLocalDir, ContainerLocalizer.USERCACHE); Path usersdir = new Path(sLocalDir, ContainerLocalizer.USERCACHE);
Path userdir = new Path(usersdir, userName); Path userdir = new Path(usersdir, user);
Path appCacheDir = new Path(userdir, ContainerLocalizer.APPCACHE); Path appCacheDir = new Path(userdir, ContainerLocalizer.APPCACHE);
Path appDir = new Path(appCacheDir, appIdStr); Path appDir = new Path(appCacheDir, appIdStr);
Path containerDir = new Path(appDir, containerIdStr); Path containerDir = new Path(appDir, containerIdStr);
createDir(containerDir, dirPerm, true, userName); createDir(containerDir, dirPerm, true, user);
} }
// Create the container log-dirs on all disks // Create the container log-dirs on all disks
createContainerLogDirs(appIdStr, containerIdStr, logDirs, userName); createContainerLogDirs(appIdStr, containerIdStr, logDirs, user);
Path tmpDir = new Path(containerWorkDir, Path tmpDir = new Path(containerWorkDir,
YarnConfiguration.DEFAULT_CONTAINER_TEMP_DIR); YarnConfiguration.DEFAULT_CONTAINER_TEMP_DIR);
createDir(tmpDir, dirPerm, false, userName); createDir(tmpDir, dirPerm, false, user);
// copy launch script to work dir
Path launchDst =
new Path(containerWorkDir, ContainerLaunch.CONTAINER_SCRIPT);
copyFile(nmPrivateContainerScriptPath, launchDst, userName);
// copy container tokens to work dir // copy container tokens to work dir
Path tokenDst = Path tokenDst =
new Path(containerWorkDir, ContainerLaunch.FINAL_CONTAINER_TOKENS_FILE); new Path(containerWorkDir, ContainerLaunch.FINAL_CONTAINER_TOKENS_FILE);
copyFile(nmPrivateTokensPath, tokenDst, userName); copyFile(nmPrivateTokensPath, tokenDst, user);
// copy launch script to work dir
Path launchDst =
new Path(containerWorkDir, ContainerLaunch.CONTAINER_SCRIPT);
copyFile(nmPrivateContainerScriptPath, launchDst, user);
// Create new local launch wrapper script // Create new local launch wrapper script
LocalWrapperScriptBuilder sb = getLocalWrapperScriptBuilder( LocalWrapperScriptBuilder sb = getLocalWrapperScriptBuilder(
@ -184,20 +192,16 @@ public int launchContainer(Container container,
// create log dir under app // create log dir under app
// fork script // fork script
ShellCommandExecutor shExec = null; Shell.CommandExecutor shExec = null;
try { try {
setScriptExecutable(launchDst, userName); setScriptExecutable(launchDst, user);
setScriptExecutable(sb.getWrapperScriptPath(), userName); setScriptExecutable(sb.getWrapperScriptPath(), user);
// Setup command to run shExec = buildCommandExecutor(sb.getWrapperScriptPath().toString(),
String[] command = getRunCommand(sb.getWrapperScriptPath().toString(), containerIdStr, user, pidFile,
containerIdStr, userName, pidFile, this.getConf());
LOG.info("launchContainer: " + Arrays.toString(command));
shExec = new ShellCommandExecutor(
command,
new File(containerWorkDir.toUri().getPath()), new File(containerWorkDir.toUri().getPath()),
container.getLaunchContext().getEnvironment()); // sanitized env container.getLaunchContext().getEnvironment());
if (isContainerActive(containerId)) { if (isContainerActive(containerId)) {
shExec.execute(); shExec.execute();
} }
@ -242,11 +246,26 @@ public int launchContainer(Container container,
} }
return exitCode; return exitCode;
} finally { } finally {
; // if (shExec != null) shExec.close();
} }
return 0; return 0;
} }
protected CommandExecutor buildCommandExecutor(String wrapperScriptPath,
String containerIdStr, String user, Path pidFile, File wordDir,
Map<String, String> environment)
throws IOException {
String[] command = getRunCommand(wrapperScriptPath,
containerIdStr, user, pidFile, this.getConf());
LOG.info("launchContainer: " + Arrays.toString(command));
return new ShellCommandExecutor(
command,
wordDir,
environment);
}
protected LocalWrapperScriptBuilder getLocalWrapperScriptBuilder( protected LocalWrapperScriptBuilder getLocalWrapperScriptBuilder(
String containerIdStr, Path containerWorkDir) { String containerIdStr, Path containerWorkDir) {
return Shell.WINDOWS ? return Shell.WINDOWS ?
@ -421,7 +440,7 @@ public static boolean containerIsAlive(String pid) throws IOException {
* @param signal signal to send * @param signal signal to send
* (for logging). * (for logging).
*/ */
private void killContainer(String pid, Signal signal) throws IOException { protected void killContainer(String pid, Signal signal) throws IOException {
new ShellCommandExecutor(Shell.getSignalKillCommand(signal.getValue(), pid)) new ShellCommandExecutor(Shell.getSignalKillCommand(signal.getValue(), pid))
.execute(); .execute();
} }

View File

@ -194,9 +194,12 @@ public void init() throws IOException {
@Override @Override
public void startLocalizer(Path nmPrivateContainerTokensPath, public void startLocalizer(Path nmPrivateContainerTokensPath,
InetSocketAddress nmAddr, String user, String appId, String locId, InetSocketAddress nmAddr, String user, String appId, String locId,
List<String> localDirs, List<String> logDirs) LocalDirsHandlerService dirsHandler)
throws IOException, InterruptedException { throws IOException, InterruptedException {
List<String> localDirs = dirsHandler.getLocalDirs();
List<String> logDirs = dirsHandler.getLogDirs();
verifyUsernamePattern(user); verifyUsernamePattern(user);
String runAsUser = getRunAsUser(user); String runAsUser = getRunAsUser(user);
List<String> command = new ArrayList<String>(); List<String> command = new ArrayList<String>();

View File

@ -17,36 +17,272 @@
*/ */
package org.apache.hadoop.yarn.server.nodemanager; package org.apache.hadoop.yarn.server.nodemanager;
import java.io.BufferedReader;
import java.io.File; import java.io.File;
import java.io.FileDescriptor;
import java.io.FileInputStream;
import java.io.FileOutputStream;
import java.io.IOException; import java.io.IOException;
import java.io.InputStream;
import java.io.InputStreamReader;
import java.io.OutputStream;
import java.io.PrintStream; import java.io.PrintStream;
import java.net.InetSocketAddress; import java.net.InetSocketAddress;
import java.net.URISyntaxException;
import java.util.ArrayList; import java.util.ArrayList;
import java.util.HashMap; import java.util.HashMap;
import java.util.List; import java.util.List;
import java.util.Map; import java.util.Map;
import org.apache.commons.lang.StringUtils;
import org.apache.commons.logging.Log; import org.apache.commons.logging.Log;
import org.apache.commons.logging.LogFactory; import org.apache.commons.logging.LogFactory;
import org.apache.hadoop.conf.Configuration; import org.apache.hadoop.conf.Configuration;
import org.apache.hadoop.fs.DelegateToFileSystem;
import org.apache.hadoop.fs.FileContext;
import org.apache.hadoop.fs.FileUtil; import org.apache.hadoop.fs.FileUtil;
import org.apache.hadoop.fs.FsConstants;
import org.apache.hadoop.fs.Path; import org.apache.hadoop.fs.Path;
import org.apache.hadoop.fs.RawLocalFileSystem;
import org.apache.hadoop.fs.permission.FsPermission; import org.apache.hadoop.fs.permission.FsPermission;
import org.apache.hadoop.io.nativeio.NativeIO.Windows;
import org.apache.hadoop.io.nativeio.NativeIOException;
import org.apache.hadoop.util.NativeCodeLoader;
import org.apache.hadoop.util.Shell; import org.apache.hadoop.util.Shell;
import org.apache.hadoop.util.Shell.ShellCommandExecutor; import org.apache.hadoop.util.Shell.CommandExecutor;
import org.apache.hadoop.yarn.conf.YarnConfiguration; import org.apache.hadoop.yarn.conf.YarnConfiguration;
import org.apache.hadoop.yarn.server.nodemanager.DefaultContainerExecutor.LocalWrapperScriptBuilder;
import org.apache.hadoop.yarn.server.nodemanager.containermanager.localizer.ContainerLocalizer; import org.apache.hadoop.yarn.server.nodemanager.containermanager.localizer.ContainerLocalizer;
import org.apache.hadoop.yarn.server.nodemanager.containermanager.localizer.ResourceLocalizationService;
/** /**
* Windows secure container executor. Uses winutils task createAsUser. * Windows secure container executor (WSCE).
* * This class offers a secure container executor on Windows, similar to the
* LinuxContainerExecutor. As the NM does not run on a high privileged context,
* this class delegates elevated operations to the helper hadoopwintuilsvc,
* implemented by the winutils.exe running as a service.
* JNI and LRPC is used to communicate with the privileged service.
*/ */
public class WindowsSecureContainerExecutor extends DefaultContainerExecutor { public class WindowsSecureContainerExecutor extends DefaultContainerExecutor {
private static final Log LOG = LogFactory private static final Log LOG = LogFactory
.getLog(WindowsSecureContainerExecutor.class); .getLog(WindowsSecureContainerExecutor.class);
public static final String LOCALIZER_PID_FORMAT = "STAR_LOCALIZER_%s";
/**
* This class is a container for the JNI Win32 native methods used by WSCE.
*/
private static class Native {
private static boolean nativeLoaded = false;
static {
if (NativeCodeLoader.isNativeCodeLoaded()) {
try {
initWsceNative();
nativeLoaded = true;
} catch (Throwable t) {
LOG.info("Unable to initialize WSCE Native libraries", t);
}
}
}
/** Initialize the JNI method ID and class ID cache */
private static native void initWsceNative();
/**
* This class contains methods used by the WindowsSecureContainerExecutor
* file system operations.
*/
public static class Elevated {
private static final int MOVE_FILE = 1;
private static final int COPY_FILE = 2;
public static void mkdir(Path dirName) throws IOException {
if (!nativeLoaded) {
throw new IOException("Native WSCE libraries are required for mkdir");
}
elevatedMkDirImpl(dirName.toString());
}
private static native void elevatedMkDirImpl(String dirName)
throws IOException;
public static void chown(Path fileName, String user, String group)
throws IOException {
if (!nativeLoaded) {
throw new IOException("Native WSCE libraries are required for chown");
}
elevatedChownImpl(fileName.toString(), user, group);
}
private static native void elevatedChownImpl(String fileName, String user,
String group) throws IOException;
public static void move(Path src, Path dst, boolean replaceExisting)
throws IOException {
if (!nativeLoaded) {
throw new IOException("Native WSCE libraries are required for move");
}
elevatedCopyImpl(MOVE_FILE, src.toString(), dst.toString(),
replaceExisting);
}
public static void copy(Path src, Path dst, boolean replaceExisting)
throws IOException {
if (!nativeLoaded) {
throw new IOException("Native WSCE libraries are required for copy");
}
elevatedCopyImpl(COPY_FILE, src.toString(), dst.toString(),
replaceExisting);
}
private static native void elevatedCopyImpl(int operation, String src,
String dst, boolean replaceExisting) throws IOException;
public static void chmod(Path fileName, int mode) throws IOException {
if (!nativeLoaded) {
throw new IOException("Native WSCE libraries are required for chmod");
}
elevatedChmodImpl(fileName.toString(), mode);
}
private static native void elevatedChmodImpl(String path, int mode)
throws IOException;
public static void killTask(String containerName) throws IOException {
if (!nativeLoaded) {
throw new IOException("Native WSCE libraries are required for killTask");
}
elevatedKillTaskImpl(containerName);
}
private static native void elevatedKillTaskImpl(String containerName)
throws IOException;
public static OutputStream create(Path f, boolean append)
throws IOException {
if (!nativeLoaded) {
throw new IOException("Native WSCE libraries are required for create");
}
long desiredAccess = Windows.GENERIC_WRITE;
long shareMode = 0L;
long creationDisposition = append ?
Windows.OPEN_ALWAYS : Windows.CREATE_ALWAYS;
long flags = Windows.FILE_ATTRIBUTE_NORMAL;
String fileName = f.toString();
fileName = fileName.replace('/', '\\');
long hFile = elevatedCreateImpl(
fileName, desiredAccess, shareMode, creationDisposition, flags);
return new FileOutputStream(
WinutilsProcessStub.getFileDescriptorFromHandle(hFile));
}
private static native long elevatedCreateImpl(String path,
long desiredAccess, long shareMode,
long creationDisposition, long flags) throws IOException;
public static boolean deleteFile(Path path) throws IOException {
if (!nativeLoaded) {
throw new IOException("Native WSCE libraries are required for deleteFile");
}
return elevatedDeletePathImpl(path.toString(), false);
}
public static boolean deleteDirectory(Path path) throws IOException {
if (!nativeLoaded) {
throw new IOException("Native WSCE libraries are required for deleteDirectory");
}
return elevatedDeletePathImpl(path.toString(), true);
}
public native static boolean elevatedDeletePathImpl(String path,
boolean isDir) throws IOException;
}
/**
* Wraps a process started by the winutils service helper.
*
*/
public static class WinutilsProcessStub extends Process {
private final long hProcess;
private final long hThread;
private boolean disposed = false;
private final InputStream stdErr;
private final InputStream stdOut;
private final OutputStream stdIn;
public WinutilsProcessStub(long hProcess, long hThread, long hStdIn,
long hStdOut, long hStdErr) {
this.hProcess = hProcess;
this.hThread = hThread;
this.stdIn = new FileOutputStream(getFileDescriptorFromHandle(hStdIn));
this.stdOut = new FileInputStream(getFileDescriptorFromHandle(hStdOut));
this.stdErr = new FileInputStream(getFileDescriptorFromHandle(hStdErr));
}
public static native FileDescriptor getFileDescriptorFromHandle(long handle);
@Override
public native void destroy();
@Override
public native int exitValue();
@Override
public InputStream getErrorStream() {
return stdErr;
}
@Override
public InputStream getInputStream() {
return stdOut;
}
@Override
public OutputStream getOutputStream() {
return stdIn;
}
@Override
public native int waitFor() throws InterruptedException;
public synchronized native void dispose();
public native void resume() throws NativeIOException;
}
public synchronized static WinutilsProcessStub createTaskAsUser(
String cwd, String jobName, String user, String pidFile, String cmdLine)
throws IOException {
if (!nativeLoaded) {
throw new IOException(
"Native WSCE libraries are required for createTaskAsUser");
}
synchronized(Shell.WindowsProcessLaunchLock) {
return createTaskAsUser0(cwd, jobName, user, pidFile, cmdLine);
}
}
private static native WinutilsProcessStub createTaskAsUser0(
String cwd, String jobName, String user, String pidFile, String cmdLine)
throws NativeIOException;
}
/**
* A shell script wrapper builder for WSCE.
* Overwrites the default behavior to remove the creation of the PID file in
* the script wrapper. WSCE creates the pid file as part of launching the
* task in winutils.
*/
private class WindowsSecureWrapperScriptBuilder private class WindowsSecureWrapperScriptBuilder
extends LocalWrapperScriptBuilder { extends LocalWrapperScriptBuilder {
@ -60,19 +296,276 @@ protected void writeLocalWrapperScript(Path launchDst, Path pidFile, PrintStream
} }
} }
/**
* This is a skeleton file system used to elevate certain operations.
* WSCE has to create container dirs under local/userchache/$user but
* this dir itself is owned by $user, with chmod 750. As ther NM has no
* write access, it must delegate the write operations to the privileged
* hadoopwintuilsvc.
*/
private static class ElevatedFileSystem extends DelegateToFileSystem {
/**
* This overwrites certain RawLocalSystem operations to be performed by a
* privileged process.
*
*/
private static class ElevatedRawLocalFilesystem extends RawLocalFileSystem {
@Override
protected boolean mkOneDir(File p2f) throws IOException {
Path path = new Path(p2f.getAbsolutePath());
if (LOG.isDebugEnabled()) {
LOG.debug(String.format("EFS:mkOneDir: %s", path));
}
boolean ret = false;
// File.mkdir returns false, does not throw. Must mimic it.
try {
Native.Elevated.mkdir(path);
ret = true;
}
catch(Throwable e) {
if (LOG.isDebugEnabled()) {
LOG.debug(String.format("EFS:mkOneDir: %s",
org.apache.hadoop.util.StringUtils.stringifyException(e)));
}
}
return ret;
}
@Override
public void setPermission(Path p, FsPermission permission)
throws IOException {
if (LOG.isDebugEnabled()) {
LOG.debug(String.format("EFS:setPermission: %s %s", p, permission));
}
Native.Elevated.chmod(p, permission.toShort());
}
@Override
public void setOwner(Path p, String username, String groupname)
throws IOException {
if (LOG.isDebugEnabled()) {
LOG.debug(String.format("EFS:setOwner: %s %s %s",
p, username, groupname));
}
Native.Elevated.chown(p, username, groupname);
}
@Override
protected OutputStream createOutputStream(Path f, boolean append)
throws IOException {
if (LOG.isDebugEnabled()) {
LOG.debug(String.format("EFS:create: %s %b", f, append));
}
return Native.Elevated.create(f, append);
}
@Override
public boolean delete(Path p, boolean recursive) throws IOException {
if (LOG.isDebugEnabled()) {
LOG.debug(String.format("EFS:delete: %s %b", p, recursive));
}
// The super delete uses the FileUtil.fullyDelete,
// but we cannot rely on that because we need to use the elevated
// operations to remove the files
//
File f = pathToFile(p);
if (!f.exists()) {
//no path, return false "nothing to delete"
return false;
}
else if (f.isFile()) {
return Native.Elevated.deleteFile(p);
}
else if (f.isDirectory()) {
// This is a best-effort attempt. There are race conditions in that
// child files can be created/deleted after we snapped the list.
// No need to protect against that case.
File[] files = FileUtil.listFiles(f);
int childCount = files.length;
if (recursive) {
for(File child:files) {
if (delete(new Path(child.getPath()), recursive)) {
--childCount;
}
}
}
if (childCount == 0) {
return Native.Elevated.deleteDirectory(p);
}
else {
throw new IOException("Directory " + f.toString() + " is not empty");
}
}
else {
// This can happen under race conditions if an external agent
// is messing with the file type between IFs
throw new IOException("Path " + f.toString() +
" exists, but is neither a file nor a directory");
}
}
}
protected ElevatedFileSystem() throws IOException, URISyntaxException {
super(FsConstants.LOCAL_FS_URI,
new ElevatedRawLocalFilesystem(),
new Configuration(),
FsConstants.LOCAL_FS_URI.getScheme(),
false);
}
}
private static class WintuilsProcessStubExecutor
implements Shell.CommandExecutor {
private Native.WinutilsProcessStub processStub;
private StringBuilder output = new StringBuilder();
private int exitCode;
private enum State {
INIT,
RUNNING,
COMPLETE
};
private State state;;
private final String cwd;
private final String jobName;
private final String userName;
private final String pidFile;
private final String cmdLine;
public WintuilsProcessStubExecutor(
String cwd,
String jobName,
String userName,
String pidFile,
String cmdLine) {
this.cwd = cwd;
this.jobName = jobName;
this.userName = userName;
this.pidFile = pidFile;
this.cmdLine = cmdLine;
this.state = State.INIT;
}
private void assertComplete() throws IOException {
if (state != State.COMPLETE) {
throw new IOException("Process is not complete");
}
}
public String getOutput () throws IOException {
assertComplete();
return output.toString();
}
public int getExitCode() throws IOException {
assertComplete();
return exitCode;
}
public void validateResult() throws IOException {
assertComplete();
if (0 != exitCode) {
LOG.warn(output.toString());
throw new IOException("Processs exit code is:" + exitCode);
}
}
private Thread startStreamReader(final InputStream stream)
throws IOException {
Thread streamReaderThread = new Thread() {
@Override
public void run() {
try
{
BufferedReader lines = new BufferedReader(
new InputStreamReader(stream));
char[] buf = new char[512];
int nRead;
while ((nRead = lines.read(buf, 0, buf.length)) > 0) {
output.append(buf, 0, nRead);
}
}
catch(Throwable t) {
LOG.error("Error occured reading the process stdout", t);
}
}
};
streamReaderThread.start();
return streamReaderThread;
}
public void execute() throws IOException {
if (state != State.INIT) {
throw new IOException("Process is already started");
}
processStub = Native.createTaskAsUser(cwd,
jobName, userName, pidFile, cmdLine);
state = State.RUNNING;
Thread stdOutReader = startStreamReader(processStub.getInputStream());
Thread stdErrReader = startStreamReader(processStub.getErrorStream());
try {
processStub.resume();
processStub.waitFor();
stdOutReader.join();
stdErrReader.join();
}
catch(InterruptedException ie) {
throw new IOException(ie);
}
exitCode = processStub.exitValue();
state = State.COMPLETE;
}
@Override
public void close() {
if (processStub != null) {
processStub.dispose();
}
}
}
private String nodeManagerGroup; private String nodeManagerGroup;
/**
* Permissions for user WSCE dirs.
*/
static final short DIR_PERM = (short)0750;
public WindowsSecureContainerExecutor()
throws IOException, URISyntaxException {
super(FileContext.getFileContext(new ElevatedFileSystem(),
new Configuration()));
}
@Override @Override
public void setConf(Configuration conf) { public void setConf(Configuration conf) {
super.setConf(conf); super.setConf(conf);
nodeManagerGroup = conf.get(YarnConfiguration.NM_WINDOWS_SECURE_CONTAINER_GROUP); nodeManagerGroup = conf.get(
YarnConfiguration.NM_WINDOWS_SECURE_CONTAINER_GROUP);
} }
@Override @Override
protected String[] getRunCommand(String command, String groupId, protected String[] getRunCommand(String command, String groupId,
String userName, Path pidFile, Configuration conf) { String userName, Path pidFile, Configuration conf) {
return new String[] { Shell.WINUTILS, "task", "createAsUser", groupId, userName, File f = new File(command);
pidFile.toString(), "cmd /c " + command }; if (LOG.isDebugEnabled()) {
LOG.debug(String.format("getRunCommand: %s exists:%b",
command, f.exists()));
}
return new String[] { Shell.WINUTILS, "task", "createAsUser", groupId,
userName, pidFile.toString(), "cmd /c " + command };
} }
@Override @Override
@ -83,34 +576,68 @@ protected LocalWrapperScriptBuilder getLocalWrapperScriptBuilder(
@Override @Override
protected void copyFile(Path src, Path dst, String owner) throws IOException { protected void copyFile(Path src, Path dst, String owner) throws IOException {
super.copyFile(src, dst, owner); if (LOG.isDebugEnabled()) {
lfs.setOwner(dst, owner, nodeManagerGroup); LOG.debug(String.format("copyFile: %s -> %s owner:%s", src.toString(),
dst.toString(), owner));
}
Native.Elevated.copy(src, dst, true);
Native.Elevated.chown(dst, owner, nodeManagerGroup);
} }
@Override @Override
protected void createDir(Path dirPath, FsPermission perms, protected void createDir(Path dirPath, FsPermission perms,
boolean createParent, String owner) throws IOException { boolean createParent, String owner) throws IOException {
// WSCE requires dirs to be 750, not 710 as DCE.
// This is similar to how LCE creates dirs
//
perms = new FsPermission(DIR_PERM);
if (LOG.isDebugEnabled()) {
LOG.debug(String.format("createDir: %s perm:%s owner:%s",
dirPath.toString(), perms.toString(), owner));
}
super.createDir(dirPath, perms, createParent, owner); super.createDir(dirPath, perms, createParent, owner);
lfs.setOwner(dirPath, owner, nodeManagerGroup); lfs.setOwner(dirPath, owner, nodeManagerGroup);
} }
@Override @Override
protected void setScriptExecutable(Path script, String owner) throws IOException { protected void setScriptExecutable(Path script, String owner)
super.setScriptExecutable(script, null); throws IOException {
lfs.setOwner(script, owner, nodeManagerGroup); if (LOG.isDebugEnabled()) {
LOG.debug(String.format("setScriptExecutable: %s owner:%s",
script.toString(), owner));
}
super.setScriptExecutable(script, owner);
Native.Elevated.chown(script, owner, nodeManagerGroup);
} }
@Override @Override
public void localizeClasspathJar(Path classpathJar, String owner) throws IOException { public Path localizeClasspathJar(Path classPathJar, Path pwd, String owner)
lfs.setOwner(classpathJar, owner, nodeManagerGroup); throws IOException {
if (LOG.isDebugEnabled()) {
LOG.debug(String.format("localizeClasspathJar: %s %s o:%s",
classPathJar, pwd, owner));
}
createDir(pwd, new FsPermission(DIR_PERM), true, owner);
String fileName = classPathJar.getName();
Path dst = new Path(pwd, fileName);
Native.Elevated.move(classPathJar, dst, true);
Native.Elevated.chown(dst, owner, nodeManagerGroup);
return dst;
} }
@Override @Override
public void startLocalizer(Path nmPrivateContainerTokens, public void startLocalizer(Path nmPrivateContainerTokens,
InetSocketAddress nmAddr, String user, String appId, String locId, InetSocketAddress nmAddr, String user, String appId, String locId,
List<String> localDirs, List<String> logDirs) throws IOException, LocalDirsHandlerService dirsHandler) throws IOException,
InterruptedException { InterruptedException {
List<String> localDirs = dirsHandler.getLocalDirs();
List<String> logDirs = dirsHandler.getLogDirs();
Path classpathJarPrivateDir = dirsHandler.getLocalPathForWrite(
ResourceLocalizationService.NM_PRIVATE_DIR);
createUserLocalDirs(localDirs, user); createUserLocalDirs(localDirs, user);
createUserCacheDirs(localDirs, user); createUserCacheDirs(localDirs, user);
createAppDirs(localDirs, user, appId); createAppDirs(localDirs, user, appId);
@ -118,55 +645,87 @@ public void startLocalizer(Path nmPrivateContainerTokens,
Path appStorageDir = getWorkingDir(localDirs, user, appId); Path appStorageDir = getWorkingDir(localDirs, user, appId);
String tokenFn = String.format(ContainerLocalizer.TOKEN_FILE_NAME_FMT, locId); String tokenFn = String.format(
ContainerLocalizer.TOKEN_FILE_NAME_FMT, locId);
Path tokenDst = new Path(appStorageDir, tokenFn); Path tokenDst = new Path(appStorageDir, tokenFn);
LOG.info("Copying from " + nmPrivateContainerTokens + " to " + tokenDst);
copyFile(nmPrivateContainerTokens, tokenDst, user); copyFile(nmPrivateContainerTokens, tokenDst, user);
List<String> command ;
String[] commandArray;
ShellCommandExecutor shExec;
File cwdApp = new File(appStorageDir.toString()); File cwdApp = new File(appStorageDir.toString());
LOG.info(String.format("cwdApp: %s", cwdApp)); if (LOG.isDebugEnabled()) {
LOG.debug(String.format("cwdApp: %s", cwdApp));
}
List<String> command ;
command = new ArrayList<String>(); command = new ArrayList<String>();
command.add(Shell.WINUTILS);
command.add("task");
command.add("createAsUser");
command.add("START_LOCALIZER_" + locId);
command.add(user);
command.add("nul:"); // PID file
//use same jvm as parent //use same jvm as parent
File jvm = new File(new File(System.getProperty("java.home"), "bin"), "java.exe"); File jvm = new File(
new File(System.getProperty("java.home"), "bin"), "java.exe");
command.add(jvm.toString()); command.add(jvm.toString());
Path cwdPath = new Path(cwdApp.getPath());
// Build a temp classpath jar. See ContainerLaunch.sanitizeEnv(). // Build a temp classpath jar. See ContainerLaunch.sanitizeEnv().
// Passing CLASSPATH explicitly is *way* too long for command line. // Passing CLASSPATH explicitly is *way* too long for command line.
String classPath = System.getProperty("java.class.path"); String classPath = System.getProperty("java.class.path");
Map<String, String> env = new HashMap<String, String>(System.getenv()); Map<String, String> env = new HashMap<String, String>(System.getenv());
String[] jarCp = FileUtil.createJarWithClassPath(classPath, appStorageDir, env); String jarCp[] = FileUtil.createJarWithClassPath(classPath,
String classPathJar = jarCp[0]; classpathJarPrivateDir, cwdPath, env);
localizeClasspathJar(new Path(classPathJar), user); String classPathJar = localizeClasspathJar(
String replacementClassPath = classPathJar + jarCp[1]; new Path(jarCp[0]), cwdPath, user).toString();
command.add("-classpath"); command.add("-classpath");
command.add(replacementClassPath); command.add(classPathJar + jarCp[1]);
String javaLibPath = System.getProperty("java.library.path"); String javaLibPath = System.getProperty("java.library.path");
if (javaLibPath != null) { if (javaLibPath != null) {
command.add("-Djava.library.path=" + javaLibPath); command.add("-Djava.library.path=" + javaLibPath);
} }
ContainerLocalizer.buildMainArgs(command, user, appId, locId, nmAddr, localDirs); ContainerLocalizer.buildMainArgs(command, user, appId, locId, nmAddr,
commandArray = command.toArray(new String[command.size()]); localDirs);
shExec = new ShellCommandExecutor( String cmdLine = StringUtils.join(command, " ");
commandArray, cwdApp);
shExec.execute(); String localizerPid = String.format(LOCALIZER_PID_FORMAT, locId);
WintuilsProcessStubExecutor stubExecutor = new WintuilsProcessStubExecutor(
cwdApp.getAbsolutePath(),
localizerPid, user, "nul:", cmdLine);
try {
stubExecutor.execute();
stubExecutor.validateResult();
}
finally {
stubExecutor.close();
try
{
killContainer(localizerPid, Signal.KILL);
}
catch(Throwable e) {
LOG.warn(String.format(
"An exception occured during the cleanup of localizer job %s:\n%s",
localizerPid,
org.apache.hadoop.util.StringUtils.stringifyException(e)));
}
}
}
@Override
protected CommandExecutor buildCommandExecutor(String wrapperScriptPath,
String containerIdStr,
String userName, Path pidFile,File wordDir, Map<String, String> environment)
throws IOException {
return new WintuilsProcessStubExecutor(
wordDir.toString(),
containerIdStr, userName, pidFile.toString(),
"cmd /c " + wrapperScriptPath);
}
@Override
protected void killContainer(String pid, Signal signal) throws IOException {
Native.Elevated.killTask(pid);
} }
} }

View File

@ -212,7 +212,9 @@ public Integer call() {
+ Path.SEPARATOR + Path.SEPARATOR
+ String.format(ContainerLocalizer.TOKEN_FILE_NAME_FMT, + String.format(ContainerLocalizer.TOKEN_FILE_NAME_FMT,
containerIdStr)); containerIdStr));
Path nmPrivateClasspathJarDir =
dirsHandler.getLocalPathForWrite(
getContainerPrivateDir(appIdStr, containerIdStr));
DataOutputStream containerScriptOutStream = null; DataOutputStream containerScriptOutStream = null;
DataOutputStream tokensOutStream = null; DataOutputStream tokensOutStream = null;
@ -263,7 +265,7 @@ public Integer call() {
FINAL_CONTAINER_TOKENS_FILE).toUri().getPath()); FINAL_CONTAINER_TOKENS_FILE).toUri().getPath());
// Sanitize the container's environment // Sanitize the container's environment
sanitizeEnv(environment, containerWorkDir, appDirs, containerLogDirs, sanitizeEnv(environment, containerWorkDir, appDirs, containerLogDirs,
localResources); localResources, nmPrivateClasspathJarDir);
// Write out the environment // Write out the environment
writeLaunchEnv(containerScriptOutStream, environment, localResources, writeLaunchEnv(containerScriptOutStream, environment, localResources,
@ -658,7 +660,8 @@ private static void putEnvIfAbsent(
public void sanitizeEnv(Map<String, String> environment, Path pwd, public void sanitizeEnv(Map<String, String> environment, Path pwd,
List<Path> appDirs, List<String> containerLogDirs, List<Path> appDirs, List<String> containerLogDirs,
Map<Path, List<String>> resources) throws IOException { Map<Path, List<String>> resources,
Path nmPrivateClasspathJarDir) throws IOException {
/** /**
* Non-modifiable environment variables * Non-modifiable environment variables
*/ */
@ -722,6 +725,7 @@ public void sanitizeEnv(Map<String, String> environment, Path pwd,
// TODO: Remove Windows check and use this approach on all platforms after // TODO: Remove Windows check and use this approach on all platforms after
// additional testing. See YARN-358. // additional testing. See YARN-358.
if (Shell.WINDOWS) { if (Shell.WINDOWS) {
String inputClassPath = environment.get(Environment.CLASSPATH.name()); String inputClassPath = environment.get(Environment.CLASSPATH.name());
if (inputClassPath != null && !inputClassPath.isEmpty()) { if (inputClassPath != null && !inputClassPath.isEmpty()) {
StringBuilder newClassPath = new StringBuilder(inputClassPath); StringBuilder newClassPath = new StringBuilder(inputClassPath);
@ -765,11 +769,11 @@ public void sanitizeEnv(Map<String, String> environment, Path pwd,
mergedEnv.putAll(environment); mergedEnv.putAll(environment);
String[] jarCp = FileUtil.createJarWithClassPath( String[] jarCp = FileUtil.createJarWithClassPath(
newClassPath.toString(), pwd, mergedEnv); newClassPath.toString(), nmPrivateClasspathJarDir, pwd, mergedEnv);
String classPathJar = jarCp[0];
// In a secure cluster the classpath jar must be localized to grant access // In a secure cluster the classpath jar must be localized to grant access
this.exec.localizeClasspathJar(new Path(classPathJar), container.getUser()); Path localizedClassPathJar = exec.localizeClasspathJar(
String replacementClassPath = classPathJar + jarCp[1]; new Path(jarCp[0]), pwd, container.getUser());
String replacementClassPath = localizedClassPathJar.toString() + jarCp[1];
environment.put(Environment.CLASSPATH.name(), replacementClassPath); environment.put(Environment.CLASSPATH.name(), replacementClassPath);
} }
} }

View File

@ -369,10 +369,15 @@ public static void main(String[] argv) throws Throwable {
new ContainerLocalizer(FileContext.getLocalFSFileContext(), user, new ContainerLocalizer(FileContext.getLocalFSFileContext(), user,
appId, locId, localDirs, appId, locId, localDirs,
RecordFactoryProvider.getRecordFactory(null)); RecordFactoryProvider.getRecordFactory(null));
System.exit(localizer.runLocalization(nmAddr)); int nRet = localizer.runLocalization(nmAddr);
if (LOG.isDebugEnabled()) {
LOG.debug(String.format("nRet: %d", nRet));
}
System.exit(nRet);
} catch (Throwable e) { } catch (Throwable e) {
// Print error to stdout so that LCE can use it. // Print error to stdout so that LCE can use it.
e.printStackTrace(System.out); e.printStackTrace(System.out);
LOG.error("Exception in main:", e);
throw e; throw e;
} }
} }

View File

@ -1088,7 +1088,8 @@ public void run() {
ConverterUtils.toString( ConverterUtils.toString(
context.getContainerId(). context.getContainerId().
getApplicationAttemptId().getApplicationId()), getApplicationAttemptId().getApplicationId()),
localizerId, localDirs, logDirs); localizerId,
dirsHandler);
} else { } else {
throw new IOException("All disks failed. " throw new IOException("All disks failed. "
+ dirsHandler.getDisksHealthReport(false)); + dirsHandler.getDisksHealthReport(false));

View File

@ -303,6 +303,7 @@ public Object answer(InvocationOnMock invocationOnMock)
public void testStartLocalizer() public void testStartLocalizer()
throws IOException, InterruptedException { throws IOException, InterruptedException {
InetSocketAddress localizationServerAddress; InetSocketAddress localizationServerAddress;
final Path firstDir = new Path(BASE_TMP_PATH, "localDir1"); final Path firstDir = new Path(BASE_TMP_PATH, "localDir1");
List<String> localDirs = new ArrayList<String>(); List<String> localDirs = new ArrayList<String>();
final Path secondDir = new Path(BASE_TMP_PATH, "localDir2"); final Path secondDir = new Path(BASE_TMP_PATH, "localDir2");
@ -383,9 +384,14 @@ public Object answer(InvocationOnMock invocationOnMock)
String appSubmitter = "nobody"; String appSubmitter = "nobody";
String appId = "APP_ID"; String appId = "APP_ID";
String locId = "LOC_ID"; String locId = "LOC_ID";
LocalDirsHandlerService dirsHandler = mock(LocalDirsHandlerService.class);
when(dirsHandler.getLocalDirs()).thenReturn(localDirs);
when(dirsHandler.getLogDirs()).thenReturn(logDirs);
try { try {
mockExec.startLocalizer(nmPrivateCTokensPath, localizationServerAddress, mockExec.startLocalizer(nmPrivateCTokensPath, localizationServerAddress,
appSubmitter, appId, locId, localDirs, logDirs); appSubmitter, appId, locId, dirsHandler);
} catch (IOException e) { } catch (IOException e) {
Assert.fail("StartLocalizer failed to copy token file " + e); Assert.fail("StartLocalizer failed to copy token file " + e);
} finally { } finally {

View File

@ -266,7 +266,7 @@ public void buildMainArgs(List<String> command, String user, String appId,
exec.setConf(conf); exec.setConf(conf);
exec.startLocalizer(nmPrivateContainerTokensPath, nmAddr, appSubmitter, exec.startLocalizer(nmPrivateContainerTokensPath, nmAddr, appSubmitter,
appId, locId, localDirs, logDirs); appId, locId, dirsHandler);
String locId2 = "container_01_02"; String locId2 = "container_01_02";
Path nmPrivateContainerTokensPath2 = Path nmPrivateContainerTokensPath2 =
@ -276,7 +276,7 @@ public void buildMainArgs(List<String> command, String user, String appId,
+ String.format(ContainerLocalizer.TOKEN_FILE_NAME_FMT, locId2)); + String.format(ContainerLocalizer.TOKEN_FILE_NAME_FMT, locId2));
files.create(nmPrivateContainerTokensPath2, EnumSet.of(CREATE, OVERWRITE)); files.create(nmPrivateContainerTokensPath2, EnumSet.of(CREATE, OVERWRITE));
exec.startLocalizer(nmPrivateContainerTokensPath2, nmAddr, appSubmitter, exec.startLocalizer(nmPrivateContainerTokensPath2, nmAddr, appSubmitter,
appId, locId2, localDirs, logDirs); appId, locId2, dirsHandler);
} }
@Test @Test

View File

@ -184,7 +184,7 @@ public void testStartLocalizer() throws IOException {
Path nmPrivateCTokensPath= new Path("file:///bin/nmPrivateCTokensPath"); Path nmPrivateCTokensPath= new Path("file:///bin/nmPrivateCTokensPath");
try { try {
mockExec.startLocalizer(nmPrivateCTokensPath, address, "test", "application_0", "12345", dirsHandler.getLocalDirs(), dirsHandler.getLogDirs()); mockExec.startLocalizer(nmPrivateCTokensPath, address, "test", "application_0", "12345", dirsHandler);
List<String> result=readMockParams(); List<String> result=readMockParams();
Assert.assertEquals(result.size(), 17); Assert.assertEquals(result.size(), 17);
Assert.assertEquals(result.get(0), YarnConfiguration.DEFAULT_NM_NONSECURE_MODE_LOCAL_USER); Assert.assertEquals(result.get(0), YarnConfiguration.DEFAULT_NM_NONSECURE_MODE_LOCAL_USER);

View File

@ -850,7 +850,7 @@ public boolean matches(Object o) {
ArgumentCaptor<Path> tokenPathCaptor = ArgumentCaptor.forClass(Path.class); ArgumentCaptor<Path> tokenPathCaptor = ArgumentCaptor.forClass(Path.class);
verify(exec).startLocalizer(tokenPathCaptor.capture(), verify(exec).startLocalizer(tokenPathCaptor.capture(),
isA(InetSocketAddress.class), eq("user0"), eq(appStr), eq(ctnrStr), isA(InetSocketAddress.class), eq("user0"), eq(appStr), eq(ctnrStr),
isA(List.class), isA(List.class)); isA(LocalDirsHandlerService.class));
Path localizationTokenPath = tokenPathCaptor.getValue(); Path localizationTokenPath = tokenPathCaptor.getValue();
// heartbeat from localizer // heartbeat from localizer

View File

@ -83,11 +83,15 @@ min.user.id=1000#Prevent other super-users
+---+ +---+
** Windows Secure Container Executor ** Windows Secure Container Executor (WSCE)
The Windows environment secure container executor is the <<<WindowsSecureContainerExecutor>>>. The Windows environment secure container executor is the <<<WindowsSecureContainerExecutor>>>.
It uses the Windows S4U infrastructure to launch the container as the It uses the Windows S4U infrastructure to launch the container as the
YARN application user. YARN application user. The WSCE requires the presense of the <<<hadoopwinutilsvc>>> service. This services
is hosted by <<<%HADOOP_HOME%\bin\winutils.exe>>> started with the <<<service>>> command line argument. This
service offers some privileged operations that require LocalSystem authority so that the NM is not required
to run the entire JVM and all the NM code in an elevated context. The NM interacts with the <<<hadoopwintulsvc>>>
service by means of Local RPC (LRPC) via calls JNI to the RCP client hosted in <<<hadoop.dll>>>.
*** Configuration *** Configuration
@ -102,17 +106,71 @@ min.user.id=1000#Prevent other super-users
<property> <property>
<name>yarn.nodemanager.windows-secure-container-executor.group</name> <name>yarn.nodemanager.windows-secure-container-executor.group</name>
<value>hadoop</value> <value>yarn</value>
</property> </property>
+---+ +---+
*** wsce-site.xml
The NodeManager must run as a member of the local <<<Administrators>>> group or as The hadoopwinutilsvc uses <<<%HADOOP_HOME%\etc\hadoop\wsce_site.xml to configure access to the privileged operations.
<<<LocalSystem>>>. It is not enough for the NodeManager to simply impersonate such an user.
+---+
<property>
<name>yarn.nodemanager.windows-secure-container-executor.impersonate.allowed</name>
<value>HadoopUsers</value>
</property>
<property>
<name>yarn.nodemanager.windows-secure-container-executor.impersonate.denied</name>
<value>HadoopServices,Administrators</value>
</property>
<property>
<name>yarn.nodemanager.windows-secure-container-executor.allowed</name>
<value>nodemanager</value>
</property>
<property>
<name>yarn.nodemanager.windows-secure-container-executor.local-dirs</name>
<value>nm-local-dir, nm-log-dirs</value>
</property>
<property>
<name>yarn.nodemanager.windows-secure-container-executor.job-name</name>
<value>nodemanager-job-name</value>
</property>
+---+
<<<yarn.nodemanager.windows-secure-container-executor.allowed>>> should contain the name of the service account running the
nodemanager. This user will be allowed to access the hadoopwintuilsvc functions.
<<<yarn.nodemanager.windows-secure-container-executor.impersonate.allowed>>> should contain the users that are allowed to create
containers in the cluster. These users will be allowed to be impersonated by hadoopwinutilsvc.
<<<yarn.nodemanager.windows-secure-container-executor.impersonate.denied>>> should contain users that are explictly forbiden from
creating containers. hadoopwinutilsvc will refuse to impersonate these users.
<<<yarn.nodemanager.windows-secure-container-executor.local-dirs>>> should contain the nodemanager local dirs. hadoopwinutilsvc will
allow only file operations under these directories. This should contain the same values as <<<${yarn.nodemanager.local-dirs}, ${yarn.nodemanager.log-dirs}>>>
but note that hadoopwinutilsvc XML configuration processing does not do substitutions so the value must be the final value. All paths
must be absolute and no environment variable substitution will be performed. The paths are compared LOCAL_INVARIANT case insensitive string comparison,
the file path validated must start with one of the paths listed in local-dirs configuration. Use comma as path separator:<<<,>>>
<<<yarn.nodemanager.windows-secure-container-executor.job-name>>> should contain an Windows NT job name that all containers should be added to.
This configuration is optional. If not set, the container is not added to a global NodeManager job. Normally this should be set to the job that the NM is assigned to,
so that killing the NM kills also all containers. Hadoopwinutilsvc will not attempt to create this job, the job must exists when the container is launched.
If the value is set and the job does not exists, container launch will fail with error 2 <<<The system cannot find the file specified>>>.
Note that this global NM job is not related to the container job, which always gets created for each container and is named after the container ID.
This setting controls a global job that spans all containers and the parent NM, and as such it requires nested jobs.
Nested jobs are available only post Windows 8 and Windows Server 2012.
*** Useful Links *** Useful Links
* {{{http://msdn.microsoft.com/en-us/magazine/cc188757.aspx}Exploring S4U Kerberos Extensions in Windows Server 2003}} * {{{http://msdn.microsoft.com/en-us/magazine/cc188757.aspx}Exploring S4U Kerberos Extensions in Windows Server 2003}}
* {{{http://msdn.microsoft.com/en-us/library/windows/desktop/hh448388(v=vs.85).aspx}Nested Jobs}}
* {{{https://issues.apache.org/jira/browse/YARN-1063}Winutils needs ability to create task as domain user}} * {{{https://issues.apache.org/jira/browse/YARN-1063}Winutils needs ability to create task as domain user}}
* {{{https://issues.apache.org/jira/browse/YARN-1972}Implement secure Windows Container Executor}} * {{{https://issues.apache.org/jira/browse/YARN-1972}Implement secure Windows Container Executor}}
* {{{https://issues.apache.org/jira/browse/YARN-2198}Remove the need to run NodeManager as privileged account for Windows Secure Container Executor}}