static int cmd_set_sec_level(char *level) { BtIOSecLevel sec_level; GError *gerr = NULL; if (strcasecmp(level, "low") == 0) { sec_level = BT_IO_SEC_LOW; } else if (strcasecmp(level, "medium") == 0) { sec_level = BT_IO_SEC_MEDIUM; } else if (strcasecmp(level, "high") == 0) { sec_level = BT_IO_SEC_HIGH; } else { printf("Invalid security level\n"); return -1; } if (get_state() != STATE_CONNECTED) { printf("Device not connected\n"); return -2; } bt_io_set(iochannel, &gerr, BT_IO_OPT_SEC_LEVEL, sec_level, BT_IO_OPT_INVALID); if (gerr) { printf("Error: %s\n", gerr->message); g_error_free(gerr); return -3; } return 0; }
static void confirm_cb(GIOChannel *io, gpointer user_data) { char addr[18]; struct io_data *data = user_data; GError *err = NULL; if (!bt_io_get(io, &err, BT_IO_OPT_DEST, addr, BT_IO_OPT_INVALID)) { printf("bt_io_get(OPT_DEST): %s\n", err->message); g_clear_error(&err); } else printf("Got confirmation request for %s\n", addr); if (data->accept < 0 && data->reject < 0) return; if (data->reject == 0) { printf("Rejecting connection\n"); g_io_channel_shutdown(io, TRUE, NULL); return; } if (data->voice) { if (!bt_io_set(io, &err, BT_IO_OPT_VOICE, data->voice, BT_IO_OPT_INVALID)) { printf("bt_io_set(OPT_VOICE): %s\n", err->message); g_clear_error(&err); } } data->io = g_io_channel_ref(io); io_data_ref(data); if (data->accept == 0) { if (!bt_io_accept(io, connect_cb, data, (GDestroyNotify) io_data_unref, &err)) { printf("bt_io_accept() failed: %s\n", err->message); g_clear_error(&err); io_data_unref(data); return; } } else { int seconds = (data->reject > 0) ? data->reject : data->accept; g_timeout_add_seconds_full(G_PRIORITY_DEFAULT, seconds, confirm_timeout, data, (GDestroyNotify) io_data_unref); } }
static void cmd_sec_level(int argcp, char **argvp) { GError *gerr = NULL; BtIOSecLevel sec_level; if (argcp < 2) { printf("sec-level: %s\n", opt_sec_level); goto done; } if (strcasecmp(argvp[1], "medium") == 0) sec_level = BT_IO_SEC_MEDIUM; else if (strcasecmp(argvp[1], "high") == 0) sec_level = BT_IO_SEC_HIGH; else if (strcasecmp(argvp[1], "low") == 0) sec_level = BT_IO_SEC_LOW; else { printf("Allowed values: low | medium | high\n"); goto done; } g_free(opt_sec_level); opt_sec_level = g_strdup(argvp[1]); if (conn_state != STATE_CONNECTED) goto done; if (opt_psm) { printf("It must be reconnected to this change take effect\n"); goto done; } bt_io_set(iochannel, BT_IO_L2CAP, &gerr, BT_IO_OPT_SEC_LEVEL, sec_level, BT_IO_OPT_INVALID); if (gerr) { printf("Error: %s\n", gerr->message); g_error_free(gerr); } return; done: printf("\r%s", get_prompt()); fflush(stdout); }
static void update_char_desc(guint8 status, const guint8 *pdu, guint16 len, gpointer user_data) { struct query_data *current = user_data; struct gatt_service *gatt = current->gatt; struct characteristic *chr = current->chr; INFO("%s",chr->path); if (status == 0) { g_free(chr->desc); chr->desc = g_malloc(len); memcpy(chr->desc, pdu + 1, len - 1); chr->desc[len - 1] = '\0'; store_attribute(gatt, current->handle, GATT_CHARAC_USER_DESC_UUID, (void *) chr->desc, len); } else if (status == ATT_ECODE_INSUFF_ENC) { GIOChannel *io = g_attrib_get_channel(gatt->attrib); BtIOSecLevel level = BT_IO_SEC_HIGH; bt_io_get(io, BT_IO_L2CAP, NULL, BT_IO_OPT_SEC_LEVEL, &level, BT_IO_OPT_INVALID); if (level < BT_IO_SEC_HIGH) level++; if (bt_io_set(io, BT_IO_L2CAP, NULL, BT_IO_OPT_SEC_LEVEL, level, BT_IO_OPT_INVALID)) { gatt_read_char(gatt->attrib, current->handle, 0, update_char_desc, current); return; } } add_characteristic_descriptor(current, GATT_CHARAC_USER_DESC_UUID); query_list_remove(gatt, current); g_free(current); }
gboolean g_attrib_set_mtu(GAttrib *attrib, int mtu) { if (mtu < ATT_DEFAULT_LE_MTU) mtu = ATT_DEFAULT_LE_MTU; if (mtu > ATT_MAX_MTU) mtu = ATT_MAX_MTU; if (!bt_io_set(attrib->io, BT_IO_L2CAP, NULL, BT_IO_OPT_OMTU, mtu, BT_IO_OPT_INVALID)) return FALSE; attrib->buf = g_realloc(attrib->buf, mtu); attrib->buflen = mtu; return TRUE; }
static void cmd_sec_level(int argcp, char **argvp) { GError *gerr = NULL; BtIOSecLevel sec_level; if (argcp < 2) { resp_error(err_BAD_PARAM);; return; } if (strcasecmp(argvp[1], "medium") == 0) sec_level = BT_IO_SEC_MEDIUM; else if (strcasecmp(argvp[1], "high") == 0) sec_level = BT_IO_SEC_HIGH; else if (strcasecmp(argvp[1], "low") == 0) sec_level = BT_IO_SEC_LOW; else { resp_error(err_BAD_PARAM);; return; } g_free(opt_sec_level); opt_sec_level = g_strdup(argvp[1]); if (conn_state != STATE_CONNECTED) return; assert(!opt_psm); bt_io_set(iochannel, &gerr, BT_IO_OPT_SEC_LEVEL, sec_level, BT_IO_OPT_INVALID); if (gerr) { printf("# Error: %s\n", gerr->message); resp_error(err_PROTO_ERR); g_error_free(gerr); } else { /* Tell bluepy the security level * has been changed successfuly */ cmd_status(0, NULL); } }
static void cmd_sec_level(int argcp, char **argvp) { GError *gerr = NULL; BtIOSecLevel sec_level; if (argcp < 2) { rl_printf("sec-level: %s\n", opt_sec_level); return; } if (strcasecmp(argvp[1], "medium") == 0) sec_level = BT_IO_SEC_MEDIUM; else if (strcasecmp(argvp[1], "high") == 0) sec_level = BT_IO_SEC_HIGH; else if (strcasecmp(argvp[1], "low") == 0) sec_level = BT_IO_SEC_LOW; else { rl_printf("Allowed values: low | medium | high\n"); return; } g_free(opt_sec_level); opt_sec_level = g_strdup(argvp[1]); if (conn_state != STATE_CONNECTED) return; if (opt_psm) { rl_printf("Change will take effect on reconnection\n"); return; } bt_io_set(iochannel, &gerr, BT_IO_OPT_SEC_LEVEL, sec_level, BT_IO_OPT_INVALID); if (gerr) { error("%s\n", gerr->message); g_error_free(gerr); } }
static void update_sec_level(struct io_data *data) { GError *err = NULL; int sec_level; if (!bt_io_get(data->io, &err, BT_IO_OPT_SEC_LEVEL, &sec_level, BT_IO_OPT_INVALID)) { printf("bt_io_get(OPT_SEC_LEVEL): %s\n", err->message); g_clear_error(&err); return; } printf("sec_level=%d\n", sec_level); if (opt_update_sec == sec_level) return; if (!bt_io_set(data->io, &err, BT_IO_OPT_SEC_LEVEL, opt_update_sec, BT_IO_OPT_INVALID)) { printf("bt_io_set(OPT_SEC_LEVEL): %s\n", err->message); g_clear_error(&err); } }
static void update_char_value(guint8 status, const guint8 *pdu, guint16 len, gpointer user_data) { struct query_data *current = user_data; struct gatt_service *gatt = current->gatt; struct characteristic *chr = current->chr; if (status == 0) characteristic_set_value(chr, pdu + 1, len - 1); else if (status == ATT_ECODE_INSUFF_ENC) { GIOChannel *io = g_attrib_get_channel(gatt->attrib); if (bt_io_set(io, BT_IO_L2CAP, NULL, BT_IO_OPT_SEC_LEVEL, BT_IO_SEC_HIGH, BT_IO_OPT_INVALID)) { gatt_read_char(gatt->attrib, chr->handle, 0, update_char_value, current); return; } } query_list_remove(gatt, current); g_free(current); }
static int hidp_add_connection(struct input_device *idev) { struct hidp_connadd_req *req; sdp_record_t *rec; char src_addr[18], dst_addr[18]; char filename[PATH_MAX + 1]; GKeyFile *key_file; char handle[11], *str; GError *gerr = NULL; int err; req = g_new0(struct hidp_connadd_req, 1); req->ctrl_sock = g_io_channel_unix_get_fd(idev->ctrl_io); req->intr_sock = g_io_channel_unix_get_fd(idev->intr_io); req->flags = 0; req->idle_to = idle_timeout; ba2str(&idev->src, src_addr); ba2str(&idev->dst, dst_addr); snprintf(filename, PATH_MAX, STORAGEDIR "/%s/cache/%s", src_addr, dst_addr); filename[PATH_MAX] = '\0'; sprintf(handle, "0x%8.8X", idev->handle); key_file = g_key_file_new(); g_key_file_load_from_file(key_file, filename, 0, NULL); str = g_key_file_get_string(key_file, "ServiceRecords", handle, NULL); g_key_file_free(key_file); if (!str) { error("Rejected connection from unknown device %s", dst_addr); err = -EPERM; goto cleanup; } rec = record_from_string(str); g_free(str); err = extract_hid_record(rec, req); sdp_record_free(rec); if (err < 0) { error("Could not parse HID SDP record: %s (%d)", strerror(-err), -err); goto cleanup; } req->vendor = btd_device_get_vendor(idev->device); req->product = btd_device_get_product(idev->device); req->version = btd_device_get_version(idev->device); if (idev->name) strncpy(req->name, idev->name, sizeof(req->name) - 1); /* Encryption is mandatory for keyboards */ if (req->subclass & 0x40) { if (!bt_io_set(idev->intr_io, &gerr, BT_IO_OPT_SEC_LEVEL, BT_IO_SEC_MEDIUM, BT_IO_OPT_INVALID)) { error("btio: %s", gerr->message); g_error_free(gerr); err = -EFAULT; goto cleanup; } idev->req = req; idev->sec_watch = g_io_add_watch(idev->intr_io, G_IO_OUT, encrypt_notify, idev); return 0; } err = ioctl_connadd(req); cleanup: g_free(req->rd_data); g_free(req); return err; }
static void confirm_sco_cb(GIOChannel *chan, gpointer user_data) { char address[18]; bdaddr_t bdaddr; GError *err = NULL; struct bt_sco *sco = user_data; uint16_t voice_settings; DBG(""); bt_io_get(chan, &err, BT_IO_OPT_DEST, address, BT_IO_OPT_DEST_BDADDR, &bdaddr, BT_IO_OPT_INVALID); if (err) { error("sco: audio confirm failed (%s)", err->message); g_error_free(err); goto drop; } if (!sco->confirm_cb || !sco->connect_cb) { error("sco: Connect and/or confirm callback not registered "); goto drop; } /* Check if there is SCO */ if (sco->io) { error("sco: SCO is in progress"); goto drop; } if (!sco->confirm_cb(&bdaddr, &voice_settings)) { error("sco: Audio connection from %s rejected", address); goto drop; } bacpy(&sco->remote_addr, &bdaddr); DBG("Incoming SCO connection from %s, voice settings 0x%x", address, voice_settings); err = NULL; bt_io_set(chan, &err, BT_IO_OPT_VOICE, voice_settings, BT_IO_OPT_INVALID); if (err) { error("sco: Could not set voice settings (%s)", err->message); g_error_free(err); goto drop; } if (!bt_io_accept(chan, connect_sco_cb, sco, NULL, NULL)) { error("sco: Failed to accept audio connection"); goto drop; } sco->io = g_io_channel_ref(chan); return; drop: g_io_channel_shutdown(chan, TRUE, NULL); }
static int hidp_add_connection(struct input_device *idev, struct input_conn *iconn) { struct hidp_connadd_req *req; struct fake_hid *fake_hid; struct fake_input *fake; sdp_record_t *rec; char src_addr[18], dst_addr[18]; GError *gerr = NULL; int err; DBG("idev %p", idev); req = g_new0(struct hidp_connadd_req, 1); req->ctrl_sock = g_io_channel_unix_get_fd(iconn->ctrl_io); req->intr_sock = g_io_channel_unix_get_fd(iconn->intr_io); req->flags = 0; req->idle_to = iconn->timeout; ba2str(&idev->src, src_addr); ba2str(&idev->dst, dst_addr); rec = fetch_record(src_addr, dst_addr, idev->handle); if (!rec) { error("Rejected connection from unknown device %s", dst_addr); err = -EPERM; goto cleanup; } extract_hid_record(rec, req); extract_hid_props(idev, rec); sdp_record_free(rec); req->vendor = btd_device_get_vendor(idev->device); req->product = btd_device_get_product(idev->device); req->version = btd_device_get_version(idev->device); fake_hid = get_fake_hid(req->vendor, req->product); if (fake_hid) { err = 0; fake = g_new0(struct fake_input, 1); fake->connect = fake_hid_connect; fake->disconnect = fake_hid_disconnect; fake->priv = fake_hid; fake->idev = idev; fake = fake_hid_connadd(fake, iconn->intr_io, fake_hid); if (fake == NULL) err = -ENOMEM; else fake->flags |= FI_FLAG_CONNECTED; goto cleanup; } if (idev->name) strncpy(req->name, idev->name, sizeof(req->name) - 1); /* Encryption is mandatory for keyboards */ if (req->subclass & 0x40) { struct btd_adapter *adapter = device_get_adapter(idev->device); err = btd_adapter_encrypt_link(adapter, (bdaddr_t *) &idev->dst, encrypt_completed, req); if (err == 0) { /* Waiting async encryption */ return 0; } if (err == -ENOSYS) goto nosys; if (err != -EALREADY) { error("encrypt_link: %s (%d)", strerror(-err), -err); goto cleanup; } } err = ioctl_connadd(req); cleanup: free(req->rd_data); g_free(req); return err; nosys: if (!bt_io_set(iconn->intr_io, BT_IO_L2CAP, &gerr, BT_IO_OPT_SEC_LEVEL, BT_IO_SEC_MEDIUM, BT_IO_OPT_INVALID)) { error("btio: %s", gerr->message); g_error_free(gerr); goto cleanup; } iconn->req = req; iconn->sec_watch = g_io_add_watch(iconn->intr_io, G_IO_OUT, encrypt_notify, iconn); return 0; }