├── Makefile
├── README.md
├── libunionpreload.c
└── test-libunionpreload.sh
/Makefile:
--------------------------------------------------------------------------------
1 | a:
2 | gcc -shared -fPIC libunionpreload.c -o libunionpreload.so -ldl -DUNION_LIBNAME=\"libunionpreload.so\"
3 | strip libunionpreload.so
--------------------------------------------------------------------------------
/README.md:
--------------------------------------------------------------------------------
1 | # libunionpreload | [Download amd64](https://github.com/project-portable/libunionpreload/releases/download/amd64/libunionpreload.so)
2 | This lib tries to create the impression of a union filesystem, effectively creating the impression to the payload application that the specified directory are overlayed over /. This works as long as the payload application is a dynamically linked binary. It also intercepts "execve" calls to ensure that the sub processes also believe that the current directory is overlayed.
3 |
4 | # How it works in praticle:
5 |
6 | ```bash
7 | export CURRENT_DIRECTORY="$(dirname "$(readlink -f "${0}")")"
8 | export UNION_PRELOAD="/example"
9 | export LD_PRELOAD="${CURRENT_DIRECTORY}/libunionpreload.so"
10 |
11 | # If The application try access /usr/lib/bar.so, libunionpreload.so will "replace"
12 | # the / directory with /example, so, application now is looking for /example/usr/lib/bar.so,
13 | # if the file exists it is will be used even if the original looking file (/usr/lib/bar.so) exist.
14 | # If /example/usr/lib/bar.so doesn't exists, the /usr/lib/bar.so is used
15 | ```
16 |
17 | # How to use
18 | Since it works by reading an environment variable and needs to be preloaded, you will need a `bash script` with the following lines before running your application:
19 |
20 | ```bash
21 | #!/usr/bin/env sh
22 |
23 | export CURRENT_DIRECTORY="$(dirname "$(readlink -f "${0}")")"
24 | export UNION_PRELOAD="${CURRENT_DIRECTORY}"
25 | export LD_PRELOAD="${CURRENT_DIRECTORY}/libunionpreload.so"
26 |
27 | # call your application here
28 | ```
29 |
30 | # How to build
31 | 1. Clone repository:
32 |
33 | ```
34 | git clone https://github.com/project-portable/libunionpreload.git
35 | ```
36 | 2. Enter repository
37 | ```
38 | cd libunionpreload
39 | ```
40 | 3. Run `make`
41 | ```
42 | make
43 | ```
44 | # See in action
45 |
46 | Run test script:
47 |
48 | ```
49 | sh ./test-libunionpreload.sh
50 | ```
51 | # Credits
52 |
53 | * [Michael Terry](https://github.com/mikix/deb2snap/blob/master/src/preload.c)
54 |
--------------------------------------------------------------------------------
/libunionpreload.c:
--------------------------------------------------------------------------------
1 | /* -*- Mode: C; indent-tabs-mode: nil; tab-width: 4 -*-
2 | *
3 | * Copyright (C) 2015 Canonical, Ltd.
4 | *
5 | * This program is free software; you can redistribute it and/or modify
6 | * it under the terms of the GNU General Public License as published by
7 | * the Free Software Foundation; version 3.
8 | *
9 | * This program is distributed in the hope that it will be useful,
10 | * but WITHOUT ANY WARRANTY; without even the implied warranty of
11 | * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
12 | * GNU General Public License for more details.
13 | *
14 | * You should have received a copy of the GNU General Public License
15 | * along with this program. If not, see .
16 | */
17 |
18 | #define _GNU_SOURCE
19 | #define __USE_GNU
20 |
21 | #include
22 | #include
23 | #include
24 | #include
25 | #include
26 | #include
27 | #include
28 | #include
29 | #include
30 | #include
31 | #include
32 | #include
33 | #include
34 | #include
35 |
36 | #define LD_PRELOAD "LD_PRELOAD"
37 | #define UNION_PRELOAD "UNION_PRELOAD"
38 | static char **saved_ld_preloads = NULL;
39 | static size_t num_saved_ld_preloads = 0;
40 | static char *saved_union_preload = NULL;
41 | static char *saved_tmpdir = NULL;
42 | static char *saved_varlib = NULL;
43 |
44 | static void constructor() __attribute__((constructor));
45 |
46 | static char *
47 | getenvdup (const char *varname)
48 | {
49 | char *envvar = secure_getenv (varname);
50 | if (envvar == NULL || envvar[0] == 0) // identical for our purposes
51 | return NULL;
52 | else
53 | return strdup (envvar);
54 | }
55 |
56 | void constructor()
57 | {
58 | char *ld_preload_copy, *p, *savedptr = NULL;
59 | size_t libnamelen;
60 |
61 | // We need to save LD_PRELOAD and UNION_PRELOAD in case we need to
62 | // propagate the values to an exec'd program.
63 | ld_preload_copy = getenvdup (LD_PRELOAD);
64 | if (ld_preload_copy == NULL) {
65 | return;
66 | }
67 |
68 | saved_union_preload = getenvdup (UNION_PRELOAD);
69 | if (saved_union_preload == NULL) {
70 | free (ld_preload_copy);
71 | return;
72 | }
73 |
74 | saved_tmpdir = getenvdup ("UNION_APP_TMPDIR");
75 | if (!saved_tmpdir) {
76 | saved_tmpdir = getenvdup ("TMPDIR");
77 | }
78 |
79 | saved_varlib = getenvdup ("UNION_APP_DATA_PATH");
80 | if (!saved_varlib) {
81 | saved_varlib = getenvdup ("UNION_APP_DATA_PATH");
82 | }
83 |
84 | // Pull out each absolute-pathed libunionpreload.so we find. Better to
85 | // accidentally include some other libunionpreload than not propagate
86 | // ourselves.
87 | libnamelen = strlen (UNION_LIBNAME);
88 | for (p = strtok_r (ld_preload_copy, " :", &savedptr);
89 | p;
90 | p = strtok_r (NULL, " :", &savedptr)) {
91 | size_t plen = strlen (p);
92 | if (plen > libnamelen && p[0] == '/' && strcmp (p + strlen (p) - strlen (UNION_LIBNAME) - 1, "/" UNION_LIBNAME) == 0) {
93 | num_saved_ld_preloads++;
94 | saved_ld_preloads = realloc (saved_ld_preloads, (num_saved_ld_preloads + 1) * sizeof (char *));
95 | saved_ld_preloads[num_saved_ld_preloads - 1] = strdup (p);
96 | saved_ld_preloads[num_saved_ld_preloads] = NULL;
97 | }
98 | }
99 | free (ld_preload_copy);
100 | }
101 |
102 | static char *
103 | redirect_writable_path (const char *pathname, const char *basepath)
104 | {
105 | char *redirected_pathname;
106 | int chop = 0;
107 |
108 | if (pathname[0] == 0) {
109 | return strdup (basepath);
110 | }
111 |
112 | redirected_pathname = malloc (PATH_MAX);
113 |
114 | if (basepath[strlen (basepath) - 1] == '/') {
115 | chop = 1;
116 | }
117 | strncpy (redirected_pathname, basepath, PATH_MAX - 1 - chop);
118 |
119 | strncat (redirected_pathname, pathname, PATH_MAX - 1 - strlen (redirected_pathname));
120 |
121 | // No need to see if it already exists -- app can only be in TMPDIR, not /tmp
122 | return redirected_pathname;
123 | }
124 |
125 | static char *
126 | redirect_path_full (const char *pathname, int check_parent, int only_if_absolute)
127 | {
128 | int (*_access) (const char *pathname, int mode);
129 | char *redirected_pathname;
130 | char *preload_dir;
131 | int ret;
132 | int chop = 0;
133 | char *slash = 0;
134 |
135 | if (pathname == NULL) {
136 | return NULL;
137 | }
138 |
139 | preload_dir = saved_union_preload;
140 | if (preload_dir == NULL) {
141 | return strdup (pathname);
142 | }
143 |
144 | // Do not redirect when accessing /dev
145 | if (strcmp (pathname, "/dev") == 0 || strncmp (pathname, "/dev/", 5) == 0) {
146 | return strdup (pathname);
147 | }
148 |
149 | if (only_if_absolute && pathname[0] != '/') {
150 | return strdup (pathname);
151 | }
152 |
153 | // Sometimes programs will hardcode /tmp (like Xorg does for its lock file).
154 | // In that case, let's redirect to TMPDIR.
155 | if (strcmp (pathname, "/tmp") == 0 || strncmp (pathname, "/tmp/", 5) == 0) {
156 | if (saved_tmpdir && strncmp (pathname, saved_tmpdir, strlen (saved_tmpdir)) != 0) {
157 | return redirect_writable_path (pathname + 4, saved_tmpdir);
158 | } else {
159 | return strdup (pathname);
160 | }
161 | }
162 |
163 | _access = (int (*)(const char *pathname, int mode)) dlsym (RTLD_NEXT, "access");
164 |
165 | // And each app should have its own /var/lib writable tree. Here, we want
166 | // to support reading the base system's files if they exist, else let the app
167 | // play in /var/lib themselves. So we reverse the normal check: first see if
168 | // it exists in root, else do our redirection.
169 | if (strcmp (pathname, "/var/lib") == 0 || strncmp (pathname, "/var/lib/", 9) == 0) {
170 | if (saved_varlib && strncmp (pathname, saved_varlib, strlen (saved_varlib)) != 0 && _access (pathname, F_OK) != 0) {
171 | return redirect_writable_path (pathname + 8, saved_varlib);
172 | } else {
173 | return strdup (pathname);
174 | }
175 | }
176 |
177 | redirected_pathname = malloc (PATH_MAX);
178 |
179 | if (preload_dir[strlen (preload_dir) - 1] == '/') {
180 | chop = 1;
181 | }
182 | strncpy (redirected_pathname, preload_dir, PATH_MAX - 1 - chop);
183 |
184 | if (pathname[0] != '/') {
185 | size_t cursize = strlen (redirected_pathname);
186 | if (getcwd (redirected_pathname + cursize, PATH_MAX - cursize) == NULL) {
187 | free (redirected_pathname);
188 | return strdup (pathname);
189 | }
190 | strncat (redirected_pathname, "/", PATH_MAX - 1 - strlen (redirected_pathname));
191 | }
192 |
193 | strncat (redirected_pathname, pathname, PATH_MAX - 1 - strlen (redirected_pathname));
194 |
195 | if (check_parent) {
196 | slash = strrchr (redirected_pathname, '/');
197 | if (slash) { // should always be true
198 | *slash = 0;
199 | }
200 | }
201 |
202 | ret = _access (redirected_pathname, F_OK);
203 |
204 | if (check_parent && slash) {
205 | *slash = '/';
206 | }
207 |
208 | if (ret == 0 || errno == ENOTDIR) { // ENOTDIR is OK because it exists at least
209 | return redirected_pathname;
210 | } else {
211 | free (redirected_pathname);
212 | return strdup (pathname);
213 | }
214 | }
215 |
216 | static char *
217 | redirect_path (const char *pathname)
218 | {
219 | return redirect_path_full (pathname, 0, 0);
220 | }
221 |
222 | static char *
223 | redirect_path_target (const char *pathname)
224 | {
225 | return redirect_path_full (pathname, 1, 0);
226 | }
227 |
228 | static char *
229 | redirect_path_if_absolute (const char *pathname)
230 | {
231 | return redirect_path_full (pathname, 0, 1);
232 | }
233 |
234 | #define REDIRECT_1_1(RET, NAME) \
235 | RET \
236 | NAME (const char *path) \
237 | { \
238 | RET (*_NAME) (const char *path); \
239 | char *new_path = NULL; \
240 | RET result; \
241 | _NAME = (RET (*)(const char *path)) dlsym (RTLD_NEXT, #NAME); \
242 | new_path = redirect_path (path); \
243 | result = _NAME (new_path); \
244 | free (new_path); \
245 | return result; \
246 | }
247 |
248 | #define REDIRECT_1_2(RET, NAME, T2) \
249 | RET \
250 | NAME (const char *path, T2 A2) \
251 | { \
252 | RET (*_NAME) (const char *path, T2 A2); \
253 | char *new_path = NULL; \
254 | RET result; \
255 | _NAME = (RET (*)(const char *path, T2 A2)) dlsym (RTLD_NEXT, #NAME); \
256 | new_path = redirect_path (path); \
257 | result = _NAME (new_path, A2); \
258 | free (new_path); \
259 | return result; \
260 | }
261 |
262 | #define REDIRECT_1_3(RET, NAME, T2, T3) \
263 | RET \
264 | NAME (const char *path, T2 A2, T3 A3) \
265 | { \
266 | RET (*_NAME) (const char *path, T2 A2, T3 A3); \
267 | char *new_path = NULL; \
268 | RET result; \
269 | _NAME = (RET (*)(const char *path, T2 A2, T3 A3)) dlsym (RTLD_NEXT, #NAME); \
270 | new_path = redirect_path (path); \
271 | result = _NAME (new_path, A2, A3); \
272 | free (new_path); \
273 | return result; \
274 | }
275 |
276 | #define REDIRECT_2_2(RET, NAME, T1) \
277 | RET \
278 | NAME (T1 A1, const char *path) \
279 | { \
280 | RET (*_NAME) (T1 A1, const char *path); \
281 | char *new_path = NULL; \
282 | RET result; \
283 | _NAME = (RET (*)(T1 A1, const char *path)) dlsym (RTLD_NEXT, #NAME); \
284 | new_path = redirect_path (path); \
285 | result = _NAME (A1, new_path); \
286 | free (new_path); \
287 | return result; \
288 | }
289 |
290 | #define REDIRECT_2_3(RET, NAME, T1, T3) \
291 | RET \
292 | NAME (T1 A1, const char *path, T3 A3) \
293 | { \
294 | RET (*_NAME) (T1 A1, const char *path, T3 A3); \
295 | char *new_path = NULL; \
296 | RET result; \
297 | _NAME = (RET (*)(T1 A1, const char *path, T3 A3)) dlsym (RTLD_NEXT, #NAME); \
298 | new_path = redirect_path (path); \
299 | result = _NAME (A1, new_path, A3); \
300 | free (new_path); \
301 | return result; \
302 | }
303 |
304 | #define REDIRECT_2_3_AT(RET, NAME, T1, T3) \
305 | RET \
306 | NAME (T1 A1, const char *path, T3 A3) \
307 | { \
308 | RET (*_NAME) (T1 A1, const char *path, T3 A3); \
309 | char *new_path = NULL; \
310 | RET result; \
311 | _NAME = (RET (*)(T1 A1, const char *path, T3 A3)) dlsym (RTLD_NEXT, #NAME); \
312 | new_path = redirect_path_if_absolute (path); \
313 | result = _NAME (A1, new_path, A3); \
314 | free (new_path); \
315 | return result; \
316 | }
317 |
318 | #define REDIRECT_2_4_AT(RET, NAME, T1, T3, T4) \
319 | RET \
320 | NAME (T1 A1, const char *path, T3 A3, T4 A4) \
321 | { \
322 | RET (*_NAME) (T1 A1, const char *path, T3 A3, T4 A4); \
323 | char *new_path = NULL; \
324 | RET result; \
325 | _NAME = (RET (*)(T1 A1, const char *path, T3 A3, T4 A4)) dlsym (RTLD_NEXT, #NAME); \
326 | new_path = redirect_path_if_absolute (path); \
327 | result = _NAME (A1, new_path, A3, A4); \
328 | free (new_path); \
329 | return result; \
330 | }
331 |
332 | #define REDIRECT_3_5(RET, NAME, T1, T2, T4, T5) \
333 | RET \
334 | NAME (T1 A1, T2 A2, const char *path, T4 A4, T5 A5) \
335 | { \
336 | RET (*_NAME) (T1 A1, T2 A2, const char *path, T4 A4, T5 A5); \
337 | char *new_path = NULL; \
338 | RET result; \
339 | _NAME = (RET (*)(T1 A1, T2 A2, const char *path, T4 A4, T5 A5)) dlsym (RTLD_NEXT, #NAME); \
340 | new_path = redirect_path (path); \
341 | result = _NAME (A1, A2, new_path, A4, A5); \
342 | free (new_path); \
343 | return result; \
344 | }
345 |
346 | #define REDIRECT_TARGET(RET, NAME) \
347 | RET \
348 | NAME (const char *path, const char *target) \
349 | { \
350 | RET (*_NAME) (const char *path, const char *target); \
351 | char *new_path = NULL; \
352 | char *new_target = NULL; \
353 | RET result; \
354 | _NAME = (RET (*)(const char *path, const char *target)) dlsym (RTLD_NEXT, #NAME); \
355 | new_path = redirect_path (path); \
356 | new_target = redirect_path_target (target); \
357 | result = _NAME (new_path, new_target); \
358 | free (new_path); \
359 | free (new_target); \
360 | return result; \
361 | }
362 |
363 | #define REDIRECT_OPEN(NAME) \
364 | int \
365 | NAME (const char *path, int flags, ...) \
366 | { \
367 | int mode = 0; \
368 | int (*_NAME) (const char *path, int flags, mode_t mode); \
369 | char *new_path = NULL; \
370 | int result; \
371 | if (flags & (O_CREAT|O_TMPFILE)) \
372 | { \
373 | va_list ap; \
374 | va_start (ap, flags); \
375 | mode = va_arg (ap, mode_t); \
376 | va_end (ap); \
377 | } \
378 | _NAME = (int (*)(const char *path, int flags, mode_t mode)) dlsym (RTLD_NEXT, #NAME); \
379 | new_path = redirect_path (path); \
380 | result = _NAME (new_path, flags, mode); \
381 | free (new_path); \
382 | return result; \
383 | }
384 |
385 | #define REDIRECT_OPEN_AT(NAME) \
386 | int \
387 | NAME (int dirfp, const char *path, int flags, ...) \
388 | { \
389 | int mode = 0; \
390 | int (*_NAME) (int dirfp, const char *path, int flags, mode_t mode); \
391 | char *new_path = NULL; \
392 | int result; \
393 | if (flags & (O_CREAT|O_TMPFILE)) \
394 | { \
395 | va_list ap; \
396 | va_start (ap, flags); \
397 | mode = va_arg (ap, mode_t); \
398 | va_end (ap); \
399 | } \
400 | _NAME = (int (*)(int dirfp, const char *path, int flags, mode_t mode)) dlsym (RTLD_NEXT, #NAME); \
401 | new_path = redirect_path_if_absolute (path); \
402 | result = _NAME (dirfp, new_path, flags, mode); \
403 | free (new_path); \
404 | return result; \
405 | }
406 |
407 | REDIRECT_1_2(FILE *, fopen, const char *)
408 | REDIRECT_1_2(FILE *, fopen64, const char *)
409 | REDIRECT_1_1(int, unlink)
410 | REDIRECT_2_3_AT(int, unlinkat, int, int)
411 | REDIRECT_1_2(int, access, int)
412 | REDIRECT_1_2(int, eaccess, int)
413 | REDIRECT_1_2(int, euidaccess, int)
414 | REDIRECT_2_4_AT(int, faccessat, int, int, int)
415 | REDIRECT_1_2(int, stat, struct stat *)
416 | REDIRECT_1_2(int, stat64, struct stat64 *)
417 | REDIRECT_1_2(int, lstat, struct stat *)
418 | REDIRECT_1_2(int, lstat64, struct stat64 *)
419 | REDIRECT_1_2(int, creat, mode_t)
420 | REDIRECT_1_2(int, creat64, mode_t)
421 | REDIRECT_1_2(int, truncate, off_t)
422 | REDIRECT_2_2(char *, bindtextdomain, const char *)
423 | REDIRECT_2_3(int, __xstat, int, struct stat *)
424 | REDIRECT_2_3(int, __xstat64, int, struct stat64 *)
425 | REDIRECT_2_3(int, __lxstat, int, struct stat *)
426 | REDIRECT_2_3(int, __lxstat64, int, struct stat64 *)
427 | REDIRECT_3_5(int, __fxstatat, int, int, struct stat *, int)
428 | REDIRECT_3_5(int, __fxstatat64, int, int, struct stat64 *, int)
429 | REDIRECT_1_2(int, statfs, struct statfs *)
430 | REDIRECT_1_2(int, statfs64, struct statfs64 *)
431 | REDIRECT_1_2(int, statvfs, struct statvfs *)
432 | REDIRECT_1_2(int, statvfs64, struct statvfs64 *)
433 | REDIRECT_1_2(long, pathconf, int)
434 | REDIRECT_1_1(DIR *, opendir)
435 | REDIRECT_1_2(int, mkdir, mode_t)
436 | REDIRECT_1_1(int, rmdir)
437 | REDIRECT_1_3(int, chown, uid_t, gid_t)
438 | REDIRECT_1_3(int, lchown, uid_t, gid_t)
439 | REDIRECT_1_2(int, chmod, mode_t)
440 | REDIRECT_1_2(int, lchmod, mode_t)
441 | REDIRECT_1_1(int, chdir)
442 | REDIRECT_1_3(ssize_t, readlink, char *, size_t)
443 | REDIRECT_1_2(char *, realpath, char *)
444 | REDIRECT_TARGET(int, link)
445 | REDIRECT_TARGET(int, rename)
446 | REDIRECT_OPEN(open)
447 | REDIRECT_OPEN(open64)
448 | REDIRECT_OPEN_AT(openat)
449 | REDIRECT_OPEN_AT(openat64)
450 | REDIRECT_2_3(int, inotify_add_watch, int, uint32_t)
451 |
452 | int
453 | scandir (const char *dirp, struct dirent ***namelist, int (*filter)(const struct dirent *), int (*compar)(const struct dirent **, const struct dirent **))
454 | {
455 | int (*_scandir) (const char *dirp, struct dirent ***namelist, int (*filter)(const struct dirent *), int (*compar)(const struct dirent **, const struct dirent **));
456 | char *new_path = NULL;
457 | int ret;
458 |
459 | _scandir = (int (*)(const char *dirp, struct dirent ***namelist, int (*filter)(const struct dirent *), int (*compar)(const struct dirent **, const struct dirent **))) dlsym (RTLD_NEXT, "scandir");
460 |
461 | new_path = redirect_path (dirp);
462 | ret = _scandir (new_path, namelist, filter, compar);
463 | free (new_path);
464 |
465 | return ret;
466 | }
467 |
468 | int
469 | scandir64 (const char *dirp, struct dirent64 ***namelist,
470 | int (*filter)(const struct dirent64 *),
471 | int (*compar)(const struct dirent64 **, const struct dirent64 **))
472 | {
473 | int (*_scandir64) (const char *dirp, struct dirent64 ***namelist,
474 | int (*filter)(const struct dirent64 *),
475 | int (*compar)(const struct dirent64 **, const struct dirent64 **));
476 | char *new_path = NULL;
477 | int ret;
478 |
479 | _scandir64 = (int (*)(const char *dirp, struct dirent64 ***namelist,
480 | int (*filter)(const struct dirent64 *),
481 | int (*compar)(const struct dirent64 **, const struct dirent64 **)))
482 | dlsym (RTLD_NEXT, "scandir64");
483 |
484 | new_path = redirect_path (dirp);
485 | ret = _scandir64 (new_path, namelist, filter, compar);
486 | free (new_path);
487 |
488 | return ret;
489 | }
490 |
491 |
492 | int
493 | scandirat (int dirfd, const char *dirp, struct dirent ***namelist, int (*filter)(const struct dirent *), int (*compar)(const struct dirent **, const struct dirent **))
494 | {
495 | int (*_scandirat) (int dirfd, const char *dirp, struct dirent ***namelist, int (*filter)(const struct dirent *), int (*compar)(const struct dirent **, const struct dirent **));
496 | char *new_path = NULL;
497 | int ret;
498 |
499 | _scandirat = (int (*)(int dirfd, const char *dirp, struct dirent ***namelist, int (*filter)(const struct dirent *), int (*compar)(const struct dirent **, const struct dirent **))) dlsym (RTLD_NEXT, "scandirat");
500 |
501 | new_path = redirect_path_if_absolute (dirp);
502 | ret = _scandirat (dirfd, new_path, namelist, filter, compar);
503 | free (new_path);
504 |
505 | return ret;
506 | }
507 |
508 | int
509 | scandirat64 (int dirfd, const char *dirp, struct dirent64 ***namelist,
510 | int (*filter)(const struct dirent64 *),
511 | int (*compar)(const struct dirent64 **, const struct dirent64 **))
512 | {
513 | int (*_scandirat64) (int dirfd, const char *dirp, struct dirent64 ***namelist,
514 | int (*filter)(const struct dirent64 *),
515 | int (*compar)(const struct dirent64 **, const struct dirent64 **));
516 | char *new_path = NULL;
517 | int ret;
518 |
519 | _scandirat64 = (int (*)(int dirfd, const char *dirp, struct dirent64 ***namelist,
520 | int (*filter)(const struct dirent64 *),
521 | int (*compar)(const struct dirent64 **, const struct dirent64 **)))
522 | dlsym (RTLD_NEXT, "scandirat64");
523 |
524 | new_path = redirect_path_if_absolute (dirp);
525 | ret = _scandirat64 (dirfd, new_path, namelist, filter, compar);
526 | free (new_path);
527 |
528 | return ret;
529 | }
530 |
531 | int
532 | bind (int sockfd, const struct sockaddr *addr, socklen_t addrlen)
533 | {
534 | int (*_bind) (int sockfd, const struct sockaddr *addr, socklen_t addrlen);
535 | int result;
536 |
537 | _bind = (int (*)(int sockfd, const struct sockaddr *addr, socklen_t addrlen)) dlsym (RTLD_NEXT, "bind");
538 |
539 | if (addr->sa_family == AF_UNIX && ((const struct sockaddr_un *)addr)->sun_path[0] != 0) { // could be abstract socket
540 | char *new_path = NULL;
541 | struct sockaddr_un new_addr;
542 |
543 | new_path = redirect_path (((const struct sockaddr_un *)addr)->sun_path);
544 |
545 | new_addr.sun_family = AF_UNIX;
546 | strcpy (new_addr.sun_path, new_path);
547 | free (new_path);
548 |
549 | result = _bind (sockfd, (const struct sockaddr *)&new_addr, sizeof(new_addr));
550 | } else {
551 | result = _bind (sockfd, addr, addrlen);
552 | }
553 |
554 | return result;
555 | }
556 |
557 | int
558 | connect (int sockfd, const struct sockaddr *addr, socklen_t addrlen)
559 | {
560 | int (*_connect) (int sockfd, const struct sockaddr *addr, socklen_t addrlen);
561 |
562 | _connect = (int (*)(int sockfd, const struct sockaddr *addr, socklen_t addrlen)) dlsym (RTLD_NEXT, "connect");
563 |
564 | /* addrlen == sizeof(sa_family_t) is the case of unnamed sockets,
565 | * and first byte of sun_path is 0 for abstract sockets.
566 | */
567 | if (addr->sa_family == AF_UNIX
568 | && addrlen > sizeof(sa_family_t)
569 | && ((const struct sockaddr_un *) addr)->sun_path[0] != '\0') {
570 |
571 | const struct sockaddr_un *un_addr = (const struct sockaddr_un *) addr;
572 | char *new_path = NULL;
573 | struct sockaddr_un new_addr;
574 |
575 | new_path = redirect_path (un_addr->sun_path);
576 |
577 | new_addr.sun_family = AF_UNIX;
578 | strcpy (new_addr.sun_path, new_path);
579 | free (new_path);
580 |
581 | return _connect (sockfd, (const struct sockaddr *)&new_addr, sizeof(new_addr));
582 | }
583 |
584 | return _connect (sockfd, addr, addrlen);
585 | }
586 |
587 | void *
588 | dlopen (const char *path, int mode)
589 | {
590 | void *(*_dlopen) (const char *path, int mode);
591 | char *new_path = NULL;
592 | void *result;
593 |
594 | _dlopen = (void *(*)(const char *path, int mode)) dlsym (RTLD_NEXT, "dlopen");
595 |
596 | if (path && path[0] == '/') {
597 | new_path = redirect_path (path);
598 | result = _dlopen (new_path, mode);
599 | free (new_path);
600 | } else {
601 | // non-absolute library paths aren't simply relative paths, they need
602 | // a whole lookup algorithm
603 | result = _dlopen (path, mode);
604 | }
605 |
606 | return result;
607 | }
608 |
609 | static char *
610 | ensure_in_ld_preload (char *ld_preload, const char *to_be_added)
611 | {
612 | if (ld_preload && ld_preload[0] != 0) {
613 | char *ld_preload_copy;
614 | char *p, *savedptr = NULL;
615 | int found = 0;
616 |
617 | // Check if we are already in LD_PRELOAD and thus can bail
618 | ld_preload_copy = strdup (ld_preload);
619 | for (p = strtok_r (ld_preload_copy + strlen (LD_PRELOAD) + 1, " :", &savedptr);
620 | p;
621 | p = strtok_r (NULL, " :", &savedptr)) {
622 | if (strcmp (p, to_be_added) == 0) {
623 | found = 1;
624 | break;
625 | }
626 | }
627 | free (ld_preload_copy);
628 |
629 | if (!found) {
630 | ld_preload = realloc (ld_preload, strlen (to_be_added) + strlen (ld_preload) + 2);
631 | strcat (ld_preload, ":");
632 | strcat (ld_preload, to_be_added);
633 | }
634 | } else {
635 | ld_preload = realloc (ld_preload, strlen (to_be_added) + strlen (LD_PRELOAD) + 2);
636 | strcpy (ld_preload, LD_PRELOAD "=");
637 | strcat (ld_preload, to_be_added);
638 | }
639 |
640 | return ld_preload;
641 | }
642 |
643 | static char **
644 | execve_copy_envp (char *const envp[])
645 | {
646 | int i, num_elements;
647 | char **new_envp = NULL;
648 | char *ld_preload = NULL;
649 | char *union_preload = NULL;
650 |
651 | for (num_elements = 0; envp && envp[num_elements]; num_elements++) {
652 | // this space intentionally left blank
653 | }
654 |
655 | new_envp = malloc (sizeof (char *) * (num_elements + 3));
656 |
657 | for (i = 0; i < num_elements; i++) {
658 | new_envp[i] = strdup (envp[i]);
659 | if (strncmp (envp[i], LD_PRELOAD "=", strlen (LD_PRELOAD) + 1) == 0) {
660 | ld_preload = new_envp[i]; // point at last defined LD_PRELOAD
661 | }
662 | }
663 |
664 | if (saved_ld_preloads) {
665 | size_t j;
666 | char *ld_preload_copy;
667 | ld_preload_copy = ld_preload ? strdup (ld_preload) : NULL;
668 | for (j = 0; j < num_saved_ld_preloads; j++) {
669 | ld_preload_copy = ensure_in_ld_preload(ld_preload_copy, saved_ld_preloads[j]);
670 | }
671 | new_envp[i++] = ld_preload_copy;
672 | }
673 |
674 | if (saved_union_preload) {
675 | union_preload = malloc (strlen (saved_union_preload) + strlen (UNION_PRELOAD) + 2);
676 | strcpy (union_preload, UNION_PRELOAD "=");
677 | strcat (union_preload, saved_union_preload);
678 | new_envp[i++] = union_preload;
679 | }
680 |
681 | new_envp[i++] = NULL;
682 | return new_envp;
683 | }
684 |
685 | static int
686 | execve32_wrapper (int (*_execve) (const char *path, char *const argv[], char *const envp[]), char *path, char *const argv[], char *const envp[])
687 | {
688 | char *custom_loader = NULL;
689 | char **new_argv;
690 | int i, num_elements, result;
691 |
692 | custom_loader = redirect_path ("/lib/ld-linux.so.2");
693 | if (strcmp (custom_loader, "/lib/ld-linux.so.2") == 0) {
694 | free (custom_loader);
695 | return 0;
696 | }
697 |
698 | // envp is already adjusted for our needs. But we need to shift argv
699 | for (num_elements = 0; argv && argv[num_elements]; num_elements++) {
700 | // this space intentionally left blank
701 | }
702 | new_argv = malloc (sizeof (char *) * (num_elements + 2));
703 | new_argv[0] = path;
704 | for (i = 0; i < num_elements; i++) {
705 | new_argv[i + 1] = argv[i];
706 | }
707 | new_argv[num_elements + 1] = 0;
708 |
709 | // Now actually run execve with our loader and adjusted argv
710 | result = _execve (custom_loader, new_argv, envp);
711 |
712 | // Cleanup on error
713 | free (new_argv);
714 | free (custom_loader);
715 | return result;
716 | }
717 |
718 | static int
719 | execve_wrapper (const char *func, const char *path, char *const argv[], char *const envp[])
720 | {
721 | int (*_execve) (const char *path, char *const argv[], char *const envp[]);
722 | char *new_path = NULL;
723 | char **new_envp = NULL;
724 | int i, result;
725 |
726 | _execve = (int (*)(const char *path, char *const argv[], char *const envp[])) dlsym (RTLD_NEXT, func);
727 |
728 | new_path = redirect_path (path);
729 |
730 | // Make sure we inject our original preload values, can't trust this
731 | // program to pass them along in envp for us.
732 | new_envp = execve_copy_envp (envp);
733 |
734 | result = _execve (new_path, argv, new_envp);
735 |
736 | if (result == -1 && errno == ENOENT) {
737 | // OK, get prepared for gross hacks here. In order to run 32-bit ELF
738 | // executables -- which will hardcode /lib/ld-linux.so.2 as their ld.so
739 | // loader, we must redirect that check to our own version of ld-linux.so.2.
740 | // But that lookup is done behind the scenes by execve, so we can't
741 | // intercept it like normal. Instead, we'll prefix the command by the
742 | // ld.so loader which will only work if the architecture matches. So if
743 | // we failed to run it normally above because the loader couldn't find
744 | // something, try with our own 32-bit loader.
745 | int (*_access) (const char *pathname, int mode);
746 | _access = (int (*)(const char *pathname, int mode)) dlsym (RTLD_NEXT, "access");
747 | if (_access (new_path, F_OK) == 0) {
748 | // Only actually try this if the path actually did exist. That
749 | // means the ENOENT must have been a missing linked library or the
750 | // wrong ld.so loader. Lets assume the latter and try to run as
751 | // a 32-bit executable.
752 | result = execve32_wrapper (_execve, new_path, argv, new_envp);
753 | }
754 | }
755 |
756 | free (new_path);
757 | for (i = 0; new_envp[i]; i++) {
758 | free (new_envp[i]);
759 | }
760 | free (new_envp);
761 |
762 | return result;
763 | }
764 |
765 | int
766 | execv (const char *path, char *const argv[])
767 | {
768 | return execve (path, argv, environ);
769 | }
770 |
771 | int
772 | execve (const char *path, char *const argv[], char *const envp[])
773 | {
774 | return execve_wrapper ("execve", path, argv, envp);
775 | }
776 |
777 | int
778 | __execve (const char *path, char *const argv[], char *const envp[])
779 | {
780 | return execve_wrapper ("__execve", path, argv, envp);
781 | }
782 |
--------------------------------------------------------------------------------
/test-libunionpreload.sh:
--------------------------------------------------------------------------------
1 | #!/usr/bin/env bash
2 |
3 | echo "\nContent of / before libunionpreload"
4 | echo "-----------------------------------\n"
5 |
6 | ls /
7 |
8 | export CURRENT_DIRECTORY="$(dirname "$(readlink -f "${0}")")"
9 | export UNION_PRELOAD="${CURRENT_DIRECTORY}"
10 | export LD_PRELOAD="${CURRENT_DIRECTORY}/libunionpreload.so"
11 |
12 | echo "\nContent of / after libunionpreload"
13 | echo "-----------------------------------\n"
14 |
15 | ls /
16 |
17 | echo
18 |
--------------------------------------------------------------------------------