Message ID | 1262437456-24476-8-git-send-email-sam@synack.fr |
---|---|
State | RFC, archived |
Delegated to: | David Miller |
Headers | show |
Samir Bellabes wrote: > +++ b/security/snet/include/snet_netlink.h > @@ -0,0 +1,201 @@ > +#ifndef _SNET_NETLINK_H > +#define _SNET_NETLINK_H > + > +#include <linux/in6.h> > +#include "snet_hooks.h" > + > +extern unsigned int snet_verdict_delay; As this file defines the userspace interface, it probably shouldn't contain declarations of kernel-internal variables (same for snet_hooks.h). It would also be better placed in include/linux as the other netlink API definitions. > + > +/* commands */ > +enum { > + SNET_C_UNSPEC, > + SNET_C_VERSION, > + SNET_C_REGISTER, > + SNET_C_UNREGISTER, > + SNET_C_INSERT, > + SNET_C_REMOVE, > + SNET_C_FLUSH, > + SNET_C_LIST, > + SNET_C_VERDICT, > + SNET_C_VERDICT_DELAY, > + __SNET_C_MAX, > +}; > + > +#define SNET_C_MAX (__SNET_C_MAX - 1) > + > +/* attributes */ > +enum { > + SNET_A_UNSPEC, > + SNET_A_VERSION, /* (NLA_U32) the snet protocol version */ You're using this to check for a "compliant protocol version" below. This shouldn't be needed as any protocol changes need to be done in a compatible fashion. > + SNET_A_SYSCALL, /* (NLA_U8) a syscall identifier */ We're already using 299 syscalls on x86, so perhaps a larger type would be better suited. > + SNET_A_PROTOCOL, /* (NLA_U8) a protocol identifier */ > + SNET_A_INSERTED, > + SNET_A_REMOVED, > + SNET_A_FLUSHED, > + SNET_A_REGISTERED, > + SNET_A_UNREGISTERED, > + SNET_A_VERDICT_ID, > + SNET_A_FAMILY, > + SNET_A_UID, > + SNET_A_PID, > + SNET_A_VERDICT, > + SNET_A_DATA_EXT, > + SNET_A_VERDICT_DELAY, > + SNET_A_VERDICT_DELAYED, > + __SNET_A_MAX, > +}; > + > +#define SNET_A_MAX (__SNET_A_MAX - 1) > + > +#define SNET_GENL_NAME "SNET" > +#define SNET_GENL_VERSION SNET_VERSION > + > +int snet_nl_send_event(const u32 verdict_id, const enum snet_syscall syscall, > + const u8 protocol, const u8 family, void *data, > + const unsigned int len); > + > +int snet_nl_list_fill_info(struct sk_buff *skb, u32 pid, u32 seq, > + u32 flags, u8 protocol, enum snet_syscall syscall); > + > +void snet_netlink_exit(void); > + > +struct snet_sock_half { > + struct { > + union { > + __be32 ip; > + struct in6_addr ip6; > + }; > + } u3; > + struct { > + __be16 port; > + } u; > +}; > + > +struct snet_sock_info { > + struct snet_sock_half src; > + struct snet_sock_half dst; > + int type; > +}; How about using a struct sockaddr or encoding the values within netlink attributes? That would provide a bit more flexibility in case you want to support more protocols in the future. > + > +#endif /* _SNET_NETLINK_H */ > diff --git a/security/snet/snet_netlink.c b/security/snet/snet_netlink.c > new file mode 100644 > index 0000000..cc21d6c > --- /dev/null > +++ b/security/snet/snet_netlink.c > @@ -0,0 +1,541 @@ > +#include <linux/sched.h> > +#include <net/genetlink.h> > +#include <linux/in6.h> > + > +#include "snet.h" > +#include "snet_netlink.h" > +#include "snet_verdict.h" > +#include "snet_event.h" > +#include <snet_utils.h> > + > +atomic_t snet_nl_seq = ATOMIC_INIT(0); > +static uint32_t snet_nl_pid; > +static struct genl_family snet_genl_family; > +atomic_t snet_num_listeners = ATOMIC_INIT(0); The num_listeners seem to be redundant as you only support a single listener anyways, whose presence is indicated by snet_nl_pid != 0. > + > +/* > + * snet genetlink > + */ > +int snet_nl_send_event(const u32 verdict_id, const enum snet_syscall syscall, > + const u8 protocol, const u8 family, void *data, > + const unsigned int len) > +{ > + struct sk_buff *skb_rsp; > + void *msg_head; > + int ret = -ENOMEM; > + > + skb_rsp = genlmsg_new(NLMSG_GOODSIZE, GFP_KERNEL); > + if (skb_rsp == NULL) > + return 0; You could decrease the chance of rcvqueue overflow by using a smaller allocation size. > + > + msg_head = genlmsg_put(skb_rsp, snet_nl_pid, > + atomic_inc_return(&snet_nl_seq), > + &snet_genl_family, 0, SNET_C_VERDICT); > + if (msg_head == NULL) > + goto send_event_failure; > + > + snet_dbg("verdict_id=0x%x syscall=%s protocol=%u " > + "family=%u uid=%u pid=%u\n", > + verdict_id, snet_syscall_name(syscall), > + protocol, family, current_uid(), current->pid); > + > + if (verdict_id) { > + ret = nla_put_u32(skb_rsp, SNET_A_VERDICT_ID, verdict_id); > + if (ret != 0) > + goto send_event_failure; > + } > + ret = nla_put_u8(skb_rsp, SNET_A_SYSCALL, syscall); > + if (ret != 0) > + goto send_event_failure; > + ret = nla_put_u8(skb_rsp, SNET_A_PROTOCOL, protocol); > + if (ret != 0) > + goto send_event_failure; > + ret = nla_put_u8(skb_rsp, SNET_A_FAMILY, family); > + if (ret != 0) > + goto send_event_failure; > + ret = nla_put_u32(skb_rsp, SNET_A_UID, current_uid()); > + if (ret != 0) > + goto send_event_failure; > + ret = nla_put_u32(skb_rsp, SNET_A_PID, current->pid); > + if (ret != 0) > + goto send_event_failure; > + ret = nla_put(skb_rsp, SNET_A_DATA_EXT, len, data); > + if (ret != 0) > + goto send_event_failure; I guess its a matter of taste, but the NLA_PUT macros already take care of exception handling and keep the code smaller. > + > + ret = genlmsg_end(skb_rsp, msg_head); > + if (ret < 0) > + goto send_event_failure; > + > + genlmsg_unicast(&init_net, skb_rsp, snet_nl_pid); > + return 0; > + > +send_event_failure: > + kfree_skb(skb_rsp); > + return 0; Shouldn't this error be returned to the caller to avoid waiting for the timeout to occur (same question for the return value of genlmsg_unicast, which can also fail). > +} > + > +/* > + * snet genetlink helper functions > + */ > +static int snet_nl_response_flag(struct genl_info *info, > + struct genl_family *family, > + u8 cmd, int attrtype, u8 set_resp_flag) > +{ > + int ret = -EINVAL; > + struct sk_buff *skb_rsp = NULL; > + void *msg_head; > + > + skb_rsp = genlmsg_new(NLMSG_GOODSIZE, GFP_KERNEL); > + if (skb_rsp == NULL) > + return -ENOMEM; > + msg_head = genlmsg_put_reply(skb_rsp, info, family, 0, cmd); > + if (msg_head == NULL) > + goto response_failure; > + > + /* we put flag only if it is asked */ > + if (set_resp_flag) { > + ret = nla_put_flag(skb_rsp, attrtype); > + if (ret != 0) > + goto response_failure; > + } > + > + genlmsg_end(skb_rsp, msg_head); > + ret = genlmsg_reply(skb_rsp, info); > + if (ret != 0) > + goto response_failure; > + return 0; > + > +response_failure: > + kfree_skb(skb_rsp); > + return ret; > +} > + > +/* > + * snet genetlink functions > + */ > + > +static struct genl_family snet_genl_family = { > + .id = GENL_ID_GENERATE, > + .hdrsize = 0, > + .name = SNET_GENL_NAME, > + .version = SNET_GENL_VERSION, > + .maxattr = SNET_A_MAX, > +}; > + > +static const struct nla_policy snet_genl_policy[SNET_A_MAX + 1] > +__read_mostly = { You don't need __read_mostly for const. If I recall correctly, it even causes an error with certain compiler or linker versions. > + [SNET_A_VERSION] = { .type = NLA_U32 }, > + [SNET_A_SYSCALL] = { .type = NLA_U8 }, > + [SNET_A_PROTOCOL] = { .type = NLA_U8 }, > + [SNET_A_INSERTED] = { .type = NLA_FLAG }, > + [SNET_A_REMOVED] = { .type = NLA_FLAG }, > + [SNET_A_FLUSHED] = { .type = NLA_FLAG }, > + [SNET_A_REGISTERED] = { .type = NLA_FLAG }, > + [SNET_A_UNREGISTERED] = { .type = NLA_FLAG }, > + [SNET_A_VERDICT_ID] = { .type = NLA_U32 }, > + [SNET_A_FAMILY] = { .type = NLA_U8 }, > + [SNET_A_UID] = { .type = NLA_U32 }, > + [SNET_A_PID] = { .type = NLA_U32 }, > + [SNET_A_VERDICT] = { .type = NLA_U8 }, > + [SNET_A_DATA_EXT] = { .type = NLA_BINARY, > + .len = sizeof(struct snet_sock_info) }, > + [SNET_A_VERDICT_DELAY] = { .type = NLA_U32 }, > + [SNET_A_VERDICT_DELAYED] = { .type = NLA_FLAG }, > +}; > + > +/** > + * snet_nl_version - Handle a VERSION message > + * @skb: the NETLINK buffer > + * @info: the Generic NETLINK info block > + * > + * Description: > + * Process a user generated VERSION message and respond accordingly. > + * Returns zero on success, negative values on failure. > + */ > +static int snet_nl_version(struct sk_buff *skb, struct genl_info *info) > +{ > + int ret = -ENOMEM; > + struct sk_buff *skb_rsp = NULL; > + void *msg_head; > + > + atomic_set(&snet_nl_seq, info->snd_seq); > + > + if (atomic_read(&snet_num_listeners) <= 0) > + return 0; In all these checks for listeners, I think it would make sense to provide an error to userspace if it hasn't registered properly, perhaps ENOTCONN or something like that. > + > + skb_rsp = genlmsg_new(NLMSG_GOODSIZE, GFP_KERNEL); > + if (skb_rsp == NULL) > + return -ENOMEM; > + msg_head = genlmsg_put_reply(skb_rsp, info, &snet_genl_family, > + 0, SNET_C_VERSION); > + if (msg_head == NULL) > + goto version_failure; > + > + ret = nla_put_u32(skb_rsp, SNET_A_VERSION, SNET_VERSION); > + if (ret != 0) > + goto version_failure; > + > + genlmsg_end(skb_rsp, msg_head); > + > + ret = genlmsg_reply(skb_rsp, info); > + if (ret != 0) > + goto version_failure; > + return 0; > + > +version_failure: > + kfree_skb(skb_rsp); > + return ret; > +} > + > +/** > + * snet_nl_register - Handle a REGISTER message > + * @skb: the NETLINK buffer > + * @info: the Generic NETLINK info block > + * > + * Description: > + * Notify the kernel that an application is listening for events. > + * Returns zero on success, negative values on failure. > + */ > +static int snet_nl_register(struct sk_buff *skb, struct genl_info *info) > +{ > + int ret = -EINVAL; > + u32 version = 0; > + u8 set_resp_flag = 0; > + > + atomic_set(&snet_nl_seq, info->snd_seq); > + > + if (!info->attrs[SNET_A_VERSION]) > + return -EINVAL; > + version = nla_get_u32(info->attrs[SNET_A_VERSION]); > + > + if (version == SNET_VERSION) { /* version is compliant */ > + atomic_inc(&snet_num_listeners); > + set_resp_flag = 1; > + } > + > + ret = snet_nl_response_flag(info, &snet_genl_family, > + SNET_C_REGISTER, SNET_A_REGISTERED, > + set_resp_flag); Is this really needed? A return value of 0 should already tell userspace that the command was successful. If it really wants a seperate success message, it can use NLM_F_ACK. This will also automatically take care of using the proper sequence number, so the snet_nl_seq handling isn't required anymore. Same for all similar cases below. > + > + snet_nl_pid = info->snd_pid; > + snet_dbg("pid=%u num_listeners=%d\n", > + snet_nl_pid, atomic_read(&snet_num_listeners)); > + return ret; > +} > + > +/** > + * snet_nl_unregister - Handle a UNREGISTER message > + * @skb: the NETLINK buffer > + * @info: the Generic NETLINK info block > + * > + * Description: > + * Notify the kernel that the application is no more listening for events. > + * Returns zero on success, negative values on failure. > + */ > +static int snet_nl_unregister(struct sk_buff *skb, struct genl_info *info) > +{ > + int ret = -EINVAL; > + > + atomic_set(&snet_nl_seq, info->snd_seq); > + > + if (atomic_read(&snet_num_listeners)) > + atomic_dec(&snet_num_listeners); > + ret = snet_nl_response_flag(info, &snet_genl_family, > + SNET_C_UNREGISTER, SNET_A_UNREGISTERED, 1); > + snet_dbg("pid=%u num_listeners=%d\n", > + snet_nl_pid, atomic_read(&snet_num_listeners)); > + return ret; > +} > + > +/** > + * snet_nl_insert - Handle a INSERT message > + * @skb: the NETLINK buffer > + * @info: the Generic NETLINK info block > + * > + * Description: > + * Insert a new event to the events' hashtable. Returns zero on success, > + * negative values on failure. > + */ > +static int snet_nl_insert(struct sk_buff *skb, struct genl_info *info) > +{ > + int ret_event = -EINVAL, ret = -EINVAL; > + enum snet_syscall syscall; > + u8 protocol; > + u8 set_resp_flag = 0; > + > + atomic_set(&snet_nl_seq, info->snd_seq); > + > + if (atomic_read(&snet_num_listeners) <= 0) > + return 0; > + > + if (!info->attrs[SNET_A_SYSCALL] || !info->attrs[SNET_A_PROTOCOL]) > + return -EINVAL; > + > + syscall = nla_get_u8(info->attrs[SNET_A_SYSCALL]); > + protocol = nla_get_u8(info->attrs[SNET_A_PROTOCOL]); > + ret_event = snet_event_insert(syscall, protocol); > + snet_dbg("syscall=%s protocol=%u insert=%s\n", > + snet_syscall_name(syscall), protocol, > + (ret_event == 0) ? "success" : "failed"); > + > + if (ret_event == 0) > + set_resp_flag = 1; > + > + ret = snet_nl_response_flag(info, &snet_genl_family, > + SNET_C_INSERT, SNET_A_INSERTED, > + set_resp_flag); > + return ret; > +} > + > +/** > + * snet_nl_remove - Handle a REMOVE message > + * @skb: the NETLINK buffer > + * @info: the Generic NETLINK info block > + * > + * Description: > + * Remove a event from the events' hastable. Returns zero on success, > + * negative values on failure. > + */ > +static int snet_nl_remove(struct sk_buff *skb, struct genl_info *info) > +{ > + int ret_event = -EINVAL, ret = -EINVAL; > + enum snet_syscall syscall; > + u8 protocol; > + u8 set_resp_flag = 0; > + > + atomic_set(&snet_nl_seq, info->snd_seq); > + > + if (atomic_read(&snet_num_listeners) <= 0) > + return 0; > + > + if (!info->attrs[SNET_A_SYSCALL] || !info->attrs[SNET_A_PROTOCOL]) > + return -EINVAL; > + > + syscall = nla_get_u8(info->attrs[SNET_A_SYSCALL]); > + protocol = nla_get_u8(info->attrs[SNET_A_PROTOCOL]); > + ret_event = snet_event_remove(syscall, protocol); > + snet_dbg("syscall=%s protocol=%u remove=%s\n", > + snet_syscall_name(syscall), protocol, > + (ret_event == 0) ? "success" : "failed"); > + > + if (ret_event == 0) > + set_resp_flag = 1; > + > + ret = snet_nl_response_flag(info, &snet_genl_family, > + SNET_C_REMOVE, SNET_A_REMOVED, > + set_resp_flag); > + return ret; > +} > + > +/** > + * snet_nl_flush - Handle a FLUSH message > + * @skb: the NETLINK buffer > + * @info: the Generic NETLINK info block > + * > + * Description: > + * Remove all events from the hashtable. Returns zero on success, > + * negative values on failure. > + */ > +static int snet_nl_flush(struct sk_buff *skb, struct genl_info *info) > +{ > + int ret = -EINVAL; > + u8 set_resp_flag = 0; > + > + atomic_set(&snet_nl_seq, info->snd_seq); > + > + if (atomic_read(&snet_num_listeners) <= 0) > + return 0; > + > + snet_event_flush(); > + > + set_resp_flag = 1; > + > + ret = snet_nl_response_flag(info, &snet_genl_family, > + SNET_C_FLUSH, SNET_A_FLUSHED, > + set_resp_flag); > + return ret; > +} > + > +int snet_nl_list_fill_info(struct sk_buff *skb, u32 pid, u32 seq, > + u32 flags, u8 protocol, enum snet_syscall syscall) > +{ > + void *hdr; > + int ret = -1; > + > + hdr = genlmsg_put(skb, pid, seq, &snet_genl_family, flags, SNET_C_LIST); > + if (hdr == NULL) > + return -1; > + > + ret = nla_put_u8(skb, SNET_A_SYSCALL, syscall); > + if (ret != 0) > + goto list_failure; > + > + ret = nla_put_u8(skb, SNET_A_PROTOCOL, protocol); > + if (ret != 0) > + goto list_failure; > + > + return genlmsg_end(skb, hdr); > + > +list_failure: > + genlmsg_cancel(skb, hdr); > + return 0; > +} > +/** > + * snet_nl_list - Handle a LIST message > + * @skb: the NETLINK buffer > + * @cb: > + * > + * Description: > + * Process a user LIST message and respond. Returns zero on success, > + * and negative values on error. > + */ > +static int snet_nl_list(struct sk_buff *skb, struct netlink_callback *cb) > +{ > + unsigned int len = 0; > + > + atomic_set(&snet_nl_seq, cb->nlh->nlmsg_seq); > + len = snet_event_fill_info(skb, cb); > + return len; > +} > + > +/** > + * snet_nl_verdict - Handle a VERDICT message > + * @skb: the NETLINK buffer > + * @info the Generic NETLINK info block > + * > + * Description: > + * Provides userspace with a VERDICT message, ie we are sending informations > + * with this command. Userspace is sending the appropriate verdict for the > + * event. Returns zero on success,and negative values on error. > + */ > +static int snet_nl_verdict(struct sk_buff *skb, > + struct genl_info *info) > +{ > + u32 verdict_id; > + enum snet_verdict verdict; > + > + atomic_set(&snet_nl_seq, info->snd_seq); > + > + if (atomic_read(&snet_num_listeners) <= 0) > + return 0; > + > + if (!info->attrs[SNET_A_VERDICT_ID] || !info->attrs[SNET_A_VERDICT]) > + return -EINVAL; > + > + verdict_id = nla_get_u32(info->attrs[SNET_A_VERDICT_ID]); > + verdict = nla_get_u8(info->attrs[SNET_A_VERDICT]); > + snet_verdict_set(verdict_id, verdict); > + return 0; > +} > + > +/** > + * snet_nl_verdict_delay - Handle a VERDICT_DELAY message > + * @skb: the NETLINK buffer > + * @info the Generic NETLINK info block > + * > + * Description: > + * Provides userspace with a VERDICT_DELAY message, ie userspace application > + * is able to set the value of the timeout for verdicts > + * Returns zero on success, and negative values on error. > + */ > +static int snet_nl_verdict_delay(struct sk_buff *skb, > + struct genl_info *info) > +{ > + int ret = -EINVAL; > + > + atomic_set(&snet_nl_seq, info->snd_seq); > + > + if (atomic_read(&snet_num_listeners) <= 0) > + return 0; > + > + if (!info->attrs[SNET_A_VERDICT_DELAY]) > + return -EINVAL; > + > + snet_verdict_delay = nla_get_u32(info->attrs[SNET_A_VERDICT_DELAY]); > + /* FIXME: do something */ > + ret = snet_nl_response_flag(info, &snet_genl_family, > + SNET_C_VERDICT_DELAY, SNET_A_VERDICT_DELAYED, > + 1); > + return ret; > +} > + > +static struct genl_ops snet_genl_ops[] = { > + { > + .cmd = SNET_C_VERSION, > + .flags = GENL_ADMIN_PERM, > + .policy = snet_genl_policy, > + .doit = snet_nl_version, > + .dumpit = NULL, The NULL initializations aren't neccessary. > + }, > + { > + .cmd = SNET_C_REGISTER, > + .flags = GENL_ADMIN_PERM, > + .policy = snet_genl_policy, > + .doit = snet_nl_register, > + .dumpit = NULL, > + }, > + { > + .cmd = SNET_C_UNREGISTER, > + .flags = GENL_ADMIN_PERM, > + .policy = snet_genl_policy, > + .doit = snet_nl_unregister, > + .dumpit = NULL, > + }, > + { > + .cmd = SNET_C_INSERT, > + .flags = GENL_ADMIN_PERM, > + .policy = snet_genl_policy, > + .doit = snet_nl_insert, > + .dumpit = NULL, > + }, > + { > + .cmd = SNET_C_REMOVE, > + .flags = GENL_ADMIN_PERM, > + .policy = snet_genl_policy, > + .doit = snet_nl_remove, > + .dumpit = NULL, > + }, > + { > + .cmd = SNET_C_FLUSH, > + .flags = GENL_ADMIN_PERM, > + .policy = snet_genl_policy, > + .doit = snet_nl_flush, > + .dumpit = NULL, > + }, > + { > + .cmd = SNET_C_LIST, > + .flags = GENL_ADMIN_PERM, > + .policy = snet_genl_policy, > + .doit = NULL, > + .dumpit = snet_nl_list, > + }, > + { > + .cmd = SNET_C_VERDICT, > + .flags = GENL_ADMIN_PERM, > + .policy = snet_genl_policy, > + .doit = snet_nl_verdict, > + .dumpit = NULL, > + }, > + { > + .cmd = SNET_C_VERDICT_DELAY, > + .flags = GENL_ADMIN_PERM, > + .policy = snet_genl_policy, > + .doit = snet_nl_verdict_delay, > + .dumpit = NULL, > + }, > +}; > + > +static __init int snet_netlink_init(void) > +{ > + return genl_register_family_with_ops(&snet_genl_family, > + snet_genl_ops, > + ARRAY_SIZE(snet_genl_ops)); > +} > + > +void snet_netlink_exit(void) > +{ > + genl_unregister_family(&snet_genl_family); > +} > + > +__initcall(snet_netlink_init); -- To unsubscribe from this list: send the line "unsubscribe netdev" in the body of a message to majordomo@vger.kernel.org More majordomo info at http://vger.kernel.org/majordomo-info.html
Patrick McHardy <kaber@trash.net> writes: > Samir Bellabes wrote: >> +++ b/security/snet/include/snet_netlink.h >> + SNET_A_SYSCALL, /* (NLA_U8) a syscall identifier */ > > We're already using 299 syscalls on x86, so perhaps a larger type > would be better suited. I moved all references to u16 thanks -- To unsubscribe from this list: send the line "unsubscribe netdev" in the body of a message to majordomo@vger.kernel.org More majordomo info at http://vger.kernel.org/majordomo-info.html
Patrick McHardy <kaber@trash.net> writes: > Samir Bellabes wrote: >> +++ b/security/snet/include/snet_netlink.h >> + SNET_A_VERSION, /* (NLA_U32) the snet protocol version */ > > You're using this to check for a "compliant protocol version" below. > This shouldn't be needed as any protocol changes need to be done > in a compatible fashion. what if userspace lib is using a old protocol version ? kernel and userspace will use incompatible protocol, which may result in errors. The idea of this 'version' mecanism is to prevent such incompatibilities, even if the userspace is (un)volontary not using the good library (which may be the one tagged in the same time as the kernel running) sam -- To unsubscribe from this list: send the line "unsubscribe netdev" in the body of a message to majordomo@vger.kernel.org More majordomo info at http://vger.kernel.org/majordomo-info.html
Patrick McHardy <kaber@trash.net> writes: > Samir Bellabes wrote: >> +++ b/security/snet/include/snet_netlink.h >> + >> +struct snet_sock_half { >> + struct { >> + union { >> + __be32 ip; >> + struct in6_addr ip6; >> + }; >> + } u3; >> + struct { >> + __be16 port; >> + } u; >> +}; >> + >> +struct snet_sock_info { >> + struct snet_sock_half src; >> + struct snet_sock_half dst; >> + int type; >> +}; > > How about using a struct sockaddr or encoding the values within > netlink attributes? That would provide a bit more flexibility in > case you want to support more protocols in the future. indeed, I already move to the encoding of values independantly within netlink attributes. This had to be done before, and it was in the TODO, so now it's done. At first, I tried to use a attribute NLA_BINARY with all the datas inside snet_sock_info, so it won't break the netlink protocol between userspace and kernel, at each modification (adding/removing element inside the structure) -- To unsubscribe from this list: send the line "unsubscribe netdev" in the body of a message to majordomo@vger.kernel.org More majordomo info at http://vger.kernel.org/majordomo-info.html
Patrick McHardy <kaber@trash.net> writes: >> +atomic_t snet_nl_seq = ATOMIC_INIT(0); >> +static uint32_t snet_nl_pid; >> +static struct genl_family snet_genl_family; >> +atomic_t snet_num_listeners = ATOMIC_INIT(0); > > The num_listeners seem to be redundant as you only support a > single listener anyways, whose presence is indicated by > snet_nl_pid != 0. Actually, I may want to support multiple listeners, or simultaneous communication with userspace, as we may want to listen for event and reply for verdict in the same time as adding or deleting events. I will fix this mecanism. thanks Patrick, sam -- To unsubscribe from this list: send the line "unsubscribe netdev" in the body of a message to majordomo@vger.kernel.org More majordomo info at http://vger.kernel.org/majordomo-info.html
Samir Bellabes wrote: > Patrick McHardy <kaber@trash.net> writes: > >> Samir Bellabes wrote: >>> +++ b/security/snet/include/snet_netlink.h >>> + SNET_A_VERSION, /* (NLA_U32) the snet protocol version */ >> You're using this to check for a "compliant protocol version" below. >> This shouldn't be needed as any protocol changes need to be done >> in a compatible fashion. > > what if userspace lib is using a old protocol version ? kernel and > userspace will use incompatible protocol, which may result in errors. Any protocol changes need to be done in a compatible fashion once this is in the upstream kernel, so that case should never happen. > The idea of this 'version' mecanism is to prevent such > incompatibilities, even if the userspace is (un)volontary not using the > good library (which may be the one tagged in the same time as the kernel > running) -- To unsubscribe from this list: send the line "unsubscribe netdev" in the body of a message to majordomo@vger.kernel.org More majordomo info at http://vger.kernel.org/majordomo-info.html
Patrick McHardy <kaber@trash.net> writes: > Samir Bellabes wrote: >> + >> + msg_head = genlmsg_put(skb_rsp, snet_nl_pid, >> + atomic_inc_return(&snet_nl_seq), >> + &snet_genl_family, 0, SNET_C_VERDICT); >> + if (msg_head == NULL) >> + goto send_event_failure; >> + >> + snet_dbg("verdict_id=0x%x syscall=%s protocol=%u " >> + "family=%u uid=%u pid=%u\n", >> + verdict_id, snet_syscall_name(syscall), >> + protocol, family, current_uid(), current->pid); >> + >> + if (verdict_id) { >> + ret = nla_put_u32(skb_rsp, SNET_A_VERDICT_ID, verdict_id); >> + if (ret != 0) >> + goto send_event_failure; >> + } >> + ret = nla_put_u8(skb_rsp, SNET_A_SYSCALL, syscall); >> + if (ret != 0) >> + goto send_event_failure; >> + ret = nla_put_u8(skb_rsp, SNET_A_PROTOCOL, protocol); >> + if (ret != 0) >> + goto send_event_failure; >> + ret = nla_put_u8(skb_rsp, SNET_A_FAMILY, family); >> + if (ret != 0) >> + goto send_event_failure; >> + ret = nla_put_u32(skb_rsp, SNET_A_UID, current_uid()); >> + if (ret != 0) >> + goto send_event_failure; >> + ret = nla_put_u32(skb_rsp, SNET_A_PID, current->pid); >> + if (ret != 0) >> + goto send_event_failure; >> + ret = nla_put(skb_rsp, SNET_A_DATA_EXT, len, data); >> + if (ret != 0) >> + goto send_event_failure; > > I guess its a matter of taste, but the NLA_PUT macros already take > care of exception handling and keep the code smaller. indeed, I moved the code to use NLA_PUT and the hidden failure label >> + >> + ret = genlmsg_end(skb_rsp, msg_head); >> + if (ret < 0) >> + goto send_event_failure; >> + >> + genlmsg_unicast(&init_net, skb_rsp, snet_nl_pid); >> + return 0; >> + >> +send_event_failure: >> + kfree_skb(skb_rsp); >> + return 0; > > Shouldn't this error be returned to the caller to avoid waiting > for the timeout to occur (same question for the return value of > genlmsg_unicast, which can also fail). indeed, if error occurred, no need to wait, we can apply the polocy verdict. genlmsg_unicast() is taking care of freeing the skb if error occured, so we can return directly the value of genlmsg_unicast() thanks Patrick sam -- To unsubscribe from this list: send the line "unsubscribe netdev" in the body of a message to majordomo@vger.kernel.org More majordomo info at http://vger.kernel.org/majordomo-info.html
Patrick McHardy <kaber@trash.net> writes: > Samir Bellabes wrote: >> +static const struct nla_policy snet_genl_policy[SNET_A_MAX + 1] >> +__read_mostly = { > > You don't need __read_mostly for const. If I recall correctly, it even > causes an error with certain compiler or linker versions. fixed, thanks Patrick I didn't had errors with (fedora core 11): gcc (GCC) 4.4.1 20090725 (Red Hat 4.4.1-2) GNU ld version 2.19.51.0.2-17.fc11 20090204 nor (ubuntu 9.10): gcc (Ubuntu 4.4.1-4ubuntu8) 4.4.1 GNU ld (GNU Binutils for Ubuntu) 2.20 (2.20-0ubuntu2) sam -- To unsubscribe from this list: send the line "unsubscribe netdev" in the body of a message to majordomo@vger.kernel.org More majordomo info at http://vger.kernel.org/majordomo-info.html
Patrick McHardy <kaber@trash.net> writes: > Samir Bellabes wrote: >> +/** >> + * snet_nl_register - Handle a REGISTER message >> + * @skb: the NETLINK buffer >> + * @info: the Generic NETLINK info block >> + * >> + * Description: >> + * Notify the kernel that an application is listening for events. >> + * Returns zero on success, negative values on failure. >> + */ >> +static int snet_nl_register(struct sk_buff *skb, struct genl_info *info) >> +{ >> + int ret = -EINVAL; >> + u32 version = 0; >> + u8 set_resp_flag = 0; >> + >> + atomic_set(&snet_nl_seq, info->snd_seq); >> + >> + if (!info->attrs[SNET_A_VERSION]) >> + return -EINVAL; >> + version = nla_get_u32(info->attrs[SNET_A_VERSION]); >> + >> + if (version == SNET_VERSION) { /* version is compliant */ >> + atomic_inc(&snet_num_listeners); >> + set_resp_flag = 1; >> + } >> + >> + ret = snet_nl_response_flag(info, &snet_genl_family, >> + SNET_C_REGISTER, SNET_A_REGISTERED, >> + set_resp_flag); > > Is this really needed? A return value of 0 should already tell userspace > that the command was successful. If it really wants a seperate success > message, it can use NLM_F_ACK. This will also automatically take care > of using the proper sequence number, so the snet_nl_seq handling isn't > required anymore. Same for all similar cases below. Indeed, we can use the return value to inform the userspace about the status of operations. I moved all the code, and deleted the no longer used function snet_nl_response_flag() Thanks Patrick, sam -- To unsubscribe from this list: send the line "unsubscribe netdev" in the body of a message to majordomo@vger.kernel.org More majordomo info at http://vger.kernel.org/majordomo-info.html
Patrick McHardy <kaber@trash.net> writes: >> +atomic_t snet_num_listeners = ATOMIC_INIT(0); > > The num_listeners seem to be redundant as you only support a > single listener anyways, whose presence is indicated by > snet_nl_pid != 0. I simplified the code, by removing the variable snet_num_listeners and use the value of snet_nl_pid * if snet_nl_pid == 0, then there is no userspace listening application * if snet_nl_pid > 0, then its value is the PID of the listening application In the same time, I deleted the check on a listener for this operations on verdict : snet_nl_version, snet_nl_insert, snet_nl_remove, snet_nl_flush, snet_nl_verdict_delay. In this way, it's possible to execute this operations and get events in the same time (which means have a listeners) Patrick, thanks again for reviewing sam -- To unsubscribe from this list: send the line "unsubscribe netdev" in the body of a message to majordomo@vger.kernel.org More majordomo info at http://vger.kernel.org/majordomo-info.html
Patrick McHardy <kaber@trash.net> writes: > Samir Bellabes wrote: >> + skb_rsp = genlmsg_new(NLMSG_GOODSIZE, GFP_KERNEL); >> + if (skb_rsp == NULL) >> + return 0; > > You could decrease the chance of rcvqueue overflow by using a smaller > allocation size. I took care of this by calculating the exact needed size. thanks, sam -- To unsubscribe from this list: send the line "unsubscribe netdev" in the body of a message to majordomo@vger.kernel.org More majordomo info at http://vger.kernel.org/majordomo-info.html
Patrick McHardy <kaber@trash.net> writes: > Samir Bellabes wrote: >> +++ b/security/snet/include/snet_netlink.h >> @@ -0,0 +1,201 @@ >> +#ifndef _SNET_NETLINK_H >> +#define _SNET_NETLINK_H >> + >> +#include <linux/in6.h> >> +#include "snet_hooks.h" >> + >> +extern unsigned int snet_verdict_delay; > > As this file defines the userspace interface, it probably shouldn't > contain declarations of kernel-internal variables (same for > snet_hooks.h). It would also be better placed in include/linux as > the other netlink API definitions. sorry the the delay on this. indeed, this is fixed now, thanks Patrick sam -- To unsubscribe from this list: send the line "unsubscribe netdev" in the body of a message to majordomo@vger.kernel.org More majordomo info at http://vger.kernel.org/majordomo-info.html
diff --git a/security/snet/include/snet_netlink.h b/security/snet/include/snet_netlink.h new file mode 100644 index 0000000..d739f66 --- /dev/null +++ b/security/snet/include/snet_netlink.h @@ -0,0 +1,201 @@ +#ifndef _SNET_NETLINK_H +#define _SNET_NETLINK_H + +/* + * The following payloads are supported. + * + * o VERSION: + * Sent by an application to verify the snet version. + * When sent by an application, there is no payload. + * When sent by the kernel, it's a response to an VERSION request. + * + * Required attributes: + * + * SNET_A_VERSION + * + * o REGISTER: + * Sent by an application to notify listening for events. + * + * o UNREGISTER: + * Sent by an application to notify unlistening for events. + * + * o INSERT: + * Sent by an application to insert a new event. + * + * Required attributes: + * + * SNET_A_SYSCALL + * SNET_A_PROTOCOL + * + * o REMOVE + * Sent by an application to remove a event. + * + * Required attributes: + * + * SNET_A_SYSCALL + * SNET_A_PROTOCOL + * + * o FLUSH + * Sent by an application to flush the events' hashtable + * + * o LIST + * Sent by an application to list all events on the hashtable. + * When sent by an application there is no payload and the NLM_F_DUMP + * flag should be set. The kernel should respond with a series of + * the following messages. + * + * o VERDICT + * kernel -> userspace + * Sent by the kernel to notify for a syscall is pending for a + * verdict or to notify for a network event. + * + * Required attributes: + * + * SNET_A_VERDICT_ID + * SNET_A_SYSCALL + * SNET_A_PROTOCOL + * SNET_A_FAMILY + * SNET_A_UID + * SNET_A_PID + * + * If using SNET_SOCKET_CREATE + * the following attributes are required: + * + * SNET_A_TYPE + * + * If using SNET_SOCKET_LISTEN or SNET_SOCKET_BIND or SNET_SOCKET_ACCEPT + * the following attributes are required: + * + * SNET_A_SADDR or SNET_A_SADDR6 depending on sk->family + * SNET_A_SPORT + * + * If using SNET_SOCKET_CONNECT + * the following attributes are required: + * + * SNET_A_SADDR or SNET_A_SADDR6 depending on sk->family + * SNET_A_DADDR or SNET_A_DADDR6 depending on sk->family + * SNET_A_SPORT + * SNET_A_DPORT + * + * If using SNET_SOCKET_SENDMSG + * the following attributes are required: + * + * SNET_A_SADDR or SNET_A_SADDR6 depending on sk->family + * SNET_A_DADDR or SNET_A_DADDR6 depending on sk->family + * SNET_A_SPORT + * SNET_A_DPORT + * SNET_A_BUFFER_LEN (msg->msg_iov->iov_len) + * SNET_A_BUFFER (msg->msg_iov->iov_base) + * + * If using SNET_SOCKET_RECVMSG + * the following attributes are required: + * + * SNET_A_SADDR or SNET_A_SADDR6 depending on sk->family + * SNET_A_DADDR or SNET_A_DADDR6 depending on sk->family + * SNET_A_SPORT + * SNET_A_DPORT + * + * If using SNET_SOCKET_SOCK_RCV_SKB + * the following attributes are required: + * + * SNET_A_SADDR or SNET_A_SADDR6 depending on sk->family + * SNET_A_DADDR or SNET_A_DADDR6 depending on sk->family + * SNET_A_SPORT + * SNET_A_DPORT + * SNET_A_BUFFER_LEN () + * SNET_A_BUFFER () + * + * userspace -> kernel + * Sent by a application to set the verdict for a pending event. + * + * Required attributes: + * + * SNET_A_VERDICT_ID + * SNET_A_VERDICT + * + * o VERDICT_DELAY + * Sent by an application to set the timeout value for verdicts. + * + * Required attributes: + * + * SNET_A_VERDICT_DELAY + * + */ + +#include <linux/in6.h> +#include "snet_hooks.h" + +extern unsigned int snet_verdict_delay; + +/* commands */ +enum { + SNET_C_UNSPEC, + SNET_C_VERSION, + SNET_C_REGISTER, + SNET_C_UNREGISTER, + SNET_C_INSERT, + SNET_C_REMOVE, + SNET_C_FLUSH, + SNET_C_LIST, + SNET_C_VERDICT, + SNET_C_VERDICT_DELAY, + __SNET_C_MAX, +}; + +#define SNET_C_MAX (__SNET_C_MAX - 1) + +/* attributes */ +enum { + SNET_A_UNSPEC, + SNET_A_VERSION, /* (NLA_U32) the snet protocol version */ + SNET_A_SYSCALL, /* (NLA_U8) a syscall identifier */ + SNET_A_PROTOCOL, /* (NLA_U8) a protocol identifier */ + SNET_A_INSERTED, + SNET_A_REMOVED, + SNET_A_FLUSHED, + SNET_A_REGISTERED, + SNET_A_UNREGISTERED, + SNET_A_VERDICT_ID, + SNET_A_FAMILY, + SNET_A_UID, + SNET_A_PID, + SNET_A_VERDICT, + SNET_A_DATA_EXT, + SNET_A_VERDICT_DELAY, + SNET_A_VERDICT_DELAYED, + __SNET_A_MAX, +}; + +#define SNET_A_MAX (__SNET_A_MAX - 1) + +#define SNET_GENL_NAME "SNET" +#define SNET_GENL_VERSION SNET_VERSION + +int snet_nl_send_event(const u32 verdict_id, const enum snet_syscall syscall, + const u8 protocol, const u8 family, void *data, + const unsigned int len); + +int snet_nl_list_fill_info(struct sk_buff *skb, u32 pid, u32 seq, + u32 flags, u8 protocol, enum snet_syscall syscall); + +void snet_netlink_exit(void); + +struct snet_sock_half { + struct { + union { + __be32 ip; + struct in6_addr ip6; + }; + } u3; + struct { + __be16 port; + } u; +}; + +struct snet_sock_info { + struct snet_sock_half src; + struct snet_sock_half dst; + int type; +}; + +#endif /* _SNET_NETLINK_H */ diff --git a/security/snet/snet_netlink.c b/security/snet/snet_netlink.c new file mode 100644 index 0000000..cc21d6c --- /dev/null +++ b/security/snet/snet_netlink.c @@ -0,0 +1,541 @@ +#include <linux/sched.h> +#include <net/genetlink.h> +#include <linux/in6.h> + +#include "snet.h" +#include "snet_netlink.h" +#include "snet_verdict.h" +#include "snet_event.h" +#include <snet_utils.h> + +atomic_t snet_nl_seq = ATOMIC_INIT(0); +static uint32_t snet_nl_pid; +static struct genl_family snet_genl_family; +atomic_t snet_num_listeners = ATOMIC_INIT(0); + +/* + * snet genetlink + */ +int snet_nl_send_event(const u32 verdict_id, const enum snet_syscall syscall, + const u8 protocol, const u8 family, void *data, + const unsigned int len) +{ + struct sk_buff *skb_rsp; + void *msg_head; + int ret = -ENOMEM; + + skb_rsp = genlmsg_new(NLMSG_GOODSIZE, GFP_KERNEL); + if (skb_rsp == NULL) + return 0; + + msg_head = genlmsg_put(skb_rsp, snet_nl_pid, + atomic_inc_return(&snet_nl_seq), + &snet_genl_family, 0, SNET_C_VERDICT); + if (msg_head == NULL) + goto send_event_failure; + + snet_dbg("verdict_id=0x%x syscall=%s protocol=%u " + "family=%u uid=%u pid=%u\n", + verdict_id, snet_syscall_name(syscall), + protocol, family, current_uid(), current->pid); + + if (verdict_id) { + ret = nla_put_u32(skb_rsp, SNET_A_VERDICT_ID, verdict_id); + if (ret != 0) + goto send_event_failure; + } + ret = nla_put_u8(skb_rsp, SNET_A_SYSCALL, syscall); + if (ret != 0) + goto send_event_failure; + ret = nla_put_u8(skb_rsp, SNET_A_PROTOCOL, protocol); + if (ret != 0) + goto send_event_failure; + ret = nla_put_u8(skb_rsp, SNET_A_FAMILY, family); + if (ret != 0) + goto send_event_failure; + ret = nla_put_u32(skb_rsp, SNET_A_UID, current_uid()); + if (ret != 0) + goto send_event_failure; + ret = nla_put_u32(skb_rsp, SNET_A_PID, current->pid); + if (ret != 0) + goto send_event_failure; + ret = nla_put(skb_rsp, SNET_A_DATA_EXT, len, data); + if (ret != 0) + goto send_event_failure; + + ret = genlmsg_end(skb_rsp, msg_head); + if (ret < 0) + goto send_event_failure; + + genlmsg_unicast(&init_net, skb_rsp, snet_nl_pid); + return 0; + +send_event_failure: + kfree_skb(skb_rsp); + return 0; +} + +/* + * snet genetlink helper functions + */ +static int snet_nl_response_flag(struct genl_info *info, + struct genl_family *family, + u8 cmd, int attrtype, u8 set_resp_flag) +{ + int ret = -EINVAL; + struct sk_buff *skb_rsp = NULL; + void *msg_head; + + skb_rsp = genlmsg_new(NLMSG_GOODSIZE, GFP_KERNEL); + if (skb_rsp == NULL) + return -ENOMEM; + msg_head = genlmsg_put_reply(skb_rsp, info, family, 0, cmd); + if (msg_head == NULL) + goto response_failure; + + /* we put flag only if it is asked */ + if (set_resp_flag) { + ret = nla_put_flag(skb_rsp, attrtype); + if (ret != 0) + goto response_failure; + } + + genlmsg_end(skb_rsp, msg_head); + ret = genlmsg_reply(skb_rsp, info); + if (ret != 0) + goto response_failure; + return 0; + +response_failure: + kfree_skb(skb_rsp); + return ret; +} + +/* + * snet genetlink functions + */ + +static struct genl_family snet_genl_family = { + .id = GENL_ID_GENERATE, + .hdrsize = 0, + .name = SNET_GENL_NAME, + .version = SNET_GENL_VERSION, + .maxattr = SNET_A_MAX, +}; + +static const struct nla_policy snet_genl_policy[SNET_A_MAX + 1] +__read_mostly = { + [SNET_A_VERSION] = { .type = NLA_U32 }, + [SNET_A_SYSCALL] = { .type = NLA_U8 }, + [SNET_A_PROTOCOL] = { .type = NLA_U8 }, + [SNET_A_INSERTED] = { .type = NLA_FLAG }, + [SNET_A_REMOVED] = { .type = NLA_FLAG }, + [SNET_A_FLUSHED] = { .type = NLA_FLAG }, + [SNET_A_REGISTERED] = { .type = NLA_FLAG }, + [SNET_A_UNREGISTERED] = { .type = NLA_FLAG }, + [SNET_A_VERDICT_ID] = { .type = NLA_U32 }, + [SNET_A_FAMILY] = { .type = NLA_U8 }, + [SNET_A_UID] = { .type = NLA_U32 }, + [SNET_A_PID] = { .type = NLA_U32 }, + [SNET_A_VERDICT] = { .type = NLA_U8 }, + [SNET_A_DATA_EXT] = { .type = NLA_BINARY, + .len = sizeof(struct snet_sock_info) }, + [SNET_A_VERDICT_DELAY] = { .type = NLA_U32 }, + [SNET_A_VERDICT_DELAYED] = { .type = NLA_FLAG }, +}; + +/** + * snet_nl_version - Handle a VERSION message + * @skb: the NETLINK buffer + * @info: the Generic NETLINK info block + * + * Description: + * Process a user generated VERSION message and respond accordingly. + * Returns zero on success, negative values on failure. + */ +static int snet_nl_version(struct sk_buff *skb, struct genl_info *info) +{ + int ret = -ENOMEM; + struct sk_buff *skb_rsp = NULL; + void *msg_head; + + atomic_set(&snet_nl_seq, info->snd_seq); + + if (atomic_read(&snet_num_listeners) <= 0) + return 0; + + skb_rsp = genlmsg_new(NLMSG_GOODSIZE, GFP_KERNEL); + if (skb_rsp == NULL) + return -ENOMEM; + msg_head = genlmsg_put_reply(skb_rsp, info, &snet_genl_family, + 0, SNET_C_VERSION); + if (msg_head == NULL) + goto version_failure; + + ret = nla_put_u32(skb_rsp, SNET_A_VERSION, SNET_VERSION); + if (ret != 0) + goto version_failure; + + genlmsg_end(skb_rsp, msg_head); + + ret = genlmsg_reply(skb_rsp, info); + if (ret != 0) + goto version_failure; + return 0; + +version_failure: + kfree_skb(skb_rsp); + return ret; +} + +/** + * snet_nl_register - Handle a REGISTER message + * @skb: the NETLINK buffer + * @info: the Generic NETLINK info block + * + * Description: + * Notify the kernel that an application is listening for events. + * Returns zero on success, negative values on failure. + */ +static int snet_nl_register(struct sk_buff *skb, struct genl_info *info) +{ + int ret = -EINVAL; + u32 version = 0; + u8 set_resp_flag = 0; + + atomic_set(&snet_nl_seq, info->snd_seq); + + if (!info->attrs[SNET_A_VERSION]) + return -EINVAL; + version = nla_get_u32(info->attrs[SNET_A_VERSION]); + + if (version == SNET_VERSION) { /* version is compliant */ + atomic_inc(&snet_num_listeners); + set_resp_flag = 1; + } + + ret = snet_nl_response_flag(info, &snet_genl_family, + SNET_C_REGISTER, SNET_A_REGISTERED, + set_resp_flag); + + snet_nl_pid = info->snd_pid; + snet_dbg("pid=%u num_listeners=%d\n", + snet_nl_pid, atomic_read(&snet_num_listeners)); + return ret; +} + +/** + * snet_nl_unregister - Handle a UNREGISTER message + * @skb: the NETLINK buffer + * @info: the Generic NETLINK info block + * + * Description: + * Notify the kernel that the application is no more listening for events. + * Returns zero on success, negative values on failure. + */ +static int snet_nl_unregister(struct sk_buff *skb, struct genl_info *info) +{ + int ret = -EINVAL; + + atomic_set(&snet_nl_seq, info->snd_seq); + + if (atomic_read(&snet_num_listeners)) + atomic_dec(&snet_num_listeners); + ret = snet_nl_response_flag(info, &snet_genl_family, + SNET_C_UNREGISTER, SNET_A_UNREGISTERED, 1); + snet_dbg("pid=%u num_listeners=%d\n", + snet_nl_pid, atomic_read(&snet_num_listeners)); + return ret; +} + +/** + * snet_nl_insert - Handle a INSERT message + * @skb: the NETLINK buffer + * @info: the Generic NETLINK info block + * + * Description: + * Insert a new event to the events' hashtable. Returns zero on success, + * negative values on failure. + */ +static int snet_nl_insert(struct sk_buff *skb, struct genl_info *info) +{ + int ret_event = -EINVAL, ret = -EINVAL; + enum snet_syscall syscall; + u8 protocol; + u8 set_resp_flag = 0; + + atomic_set(&snet_nl_seq, info->snd_seq); + + if (atomic_read(&snet_num_listeners) <= 0) + return 0; + + if (!info->attrs[SNET_A_SYSCALL] || !info->attrs[SNET_A_PROTOCOL]) + return -EINVAL; + + syscall = nla_get_u8(info->attrs[SNET_A_SYSCALL]); + protocol = nla_get_u8(info->attrs[SNET_A_PROTOCOL]); + ret_event = snet_event_insert(syscall, protocol); + snet_dbg("syscall=%s protocol=%u insert=%s\n", + snet_syscall_name(syscall), protocol, + (ret_event == 0) ? "success" : "failed"); + + if (ret_event == 0) + set_resp_flag = 1; + + ret = snet_nl_response_flag(info, &snet_genl_family, + SNET_C_INSERT, SNET_A_INSERTED, + set_resp_flag); + return ret; +} + +/** + * snet_nl_remove - Handle a REMOVE message + * @skb: the NETLINK buffer + * @info: the Generic NETLINK info block + * + * Description: + * Remove a event from the events' hastable. Returns zero on success, + * negative values on failure. + */ +static int snet_nl_remove(struct sk_buff *skb, struct genl_info *info) +{ + int ret_event = -EINVAL, ret = -EINVAL; + enum snet_syscall syscall; + u8 protocol; + u8 set_resp_flag = 0; + + atomic_set(&snet_nl_seq, info->snd_seq); + + if (atomic_read(&snet_num_listeners) <= 0) + return 0; + + if (!info->attrs[SNET_A_SYSCALL] || !info->attrs[SNET_A_PROTOCOL]) + return -EINVAL; + + syscall = nla_get_u8(info->attrs[SNET_A_SYSCALL]); + protocol = nla_get_u8(info->attrs[SNET_A_PROTOCOL]); + ret_event = snet_event_remove(syscall, protocol); + snet_dbg("syscall=%s protocol=%u remove=%s\n", + snet_syscall_name(syscall), protocol, + (ret_event == 0) ? "success" : "failed"); + + if (ret_event == 0) + set_resp_flag = 1; + + ret = snet_nl_response_flag(info, &snet_genl_family, + SNET_C_REMOVE, SNET_A_REMOVED, + set_resp_flag); + return ret; +} + +/** + * snet_nl_flush - Handle a FLUSH message + * @skb: the NETLINK buffer + * @info: the Generic NETLINK info block + * + * Description: + * Remove all events from the hashtable. Returns zero on success, + * negative values on failure. + */ +static int snet_nl_flush(struct sk_buff *skb, struct genl_info *info) +{ + int ret = -EINVAL; + u8 set_resp_flag = 0; + + atomic_set(&snet_nl_seq, info->snd_seq); + + if (atomic_read(&snet_num_listeners) <= 0) + return 0; + + snet_event_flush(); + + set_resp_flag = 1; + + ret = snet_nl_response_flag(info, &snet_genl_family, + SNET_C_FLUSH, SNET_A_FLUSHED, + set_resp_flag); + return ret; +} + +int snet_nl_list_fill_info(struct sk_buff *skb, u32 pid, u32 seq, + u32 flags, u8 protocol, enum snet_syscall syscall) +{ + void *hdr; + int ret = -1; + + hdr = genlmsg_put(skb, pid, seq, &snet_genl_family, flags, SNET_C_LIST); + if (hdr == NULL) + return -1; + + ret = nla_put_u8(skb, SNET_A_SYSCALL, syscall); + if (ret != 0) + goto list_failure; + + ret = nla_put_u8(skb, SNET_A_PROTOCOL, protocol); + if (ret != 0) + goto list_failure; + + return genlmsg_end(skb, hdr); + +list_failure: + genlmsg_cancel(skb, hdr); + return 0; +} +/** + * snet_nl_list - Handle a LIST message + * @skb: the NETLINK buffer + * @cb: + * + * Description: + * Process a user LIST message and respond. Returns zero on success, + * and negative values on error. + */ +static int snet_nl_list(struct sk_buff *skb, struct netlink_callback *cb) +{ + unsigned int len = 0; + + atomic_set(&snet_nl_seq, cb->nlh->nlmsg_seq); + len = snet_event_fill_info(skb, cb); + return len; +} + +/** + * snet_nl_verdict - Handle a VERDICT message + * @skb: the NETLINK buffer + * @info the Generic NETLINK info block + * + * Description: + * Provides userspace with a VERDICT message, ie we are sending informations + * with this command. Userspace is sending the appropriate verdict for the + * event. Returns zero on success,and negative values on error. + */ +static int snet_nl_verdict(struct sk_buff *skb, + struct genl_info *info) +{ + u32 verdict_id; + enum snet_verdict verdict; + + atomic_set(&snet_nl_seq, info->snd_seq); + + if (atomic_read(&snet_num_listeners) <= 0) + return 0; + + if (!info->attrs[SNET_A_VERDICT_ID] || !info->attrs[SNET_A_VERDICT]) + return -EINVAL; + + verdict_id = nla_get_u32(info->attrs[SNET_A_VERDICT_ID]); + verdict = nla_get_u8(info->attrs[SNET_A_VERDICT]); + snet_verdict_set(verdict_id, verdict); + return 0; +} + +/** + * snet_nl_verdict_delay - Handle a VERDICT_DELAY message + * @skb: the NETLINK buffer + * @info the Generic NETLINK info block + * + * Description: + * Provides userspace with a VERDICT_DELAY message, ie userspace application + * is able to set the value of the timeout for verdicts + * Returns zero on success, and negative values on error. + */ +static int snet_nl_verdict_delay(struct sk_buff *skb, + struct genl_info *info) +{ + int ret = -EINVAL; + + atomic_set(&snet_nl_seq, info->snd_seq); + + if (atomic_read(&snet_num_listeners) <= 0) + return 0; + + if (!info->attrs[SNET_A_VERDICT_DELAY]) + return -EINVAL; + + snet_verdict_delay = nla_get_u32(info->attrs[SNET_A_VERDICT_DELAY]); + /* FIXME: do something */ + ret = snet_nl_response_flag(info, &snet_genl_family, + SNET_C_VERDICT_DELAY, SNET_A_VERDICT_DELAYED, + 1); + return ret; +} + +static struct genl_ops snet_genl_ops[] = { + { + .cmd = SNET_C_VERSION, + .flags = GENL_ADMIN_PERM, + .policy = snet_genl_policy, + .doit = snet_nl_version, + .dumpit = NULL, + }, + { + .cmd = SNET_C_REGISTER, + .flags = GENL_ADMIN_PERM, + .policy = snet_genl_policy, + .doit = snet_nl_register, + .dumpit = NULL, + }, + { + .cmd = SNET_C_UNREGISTER, + .flags = GENL_ADMIN_PERM, + .policy = snet_genl_policy, + .doit = snet_nl_unregister, + .dumpit = NULL, + }, + { + .cmd = SNET_C_INSERT, + .flags = GENL_ADMIN_PERM, + .policy = snet_genl_policy, + .doit = snet_nl_insert, + .dumpit = NULL, + }, + { + .cmd = SNET_C_REMOVE, + .flags = GENL_ADMIN_PERM, + .policy = snet_genl_policy, + .doit = snet_nl_remove, + .dumpit = NULL, + }, + { + .cmd = SNET_C_FLUSH, + .flags = GENL_ADMIN_PERM, + .policy = snet_genl_policy, + .doit = snet_nl_flush, + .dumpit = NULL, + }, + { + .cmd = SNET_C_LIST, + .flags = GENL_ADMIN_PERM, + .policy = snet_genl_policy, + .doit = NULL, + .dumpit = snet_nl_list, + }, + { + .cmd = SNET_C_VERDICT, + .flags = GENL_ADMIN_PERM, + .policy = snet_genl_policy, + .doit = snet_nl_verdict, + .dumpit = NULL, + }, + { + .cmd = SNET_C_VERDICT_DELAY, + .flags = GENL_ADMIN_PERM, + .policy = snet_genl_policy, + .doit = snet_nl_verdict_delay, + .dumpit = NULL, + }, +}; + +static __init int snet_netlink_init(void) +{ + return genl_register_family_with_ops(&snet_genl_family, + snet_genl_ops, + ARRAY_SIZE(snet_genl_ops)); +} + +void snet_netlink_exit(void) +{ + genl_unregister_family(&snet_genl_family); +} + +__initcall(snet_netlink_init);
this patch adds the snet communication's subsystem. snet_netlink is using genetlink for sending/receiving messages to/from userspace. the genetlink operations permit to receive orders to manage the table of events - events are values [syscall, protocol] - which is used to know which syscall and protocol have to be protected. genl operations are also used to manage communication of events to userspace, and to receive the related verdict. Signed-off-by: Samir Bellabes <sam@synack.fr> --- security/snet/include/snet_netlink.h | 201 +++++++++++++ security/snet/snet_netlink.c | 541 ++++++++++++++++++++++++++++++++++ 2 files changed, 742 insertions(+), 0 deletions(-) create mode 100644 security/snet/include/snet_netlink.h create mode 100644 security/snet/snet_netlink.c