From 9d002bc47df6b5d5b7d548b36af94452981044b2 Mon Sep 17 00:00:00 2001 From: elad Date: Sat, 31 Dec 2005 12:33:41 +0000 Subject: [PATCH] Give systrace its own version of realpath() that does what it wants, call it intercept_realpath(). Unbreaks systrace. --- bin/systrace/intercept.c | 148 +++++++++++++++++++++++++++++++++++++-- bin/systrace/intercept.h | 4 +- 2 files changed, 147 insertions(+), 5 deletions(-) diff --git a/bin/systrace/intercept.c b/bin/systrace/intercept.c index af5e88f7b25b..347100ffc0a3 100644 --- a/bin/systrace/intercept.c +++ b/bin/systrace/intercept.c @@ -1,4 +1,4 @@ -/* $NetBSD: intercept.c,v 1.25 2005/06/27 17:11:20 elad Exp $ */ +/* $NetBSD: intercept.c,v 1.26 2005/12/31 12:33:41 elad Exp $ */ /* $OpenBSD: intercept.c,v 1.29 2002/08/28 03:30:27 itojun Exp $ */ /* * Copyright 2002 Niels Provos @@ -30,7 +30,7 @@ * THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. */ #include -__RCSID("$NetBSD: intercept.c,v 1.25 2005/06/27 17:11:20 elad Exp $"); +__RCSID("$NetBSD: intercept.c,v 1.26 2005/12/31 12:33:41 elad Exp $"); #include #include @@ -672,7 +672,7 @@ normalize_filename(int fd, pid_t pid, char *name, int userp) /* If realpath fails then the filename does not exist, * or we are supposed to not resolve the last component */ - if (realpath(cwd, rcwd) == NULL) { + if (intercept_realpath(cwd, rcwd) == NULL) { char *dir, *file; struct stat st; @@ -691,7 +691,7 @@ normalize_filename(int fd, pid_t pid, char *name, int userp) goto error; /* So, try again */ - if (realpath(dir, rcwd) == NULL) { + if (intercept_realpath(dir, rcwd) == NULL) { failed = 1; goto out; } @@ -990,3 +990,143 @@ intercept_policy_free(int policynr) { (*intercept_pfreecb)(policynr, intercept_pfreearg); } + +char * +intercept_realpath(const char *path, char *resolved) +{ + struct stat sb; + int idx = 0, n, nlnk = 0, serrno = errno; + const char *q; + char *p, wbuf[2][MAXPATHLEN]; + size_t len; + + /* + * Build real path one by one with paying an attention to ., + * .. and symbolic link. + */ + + /* + * `p' is where we'll put a new component with prepending + * a delimiter. + */ + p = resolved; + + if (*path == 0) { + *p = 0; + errno = ENOENT; + return (NULL); + } + + /* If relative path, start from current working directory. */ + if (*path != '/') { + if (getcwd(resolved, MAXPATHLEN) == NULL) { + p[0] = '.'; + p[1] = 0; + return (NULL); + } + len = strlen(resolved); + if (len > 1) + p += len; + } + +loop: + /* Skip any slash. */ + while (*path == '/') + path++; + + if (*path == 0) { + if (p == resolved) + *p++ = '/'; + *p = 0; + return (resolved); + } + + /* Find the end of this component. */ + q = path; + do + q++; + while (*q != '/' && *q != 0); + + /* Test . or .. */ + if (path[0] == '.') { + if (q - path == 1) { + path = q; + goto loop; + } + if (path[1] == '.' && q - path == 2) { + /* Trim the last component. */ + if (p != resolved) + while (*--p != '/') + ; + path = q; + goto loop; + } + } + + /* Append this component. */ + if (p - resolved + 1 + q - path + 1 > MAXPATHLEN) { + errno = ENAMETOOLONG; + if (p == resolved) + *p++ = '/'; + *p = 0; + return (NULL); + } + p[0] = '/'; + memcpy(&p[1], path, + /* LINTED We know q > path. */ + q - path); + p[1 + q - path] = 0; + + /* + * If this component is a symlink, toss it and prepend link + * target to unresolved path. + */ + if (lstat(resolved, &sb) == -1) { + /* Allow nonexistent component if this is the last one. */ + while (*q == '/') + q++; + + if (*q == 0 && errno == ENOENT) { + errno = serrno; + return (resolved); + } + + return (NULL); + } + if (S_ISLNK(sb.st_mode)) { + if (nlnk++ >= MAXSYMLINKS) { + errno = ELOOP; + return (NULL); + } + n = readlink(resolved, wbuf[idx], sizeof(wbuf[0]) - 1); + if (n < 0) + return (NULL); + if (n == 0) { + errno = ENOENT; + return (NULL); + } + + /* Append unresolved path to link target and switch to it. */ + if (n + (len = strlen(q)) + 1 > sizeof(wbuf[0])) { + errno = ENAMETOOLONG; + return (NULL); + } + memcpy(&wbuf[idx][n], q, len + 1); + path = wbuf[idx]; + idx ^= 1; + + /* If absolute symlink, start from root. */ + if (*path == '/') + p = resolved; + goto loop; + } + if (*q == '/' && !S_ISDIR(sb.st_mode)) { + errno = ENOTDIR; + return (NULL); + } + + /* Advance both resolved and unresolved path. */ + p += 1 + q - path; + path = q; + goto loop; +} diff --git a/bin/systrace/intercept.h b/bin/systrace/intercept.h index 57010e85d634..c15d66499814 100644 --- a/bin/systrace/intercept.h +++ b/bin/systrace/intercept.h @@ -1,4 +1,4 @@ -/* $NetBSD: intercept.h,v 1.17 2005/06/27 17:11:20 elad Exp $ */ +/* $NetBSD: intercept.h,v 1.18 2005/12/31 12:33:41 elad Exp $ */ /* $OpenBSD: intercept.h,v 1.11 2002/08/04 04:15:50 provos Exp $ */ /* * Copyright 2002 Niels Provos @@ -203,4 +203,6 @@ void intercept_newimage(int, pid_t, int, const char *, char *, int intercept_isvalidsystemcall(char *, char *); +char *intercept_realpath(const char *, char *); + #endif /* _INTERCEPT_H_ */