/* * returns: -3 deprecated, -2 success - no reply, -1 error - noreply, * 0 success - reply, > 0 success - rule */ static int setopt(int count, int lineno, char *vars[]) { int c; int retval = 0, rc; optind = 0; opterr = 0; key[0] = 0; keylen = AUDIT_MAX_KEY_LEN; while ((retval >= 0) && (c = getopt(count, vars, "hicslDvtC:e:f:r:b:a:A:d:S:F:m:R:w:W:k:p:q:")) != EOF) { int flags = AUDIT_FILTER_UNSET; rc = 10; // Init to something impossible to see if unused. switch (c) { case 'h': usage(); retval = -1; break; case 'i': ignore = 1; retval = -2; break; case 'c': ignore = 1; continue_error = 1; retval = -2; break; case 's': retval = audit_request_status(fd); if (retval <= 0) retval = -1; else retval = 0; /* success - just get the reply */ break; case 'e': if (optarg && ((strcmp(optarg, "0") == 0) || (strcmp(optarg, "1") == 0) || (strcmp(optarg, "2") == 0))) { if (audit_set_enabled(fd, strtoul(optarg,NULL,0)) > 0) audit_request_status(fd); else retval = -1; } else { fprintf(stderr, "Enable must be 0, 1, or 2 was %s\n", optarg); retval = -1; } break; case 'f': if (optarg && ((strcmp(optarg, "0") == 0) || (strcmp(optarg, "1") == 0) || (strcmp(optarg, "2") == 0))) { if (audit_set_failure(fd, strtoul(optarg,NULL,0)) > 0) audit_request_status(fd); else return -1; } else { fprintf(stderr, "Failure must be 0, 1, or 2 was %s\n", optarg); retval = -1; } break; case 'r': if (optarg && isdigit(optarg[0])) { uint32_t rate; errno = 0; rate = strtoul(optarg,NULL,0); if (errno) { fprintf(stderr, "Error converting rate\n"); return -1; } if (audit_set_rate_limit(fd, rate) > 0) audit_request_status(fd); else return -1; } else { fprintf(stderr, "Rate must be a numeric value was %s\n", optarg); retval = -1; } break; case 'b': if (optarg && isdigit(optarg[0])) { uint32_t limit; errno = 0; limit = strtoul(optarg,NULL,0); if (errno) { fprintf(stderr, "Error converting backlog\n"); return -1; } if (audit_set_backlog_limit(fd, limit) > 0) audit_request_status(fd); else return -1; } else { fprintf(stderr, "Backlog must be a numeric value was %s\n", optarg); retval = -1; } break; case 'l': if (count > 4 || count == 3) { fprintf(stderr, "Wrong number of options for list request\n"); retval = -1; break; } if (count == 4) { if (strcmp(vars[optind], "-k") == 0) { strncat(key, vars[3], keylen); count -= 2; } else { fprintf(stderr, "Only the -k option is allowed\n"); retval = -1; break; } } if (audit_request_rule_list(fd)) retval = -2; else retval = -1; break; case 'a': if (strstr(optarg, "task") && _audit_syscalladded) { fprintf(stderr, "Syscall auditing requested for task list\n"); retval = -1; } else { rc = audit_rule_setup(optarg, &add, &action, lineno); if (rc == 3) { fprintf(stderr, "Multiple rule insert/delete operations are not allowed\n"); retval = -1; } else if (rc == 2) { fprintf(stderr, "Append rule - bad keyword %s\n", optarg); retval = -1; } else if (rc == 1) { fprintf(stderr, "Append rule - possible is deprecated\n"); return -3; /* deprecated - eat it */ } else retval = 1; /* success - please send */ } break; case 'A': if (strstr(optarg, "task") && _audit_syscalladded) { fprintf(stderr, "Error: syscall auditing requested for task list\n"); retval = -1; } else { rc = audit_rule_setup(optarg, &add, &action, lineno); if (rc == 3) { fprintf(stderr, "Multiple rule insert/delete operations are not allowed\n"); retval = -1; } else if (rc == 2) { fprintf(stderr, "Add rule - bad keyword %s\n", optarg); retval = -1; } else if (rc == 1) { fprintf(stderr, "Append rule - possible is deprecated\n"); return -3; /* deprecated - eat it */ } else { add |= AUDIT_FILTER_PREPEND; retval = 1; /* success - please send */ } } break; case 'd': rc = audit_rule_setup(optarg, &del, &action, lineno); if (rc == 3) { fprintf(stderr, "Multiple rule insert/delete operations are not allowed\n"); retval = -1; } else if (rc == 2) { fprintf(stderr, "Delete rule - bad keyword %s\n", optarg); retval = -1; } else if (rc == 1) { fprintf(stderr, "Delete rule - possible is deprecated\n"); return -3; /* deprecated - eat it */ } else retval = 1; /* success - please send */ break; case 'S': { int unknown_arch = !_audit_elf; /* Do some checking to make sure that we are not adding a * syscall rule to a list that does not make sense. */ if (((add & (AUDIT_FILTER_MASK|AUDIT_FILTER_UNSET)) == AUDIT_FILTER_TASK || (del & (AUDIT_FILTER_MASK|AUDIT_FILTER_UNSET)) == AUDIT_FILTER_TASK)) { fprintf(stderr, "Error: syscall auditing being added to task list\n"); return -1; } else if (((add & (AUDIT_FILTER_MASK|AUDIT_FILTER_UNSET)) == AUDIT_FILTER_USER || (del & (AUDIT_FILTER_MASK|AUDIT_FILTER_UNSET)) == AUDIT_FILTER_USER)) { fprintf(stderr, "Error: syscall auditing being added to user list\n"); return -1; } else if (exclude) { fprintf(stderr, "Error: syscall auditing cannot be put on exclude list\n"); return -1; } else { if (unknown_arch) { int machine; unsigned int elf; machine = audit_detect_machine(); if (machine < 0) { fprintf(stderr, "Error detecting machine type\n"); return -1; } elf = audit_machine_to_elf(machine); if (elf == 0) { fprintf(stderr, "Error looking up elf type\n"); return -1; } _audit_elf = elf; } } rc = audit_rule_syscallbyname_data(rule_new, optarg); switch (rc) { case 0: _audit_syscalladded = 1; if (unknown_arch && add != AUDIT_FILTER_UNSET) check_rule_mismatch(lineno, optarg); break; case -1: fprintf(stderr, "Syscall name unknown: %s\n", optarg); retval = -1; break; case -2: fprintf(stderr, "Elf type unknown: 0x%x\n", _audit_elf); retval = -1; break; }} break; case 'F': if (add != AUDIT_FILTER_UNSET) flags = add & AUDIT_FILTER_MASK; else if (del != AUDIT_FILTER_UNSET) flags = del & AUDIT_FILTER_MASK; // if the field is arch & there is a -t option...we // can allow it else if ((optind >= count) || (strstr(optarg, "arch=") == NULL) || (strcmp(vars[optind], "-t") != 0)) { fprintf(stderr, "List must be given before field\n"); retval = -1; break; } rc = audit_rule_fieldpair_data(&rule_new,optarg,flags); if (rc != 0) { audit_number_to_errmsg(rc, optarg); retval = -1; } else { if (rule_new->fields[rule_new->field_count-1] == AUDIT_PERM) _audit_permadded = 1; } break; case 'C': if (add != AUDIT_FILTER_UNSET) flags = add & AUDIT_FILTER_MASK; else if (del != AUDIT_FILTER_UNSET) flags = del & AUDIT_FILTER_MASK; rc = audit_rule_interfield_comp_data(&rule_new, optarg, flags); if (rc != 0) { audit_number_to_errmsg(rc, optarg); retval = -1; } else { if (rule_new->fields[rule_new->field_count - 1] == AUDIT_PERM) _audit_permadded = 1; } break; case 'm': if (count > 3) { fprintf(stderr, "The -m option must be only the only option and takes 1 parameter\n"); retval = -1; } else if (audit_log_user_message( fd, AUDIT_USER, optarg, NULL, NULL, NULL, 1) <= 0) retval = -1; else return -2; // success - no reply for this break; case 'R': fprintf(stderr, "Error - nested rule files not supported\n"); retval = -1; break; case 'D': if (count > 4 || count == 3) { fprintf(stderr, "Wrong number of options for Delete all request\n"); retval = -1; break; } if (count == 4) { if (strcmp(vars[optind], "-k") == 0) { strncat(key, vars[3], keylen); count -= 2; } else { fprintf(stderr, "Only the -k option is allowed\n"); retval = -1; break; } } retval = delete_all_rules(fd); if (retval == 0) { (void)audit_request_rule_list(fd); key[0] = 0; retval = -2; } break; case 'w': if (add != AUDIT_FILTER_UNSET || del != AUDIT_FILTER_UNSET) { fprintf(stderr, "watch option can't be given with a syscall\n"); retval = -1; } else if (optarg) { add = AUDIT_FILTER_EXIT; action = AUDIT_ALWAYS; _audit_syscalladded = 1; retval = audit_setup_watch_name(&rule_new, optarg); } else { fprintf(stderr, "watch option needs a path\n"); retval = -1; } break; case 'W': if (optarg) { del = AUDIT_FILTER_EXIT; action = AUDIT_ALWAYS; _audit_syscalladded = 1; retval = audit_setup_watch_name(&rule_new, optarg); } else { fprintf(stderr, "watch option needs a path\n"); retval = -1; } break; case 'k': if (!(_audit_syscalladded || _audit_permadded ) || (add==AUDIT_FILTER_UNSET && del==AUDIT_FILTER_UNSET)) { fprintf(stderr, "key option needs a watch or syscall given prior to it\n"); retval = -1; } else if (!optarg) { fprintf(stderr, "key option needs a value\n"); retval = -1; } else if ((strlen(optarg)+strlen(key)+(!!key[0])) > AUDIT_MAX_KEY_LEN) { fprintf(stderr, "key option exceeds size limit\n"); retval = -1; } else { if (strncmp(optarg, "ids-", 4) == 0) { if (check_ids_key(optarg)) { retval = -1; break; } } if (strchr(optarg, AUDIT_KEY_SEPARATOR)) fprintf(stderr, "key %s has illegal character\n", optarg); if (key[0]) { // Add the separator if we need to strcat(key, key_sep); keylen--; } strncat(key, optarg, keylen); keylen = AUDIT_MAX_KEY_LEN - strlen(key); } break; case 'p': if (!add && !del) { fprintf(stderr, "permission option needs a watch given prior to it\n"); retval = -1; } else if (!optarg) { fprintf(stderr, "permission option needs a filter\n"); retval = -1; } else retval = audit_setup_perms(rule_new, optarg); break; case 'q': if (_audit_syscalladded) { fprintf(stderr, "Syscall auditing requested for make equivalent\n"); retval = -1; } else { char *mp, *sub; retval = equiv_parse(optarg, &mp, &sub); if (retval < 0) { fprintf(stderr, "Error parsing equivalent parts\n"); retval = -1; } else { retval = audit_make_equivalent(fd, mp, sub); if (retval <= 0) { retval = -1; } else return -2; // success - no reply needed } } break; case 't': retval = audit_trim_subtrees(fd); if (retval <= 0) retval = -1; else return -2; // success - no reply for this break; case 'v': printf("auditctl version %s\n", VERSION); retval = -2; break; default: usage(); retval = -1; break; } } /* catch extra args or errors where the user types "- s" */ if (optind == 1) retval = -1; else if ((optind < count) && (retval != -1)) { fprintf(stderr, "parameter passed without an option given\n"); retval = -1; } /* See if we were adding a key */ if (key[0] && list_requested == 0) { int flags = 0; char *cmd=NULL; /* Get the flag */ if (add != AUDIT_FILTER_UNSET) flags = add & AUDIT_FILTER_MASK; else if (del != AUDIT_FILTER_UNSET) flags = del & AUDIT_FILTER_MASK; /* Build the command */ asprintf(&cmd, "key=%s", key); if (cmd) { /* Add this to the rule */ int ret = audit_rule_fieldpair_data(&rule_new, cmd, flags); if (ret < 0) retval = -1; free(cmd); } else { fprintf(stderr, "Out of memory adding key\n"); retval = -1; } } if (retval == -1 && errno == ECONNREFUSED) fprintf(stderr, "The audit system is disabled\n"); return retval; }
static int audit_receive_msg(struct sk_buff *skb, struct nlmsghdr *nlh) { u32 uid, pid, seq; void *data; struct audit_status *status_get, status_set; int err; struct audit_buffer *ab; u16 msg_type = nlh->nlmsg_type; uid_t loginuid; /* loginuid of sender */ struct audit_sig_info sig_data; err = audit_netlink_ok(NETLINK_CB(skb).eff_cap, msg_type); if (err) return err; /* As soon as there's any sign of userspace auditd, start kauditd to talk to it */ if (!kauditd_task) kauditd_task = kthread_run(kauditd_thread, NULL, "kauditd"); if (IS_ERR(kauditd_task)) { err = PTR_ERR(kauditd_task); kauditd_task = NULL; return err; } pid = NETLINK_CREDS(skb)->pid; uid = NETLINK_CREDS(skb)->uid; loginuid = NETLINK_CB(skb).loginuid; seq = nlh->nlmsg_seq; data = NLMSG_DATA(nlh); switch (msg_type) { case AUDIT_GET: status_set.enabled = audit_enabled; status_set.failure = audit_failure; status_set.pid = audit_pid; status_set.rate_limit = audit_rate_limit; status_set.backlog_limit = audit_backlog_limit; status_set.lost = atomic_read(&audit_lost); status_set.backlog = skb_queue_len(&audit_skb_queue); audit_send_reply(NETLINK_CB(skb).pid, seq, AUDIT_GET, 0, 0, &status_set, sizeof(status_set)); break; case AUDIT_SET: if (nlh->nlmsg_len < sizeof(struct audit_status)) return -EINVAL; status_get = (struct audit_status *)data; if (status_get->mask & AUDIT_STATUS_ENABLED) { err = audit_set_enabled(status_get->enabled, loginuid); if (err < 0) return err; } if (status_get->mask & AUDIT_STATUS_FAILURE) { err = audit_set_failure(status_get->failure, loginuid); if (err < 0) return err; } if (status_get->mask & AUDIT_STATUS_PID) { int old = audit_pid; audit_pid = status_get->pid; audit_log(NULL, GFP_KERNEL, AUDIT_CONFIG_CHANGE, "audit_pid=%d old=%d by auid=%u", audit_pid, old, loginuid); } if (status_get->mask & AUDIT_STATUS_RATE_LIMIT) audit_set_rate_limit(status_get->rate_limit, loginuid); if (status_get->mask & AUDIT_STATUS_BACKLOG_LIMIT) audit_set_backlog_limit(status_get->backlog_limit, loginuid); break; case AUDIT_USER: case AUDIT_FIRST_USER_MSG...AUDIT_LAST_USER_MSG: if (!audit_enabled && msg_type != AUDIT_USER_AVC) return 0; err = audit_filter_user(&NETLINK_CB(skb), msg_type); if (err == 1) { err = 0; ab = audit_log_start(NULL, GFP_KERNEL, msg_type); if (ab) { audit_log_format(ab, "user pid=%d uid=%u auid=%u msg='%.1024s'", pid, uid, loginuid, (char *)data); audit_set_pid(ab, pid); audit_log_end(ab); } } break; case AUDIT_ADD: case AUDIT_DEL: if (nlh->nlmsg_len < sizeof(struct audit_rule)) return -EINVAL; /* fallthrough */ case AUDIT_LIST: err = audit_receive_filter(nlh->nlmsg_type, NETLINK_CB(skb).pid, uid, seq, data, loginuid); break; case AUDIT_SIGNAL_INFO: sig_data.uid = audit_sig_uid; sig_data.pid = audit_sig_pid; audit_send_reply(NETLINK_CB(skb).pid, seq, AUDIT_SIGNAL_INFO, 0, 0, &sig_data, sizeof(sig_data)); break; default: err = -EINVAL; break; } return err < 0 ? err : 0; }
static int audit_receive_msg(struct sk_buff *skb, struct nlmsghdr *nlh) { u32 uid, pid, seq, sid; void *data; struct audit_status *status_get, status_set; int err; struct audit_buffer *ab; u16 msg_type = nlh->nlmsg_type; uid_t loginuid; /* loginuid of sender */ struct audit_sig_info *sig_data; char *ctx; u32 len; err = audit_netlink_ok(skb, msg_type); if (err) return err; /* As soon as there's any sign of userspace auditd, * start kauditd to talk to it */ if (!kauditd_task) kauditd_task = kthread_run(kauditd_thread, NULL, "kauditd"); if (IS_ERR(kauditd_task)) { err = PTR_ERR(kauditd_task); kauditd_task = NULL; return err; } pid = NETLINK_CREDS(skb)->pid; uid = NETLINK_CREDS(skb)->uid; loginuid = NETLINK_CB(skb).loginuid; sid = NETLINK_CB(skb).sid; seq = nlh->nlmsg_seq; data = NLMSG_DATA(nlh); switch (msg_type) { case AUDIT_GET: status_set.enabled = audit_enabled; status_set.failure = audit_failure; status_set.pid = audit_pid; status_set.rate_limit = audit_rate_limit; status_set.backlog_limit = audit_backlog_limit; status_set.lost = atomic_read(&audit_lost); status_set.backlog = skb_queue_len(&audit_skb_queue); audit_send_reply(NETLINK_CB(skb).pid, seq, AUDIT_GET, 0, 0, &status_set, sizeof(status_set)); break; case AUDIT_SET: if (nlh->nlmsg_len < sizeof(struct audit_status)) return -EINVAL; status_get = (struct audit_status *)data; if (status_get->mask & AUDIT_STATUS_ENABLED) { err = audit_set_enabled(status_get->enabled, loginuid, sid); if (err < 0) return err; } if (status_get->mask & AUDIT_STATUS_FAILURE) { err = audit_set_failure(status_get->failure, loginuid, sid); if (err < 0) return err; } if (status_get->mask & AUDIT_STATUS_PID) { int old = audit_pid; if (sid) { if ((err = selinux_sid_to_string( sid, &ctx, &len))) return err; else audit_log(NULL, GFP_KERNEL, AUDIT_CONFIG_CHANGE, "audit_pid=%d old=%d by auid=%u subj=%s", status_get->pid, old, loginuid, ctx); kfree(ctx); } else audit_log(NULL, GFP_KERNEL, AUDIT_CONFIG_CHANGE, "audit_pid=%d old=%d by auid=%u", status_get->pid, old, loginuid); audit_pid = status_get->pid; } if (status_get->mask & AUDIT_STATUS_RATE_LIMIT) err = audit_set_rate_limit(status_get->rate_limit, loginuid, sid); if (status_get->mask & AUDIT_STATUS_BACKLOG_LIMIT) err = audit_set_backlog_limit(status_get->backlog_limit, loginuid, sid); break; case AUDIT_USER: case AUDIT_FIRST_USER_MSG...AUDIT_LAST_USER_MSG: case AUDIT_FIRST_USER_MSG2...AUDIT_LAST_USER_MSG2: if (!audit_enabled && msg_type != AUDIT_USER_AVC) return 0; err = audit_filter_user(&NETLINK_CB(skb), msg_type); if (err == 1) { err = 0; ab = audit_log_start(NULL, GFP_KERNEL, msg_type); if (ab) { audit_log_format(ab, "user pid=%d uid=%u auid=%u", pid, uid, loginuid); if (sid) { if (selinux_sid_to_string( sid, &ctx, &len)) { audit_log_format(ab, " ssid=%u", sid); /* Maybe call audit_panic? */ } else audit_log_format(ab, " subj=%s", ctx); kfree(ctx); } audit_log_format(ab, " msg='%.1024s'", (char *)data); audit_set_pid(ab, pid); audit_log_end(ab); } } break; case AUDIT_ADD: case AUDIT_DEL: if (nlmsg_len(nlh) < sizeof(struct audit_rule)) return -EINVAL; if (audit_enabled == 2) { ab = audit_log_start(NULL, GFP_KERNEL, AUDIT_CONFIG_CHANGE); if (ab) { audit_log_format(ab, "pid=%d uid=%u auid=%u", pid, uid, loginuid); if (sid) { if (selinux_sid_to_string( sid, &ctx, &len)) { audit_log_format(ab, " ssid=%u", sid); /* Maybe call audit_panic? */ } else audit_log_format(ab, " subj=%s", ctx); kfree(ctx); } audit_log_format(ab, " audit_enabled=%d res=0", audit_enabled); audit_log_end(ab); } return -EPERM; } /* fallthrough */ case AUDIT_LIST: err = audit_receive_filter(nlh->nlmsg_type, NETLINK_CB(skb).pid, uid, seq, data, nlmsg_len(nlh), loginuid, sid); break; case AUDIT_ADD_RULE: case AUDIT_DEL_RULE: if (nlmsg_len(nlh) < sizeof(struct audit_rule_data)) return -EINVAL; if (audit_enabled == 2) { ab = audit_log_start(NULL, GFP_KERNEL, AUDIT_CONFIG_CHANGE); if (ab) { audit_log_format(ab, "pid=%d uid=%u auid=%u", pid, uid, loginuid); if (sid) { if (selinux_sid_to_string( sid, &ctx, &len)) { audit_log_format(ab, " ssid=%u", sid); /* Maybe call audit_panic? */ } else audit_log_format(ab, " subj=%s", ctx); kfree(ctx); } audit_log_format(ab, " audit_enabled=%d res=0", audit_enabled); audit_log_end(ab); } return -EPERM; } /* fallthrough */ case AUDIT_LIST_RULES: err = audit_receive_filter(nlh->nlmsg_type, NETLINK_CB(skb).pid, uid, seq, data, nlmsg_len(nlh), loginuid, sid); break; case AUDIT_SIGNAL_INFO: err = selinux_sid_to_string(audit_sig_sid, &ctx, &len); if (err) return err; sig_data = kmalloc(sizeof(*sig_data) + len, GFP_KERNEL); if (!sig_data) { kfree(ctx); return -ENOMEM; } sig_data->uid = audit_sig_uid; sig_data->pid = audit_sig_pid; memcpy(sig_data->ctx, ctx, len); kfree(ctx); audit_send_reply(NETLINK_CB(skb).pid, seq, AUDIT_SIGNAL_INFO, 0, 0, sig_data, sizeof(*sig_data) + len); kfree(sig_data); break; default: err = -EINVAL; break; } return err < 0 ? err : 0; }
static int audit_receive_msg(struct sk_buff *skb, struct nlmsghdr *nlh) { u32 uid, pid, seq; void *data; struct audit_status *status_get, status_set; int err; struct audit_buffer *ab; u16 msg_type = nlh->nlmsg_type; err = audit_netlink_ok(NETLINK_CB(skb).eff_cap, msg_type); if (err) return err; pid = NETLINK_CREDS(skb)->pid; uid = NETLINK_CREDS(skb)->uid; seq = nlh->nlmsg_seq; data = NLMSG_DATA(nlh); switch (msg_type) { case AUDIT_GET: status_set.enabled = audit_enabled; status_set.failure = audit_failure; status_set.pid = audit_pid; status_set.rate_limit = audit_rate_limit; status_set.backlog_limit = audit_backlog_limit; status_set.lost = atomic_read(&audit_lost); status_set.backlog = atomic_read(&audit_backlog); audit_send_reply(NETLINK_CB(skb).pid, seq, AUDIT_GET, 0, 0, &status_set, sizeof(status_set)); break; case AUDIT_SET: if (nlh->nlmsg_len < sizeof(struct audit_status)) return -EINVAL; status_get = (struct audit_status *)data; if (status_get->mask & AUDIT_STATUS_ENABLED) { err = audit_set_enabled(status_get->enabled); if (err < 0) return err; } if (status_get->mask & AUDIT_STATUS_FAILURE) { err = audit_set_failure(status_get->failure); if (err < 0) return err; } if (status_get->mask & AUDIT_STATUS_PID) { int old = audit_pid; audit_pid = status_get->pid; audit_log(current->audit_context, "audit_pid=%d old=%d", audit_pid, old); } if (status_get->mask & AUDIT_STATUS_RATE_LIMIT) audit_set_rate_limit(status_get->rate_limit); if (status_get->mask & AUDIT_STATUS_BACKLOG_LIMIT) audit_set_backlog_limit(status_get->backlog_limit); break; case AUDIT_USER: ab = audit_log_start(NULL); if (!ab) break; /* audit_panic has been called */ audit_log_format(ab, "user pid=%d uid=%d length=%d msg='%.1024s'", pid, uid, (int)(nlh->nlmsg_len - ((char *)data - (char *)nlh)), (char *)data); ab->type = AUDIT_USER; ab->pid = pid; audit_log_end(ab); break; case AUDIT_ADD: case AUDIT_DEL: if (nlh->nlmsg_len < sizeof(struct audit_rule)) return -EINVAL; /* fallthrough */ case AUDIT_LIST: #ifdef CONFIG_AUDITSYSCALL err = audit_receive_filter(nlh->nlmsg_type, NETLINK_CB(skb).pid, uid, seq, data); #else err = -EOPNOTSUPP; #endif break; default: err = -EINVAL; break; } return err < 0 ? err : 0; }
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; }
int main(int argc, char *argv[]) { struct sigaction sa; struct rlimit limit; int i, c, rc; int opt_foreground = 0, opt_allow_links = 0; enum startup_state opt_startup = startup_enable; extern char *optarg; extern int optind; struct ev_loop *loop; struct ev_io netlink_watcher; struct ev_signal sigterm_watcher; struct ev_signal sighup_watcher; struct ev_signal sigusr1_watcher; struct ev_signal sigusr2_watcher; struct ev_signal sigchld_watcher; /* Get params && set mode */ while ((c = getopt(argc, argv, "flns:")) != -1) { switch (c) { case 'f': opt_foreground = 1; break; case 'l': opt_allow_links=1; break; case 'n': do_fork = 0; break; case 's': for (i=0; i<startup_INVALID; i++) { if (strncmp(optarg, startup_states[i], strlen(optarg)) == 0) { opt_startup = i; break; } } if (i == startup_INVALID) { fprintf(stderr, "unknown startup mode '%s'\n", optarg); usage(); } break; default: usage(); } } /* check for trailing command line following options */ if (optind < argc) { usage(); } if (opt_allow_links) set_allow_links(1); if (opt_foreground) { config.daemonize = D_FOREGROUND; set_aumessage_mode(MSG_STDERR, DBG_YES); } else { config.daemonize = D_BACKGROUND; set_aumessage_mode(MSG_SYSLOG, DBG_NO); (void) umask( umask( 077 ) | 022 ); } #ifndef DEBUG /* Make sure we are root */ if (getuid() != 0) { fprintf(stderr, "You must be root to run this program.\n"); return 4; } #endif /* Register sighandlers */ sa.sa_flags = 0 ; sigemptyset( &sa.sa_mask ) ; /* Ignore all signals by default */ sa.sa_handler = SIG_IGN; for (i=1; i<NSIG; i++) sigaction( i, &sa, NULL ); atexit(clean_exit); /* Raise the rlimits in case we're being started from a shell * with restrictions. Not a fatal error. */ limit.rlim_cur = RLIM_INFINITY; limit.rlim_max = RLIM_INFINITY; setrlimit(RLIMIT_FSIZE, &limit); setrlimit(RLIMIT_CPU, &limit); /* Load the Configuration File */ if (load_config(&config, TEST_AUDITD)) return 6; if (config.priority_boost != 0) { errno = 0; rc = nice((int)-config.priority_boost); if (rc == -1 && errno) { audit_msg(LOG_ERR, "Cannot change priority (%s)", strerror(errno)); return 1; } } /* Daemonize or stay in foreground for debugging */ if (config.daemonize == D_BACKGROUND) { if (become_daemon() != 0) { audit_msg(LOG_ERR, "Cannot daemonize (%s)", strerror(errno)); tell_parent(FAILURE); return 1; } openlog("auditd", LOG_PID, LOG_DAEMON); } /* Init netlink */ if ((fd = audit_open()) < 0) { audit_msg(LOG_ERR, "Cannot open netlink audit socket"); tell_parent(FAILURE); return 1; } /* Init the event handler thread */ write_pid_file(); if (init_event(&config)) { if (pidfile) unlink(pidfile); tell_parent(FAILURE); return 1; } if (init_dispatcher(&config)) { if (pidfile) unlink(pidfile); tell_parent(FAILURE); return 1; } /* Get machine name ready for use */ if (resolve_node(&config)) { if (pidfile) unlink(pidfile); tell_parent(FAILURE); return 1; } /* Write message to log that we are alive */ { struct utsname ubuf; char start[DEFAULT_BUF_SZ]; const char *fmt = audit_lookup_format((int)config.log_format); if (fmt == NULL) fmt = "UNKNOWN"; if (uname(&ubuf) != 0) { if (pidfile) unlink(pidfile); tell_parent(FAILURE); return 1; } if (getsubj(subj)) snprintf(start, sizeof(start), "auditd start, ver=%s format=%s " "kernel=%.56s auid=%u pid=%d subj=%s res=success", VERSION, fmt, ubuf.release, audit_getloginuid(), getpid(), subj); else snprintf(start, sizeof(start), "auditd start, ver=%s format=%s " "kernel=%.56s auid=%u pid=%d res=success", VERSION, fmt, ubuf.release, audit_getloginuid(), getpid()); if (send_audit_event(AUDIT_DAEMON_START, start)) { audit_msg(LOG_ERR, "Cannot send start message"); if (pidfile) unlink(pidfile); shutdown_dispatcher(); tell_parent(FAILURE); return 1; } } /* Tell kernel not to kill us */ avoid_oom_killer(); /* let config manager init */ init_config_manager(); if (opt_startup != startup_nochange && (audit_is_enabled(fd) < 2) && audit_set_enabled(fd, (int)opt_startup) < 0) { char emsg[DEFAULT_BUF_SZ]; if (*subj) snprintf(emsg, sizeof(emsg), "auditd error halt, auid=%u pid=%d subj=%s res=failed", audit_getloginuid(), getpid(), subj); else snprintf(emsg, sizeof(emsg), "auditd error halt, auid=%u pid=%d res=failed", audit_getloginuid(), getpid()); stop = 1; send_audit_event(AUDIT_DAEMON_ABORT, emsg); audit_msg(LOG_ERR, "Unable to set initial audit startup state to '%s', exiting", startup_states[opt_startup]); close_down(); if (pidfile) unlink(pidfile); shutdown_dispatcher(); tell_parent(FAILURE); return 1; } /* Tell the kernel we are alive */ if (audit_set_pid(fd, getpid(), WAIT_YES) < 0) { char emsg[DEFAULT_BUF_SZ]; if (*subj) snprintf(emsg, sizeof(emsg), "auditd error halt, auid=%u pid=%d subj=%s res=failed", audit_getloginuid(), getpid(), subj); else snprintf(emsg, sizeof(emsg), "auditd error halt, auid=%u pid=%d res=failed", audit_getloginuid(), getpid()); stop = 1; send_audit_event(AUDIT_DAEMON_ABORT, emsg); audit_msg(LOG_ERR, "Unable to set audit pid, exiting"); close_down(); if (pidfile) unlink(pidfile); shutdown_dispatcher(); tell_parent(FAILURE); return 1; } /* Depending on value of opt_startup (-s) set initial audit state */ loop = ev_default_loop (EVFLAG_NOENV); ev_io_init (&netlink_watcher, netlink_handler, fd, EV_READ); ev_io_start (loop, &netlink_watcher); ev_signal_init (&sigterm_watcher, term_handler, SIGTERM); ev_signal_start (loop, &sigterm_watcher); ev_signal_init (&sighup_watcher, hup_handler, SIGHUP); ev_signal_start (loop, &sighup_watcher); ev_signal_init (&sigusr1_watcher, user1_handler, SIGUSR1); ev_signal_start (loop, &sigusr1_watcher); ev_signal_init (&sigusr2_watcher, user2_handler, SIGUSR2); ev_signal_start (loop, &sigusr2_watcher); ev_signal_init (&sigchld_watcher, child_handler, SIGCHLD); ev_signal_start (loop, &sigchld_watcher); if (auditd_tcp_listen_init (loop, &config)) { char emsg[DEFAULT_BUF_SZ]; if (*subj) snprintf(emsg, sizeof(emsg), "auditd error halt, auid=%u pid=%d subj=%s res=failed", audit_getloginuid(), getpid(), subj); else snprintf(emsg, sizeof(emsg), "auditd error halt, auid=%u pid=%d res=failed", audit_getloginuid(), getpid()); stop = 1; send_audit_event(AUDIT_DAEMON_ABORT, emsg); tell_parent(FAILURE); } else { /* Now tell parent that everything went OK */ tell_parent(SUCCESS); audit_msg(LOG_NOTICE, "Init complete, auditd %s listening for events (startup state %s)", VERSION, startup_states[opt_startup]); } /* Parent should be gone by now... */ if (do_fork) close(init_pipe[1]); // Init complete, start event loop if (!stop) ev_loop (loop, 0); auditd_tcp_listen_uninit (loop, &config); // Tear down IO watchers Part 1 ev_signal_stop (loop, &sighup_watcher); ev_signal_stop (loop, &sigusr1_watcher); ev_signal_stop (loop, &sigusr2_watcher); ev_signal_stop (loop, &sigterm_watcher); /* Write message to log that we are going down */ rc = audit_request_signal_info(fd); if (rc > 0) { struct audit_reply trep; rc = get_reply(fd, &trep, rc); if (rc > 0) { char txt[MAX_AUDIT_MESSAGE_LENGTH]; snprintf(txt, sizeof(txt), "auditd normal halt, sending auid=%u " "pid=%d subj=%s res=success", trep.signal_info->uid, trep.signal_info->pid, trep.signal_info->ctx); send_audit_event(AUDIT_DAEMON_END, txt); } } if (rc <= 0) send_audit_event(AUDIT_DAEMON_END, "auditd normal halt, sending auid=? " "pid=? subj=? res=success"); free(rep); // Tear down IO watchers Part 2 ev_io_stop (loop, &netlink_watcher); // Give DAEMON_END event a little time to be sent in case // of remote logging usleep(10000); // 10 milliseconds shutdown_dispatcher(); // Tear down IO watchers Part 3 ev_signal_stop (loop, &sigchld_watcher); close_down(); free_config(&config); ev_default_destroy(); return 0; }
static void do_auditd(int fd) { struct audit_reply rep; sigset_t sigs; struct sigaction sa; struct pollfd pds = { .events = POLLIN | POLLPRI | POLLERR | POLLHUP | POLLMSG, .revents = 0, .fd = fd, }; if (audit_set_pid(fd, getpid(), WAIT_YES) < 0) return; if (audit_set_enabled(fd, 1) < 0) return; memset(&sa, '\0', sizeof (sa)); sa.sa_handler = sig_done; sigaction(SIGTERM, &sa, NULL); sigaction(SIGINT, &sa, NULL); sigaction(SIGHUP, &sa, NULL); sigfillset(&sigs); sigdelset(&sigs, SIGTERM); sigdelset(&sigs, SIGINT); sigdelset(&sigs, SIGHUP); while (1) { struct timespec timeout = { -1, -1 }; int retval; memset(&rep, 0, sizeof(rep)); do { retval = ppoll(&pds, 1, &timeout, &sigs); } while (retval == -1 && errno == EINTR && !done); if (done) break; if (audit_get_reply(fd, &rep, GET_REPLY_NONBLOCKING, 0) > 0) { /* we don't actually want to do anything here. */ ; } } return; } #endif /* USESELINUX */ int audit_daemonize(void) { #ifdef USESELINUX int fd; #ifndef STANDALONE int i; pid_t child; if ((child = fork()) > 0) return 0; for (i = 0; i < getdtablesize(); i++) close(i); signal(SIGTTOU, SIG_IGN); signal(SIGTTIN, SIG_IGN); signal(SIGTSTP, SIG_IGN); if ((fd = open("/proc/self/oom_adj", O_RDWR)) >= 0) { i = write(fd, "-17", 3); close(fd); } #endif /* !defined(STANDALONE) */ fd = audit_open(); do_auditd(fd); audit_close(fd); #endif /* USESELINUX */ exit(0); }