Skip to content

Commit 9082d35

Browse files
Detect when rpath is present so that pattern is more accurate.
1 parent 21e4bfb commit 9082d35

File tree

6 files changed

+227
-10
lines changed

6 files changed

+227
-10
lines changed

src/event.c

+1
Original file line numberDiff line numberDiff line change
@@ -153,6 +153,7 @@ int new_event(const struct fanotify_event_metadata *m, event_t *e)
153153
const char *file = on->o;
154154
if (pinfo->path1 == NULL) {
155155
pinfo->path1 = strdup(file);
156+
pinfo->elf_info = gather_elf(e->fd);
156157
} else if (pinfo->path2 == NULL) {
157158
pinfo->path2 = strdup(file);
158159
pinfo->state = STATE_PARTIAL;

src/file.c

+193
Original file line numberDiff line numberDiff line change
@@ -38,8 +38,10 @@
3838
#include <rpm/rpmlog.h>
3939
#include <magic.h>
4040
#include <libudev.h>
41+
#include <elf.h>
4142
#include "file.h"
4243
#include "message.h"
44+
#include "process.h" // For elf info bit mask
4345

4446
// Local variables
4547
static struct udev *udev;
@@ -351,3 +353,194 @@ char *get_hash_from_fd(int fd)
351353
return digest;
352354
}
353355

356+
static unsigned char e_ident[EI_NIDENT];
357+
358+
static int read_preliminary_header(int fd)
359+
{
360+
ssize_t rc = safe_read(fd, (char *)e_ident, EI_NIDENT);
361+
if (rc == EI_NIDENT)
362+
return 0;
363+
return 1;
364+
}
365+
366+
static Elf32_Ehdr *read_header32(int fd)
367+
{
368+
Elf32_Ehdr *ptr = malloc(sizeof(Elf32_Ehdr));
369+
strcpy(ptr->e_ident, e_ident);
370+
ssize_t rc = safe_read(fd, (char *)&(ptr->e_type), sizeof(Elf32_Ehdr) - EI_NIDENT);
371+
if (rc == (sizeof(Elf32_Ehdr) - EI_NIDENT))
372+
return ptr;
373+
free(ptr);
374+
return NULL;
375+
}
376+
377+
static Elf64_Ehdr *read_header64(int fd)
378+
{
379+
Elf64_Ehdr *ptr = malloc(sizeof(Elf64_Ehdr));
380+
strcpy(ptr->e_ident, e_ident);
381+
ssize_t rc = safe_read(fd, (char *)&(ptr->e_type),
382+
sizeof(Elf64_Ehdr) - EI_NIDENT);
383+
if (rc == (sizeof(Elf64_Ehdr) - EI_NIDENT))
384+
return ptr;
385+
free(ptr);
386+
return NULL;
387+
}
388+
389+
uint32_t gather_elf(int fd)
390+
{
391+
//struct elf_info *e;
392+
uint32_t info = 0;
393+
if (read_preliminary_header(fd))
394+
return 0;
395+
396+
if (strncmp((char *)e_ident, ELFMAG, 4))
397+
return 0;
398+
399+
/* e = malloc(sizeof(struct elf_info));
400+
if (e == NULL)
401+
return 0;
402+
e->first_lib = NULL; */
403+
info |= IS_ELF;
404+
if (e_ident[4] == 1) {
405+
unsigned i;
406+
Elf32_Phdr *ph_tbl = NULL;
407+
408+
Elf32_Ehdr *hdr = read_header32(fd);
409+
if (hdr == NULL)
410+
return 0;
411+
412+
// Look for program header information
413+
// FIXME: Should there be a size check?
414+
ph_tbl = malloc(hdr->e_phentsize * hdr->e_phnum);
415+
if ((unsigned int)lseek(fd, (off_t)hdr->e_phoff, SEEK_SET) !=
416+
hdr->e_phoff)
417+
goto err_out32;
418+
419+
// Read in complete table
420+
if ((unsigned int)safe_read(fd, (char *)ph_tbl,
421+
hdr->e_phentsize * hdr->e_phnum) !=
422+
hdr->e_phentsize * hdr->e_phnum)
423+
goto err_out32;
424+
425+
// Check for rpath record
426+
for (i = 0; i < hdr->e_phnum; i++) {
427+
if (ph_tbl[i].p_type == PT_LOAD)
428+
info |= HAS_LOAD;
429+
else if (ph_tbl[i].p_type == PT_DYNAMIC) {
430+
unsigned int j = 0;
431+
unsigned int num;
432+
433+
info |= HAS_DYNAMIC;
434+
Elf64_Dyn *dyn_tbl = malloc(ph_tbl[i].p_filesz);
435+
if((unsigned int)lseek(fd, ph_tbl[i].p_offset,
436+
SEEK_SET) !=
437+
ph_tbl[i].p_offset)
438+
goto err_out32;
439+
440+
num = ph_tbl[i].p_filesz / sizeof(Elf64_Dyn);
441+
if (num > 1000)
442+
goto err_out32;
443+
444+
if ((unsigned int)safe_read(fd, (char *)dyn_tbl,
445+
ph_tbl[i].p_filesz) !=
446+
ph_tbl[i].p_filesz)
447+
goto err_out32;
448+
449+
while (j < num) {
450+
if (dyn_tbl[j].d_tag == DT_NEEDED) {
451+
} else if (dyn_tbl[j].d_tag == DT_RUNPATH)
452+
info |= HAS_RPATH;
453+
else if (dyn_tbl[j].d_tag == DT_RPATH) {
454+
info |= HAS_RPATH;
455+
break;
456+
}
457+
j++;
458+
}
459+
free(dyn_tbl);
460+
}
461+
if (info & HAS_RPATH)
462+
break;
463+
}
464+
goto done32;
465+
err_out32:
466+
// free(e->first_lib);
467+
// free(e);
468+
// e = NULL;
469+
info |= HAS_ERROR;
470+
done32:
471+
free(ph_tbl);
472+
free(hdr);
473+
} else if (e_ident[4] == 2) {
474+
unsigned i;
475+
Elf64_Phdr *ph_tbl;
476+
477+
Elf64_Ehdr *hdr = read_header64(fd);
478+
if (hdr == NULL)
479+
return 0;
480+
481+
// Look for program header information
482+
// FIXME: Should there be a size check?
483+
ph_tbl = malloc(hdr->e_phentsize * hdr->e_phnum);
484+
if ((unsigned int)lseek(fd, (off_t)hdr->e_phoff, SEEK_SET) !=
485+
hdr->e_phoff)
486+
goto err_out64;
487+
488+
// Read in complete table
489+
if ((unsigned int)safe_read(fd, (char *)ph_tbl,
490+
hdr->e_phentsize * hdr->e_phnum) !=
491+
hdr->e_phentsize * hdr->e_phnum)
492+
goto err_out64;
493+
494+
// Check for rpath record
495+
for (i = 0; i < hdr->e_phnum; i++) {
496+
if (ph_tbl[i].p_type == PT_LOAD)
497+
info |= HAS_LOAD;
498+
if (ph_tbl[i].p_type == PT_DYNAMIC) {
499+
unsigned int j = 0;
500+
unsigned int num;
501+
502+
info |= HAS_DYNAMIC;
503+
Elf64_Dyn *dyn_tbl = malloc(ph_tbl[i].p_filesz);
504+
if ((unsigned int)lseek(fd, ph_tbl[i].p_offset,
505+
SEEK_SET) !=
506+
ph_tbl[i].p_offset)
507+
goto err_out64;
508+
509+
num = ph_tbl[i].p_filesz / sizeof(Elf64_Dyn);
510+
if (num > 1000)
511+
goto err_out64;
512+
513+
if ((unsigned int)safe_read(fd, (char *)dyn_tbl,
514+
ph_tbl[i].p_filesz) !=
515+
ph_tbl[i].p_filesz)
516+
goto err_out64;
517+
518+
while (j < num) {
519+
if (dyn_tbl[j].d_tag == DT_NEEDED) {
520+
} else if (dyn_tbl[j].d_tag == DT_RUNPATH)
521+
info |= HAS_RPATH;
522+
else if (dyn_tbl[j].d_tag == DT_RPATH) {
523+
info |= HAS_RPATH;
524+
break;
525+
}
526+
j++;
527+
}
528+
free(dyn_tbl);
529+
}
530+
if (info & HAS_RPATH)
531+
break;
532+
}
533+
goto done64;
534+
err_out64:
535+
// free(e->first_lib);
536+
// free(e);
537+
// e = NULL;
538+
info |= HAS_ERROR;
539+
done64:
540+
free(ph_tbl);
541+
free(hdr);
542+
}
543+
lseek(fd, 0, SEEK_SET);
544+
return info;
545+
}
546+

src/file.h

+2
Original file line numberDiff line numberDiff line change
@@ -25,6 +25,7 @@
2525
#define FILE_HEADER
2626

2727
#include <sys/types.h>
28+
#include <stdint.h>
2829

2930
// Information we will cache to identify the same executable
3031
struct file_info
@@ -46,5 +47,6 @@ char *get_device_from_stat(unsigned int device, size_t blen, char *buf);
4647
char *get_file_type_from_fd(int fd, size_t blen, char *buf);
4748
int check_packaged_from_file(const char *filename);
4849
char *get_hash_from_fd(int fd);
50+
uint32_t gather_elf(int fd);
4951

5052
#endif

src/process.c

+1
Original file line numberDiff line numberDiff line change
@@ -57,6 +57,7 @@ struct proc_info *stat_proc_entry(pid_t pid)
5757
info->path2 = NULL;
5858
info->path3 = NULL;
5959
info->state = STATE_COLLECTING;
60+
info->elf_info = 0;
6061

6162
return info;
6263
}

src/process.h

+12-2
Original file line numberDiff line numberDiff line change
@@ -25,9 +25,19 @@
2525
#define PROCESS_HEADER
2626

2727
#include <sys/types.h>
28+
#include <stdint.h>
2829

2930
typedef enum { STATE_COLLECTING=0, STATE_PARTIAL, STATE_FULL, STATE_NORMAL,
30-
STATE_LD_PRELOAD, STATE_BAD_INTERPRETER, STATE_LD_SO } state_t;
31+
STATE_NOT_ELF, STATE_LD_PRELOAD, STATE_BAD_INTERPRETER,
32+
STATE_LD_SO } state_t;
33+
34+
// This is used to determine what kind of elf file we are looking at.
35+
// HAS_LOAD but no HAS_DYNAMIC is staticly linked app. Normally you see both.
36+
#define IS_ELF 0x01
37+
#define HAS_ERROR 0x02
38+
#define HAS_RPATH 0x04
39+
#define HAS_DYNAMIC 0x08
40+
#define HAS_LOAD 0x10
3141

3242
// Information we will cache to identify the same executable
3343
struct proc_info
@@ -36,11 +46,11 @@ struct proc_info
3646
dev_t device;
3747
ino_t inode;
3848
struct timespec time;
39-
// FIXME: We can jettison paths when state reaches > Full
4049
state_t state;
4150
char *path1;
4251
char *path2;
4352
char *path3;
53+
uint32_t elf_info;
4454
};
4555

4656
struct proc_info *stat_proc_entry(pid_t pid);

src/rules.c

+18-8
Original file line numberDiff line numberDiff line change
@@ -441,9 +441,14 @@ static int subj_pattern_test(subject_attr_t *s, event_t *e)
441441
int rc = 0;
442442
struct proc_info *pinfo = e->s->info;
443443

444-
// If still collecting, we can't decide yet.
445-
if (pinfo->state == STATE_COLLECTING)
444+
// If still collecting, and its an elf file, we can't decide yet.
445+
if (pinfo->state == STATE_COLLECTING) {
446+
if (pinfo->elf_info == 0) {
447+
pinfo->state = STATE_NOT_ELF;
448+
clear_proc_info(pinfo);
449+
}
446450
return rc;
451+
}
447452

448453
// Do the analysis
449454
#ifdef NEW_WAY
@@ -498,10 +503,11 @@ msg(LOG_DEBUG, "path2: %s", pinfo->path2);
498503
// To get here, pgm matched path1
499504
if (strcmp(pinfo->path2, SYSTEM_LD_SO) == 0) {
500505
// To get here interp is ld.so
501-
if (strcmp(pinfo->path3, SYSTEM_LD_CACHE) == 0)
506+
if (strcmp(pinfo->path3, SYSTEM_LD_CACHE) == 0
507+
|| (pinfo->elf_info & HAS_RPATH))
502508
// ld.so normally checks cache first
503509
pinfo->state = STATE_NORMAL;
504-
else
510+
else
505511
// but preload does the preload
506512
pinfo->state = STATE_LD_PRELOAD;
507513
} else
@@ -514,9 +520,6 @@ msg(LOG_DEBUG, "path2: %s", pinfo->path2);
514520
if (pinfo->state == STATE_PARTIAL)
515521
return rc;
516522

517-
// Done with the paths
518-
clear_proc_info(pinfo);
519-
520523
// Make a decision
521524
switch (s->val)
522525
{
@@ -525,8 +528,12 @@ msg(LOG_DEBUG, "path2: %s", pinfo->path2);
525528
rc = 1;
526529
break;
527530
case PATTERN_LD_PRELOAD_VAL:
528-
if (pinfo->state == STATE_LD_PRELOAD)
531+
if (pinfo->state == STATE_LD_PRELOAD) {
529532
rc = 1;
533+
//msg(LOG_DEBUG, "path1: %s", pinfo->path1);
534+
//msg(LOG_DEBUG, "path2: %s", pinfo->path2);
535+
//msg(LOG_DEBUG, "path3: %s", pinfo->path3);
536+
}
530537
break;
531538
case PATTERN_BAD_INTERPRETER_VAL:
532539
if (pinfo->state == STATE_BAD_INTERPRETER)
@@ -538,6 +545,9 @@ msg(LOG_DEBUG, "path2: %s", pinfo->path2);
538545
break;
539546
}
540547

548+
// Done with the paths
549+
clear_proc_info(pinfo);
550+
541551
return rc;
542552
}
543553

0 commit comments

Comments
 (0)