1 /* Copyright (c) 2023 Nordic Semiconductor ASA
2 * SPDX-License-Identifier: Apache-2.0
3 */
4
5 #include <stdint.h>
6 #include <zephyr/bluetooth/bluetooth.h>
7 #include <zephyr/bluetooth/conn.h>
8 #include <zephyr/bluetooth/gatt.h>
9 #include <zephyr/bluetooth/l2cap.h>
10 #include <zephyr/bluetooth/uuid.h>
11 #include <zephyr/kernel.h>
12 #include <zephyr/sys/__assert.h>
13 #include <zephyr/logging/log.h>
14
15 LOG_MODULE_REGISTER(bt_testlib_security, LOG_LEVEL_INF);
16
17 struct testlib_security_ctx {
18 enum bt_security_err result;
19 struct bt_conn *conn;
20 bt_security_t new_minimum;
21 struct k_condvar done;
22 };
23
24 /* Context pool (with capacity of one). */
25 static K_SEM_DEFINE(g_ctx_free, 1, 1);
26 static K_MUTEX_DEFINE(g_ctx_lock);
27 static struct testlib_security_ctx *g_ctx;
28
security_changed(struct bt_conn * conn,bt_security_t level,enum bt_security_err err)29 static void security_changed(struct bt_conn *conn, bt_security_t level, enum bt_security_err err)
30 {
31 LOG_INF("conn %u level %d err %d", bt_conn_index(conn), level, err);
32
33 /* Mutex operations establish a happens-before relationship. This
34 * ensures variables have the expected values despite non-atomic
35 * accesses.
36 */
37 k_mutex_lock(&g_ctx_lock, K_FOREVER);
38
39 if (g_ctx && (g_ctx->conn == conn)) {
40 g_ctx->result = err;
41 /* Assumption: A security error means there will be further
42 * security changes for this connection.
43 */
44 if (err || level >= g_ctx->new_minimum) {
45 k_condvar_signal(&g_ctx->done);
46 }
47 }
48
49 k_mutex_unlock(&g_ctx_lock);
50 }
51
52 BT_CONN_CB_DEFINE(conn_callbacks) = {
53 .security_changed = security_changed,
54 };
55
bt_testlib_secure(struct bt_conn * conn,bt_security_t new_minimum)56 int bt_testlib_secure(struct bt_conn *conn, bt_security_t new_minimum)
57 {
58 int api_err = 0;
59 struct testlib_security_ctx ctx = {
60 .conn = conn,
61 .new_minimum = new_minimum,
62 };
63
64 k_condvar_init(&ctx.done);
65
66 /* The semaphore allocates `g_ctx` to this invocation of
67 * `bt_testlib_secure`, in case this function is called from multiple
68 * threads in parallel.
69 */
70 k_sem_take(&g_ctx_free, K_FOREVER);
71 /* The mutex synchronizes this function with `security_changed()`. */
72 k_mutex_lock(&g_ctx_lock, K_FOREVER);
73
74 /* Do the thing. */
75 api_err = bt_conn_set_security(conn, new_minimum);
76
77 /* Holding the mutex will pause any thread entering
78 * `security_changed_cb`, delaying it until `k_condvar_wait`. This
79 * ensures that the condition variable is signaled while this thread is
80 * in `k_condvar_wait`, even if the event happens before, e.g. between
81 * `bt_conn_get_security` and `k_condvar_wait`.
82 *
83 * If the security level is already satisfied, there is no point in
84 * waiting, and it would deadlock if security was already satisfied
85 * before the mutex was taken, `bt_conn_set_security` will result in no
86 * operation.
87 */
88 if (!api_err && bt_conn_get_security(conn) < new_minimum) {
89 /* Waiting on a condvar releases the mutex and waits for a
90 * signal on the condvar, atomically, without a gap between the
91 * release and wait. The mutex is locked again before returning.
92 */
93 g_ctx = &ctx;
94 k_condvar_wait(&ctx.done, &g_ctx_lock, K_FOREVER);
95 g_ctx = NULL;
96 }
97
98 k_mutex_unlock(&g_ctx_lock);
99 k_sem_give(&g_ctx_free);
100
101 if (api_err) {
102 __ASSERT_NO_MSG(api_err < 0);
103 return api_err;
104 }
105
106 __ASSERT_NO_MSG(ctx.result >= 0);
107 return ctx.result;
108 }
109