#include #include #include #include #include #include #include #include //#include #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; }