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: { |