/* * This function opens a connection to the kernel's audit * subsystem. You must be root for the call to succeed. On error, * a negative value is returned. On success, the file descriptor is * returned - which can be 0 or higher. */ int audit_open(void) { int saved_errno; int fd = socket(PF_NETLINK, SOCK_RAW, NETLINK_AUDIT); if (fd < 0) { saved_errno = errno; if (errno == EINVAL || errno == EPROTONOSUPPORT || errno == EAFNOSUPPORT) audit_msg(LOG_ERR, "Error - audit support not in kernel"); else audit_msg(LOG_ERR, "Error opening audit netlink socket (%s)", strerror(errno)); errno = saved_errno; return fd; } if (fcntl(fd, F_SETFD, FD_CLOEXEC) == -1) { saved_errno = errno; close(fd); audit_msg(LOG_ERR, "Error setting audit netlink socket CLOEXEC flag (%s)", strerror(errno)); errno = saved_errno; return -1; } return fd; }
/* * This function returns 0 on success and 1 on failure */ int audit_setloginuid(uid_t uid) { char loginuid[16]; int o, count, rc = 0; errno = 0; count = snprintf(loginuid, sizeof(loginuid), "%u", uid); o = open("/proc/self/loginuid", O_NOFOLLOW|O_WRONLY|O_TRUNC); if (o >= 0) { int block, offset = 0; while (count > 0) { block = write(o, &loginuid[offset], (unsigned)count); if (block < 0) { if (errno == EINTR) continue; audit_msg(LOG_ERR, "Error writing loginuid"); close(o); return 1; } offset += block; count -= block; } close(o); } else { audit_msg(LOG_ERR, "Error opening /proc/self/loginuid"); rc = 1; } return rc; }
static int write_pid_file(void) { int pidfd, len; char val[16]; len = snprintf(val, sizeof(val), "%u\n", getpid()); if (len <= 0) { audit_msg(LOG_ERR, "Pid error (%s)", strerror(errno)); pidfile = 0; return 1; } pidfd = open(pidfile, O_CREAT | O_TRUNC | O_NOFOLLOW | O_WRONLY, 0644); if (pidfd < 0) { audit_msg(LOG_ERR, "Unable to set pidfile (%s)", strerror(errno)); pidfile = 0; return 1; } if (write(pidfd, val, (unsigned int)len) != len) { audit_msg(LOG_ERR, "Unable to write pidfile (%s)", strerror(errno)); close(pidfd); pidfile = 0; return 1; } close(pidfd); return 0; }
/* Returns -1 on err, 0 on success, and 1 if eagain occurred and not an err */ int dispatch_event(const struct audit_reply *rep, int is_err) { int rc, count = 0; struct iovec vec[2]; struct audit_dispatcher_header hdr; if (disp_pipe[1] == -1) return 0; // Don't send reconfig or rotate as they are purely internal to daemon if (rep->type == AUDIT_DAEMON_RECONFIG || rep->type == AUDIT_DAEMON_ROTATE) return 0; hdr.ver = AUDISP_PROTOCOL_VER; /* Hard-coded to current protocol */ hdr.hlen = sizeof(struct audit_dispatcher_header); hdr.type = rep->type; hdr.size = rep->len; vec[0].iov_base = (void*)&hdr; vec[0].iov_len = sizeof(hdr); vec[1].iov_base = (void*)rep->message; vec[1].iov_len = rep->len; do { rc = writev(disp_pipe[1], vec, 2); } while (rc < 0 && errno == EAGAIN && count++ < 8); // close pipe if no child or peer has been lost if (rc <= 0) { if (errno == EPIPE) { shutdown_dispatcher(); n_errs = 0; } else if (errno == EAGAIN && !is_err) { return 1; } else { if (n_errs <= REPORT_LIMIT) { audit_msg(LOG_ERR, "dispatch err (%s) event lost", errno == EAGAIN ? "pipe full" : strerror(errno)); n_errs++; } if (n_errs == REPORT_LIMIT) { audit_msg(LOG_ERR, "dispatch error reporting limit" " reached - ending report" " notification."); n_errs++; } return -1; } } else n_errs = 0; return 0; }
static int path_parser(struct nv_pair *nv, int line, plugin_conf_t *config) { char *dir = NULL, *tdir; struct stat buf; if (nv->value == NULL) { config->path = NULL; return 0; } if (strncasecmp(nv->value, "builtin_", 8) == 0) { config->path = strdup(nv->value); return 0; } /* get dir form name. */ tdir = strdup(nv->value); if (tdir) dir = dirname(tdir); if (dir == NULL || strlen(dir) < 4) { // '/var' is shortest dirname audit_msg(LOG_ERR, "The directory name: %s is too short - line %d", dir, line); free(tdir); return 1; } free((void *)tdir); /* If the file exists, see that its regular, owned by root, * and not world anything */ if (stat(nv->value, &buf) < 0) { audit_msg(LOG_ERR, "Unable to stat %s (%s)", nv->value, strerror(errno)); return 1; } if (!S_ISREG(buf.st_mode)) { audit_msg(LOG_ERR, "%s is not a regular file", nv->value); return 1; } if (buf.st_uid != 0) { audit_msg(LOG_ERR, "%s is not owned by root", nv->value); return 1; } if ((buf.st_mode & (S_IRUSR|S_IWUSR|S_IXUSR|S_IRGRP|S_IXGRP)) != (S_IRUSR|S_IWUSR|S_IXUSR|S_IRGRP|S_IXGRP)) { audit_msg(LOG_ERR, "%s permissions should be 0750", nv->value); return 1; } free((void *)config->path); config->path = strdup(nv->value); config->inode = buf.st_ino; if (config->path == NULL) return 1; return 0; }
void audit_start(f_state *s, f_info *i) { if (!get_mode(s, mode_quiet)) { fprintf(stderr, "Processing: %s\n|", i->file_name); } audit_msg(s, FOREMOST_DIVIDER); audit_msg(s, "File: %s", i->file_name); audit_msg(s, "Start: %s", current_time()); }
/* * This function returns 0 on error and len on success. */ static int adjust_reply(struct audit_reply *rep, int len) { rep->type = rep->msg.nlh.nlmsg_type; rep->len = rep->msg.nlh.nlmsg_len; rep->nlh = &rep->msg.nlh; rep->status = NULL; rep->rule = NULL; rep->login = NULL; rep->message = NULL; rep->error = NULL; rep->signal_info = NULL; rep->conf = NULL; if (!NLMSG_OK(rep->nlh, (unsigned int)len)) { if (len == sizeof(rep->msg)) { audit_msg(LOG_ERR, "Netlink event from kernel is too big"); errno = EFBIG; } else { audit_msg(LOG_ERR, "Netlink message from kernel was not OK"); errno = EBADE; } return 0; } /* Next we'll set the data structure to point to msg.data. This is * to avoid having to use casts later. */ switch (rep->type) { case NLMSG_ERROR: rep->error = NLMSG_DATA(rep->nlh); break; case AUDIT_GET: rep->status = NLMSG_DATA(rep->nlh); break; case AUDIT_LIST: rep->rule = NLMSG_DATA(rep->nlh); break; case AUDIT_LIST_RULES: rep->ruledata = NLMSG_DATA(rep->nlh); break; case AUDIT_USER: case AUDIT_LOGIN: case AUDIT_KERNEL: case AUDIT_FIRST_USER_MSG...AUDIT_LAST_USER_MSG: case AUDIT_FIRST_USER_MSG2...AUDIT_LAST_USER_MSG2: case AUDIT_FIRST_EVENT...AUDIT_INTEGRITY_LAST_MSG: rep->message = NLMSG_DATA(rep->nlh); break; case AUDIT_SIGNAL_INFO: rep->signal_info = NLMSG_DATA(rep->nlh); break; } return len; }
static int audit_failure_parser(const char *val, int line) { int i; audit_msg(LOG_DEBUG, "audit_failure_parser called with: %s", val); for (i=0; failure_actions[i].name != NULL; i++) { if (strcasecmp(val, failure_actions[i].name) == 0) { config.failure_action = failure_actions[i].option; return 0; } } audit_msg(LOG_ERR, "Option %s not found - line %d", val, line); return 1; }
/* This function returns 1 on error & 0 on success */ int init_dispatcher(const struct daemon_conf *config) { if (config->dispatcher == NULL) return 0; if (socketpair(AF_UNIX, SOCK_STREAM, 0, disp_pipe)) { audit_msg(LOG_ERR, "Failed creating disp_pipe"); return 1; } /* Make both disp_pipe non-blocking */ if (config->qos == QOS_NON_BLOCKING) { if (set_flags(disp_pipe[0], O_NONBLOCK) < 0 || set_flags(disp_pipe[1], O_NONBLOCK) < 0) { audit_msg(LOG_ERR, "Failed to set O_NONBLOCK flag"); return 1; } } // do the fork pid = fork(); switch(pid) { case 0: // child dup2(disp_pipe[0], 0); close(disp_pipe[0]); close(disp_pipe[1]); setsid(); execl(config->dispatcher, config->dispatcher, NULL); audit_msg(LOG_ERR, "exec() failed"); exit(1); break; case -1: // error return 1; break; default: // parent close(disp_pipe[0]); disp_pipe[0] = -1; /* Avoid leaking this */ if (fcntl(disp_pipe[1], F_SETFD, FD_CLOEXEC) < 0) { audit_msg(LOG_ERR, "Failed to set FD_CLOEXEC flag"); return 1; } audit_msg(LOG_INFO, "Started dispatcher: %s pid: %u", config->dispatcher, pid); break; } return 0; }
/* * This function is part of the directory auditing code */ int audit_make_equivalent(int fd, const char *mount_point, const char *subtree) { int rc; size_t len1 = strlen(mount_point); size_t len2 = strlen(subtree); struct { uint32_t sizes[2]; unsigned char buf[]; } *cmd = malloc(sizeof(*cmd) + len1 + len2); memset(cmd, 0, sizeof(*cmd) + len1 + len2); cmd->sizes[0] = len1; cmd->sizes[1] = len2; memcpy(&cmd->buf[0], mount_point, len1); memcpy(&cmd->buf[len1], subtree, len2); rc = audit_send(fd, AUDIT_MAKE_EQUIV, cmd, sizeof(*cmd) + len1 + len2); if (rc < 0) audit_msg(audit_priority(errno), "Error sending make_equivalent command (%s)", strerror(-rc)); free(cmd); return rc; }
static char *get_line(FILE *f, char *buf, unsigned size, int *lineno, const char *file) { int too_long = 0; while (fgets_unlocked(buf, size, f)) { /* remove newline */ char *ptr = strchr(buf, 0x0a); if (ptr) { if (!too_long) { *ptr = 0; return buf; } // Reset and start with the next line too_long = 0; *lineno = *lineno + 1; } else { // If a line is too long skip it. // Only output 1 warning if (!too_long) audit_msg(LOG_ERR, "Skipping line %d in %s: too long", *lineno, file); too_long = 1; } } return NULL; }
/* * This function returns -1 on error and 1 on success. */ int audit_set_pid(int fd, uint32_t pid, rep_wait_t wmode) { struct audit_status s; struct audit_reply rep; struct pollfd pfd[1]; int rc; memset(&s, 0, sizeof(s)); s.mask = AUDIT_STATUS_PID; s.pid = pid; rc = audit_send(fd, AUDIT_SET, &s, sizeof(s)); if (rc < 0) { audit_msg(audit_priority(errno), "Error setting audit daemon pid (%s)", strerror(-rc)); return rc; } if (wmode == WAIT_NO) return 1; /* Now we'll see if there's any reply message. This only happens on error. It is not fatal if there is no message. As a matter of fact, we don't do anything with the message besides gobble it. */ pfd[0].fd = fd; pfd[0].events = POLLIN; do { rc = poll(pfd, 1, 100); /* .1 second */ } while (rc < 0 && errno == EINTR); (void)audit_get_reply(fd, &rep, GET_REPLY_NONBLOCKING, 0); return 1; }
int audit_request_status(int fd) { int rc = audit_send(fd, AUDIT_GET, NULL, 0); if (rc < 0) audit_msg(audit_priority(errno), "Error sending status request (%s)", strerror(-rc)); return rc; }
int audit_delete_rule(int fd, struct audit_rule *rule, int flags, int action) { int rc; rule->flags = flags; rule->action = action; rc = audit_send(fd, AUDIT_DEL, rule, sizeof(struct audit_rule)); if (rc < 0) { if (rc == -ENOENT) audit_msg(LOG_WARNING, "Error sending delete rule request (No rule matches)"); else audit_msg(audit_priority(errno), "Error sending delete rule request (%s)", strerror(-rc)); } return rc; }
int audit_request_signal_info(int fd) { int rc = audit_send(fd, AUDIT_SIGNAL_INFO, NULL, 0); if (rc < 0) audit_msg(LOG_WARNING, "Error sending signal_info request (%s)", strerror(-rc)); return rc; }
int audit_request_rules_list(int fd) { int rc = audit_send(fd, AUDIT_LIST, NULL, 0); if (rc < 0) audit_msg(audit_priority(errno), "Error sending rule list request (%s)", strerror(-rc)); return rc; }
int audit_request_rules_list_data(int fd) { int rc = audit_send(fd, AUDIT_LIST_RULES, NULL, 0); if (rc < 0 && rc != -EINVAL) audit_msg(audit_priority(errno), "Error sending rule list data request (%s)", strerror(-rc)); return rc; }
/* * This function is part of the directory auditing code */ int audit_trim_subtrees(int fd) { int rc = audit_send(fd, AUDIT_TRIM, NULL, 0); if (rc < 0) audit_msg(audit_priority(errno), "Error sending trim subtrees command (%s)", strerror(-rc)); return rc; }
int audit_add_watch_dir(int type, struct audit_rule_data **rulep, const char *path) { size_t len = strlen(path); struct audit_rule_data *rule = *rulep; if (rule && rule->field_count) { audit_msg(LOG_ERR, "Rule is not empty\n"); return -1; } if (type != AUDIT_WATCH && type != AUDIT_DIR) { audit_msg(LOG_ERR, "Invalid type used\n"); return -1; } *rulep = realloc(rule, len + sizeof(*rule)); if (*rulep == NULL) { free(rule); audit_msg(LOG_ERR, "Cannot realloc memory!\n"); return -1; } rule = *rulep; memset(rule, 0, len + sizeof(*rule)); rule->flags = AUDIT_FILTER_EXIT; rule->action = AUDIT_ALWAYS; audit_rule_syscallbyname_data(rule, "all"); rule->field_count = 2; rule->fields[0] = type; rule->values[0] = len; rule->fieldflags[0] = AUDIT_EQUAL; rule->buflen = len; memcpy(&rule->buf[0], path, len); // Default to all permissions rule->fields[1] = AUDIT_PERM; rule->fieldflags[1] = AUDIT_EQUAL; rule->values[1] = AUDIT_PERM_READ | AUDIT_PERM_WRITE | AUDIT_PERM_EXEC | AUDIT_PERM_ATTR; _audit_permadded = 1; return 0; }
/* * This function returns -1 on error, 0 if error response received, * and > 0 if packet OK. */ int audit_get_reply(int fd, struct audit_reply *rep, reply_t block, int peek) { int len; struct sockaddr_nl nladdr; socklen_t nladdrlen = sizeof(nladdr); if (fd < 0) return -EBADF; if (block == GET_REPLY_NONBLOCKING) block = MSG_DONTWAIT; retry: len = recvfrom(fd, &rep->msg, sizeof(rep->msg), block|peek, (struct sockaddr*)&nladdr, &nladdrlen); if (len < 0) { if (errno == EINTR) goto retry; if (errno != EAGAIN) { int saved_errno = errno; audit_msg(LOG_ERR, "Error receiving audit netlink packet (%s)", strerror(errno)); errno = saved_errno; } return -errno; } if (nladdrlen != sizeof(nladdr)) { audit_msg(LOG_ERR, "Bad address size reading audit netlink socket"); return -EPROTO; } if (nladdr.nl_pid) { audit_msg(LOG_ERR, "Spoofed packet received on audit netlink socket"); return -EINVAL; } len = adjust_reply(rep, len); if (len == 0) len = -errno; return len; }
/* * This function is where we do the integrated check of the audispd config * options. At this point, all fields have been read. Returns 0 if no * problems and 1 if problems detected. */ static int sanity_check(plugin_conf_t *config, const char *file) { /* Error checking */ if (config->active == A_YES && config->path == NULL) { audit_msg(LOG_ERR, "Error - plugin (%s) is active but no path given", file); return 1; } return 0; }
/* * A clean exit means : * 1) we log that we are going down * 2) deregister with kernel * 3) close the netlink socket */ static void clean_exit(void) { audit_msg(LOG_INFO, "The audit daemon is exiting."); if (fd >= 0) { audit_set_pid(fd, 0, WAIT_NO); audit_close(fd); } if (pidfile) unlink(pidfile); closelog(); }
int audit_add_rule_data(int fd, struct audit_rule_data *rule, int flags, int action) { int rc; if (flags == AUDIT_FILTER_ENTRY) { audit_msg(LOG_WARNING, "Use of entry filter is deprecated"); return -2; } rule->flags = flags; rule->action = action; rc = audit_send(fd, AUDIT_ADD_RULE, rule, sizeof(struct audit_rule_data) + rule->buflen); if (rc < 0) audit_msg(audit_priority(errno), "Error sending add rule data request (%s)", errno == EEXIST ? "Rule exists" : strerror(-rc)); return rc; }
void audit_layout(f_state *s) { audit_msg(s, "Num\t %s (bs=%d)\t %10s\t %s\t %s \n", "Name", s->block_size, "Size", "File Offset", "Comment"); }
int user_interrupt (f_state * s, f_info * i) { audit_msg(s, "Interrupt received at %s", current_time()); /* RBF - Write user_interrupt */ fclose(i->handle); free(s); free(i); cleanup_output(s); exit(-1); return FALSE; }
void setup_stream(f_state *s, f_info *i) { char buffer[MAX_STRING_LENGTH]; u_int64_t skip = (((u_int64_t) s->skip) * ((u_int64_t) s->block_size)); #ifdef DEBUG printf("s->skip=%d s->block_size=%d total=%llu\n", s->skip, s->block_size, (((u_int64_t) s->skip) * ((u_int64_t) s->block_size))); #endif i->bytes_read = 0; i->total_megs = i->total_bytes / ONE_MEGABYTE; if (i->total_bytes != 0) { audit_msg(s, "Length: %s (%llu bytes)", human_readable(i->total_bytes, buffer), i->total_bytes); } else audit_msg(s, "Length: Unknown"); if (s->skip != 0) { audit_msg(s, "Skipping: %s (%llu bytes)", human_readable(skip, buffer), skip); fseeko(i->handle, skip, SEEK_SET); if (i->total_bytes != 0) i->total_bytes -= skip; } audit_msg(s, " "); #ifdef __WIN32 i->last_read = 0; i->overflow_count = 0; #endif }
int audit_set_enabled(int fd, uint32_t enabled) { int rc; struct audit_status s; memset(&s, 0, sizeof(s)); s.mask = AUDIT_STATUS_ENABLED; s.enabled = enabled; rc = audit_send(fd, AUDIT_SET, &s, sizeof(s)); if (rc < 0) audit_msg(audit_priority(errno), "Error sending enable request (%s)", strerror(-rc)); return rc; }
static int format_parser(struct nv_pair *nv, int line, plugin_conf_t *config) { int i; for (i=0; formats[i].name != NULL; i++) { if (strcasecmp(nv->value, formats[i].name) == 0) { config->format = formats[i].option; return 0; } } audit_msg(LOG_ERR, "Option %s not found - line %d", nv->value, line); return 1; }
int audit_add_rule(int fd, struct audit_rule *rule, int flags, int action) { int rc; rule->flags = flags; rule->action = action; rc = audit_send(fd, AUDIT_ADD, rule, sizeof(struct audit_rule)); if (rc < 0) audit_msg(audit_priority(errno), "Error sending add rule request (%s)", errno == EEXIST ? "Rule exists" : strerror(-rc)); return rc; }
/* set_flags: to set flags to file desc */ static int set_flags(int fn, int flags) { int fl; if ((fl = fcntl(fn, F_GETFL, 0)) < 0) { audit_msg(LOG_ERR, "fcntl failed. Cannot get flags (%s)\n", strerror(errno)); return fl; } fl |= flags; return fcntl(fn, F_SETFL, fl); }