summaryrefslogtreecommitdiff
path: root/src/proc_ledger.c
diff options
context:
space:
mode:
Diffstat (limited to 'src/proc_ledger.c')
-rw-r--r--src/proc_ledger.c344
1 files changed, 344 insertions, 0 deletions
diff --git a/src/proc_ledger.c b/src/proc_ledger.c
new file mode 100644
index 0000000..9faf2f6
--- /dev/null
+++ b/src/proc_ledger.c
@@ -0,0 +1,344 @@
+#include <stdlib.h>
+#include <stdio.h>
+#include <string.h>
+#include <unistd.h>
+#include <time.h>
+#include <sys/types.h>
+#include <sys/stat.h>
+#include <dirent.h>
+//#include <pthread.h>
+
+#include "agent_context.h"
+#include "proc_ledger.h"
+#include "hash_ledger.h"
+#include "output.h"
+#include "error.h"
+#include "json.h"
+#include "proc.h"
+#include "md5.h"
+#include "sha256.h"
+#include "time_common.h"
+
+// TODO these should probably be moved into agent context
+extern unsigned long boot_time;
+extern unsigned long maxsize;
+extern long ticks;
+
+
+bool proc_has_tty(pid_t pid) {
+ char path[PATH_MAX];
+
+ snprintf(path, sizeof(path), "/proc/%d/fd/0", pid);
+
+ struct stat sb;
+ if (stat(path, &sb) == -1) {
+ return false;
+ }
+
+ return S_ISCHR(sb.st_mode);
+}
+
+struct proc_ledger *proc_ledger_init(size_t num_buckets) {
+ struct proc_ledger *ledger = calloc(1, sizeof(struct proc_ledger));
+ if (!ledger) {
+ error_fatal("ledger calloc: %s", strerror(errno));
+ }
+
+ ledger->buckets = calloc(num_buckets, sizeof(struct proc_ledger_entry *));
+ if (!ledger->buckets) {
+ free(ledger);
+ error_fatal("proc ledger buckets calloc: %s", strerror(errno));
+ }
+
+ ledger->num_buckets = num_buckets;
+
+ //if (pthread_mutex_init(&ledger->lock, NULL) != 0) {
+ // error("pthread_mutex_init: %s", strerror(errno));
+ // free(ledger->buckets);
+ // free(ledger);
+ // exit(EXIT_FAILURE);
+ //}
+
+ return ledger;
+}
+
+void proc_ledger_destroy(struct proc_ledger *ledger) {
+ if (!ledger) {
+ return;
+ }
+
+ for (size_t i = 0; i < ledger->num_buckets; i++) {
+ struct proc_ledger_entry *entry = ledger->buckets[i];
+ while(entry) {
+ struct proc_ledger_entry *next = entry->next;
+ free(entry);
+ entry = next;
+ }
+ }
+
+ free(ledger->buckets);
+ //pthread_mutex_destroy(&ledger->lock);
+ free(ledger);
+}
+
+size_t proc_ledger_bucket(struct proc_ledger *ledger, pid_t pid) {
+ return ((size_t)pid) % ledger->num_buckets;
+}
+
+bool proc_ledger_add(struct proc_ledger *ledger, struct proc_ledger_entry *new_entry) {
+ if (!ledger || !new_entry) {
+ return false;
+ }
+
+ //pthread_mutex_lock(&ledger->lock);
+
+ size_t idx = proc_ledger_bucket(ledger, new_entry->pid);
+ new_entry->next = ledger->buckets[idx];
+ ledger->buckets[idx] = new_entry;
+
+ //pthread_mutex_unlock(&ledger->lock);
+
+ return true;
+}
+
+bool proc_ledger_remove(struct proc_ledger *ledger, pid_t pid) {
+ if (!ledger) {
+ return false;
+ }
+
+ //pthread_mutex_lock(&ledger->lock);
+
+ size_t idx = proc_ledger_bucket(ledger, pid);
+ struct proc_ledger_entry *prev = NULL;
+ struct proc_ledger_entry *entry = ledger->buckets[idx];
+
+ while (entry) {
+ if (entry->pid == pid) {
+ if (prev) {
+ prev->next = entry->next;
+ } else {
+ ledger->buckets[idx] = entry->next;
+ }
+
+ free(entry);
+ //pthread_mutex_unlock(&ledger->lock);
+ return true;
+ }
+
+ prev = entry;
+ entry = entry->next;
+ }
+
+ //pthread_mutex_unlock(&ledger->lock);
+ return false; // not found
+}
+
+bool proc_ledger_replace(struct proc_ledger *ledger, struct proc_ledger_entry *new_entry) {
+ if (!ledger || !new_entry) {
+ return false;
+ }
+
+ size_t idx = proc_ledger_bucket(ledger, new_entry->pid);
+ struct proc_ledger_entry **cur = &ledger->buckets[idx];
+
+ while (*cur) {
+ if ((*cur)->pid == new_entry->pid) {
+ new_entry->next = (*cur)->next;
+ free(*cur);
+ *cur = new_entry;
+ return true;
+ }
+
+ cur = &(*cur)->next;
+ }
+
+ // replace if found, otherwise add new entry to bucket
+ new_entry->next = ledger->buckets[idx];
+ ledger->buckets[idx] = new_entry;
+
+ return true;
+}
+
+struct proc_ledger_entry *proc_ledger_find(struct proc_ledger *ledger, pid_t pid) {
+ size_t idx = proc_ledger_bucket(ledger, pid);
+ struct proc_ledger_entry *entry = ledger->buckets[idx];
+
+ if (!entry) {
+ return NULL;
+ }
+
+ if (entry->pid == pid) {
+ return entry;
+ }
+
+ // search list if necessary
+ for (entry = entry->next; entry; entry = entry->next) {
+ if (entry->pid == pid) {
+ return entry;
+ }
+ }
+
+ return NULL; // not found
+}
+
+struct proc_ledger_entry *proc_ledger_entry_create(pid_t pid, agent_context_t *ctx) {
+ struct proc_status ps = proc_get_status(pid);
+ if (ps.pid == -1) { // couldn't fetch status.
+ return NULL;
+ }
+
+ struct proc_stat pstat = proc_parse_stat(pid); // TODO rename this?
+
+ struct proc_ledger_entry *entry = calloc(1, sizeof(struct proc_ledger_entry));
+ if (!entry) {
+ return NULL;
+ }
+
+ entry->pid = ps.pid;
+ entry->tgid = ps.tgid;
+ entry->ppid = ps.ppid;
+ entry->uid = ps.uid;
+ entry->euid = ps.euid;
+ entry->gid = ps.gid;
+ entry->egid = ps.egid;
+ entry->start_time = boot_time + (pstat.starttime / ticks);
+
+ char *exe_path = proc_get_exe_path(pid);
+ if (exe_path) {
+ strncpy(entry->exe, exe_path, sizeof(entry->exe) - 1);
+ entry->exe[sizeof(entry->exe) - 1] = '\0';
+
+ struct stat sb;
+ if (stat(exe_path, &sb) == 0 && sb.st_size < maxsize) {
+ hash_ledger_add_or_update(ctx->hash_ledger, exe_path, &sb);
+ }
+ }
+
+ char *cmdline = proc_get_cmdline(pid);
+ if (cmdline) {
+ strncpy(entry->cmdline, cmdline, sizeof(entry->cmdline) - 1);
+ entry->cmdline[sizeof(entry->cmdline) - 1] = '\0';
+ }
+
+ char *cwd = proc_cwd(pid);
+ if (cwd) {
+ strncpy(entry->cwd, cwd, sizeof(entry->cwd) - 1);
+ entry->cwd[sizeof(entry->cwd) - 1] = '\0';
+ }
+
+ strncpy(entry->comm, ps.name, sizeof(entry->comm) - 1);
+ entry->comm[sizeof(entry->comm) - 1] = '\0';
+
+ entry->daemonized = (ps.ppid == 1); // child of init
+ entry->is_traced = (ps.tracer_pid != 0); // being ptraced
+ entry->tracer_pid = ps.tracer_pid;
+ entry->state = ps.state;
+ entry->seccomp = ps.seccomp;
+ entry->cap_eff = ps.cap_eff;
+ entry->threads = ps.threads;
+ entry->has_tty = proc_has_tty(pid);
+
+ return entry;
+}
+
+void proc_ledger_hydrate(agent_context_t *ctx) {
+ DIR *proc = opendir("/proc");
+ struct dirent *de;
+
+ if (!proc) {
+ error("opendir /proc: %s", strerror(errno));
+ return;
+ }
+
+ while ((de = readdir(proc)) != NULL) {
+ pid_t pid = atoi(de->d_name);
+ if (pid <= 0) {
+ continue;
+ }
+
+ struct proc_ledger_entry *entry = proc_ledger_entry_create(pid, ctx);
+ if (!entry) { // TODO should this have a visible error?
+ continue;
+ }
+
+ proc_ledger_add(ctx->proc_ledger, entry);
+
+ struct stat sb;
+ if (entry->exe[0] && stat(entry->exe, &sb) == 0 && sb.st_size < maxsize) {
+ hash_ledger_add_or_update(ctx->hash_ledger, entry->exe, &sb);
+ }
+
+ json_t json = proc_ledger_entry_to_json(entry, "hydrate", ctx);
+ output(json_get(&json));
+ json_free(&json);
+ }
+
+ closedir(proc);
+}
+
+json_t proc_ledger_entry_to_json(struct proc_ledger_entry *entry,
+ const char *event_type,
+ struct agent_context *ctx) {
+ json_t buf = {0};
+ json_init(&buf);
+ json_start_object(&buf);
+
+ json_add_double(&buf, "timestamp", timestamp());
+ json_add_string(&buf, "hostname", ctx->hostname);
+ json_add_string(&buf, "event_type", event_type);
+ json_add_int(&buf, "pid", entry->pid);
+ json_add_int(&buf, "ppid", entry->ppid);
+ json_add_int(&buf, "tgid", entry->tgid);
+
+ if (entry->exe[0]) {
+ json_add_string(&buf, "exepath", entry->exe);
+
+ struct hash_entry *he = hash_ledger_find(ctx->hash_ledger, entry->exe);
+ if (he) {
+ json_add_string_or_null(&buf, "md5", he->md5);
+ json_add_string_or_null(&buf, "sha256", he->sha256);
+ json_add_double(&buf, "entropy", he->entropy);
+ }
+ } else {
+ json_add_null(&buf, "exepath");
+ json_add_null(&buf, "md5");
+ json_add_null(&buf, "sha256");
+ json_add_null(&buf, "entropy");
+ }
+
+ json_add_string_or_null(&buf, "comm", entry->comm);
+ json_add_string_or_null(&buf, "cmdline", entry->cmdline);
+ json_add_string_or_null(&buf, "cwd", entry->cwd);
+
+ json_add_int(&buf, "uid", entry->uid);
+ json_add_int(&buf, "euid", entry->euid);
+ json_add_int(&buf, "gid", entry->gid);
+ json_add_int(&buf, "egid", entry->egid);
+
+ json_add_int64(&buf, "start_time", entry->start_time);
+ json_add_int64(&buf, "cpu_user_ticks", entry->cpu_user_ticks);
+ json_add_int64(&buf, "cpu_kernel_ticks", entry->cpu_kernel_ticks);
+ json_add_int64(&buf, "rss_bytes", entry->rss);
+ json_add_int64(&buf, "vsize_bytes", entry->vsize);
+
+ if (entry->daemonized) {
+ json_add_bool(&buf, "daemonized", entry->daemonized);
+ }
+ if (entry->is_traced) {
+ json_add_bool(&buf, "is_traced", entry->is_traced);
+ }
+
+ json_add_int(&buf, "tracer_pid", entry->tracer_pid);
+
+ char state[2] = { entry->state, '\0' };
+ json_add_string(&buf, "state", state);
+
+ json_add_int(&buf, "seccomp", entry->seccomp);
+ json_add_uint64(&buf, "cap_eff", entry->cap_eff);
+ json_add_int(&buf, "threads", entry->threads);
+ json_add_bool(&buf, "has_tty", entry->has_tty);
+
+ json_end_object(&buf);
+
+ return buf;
+}