cdba-server: Provide QDL access control list

The newly introduced support for QDL-based remote flashing of devices is
powerful, but provides an easy way to alter shared boards in unwanted
ways.

Introduce an optional "qdl_access" entry to the device configuration,
which allows to specify a list of entries each one defining a set of
users and their accepted flash targets.

A special target "all" can be used to give e.g. administrators
unrestricted access to the flash.

Signed-off-by: Bjorn Andersson <bjorn.andersson@oss.qualcomm.com>
This commit is contained in:
Bjorn Andersson committed 2026-06-14 22:14:26 +00:00
1 parent 4be6c26fbe
commit 48efa3096e
7 files changed
+189

No files matched your search

+27
View File
@@ -81,6 +81,33 @@ devices:
fastboot_set_active: true
fastboot_key_timeout: 2
- board: qdlboard
users:
- username
console: /dev/ttyUSB1
fastboot: abcdef04
qdl_programmer: /path/to/prog_firehose_ddr.elf
qdl_access:
- user: username
targets:
- boot_a
- boot_b
- 0/efiesp
- 0/123456
- user: admin
targets:
- all
QDL access targets use QDL's target syntax: <partition-name>, <lun>,
<lun>/<partition-name>, or <lun>/<start-sector>. The CDBA server checks the
requested target string against the configured access list before invoking QDL,
but does not resolve the target to a physical partition itself. For a bare
<partition-name>, QDL searches across all LUNs and flashes the first matching
partition. If multiple LUNs might contain the same partition name, or if the ACL
must identify an exact location, use the explicit <lun>/<partition-name> or
<lun>/<start-sector> form instead. The special target "all" allows any QDL
target.
- board: testboard
console: /dev/serial/by-id/usb-1234-if00-port0
name: GPIO controller board
+16
View File
@@ -148,8 +148,24 @@ static void msg_edl_flash(const void *data, size_t len)
{
const char *target = data;
if (!selected_device || !current_edl_file)
return;
if (!len || target[len - 1]) {
fprintf(stderr, "invalid EDL flash target\n");
watch_quit();
return;
}
fprintf(stderr, "edl flash into '%s'\n", target);
if (!device_qdl_access_allowed(selected_device, username, target)) {
fprintf(stderr, "user '%s' is not allowed to flash EDL target '%s' on %s\n",
username, target, selected_device->board);
watch_quit();
return;
}
current_edl_file->target = strdup(target);
current_edl_file = NULL;
}
+8
View File
@@ -11,3 +11,11 @@ devices:
qdl_programmer: /path/to/prog_firehose_ddr.elf
qdl_serial: 1234567890ABCDEF
qdl_storage: ufs
qdl_access:
- user: alice
targets:
- all
- user: bob
targets:
- userdata
- 0/123456
+27
View File
@@ -410,6 +410,33 @@ void device_info(const char *username, const void *data, size_t dlen)
cdba_send_buf(MSG_BOARD_INFO, len, description);
}
bool device_qdl_access_allowed(struct device *device,
const char *username,
const char *target)
{
struct device_qdl_user *user;
struct device_qdl_target *qdl_target;
if (!device->qdl_access)
return true;
if (!username || !target)
return false;
list_for_each_entry(user, device->qdl_access, node) {
if (strcmp(user->username, username))
continue;
list_for_each_entry(qdl_target, &user->targets, node) {
if (!strcmp(qdl_target->target, "all") ||
!strcmp(qdl_target->target, target))
return true;
}
}
return false;
}
void device_close(struct device *dev)
{
if (!dev->usb_always_on)
+17
View File
@@ -67,6 +67,7 @@ struct device {
char *qdl_programmer;
char *qdl_serial;
char *qdl_storage;
struct list_head *qdl_access;
char *status_cmd;
@@ -79,6 +80,19 @@ struct device_user {
struct list_head node;
};
struct device_qdl_target {
const char *target;
struct list_head node;
};
struct device_qdl_user {
const char *username;
struct list_head targets;
struct list_head node;
};
void device_add(struct device *device);
struct device *device_open(const char *board,
@@ -104,6 +118,9 @@ void device_list_devices(const char *username);
void device_info(const char *username, const void *data, size_t dlen);
void device_fastboot_continue(struct device *device);
bool device_is_running(struct device *device);
bool device_qdl_access_allowed(struct device *device,
const char *username,
const char *target);
extern const struct control_ops alpaca_ops;
extern const struct control_ops cdb_assist_ops;
+69
View File
@@ -128,6 +128,70 @@ static void parse_users(struct device_parser *dp, struct device *dev)
device_parser_expect(dp, YAML_SEQUENCE_END_EVENT, NULL, 0);
}
static void parse_qdl_targets(struct device_parser *dp, struct list_head *targets)
{
char value[TOKEN_LENGTH];
if (device_parser_accept(dp, YAML_SCALAR_EVENT, value, 0))
return;
device_parser_expect(dp, YAML_SEQUENCE_START_EVENT, NULL, 0);
while (device_parser_accept(dp, YAML_SCALAR_EVENT, value, TOKEN_LENGTH)) {
struct device_qdl_target *target = calloc(1, sizeof(*target));
target->target = strdup(value);
list_append(targets, &target->node);
}
device_parser_expect(dp, YAML_SEQUENCE_END_EVENT, NULL, 0);
}
static void parse_qdl_access(struct device_parser *dp, struct device *dev)
{
char key[TOKEN_LENGTH];
dev->qdl_access = calloc(1, sizeof(*dev->qdl_access));
list_init(dev->qdl_access);
if (device_parser_accept(dp, YAML_SCALAR_EVENT, key, 0))
return;
device_parser_expect(dp, YAML_SEQUENCE_START_EVENT, NULL, 0);
while (device_parser_accept(dp, YAML_MAPPING_START_EVENT, NULL, 0)) {
struct device_qdl_user *user = calloc(1, sizeof(*user));
bool have_username = false;
bool have_targets = false;
list_init(&user->targets);
while (device_parser_accept(dp, YAML_SCALAR_EVENT, key, TOKEN_LENGTH)) {
if (!strcmp(key, "user")) {
device_parser_expect(dp, YAML_SCALAR_EVENT, key, TOKEN_LENGTH);
user->username = strdup(key);
have_username = true;
} else if (!strcmp(key, "targets")) {
parse_qdl_targets(dp, &user->targets);
have_targets = true;
} else {
fprintf(stderr, "device parser: unknown qdl_access key \"%s\"\n", key);
exit(1);
}
}
if (!have_username || !have_targets || list_empty(&user->targets)) {
fprintf(stderr, "device parser: incomplete qdl_access entry\n");
exit(1);
}
list_append(dev->qdl_access, &user->node);
device_parser_expect(dp, YAML_MAPPING_END_EVENT, NULL, 0);
}
device_parser_expect(dp, YAML_SEQUENCE_END_EVENT, NULL, 0);
}
static void parse_board(struct device_parser *dp)
{
struct device *dev;
@@ -142,6 +206,11 @@ static void parse_board(struct device_parser *dp)
continue;
}
if (!strcmp(key, "qdl_access")) {
parse_qdl_access(dp, dev);
continue;
}
if (!strcmp(key, "local_gpio")) {
dev->control_options = local_gpio_ops.parse_options(dp);
if (dev->control_options)
+25
View File
@@ -94,6 +94,31 @@ properties:
description: storage type to pass to QDL when flashing
type: string
qdl_access:
description: >
User access allowance for QDL flash targets. Targets use QDL syntax:
<partition-name>, <lun>, <lun>/<partition-name>, or
<lun>/<start-sector>. Bare partition names are resolved by QDL
across all LUNs; use "all" to allow any target.
type: array
uniqueItems: true
minItems: 1
items:
type: object
additionalProperties: false
properties:
user:
type: string
targets:
type: array
uniqueItems: true
minItems: 1
items:
type: string
required:
- user
- targets
qcomlt_debug_board:
description: Qlt Debug Board control tty device path
$ref: "#/$defs/device_path"