/* BlueZ - Bluetooth protocol stack for Linux Copyright (C) 2000-2001 Qualcomm Incorporated
Written 2000,2001 by Maxim Krasnyansky <maxk@qualcomm.com>
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.
*/
/* Check if peer credentials is set */ if (!sk->sk_peer_pid) { /* Check if parent peer credentials is set */ if (bt_sk(sk)->parent && bt_sk(sk)->parent->sk_peer_pid)
sk = bt_sk(sk)->parent; else return;
}
/* Check if scm_creds already set */ if (creds->pid == pid_vnr(sk->sk_peer_pid)) return;
switch (hci_skb_pkt_type(skb)) { case HCI_COMMAND_PKT:
opcode = cpu_to_le16(HCI_MON_COMMAND_PKT); break; case HCI_EVENT_PKT:
opcode = cpu_to_le16(HCI_MON_EVENT_PKT); break; case HCI_ACLDATA_PKT: if (bt_cb(skb)->incoming)
opcode = cpu_to_le16(HCI_MON_ACL_RX_PKT); else
opcode = cpu_to_le16(HCI_MON_ACL_TX_PKT); break; case HCI_SCODATA_PKT: if (bt_cb(skb)->incoming)
opcode = cpu_to_le16(HCI_MON_SCO_RX_PKT); else
opcode = cpu_to_le16(HCI_MON_SCO_TX_PKT); break; case HCI_ISODATA_PKT: if (bt_cb(skb)->incoming)
opcode = cpu_to_le16(HCI_MON_ISO_RX_PKT); else
opcode = cpu_to_le16(HCI_MON_ISO_TX_PKT); break; case HCI_DRV_PKT: if (bt_cb(skb)->incoming)
opcode = cpu_to_le16(HCI_MON_DRV_RX_PKT); else
opcode = cpu_to_le16(HCI_MON_DRV_TX_PKT); break; case HCI_DIAG_PKT:
opcode = cpu_to_le16(HCI_MON_VENDOR_DIAG); break; default: return;
}
/* Create a private copy with headroom */
skb_copy = __pskb_copy_fclone(skb, HCI_MON_HDR_SIZE, GFP_ATOMIC, true); if (!skb_copy) return;
hci_sock_copy_creds(skb->sk, skb_copy);
/* Put header before the data */
hdr = skb_push(skb_copy, HCI_MON_HDR_SIZE);
hdr->opcode = opcode;
hdr->index = cpu_to_le16(hdev->id);
hdr->len = cpu_to_le16(skb->len);
/* No message needed when cookie is not present */ if (!hci_pi(sk)->cookie) return NULL;
switch (hci_pi(sk)->channel) { case HCI_CHANNEL_RAW:
format = 0x0000;
ver[0] = BT_SUBSYS_VERSION;
put_unaligned_le16(BT_SUBSYS_REVISION, ver + 1); break; case HCI_CHANNEL_USER:
format = 0x0001;
ver[0] = BT_SUBSYS_VERSION;
put_unaligned_le16(BT_SUBSYS_REVISION, ver + 1); break; case HCI_CHANNEL_CONTROL:
format = 0x0002;
mgmt_fill_version_info(ver); break; default: /* No message for unsupported format */ return NULL;
}
skb = bt_skb_alloc(14 + TASK_COMM_LEN, GFP_ATOMIC); if (!skb) return NULL;
/* No message needed when cookie is not present */ if (!hci_pi(sk)->cookie) return NULL;
switch (hci_pi(sk)->channel) { case HCI_CHANNEL_RAW: case HCI_CHANNEL_USER: case HCI_CHANNEL_CONTROL: break; default: /* No message for unsupported format */ return NULL;
}
skb = bt_skb_alloc(4, GFP_ATOMIC); if (!skb) return NULL;
/* Wake up sockets using this dead device */
read_lock(&hci_sk_list.lock);
sk_for_each(sk, &hci_sk_list.head) { if (hci_pi(sk)->hdev == hdev) {
sk->sk_err = EPIPE;
sk->sk_state_change(sk);
}
}
read_unlock(&hci_sk_list.lock);
}
}
switch (hci_pi(sk)->channel) { case HCI_CHANNEL_MONITOR:
atomic_dec(&monitor_promisc); break; case HCI_CHANNEL_RAW: case HCI_CHANNEL_USER: case HCI_CHANNEL_CONTROL: /* Send event to monitor */
skb = create_monitor_ctrl_close(sk); if (skb) {
hci_send_to_channel(HCI_CHANNEL_MONITOR, skb,
HCI_SOCK_TRUSTED, NULL);
kfree_skb(skb);
}
hci_sock_free_cookie(sk); break;
}
bt_sock_unlink(&hci_sk_list, sk);
hdev = hci_pi(sk)->hdev; if (hdev) { if (hci_pi(sk)->channel == HCI_CHANNEL_USER &&
!hci_dev_test_flag(hdev, HCI_UNREGISTER)) { /* When releasing a user channel exclusive access, * call hci_dev_do_close directly instead of calling * hci_dev_close to ensure the exclusive access will * be released and the controller brought back down. * * The checking of HCI_AUTO_OFF is not needed in this * case since it will have been cleared already when * opening the user channel. * * Make sure to also check that we haven't already * unregistered since all the cleanup will have already * been complete and hdev will get released when we put * below.
*/
hci_dev_do_close(hdev);
hci_dev_clear_flag(hdev, HCI_USER_CHANNEL);
mgmt_index_added(hdev);
}
/* Make sure the cmd is valid before doing anything */ switch (cmd) { case HCIGETDEVLIST: case HCIGETDEVINFO: case HCIGETCONNLIST: case HCIDEVUP: case HCIDEVDOWN: case HCIDEVRESET: case HCIDEVRESTAT: case HCISETSCAN: case HCISETAUTH: case HCISETENCRYPT: case HCISETPTYPE: case HCISETLINKPOL: case HCISETLINKMODE: case HCISETACLMTU: case HCISETSCOMTU: case HCIINQUIRY: case HCISETRAW: case HCIGETCONNINFO: case HCIGETAUTHINFO: case HCIBLOCKADDR: case HCIUNBLOCKADDR: break; default: return -ENOIOCTLCMD;
}
/* When calling an ioctl on an unbound raw socket, then ensure * that the monitor gets informed. Ensure that the resulting event * is only send once by checking if the cookie exists or not. The * socket cookie will be only ever generated once for the lifetime * of a given socket.
*/ if (hci_sock_gen_cookie(sk)) { struct sk_buff *skb;
/* Perform careful checks before setting the HCI_SOCK_TRUSTED * flag. Make sure that not only the current task but also * the socket opener has the required capability, since * privileged programs can be tricked into making ioctl calls * on HCI sockets, and the socket should not be marked as * trusted simply because the ioctl caller is privileged.
*/ if (sk_capable(sk, CAP_NET_ADMIN))
hci_sock_set_flag(sk, HCI_SOCK_TRUSTED);
/* Send event to monitor */
skb = create_monitor_ctrl_open(sk); if (skb) {
hci_send_to_channel(HCI_CHANNEL_MONITOR, skb,
HCI_SOCK_TRUSTED, NULL);
kfree_skb(skb);
}
}
release_sock(sk);
switch (cmd) { case HCIGETDEVLIST: return hci_get_dev_list(argp);
case HCIGETDEVINFO: return hci_get_dev_info(argp);
case HCIGETCONNLIST: return hci_get_conn_list(argp);
case HCIDEVUP: if (!capable(CAP_NET_ADMIN)) return -EPERM; return hci_dev_open(arg);
case HCIDEVDOWN: if (!capable(CAP_NET_ADMIN)) return -EPERM; return hci_dev_close(arg);
case HCIDEVRESET: if (!capable(CAP_NET_ADMIN)) return -EPERM; return hci_dev_reset(arg);
case HCIDEVRESTAT: if (!capable(CAP_NET_ADMIN)) return -EPERM; return hci_dev_reset_stat(arg);
case HCISETSCAN: case HCISETAUTH: case HCISETENCRYPT: case HCISETPTYPE: case HCISETLINKPOL: case HCISETLINKMODE: case HCISETACLMTU: case HCISETSCOMTU: if (!capable(CAP_NET_ADMIN)) return -EPERM; return hci_dev_cmd(cmd, argp);
case HCIINQUIRY: return hci_inquiry(argp);
}
lock_sock(sk);
err = hci_sock_bound_ioctl(sk, cmd, arg);
done:
release_sock(sk); return err;
}
#ifdef CONFIG_COMPAT staticint hci_sock_compat_ioctl(struct socket *sock, unsignedint cmd, unsignedlong arg)
{ switch (cmd) { case HCIDEVUP: case HCIDEVDOWN: case HCIDEVRESET: case HCIDEVRESTAT: return hci_sock_ioctl(sock, cmd, arg);
}
if (haddr.hci_family != AF_BLUETOOTH) return -EINVAL;
lock_sock(sk);
/* Allow detaching from dead device and attaching to alive device, if * the caller wants to re-bind (instead of close) this socket in * response to hci_sock_dev_event(HCI_DEV_UNREG) notification.
*/
hdev = hci_pi(sk)->hdev; if (hdev && hci_dev_test_flag(hdev, HCI_UNREGISTER)) {
hci_pi(sk)->hdev = NULL;
sk->sk_state = BT_OPEN;
hci_dev_put(hdev);
}
hdev = NULL;
switch (haddr.hci_channel) { case HCI_CHANNEL_RAW: if (hci_pi(sk)->hdev) {
err = -EALREADY; goto done;
}
if (haddr.hci_dev != HCI_DEV_NONE) {
hdev = hci_dev_get(haddr.hci_dev); if (!hdev) {
err = -ENODEV; goto done;
}
atomic_inc(&hdev->promisc);
}
hci_pi(sk)->channel = haddr.hci_channel;
if (!hci_sock_gen_cookie(sk)) { /* In the case when a cookie has already been assigned, * then there has been already an ioctl issued against * an unbound socket and with that triggered an open * notification. Send a close notification first to * allow the state transition to bounded.
*/
skb = create_monitor_ctrl_close(sk); if (skb) {
hci_send_to_channel(HCI_CHANNEL_MONITOR, skb,
HCI_SOCK_TRUSTED, NULL);
kfree_skb(skb);
}
}
if (capable(CAP_NET_ADMIN))
hci_sock_set_flag(sk, HCI_SOCK_TRUSTED);
hci_pi(sk)->hdev = hdev;
/* Send event to monitor */
skb = create_monitor_ctrl_open(sk); if (skb) {
hci_send_to_channel(HCI_CHANNEL_MONITOR, skb,
HCI_SOCK_TRUSTED, NULL);
kfree_skb(skb);
} break;
case HCI_CHANNEL_USER: if (hci_pi(sk)->hdev) {
err = -EALREADY; goto done;
}
err = hci_dev_open(hdev->id); if (err) { if (err == -EALREADY) { /* In case the transport is already up and * running, clear the error here. * * This can happen when opening a user * channel and HCI_AUTO_OFF grace period * is still active.
*/
err = 0;
} else {
hci_dev_clear_flag(hdev, HCI_USER_CHANNEL);
mgmt_index_added(hdev);
hci_dev_put(hdev); goto done;
}
}
hci_pi(sk)->channel = haddr.hci_channel;
if (!hci_sock_gen_cookie(sk)) { /* In the case when a cookie has already been assigned, * this socket will transition from a raw socket into * a user channel socket. For a clean transition, send * the close notification first.
*/
skb = create_monitor_ctrl_close(sk); if (skb) {
hci_send_to_channel(HCI_CHANNEL_MONITOR, skb,
HCI_SOCK_TRUSTED, NULL);
kfree_skb(skb);
}
}
/* The user channel is restricted to CAP_NET_ADMIN * capabilities and with that implicitly trusted.
*/
hci_sock_set_flag(sk, HCI_SOCK_TRUSTED);
hci_pi(sk)->hdev = hdev;
/* Send event to monitor */
skb = create_monitor_ctrl_open(sk); if (skb) {
hci_send_to_channel(HCI_CHANNEL_MONITOR, skb,
HCI_SOCK_TRUSTED, NULL);
kfree_skb(skb);
}
atomic_inc(&hdev->promisc); break;
case HCI_CHANNEL_MONITOR: if (haddr.hci_dev != HCI_DEV_NONE) {
err = -EINVAL; goto done;
}
if (!capable(CAP_NET_RAW)) {
err = -EPERM; goto done;
}
hci_pi(sk)->channel = haddr.hci_channel;
/* The monitor interface is restricted to CAP_NET_RAW * capabilities and with that implicitly trusted.
*/
hci_sock_set_flag(sk, HCI_SOCK_TRUSTED);
send_monitor_note(sk, "Linux version %s (%s)",
init_utsname()->release,
init_utsname()->machine);
send_monitor_note(sk, "Bluetooth subsystem version %u.%u",
BT_SUBSYS_VERSION, BT_SUBSYS_REVISION);
send_monitor_replay(sk);
send_monitor_control_replay(sk);
atomic_inc(&monitor_promisc); break;
case HCI_CHANNEL_LOGGING: if (haddr.hci_dev != HCI_DEV_NONE) {
err = -EINVAL; goto done;
}
if (!capable(CAP_NET_ADMIN)) {
err = -EPERM; goto done;
}
hci_pi(sk)->channel = haddr.hci_channel; break;
default: if (!hci_mgmt_chan_find(haddr.hci_channel)) {
err = -EINVAL; goto done;
}
/* Users with CAP_NET_ADMIN capabilities are allowed * access to all management commands and events. For * untrusted users the interface is restricted and * also only untrusted events are sent.
*/ if (capable(CAP_NET_ADMIN))
hci_sock_set_flag(sk, HCI_SOCK_TRUSTED);
hci_pi(sk)->channel = haddr.hci_channel;
/* At the moment the index and unconfigured index events * are enabled unconditionally. Setting them on each * socket when binding keeps this functionality. They * however might be cleared later and then sending of these * events will be disabled, but that is then intentional. * * This also enables generic events that are safe to be * received by untrusted users. Example for such events * are changes to settings, class of device, name etc.
*/ if (hci_pi(sk)->channel == HCI_CHANNEL_CONTROL) { if (!hci_sock_gen_cookie(sk)) { /* In the case when a cookie has already been * assigned, this socket will transition from * a raw socket into a control socket. To * allow for a clean transition, send the * close notification first.
*/
skb = create_monitor_ctrl_close(sk); if (skb) {
hci_send_to_channel(HCI_CHANNEL_MONITOR, skb,
HCI_SOCK_TRUSTED, NULL);
kfree_skb(skb);
}
}
/* Send event to monitor */
skb = create_monitor_ctrl_open(sk); if (skb) {
hci_send_to_channel(HCI_CHANNEL_MONITOR, skb,
HCI_SOCK_TRUSTED, NULL);
kfree_skb(skb);
}
/* The logging frame consists at minimum of the standard header, * the priority byte, the ident length byte and at least one string * terminator NUL byte. Anything shorter are invalid packets.
*/ if (skb->len < sizeof(*hdr) + 3) return -EINVAL;
hdr = (void *)skb->data;
if (__le16_to_cpu(hdr->len) != skb->len - sizeof(*hdr)) return -EINVAL;
/* Only the priorities 0-7 are valid and with that any other * value results in an invalid packet. * * The priority byte is followed by an ident length byte and * the NUL terminated ident string. Check that the ident * length is not overflowing the packet and also that the * ident string itself is NUL terminated. In case the ident * length is zero, the length value actually doubles as NUL * terminator identifier. * * The message follows the ident string (if present) and * must be NUL terminated. Otherwise it is not a valid packet.
*/ if (priority > 7 || skb->data[skb->len - 1] != 0x00 ||
ident_len > skb->len - sizeof(*hdr) - 3 ||
skb->data[sizeof(*hdr) + ident_len + 1] != 0x00) return -EINVAL;
} else { return -EINVAL;
}
index = __le16_to_cpu(hdr->index);
if (index != MGMT_INDEX_NONE) {
hdev = hci_dev_get(index); if (!hdev) return -ENODEV;
} else {
hdev = NULL;
}
if (hci_pi(sk)->channel == HCI_CHANNEL_USER) { /* No permission check is needed for user channel * since that gets enforced when binding the socket. * * However check that the packet type is valid.
*/ if (hci_skb_pkt_type(skb) != HCI_COMMAND_PKT &&
hci_skb_pkt_type(skb) != HCI_ACLDATA_PKT &&
hci_skb_pkt_type(skb) != HCI_SCODATA_PKT &&
hci_skb_pkt_type(skb) != HCI_ISODATA_PKT &&
hci_skb_pkt_type(skb) != HCI_DRV_PKT) {
err = -EINVAL; goto drop;
}
staticint hci_sock_setsockopt(struct socket *sock, int level, int optname,
sockptr_t optval, unsignedint optlen)
{ struct sock *sk = sock->sk; int err = 0;
u16 opt;
BT_DBG("sk %p, opt %d", sk, optname);
if (level == SOL_HCI) return hci_sock_setsockopt_old(sock, level, optname, optval,
optlen);
if (level != SOL_BLUETOOTH) return -ENOPROTOOPT;
lock_sock(sk);
switch (optname) { case BT_SNDMTU: case BT_RCVMTU: switch (hci_pi(sk)->channel) { /* Don't allow changing MTU for channels that are meant for HCI * traffic only.
*/ case HCI_CHANNEL_RAW: case HCI_CHANNEL_USER:
err = -ENOPROTOOPT; goto done;
}
err = copy_safe_from_sockptr(&opt, sizeof(opt), optval, optlen); if (err) break;
hci_pi(sk)->mtu = opt; break;
default:
err = -ENOPROTOOPT; break;
}
done:
release_sock(sk); return err;
}
staticint hci_sock_getsockopt_old(struct socket *sock, int level, int optname, char __user *optval, int __user *optlen)
{ struct hci_ufilter uf; struct sock *sk = sock->sk; int len, opt, err = 0;
Die Informationen auf dieser Webseite wurden
nach bestem Wissen sorgfältig zusammengestellt. Es wird jedoch weder Vollständigkeit, noch Richtigkeit,
noch Qualität der bereit gestellten Informationen zugesichert.
Bemerkung:
Die farbliche Syntaxdarstellung und die Messung sind noch experimentell.