static uint8_t check_permissions(struct bt_gatt_server *server, struct gatt_db_attribute *attr, uint32_t mask) { uint32_t perm; int security; perm = gatt_db_attribute_get_permissions(attr); if (perm && mask & BT_ATT_PERM_READ && !(perm & BT_ATT_PERM_READ)) return BT_ATT_ERROR_READ_NOT_PERMITTED; if (perm && mask & BT_ATT_PERM_WRITE && !(perm & BT_ATT_PERM_WRITE)) return BT_ATT_ERROR_WRITE_NOT_PERMITTED; perm &= mask; if (!perm) return 0; security = bt_att_get_security(server->att); if (perm & BT_ATT_PERM_SECURE && security < BT_ATT_SECURITY_FIPS) return BT_ATT_ERROR_AUTHENTICATION; if (perm & BT_ATT_PERM_AUTHEN && security < BT_ATT_SECURITY_HIGH) return BT_ATT_ERROR_AUTHENTICATION; if (perm & BT_ATT_PERM_ENCRYPT && security < BT_ATT_SECURITY_MEDIUM) return BT_ATT_ERROR_INSUFFICIENT_ENCRYPTION; return 0; }
static void handle_read_req(struct bt_gatt_server *server, uint8_t opcode, uint16_t handle, uint16_t offset) { struct gatt_db_attribute *attr; uint8_t ecode; uint32_t perm; struct async_read_op *op = NULL; attr = gatt_db_get_attribute(server->db, handle); if (!attr) { ecode = BT_ATT_ERROR_INVALID_HANDLE; goto error; } util_debug(server->debug_callback, server->debug_data, "Read %sReq - handle: 0x%04x", opcode == BT_ATT_OP_READ_BLOB_REQ ? "Blob " : "", handle); perm = gatt_db_attribute_get_permissions(attr); if (perm && !(perm & BT_ATT_PERM_READ)) { ecode = BT_ATT_ERROR_READ_NOT_PERMITTED; goto error; } if (server->pending_read_op) { ecode = BT_ATT_ERROR_UNLIKELY; goto error; } op = new0(struct async_read_op, 1); if (!op) { ecode = BT_ATT_ERROR_INSUFFICIENT_RESOURCES; goto error; } op->opcode = opcode; op->server = server; server->pending_read_op = op; if (gatt_db_attribute_read(attr, offset, opcode, server->att, read_complete_cb, op)) return; ecode = BT_ATT_ERROR_UNLIKELY; error: if (op) async_read_op_destroy(op); bt_att_send_error_rsp(server->att, opcode, handle, ecode); }
static uint8_t check_permissions(struct bt_gatt_server *server, struct gatt_db_attribute *attr, uint32_t mask) { uint8_t enc_size; uint32_t perm; int security; perm = gatt_db_attribute_get_permissions(attr); if (perm && mask & BT_ATT_PERM_READ && !(perm & BT_ATT_PERM_READ)) return BT_ATT_ERROR_READ_NOT_PERMITTED; if (perm && mask & BT_ATT_PERM_WRITE && !(perm & BT_ATT_PERM_WRITE)) return BT_ATT_ERROR_WRITE_NOT_PERMITTED; perm &= mask; if (!perm) return 0; security = bt_att_get_security(server->att, &enc_size); if (security < 0) return BT_ATT_ERROR_UNLIKELY; if (perm & BT_ATT_PERM_SECURE) { if (security < BT_ATT_SECURITY_FIPS) return BT_ATT_ERROR_AUTHENTICATION; if (!check_min_key_size(server->min_enc_size, enc_size)) return BT_ATT_ERROR_INSUFFICIENT_ENCRYPTION_KEY_SIZE; } if (perm & BT_ATT_PERM_AUTHEN) { if (security < BT_ATT_SECURITY_HIGH) return BT_ATT_ERROR_AUTHENTICATION; if (!check_min_key_size(server->min_enc_size, enc_size)) return BT_ATT_ERROR_INSUFFICIENT_ENCRYPTION_KEY_SIZE; } if (perm & BT_ATT_PERM_ENCRYPT) { if (security < BT_ATT_SECURITY_MEDIUM) return BT_ATT_ERROR_INSUFFICIENT_ENCRYPTION; if (!check_min_key_size(server->min_enc_size, enc_size)) return BT_ATT_ERROR_INSUFFICIENT_ENCRYPTION_KEY_SIZE; } return 0; }
static void process_read_by_type(struct async_read_op *op) { struct bt_gatt_server *server = op->server; uint8_t ecode; struct gatt_db_attribute *attr; uint32_t perm; attr = queue_pop_head(op->db_data); if (op->done || !attr) { bt_att_send(server->att, BT_ATT_OP_READ_BY_TYPE_RSP, op->pdu, op->pdu_len, NULL, NULL, NULL); async_read_op_destroy(op); return; } perm = gatt_db_attribute_get_permissions(attr); /* * Check for the READ access permission. Encryption, * authentication, and authorization permissions need to be * checked by the read handler, since bt_att is agnostic to * connection type and doesn't have security information on it. */ if (perm && !(perm & BT_ATT_PERM_READ)) { ecode = BT_ATT_ERROR_READ_NOT_PERMITTED; goto error; } if (gatt_db_attribute_read(attr, 0, op->opcode, server->att, read_by_type_read_complete_cb, op)) return; ecode = BT_ATT_ERROR_UNLIKELY; error: bt_att_send_error_rsp(server->att, BT_ATT_OP_READ_BY_TYPE_REQ, gatt_db_attribute_get_handle(attr), ecode); async_read_op_destroy(op); }
static void read_multiple_complete_cb(struct gatt_db_attribute *attr, int err, const uint8_t *value, size_t len, void *user_data) { struct read_multiple_resp_data *data = user_data; struct gatt_db_attribute *next_attr; uint32_t perm; uint16_t handle = gatt_db_attribute_get_handle(attr); if (err != 0) { bt_att_send_error_rsp(data->server->att, BT_ATT_OP_READ_MULT_REQ, handle, err); read_multiple_resp_data_free(data); return; } perm = gatt_db_attribute_get_permissions(attr); if (perm && !(perm & BT_ATT_PERM_READ)) { bt_att_send_error_rsp(data->server->att, BT_ATT_OP_READ_MULT_REQ, handle, BT_ATT_ERROR_READ_NOT_PERMITTED); read_multiple_resp_data_free(data); return; } len = MIN(len, data->mtu - data->length - 1); memcpy(data->rsp_data + data->length, value, len); data->length += len; data->cur_handle++; if ((data->length >= data->mtu - 1) || (data->cur_handle == data->num_handles)) { bt_att_send(data->server->att, BT_ATT_OP_READ_MULT_RSP, data->rsp_data, data->length, NULL, NULL, NULL); read_multiple_resp_data_free(data); return; } util_debug(data->server->debug_callback, data->server->debug_data, "Read Multiple Req - #%zu of %zu: 0x%04x", data->cur_handle + 1, data->num_handles, data->handles[data->cur_handle]); next_attr = gatt_db_get_attribute(data->server->db, data->handles[data->cur_handle]); if (!next_attr) { bt_att_send_error_rsp(data->server->att, BT_ATT_OP_READ_MULT_REQ, data->handles[data->cur_handle], BT_ATT_ERROR_INVALID_HANDLE); read_multiple_resp_data_free(data); return; } if (!gatt_db_attribute_read(next_attr, 0, BT_ATT_OP_READ_MULT_REQ, data->server->att, read_multiple_complete_cb, data)) { bt_att_send_error_rsp(data->server->att, BT_ATT_OP_READ_MULT_REQ, data->handles[data->cur_handle], BT_ATT_ERROR_UNLIKELY); read_multiple_resp_data_free(data); } }
static void write_cb(uint8_t opcode, const void *pdu, uint16_t length, void *user_data) { struct bt_gatt_server *server = user_data; struct gatt_db_attribute *attr; uint16_t handle = 0; struct async_write_op *op = NULL; uint8_t ecode; uint32_t perm; if (length < 2) { ecode = BT_ATT_ERROR_INVALID_PDU; goto error; } handle = get_le16(pdu); attr = gatt_db_get_attribute(server->db, handle); if (!attr) { ecode = BT_ATT_ERROR_INVALID_HANDLE; goto error; } util_debug(server->debug_callback, server->debug_data, "Write %s - handle: 0x%04x", (opcode == BT_ATT_OP_WRITE_REQ) ? "Req" : "Cmd", handle); perm = gatt_db_attribute_get_permissions(attr); if (!(perm & BT_ATT_PERM_WRITE)) { ecode = BT_ATT_ERROR_WRITE_NOT_PERMITTED; goto error; } if (server->pending_write_op) { ecode = BT_ATT_ERROR_UNLIKELY; goto error; } op = new0(struct async_write_op, 1); if (!op) { ecode = BT_ATT_ERROR_INSUFFICIENT_RESOURCES; goto error; } op->server = server; op->opcode = opcode; server->pending_write_op = op; if (gatt_db_attribute_write(attr, 0, pdu + 2, length - 2, opcode, server->att, write_complete_cb, op)) return; if (op) async_write_op_destroy(op); ecode = BT_ATT_ERROR_UNLIKELY; error: if (opcode == BT_ATT_OP_WRITE_CMD) return; bt_att_send_error_rsp(server->att, opcode, handle, ecode); }
static void prep_write_cb(uint8_t opcode, const void *pdu, uint16_t length, void *user_data) { struct bt_gatt_server *server = user_data; struct prep_write_data *prep_data = NULL; uint16_t handle = 0; uint16_t offset; struct gatt_db_attribute *attr; uint8_t ecode; uint32_t perm; if (length < 4) { ecode = BT_ATT_ERROR_INVALID_PDU; goto error; } if (queue_length(server->prep_queue) >= server->max_prep_queue_len) { ecode = BT_ATT_ERROR_PREPARE_QUEUE_FULL; goto error; } handle = get_le16(pdu); offset = get_le16(pdu + 2); attr = gatt_db_get_attribute(server->db, handle); if (!attr) { ecode = BT_ATT_ERROR_INVALID_HANDLE; goto error; } util_debug(server->debug_callback, server->debug_data, "Prep Write Req - handle: 0x%04x", handle); perm = gatt_db_attribute_get_permissions(attr); /* * TODO: The "Prepare Write" request requires security permission checks * to be performed before the write is executed. I.e., we can't leave * the permission check to the upper layer since we can't call * gatt_db_write until the entire queue is atomically processed during * an "Execute Write" request. Figure out how to make this check here. */ if (!(perm & BT_ATT_PERM_WRITE)) { ecode = BT_ATT_ERROR_WRITE_NOT_PERMITTED; goto error; } prep_data = new0(struct prep_write_data, 1); if (!prep_data) { ecode = BT_ATT_ERROR_INSUFFICIENT_RESOURCES; goto error; } prep_data->length = length - 4; if (prep_data->length) { prep_data->value = malloc(prep_data->length); if (!prep_data->value) { ecode = BT_ATT_ERROR_INSUFFICIENT_RESOURCES; goto error; } } prep_data->server = server; prep_data->handle = handle; prep_data->offset = offset; memcpy(prep_data->value, pdu + 4, prep_data->length); queue_push_tail(server->prep_queue, prep_data); bt_att_send(server->att, BT_ATT_OP_PREP_WRITE_RSP, pdu, length, NULL, NULL, NULL); return; error: if (prep_data) prep_write_data_destroy(prep_data); bt_att_send_error_rsp(server->att, opcode, handle, ecode); }