From 76f1830cdabb731316f7031e88ac503b30f23e62 Mon Sep 17 00:00:00 2001 From: Mattias Andrée Date: Mon, 11 Aug 2014 11:32:20 +0200 Subject: the kernel messes with /proc/*/exe (security precaution?), so we read /proc/self/exec when we start the server MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Signed-off-by: Mattias Andrée --- src/libmdsserver/util.c | 57 +++++++++++++++++++++++++++++++++++++++---------- src/libmdsserver/util.h | 24 +++++++++++++++++++++ src/mds-base.c | 5 +++++ 3 files changed, 75 insertions(+), 11 deletions(-) (limited to 'src') diff --git a/src/libmdsserver/util.c b/src/libmdsserver/util.c index 83641f2..469b616 100644 --- a/src/libmdsserver/util.c +++ b/src/libmdsserver/util.c @@ -33,6 +33,14 @@ +/** + * The content of `/proc/self/exe`, when + * `prepare_reexec` was invoked. + */ +static char self_exe[PATH_MAX] = {0}; + + + /** * Convert a client ID string into a client ID integer * @@ -72,28 +80,55 @@ char* getenv_nonempty(const char* var) } +/** + * Prepare the server so that it can reexec into + * a newer version of the executed file. + * + * This is required for two reasons: + * 1: We cannot use argv[0] as PATH-resolution may + * cause it to reexec into another pathname, and + * maybe to wrong program. Additionally argv[0] + * may not even refer to the program, and chdir + * could also hinter its use. + * 2: The kernel appends ` (deleted)` to + * `/proc/self/exe` once it has been removed, + * so it cannot be replaced. + * + * The function will should be called immediately, it + * will store the content of `/proc/self/exe`. + * + * @return Zero on success, -1 on error + */ +int prepare_reexec(void) +{ + ssize_t len; + len = readlink(SELF_EXE, self_exe, (sizeof(self_exe) / sizeof(char)) - 1); + if (len < 0) + return -1; + /* ‘readlink() does not append a null byte to buf.’ */ + self_exe[len] = '\0'; + return 0; +} + + /** * Re-exec the server. * This function only returns on failure. * - * @param argc The number of elements in `argv` - * @param argv The command line arguments - * @param reexeced Whether the server has previously been re-exec:ed + * If `prepare_reexec` failed or has not been called, + * `argv[0]` will be used as a fallback. + * + * @param argc The number of elements in `argv` + * @param argv The command line arguments + * @param reexeced Whether the server has previously been re-exec:ed */ void reexec_server(int argc, char** argv, int reexeced) { - char readlink_buf[PATH_MAX]; - ssize_t readlink_ptr; char** reexec_args; char** reexec_args_; int i; /* Re-exec the server. */ - readlink_ptr = readlink(SELF_EXE, readlink_buf, (sizeof(readlink_buf) / sizeof(char)) - 1); - if (readlink_ptr < 0) - return; - /* ‘readlink() does not append a null byte to buf.’ */ - readlink_buf[readlink_ptr] = '\0'; reexec_args = alloca(((size_t)argc + 2) * sizeof(char*)); reexec_args_ = reexec_args; if (reexeced == 0) @@ -110,7 +145,7 @@ void reexec_server(int argc, char** argv, int reexeced) for (i = 1; i < argc; i++) reexec_args_[i] = argv[i]; reexec_args_[argc] = NULL; - execv(readlink_buf, reexec_args); + execv(self_exe[0] ? self_exe : argv[0], reexec_args); } diff --git a/src/libmdsserver/util.h b/src/libmdsserver/util.h index 3220adb..7e125dd 100644 --- a/src/libmdsserver/util.h +++ b/src/libmdsserver/util.h @@ -40,10 +40,34 @@ uint64_t parse_client_id(const char* str); */ char* getenv_nonempty(const char* var); +/** + * Prepare the server so that it can reexec into + * a newer version of the executed file. + * + * This is required for two reasons: + * 1: We cannot use argv[0] as PATH-resolution may + * cause it to reexec into another pathname, and + * maybe to wrong program. Additionally argv[0] + * may not even refer to the program, and chdir + * could also hinter its use. + * 2: The kernel appends ` (deleted)` to + * `/proc/self/exe` once it has been removed, + * so it cannot be replaced. + * + * The function will should be called immediately, it + * will store the content of `/proc/self/exe`. + * + * @return Zero on success, -1 on error + */ +int prepare_reexec(void); + /** * Re-exec the server. * This function only returns on failure. * + * If `prepare_reexec` failed or has not been called, + * `argv[0]` will be used as a fallback. + * * @param argc The number of elements in `argv` * @param argv The command line arguments * @param reexeced Whether the server has previously been re-exec:ed diff --git a/src/mds-base.c b/src/mds-base.c index 1bae36b..95eb4e0 100644 --- a/src/mds-base.c +++ b/src/mds-base.c @@ -453,6 +453,11 @@ int main(int argc_, char** argv_) fail_if (drop_privileges()); + /* Use /proc/self/exe when re:exec-ing */ + if (prepare_reexec()) + xperror(*argv); + + /* Sanity check the number of command line arguments. */ exit_if (argc > ARGC_LIMIT + LIBEXEC_ARGC_EXTRA_LIMIT, eprint("that number of arguments is ridiculous, I will not allow it.");); -- cgit v1.2.3-70-g09d2