Skip to content

Commit f521dac

Browse files
authored
allow symlinks
Signed-off-by: Hasan ÇALIŞIR <hasan.calisir@psauxit.com>
1 parent ef8f6c6 commit f521dac

File tree

1 file changed

+58
-3
lines changed

1 file changed

+58
-3
lines changed

safexec/safexec.c

Lines changed: 58 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -206,15 +206,70 @@ static void s_perror(const char *s) {
206206
if (!QUIET) perror(s);
207207
}
208208

209-
// Safe .so (must be root:root, regular file, and !group/other-writable)
209+
/* Allowed roots */
210+
static const char *const TRUSTED_LIB_ROOTS[] = {
211+
"/usr/lib",
212+
"/lib",
213+
"/usr/lib64",
214+
"/lib64",
215+
NULL
216+
};
217+
218+
static int has_trusted_root(const char *real) {
219+
for (const char *const *p = TRUSTED_LIB_ROOTS; *p; ++p) {
220+
size_t n = strlen(*p);
221+
/* must be prefix and either exactly equal or followed by '/' */
222+
if (strncmp(real, *p, n) == 0 && (real[n] == '\0' || real[n] == '/'))
223+
return 1;
224+
}
225+
return 0;
226+
}
227+
228+
/* Check that the path (already absolute) contains a component exactly "npp" */
229+
static int path_has_component_npp(const char *abs) {
230+
/* Walk components between '/' ... '/' boundaries */
231+
const char *p = abs;
232+
while (*p) {
233+
/* skip repeated '/' */
234+
while (*p == '/') p++;
235+
if (!*p) break;
236+
const char *start = p;
237+
while (*p && *p != '/') p++;
238+
size_t len = (size_t)(p - start);
239+
if (len == 3 && start[0] == 'n' && start[1] == 'p' && start[2] == 'p')
240+
return 1;
241+
}
242+
return 0;
243+
}
244+
210245
static int is_secure_so(const char *path) {
211-
int fd = open(path, O_RDONLY|O_CLOEXEC|O_NOFOLLOW);
212-
if (fd < 0) return 0;
246+
if (!path || !*path) return 0;
247+
248+
/* Resolve to a canonical absolute path (resolves all symlinks) */
249+
char real[PATH_MAX];
250+
if (!realpath(path, real)) return 0;
251+
252+
/* 1) Must be under one of the trusted roots */
253+
if (!has_trusted_root(real)) return 0;
254+
255+
/* 2) Must include a path component exactly named "npp" somewhere below the root */
256+
if (!path_has_component_npp(real)) return 0;
257+
258+
/* 3) Reject final symlink and TOCTOU: open final target with O_NOFOLLOW */
259+
int fd = open(real, O_RDONLY | O_CLOEXEC | O_NOFOLLOW);
260+
if (fd < 0) {
261+
/* If ELOOP, final component is a symlink -> reject */
262+
return 0;
263+
}
264+
265+
/* 4) Ownership/permissions: root:root, regular file, not group/other writable */
213266
struct stat st;
214267
int ok = (fstat(fd, &st) == 0) &&
215268
S_ISREG(st.st_mode) &&
216269
st.st_uid == 0 &&
270+
st.st_gid == 0 &&
217271
((st.st_mode & 022) == 0);
272+
218273
close(fd);
219274
return ok;
220275
}

0 commit comments

Comments
 (0)