/* * This function will send a user space message to the kernel. * It returns the sequence number which is > 0 on success * or <= 0 on error. (pam uses this) This is the main audit sending * function now. */ int audit_send_user_message(int fd, int type, hide_t hide_error, const char *message) { int retry_cnt = 0; int rc; retry: rc = audit_send(fd, type, message, strlen(message)+1); if (rc == -ECONNREFUSED) { /* This is here to let people that build their own kernel and disable the audit system get in. ECONNREFUSED is issued by the kernel when there is "no on listening". */ return 0; } else if (rc == -EPERM && getuid() != 0 && hide_error == HIDE_IT) { /* If we get this, then the kernel supports auditing * but we don't have enough privilege to write to the * socket. Therefore, we have already been authenticated * and we are a common user. Just act as though auditing * is not enabled. Any other error we take seriously. * This is here basically to satisfy Xscreensaver. */ return 0; } else if (rc == -EINVAL) { /* If we get this, the kernel doesn't understand the * netlink message type. This is most likely due to * being an old kernel. Use the old message type. */ if (type >= AUDIT_FIRST_USER_MSG && type <= AUDIT_LAST_USER_MSG && !retry_cnt) { /* do retry */ type = AUDIT_USER; retry_cnt++; goto retry; } } return rc; }
/* * 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_setup(int fd, pid_t pid) { int rc; struct audit_message rep; struct audit_status status; memset(&status, 0, sizeof(status)); /* * In order to set the auditd PID we send an audit message over the netlink * socket with the pid field of the status struct set to our current pid, * and the the mask set to AUDIT_STATUS_PID */ status.pid = pid; status.mask = AUDIT_STATUS_PID; /* Let the kernel know this pid will be registering for audit events */ rc = audit_send(fd, AUDIT_SET, &status, sizeof(status)); if (rc < 0) { return rc; } /* * In a request where we need to wait for a response, wait for the message * and discard it. This message confirms and sync's us with the kernel. * This daemon is now registered as the audit logger. * * TODO * If the daemon dies and restarts the message didn't come back, * so I went to non-blocking and it seemed to fix the bug. * Need to investigate further. */ audit_get_reply(fd, &rep, GET_REPLY_NONBLOCKING, 0); 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; }
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_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_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_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_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; }
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; }
int audit_set_failure(int fd, uint32_t failure) { int rc; struct audit_status s; memset(&s, 0, sizeof(s)); s.mask = AUDIT_STATUS_FAILURE; s.failure = failure; rc = audit_send(fd, AUDIT_SET, &s, sizeof(s)); if (rc < 0) audit_msg(audit_priority(errno), "Error sending failure mode request (%s)", strerror(-rc)); return rc; }
int audit_set_backlog_limit(int fd, uint32_t limit) { int rc; struct audit_status s; memset(&s, 0, sizeof(s)); s.mask = AUDIT_STATUS_BACKLOG_LIMIT; s.backlog_limit = limit; rc = audit_send(fd, AUDIT_SET, &s, sizeof(s)); if (rc < 0) audit_msg(audit_priority(errno), "Error sending backlog limit 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_rate_limit(int fd, unsigned rate_limit) { int rc; struct audit_message rep; struct audit_status status; memset(&status, 0, sizeof(status)); status.mask = AUDIT_STATUS_RATE_LIMIT; status.rate_limit = rate_limit; /* audit entries per second */ rc = audit_send(fd, AUDIT_SET, &status, sizeof(status)); if (rc < 0) { return rc; } audit_get_reply(fd, &rep, GET_REPLY_NONBLOCKING, 0); return 0; }
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; }
int audit_delete_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_DEL_RULE, rule, sizeof(struct audit_rule_data) + rule->buflen); 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 data request (%s)", strerror(-rc)); } return rc; }
static int audit_rules_parse_and_add(int audit_fd, char *line) { char *argv[AUDIT_MAX_FIELDS]; int argc; int rc = 0; int added_rule = 0; char p; int opt; size_t len; struct audit_rule_data *rule; /* Strip crlf */ line[strlen(line) -1] = '\0'; argv[0] = "auditd"; for (argc=1; argc < AUDIT_MAX_FIELDS - 1; argc++) { argv[argc] = strsep(&line, " \n\r"); if (argv[argc] == NULL) { break; } } optind = 0; char *field; char *oper; size_t length; int audit_field; int oper_field; int i; while ((opt = getopt(argc, argv, "w:e:p:F:")) != -1) { switch(opt) { case 'w': if (audit_add_dir(&rule, optarg)) { SLOGE("Error adding rule"); return -1; } added_rule = 1; break; case 'e': if (audit_set_enabled(audit_fd, strtoul(optarg, NULL, 10))) { return -1; } break; case 'F': if (added_rule == 0) { SLOGE("Specify rule type before permissions"); return -1; } length = strcspn(optarg, OPERS); field = strndup(optarg, length); if (field == NULL) { SLOGE("Out of memory!"); return -1; } audit_field = string_to_audit_field(field); if (audit_field == 0) { SLOGE("Invalid field: %s", field); free(field); return -1; } free(field); optarg = &optarg[length]; length = strspn(optarg, OPERS); oper = strndup(optarg, length); oper_field = string_to_oper(oper); if (oper_field == -1) { SLOGE("Invalid operator: %s", oper); free(oper); return -1; } free(oper); optarg = &optarg[length]; if (audit_add_field(rule, audit_field, oper_field, optarg) < 0) { SLOGE("Adding field failed"); return -1; } break; case 'p': if (added_rule == 0) { SLOGE("Specify rule type before permissions"); return -1; } uint32_t perms = 0; for (len=0; len < strlen(optarg); len++) { switch(optarg[len]) { case 'w': perms |= AUDIT_PERM_WRITE; break; case 'e': perms |= AUDIT_PERM_EXEC; break; case 'r': perms |= AUDIT_PERM_READ; break; case 'a': perms |= AUDIT_PERM_ATTR; break; default: SLOGE("Unknown permission %c", optarg[len]); break; } } if (audit_update_watch_perms(rule, perms)) { SLOGE("Could not set perms on rule"); return -1; } break; case '?': SLOGE("Unsupported option: %c", optopt); break; } } if (added_rule) { rc = audit_send(audit_fd, AUDIT_ADD_RULE, rule, sizeof(*rule) + rule->buflen); free(rule); } return rc; }