/* BlueZ - Bluetooth protocol stack for Linux Copyright (C) 2000-2001 Qualcomm Incorporated Written 2000,2001 by Maxim Krasnyansky This program is free software; you can redistribute it and/or modify it under the terms of the GNU General Public License version 2 as published by the Free Software Foundation; THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT OF THIRD PARTY RIGHTS. IN NO EVENT SHALL THE COPYRIGHT HOLDER(S) AND AUTHOR(S) BE LIABLE FOR ANY CLAIM, OR ANY SPECIAL INDIRECT OR CONSEQUENTIAL DAMAGES, OR ANY DAMAGES WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE. ALL LIABILITY, INCLUDING LIABILITY FOR INFRINGEMENT OF ANY PATENTS, COPYRIGHTS, TRADEMARKS OR OTHER RIGHTS, RELATING TO USE OF THIS SOFTWARE IS DISCLAIMED. */ /* Bluetooth L2CAP core and sockets. */ #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #ifndef CONFIG_BT_L2CAP_DEBUG #undef BT_DBG #define BT_DBG(D...) #endif #define VERSION "2.7" static struct proto_ops l2cap_sock_ops; static struct bt_sock_list l2cap_sk_list = { .lock = RW_LOCK_UNLOCKED }; static int l2cap_conn_del(struct hci_conn *conn, int err); static void __l2cap_chan_add(struct l2cap_conn *conn, struct sock *sk, struct sock *parent); static void l2cap_chan_del(struct sock *sk, int err); static void __l2cap_sock_close(struct sock *sk, int reason); static void l2cap_sock_close(struct sock *sk); static void l2cap_sock_kill(struct sock *sk); static struct sk_buff *l2cap_build_cmd(struct l2cap_conn *conn, u8 code, u8 ident, u16 dlen, void *data); /* ---- L2CAP timers ---- */ static void l2cap_sock_timeout(unsigned long arg) { struct sock *sk = (struct sock *) arg; BT_DBG("sock %p state %d", sk, sk->sk_state); bh_lock_sock(sk); __l2cap_sock_close(sk, ETIMEDOUT); bh_unlock_sock(sk); l2cap_sock_kill(sk); sock_put(sk); } static void l2cap_sock_set_timer(struct sock *sk, long timeout) { BT_DBG("sk %p state %d timeout %ld", sk, sk->sk_state, timeout); sk_reset_timer(sk, &sk->sk_timer, jiffies + timeout); } static void l2cap_sock_clear_timer(struct sock *sk) { BT_DBG("sock %p state %d", sk, sk->sk_state); sk_stop_timer(sk, &sk->sk_timer); } static void l2cap_sock_init_timer(struct sock *sk) { init_timer(&sk->sk_timer); sk->sk_timer.function = l2cap_sock_timeout; sk->sk_timer.data = (unsigned long)sk; } /* ---- L2CAP connections ---- */ static struct l2cap_conn *l2cap_conn_add(struct hci_conn *hcon, u8 status) { struct l2cap_conn *conn; if ((conn = hcon->l2cap_data)) return conn; if (status) return conn; if (!(conn = kmalloc(sizeof(struct l2cap_conn), GFP_ATOMIC))) return NULL; memset(conn, 0, sizeof(struct l2cap_conn)); hcon->l2cap_data = conn; conn->hcon = hcon; conn->mtu = hcon->hdev->acl_mtu; conn->src = &hcon->hdev->bdaddr; conn->dst = &hcon->dst; spin_lock_init(&conn->lock); rwlock_init(&conn->chan_list.lock); BT_DBG("hcon %p conn %p", hcon, conn); return conn; } static int l2cap_conn_del(struct hci_conn *hcon, int err) { struct l2cap_conn *conn; struct sock *sk; if (!(conn = hcon->l2cap_data)) return 0; BT_DBG("hcon %p conn %p, err %d", hcon, conn, err); if (conn->rx_skb) kfree_skb(conn->rx_skb); /* Kill channels */ while ((sk = conn->chan_list.head)) { bh_lock_sock(sk); l2cap_chan_del(sk, err); bh_unlock_sock(sk); l2cap_sock_kill(sk); } hcon->l2cap_data = NULL; kfree(conn); return 0; } static inline void l2cap_chan_add(struct l2cap_conn *conn, struct sock *sk, struct sock *parent) { struct l2cap_chan_list *l = &conn->chan_list; write_lock(&l->lock); __l2cap_chan_add(conn, sk, parent); write_unlock(&l->lock); } static inline u8 l2cap_get_ident(struct l2cap_conn *conn) { u8 id; /* Get next available identificator. * 1 - 128 are used by kernel. * 129 - 199 are reserved. * 200 - 254 are used by utilities like l2ping, etc. */ spin_lock(&conn->lock); if (++conn->tx_ident > 128) conn->tx_ident = 1; id = conn->tx_ident; spin_unlock(&conn->lock); return id; } static inline int l2cap_send_cmd(struct l2cap_conn *conn, u8 ident, u8 code, u16 len, void *data) { struct sk_buff *skb = l2cap_build_cmd(conn, code, ident, len, data); BT_DBG("code 0x%2.2x", code); if (!skb) return -ENOMEM; return hci_send_acl(conn->hcon, skb, 0); } /* ---- Socket interface ---- */ static struct sock *__l2cap_get_sock_by_addr(u16 psm, bdaddr_t *src) { struct sock *sk; struct hlist_node *node; sk_for_each(sk, node, &l2cap_sk_list.head) if (l2cap_pi(sk)->sport == psm && !bacmp(&bt_sk(sk)->src, src)) goto found; sk = NULL; found: return sk; } /* Find socket with psm and source bdaddr. * Returns closest match. */ static struct sock *__l2cap_get_sock_by_psm(int state, u16 psm, bdaddr_t *src) { struct sock *sk = NULL, *sk1 = NULL; struct hlist_node *node; sk_for_each(sk, node, &l2cap_sk_list.head) { if (state && sk->sk_state != state) continue; if (l2cap_pi(sk)->psm == psm) { /* Exact match. */ if (!bacmp(&bt_sk(sk)->src, src)) break; /* Closest match */ if (!bacmp(&bt_sk(sk)->src, BDADDR_ANY)) sk1 = sk; } } return node ? sk : sk1; } /* Find socket with given address (psm, src). * Returns locked socket */ static inline struct sock *l2cap_get_sock_by_psm(int state, u16 psm, bdaddr_t *src) { struct sock *s; read_lock(&l2cap_sk_list.lock); s = __l2cap_get_sock_by_psm(state, psm, src); if (s) bh_lock_sock(s); read_unlock(&l2cap_sk_list.lock); return s; } static void l2cap_sock_destruct(struct sock *sk) { BT_DBG("sk %p", sk); skb_queue_purge(&sk->sk_receive_queue); skb_queue_purge(&sk->sk_write_queue); } static void l2cap_sock_cleanup_listen(struct sock *parent) { struct sock *sk; BT_DBG("parent %p", parent); /* Close not yet accepted channels */ while ((sk = bt_accept_dequeue(parent, NULL))) l2cap_sock_close(sk); parent->sk_state = BT_CLOSED; sock_set_flag(parent, SOCK_ZAPPED); } /* Kill socket (only if zapped and orphan) * Must be called on unlocked socket. */ static void l2cap_sock_kill(struct sock *sk) { if (!sock_flag(sk, SOCK_ZAPPED) || sk->sk_socket) return; BT_DBG("sk %p state %d", sk, sk->sk_state); /* Kill poor orphan */ bt_sock_unlink(&l2cap_sk_list, sk); sock_set_flag(sk, SOCK_DEAD); sock_put(sk); } static void __l2cap_sock_close(struct sock *sk, int reason) { BT_DBG("sk %p state %d socket %p", sk, sk->sk_state, sk->sk_socket); switch (sk->sk_state) { case BT_LISTEN: l2cap_sock_cleanup_listen(sk); break; case BT_CONNECTED: case BT_CONFIG: case BT_CONNECT2: if (sk->sk_type == SOCK_SEQPACKET) { struct l2cap_conn *conn = l2cap_pi(sk)->conn; struct l2cap_disconn_req req; sk->sk_state = BT_DISCONN; l2cap_sock_set_timer(sk, sk->sk_sndtimeo); req.dcid = __cpu_to_le16(l2cap_pi(sk)->dcid); req.scid = __cpu_to_le16(l2cap_pi(sk)->scid); l2cap_send_cmd(conn, l2cap_get_ident(conn), L2CAP_DISCONN_REQ, sizeof(req), &req); } else { l2cap_chan_del(sk, reason); } break; case BT_CONNECT: case BT_DISCONN: l2cap_chan_del(sk, reason); break; default: sock_set_flag(sk, SOCK_ZAPPED); break; } } /* Must be called on unlocked socket. */ static void l2cap_sock_close(struct sock *sk) { l2cap_sock_clear_timer(sk); lock_sock(sk); __l2cap_sock_close(sk, ECONNRESET); release_sock(sk); l2cap_sock_kill(sk); } static void l2cap_sock_init(struct sock *sk, struct sock *parent) { struct l2cap_pinfo *pi = l2cap_pi(sk); BT_DBG("sk %p", sk); if (parent) { sk->sk_type = parent->sk_type; pi->imtu = l2cap_pi(parent)->imtu; pi->omtu = l2cap_pi(parent)->omtu; pi->link_mode = l2cap_pi(parent)->link_mode; } else { pi->imtu = L2CAP_DEFAULT_MTU; pi->omtu = 0; pi->link_mode = 0; } /* Default config options */ pi->conf_mtu = L2CAP_DEFAULT_MTU; pi->flush_to = L2CAP_DEFAULT_FLUSH_TO; } static struct proto l2cap_proto = { .name = "L2CAP", .owner = THIS_MODULE, .obj_size = sizeof(struct l2cap_pinfo) }; static struct sock *l2cap_sock_alloc(struct socket *sock, int proto, unsigned int __nocast prio) { struct sock *sk; sk = sk_alloc(PF_BLUETOOTH, prio, &l2cap_proto, 1); if (!sk) return NULL; sock_init_data(sock, sk); INIT_LIST_HEAD(&bt_sk(sk)->accept_q); sk->sk_destruct = l2cap_sock_destruct; sk->sk_sndtimeo = L2CAP_CONN_TIMEOUT; sock_reset_flag(sk, SOCK_ZAPPED); sk->sk_protocol = proto; sk->sk_state = BT_OPEN; l2cap_sock_init_timer(sk); bt_sock_link(&l2cap_sk_list, sk); return sk; } static int l2cap_sock_create(struct socket *sock, int protocol) { struct sock *sk; BT_DBG("sock %p", sock); sock->state = SS_UNCONNECTED; if (sock->type != SOCK_SEQPACKET && sock->type != SOCK_DGRAM && sock->type != SOCK_RAW) return -ESOCKTNOSUPPORT; if (sock->type == SOCK_RAW && !capable(CAP_NET_RAW)) return -EPERM; sock->ops = &l2cap_sock_ops; sk = l2cap_sock_alloc(sock, protocol, GFP_KERNEL); if (!sk) return -ENOMEM; l2cap_sock_init(sk, NULL); return 0; } static int l2cap_sock_bind(struct socket *sock, struct sockaddr *addr, int addr_len) { struct sockaddr_l2 *la = (struct sockaddr_l2 *) addr; struct sock *sk = sock->sk; int err = 0; BT_DBG("sk %p, %s %d", sk, batostr(&la->l2_bdaddr), la->l2_psm); if (!addr || addr->sa_family != AF_BLUETOOTH) return -EINVAL; lock_sock(sk); if (sk->sk_state != BT_OPEN) { err = -EBADFD; goto done; } write_lock_bh(&l2cap_sk_list.lock); if (la->l2_psm && __l2cap_get_sock_by_addr(la->l2_psm, &la->l2_bdaddr)) { err = -EADDRINUSE; } else { /* Save source address */ bacpy(&bt_sk(sk)->src, &la->l2_bdaddr); l2cap_pi(sk)->psm = la->l2_psm; l2cap_pi(sk)->sport = la->l2_psm; sk->sk_state = BT_BOUND; } write_unlock_bh(&l2cap_sk_list.lock); done: release_sock(sk); return err; } static int l2cap_do_connect(struct sock *sk) { bdaddr_t *src = &bt_sk(sk)->src; bdaddr_t *dst = &bt_sk(sk)->dst; struct l2cap_conn *conn; struct hci_conn *hcon; struct hci_dev *hdev; int err = 0; BT_DBG("%s -> %s psm 0x%2.2x", batostr(src), batostr(dst), l2cap_pi(sk)->psm); if (!(hdev = hci_get_route(dst, src))) return -EHOSTUNREACH; hci_dev_lock_bh(hdev); err = -ENOMEM; hcon = hci_connect(hdev, ACL_LINK, dst); if (!hcon) goto done; conn = l2cap_conn_add(hcon, 0); if (!conn) { hci_conn_put(hcon); goto done; } err = 0; /* Update source addr of the socket */ bacpy(src, conn->src); l2cap_chan_add(conn, sk, NULL); sk->sk_state = BT_CONNECT; l2cap_sock_set_timer(sk, sk->sk_sndtimeo); if (hcon->state == BT_CONNECTED) { if (sk->sk_type == SOCK_SEQPACKET) { struct l2cap_conn_req req; l2cap_pi(sk)->ident = l2cap_get_ident(conn); req.scid = __cpu_to_le16(l2cap_pi(sk)->scid); req.psm = l2cap_pi(sk)->psm; l2cap_send_cmd(conn, l2cap_pi(sk)->ident, L2CAP_CONN_REQ, sizeof(req), &req); } else { l2cap_sock_clear_timer(sk); sk->sk_state = BT_CONNECTED; } } done: hci_dev_unlock_bh(hdev); hci_dev_put(hdev); return err; } static int l2cap_sock_connect(struct socket *sock, struct sockaddr *addr, int alen, int flags) { struct sockaddr_l2 *la = (struct sockaddr_l2 *) addr; struct sock *sk = sock->sk; int err = 0; lock_sock(sk); BT_DBG("sk %p", sk); if (addr->sa_family != AF_BLUETOOTH || alen < sizeof(struct sockaddr_l2)) { err = -EINVAL; goto done; } if (sk->sk_type == SOCK_SEQPACKET && !la->l2_psm) { err = -EINVAL; goto done; } switch(sk->sk_state) { case BT_CONNECT: case BT_CONNECT2: case BT_CONFIG: /* Already connecting */ goto wait; case BT_CONNECTED: /* Already connected */ goto done; case BT_OPEN: case BT_BOUND: /* Can connect */ break; default: err = -EBADFD; goto done; } /* Set destination address and psm */ bacpy(&bt_sk(sk)->dst, &la->l2_bdaddr); l2cap_pi(sk)->psm = la->l2_psm; if ((err = l2cap_do_connect(sk))) goto done; wait: err = bt_sock_wait_state(sk, BT_CONNECTED, sock_sndtimeo(sk, flags & O_NONBLOCK)); done: release_sock(sk); return err; } static int l2cap_sock_listen(struct socket *sock, int backlog) { struct sock *sk = sock->sk; int err = 0; BT_DBG("sk %p backlog %d", sk, backlog); lock_sock(sk); if (sk->sk_state != BT_BOUND || sock->type != SOCK_SEQPACKET) { err = -EBADFD; goto done; } if (!l2cap_pi(sk)->psm) { bdaddr_t *src = &bt_sk(sk)->src; u16 psm; err = -EINVAL; write_lock_bh(&l2cap_sk_list.lock); for (psm = 0x1001; psm < 0x1100; psm += 2) if (!__l2cap_get_sock_by_addr(psm, src)) { l2cap_pi(sk)->psm = htobs(psm); l2cap_pi(sk)->sport = htobs(psm); err = 0; break; } write_unlock_bh(&l2cap_sk_list.lock); if (err < 0) goto done; } sk->sk_max_ack_backlog = backlog; sk->sk_ack_backlog = 0; sk->sk_state = BT_LISTEN; done: release_sock(sk); return err; } static int l2cap_sock_accept(struct socket *sock, struct socket *newsock, int flags) { DECLARE_WAITQUEUE(wait, current); struct sock *sk = sock->sk, *nsk; long timeo; int err = 0; lock_sock(sk); if (sk->sk_state != BT_LISTEN) { err = -EBADFD; goto done; } timeo = sock_rcvtimeo(sk, flags & O_NONBLOCK); BT_DBG("sk %p timeo %ld", sk, timeo); /* Wait for an incoming connection. (wake-one). */ add_wait_queue_exclusive(sk->sk_sleep, &wait); while (!(nsk = bt_accept_dequeue(sk, newsock))) { set_current_state(TASK_INTERRUPTIBLE); if (!timeo) { err = -EAGAIN; break; } release_sock(sk); timeo = schedule_timeout(timeo); lock_sock(sk); if (sk->sk_state != BT_LISTEN) { err = -EBADFD; break; } if (signal_pending(current)) { err = sock_intr_errno(timeo); break; } } set_current_state(TASK_RUNNING); remove_wait_queue(sk->sk_sleep, &wait); if (err) goto done; newsock->state = SS_CONNECTED; BT_DBG("new socket %p", nsk); done: release_sock(sk); return err; } static int l2cap_sock_getname(struct socket *sock, struct sockaddr *addr, int *len, int peer) { struct sockaddr_l2 *la = (struct sockaddr_l2 *) addr; struct sock *sk = sock->sk; BT_DBG("sock %p, sk %p", sock, sk); addr->sa_family = AF_BLUETOOTH; *len = sizeof(struct sockaddr_l2); if (peer) bacpy(&la->l2_bdaddr, &bt_sk(sk)->dst); else bacpy(&la->l2_bdaddr, &bt_sk(sk)->src); la->l2_psm = l2cap_pi(sk)->psm; return 0; } static inline int l2cap_do_send(struct sock *sk, struct msghdr *msg, int len) { struct l2cap_conn *conn = l2cap_pi(sk)->conn; struct sk_buff *skb, **frag; int err, hlen, count, sent=0; struct l2cap_hdr *lh; BT_DBG("sk %p len %d", sk, len); /* First fragment (with L2CAP header) */ if (sk->sk_type == SOCK_DGRAM) hlen = L2CAP_HDR_SIZE + 2; else hlen = L2CAP_HDR_SIZE; count = min_t(unsigned int, (conn->mtu - hlen), len); skb = bt_skb_send_alloc(sk, hlen + count, msg->msg_flags & MSG_DONTWAIT, &err); if (!skb) return err; /* Create L2CAP header */ lh = (struct l2cap_hdr *) skb_put(skb, L2CAP_HDR_SIZE); lh->cid = __cpu_to_le16(l2cap_pi(sk)->dcid); lh->len = __cpu_to_le16(len + (hlen - L2CAP_HDR_SIZE)); if (sk->sk_type == SOCK_DGRAM) put_unaligned(l2cap_pi(sk)->psm, (u16 *) skb_put(skb, 2)); if (memcpy_fromiovec(skb_put(skb, count), msg->msg_iov, count)) { err = -EFAULT; goto fail; } sent += count; len -= count; /* Continuation fragments (no L2CAP header) */ frag = &skb_shinfo(skb)->frag_list; while (len) { count = min_t(unsigned int, conn->mtu, len); *frag = bt_skb_send_alloc(sk, count, msg->msg_flags & MSG_DONTWAIT, &err); if (!*frag) goto fail; if (memcpy_fromiovec(skb_put(*frag, count), msg->msg_iov, count)) { err = -EFAULT; goto fail; } sent += count; len -= count; frag = &(*frag)->next; } if ((err = hci_send_acl(conn->hcon, skb, 0)) < 0) goto fail; return sent; fail: kfree_skb(skb); return err; } static int l2cap_sock_sendmsg(struct kiocb *iocb, struct socket *sock, struct msghdr *msg, size_t len) { struct sock *sk = sock->sk; int err = 0; BT_DBG("sock %p, sk %p", sock, sk); if (sk->sk_err) return sock_error(sk); if (msg->msg_flags & MSG_OOB) return -EOPNOTSUPP; /* Check outgoing MTU */ if (sk->sk_type != SOCK_RAW && len > l2cap_pi(sk)->omtu) return -EINVAL; lock_sock(sk); if (sk->sk_state == BT_CONNECTED) err = l2cap_do_send(sk, msg, len); else err = -ENOTCONN; release_sock(sk); return err; } static int l2cap_sock_setsockopt(struct socket *sock, int level, int optname, char __user *optval, int optlen) { struct sock *sk = sock->sk; struct l2cap_options opts; int err = 0, len; u32 opt; BT_DBG("sk %p", sk); lock_sock(sk); switch (optname) { case L2CAP_OPTIONS: len = min_t(unsigned int, sizeof(opts), optlen); if (copy_from_user((char *) &opts, optval, len)) { err = -EFAULT; break; } l2cap_pi(sk)->imtu = opts.imtu; l2cap_pi(sk)->omtu = opts.omtu; break; case L2CAP_LM: if (get_user(opt, (u32 __user *) optval)) { err = -EFAULT; break; } l2cap_pi(sk)->link_mode = opt; break; default: err = -ENOPROTOOPT; break; } release_sock(sk); return err; } static int l2cap_sock_getsockopt(struct socket *sock, int level, int optname, char __user *optval, int __user *optlen) { struct sock *sk = sock->sk; struct l2cap_options opts; struct l2cap_conninfo cinfo; int len, err = 0; BT_DBG("sk %p", sk); if (get_user(len, optlen)) return -EFAULT; lock_sock(sk); switch (optname) { case L2CAP_OPTIONS: opts.imtu = l2cap_pi(sk)->imtu; opts.omtu = l2cap_pi(sk)->omtu; opts.flush_to = l2cap_pi(sk)->flush_to; opts.mode = 0x00; len = min_t(unsigned int, len, sizeof(opts)); if (copy_to_user(optval, (char *) &opts, len)) err = -EFAULT; break; case L2CAP_LM: if (put_user(l2cap_pi(sk)->link_mode, (u32 __user *) optval)) err = -EFAULT; break; case L2CAP_CONNINFO: if (sk->sk_state != BT_CONNECTED) { err = -ENOTCONN; break; } cinfo.hci_handle = l2cap_pi(sk)->conn->hcon->handle; memcpy(cinfo.dev_class, l2cap_pi(sk)->conn->hcon->dev_class, 3); len = min_t(unsigned int, len, sizeof(cinfo)); if (copy_to_user(optval, (char *) &cinfo, len)) err = -EFAULT; break; default: err = -ENOPROTOOPT; break; } release_sock(sk); return err; } static int l2cap_sock_shutdown(struct socket *sock, int how) { struct sock *sk = sock->sk; int err = 0; BT_DBG("sock %p, sk %p", sock, sk); if (!sk) return 0; lock_sock(sk); if (!sk->sk_shutdown) { sk->sk_shutdown = SHUTDOWN_MASK; l2cap_sock_clear_timer(sk); __l2cap_sock_close(sk, 0); if (sock_flag(sk, SOCK_LINGER) && sk->sk_lingertime) err = bt_sock_wait_state(sk, BT_CLOSED, sk->sk_lingertime); } release_sock(sk); return err; } static int l2cap_sock_release(struct socket *sock) { struct sock *sk = sock->sk; int err; BT_DBG("sock %p, sk %p", sock, sk); if (!sk) return 0; err = l2cap_sock_shutdown(sock, 2); sock_orphan(sk); l2cap_sock_kill(sk); return err; } /* ---- L2CAP channels ---- */ static struct sock *__l2cap_get_chan_by_dcid(struct l2cap_chan_list *l, u16 cid) { struct sock *s; for (s = l->head; s; s = l2cap_pi(s)->next_c) { if (l2cap_pi(s)->dcid == cid) break; } return s; } static struct sock *__l2cap_get_chan_by_scid(struct l2cap_chan_list *l, u16 cid) { struct sock *s; for (s = l->head; s; s = l2cap_pi(s)->next_c) { if (l2cap_pi(s)->scid == cid) break; } return s; } /* Find channel with given SCID. * Returns locked socket */ static inline struct sock *l2cap_get_chan_by_scid(struct l2cap_chan_list *l, u16 cid) { struct sock *s; read_lock(&l->lock); s = __l2cap_get_chan_by_scid(l, cid); if (s) bh_lock_sock(s); read_unlock(&l->lock); return s; } static struct sock *__l2cap_get_chan_by_ident(struct l2cap_chan_list *l, u8 ident) { struct sock *s; for (s = l->head; s; s = l2cap_pi(s)->next_c) { if (l2cap_pi(s)->ident == ident) break; } return s; } static inline struct sock *l2cap_get_chan_by_ident(struct l2cap_chan_list *l, u8 ident) { struct sock *s; read_lock(&l->lock); s = __l2cap_get_chan_by_ident(l, ident); if (s) bh_lock_sock(s); read_unlock(&l->lock); return s; } static u16 l2cap_alloc_cid(struct l2cap_chan_list *l) { u16 cid = 0x0040; for (; cid < 0xffff; cid++) { if(!__l2cap_get_chan_by_scid(l, cid)) return cid; } return 0; } static inline void __l2cap_chan_link(struct l2cap_chan_list *l, struct sock *sk) { sock_hold(sk); if (l->head) l2cap_pi(l->head)->prev_c = sk; l2cap_pi(sk)->next_c = l->head; l2cap_pi(sk)->prev_c = NULL; l->head = sk; } static inline void l2cap_chan_unlink(struct l2cap_chan_list *l, struct sock *sk) { struct sock *next = l2cap_pi(sk)->next_c, *prev = l2cap_pi(sk)->prev_c; write_lock(&l->lock); if (sk == l->head) l->head = next; if (next) l2cap_pi(next)->prev_c = prev; if (prev) l2cap_pi(prev)->next_c = next; write_unlock(&l->lock); __sock_put(sk); } static void __l2cap_chan_add(struct l2cap_conn *conn, struct sock *sk, struct sock *parent) { struct l2cap_chan_list *l = &conn->chan_list; BT_DBG("conn %p, psm 0x%2.2x, dcid 0x%4.4x", conn, l2cap_pi(sk)->psm, l2cap_pi(sk)->dcid); l2cap_pi(sk)->conn = conn; if (sk->sk_type == SOCK_SEQPACKET) { /* Alloc CID for connection-oriented socket */ l2cap_pi(sk)->scid = l2cap_alloc_cid(l); } else if (sk->sk_type == SOCK_DGRAM) { /* Connectionless socket */ l2cap_pi(sk)->scid = 0x0002; l2cap_pi(sk)->dcid = 0x0002; l2cap_pi(sk)->omtu = L2CAP_DEFAULT_MTU; } else { /* Raw socket can send/recv signalling messages only */ l2cap_pi(sk)->scid = 0x0001; l2cap_pi(sk)->dcid = 0x0001; l2cap_pi(sk)->omtu = L2CAP_DEFAULT_MTU; } __l2cap_chan_link(l, sk); if (parent) bt_accept_enqueue(parent, sk); } /* Delete channel. * Must be called on the locked socket. */ static void l2cap_chan_del(struct sock *sk, int err) { struct l2cap_conn *conn = l2cap_pi(sk)->conn; struct sock *parent = bt_sk(sk)->parent; l2cap_sock_clear_timer(sk); BT_DBG("sk %p, conn %p, err %d", sk, conn, err); if (conn) { /* Unlink from channel list */ l2cap_chan_unlink(&conn->chan_list, sk); l2cap_pi(sk)->conn = NULL; hci_conn_put(conn->hcon); } sk->sk_state = BT_CLOSED; sock_set_flag(sk, SOCK_ZAPPED); if (err) sk->sk_err = err; if (parent) { bt_accept_unlink(sk); parent->sk_data_ready(parent, 0); } else sk->sk_state_change(sk); } static void l2cap_conn_ready(struct l2cap_conn *conn) { struct l2cap_chan_list *l = &conn->chan_list; struct sock *sk; BT_DBG("conn %p", conn); read_lock(&l->lock); for (sk = l->head; sk; sk = l2cap_pi(sk)->next_c) { bh_lock_sock(sk); if (sk->sk_type != SOCK_SEQPACKET) { l2cap_sock_clear_timer(sk); sk->sk_state = BT_CONNECTED; sk->sk_state_change(sk); } else if (sk->sk_state == BT_CONNECT) { struct l2cap_conn_req req; l2cap_pi(sk)->ident = l2cap_get_ident(conn); req.scid = __cpu_to_le16(l2cap_pi(sk)->scid); req.psm = l2cap_pi(sk)->psm; l2cap_send_cmd(conn, l2cap_pi(sk)->ident, L2CAP_CONN_REQ, sizeof(req), &req); } bh_unlock_sock(sk); } read_unlock(&l->lock); } /* Notify sockets that we cannot guaranty reliability anymore */ static void l2cap_conn_unreliable(struct l2cap_conn *conn, int err) { struct l2cap_chan_list *l = &conn->chan_list; struct sock *sk; BT_DBG("conn %p", conn); read_lock(&l->lock); for (sk = l->head; sk; sk = l2cap_pi(sk)->next_c) { if (l2cap_pi(sk)->link_mode & L2CAP_LM_RELIABLE) search_pattern = "(|" for field in search_fields: search_pattern += "(" + field + "=%(match)s)" search_pattern += ")" gen_search_pattern = lambda word: search_pattern % {'match':word} # construct the giant match for all words exact_match_filter = "(&" partial_match_filter = "(|" for word in criteria_words: exact_match_filter += gen_search_pattern(word) partial_match_filter += gen_search_pattern("*%s*" % word) exact_match_filter += ")" partial_match_filter += ")" return (exact_match_filter, partial_match_filter) # TODO: rethink the get_entry vs get_list API calls. # they currently restrict the data coming back without # restricting scope. For now adding a get_base/sub_entry() # calls, but the API isn't great. def get_entry (base, scope, searchfilter, sattrs=None): """Get a specific entry (with a parametized scope). Return as a dict of values. Multi-valued fields are represented as lists. """ ent="" ent = context.ldap.conn.getEntry(base, scope, searchfilter, sattrs) return convert_entry(ent) def get_base_entry (base, searchfilter, sattrs=None): """Get a specific entry (with a scope of BASE). Return as a dict of values. Multi-valued fields are represented as lists. """ return get_entry(base, ldap.SCOPE_BASE, searchfilter, sattrs) def get_sub_entry (base, searchfilter, sattrs=None): """Get a specific entry (with a scope of SUB). Return as a dict of values. Multi-valued fields are represented as lists. """ return get_entry(base, ldap.SCOPE_SUBTREE, searchfilter, sattrs) def get_one_entry (base, searchfilter, sattrs=None): """Get the children of an entry (with a scope of ONE). Return as a list of dict of values. Multi-valued fields are represented as lists. """ return get_list(base, searchfilter, sattrs, ldap.SCOPE_ONELEVEL) def get_list (base, searchfilter, sattrs=None, scope=ldap.SCOPE_SUBTREE): """Gets a list of entries. Each is converted to a dict of values. Multi-valued fields are represented as lists. """ entries = [] entries = context.ldap.conn.getList(base, scope, searchfilter, sattrs) return map(convert_entry, entries) def has_nsaccountlock(dn): """Check to see if an entry has the nsaccountlock attribute. This attribute is provided by the Class of Service plugin so doing a search isn't enough. It is provided by the two entries cn=inactivated and cn=activated. So if the entry has the attribute and isn't in either cn=activated or cn=inactivated then the attribute must be in the entry itself. Returns True or False """ # First get the entry. If it doesn't have nsaccountlock at all we # can exit early. entry = get_entry_by_dn(dn, ['dn', 'nsaccountlock', 'memberof']) if not entry.get('nsaccountlock'): return False # Now look to see if they are in activated or inactivated # entry is a member memberof = entry.get('memberof') if isinstance(memberof, basestring): memberof = [memberof] for m in memberof: inactivated = m.find("cn=inactivated") activated = m.find("cn=activated") # if they are in either group that means that the nsaccountlock # value comes from there, otherwise it must be in this entry. if inactivated >= 0 or activated >= 0: return False return True # General searches def get_entry_by_dn (dn, sattrs=None): """Get a specific entry. Return as a dict of values. Multi-valued fields are represented as lists. """ searchfilter = "(objectClass=*)" api.log.info("IPA: get_entry_by_dn '%s'" % dn) return get_base_entry(dn, searchfilter, sattrs) def get_entry_by_cn (cn, sattrs): """Get a specific entry by cn. Return as a dict of values. Multi-valued fields are represented as lists. """ api.log.info("IPA: get_entry_by_cn '%s'" % cn) # cn = self.__safe_filter(cn) searchfilter = "(cn=%s)" % cn return get_sub_entry("cn=accounts," + api.env.basedn, searchfilter, sattrs) def get_user_by_uid(uid, sattrs): """Get a specific user's entry.""" # FIXME: should accept a container to look in # uid = self.__safe_filter(uid) searchfilter = "(&(uid=%s)(objectclass=posixAccount))" % uid return get_sub_entry("cn=accounts," + api.env.basedn, searchfilter, sattrs) # User support def entry_exists(dn): """Return True if the entry exists, False otherwise.""" try: get_base_entry(dn, "objectclass=*", ['dn','objectclass']) return True except errors.NotFound: return False def get_user_by_uid (uid, sattrs): """Get a specific user's entry. Return as a dict of values. Multi-valued fields are represented as lists. """ if not isinstance(uid,basestring) or len(uid) == 0: raise SyntaxError("uid is not a string") # raise ipaerror.gen_exception(ipaerror.INPUT_INVALID_PARAMETER) if sattrs is not None and not isinstance(sattrs,list): raise SyntaxError("sattrs is not a list") # raise ipaerror.gen_exception(ipaerror.INPUT_INVALID_PARAMETER) api.log.info("IPA: get_user_by_uid '%s'" % uid) # uid = self.__safe_filter(uid) searchfilter = "(uid=" + uid + ")" return get_sub_entry("cn=accounts," + api.env.basedn, searchfilter, sattrs) def uid_too_long(uid): """Verify that the new uid is within the limits we set. This is a very narrow test. Returns True if it is longer than allowed False otherwise """ if not isinstance(uid,basestring) or len(uid) == 0: # It is bad, but not too long return False api.log.debug("IPA: __uid_too_long(%s)" % uid) try: config = get_ipa_config() maxlen = int(config.get('ipamaxusernamelength', 0)) if maxlen > 0 and len(uid) > maxlen: return True except Exception, e: api.log.debug("There was a problem " + str(e)) pass return False def update_entry (entry, remove_keys=[]): """Update an LDAP entry entry is a dict remove_keys is a list of attributes to remove from this entry This refreshes the record from LDAP in order to obtain the list of attributes that has changed. It only retrieves the attributes that are in the update so attributes aren't inadvertantly lost. """ assert type(remove_keys) is list attrs = entry.keys() o = get_base_entry(entry['dn'], "objectclass=*", attrs + remove_keys) oldentry = convert_scalar_values(o) newentry = convert_scalar_values(entry) # Should be able to get this from either the old or new entry # but just in case someone has decided to try changing it, use the # original try: moddn = oldentry['dn'] except KeyError, e: # FIXME: return a missing DN error message raise e return context.ldap.conn.updateEntry(moddn, oldentry, newentry) def add_entry(entry): """Add a new entry""" return context.ldap.conn.addEntry(entry) def delete_entry(dn): """Remove an entry""" return context.ldap.conn.deleteEntry(dn) # FIXME, get time and search limit from cn=ipaconfig def search(base, filter, attributes, timelimit=1, sizelimit=3000, scope=ldap.SCOPE_SUBTREE): """Perform an LDAP query""" try: timelimit = float(timelimit) results = context.ldap.conn.getListAsync(base, scope, filter, attributes, 0, None, None, timelimit, sizelimit) except ldap.NO_SUCH_OBJECT: raise errors.NotFound(reason=filter) counter = results[0] entries = [counter] for r in results[1:]: entries.append(convert_entry(r)) return entries def uniq_list(x): """Return a unique list, preserving order and ignoring case""" myset = {} return [myset.setdefault(e.lower(),e) for e in x if e.lower() not in myset] def get_schema(): """Retrieves the current LDAP schema from the LDAP server.""" schema_entry = get_base_entry("", "objectclass=*", ['dn','subschemasubentry']) schema_cn = schema_entry.get('subschemasubentry') schema = get_base_entry(schema_cn, "objectclass=*", ['*']) return schema def get_objectclasses(): """Returns a list of available objectclasses that the LDAP server supports. This parses out the syntax, attributes, etc and JUST returns a lower-case list of the names.""" schema = get_schema() objectclasses = schema.get('objectclasses') # Convert this list into something more readable result = [] for i in range(len(objectclasses)): oc = objectclasses[i].lower().split(" ") result.append(oc[3].replace("'","")) return result def get_ipa_config(): """Retrieve the IPA configuration""" searchfilter = "cn=ipaconfig" try: config = get_sub_entry("cn=etc," + api.env.basedn, searchfilter) except ldap.NO_SUCH_OBJECT: # FIXME raise errors.NotFound(reason="IPA configuration cannot be loaded") return config def modify_password(dn, oldpass, newpass): return context.ldap.conn.modifyPassword(dn, oldpass, newpass) def mark_entry_active (dn): """Mark an entry as active in LDAP.""" # This can be tricky. The entry itself can be marked inactive # by being in the inactivated group. It can also be inactivated by # being the member of an inactive group. # # First we try to remove the entry from the inactivated group. Then # if it is still inactive we have to add it to the activated group # which will override the group membership. res = "" # First, check the entry status entry = get_entry_by_dn(dn, ['dn', 'nsAccountlock']) if entry.get('nsaccountlock', 'false').lower() == "false": api.log.debug("IPA: already active") raise errors.AlreadyActive() if has_nsaccountlock(dn): api.log.debug("IPA: appears to have the nsaccountlock attribute") raise errors.HasNSAccountLock() group = get_entry_by_cn("inactivated", None) try: remove_member_from_group(entry.get('dn'), group.get('dn')) except errors.NotGroupMember: # Perhaps the user is there as a result of group membership pass # Now they aren't a member of inactivated directly, what is the status # now? entry = get_entry_by_dn(dn, ['dn', 'nsAccountlock']) if entry.get('nsaccountlock', 'false').lower() == "false": # great, we're done api.log.debug("IPA: removing from inactivated did it.") return True # So still inactive, add them to activated group = get_entry_by_cn("activated", None) res = add_member_to_group(dn, group.get('dn')) api.log.debug("IPA: added to activated.") return res def mark_entry_inactive (dn): """Mark an entry as inactive in LDAP.""" entry = get_entry_by_dn(dn, ['dn', 'nsAccountlock', 'memberOf']) if entry.get('nsaccountlock', 'false').lower() == "true": api.log.debug("IPA: already marked as inactive") raise errors.AlreadyInactive() if has_nsaccountlock(dn): api.log.debug("IPA: appears to have the nsaccountlock attribute") raise errors.HasNSAccountLock() # First see if they are in the activated group as this will override # the our inactivation. group = get_entry_by_cn("activated", None) try: remove_member_from_group(dn, group.get('dn')) except errors.NotGroupMember: # this is fine, they may not be explicitly in this group pass # Now add them to inactivated group = get_entry_by_cn("inactivated", None) res = add_member_to_group(dn, group.get('dn')) return res def add_member_to_group(member_dn, group_dn, memberattr='member'): """ Add a member to an existing group. """ api.log.info("IPA: add_member_to_group '%s' to '%s'" % (member_dn, group_dn)) if member_dn.lower() == group_dn.lower(): # You can't add a group to itself raise errors.RecursiveGroup() group = get_entry_by_dn(group_dn, None) if group is None: raise errors.NotFound(reason="cannot find group %s" % group_dn) # check to make sure member_dn exists member_entry = get_base_entry(member_dn, "(objectClass=*)", ['dn','objectclass']) if not member_entry: raise errors.NotFound(reason="cannot find member %s" % member_dn) # Add the new member to the group member attribute members = group.get(memberattr, []) if isinstance(members, basestring): members = [members] members.append(member_dn) group[memberattr] = members return update_entry(group) def remove_member_from_group(member_dn, group_dn, memberattr='member'): """Remove a member_dn from an existing group.""" group = get_entry_by_dn(group_dn, None) if group is None: raise errors.NotFound(reason="cannot find group %s" % group_dn) """ if group.get('cn') == "admins": member = get_entry_by_dn(member_dn, ['dn','uid']) if member.get('uid') == "admin": raise ipaerror.gen_exception(ipaerror.INPUT_ADMIN_REQUIRED_IN_ADMINS) """ api.log.info("IPA: remove_member_from_group '%s' from '%s'" % (member_dn, group_dn)) members = group.get(memberattr, False) if not members: raise errors.NotGroupMember() if isinstance(members,basestring): members = [members] for i in range(len(members)): members[i] = ipaldap.IPAdmin.normalizeDN(members[i]) try: members.remove(member_dn) except ValueError: raise errors.NotGroupMember() except Exception, e: raise e group[memberattr] = members return update_entry(group)