| Index: src/trusted/service_runtime/nacl_syscall_common.c
|
| diff --git a/src/trusted/service_runtime/nacl_syscall_common.c b/src/trusted/service_runtime/nacl_syscall_common.c
|
| index 4a19502c52ceb198b07054532eb055a5b5a998ec..01165d81a3831824d95d3551e7c28bbd43ff82f4 100644
|
| --- a/src/trusted/service_runtime/nacl_syscall_common.c
|
| +++ b/src/trusted/service_runtime/nacl_syscall_common.c
|
| @@ -12,6 +12,7 @@
|
|
|
| #include <errno.h>
|
| #include <stdio.h>
|
| +#include <string.h>
|
|
|
| #include "native_client/src/include/build_config.h"
|
|
|
| @@ -91,6 +92,171 @@ void NaClInsecurelyBypassAllAclChecks(void) {
|
| NaClAclBypassChecks = 1;
|
| }
|
|
|
| +char *NaClRootDir = NULL;
|
| +size_t NaClRootDirLen = 0;
|
| +
|
| +#if NACL_WINDOWS
|
| +int NaClMountRootDir(char *root) {
|
| + return 0;
|
| +}
|
| +
|
| +int NaClSanitizePathLexically(char *path) {
|
| + /*
|
| + * TODO(jtolds): this could totally be made to work with Windows, but I'm
|
| + * unclear what the path separator and drive letter requirements are at this
|
| + * level.
|
| + */
|
| + return 0;
|
| +}
|
| +#elif NACL_LINUX || NACL_OSX
|
| +int NaClMountRootDir(char *root) {
|
| + NaClRootDir = strdup(root);
|
| + if (!NaClSanitizePathLexically(NaClRootDir)) {
|
| + goto fail;
|
| + }
|
| + NaClRootDirLen = strlen(NaClRootDir);
|
| + if (NaClRootDirLen <= 0) {
|
| + goto fail;
|
| + }
|
| + while (NaClRootDirLen > 0 && NaClRootDir[NaClRootDirLen-1] == '/') {
|
| + NaClRootDir[--NaClRootDirLen] = '\0';
|
| + }
|
| + return 1;
|
| +fail:
|
| + free(NaClRootDir);
|
| + NaClRootDir = NULL;
|
| + return 0;
|
| +}
|
| +
|
| +int NaClSanitizePathLexically(char *path) {
|
| + size_t last_path_sep = 0, prev_sep = 0, next_char = 0;
|
| + size_t path_len = strlen(path);
|
| +
|
| + /*
|
| + * Overall strategy:
|
| + * We're going to keep track of the last path separator we saw. Each time
|
| + * we see a new path separator, we'll mark off the last path element, then
|
| + * decide what to do with it. We'll either:
|
| + * * decide the path element was empty (a double slash) and slide everything
|
| + * back a byte.
|
| + * * decide the path element was '.' and slide everything back two bytes.
|
| + * * decide the path element is '..' and it's immediately after the root, so
|
| + * we'll slide everything back three bytes
|
| + * * decide the path element is '..' and it's not after the root, so we'll
|
| + * go find the previous path separator and slide everything back to it.
|
| + * * decide the path element is fine and continue with the new path
|
| + * separator.
|
| + *
|
| + * This strategy avoids mallocs.
|
| + */
|
| +
|
| + /*
|
| + * This algorithm assumes that there is always at least space for
|
| + * "/\0", and the path is absolute, so make sure strlen(path) >= 1 and the
|
| + * path starts with '/'.
|
| + */
|
| + if (path_len <= 0 || path[0] != '/') {
|
| + return 0;
|
| + }
|
| +
|
| + /*
|
| + * For the purposes of this algorithm, '/' and '\0' are both path separators.
|
| + * last_path_sep refers to the index of the last path separator (either one
|
| + * of the above two bytes) that we've seen so far.
|
| + *
|
| + * We'll loop as long as the last path separator isn't the null byte and do
|
| + * some cleanup after.
|
| + */
|
| + while (path[last_path_sep] != '\0') {
|
| +
|
| + /*
|
| + * Let's find the full next path element.
|
| + */
|
| + next_char = last_path_sep + 1;
|
| + while (path[next_char] != '/' && path[next_char] != '\0') {
|
| + next_char++;
|
| + }
|
| +
|
| + /* Check if the path element was empty (double-separator case) */
|
| + if (next_char == last_path_sep + 1) {
|
| + if (path[next_char] == '\0') {
|
| + /*
|
| + * Special '/\0' end case, allow a double separator when the second one
|
| + * is a null byte. This allows us to sanitize paths with or without
|
| + * trailing slashes.
|
| + */
|
| + last_path_sep = next_char;
|
| + continue;
|
| + }
|
| + /* Normal double-separator case. Just slide everything back one. */
|
| + memmove(path + last_path_sep, path + last_path_sep + 1,
|
| + (path_len + 1) - (last_path_sep + 1));
|
| + path_len -= 1;
|
| + continue;
|
| + }
|
| +
|
| + /* Check if the last path element was "." */
|
| + if (next_char == last_path_sep + 2 && path[last_path_sep + 1] == '.') {
|
| + /* Slide everything back two */
|
| + memmove(path + last_path_sep, path + last_path_sep + 2,
|
| + (path_len + 1) - (last_path_sep + 2));
|
| + path_len -= 2;
|
| + continue;
|
| + }
|
| +
|
| + /* Check if the last path element was ".." */
|
| + if (next_char == last_path_sep + 3 && path[last_path_sep + 1] == '.' &&
|
| + path[last_path_sep + 2] == '.') {
|
| + if (last_path_sep == 0) {
|
| + /* '..' after root case, just slide everything back three. */
|
| + memmove(path + last_path_sep, path + last_path_sep + 3,
|
| + (path_len + 1) - (last_path_sep + 3));
|
| + path_len -= 3;
|
| + continue;
|
| + }
|
| + /*
|
| + * Normal '..' case. Go find the previous path separator and then slide
|
| + * everything back to it.
|
| + */
|
| + prev_sep = last_path_sep - 1;
|
| + while (path[prev_sep] != '/') {
|
| + prev_sep--;
|
| + }
|
| + memmove(path + prev_sep, path + last_path_sep + 3,
|
| + (path_len + 1) - (last_path_sep + 3));
|
| + path_len -= (last_path_sep + 3 - prev_sep);
|
| + last_path_sep = prev_sep;
|
| + continue;
|
| + }
|
| +
|
| + /* Default path element case. */
|
| + last_path_sep = next_char;
|
| + }
|
| +
|
| + /*
|
| + * Cleanup scenario: whenever we slide something back, we overwrite the
|
| + * last path separator. For example, in the "/a/.\0" case, we slide
|
| + * everything starting at byte 4 back over byte 3, so the result is "/a\0".
|
| + * In the case of a full path "/.\0", the end result ends up being "\0", so
|
| + * we need to fix up root paths like this specially.
|
| + * N.B.: we know we have enough space in the buffer for this.
|
| + */
|
| + if (last_path_sep == 0) {
|
| + path[0] = '/';
|
| + path[1] = '\0';
|
| + }
|
| +
|
| + return 1;
|
| +}
|
| +
|
| +#else
|
| +#error Unsupported platform
|
| +#endif
|
| +
|
| +int NaClFileAccessEnabled(void) {
|
| + return NaClAclBypassChecks || (NaClRootDir != NULL);
|
| +}
|
| +
|
| int NaClHighResolutionTimerEnabled(void) {
|
| return NaClAclBypassChecks;
|
| }
|
| @@ -705,7 +871,7 @@ int32_t NaClSysSysconf(struct NaClAppThread *natp,
|
| break;
|
| }
|
| case NACL_ABI__SC_NACL_FILE_ACCESS_ENABLED: {
|
| - result_value = NaClAclBypassChecks;
|
| + result_value = NaClFileAccessEnabled();
|
| break;
|
| }
|
| case NACL_ABI__SC_NACL_LIST_MAPPINGS_ENABLED: {
|
|
|