diff options
author | Mattias Andrée <maandree@operamail.com> | 2014-08-11 11:32:20 +0200 |
---|---|---|
committer | Mattias Andrée <maandree@operamail.com> | 2014-08-11 11:32:20 +0200 |
commit | 76f1830cdabb731316f7031e88ac503b30f23e62 (patch) | |
tree | bcaa39f64a8f2e4f850b8fbcf9641c95196d87ed | |
parent | misc (diff) | |
download | mds-76f1830cdabb731316f7031e88ac503b30f23e62.tar.gz mds-76f1830cdabb731316f7031e88ac503b30f23e62.tar.bz2 mds-76f1830cdabb731316f7031e88ac503b30f23e62.tar.xz |
the kernel messes with /proc/*/exe (security precaution?), so we read /proc/self/exec when we start the server
Signed-off-by: Mattias Andrée <maandree@operamail.com>
-rw-r--r-- | src/libmdsserver/util.c | 57 | ||||
-rw-r--r-- | src/libmdsserver/util.h | 24 | ||||
-rw-r--r-- | src/mds-base.c | 5 |
3 files changed, 75 insertions, 11 deletions
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 @@ -34,6 +34,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 * * @param str The client ID string @@ -73,27 +81,54 @@ 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 @@ -41,9 +41,33 @@ 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.");); |