aboutsummaryrefslogtreecommitdiffstats
diff options
context:
space:
mode:
authorMattias Andrée <maandree@operamail.com>2014-08-11 11:32:20 +0200
committerMattias Andrée <maandree@operamail.com>2014-08-11 11:32:20 +0200
commit76f1830cdabb731316f7031e88ac503b30f23e62 (patch)
treebcaa39f64a8f2e4f850b8fbcf9641c95196d87ed
parentmisc (diff)
downloadmds-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.c57
-rw-r--r--src/libmdsserver/util.h24
-rw-r--r--src/mds-base.c5
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."););