Line data Source code
1 : #define _GNU_SOURCE
2 : #include <dlfcn.h>
3 : #include <fcntl.h>
4 : #include <kdb.h>
5 : #include <libgen.h>
6 : #include <limits.h>
7 : #include <linux/limits.h>
8 : #include <pwd.h>
9 : #include <stdarg.h>
10 : #include <stddef.h>
11 : #include <stdio.h>
12 : #include <stdlib.h>
13 : #include <string.h>
14 : #include <sys/time.h>
15 : #include <sys/types.h>
16 : #include <unistd.h>
17 :
18 : #include <kdb.h>
19 : #include <kdbmodule.h>
20 : #include <kdbprivate.h>
21 :
22 : #define PRELOAD_PATH "/elektra/intercept/open"
23 : #define TV_MAX_DIGITS 26
24 : #define RELEVANT_FRAME 1
25 :
26 : struct _Node
27 : {
28 : char * key;
29 : char * value;
30 : unsigned short oflags;
31 : char * exportType;
32 : char * exportKey;
33 : time_t creationTime;
34 : struct _Node * next;
35 : };
36 : typedef struct _Node Node;
37 : static Node * head = NULL;
38 :
39 0 : static void canonicalizePath (char * buffer, char * toAppend)
40 : {
41 0 : char * destPtr = buffer + strlen (buffer);
42 0 : for (unsigned int i = 0; i < strlen (toAppend); ++i)
43 : {
44 0 : if (!strncmp ((toAppend + i), "../", 3))
45 : {
46 0 : i += 2;
47 0 : const char * dir = dirname (buffer);
48 0 : size_t dirLen = strlen (dir);
49 0 : destPtr = buffer + dirLen;
50 0 : if (strcmp (dir, "/")) *destPtr++ = '/';
51 0 : *destPtr = '\0';
52 0 : continue;
53 : }
54 0 : else if (!strncmp ((toAppend + i), "./", 2))
55 : {
56 0 : ++i;
57 0 : continue;
58 : }
59 0 : else if (!strncmp ((toAppend + i), "//", 2))
60 : {
61 0 : continue;
62 : }
63 : else
64 : {
65 0 : *destPtr++ = toAppend[i];
66 : }
67 : }
68 0 : }
69 :
70 0 : static char * createAbsolutePath (const char * path, const char * cwd)
71 : {
72 0 : if (path[0] == '/')
73 0 : return elektraStrDup (path);
74 : else
75 : {
76 0 : char * absPath = NULL;
77 : size_t pathlen;
78 0 : char * pathPtr = NULL;
79 0 : if (path[0] == '~')
80 : {
81 0 : struct passwd * pwd = getpwuid (getuid ());
82 0 : pathlen = strlen (path) + strlen (pwd->pw_dir) + 2;
83 0 : absPath = calloc (pathlen, sizeof (char));
84 0 : snprintf (absPath, pathlen, "%s/", pwd->pw_dir);
85 0 : pathPtr = (char *) (path + 2);
86 : }
87 : else
88 : {
89 0 : pathlen = strlen (path) + strlen (cwd) + 2;
90 0 : absPath = calloc (pathlen, sizeof (char));
91 0 : snprintf (absPath, pathlen, "%s/", cwd);
92 0 : pathPtr = (char *) path;
93 : }
94 0 : canonicalizePath (absPath, pathPtr);
95 0 : return absPath;
96 : }
97 : }
98 :
99 0 : static const char * genTemporaryFilename (void)
100 : {
101 : struct timeval tv;
102 0 : gettimeofday (&tv, 0);
103 0 : const char * fileName = "/tmp/.elektra_generated";
104 0 : size_t len = strlen (fileName) + TV_MAX_DIGITS + 1;
105 0 : char * tmpFile = elektraCalloc (len);
106 0 : snprintf (tmpFile, len, "%s_%lu:%lu", fileName, tv.tv_sec, tv.tv_usec);
107 0 : return tmpFile;
108 : }
109 :
110 : static void init (void) __attribute__ ((constructor));
111 : static void cleanup (void) __attribute__ ((destructor));
112 0 : void init (void)
113 : {
114 : char cwd[PATH_MAX];
115 0 : getcwd (cwd, PATH_MAX);
116 0 : KeySet * tmpKS = ksNew (0, KS_END);
117 0 : Key * parentKey = keyNew (PRELOAD_PATH, KEY_CASCADING_NAME, KEY_END);
118 : Key * key;
119 0 : KDB * handle = kdbOpen (parentKey);
120 0 : kdbGet (handle, tmpKS, parentKey);
121 0 : KeySet * ks = ksCut (tmpKS, parentKey);
122 0 : ksRewind (ks);
123 0 : ssize_t size = ksGetSize (ks);
124 0 : if (size <= 1) goto CleanUp;
125 0 : Node * current = head;
126 0 : ksNext (ks); // skip head
127 0 : while ((key = ksNext (ks)) != NULL)
128 : {
129 0 : if (!keyIsDirectBelow (parentKey, key)) continue;
130 0 : Node * tmp = calloc (1, sizeof (Node));
131 0 : tmp->key = createAbsolutePath (keyBaseName (key), cwd);
132 0 : if (!strcmp (keyString (key), ""))
133 0 : tmp->value = NULL;
134 : else
135 0 : tmp->value = createAbsolutePath (keyString (key), cwd);
136 0 : tmp->oflags = (unsigned short) -1;
137 0 : Key * lookupKey = keyDup (key);
138 0 : keyAddBaseName (lookupKey, "readonly");
139 0 : Key * found = ksLookup (ks, lookupKey, 0);
140 0 : if (found)
141 : {
142 0 : if (!strcmp (keyString (found), "1"))
143 : {
144 0 : tmp->oflags = O_RDONLY;
145 : }
146 : }
147 0 : keySetBaseName (lookupKey, 0);
148 0 : keyAddBaseName (lookupKey, "generate");
149 0 : found = ksLookup (ks, lookupKey, 0);
150 0 : if (found)
151 : {
152 0 : if (tmp->value == NULL) tmp->value = (char *) genTemporaryFilename ();
153 0 : tmp->exportKey = elektraStrDup (keyString (found));
154 0 : keyAddBaseName (lookupKey, "plugin");
155 0 : found = ksLookup (ks, lookupKey, 0);
156 0 : if (found)
157 : {
158 0 : tmp->exportType = elektraStrDup (keyString (found));
159 : }
160 : else
161 : {
162 0 : tmp->exportKey = NULL;
163 0 : tmp->exportType = NULL;
164 : }
165 : }
166 : else
167 : {
168 0 : tmp->exportKey = NULL;
169 0 : tmp->exportType = NULL;
170 : }
171 0 : keyDel (lookupKey);
172 0 : if (tmp->value == NULL) tmp->value = createAbsolutePath (keyBaseName (key), cwd);
173 0 : tmp->creationTime = 0;
174 0 : tmp->next = NULL;
175 0 : if (current == NULL)
176 : {
177 0 : head = tmp;
178 0 : current = head;
179 : }
180 : else
181 : {
182 0 : current->next = tmp;
183 0 : current = current->next;
184 : }
185 : }
186 : CleanUp:
187 0 : ksAppend (tmpKS, ks);
188 0 : ksDel (tmpKS);
189 0 : ksDel (ks);
190 0 : kdbClose (handle, parentKey);
191 0 : keyDel (parentKey);
192 0 : }
193 :
194 :
195 0 : void cleanup (void)
196 : {
197 0 : Node * current = head;
198 0 : while (current)
199 : {
200 0 : Node * tmp = current;
201 0 : free (current->key);
202 0 : if (current->value) free (current->value);
203 0 : if (current->exportKey)
204 : {
205 0 : free (current->exportKey);
206 0 : free (current->exportType);
207 : }
208 0 : current = current->next;
209 0 : free (tmp);
210 : }
211 0 : }
212 :
213 0 : static Node * resolvePathname (const char * pathname)
214 : {
215 0 : Node * node = NULL;
216 0 : if (pathname)
217 : {
218 : char cwd[PATH_MAX];
219 0 : getcwd (cwd, PATH_MAX);
220 0 : char * resolvedPath = NULL;
221 0 : if (pathname[0] != '/')
222 : {
223 0 : resolvedPath = createAbsolutePath (pathname, cwd);
224 : }
225 : else
226 : {
227 0 : resolvedPath = calloc (strlen (pathname) + 1, sizeof (char));
228 0 : size_t size = sizeof (resolvedPath);
229 0 : memset (resolvedPath, 0, size);
230 0 : canonicalizePath (resolvedPath, (char *) pathname);
231 : }
232 0 : Node * current = head;
233 0 : while (current)
234 : {
235 0 : if (!strcmp (current->key, resolvedPath))
236 : {
237 : node = current;
238 : break;
239 : }
240 0 : current = current->next;
241 : }
242 0 : free (resolvedPath);
243 : }
244 0 : return node;
245 : }
246 :
247 : int __xstat (int ver, const char * path, struct stat * buf);
248 : int __xstat64 (int ver, const char * path, struct stat64 * buf);
249 :
250 0 : static void exportConfiguration (Node * node)
251 : {
252 0 : Key * key = keyNew (node->exportKey, KEY_END);
253 0 : KDB * handle = kdbOpen (key);
254 0 : KeySet * ks = ksNew (0, KS_END);
255 0 : kdbGet (handle, ks, key);
256 : KeySet * exportKS;
257 0 : exportKS = ksCut (ks, key);
258 0 : KeySet * modules = ksNew (0, KS_END);
259 0 : elektraModulesInit (modules, 0);
260 0 : KeySet * conf = ksNew (0, KS_END);
261 0 : Plugin * check = elektraPluginOpen (node->exportType, modules, conf, key);
262 0 : keySetString (key, node->value);
263 0 : ksRewind (exportKS);
264 0 : check->kdbSet (check, exportKS, key);
265 0 : ksDel (conf);
266 0 : ksAppend (ks, exportKS);
267 0 : ksDel (exportKS);
268 0 : elektraModulesClose (modules, 0);
269 0 : ksDel (modules);
270 0 : keyDel (key);
271 0 : ksDel (ks);
272 0 : kdbClose (handle, 0);
273 : struct stat buf;
274 0 : if (!__xstat (3, node->value, &buf)) node->creationTime = buf.st_mtim.tv_sec;
275 0 : }
276 :
277 :
278 : // e.g. we intercept successive calls of stat and open
279 : static inline int createdWithinTimeframe (int (*f) (int, const char *, struct stat *), Node * node, int frame)
280 : {
281 : struct stat buf;
282 0 : if (!f (3, node->value, &buf))
283 : {
284 0 : if (node->creationTime && ((node->creationTime + frame) < buf.st_mtim.tv_sec)) return 0;
285 : }
286 : else
287 : {
288 : return 0;
289 : }
290 : return 1;
291 : }
292 :
293 : typedef int (*orig_open_f_type) (const char * pathname, int flags, ...);
294 :
295 : typedef union
296 : {
297 : void * d;
298 : orig_open_f_type f;
299 : } OpenSymbol;
300 :
301 :
302 : int open (const char * pathname, int flags, ...)
303 : {
304 0 : Node * node = resolvePathname (pathname);
305 0 : const char * newPath = NULL;
306 0 : unsigned short newFlags = (unsigned short) -1;
307 0 : if (!node)
308 : {
309 : newPath = pathname;
310 : }
311 : else
312 : {
313 0 : if (!(node->exportType))
314 : {
315 0 : newPath = node->value;
316 0 : newFlags = node->oflags;
317 : }
318 : else
319 : {
320 0 : newPath = node->value;
321 0 : if (!createdWithinTimeframe (__xstat, node, RELEVANT_FRAME)) exportConfiguration (node);
322 : }
323 : }
324 0 : if (newFlags == O_RDONLY)
325 : {
326 0 : flags = (flags & (~(0 | O_WRONLY | O_APPEND)));
327 : }
328 : OpenSymbol orig_open;
329 0 : orig_open.d = dlsym (RTLD_NEXT, "open");
330 :
331 : int fd;
332 0 : if (flags & O_CREAT)
333 : {
334 : int mode;
335 : va_list argptr;
336 0 : va_start (argptr, flags);
337 0 : mode = va_arg (argptr, int);
338 0 : va_end (argptr);
339 0 : fd = orig_open.f (newPath, flags, mode);
340 : }
341 : else
342 : {
343 0 : fd = orig_open.f (newPath, flags);
344 : }
345 0 : return fd;
346 : }
347 : int open64 (const char * pathname, int flags, ...)
348 : {
349 0 : Node * node = resolvePathname (pathname);
350 0 : const char * newPath = NULL;
351 0 : unsigned short newFlags = (unsigned short) -1;
352 0 : if (!node)
353 : {
354 : newPath = pathname;
355 : }
356 : else
357 : {
358 0 : if (!(node->exportType))
359 : {
360 0 : newPath = node->value;
361 0 : newFlags = node->oflags;
362 : }
363 : else
364 : {
365 0 : newPath = node->value;
366 0 : if (!createdWithinTimeframe (__xstat, node, RELEVANT_FRAME)) exportConfiguration (node);
367 : }
368 : }
369 0 : if (newFlags == O_RDONLY)
370 : {
371 0 : flags = (flags & (~(0 | O_WRONLY | O_APPEND)));
372 : }
373 :
374 : OpenSymbol orig_open64;
375 0 : orig_open64.d = dlsym (RTLD_NEXT, "open64");
376 :
377 : int fd;
378 0 : if (flags & O_CREAT)
379 : {
380 : int mode;
381 : va_list argptr;
382 0 : va_start (argptr, flags);
383 0 : mode = va_arg (argptr, int);
384 0 : va_end (argptr);
385 0 : fd = orig_open64.f (newPath, flags, mode);
386 : }
387 : else
388 : {
389 0 : fd = orig_open64.f (newPath, flags);
390 : }
391 0 : return fd;
392 : }
393 :
394 : typedef int (*orig_xstat_f_type) (int ver, const char * path, struct stat * buf);
395 : typedef int (*orig_xstat64_f_type) (int ver, const char * path, struct stat64 * buf);
396 :
397 : typedef union
398 : {
399 : void * d;
400 : orig_xstat_f_type f;
401 : } XstatSymbol;
402 :
403 : typedef union
404 : {
405 : void * d;
406 : orig_xstat64_f_type f;
407 : } Xstat64Symbol;
408 :
409 0 : int __xstat (int ver, const char * path, struct stat * buf)
410 : {
411 0 : Node * node = resolvePathname (path);
412 0 : const char * newPath = NULL;
413 : XstatSymbol orig_xstat;
414 0 : orig_xstat.d = dlsym (RTLD_NEXT, "__xstat");
415 0 : if (!node)
416 : newPath = path;
417 : else
418 : {
419 0 : if (!(node->exportType))
420 : {
421 0 : newPath = node->value;
422 : }
423 : else
424 : {
425 0 : newPath = node->value;
426 0 : if (!createdWithinTimeframe (orig_xstat.f, node, RELEVANT_FRAME)) exportConfiguration (node);
427 : }
428 : }
429 :
430 0 : return orig_xstat.f (ver, newPath, buf);
431 : }
432 :
433 0 : int __xstat64 (int ver, const char * path, struct stat64 * buf)
434 : {
435 0 : Node * node = resolvePathname (path);
436 0 : const char * newPath = NULL;
437 : Xstat64Symbol orig_xstat64;
438 0 : orig_xstat64.d = dlsym (RTLD_NEXT, "__xstat64");
439 0 : if (!node)
440 : newPath = path;
441 : else
442 : {
443 0 : if (!(node->exportType))
444 : {
445 0 : newPath = node->value;
446 : }
447 : else
448 : {
449 0 : newPath = node->value;
450 0 : if (!createdWithinTimeframe (__xstat, node, RELEVANT_FRAME)) exportConfiguration (node);
451 : }
452 : }
453 :
454 0 : return orig_xstat64.f (ver, newPath, buf);
455 : }
456 :
457 : typedef int (*orig_access_f_type) (const char * pathname, int mode);
458 :
459 : typedef union
460 : {
461 : void * d;
462 : orig_access_f_type f;
463 : } AccessSymbol;
464 :
465 : int access (const char * pathname, int mode)
466 : {
467 0 : Node * node = resolvePathname (pathname);
468 0 : if (node && mode == F_OK) return 0;
469 : AccessSymbol orig_access;
470 0 : orig_access.d = dlsym (RTLD_NEXT, "access");
471 0 : return orig_access.f (pathname, mode);
472 : }
|