summaryrefslogtreecommitdiff
path: root/sys/kern
diff options
context:
space:
mode:
Diffstat (limited to 'sys/kern')
-rw-r--r--sys/kern/kern_tame.c74
1 files changed, 72 insertions, 2 deletions
diff --git a/sys/kern/kern_tame.c b/sys/kern/kern_tame.c
index 9fc57ba1ad2..069993d25a9 100644
--- a/sys/kern/kern_tame.c
+++ b/sys/kern/kern_tame.c
@@ -1,4 +1,4 @@
-/* $OpenBSD: kern_tame.c,v 1.10 2015/07/20 18:58:53 jeremy Exp $ */
+/* $OpenBSD: kern_tame.c,v 1.11 2015/07/20 21:36:27 tedu Exp $ */
/*
* Copyright (c) 2015 Nicholas Marriott <nicm@openbsd.org>
@@ -46,6 +46,8 @@
#include <sys/syscallargs.h>
#include <sys/systm.h>
+int canonpath(const char *input, char *buf, size_t bufsize);
+
const u_int tame_syscalls[SYS_MAXSYSCALL] = {
[SYS_exit] = 0xffffffff,
@@ -273,8 +275,13 @@ tame_fail(struct proc *p, int error, int code)
* without the right flags set
*/
int
-tame_namei(struct proc *p, char *path)
+tame_namei(struct proc *p, char *origpath)
{
+ char path[PATH_MAX];
+
+ if (canonpath(origpath, path, sizeof(path)) != 0)
+ return (tame_fail(p, EPERM, TAME_RPATH));
+
/* Detect what looks like a mkstemp(3) family operation */
if ((p->p_p->ps_tame & _TM_TMPPATH) &&
(p->p_tame_syscall == SYS_open) &&
@@ -830,3 +837,66 @@ tame_dns_check(struct proc *p, in_port_t port)
return (0); /* Allow a DNS connect outbound */
return (EPERM);
}
+
+int
+canonpath(const char *input, char *buf, size_t bufsize)
+{
+ char *p, *q, *s, *end;
+
+ /* can't canon relative paths, don't bother */
+ if (input[0] != '/') {
+ if (strlcpy(buf, input, bufsize) >= bufsize)
+ return (EINVAL);
+ return (0);
+ }
+
+ /* easiest to work with strings always ending in '/' */
+ if (snprintf(buf, bufsize, "%s/", input) >= bufsize)
+ return (EINVAL);
+
+ /* after this we will only be shortening the string. */
+ p = buf;
+ q = p;
+ while (*p) {
+ if (p[0] == '/' && p[1] == '/') {
+ p += 1;
+ } else if (p[0] == '/' && p[1] == '.' &&
+ p[2] == '/') {
+ p += 2;
+ } else {
+ *q++ = *p++;
+ }
+ }
+ *q = 0;
+
+ end = buf + strlen(buf);
+ s = buf;
+ p = s;
+ while (1) {
+ /* find "/../" (where's strstr when you need it?) */
+ while (p < end) {
+ if (p[0] == '/' && strncmp(p + 1, "../", 3) == 0)
+ break;
+ p++;
+ }
+ if (p == end)
+ break;
+ if (p == s) {
+ memmove(s, p + 3, end - p - 3 + 1);
+ end -= 3;
+ } else {
+ /* s starts with '/', so we know there's one
+ * somewhere before p. */
+ q = p - 1;
+ while (*q != '/')
+ q--;
+ memmove(q, p + 3, end - p - 3 + 1);
+ end -= p + 3 - q;
+ p = q;
+ }
+ }
+ if (end > s + 1)
+ *(end - 1) = 0; /* remove trailing '/' */
+
+ return 0;
+}