YARN-5121. fix some container-executor portability issues. Contributed by Allen Wittenauer.

This commit is contained in:
Chris Nauroth 2016-07-30 08:26:00 -07:00
parent ce93595d7a
commit ef501b1a0b
16 changed files with 691 additions and 96 deletions

View File

@ -414,6 +414,38 @@ THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
(INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE
OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
For hadoop-yarn-project/hadoop-yarn/hadoop-yarn-server/hadoop-yarn-server-nodemanager/src/main/native/container-executor/impl/compat/{fstatat|openat|unlinkat}.h:
Copyright (c) 2012 The FreeBSD Foundation
All rights reserved.
This software was developed by Pawel Jakub Dawidek under sponsorship from
the FreeBSD Foundation.
Redistribution and use in source and binary forms, with or without
modification, are permitted provided that the following conditions
are met:
1. Redistributions of source code must retain the above copyright
notice, this list of conditions and the following disclaimer.
2. Redistributions in binary form must reproduce the above copyright
notice, this list of conditions and the following disclaimer in the
documentation and/or other materials provided with the distribution.
THIS SOFTWARE IS PROVIDED BY THE AUTHORS AND CONTRIBUTORS ``AS IS'' AND
ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
ARE DISCLAIMED. IN NO EVENT SHALL THE AUTHORS OR CONTRIBUTORS BE LIABLE
FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS
OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY
OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
SUCH DAMAGE.
=============
The binary distribution of this product bundles binaries of leveldb
(http://code.google.com/p/leveldb/), which is available under the following
license:

View File

@ -273,6 +273,18 @@
</configuration>
</plugin>
<plugin>
<groupId>org.apache.rat</groupId>
<artifactId>apache-rat-plugin</artifactId>
<configuration>
<excludes>
<exclude>src/main/native/container-executor/impl/compat/fstatat.h</exclude>
<exclude>src/main/native/container-executor/impl/compat/openat.h</exclude>
<exclude>src/main/native/container-executor/impl/compat/unlinkat.h</exclude>
</excludes>
</configuration>
</plugin>
<plugin>
<groupId>org.apache.hadoop</groupId>
<artifactId>hadoop-maven-plugins</artifactId>

View File

@ -24,7 +24,20 @@ string(REPLACE "-D_FILE_OFFSET_BITS=64" "" CMAKE_C_FLAGS "${CMAKE_C_FLAGS}")
string(REPLACE "-D_FILE_OFFSET_BITS=64" "" CMAKE_CXX_FLAGS "${CMAKE_CXX_FLAGS}")
include(CheckFunctionExists)
check_function_exists(canonicalize_file_name HAVE_CANONICALIZE_FILE_NAME)
check_function_exists(fcloseall HAVE_FCLOSEALL)
check_function_exists(fchmodat HAVE_FCHMODAT)
check_function_exists(fdopendir HAVE_FDOPENDIR)
check_function_exists(fstatat HAVE_FSTATAT)
check_function_exists(openat HAVE_OPENAT)
check_function_exists(unlinkat HAVE_UNLINKAT)
if(APPLE)
include_directories( /System/Library/Frameworks )
find_library(COCOA_LIBRARY Cocoa)
mark_as_advanced(COCOA_LIBRARY)
set(EXTRA_LIBS ${COCOA_LIBRARY})
endif(APPLE)
function(output_directory TGT DIR)
set_target_properties(${TGT} PROPERTIES
@ -46,6 +59,7 @@ configure_file(${CMAKE_SOURCE_DIR}/config.h.cmake ${CMAKE_BINARY_DIR}/config.h)
add_library(container
main/native/container-executor/impl/configuration.c
main/native/container-executor/impl/container-executor.c
main/native/container-executor/impl/get_executable.c
)
add_executable(container-executor
@ -60,6 +74,6 @@ add_executable(test-container-executor
main/native/container-executor/test/test-container-executor.c
)
target_link_libraries(test-container-executor
container
container ${EXTRA_LIBS}
)
output_directory(test-container-executor target/usr/local/bin)

View File

@ -20,6 +20,12 @@
#cmakedefine HADOOP_CONF_DIR "@HADOOP_CONF_DIR@"
#cmakedefine HAVE_FCLOSEALL "@HAVE_FCLOSEALL@"
#cmakedefine HAVE_CANONICALIZE_FILE_NAME @HAVE_CANONICALIZE_FILE_NAME@
#cmakedefine HAVE_FCHMODAT @HAVE_FCHMODAT@
#cmakedefine HAVE_FCLOSEALL @HAVE_FCLOSEALL@
#cmakedefine HAVE_FDOPENDIR @HAVE_FDOPENDIR@
#cmakedefine HAVE_FSTATAT @HAVE_FSTATAT@
#cmakedefine HAVE_OPENAT @HAVE_OPENAT@
#cmakedefine HAVE_UNLINKAT @HAVE_UNLINKAT@
#endif

View File

@ -0,0 +1,56 @@
/**
* 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.
*/
#ifndef _FCHMODAT_H_
#define _FCHMODAT_H_
#include <sys/stat.h>
#include <unistd.h>
#define AT_SYMLINK_NOFOLLOW 0x01
static int
fchmodat(int fd, const char *path, mode_t mode, int flag)
{
int cfd, error, ret;
cfd = open(".", O_RDONLY | O_DIRECTORY);
if (cfd == -1)
return (-1);
if (fchdir(fd) == -1) {
error = errno;
(void)close(cfd);
errno = error;
return (-1);
}
if (flag == AT_SYMLINK_NOFOLLOW)
ret = lchmod(path, mode);
else
ret = chmod(path, mode);
error = errno;
(void)fchdir(cfd);
(void)close(cfd);
errno = error;
return (ret);
}
#endif /* !_FCHMODAT_H_ */

View File

@ -0,0 +1,52 @@
/**
* 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.
*/
#ifndef _FDOPENDIR_H_
#define _FDOPENDIR_H_
#include <fcntl.h>
#include <stdarg.h>
#include <unistd.h>
DIR *
fdopendir(int fd)
{
int cfd, error;
DIR *dfd;
cfd = open(".", O_RDONLY | O_DIRECTORY);
if (cfd == -1)
return (NULL);
if (fchdir(fd) == -1) {
error = errno;
(void)close(cfd);
errno = error;
return (NULL);
}
dfd=opendir(".");
error = errno;
(void)fchdir(cfd);
(void)close(cfd);
errno = error;
return (dfd);
}
#endif /* !_FDOPENDIR_H_ */

View File

@ -0,0 +1,67 @@
/*-
* Copyright (c) 2012 The FreeBSD Foundation
* All rights reserved.
*
* This software was developed by Pawel Jakub Dawidek under sponsorship from
* the FreeBSD Foundation.
*
* Redistribution and use in source and binary forms, with or without
* modification, are permitted provided that the following conditions
* are met:
* 1. Redistributions of source code must retain the above copyright
* notice, this list of conditions and the following disclaimer.
* 2. Redistributions in binary form must reproduce the above copyright
* notice, this list of conditions and the following disclaimer in the
* documentation and/or other materials provided with the distribution.
*
* THIS SOFTWARE IS PROVIDED BY THE AUTHORS AND CONTRIBUTORS ``AS IS'' AND
* ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
* IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
* ARE DISCLAIMED. IN NO EVENT SHALL THE AUTHORS OR CONTRIBUTORS BE LIABLE
* FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
* DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS
* OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
* HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
* LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY
* OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
* SUCH DAMAGE.
*/
#ifndef _FSTATAT_H_
#define _FSTATAT_H_
#include <sys/stat.h>
#include <unistd.h>
#define AT_SYMLINK_NOFOLLOW 0x01
static int
fstatat(int fd, const char *path, struct stat *buf, int flag)
{
int cfd, error, ret;
cfd = open(".", O_RDONLY | O_DIRECTORY);
if (cfd == -1)
return (-1);
if (fchdir(fd) == -1) {
error = errno;
(void)close(cfd);
errno = error;
return (-1);
}
if (flag == AT_SYMLINK_NOFOLLOW)
ret = lstat(path, buf);
else
ret = stat(path, buf);
error = errno;
(void)fchdir(cfd);
(void)close(cfd);
errno = error;
return (ret);
}
#endif /* !_FSTATAT_H_ */

View File

@ -0,0 +1,74 @@
/*-
* Copyright (c) 2012 The FreeBSD Foundation
* All rights reserved.
*
* This software was developed by Pawel Jakub Dawidek under sponsorship from
* the FreeBSD Foundation.
*
* Redistribution and use in source and binary forms, with or without
* modification, are permitted provided that the following conditions
* are met:
* 1. Redistributions of source code must retain the above copyright
* notice, this list of conditions and the following disclaimer.
* 2. Redistributions in binary form must reproduce the above copyright
* notice, this list of conditions and the following disclaimer in the
* documentation and/or other materials provided with the distribution.
*
* THIS SOFTWARE IS PROVIDED BY THE AUTHORS AND CONTRIBUTORS ``AS IS'' AND
* ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
* IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
* ARE DISCLAIMED. IN NO EVENT SHALL THE AUTHORS OR CONTRIBUTORS BE LIABLE
* FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
* DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS
* OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
* HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
* LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY
* OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
* SUCH DAMAGE.
*/
#ifndef _OPENAT_H_
#define _OPENAT_H_
#include <fcntl.h>
#include <stdarg.h>
#include <unistd.h>
static int
openat(int fd, const char *path, int flags, ...)
{
int cfd, ffd, error;
cfd = open(".", O_RDONLY | O_DIRECTORY);
if (cfd == -1)
return (-1);
if (fchdir(fd) == -1) {
error = errno;
(void)close(cfd);
errno = error;
return (-1);
}
if ((flags & O_CREAT) != 0) {
va_list ap;
int mode;
va_start(ap, flags);
mode = va_arg(ap, int);
va_end(ap);
ffd = open(path, flags, mode);
} else {
ffd = open(path, flags);
}
error = errno;
(void)fchdir(cfd);
(void)close(cfd);
errno = error;
return (ffd);
}
#endif /* !_OPENAT_H_ */

View File

@ -0,0 +1,67 @@
/*-
* Copyright (c) 2012 The FreeBSD Foundation
* All rights reserved.
*
* This software was developed by Pawel Jakub Dawidek under sponsorship from
* the FreeBSD Foundation.
*
* Redistribution and use in source and binary forms, with or without
* modification, are permitted provided that the following conditions
* are met:
* 1. Redistributions of source code must retain the above copyright
* notice, this list of conditions and the following disclaimer.
* 2. Redistributions in binary form must reproduce the above copyright
* notice, this list of conditions and the following disclaimer in the
* documentation and/or other materials provided with the distribution.
*
* THIS SOFTWARE IS PROVIDED BY THE AUTHORS AND CONTRIBUTORS ``AS IS'' AND
* ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
* IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
* ARE DISCLAIMED. IN NO EVENT SHALL THE AUTHORS OR CONTRIBUTORS BE LIABLE
* FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
* DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS
* OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
* HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
* LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY
* OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
* SUCH DAMAGE.
*/
#ifndef _UNLINKAT_H_
#define _UNLINKAT_H_
#include <fcntl.h>
#include <unistd.h>
#define AT_REMOVEDIR 0x01
static int
unlinkat(int fd, const char *path, int flag)
{
int cfd, error, ret;
cfd = open(".", O_RDONLY | O_DIRECTORY);
if (cfd == -1)
return (-1);
if (fchdir(fd) == -1) {
error = errno;
(void)close(cfd);
errno = error;
return (-1);
}
if (flag == AT_REMOVEDIR)
ret = rmdir(path);
else
ret = unlink(path);
error = errno;
(void)fchdir(cfd);
(void)close(cfd);
errno = error;
return (ret);
}
#endif /* !_UNLINKAT_H_ */

View File

@ -17,7 +17,7 @@
*/
// ensure we get the posix version of dirname by including this first
#include <libgen.h>
#include <libgen.h>
#include "configuration.h"
#include "container-executor.h"
@ -68,7 +68,7 @@ static int is_only_root_writable(const char *file) {
return 0;
}
if ((file_stat.st_mode & (S_IWGRP | S_IWOTH)) != 0) {
fprintf(ERRORFILE,
fprintf(ERRORFILE,
"File %s must not be world or group writable, but is %03lo\n",
file, (unsigned long)file_stat.st_mode & (~S_IFMT));
return 0;
@ -92,8 +92,13 @@ char *resolve_config_path(const char* file_name, const char *root) {
real_fname = buffer;
}
#ifdef HAVE_CANONICALIZE_FILE_NAME
char * ret = (real_fname == NULL) ? NULL : canonicalize_file_name(real_fname);
#else
char * ret = (real_fname == NULL) ? NULL : realpath(real_fname, NULL);
#endif
#ifdef DEBUG
fprintf(stderr,"ret = %s\n", ret);
fprintf(stderr, "resolve_config_path(file_name=%s,root=%s)=%s\n",
file_name, root ? root : "null", ret ? ret : "null");
#endif
@ -102,7 +107,7 @@ char *resolve_config_path(const char* file_name, const char *root) {
/**
* Ensure that the configuration file and all of the containing directories
* are only writable by root. Otherwise, an attacker can change the
* are only writable by root. Otherwise, an attacker can change the
* configuration and potentially cause damage.
* returns 0 if permissions are ok
*/
@ -155,7 +160,7 @@ void read_config(const char* file_name, struct configuration *cfg) {
exit(OUT_OF_MEMORY);
}
size_read = getline(&line,&linesize,conf_file);
//feof returns true only after we read past EOF.
//so a file with no new line, at last can reach this place
//if size_read returns negative check for eof condition
@ -235,7 +240,7 @@ void read_config(const char* file_name, struct configuration *cfg) {
free(line);
}
//close the file
fclose(conf_file);

View File

@ -24,7 +24,7 @@
/**
* Ensure that the configuration file and all of the containing directories
* are only writable by root. Otherwise, an attacker can change the
* are only writable by root. Otherwise, an attacker can change the
* configuration and potentially cause damage.
* returns 0 if permissions are ok
*/
@ -78,7 +78,7 @@ void free_configurations(struct configuration *cfg);
/**
* If str is a string of the form key=val, find 'key'
*
*
* @param input The input string
* @param out Where to put the output string.
* @param out_len The length of the output buffer.
@ -91,7 +91,7 @@ int get_kv_key(const char *input, char *out, size_t out_len);
/**
* If str is a string of the form key=val, find 'val'
*
*
* @param input The input string
* @param out Where to put the output string.
* @param out_len The length of the output buffer.

View File

@ -40,6 +40,28 @@
#include <sys/mount.h>
#include <sys/wait.h>
#include "config.h"
#ifndef HAVE_FCHMODAT
#include "compat/fchmodat.h"
#endif
#ifndef HAVE_FDOPENDIR
#include "compat/fdopendir.h"
#endif
#ifndef HAVE_FSTATAT
#include "compat/fstatat.h"
#endif
#ifndef HAVE_OPENAT
#include "compat/openat.h"
#endif
#ifndef HAVE_UNLINKAT
#include "compat/unlinkat.h"
#endif
static const int DEFAULT_MIN_USERID = 1000;
static const char* DEFAULT_BANNED_USERS[] = {"yarn", "mapred", "hdfs", "bin", 0};
@ -84,31 +106,14 @@ char *get_nodemanager_group() {
return get_value(NM_GROUP_KEY, &executor_cfg);
}
/**
* get the executable filename.
*/
char* get_executable() {
char buffer[EXECUTOR_PATH_MAX];
snprintf(buffer, EXECUTOR_PATH_MAX, "/proc/%" PRId64 "/exe", (int64_t)getpid());
char *filename = malloc(EXECUTOR_PATH_MAX);
ssize_t len = readlink(buffer, filename, EXECUTOR_PATH_MAX);
if (len == -1) {
fprintf(ERRORFILE, "Can't get executable name from %s - %s\n", buffer,
strerror(errno));
exit(-1);
} else if (len >= EXECUTOR_PATH_MAX) {
fprintf(ERRORFILE, "Executable name %.*s is longer than %d characters.\n",
EXECUTOR_PATH_MAX, filename, EXECUTOR_PATH_MAX);
exit(-1);
}
filename[len] = '\0';
return filename;
}
int check_executor_permissions(char *executable_file) {
errno = 0;
#ifdef HAVE_CANONICALIZE_FILE_NAME
char * resolved_path = canonicalize_file_name(executable_file);
#else
char * resolved_path = realpath(executable_file, NULL);
#endif
if (resolved_path == NULL) {
fprintf(ERRORFILE,
"Error resolving the canonical name for the executable : %s!",
@ -119,7 +124,7 @@ int check_executor_permissions(char *executable_file) {
struct stat filestat;
errno = 0;
if (stat(resolved_path, &filestat) != 0) {
fprintf(ERRORFILE,
fprintf(ERRORFILE,
"Could not stat the executable : %s!.\n", strerror(errno));
return -1;
}
@ -181,6 +186,7 @@ static int change_effective_user(uid_t user, gid_t group) {
return 0;
}
#ifdef __linux
/**
* Write the pid of the current process to the cgroup file.
* cgroup_file: Path to cgroup file where pid needs to be written to.
@ -218,6 +224,7 @@ static int write_pid_to_cgroup_as_root(const char* cgroup_file, pid_t pid) {
return 0;
}
#endif
/**
* Write the pid of the current process into the pid file.
@ -384,7 +391,7 @@ static int wait_and_write_exit_code(pid_t pid, const char* exit_code_file) {
* priviledges.
*/
int change_user(uid_t user, gid_t group) {
if (user == getuid() && user == geteuid() &&
if (user == getuid() && user == geteuid() &&
group == getgid() && group == getegid()) {
return 0;
}
@ -396,7 +403,7 @@ int change_user(uid_t user, gid_t group) {
return SETUID_OPER_FAILED;
}
if (setgid(group) != 0) {
fprintf(LOGFILE, "unable to set group to %d - %s\n", group,
fprintf(LOGFILE, "unable to set group to %d - %s\n", group,
strerror(errno));
fprintf(LOGFILE, "Real: %d:%d; Effective: %d:%d\n",
getuid(), getgid(), geteuid(), getegid());
@ -415,7 +422,7 @@ int change_user(uid_t user, gid_t group) {
/**
* Utility function to concatenate argB to argA using the concat_pattern.
*/
char *concatenate(char *concat_pattern, char *return_path_name,
char *concatenate(char *concat_pattern, char *return_path_name,
int numArgs, ...) {
va_list ap;
va_start(ap, numArgs);
@ -592,12 +599,12 @@ int check_dir(const char* npath, mode_t st_mode, mode_t desired, int finalCompon
* Function to prepare the container directories.
* It creates the container work and log directories.
*/
static int create_container_directories(const char* user, const char *app_id,
static int create_container_directories(const char* user, const char *app_id,
const char *container_id, char* const* local_dir, char* const* log_dir, const char *work_dir) {
// create dirs as 0750
const mode_t perms = S_IRWXU | S_IRGRP | S_IXGRP;
if (app_id == NULL || container_id == NULL || user == NULL || user_detail == NULL || user_detail->pw_name == NULL) {
fprintf(LOGFILE,
fprintf(LOGFILE,
"Either app_id, container_id or the user passed is null.\n");
return -1;
}
@ -605,7 +612,7 @@ static int create_container_directories(const char* user, const char *app_id,
int result = -1;
char* const* local_dir_ptr;
for(local_dir_ptr = local_dir; *local_dir_ptr != NULL; ++local_dir_ptr) {
char *container_dir = get_container_work_directory(*local_dir_ptr, user, app_id,
char *container_dir = get_container_work_directory(*local_dir_ptr, user, app_id,
container_id);
if (container_dir == NULL) {
return -1;
@ -720,7 +727,7 @@ struct passwd* check_user(const char *user) {
char *end_ptr = NULL;
min_uid = strtol(min_uid_str, &end_ptr, 10);
if (min_uid_str == end_ptr || *end_ptr != '\0') {
fprintf(LOGFILE, "Illegal value of %s for %s in configuration\n",
fprintf(LOGFILE, "Illegal value of %s for %s in configuration\n",
min_uid_str, MIN_USERID_KEY);
fflush(LOGFILE);
free(min_uid_str);
@ -848,7 +855,7 @@ int create_directory_for_user(const char* path) {
}
if (change_effective_user(user, group) != 0) {
fprintf(LOGFILE, "Failed to change user to %i - %i\n", user, group);
ret = -1;
}
return ret;
@ -881,14 +888,14 @@ static int open_file_as_nm(const char* filename) {
* The input stream is closed.
* Return 0 if everything is ok.
*/
static int copy_file(int input, const char* in_filename,
static int copy_file(int input, const char* in_filename,
const char* out_filename, mode_t perm) {
const int buffer_size = 128*1024;
char buffer[buffer_size];
int out_fd = open(out_filename, O_WRONLY|O_CREAT|O_EXCL|O_NOFOLLOW, perm);
if (out_fd == -1) {
fprintf(LOGFILE, "Can't open %s for output - %s\n", out_filename,
fprintf(LOGFILE, "Can't open %s for output - %s\n", out_filename,
strerror(errno));
fflush(LOGFILE);
return -1;
@ -910,13 +917,13 @@ static int copy_file(int input, const char* in_filename,
len = read(input, buffer, buffer_size);
}
if (len < 0) {
fprintf(LOGFILE, "Failed to read file %s - %s\n", in_filename,
fprintf(LOGFILE, "Failed to read file %s - %s\n", in_filename,
strerror(errno));
close(out_fd);
return -1;
}
if (close(out_fd) != 0) {
fprintf(LOGFILE, "Failed to close file %s - %s\n", out_filename,
fprintf(LOGFILE, "Failed to close file %s - %s\n", out_filename,
strerror(errno));
return -1;
}
@ -1328,20 +1335,22 @@ int launch_docker_container_as_user(const char * user, const char *app_id,
}
if (pid != 0) {
#ifdef __linux
fprintf(LOGFILE, "Writing to cgroup task files...\n");
// cgroups-based resource enforcement
if (resources_key != NULL && ! strcmp(resources_key, "cgroups")) {
// write pid to cgroups
char* const* cgroup_ptr;
for (cgroup_ptr = resources_values; cgroup_ptr != NULL &&
// write pid to cgroups
char* const* cgroup_ptr;
for (cgroup_ptr = resources_values; cgroup_ptr != NULL &&
*cgroup_ptr != NULL; ++cgroup_ptr) {
if (strcmp(*cgroup_ptr, "none") != 0 &&
if (strcmp(*cgroup_ptr, "none") != 0 &&
write_pid_to_cgroup_as_root(*cgroup_ptr, pid) != 0) {
exit_code = WRITE_CGROUP_FAILED;
goto cleanup;
}
}
exit_code = WRITE_CGROUP_FAILED;
goto cleanup;
}
}
}
#endif
// write pid to pidfile
fprintf(LOGFILE, "Writing pid file...\n");
@ -1481,7 +1490,7 @@ int launch_container_as_user(const char *user, const char *app_id,
goto cleanup;
}
// setsid
// setsid
pid_t pid = setsid();
if (pid == -1) {
exit_code = SETSID_OPER_FAILED;
@ -1496,12 +1505,13 @@ int launch_container_as_user(const char *user, const char *app_id,
goto cleanup;
}
#ifdef __linux
fprintf(LOGFILE, "Writing to cgroup task files...\n");
// cgroups-based resource enforcement
if (resources_key != NULL && ! strcmp(resources_key, "cgroups")) {
// write pid to cgroups
char* const* cgroup_ptr;
for (cgroup_ptr = resources_values; cgroup_ptr != NULL &&
for (cgroup_ptr = resources_values; cgroup_ptr != NULL &&
*cgroup_ptr != NULL; ++cgroup_ptr) {
if (strcmp(*cgroup_ptr, "none") != 0 &&
write_pid_to_cgroup_as_root(*cgroup_ptr, pid) != 0) {
@ -1510,6 +1520,7 @@ int launch_container_as_user(const char *user, const char *app_id,
}
}
}
#endif
fprintf(LOGFILE, "Creating local dirs...\n");
exit_code = create_local_dirs(user, app_id, container_id,
@ -1569,11 +1580,8 @@ int signal_container_as_user(const char *user, int pid, int sig) {
if (kill(-pid, sig) < 0) {
if(errno != ESRCH) {
fprintf(LOGFILE,
"Error signalling process group %d with signal %d - %s\n",
-pid, sig, strerror(errno));
fprintf(stderr,
"Error signalling process group %d with signal %d - %s\n",
fprintf(LOGFILE,
"Error signalling process group %d with signal %d - %s\n",
-pid, sig, strerror(errno));
fflush(LOGFILE);
return UNABLE_TO_SIGNAL_CONTAINER;
@ -1794,7 +1802,7 @@ int recursive_unlink_children(const char *name)
* full_path : the path to delete
* needs_tt_user: the top level directory must be deleted by the tt user.
*/
static int delete_path(const char *full_path,
static int delete_path(const char *full_path,
int needs_tt_user) {
int ret;
@ -1900,7 +1908,7 @@ void chown_dir_contents(const char *dir_path, uid_t uid, gid_t gid) {
char *buf = stpncpy(path_tmp, dir_path, strlen(dir_path));
*buf++ = '/';
dp = opendir(dir_path);
if (dp != NULL) {
while ((ep = readdir(dp)) != NULL) {
@ -1934,7 +1942,7 @@ int mount_cgroup(const char *pair, const char *hierarchy) {
get_kv_value(pair, mount_path, strlen(pair)) < 0) {
fprintf(LOGFILE, "Failed to mount cgroup controller; invalid option: %s\n",
pair);
result = -1;
result = -1;
} else {
if (mount("none", mount_path, "cgroup", 0, controller) == 0) {
char *buf = stpncpy(hier_path, mount_path, strlen(mount_path));

View File

@ -177,7 +177,7 @@ int signal_container_as_user(const char *user, int pid, int sig);
// delete a directory (or file) recursively as the user. The directory
// could optionally be relative to the baseDir set of directories (if the same
// directory appears on multiple disk volumes, the disk volumes should be passed
// as the baseDirs). If baseDirs is not specified, then dir_to_be_deleted is
// as the baseDirs). If baseDirs is not specified, then dir_to_be_deleted is
// assumed as the absolute path
int delete_as_user(const char *user,
const char *dir_to_be_deleted,

View File

@ -0,0 +1,127 @@
/**
* 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.
*/
/*
* This code implements OS-specific ways to get the absolute
* filename of the executable. Typically, one would use
* realpath(argv[0]) (or equivalent), however, because this
* code runs as setuid and will be used later on to determine
* relative paths, we want something a big more secure
* since argv[0] is replaceable by malicious code.
*
* NOTE! The value returned will be free()'d later on!
*
*/
#include <errno.h>
#include <stdio.h>
#include <string.h>
#include <stdlib.h>
#include <unistd.h>
#include "config.h"
#include "configuration.h"
#include "container-executor.h"
/*
* A generic function to read a link and return
* the value for use with System V procfs.
* With much thanks to Tom Killian, Roger Faulkner,
* and Ron Gomes, this is pretty generic code.
*
* The various BSDs do not have (reliably)
* have /proc. Custom implementations follow.
*/
char *__get_exec_readproc(char *procfn) {
char *filename;
ssize_t len;
filename = malloc(EXECUTOR_PATH_MAX);
if (!filename) {
fprintf(ERRORFILE,"cannot allocate memory for filename: %s\n",strerror(errno));
exit(-1);
}
len = readlink(procfn, filename, EXECUTOR_PATH_MAX);
if (len == -1) {
fprintf(ERRORFILE,"Can't get executable name from %s - %s\n", procfn,
strerror(errno));
exit(-1);
} else if (len >= EXECUTOR_PATH_MAX) {
fprintf(ERRORFILE,"Executable name %.*s is longer than %d characters.\n",
EXECUTOR_PATH_MAX, filename, EXECUTOR_PATH_MAX);
exit(-1);
}
filename[len] = '\0';
return filename;
}
#ifdef __APPLE__
/*
* Mac OS X doesn't have a procfs, but there is
* libproc which we can use instead. It is available
* in most modern versions of OS X as of this writing (2016).
*/
#include <libproc.h>
char* get_executable() {
char *filename;
pid_t pid;
filename = malloc(PROC_PIDPATHINFO_MAXSIZE);
if (!filename) {
fprintf(ERRORFILE,"cannot allocate memory for filename: %s\n",strerror(errno));
exit(-1);
}
pid = getpid();
if (proc_pidpath(pid,filename,PROC_PIDPATHINFO_MAXSIZE) <= 0) {
fprintf(ERRORFILE,"Can't get executable name from pid %u - %s\n", pid,
strerror(errno));
exit(-1);
}
return filename;
}
#elif defined(__linux)
char* get_executable() {
return __get_exec_readproc("/proc/self/exe");
}
#elif defined(__sun)
/*
* It's tempting to use getexecname(), but there is no guarantee
* we will get a full path and worse, we'd be reliant on getcwd()
* being where our exec is at. Instead, we'll use the /proc
* method, using the "invisible" /proc/self link that only the
* process itself can see. (Anyone that tells you /proc/self
* doesn't exist on Solaris hasn't read the proc(4) man page.)
*/
char* get_executable() {
return __get_exec_readproc("/proc/self/path/a.out");
}
#else
#error Cannot safely determine executable path on this operating system.
#endif

View File

@ -83,7 +83,7 @@ static void flush_and_close_log_files() {
fclose(LOGFILE);
LOGFILE = NULL;
}
if (ERRORFILE != NULL) {
fflush(ERRORFILE);
fclose(ERRORFILE);
@ -96,19 +96,27 @@ in case of validation failures. Also sets up configuration / group information e
This function is to be called in every invocation of container-executor, irrespective
of whether an explicit checksetup operation is requested. */
static void assert_valid_setup(char *current_executable) {
static void assert_valid_setup() {
int ret;
char *executable_file = get_executable();
if (!executable_file) {
fprintf(ERRORFILE,"realpath of executable: %s\n",strerror(errno));
flush_and_close_log_files();
exit(-1);
}
char *orig_conf_file = HADOOP_CONF_DIR "/" CONF_FILENAME;
char *conf_file = resolve_config_path(orig_conf_file, current_executable);
char *conf_file = resolve_config_path(orig_conf_file, executable_file);
if (conf_file == NULL) {
free(executable_file);
fprintf(ERRORFILE, "Configuration file %s not found.\n", orig_conf_file);
flush_and_close_log_files();
exit(INVALID_CONFIG_FILE);
}
if (check_configuration_permissions(conf_file) != 0) {
free(executable_file);
flush_and_close_log_files();
exit(INVALID_CONFIG_FILE);
}
@ -118,28 +126,42 @@ static void assert_valid_setup(char *current_executable) {
// look up the node manager group in the config file
char *nm_group = get_nodemanager_group();
if (nm_group == NULL) {
free(executable_file);
fprintf(ERRORFILE, "Can't get configured value for %s.\n", NM_GROUP_KEY);
flush_and_close_log_files();
exit(INVALID_CONFIG_FILE);
}
struct group *group_info = getgrnam(nm_group);
if (group_info == NULL) {
free(executable_file);
fprintf(ERRORFILE, "Can't get group information for %s - %s.\n", nm_group,
strerror(errno));
flush_and_close_log_files();
exit(INVALID_CONFIG_FILE);
}
set_nm_uid(getuid(), group_info->gr_gid);
// if we are running from a setuid executable, make the real uid root
setuid(0);
// set the real and effective group id to the node manager group
setgid(group_info->gr_gid);
/*
* if we are running from a setuid executable, make the real uid root
* we're going to ignore this result just in case we aren't.
*/
ret=setuid(0);
/*
* set the real and effective group id to the node manager group
* we're going to ignore this result just in case we aren't
*/
ret=setgid(group_info->gr_gid);
/* make the unused var warning to away */
ret++;
if (check_executor_permissions(executable_file) != 0) {
free(executable_file);
fprintf(ERRORFILE, "Invalid permissions on container-executor binary.\n");
flush_and_close_log_files();
exit(INVALID_CONTAINER_EXEC_PERMISSIONS);
}
free(executable_file);
}
@ -407,7 +429,7 @@ static int validate_run_as_user_commands(int argc, char **argv, int *operation)
int main(int argc, char **argv) {
open_log_files();
assert_valid_setup(argv[0]);
assert_valid_setup();
int operation;
int ret = validate_arguments(argc, argv, &operation);

View File

@ -29,7 +29,19 @@
#include <sys/stat.h>
#include <sys/wait.h>
#define TEST_ROOT "/tmp/test-container-executor"
#ifdef __APPLE__
#include <CoreFoundation/CFString.h>
#include <CoreFoundation/CFPreferences.h>
#define TMPDIR "/private/tmp"
#define RELTMPDIR "../.."
#else
#define RELTMPDIR ".."
#define TMPDIR "/tmp"
#endif
#define TEST_ROOT TMPDIR "/test-container-executor"
#define DONT_TOUCH_FILE "dont-touch-me"
#define NM_LOCAL_DIRS TEST_ROOT "/local-1%" TEST_ROOT "/local-2%" \
TEST_ROOT "/local-3%" TEST_ROOT "/local-4%" TEST_ROOT "/local-5"
@ -155,8 +167,8 @@ void check_pid_file(const char* pid_file, pid_t mypid) {
}
void test_get_user_directory() {
char *user_dir = get_user_directory("/tmp", "user");
char *expected = "/tmp/usercache/user";
char *user_dir = get_user_directory(TMPDIR, "user");
char *expected = TMPDIR "/usercache/user";
if (strcmp(user_dir, expected) != 0) {
printf("test_get_user_directory expected %s got %s\n", expected, user_dir);
exit(1);
@ -165,8 +177,8 @@ void test_get_user_directory() {
}
void test_get_app_directory() {
char *expected = "/tmp/usercache/user/appcache/app_200906101234_0001";
char *app_dir = (char *) get_app_directory("/tmp", "user",
char *expected = TMPDIR "/usercache/user/appcache/app_200906101234_0001";
char *app_dir = (char *) get_app_directory(TMPDIR, "user",
"app_200906101234_0001");
if (strcmp(app_dir, expected) != 0) {
printf("test_get_app_directory expected %s got %s\n", expected, app_dir);
@ -176,9 +188,9 @@ void test_get_app_directory() {
}
void test_get_container_directory() {
char *container_dir = get_container_work_directory("/tmp", "owen", "app_1",
char *container_dir = get_container_work_directory(TMPDIR, "owen", "app_1",
"container_1");
char *expected = "/tmp/usercache/owen/appcache/app_1/container_1";
char *expected = TMPDIR"/usercache/owen/appcache/app_1/container_1";
if (strcmp(container_dir, expected) != 0) {
printf("Fail get_container_work_directory got %s expected %s\n",
container_dir, expected);
@ -188,9 +200,9 @@ void test_get_container_directory() {
}
void test_get_container_launcher_file() {
char *expected_file = ("/tmp/usercache/user/appcache/app_200906101234_0001"
char *expected_file = (TMPDIR"/usercache/user/appcache/app_200906101234_0001"
"/launch_container.sh");
char *app_dir = get_app_directory("/tmp", "user",
char *app_dir = get_app_directory(TMPDIR, "user",
"app_200906101234_0001");
char *container_file = get_container_launcher_file(app_dir);
if (strcmp(container_file, expected_file) != 0) {
@ -241,9 +253,9 @@ void test_resolve_config_path() {
TEST_ROOT "\n");
exit(1);
}
if (strcmp(resolve_config_path(".." TEST_ROOT, TEST_ROOT), TEST_ROOT) != 0) {
if (strcmp(resolve_config_path(RELTMPDIR TEST_ROOT, TEST_ROOT), TEST_ROOT) != 0) {
printf("FAIL: failed to resolve config_name on a relative path name: "
".." TEST_ROOT " (relative to " TEST_ROOT ")");
RELTMPDIR TEST_ROOT " (relative to " TEST_ROOT ")");
exit(1);
}
}
@ -266,9 +278,9 @@ void test_delete_container() {
exit(1);
}
char* app_dir = get_app_directory(TEST_ROOT "/local-2", yarn_username, "app_1");
char* dont_touch = get_app_directory(TEST_ROOT "/local-2", yarn_username,
char* dont_touch = get_app_directory(TEST_ROOT "/local-2", yarn_username,
DONT_TOUCH_FILE);
char* container_dir = get_container_work_directory(TEST_ROOT "/local-2",
char* container_dir = get_container_work_directory(TEST_ROOT "/local-2",
yarn_username, "app_1", "container_1");
char buffer[100000];
sprintf(buffer, "mkdir -p %s/who/let/the/dogs/out/who/who", container_dir);
@ -328,9 +340,9 @@ void test_delete_container() {
void test_delete_app() {
char* app_dir = get_app_directory(TEST_ROOT "/local-2", yarn_username, "app_2");
char* dont_touch = get_app_directory(TEST_ROOT "/local-2", yarn_username,
char* dont_touch = get_app_directory(TEST_ROOT "/local-2", yarn_username,
DONT_TOUCH_FILE);
char* container_dir = get_container_work_directory(TEST_ROOT "/local-2",
char* container_dir = get_container_work_directory(TEST_ROOT "/local-2",
yarn_username, "app_2", "container_1");
char buffer[100000];
sprintf(buffer, "mkdir -p %s/who/let/the/dogs/out/who/who", container_dir);
@ -488,7 +500,7 @@ void test_signal_container_group() {
exit(0);
}
printf("Child container launched as %" PRId64 "\n", (int64_t)child);
// there's a race condition for child calling change_user and us
// there's a race condition for child calling change_user and us
// calling signal_container_as_user, hence sleeping
sleep(3);
if (signal_container_as_user(yarn_username, child, SIGKILL) != 0) {
@ -504,7 +516,7 @@ void test_signal_container_group() {
exit(1);
}
if (WTERMSIG(status) != SIGKILL) {
printf("FAIL: child was killed with %d instead of %d\n",
printf("FAIL: child was killed with %d instead of %d\n",
WTERMSIG(status), SIGKILL);
exit(1);
}
@ -550,7 +562,7 @@ void test_init_app() {
fflush(stderr);
pid_t child = fork();
if (child == -1) {
printf("FAIL: failed to fork process for init_app - %s\n",
printf("FAIL: failed to fork process for init_app - %s\n",
strerror(errno));
exit(1);
} else if (child == 0) {
@ -632,7 +644,7 @@ void test_run_container() {
printf("FAIL: failed to seteuid back to user - %s\n", strerror(errno));
exit(1);
}
if (fprintf(script, "#!/bin/bash\n"
if (fprintf(script, "#!/usr/bin/env bash\n"
"touch foobar\n"
"exit 0") < 0) {
printf("FAIL: fprintf failed - %s\n", strerror(errno));
@ -644,17 +656,17 @@ void test_run_container() {
}
fflush(stdout);
fflush(stderr);
char* container_dir = get_container_work_directory(TEST_ROOT "/local-1",
char* container_dir = get_container_work_directory(TEST_ROOT "/local-1",
yarn_username, "app_4", "container_1");
const char * pid_file = TEST_ROOT "/pid.txt";
pid_t child = fork();
if (child == -1) {
printf("FAIL: failed to fork process for init_app - %s\n",
printf("FAIL: failed to fork process for init_app - %s\n",
strerror(errno));
exit(1);
} else if (child == 0) {
if (launch_container_as_user(yarn_username, "app_4", "container_1",
if (launch_container_as_user(yarn_username, "app_4", "container_1",
container_dir, script_name, TEST_ROOT "/creds.txt", pid_file,
local_dirs, log_dirs,
"cgroups", cgroups_pids) != 0) {
@ -802,7 +814,7 @@ void test_recursive_unlink_children() {
// effective user id. If executed by a super-user everything
// gets tested. Here are different ways of execing the test binary:
// 1. regular user assuming user == yarn user
// $ test-container-executor
// $ test-container-executor
// 2. regular user with a given yarn user
// $ test-container-executor yarn_user
// 3. super user with a given user and assuming user == yarn user
@ -810,6 +822,7 @@ void test_recursive_unlink_children() {
// 4. super user with a given user and a given yarn user
// # test-container-executor user yarn_user
int main(int argc, char **argv) {
int ret;
LOGFILE = stdout;
ERRORFILE = stderr;
@ -817,7 +830,7 @@ int main(int argc, char **argv) {
if (system("chmod -R u=rwx " TEST_ROOT "; rm -fr " TEST_ROOT)) {
exit(1);
}
if (mkdirs(TEST_ROOT "/logs/userlogs", 0755) != 0) {
exit(1);
}
@ -881,10 +894,36 @@ int main(int argc, char **argv) {
test_check_user(0);
#ifdef __APPLE__
printf("OS X: disabling CrashReporter\n");
/*
* disable the "unexpectedly quit" dialog box
* because we know we're going to make our container
* do exactly that.
*/
CFStringRef crashType = CFSTR("DialogType");
CFStringRef crashModeNone = CFSTR("None");
CFStringRef crashAppID = CFSTR("com.apple.CrashReporter");
CFStringRef crashOldMode = CFPreferencesCopyAppValue(CFSTR("DialogType"), CFSTR("com.apple.CrashReporter"));
CFPreferencesSetAppValue(crashType, crashModeNone, crashAppID);
CFPreferencesAppSynchronize(crashAppID);
#endif
// the tests that change user need to be run in a subshell, so that
// when they change user they don't give up our privs
run_test_in_child("test_signal_container_group", test_signal_container_group);
#ifdef __APPLE__
/*
* put the "unexpectedly quit" dialog back
*/
CFPreferencesSetAppValue(crashType, crashOldMode, crashAppID);
CFPreferencesAppSynchronize(crashAppID);
printf("OS X: CrashReporter re-enabled\n");
#endif
// init app and run container can't be run if you aren't testing as root
if (getuid() == 0) {
// these tests do internal forks so that the change_owner and execs
@ -893,7 +932,13 @@ int main(int argc, char **argv) {
test_run_container();
}
seteuid(0);
/*
* try to seteuid(0). if it doesn't work, carry on anyway.
* we're going to capture the return value to get rid of a
* compiler warning.
*/
ret=seteuid(0);
ret++;
// test_delete_user must run as root since that's how we use the delete_as_user
test_delete_user();
free_executor_configurations();
@ -904,11 +949,19 @@ int main(int argc, char **argv) {
}
read_executor_config(TEST_ROOT "/test.cfg");
#ifdef __APPLE__
username = "_uucp";
test_check_user(1);
username = "_networkd";
test_check_user(1);
#else
username = "bin";
test_check_user(1);
username = "sys";
test_check_user(1);
#endif
run("rm -fr " TEST_ROOT);
printf("\nFinished tests\n");