static int netlink_connect(struct socket *sock, struct sockaddr *addr, int alen, int flags) { int err = 0; struct sock *sk = sock->sk; struct sockaddr_nl *nladdr=(struct sockaddr_nl*)addr; if (addr->sa_family == AF_UNSPEC) { sk->protinfo.af_netlink->dst_pid = 0; sk->protinfo.af_netlink->dst_groups = 0; return 0; } if (addr->sa_family != AF_NETLINK) return -EINVAL; /* Only superuser is allowed to send multicasts */ if (nladdr->nl_groups && !netlink_capable(sock, NL_NONROOT_SEND)) return -EPERM; if (!sk->protinfo.af_netlink->pid) err = netlink_autobind(sock); if (err == 0) { sk->protinfo.af_netlink->dst_pid = nladdr->nl_pid; sk->protinfo.af_netlink->dst_groups = nladdr->nl_groups; } return 0; }
static int netlink_bind(struct socket *sock, struct sockaddr *addr, int addr_len) { struct sock *sk = sock->sk; int err; struct sockaddr_nl *nladdr=(struct sockaddr_nl *)addr; if (nladdr->nl_family != AF_NETLINK) return -EINVAL; /* Only superuser is allowed to listen multicasts */ if (nladdr->nl_groups && !netlink_capable(sock, NL_NONROOT_RECV)) return -EPERM; if (sk->protinfo.af_netlink->pid) { if (nladdr->nl_pid != sk->protinfo.af_netlink->pid) return -EINVAL; sk->protinfo.af_netlink->groups = nladdr->nl_groups; return 0; } if (nladdr->nl_pid == 0) { err = netlink_autobind(sock); if (err == 0) sk->protinfo.af_netlink->groups = nladdr->nl_groups; return err; } err = netlink_insert(sk, nladdr->nl_pid); if (err == 0) sk->protinfo.af_netlink->groups = nladdr->nl_groups; return err; }
static int handle_cmd(struct sk_buff *skb, struct genl_info *info) { struct sk_buff *rep_buf; struct nlmsghdr *rep_nlh; struct nlmsghdr *req_nlh = info->nlhdr; struct tipc_genlmsghdr *req_userhdr = info->userhdr; int hdr_space = nlmsg_total_size(GENL_HDRLEN + TIPC_GENL_HDRLEN); u16 cmd; if ((req_userhdr->cmd & 0xC000) && (!netlink_capable(skb, CAP_NET_ADMIN))) cmd = TIPC_CMD_NOT_NET_ADMIN; else cmd = req_userhdr->cmd; rep_buf = tipc_cfg_do_cmd(req_userhdr->dest, cmd, nlmsg_data(req_nlh) + GENL_HDRLEN + TIPC_GENL_HDRLEN, nlmsg_attrlen(req_nlh, GENL_HDRLEN + TIPC_GENL_HDRLEN), hdr_space); if (rep_buf) { skb_push(rep_buf, hdr_space); rep_nlh = nlmsg_hdr(rep_buf); memcpy(rep_nlh, req_nlh, hdr_space); rep_nlh->nlmsg_len = rep_buf->len; genlmsg_unicast(&init_net, rep_buf, NETLINK_CB(skb).portid); } return 0; }
static inline void dnrmg_receive_user_skb(struct sk_buff *skb) { struct nlmsghdr *nlh = nlmsg_hdr(skb); if (nlh->nlmsg_len < sizeof(*nlh) || skb->len < nlh->nlmsg_len) return; if (!netlink_capable(skb, CAP_NET_ADMIN)) RCV_SKB_FAIL(-EPERM); /* Eventually we might send routing messages too */ RCV_SKB_FAIL(-EINVAL); }
/* Process one complete nfnetlink message. */ static int nfnetlink_rcv_msg(struct sk_buff *skb, struct nlmsghdr *nlh) { const struct nfnl_callback *nc; const struct nfnetlink_subsystem *ss; int type, err; if (!netlink_capable(skb, CAP_NET_ADMIN)) return -EPERM; /* All the messages must at least contain nfgenmsg */ if (nlh->nlmsg_len < NLMSG_LENGTH(sizeof(struct nfgenmsg))) return 0; type = nlh->nlmsg_type; replay: ss = nfnetlink_get_subsys(type); if (!ss) { #ifdef CONFIG_MODULES nfnl_unlock(); request_module("nfnetlink-subsys-%d", NFNL_SUBSYS_ID(type)); nfnl_lock(); ss = nfnetlink_get_subsys(type); if (!ss) #endif return -EINVAL; } nc = nfnetlink_find_client(type, ss); if (!nc) return -EINVAL; { int min_len = NLMSG_SPACE(sizeof(struct nfgenmsg)); u_int8_t cb_id = NFNL_MSG_TYPE(nlh->nlmsg_type); struct nlattr *cda[ss->cb[cb_id].attr_count + 1]; struct nlattr *attr = (void *)nlh + min_len; int attrlen = nlh->nlmsg_len - min_len; err = nla_parse(cda, ss->cb[cb_id].attr_count, attr, attrlen, ss->cb[cb_id].policy); if (err < 0) return err; err = nc->call(nfnl, skb, nlh, (const struct nlattr **)cda); if (err == -EAGAIN) goto replay; return err; } }
static int netlink_sendmsg(struct kiocb *kiocb, struct socket *sock, struct msghdr *msg, size_t len) { struct sock_iocb *siocb = kiocb_to_siocb(kiocb); struct sock *sk = sock->sk; struct netlink_opt *nlk = nlk_sk(sk); struct sockaddr_nl *addr=msg->msg_name; u32 dst_pid; u32 dst_groups; struct sk_buff *skb; int err; struct scm_cookie scm; if (msg->msg_flags&MSG_OOB) return -EOPNOTSUPP; if (NULL == siocb->scm) siocb->scm = &scm; err = scm_send(sock, msg, siocb->scm); if (err < 0) return err; if (msg->msg_namelen) { if (addr->nl_family != AF_NETLINK) return -EINVAL; dst_pid = addr->nl_pid; dst_groups = addr->nl_groups; if (dst_groups && !netlink_capable(sock, NL_NONROOT_SEND)) return -EPERM; } else { dst_pid = nlk->dst_pid; dst_groups = nlk->dst_groups; } if (!nlk->pid) { err = netlink_autobind(sock); if (err) goto out; } err = -EMSGSIZE; if (len > sk->sk_sndbuf - 32) goto out; err = -ENOBUFS; skb = alloc_skb(len, GFP_KERNEL); if (skb==NULL) goto out; NETLINK_CB(skb).pid = nlk->pid; NETLINK_CB(skb).groups = nlk->groups; NETLINK_CB(skb).dst_pid = dst_pid; NETLINK_CB(skb).dst_groups = dst_groups; memcpy(NETLINK_CREDS(skb), &siocb->scm->creds, sizeof(struct ucred)); /* What can I do? Netlink is asynchronous, so that we will have to save current capabilities to check them, when this message will be delivered to corresponding kernel module. --ANK (980802) */ err = -EFAULT; if (memcpy_fromiovec(skb_put(skb,len), msg->msg_iov, len)) { kfree_skb(skb); goto out; } err = security_netlink_send(sk, skb); if (err) { kfree_skb(skb); goto out; } if (dst_groups) { atomic_inc(&skb->users); netlink_broadcast(sk, skb, dst_pid, dst_groups, GFP_KERNEL); } err = netlink_unicast(sk, skb, dst_pid, msg->msg_flags&MSG_DONTWAIT); out: return err; }
static int netlink_release(struct socket *sock) { struct sock *sk = sock->sk; struct netlink_opt *nlk; if (!sk) return 0; netlink_remove(sk); nlk = nlk_sk(sk); spin_lock(&nlk->cb_lock); if (nlk->cb) { nlk->cb->done(nlk->cb); netlink_destroy_callback(nlk->cb); nlk->cb = NULL; __sock_put(sk); } spin_unlock(&nlk->cb_lock); /* OK. Socket is unlinked, and, therefore, no new packets will arrive */ sock_orphan(sk); sock->sk = NULL; wake_up_interruptible_all(&nlk->wait); skb_queue_purge(&sk->sk_write_queue); if (nlk->pid && !nlk->groups) { struct netlink_notify n = { .protocol = sk->sk_protocol, .pid = nlk->pid, }; notifier_call_chain(&netlink_chain, NETLINK_URELEASE, &n); } sock_put(sk); return 0; } static int netlink_autobind(struct socket *sock) { struct sock *sk = sock->sk; struct nl_pid_hash *hash = &nl_table[sk->sk_protocol].hash; struct hlist_head *head; struct sock *osk; struct hlist_node *node; s32 pid = current->pid; int err; static s32 rover = -4097; retry: cond_resched(); netlink_table_grab(); head = nl_pid_hashfn(hash, pid); sk_for_each(osk, node, head) { if (nlk_sk(osk)->pid == pid) { /* Bind collision, search negative pid values. */ pid = rover--; if (rover > -4097) rover = -4097; netlink_table_ungrab(); goto retry; } } netlink_table_ungrab(); err = netlink_insert(sk, pid); if (err == -EADDRINUSE) goto retry; nlk_sk(sk)->groups = 0; return 0; } static inline int netlink_capable(struct socket *sock, unsigned int flag) { return (nl_nonroot[sock->sk->sk_protocol] & flag) || capable(CAP_NET_ADMIN); } static int netlink_bind(struct socket *sock, struct sockaddr *addr, int addr_len) { struct sock *sk = sock->sk; struct netlink_opt *nlk = nlk_sk(sk); struct sockaddr_nl *nladdr = (struct sockaddr_nl *)addr; int err; if (nladdr->nl_family != AF_NETLINK) return -EINVAL; /* Only superuser is allowed to listen multicasts */ if (nladdr->nl_groups && !netlink_capable(sock, NL_NONROOT_RECV)) return -EPERM; if (nlk->pid) { if (nladdr->nl_pid != nlk->pid) return -EINVAL; } else { err = nladdr->nl_pid ? netlink_insert(sk, nladdr->nl_pid) : netlink_autobind(sock); if (err) return err; } if (!nladdr->nl_groups && !nlk->groups) return 0; netlink_table_grab(); if (nlk->groups && !nladdr->nl_groups) __sk_del_bind_node(sk); else if (!nlk->groups && nladdr->nl_groups) sk_add_bind_node(sk, &nl_table[sk->sk_protocol].mc_list); nlk->groups = nladdr->nl_groups; netlink_table_ungrab(); return 0; } static int netlink_connect(struct socket *sock, struct sockaddr *addr, int alen, int flags) { int err = 0; struct sock *sk = sock->sk; struct netlink_opt *nlk = nlk_sk(sk); struct sockaddr_nl *nladdr=(struct sockaddr_nl*)addr; if (addr->sa_family == AF_UNSPEC) { sk->sk_state = NETLINK_UNCONNECTED; nlk->dst_pid = 0; nlk->dst_groups = 0; return 0; } if (addr->sa_family != AF_NETLINK) return -EINVAL; /* Only superuser is allowed to send multicasts */ if (nladdr->nl_groups && !netlink_capable(sock, NL_NONROOT_SEND)) return -EPERM; if (!nlk->pid) err = netlink_autobind(sock); if (err == 0) { sk->sk_state = NETLINK_CONNECTED; nlk->dst_pid = nladdr->nl_pid; nlk->dst_groups = nladdr->nl_groups; } return err; }
/** * scsi_nl_rcv_msg - Receive message handler. * @skb: socket receive buffer * * Description: Extracts message from a receive buffer. * Validates message header and calls appropriate transport message handler * * **/ static void scsi_nl_rcv_msg(struct sk_buff *skb) { struct nlmsghdr *nlh; struct scsi_nl_hdr *hdr; unsigned long flags; u32 rlen; int err, tport; while (skb->len >= NLMSG_SPACE(0)) { err = 0; nlh = nlmsg_hdr(skb); if ((nlh->nlmsg_len < (sizeof(*nlh) + sizeof(*hdr))) || (skb->len < nlh->nlmsg_len)) { printk(KERN_WARNING "%s: discarding partial skb\n", __func__); return; } rlen = NLMSG_ALIGN(nlh->nlmsg_len); if (rlen > skb->len) rlen = skb->len; if (nlh->nlmsg_type != SCSI_TRANSPORT_MSG) { err = -EBADMSG; goto next_msg; } hdr = NLMSG_DATA(nlh); if ((hdr->version != SCSI_NL_VERSION) || (hdr->magic != SCSI_NL_MAGIC)) { err = -EPROTOTYPE; goto next_msg; } if (!netlink_capable(skb, CAP_SYS_ADMIN)) { err = -EPERM; goto next_msg; } if (nlh->nlmsg_len < (sizeof(*nlh) + hdr->msglen)) { printk(KERN_WARNING "%s: discarding partial message\n", __func__); goto next_msg; } /* * Deliver message to the appropriate transport */ spin_lock_irqsave(&scsi_nl_lock, flags); tport = hdr->transport; if ((tport < SCSI_NL_MAX_TRANSPORTS) && !(transports[tport].flags & HANDLER_DELETING) && (transports[tport].msg_handler)) { transports[tport].refcnt++; spin_unlock_irqrestore(&scsi_nl_lock, flags); err = transports[tport].msg_handler(skb); spin_lock_irqsave(&scsi_nl_lock, flags); transports[tport].refcnt--; } else err = -ENOENT; spin_unlock_irqrestore(&scsi_nl_lock, flags); next_msg: if ((err) || (nlh->nlmsg_flags & NLM_F_ACK)) netlink_ack(skb, nlh, err); skb_pull(skb, rlen); } }