linux-bluetooth.vger.kernel.org archive mirror
 help / color / mirror / Atom feed
* [PATCH 0/3] Basic attribute permission support
@ 2010-12-01 16:13 Anderson Lizardo
  2010-12-01 16:13 ` [PATCH 1/3] Initial attribute permission implementation Anderson Lizardo
                   ` (2 more replies)
  0 siblings, 3 replies; 7+ messages in thread
From: Anderson Lizardo @ 2010-12-01 16:13 UTC (permalink / raw)
  To: linux-bluetooth

Hi,

This patchset adds initial support for attribute permission checks. Currently,
only access and authentication permissions are checked. Authorization
permissions require integration with the BlueZ agent, which is not implemented
yet.

There are some pending issues necessary for a minimum complete attribute
permission support (all of them are being worked on):

* The attribute client, upon receiving the "Insufficient Encryption" error,
  shall increase the security level and resend the failed request.
* The attribute server shall verify the connection permissions on each ATT
  request, and not just once on connection callback.
* On kernel side, increasing the security level (using setsockopt()) shall
  trigger SMP negotiation for a LE connection, blocking next socket I/O until
  negotiation is finished.
* On BR/EDR, link encryption needs to be done "on the fly" before resending the
  failed ATT request.

Albeit the above issues, we believe these patches are ready for commit.

Regards,
--
Anderson Lizardo
OpenBossa Labs - INdT
Manaus - Brazil


^ permalink raw reply	[flat|nested] 7+ messages in thread

* [PATCH 1/3] Initial attribute permission implementation
  2010-12-01 16:13 [PATCH 0/3] Basic attribute permission support Anderson Lizardo
@ 2010-12-01 16:13 ` Anderson Lizardo
  2010-12-02 10:10   ` Johan Hedberg
  2010-12-01 16:13 ` [PATCH 2/3] Check attribute permissions in attribute server Anderson Lizardo
  2010-12-01 16:13 ` [PATCH 3/3] Check authentication permissions on " Anderson Lizardo
  2 siblings, 1 reply; 7+ messages in thread
From: Anderson Lizardo @ 2010-12-01 16:13 UTC (permalink / raw)
  To: linux-bluetooth; +Cc: Bruna Moreira

From: Bruna Moreira <bruna.moreira@openbossa.org>

Create definitions for attribute permissions, add "perms" field to
struct attribute and add attribute permissions to attribute server.
---
 attrib/att.h        |    1 +
 attrib/example.c    |   89 ++++++++++++++++++++++++++++-----------------------
 src/attrib-server.c |    4 ++-
 src/attrib-server.h |   13 +++++++-
 4 files changed, 65 insertions(+), 42 deletions(-)

diff --git a/attrib/att.h b/attrib/att.h
index 7c98b4a..a29aae2 100644
--- a/attrib/att.h
+++ b/attrib/att.h
@@ -112,6 +112,7 @@
 struct attribute {
 	uint16_t handle;
 	uuid_t uuid;
+	uint8_t perms;
 	int len;
 	uint8_t data[0];
 };
diff --git a/attrib/example.c b/attrib/example.c
index c29e1e4..2fedfc7 100644
--- a/attrib/example.c
+++ b/attrib/example.c
@@ -101,7 +101,7 @@ static int register_attributes(void)
 	u16 = htons(GENERIC_ACCESS_PROFILE_ID);
 	atval[0] = u16 >> 8;
 	atval[1] = u16;
-	attrib_db_add(0x0001, &uuid, atval, 2);
+	attrib_db_add(0x0001, &uuid, ATT_ACCESS(ATT_READ, ATT_NONE), atval, 2);
 
 	/* GAP service: device name characteristic */
 	sdp_uuid16_create(&uuid, GATT_CHARAC_UUID);
@@ -111,20 +111,21 @@ static int register_attributes(void)
 	atval[2] = 0x00;
 	atval[3] = u16 >> 8;
 	atval[4] = u16;
-	attrib_db_add(0x0004, &uuid, atval, 5);
+	attrib_db_add(0x0004, &uuid, ATT_ACCESS(ATT_READ, ATT_NONE), atval, 5);
 
 	/* GAP service: device name attribute */
 	sdp_uuid16_create(&uuid, GATT_CHARAC_DEVICE_NAME);
 	len = strlen(devname);
 	strncpy((char *) atval, devname, len);
-	attrib_db_add(0x0006, &uuid, atval, len);
+	attrib_db_add(0x0006, &uuid, ATT_ACCESS(ATT_READ | ATT_WRITE, ATT_NONE),
+								atval, len);
 
 	/* GATT service: primary service definition */
 	sdp_uuid16_create(&uuid, GATT_PRIM_SVC_UUID);
 	u16 = htons(GENERIC_ATTRIB_PROFILE_ID);
 	atval[0] = u16 >> 8;
 	atval[1] = u16;
-	attrib_db_add(0x0010, &uuid, atval, 2);
+	attrib_db_add(0x0010, &uuid, ATT_ACCESS(ATT_READ, ATT_NONE), atval, 2);
 
 	/* GATT service: attributes opcodes characteristic */
 	sdp_uuid16_create(&uuid, GATT_CHARAC_UUID);
@@ -134,20 +135,20 @@ static int register_attributes(void)
 	atval[2] = 0x00;
 	atval[3] = u16 >> 8;
 	atval[4] = u16;
-	attrib_db_add(0x0011, &uuid, atval, 5);
+	attrib_db_add(0x0011, &uuid, ATT_ACCESS(ATT_READ, ATT_NONE), atval, 5);
 
 	/* GATT service: attribute opcodes supported */
 	sdp_uuid16_create(&uuid, OPCODES_SUPPORTED_UUID);
 	atval[0] = 0xFF;
 	atval[1] = 0x01;
-	attrib_db_add(0x0012, &uuid, atval, 2);
+	attrib_db_add(0x0012, &uuid, ATT_ACCESS(ATT_READ, ATT_NONE), atval, 2);
 
 	/* Battery state service: primary service definition */
 	sdp_uuid16_create(&uuid, GATT_PRIM_SVC_UUID);
 	u16 = htons(BATTERY_STATE_SVC_UUID);
 	atval[0] = u16 >> 8;
 	atval[1] = u16;
-	attrib_db_add(0x0100, &uuid, atval, 2);
+	attrib_db_add(0x0100, &uuid, ATT_ACCESS(ATT_READ, ATT_NONE), atval, 2);
 
 	/* Battery: battery state characteristic */
 	sdp_uuid16_create(&uuid, GATT_CHARAC_UUID);
@@ -157,18 +158,19 @@ static int register_attributes(void)
 	atval[2] = 0x01;
 	atval[3] = u16 >> 8;
 	atval[4] = u16;
-	attrib_db_add(0x0106, &uuid, atval, 5);
+	attrib_db_add(0x0106, &uuid, ATT_ACCESS(ATT_READ, ATT_NONE), atval, 5);
 
 	/* Battery: battery state attribute */
 	sdp_uuid16_create(&uuid, BATTERY_STATE_UUID);
 	atval[0] = 0x04;
-	attrib_db_add(0x0110, &uuid, atval, 1);
+	attrib_db_add(0x0110, &uuid, ATT_ACCESS(ATT_READ, ATT_NONE), atval, 1);
 
 	/* Battery: Client Characteristic Configuration */
 	sdp_uuid16_create(&uuid, GATT_CLIENT_CHARAC_CFG_UUID);
 	atval[0] = 0x00;
 	atval[1] = 0x00;
-	attrib_db_add(0x0111, &uuid, atval, 2);
+	attrib_db_add(0x0111, &uuid, ATT_ACCESS(ATT_READ, ATT_NONE) |
+			ATT_ACCESS(ATT_WRITE, ATT_AUTHENTICATION), atval, 2);
 
 	timeout_id = g_timeout_add_seconds(10, change_battery_state, NULL);
 
@@ -177,7 +179,7 @@ static int register_attributes(void)
 	u16 = htons(THERM_HUMIDITY_SVC_UUID);
 	atval[0] = u16 >> 8;
 	atval[1] = u16;
-	attrib_db_add(0x0200, &uuid, atval, 2);
+	attrib_db_add(0x0200, &uuid, ATT_ACCESS(ATT_READ, ATT_NONE), atval, 2);
 
 	/* Thermometer: Include */
 	sdp_uuid16_create(&uuid, GATT_INCLUDE_UUID);
@@ -188,14 +190,14 @@ static int register_attributes(void)
 	atval[3] = 0x05;
 	atval[4] = u16 >> 8;
 	atval[5] = u16;
-	attrib_db_add(0x0201, &uuid, atval, 6);
+	attrib_db_add(0x0201, &uuid, ATT_ACCESS(ATT_READ, ATT_NONE), atval, 6);
 
 	/* Thermometer: Include */
 	atval[0] = 0x50;
 	atval[1] = 0x05;
 	atval[2] = 0x68;
 	atval[3] = 0x05;
-	attrib_db_add(0x0202, &uuid, atval, 4);
+	attrib_db_add(0x0202, &uuid, ATT_ACCESS(ATT_READ, ATT_NONE), atval, 4);
 
 	/* Thermometer: temperature characteristic */
 	sdp_uuid16_create(&uuid, GATT_CHARAC_UUID);
@@ -205,13 +207,13 @@ static int register_attributes(void)
 	atval[2] = 0x02;
 	atval[3] = u16 >> 8;
 	atval[4] = u16;
-	attrib_db_add(0x0203, &uuid, atval, 5);
+	attrib_db_add(0x0203, &uuid, ATT_ACCESS(ATT_READ, ATT_NONE), atval, 5);
 
 	/* Thermometer: temperature characteristic value */
 	sdp_uuid16_create(&uuid, TEMPERATURE_UUID);
 	atval[0] = 0x8A;
 	atval[1] = 0x02;
-	attrib_db_add(0x0204, &uuid, atval, 2);
+	attrib_db_add(0x0204, &uuid, ATT_ACCESS(ATT_READ, ATT_NONE), atval, 2);
 
 	/* Thermometer: temperature characteristic format */
 	sdp_uuid16_create(&uuid, GATT_CHARAC_FMT_UUID);
@@ -224,13 +226,14 @@ static int register_attributes(void)
 	u16 = htons(FMT_OUTSIDE_UUID);
 	atval[5] = u16 >> 8;
 	atval[6] = u16;
-	attrib_db_add(0x0205, &uuid, atval, 7);
+	attrib_db_add(0x0205, &uuid, ATT_ACCESS(ATT_READ, ATT_NONE), atval, 7);
 
 	/* Thermometer: characteristic user description */
 	sdp_uuid16_create(&uuid, GATT_CHARAC_USER_DESC_UUID);
 	len = strlen(desc_out_temp);
 	strncpy((char *) atval, desc_out_temp, len);
-	attrib_db_add(0x0206, &uuid, atval, len);
+	attrib_db_add(0x0206, &uuid, ATT_ACCESS(ATT_READ, ATT_NONE), atval,
+									len);
 
 	/* Thermometer: relative humidity characteristic */
 	sdp_uuid16_create(&uuid, GATT_CHARAC_UUID);
@@ -240,12 +243,12 @@ static int register_attributes(void)
 	atval[2] = 0x02;
 	atval[3] = u16 >> 8;
 	atval[4] = u16;
-	attrib_db_add(0x0210, &uuid, atval, 5);
+	attrib_db_add(0x0210, &uuid, ATT_ACCESS(ATT_READ, ATT_NONE), atval, 5);
 
 	/* Thermometer: relative humidity value */
 	sdp_uuid16_create(&uuid, RELATIVE_HUMIDITY_UUID);
 	atval[0] = 0x27;
-	attrib_db_add(0x0212, &uuid, atval, 1);
+	attrib_db_add(0x0212, &uuid, ATT_ACCESS(ATT_READ, ATT_NONE), atval, 1);
 
 	/* Thermometer: relative humidity characteristic format */
 	sdp_uuid16_create(&uuid, GATT_CHARAC_FMT_UUID);
@@ -260,20 +263,21 @@ static int register_attributes(void)
 	u16 = htons(FMT_OUTSIDE_UUID);
 	atval[6] = u16 >> 8;
 	atval[7] = u16;
-	attrib_db_add(0x0213, &uuid, atval, 8);
+	attrib_db_add(0x0213, &uuid, ATT_ACCESS(ATT_READ, ATT_NONE), atval, 8);
 
 	/* Thermometer: characteristic user description */
 	sdp_uuid16_create(&uuid, GATT_CHARAC_USER_DESC_UUID);
 	len = strlen(desc_out_hum);
 	strncpy((char *) atval, desc_out_hum, len);
-	attrib_db_add(0x0214, &uuid, atval, len);
+	attrib_db_add(0x0214, &uuid, ATT_ACCESS(ATT_READ, ATT_NONE), atval,
+									len);
 
 	/* Secondary Service: Manufacturer Service */
 	sdp_uuid16_create(&uuid, GATT_SND_SVC_UUID);
 	u16 = htons(MANUFACTURER_SVC_UUID);
 	atval[0] = u16 >> 8;
 	atval[1] = u16;
-	attrib_db_add(0x0500, &uuid, atval, 2);
+	attrib_db_add(0x0500, &uuid, ATT_ACCESS(ATT_READ, ATT_NONE), atval, 2);
 
 	/* Manufacturer name characteristic definition */
 	sdp_uuid16_create(&uuid, GATT_CHARAC_UUID);
@@ -283,13 +287,14 @@ static int register_attributes(void)
 	atval[2] = 0x05;
 	atval[3] = u16 >> 8;
 	atval[4] = u16;
-	attrib_db_add(0x0501, &uuid, atval, 5);
+	attrib_db_add(0x0501, &uuid, ATT_ACCESS(ATT_READ, ATT_NONE), atval, 5);
 
 	/* Manufacturer name characteristic value */
 	sdp_uuid16_create(&uuid, MANUFACTURER_NAME_UUID);
 	len = strlen(manufacturer_name1);
 	strncpy((char *) atval, manufacturer_name1, len);
-	attrib_db_add(0x0502, &uuid, atval, len);
+	attrib_db_add(0x0502, &uuid, ATT_ACCESS(ATT_READ, ATT_NONE), atval,
+									len);
 
 	/* Manufacturer serial number characteristic */
 	sdp_uuid16_create(&uuid, GATT_CHARAC_UUID);
@@ -299,20 +304,21 @@ static int register_attributes(void)
 	atval[2] = 0x05;
 	atval[3] = u16 >> 8;
 	atval[4] = u16;
-	attrib_db_add(0x0503, &uuid, atval, 5);
+	attrib_db_add(0x0503, &uuid, ATT_ACCESS(ATT_READ, ATT_NONE), atval, 5);
 
 	/* Manufacturer serial number characteristic value */
 	sdp_uuid16_create(&uuid, MANUFACTURER_SERIAL_UUID);
 	len = strlen(serial1);
 	strncpy((char *) atval, serial1, len);
-	attrib_db_add(0x0504, &uuid, atval, len);
+	attrib_db_add(0x0504, &uuid, ATT_ACCESS(ATT_READ, ATT_NONE), atval,
+									len);
 
 	/* Secondary Service: Manufacturer Service */
 	sdp_uuid16_create(&uuid, GATT_SND_SVC_UUID);
 	u16 = htons(MANUFACTURER_SVC_UUID);
 	atval[0] = u16 >> 8;
 	atval[1] = u16;
-	attrib_db_add(0x0505, &uuid, atval, 2);
+	attrib_db_add(0x0505, &uuid, ATT_ACCESS(ATT_READ, ATT_NONE), atval, 2);
 
 	/* Manufacturer name characteristic definition */
 	sdp_uuid16_create(&uuid, GATT_CHARAC_UUID);
@@ -322,14 +328,14 @@ static int register_attributes(void)
 	atval[2] = 0x05;
 	atval[3] = u16 >> 8;
 	atval[4] = u16;
-	attrib_db_add(0x0506, &uuid, atval, 5);
+	attrib_db_add(0x0506, &uuid, ATT_ACCESS(ATT_READ, ATT_NONE), atval, 5);
 
 	/* Secondary Service: Vendor Specific Service */
 	sdp_uuid16_create(&uuid, GATT_SND_SVC_UUID);
 	u16 = htons(VENDOR_SPECIFIC_SVC_UUID);
 	atval[0] = u16 >> 8;
 	atval[1] = u16;
-	attrib_db_add(0x0550, &uuid, atval, 2);
+	attrib_db_add(0x0550, &uuid, ATT_ACCESS(ATT_READ, ATT_NONE), atval, 2);
 
 	/* Vendor Specific Type characteristic definition */
 	sdp_uuid16_create(&uuid, GATT_CHARAC_UUID);
@@ -339,7 +345,7 @@ static int register_attributes(void)
 	atval[2] = 0x05;
 	atval[3] = u16 >> 8;
 	atval[4] = u16;
-	attrib_db_add(0x0560, &uuid, atval, 5);
+	attrib_db_add(0x0560, &uuid, ATT_ACCESS(ATT_READ, ATT_NONE), atval, 5);
 
 	/* Vendor Specific Type characteristic value */
 	sdp_uuid16_create(&uuid, VENDOR_SPECIFIC_TYPE_UUID);
@@ -349,13 +355,14 @@ static int register_attributes(void)
 	atval[3] = 0x64;
 	atval[4] = 0x6F;
 	atval[5] = 0x72;
-	attrib_db_add(0x0568, &uuid, atval, 6);
+	attrib_db_add(0x0568, &uuid, ATT_ACCESS(ATT_READ, ATT_NONE), atval, 6);
 
 	/* Manufacturer name attribute */
 	sdp_uuid16_create(&uuid, MANUFACTURER_NAME_UUID);
 	len = strlen(manufacturer_name2);
 	strncpy((char *) atval, manufacturer_name2, len);
-	attrib_db_add(0x0507, &uuid, atval, len);
+	attrib_db_add(0x0507, &uuid, ATT_ACCESS(ATT_READ, ATT_NONE), atval,
+									len);
 
 	/* Characteristic: serial number */
 	sdp_uuid16_create(&uuid, GATT_CHARAC_UUID);
@@ -365,18 +372,19 @@ static int register_attributes(void)
 	atval[2] = 0x05;
 	atval[3] = u16 >> 8;
 	atval[4] = u16;
-	attrib_db_add(0x0508, &uuid, atval, 5);
+	attrib_db_add(0x0508, &uuid, ATT_ACCESS(ATT_READ, ATT_NONE), atval, 5);
 
 	/* Serial number characteristic value */
 	sdp_uuid16_create(&uuid, MANUFACTURER_SERIAL_UUID);
 	len = strlen(serial2);
 	strncpy((char *) atval, serial2, len);
-	attrib_db_add(0x0509, &uuid, atval, len);
+	attrib_db_add(0x0509, &uuid, ATT_ACCESS(ATT_READ, ATT_NONE), atval,
+									len);
 
 	/* Weight service: primary service definition */
 	sdp_uuid16_create(&uuid, GATT_PRIM_SVC_UUID);
 	memcpy(atval, prim_weight_uuid, 16);
-	attrib_db_add(0x0680, &uuid, atval, 16);
+	attrib_db_add(0x0680, &uuid, ATT_ACCESS(ATT_READ, ATT_NONE), atval, 16);
 
 	/* Weight: include */
 	sdp_uuid16_create(&uuid, GATT_INCLUDE_UUID);
@@ -387,7 +395,7 @@ static int register_attributes(void)
 	atval[3] = 0x05;
 	atval[4] = u16 >> 8;
 	atval[5] = u16;
-	attrib_db_add(0x0681, &uuid, atval, 6);
+	attrib_db_add(0x0681, &uuid, ATT_ACCESS(ATT_READ, ATT_NONE), atval, 6);
 
 	/* Weight: characteristic */
 	sdp_uuid16_create(&uuid, GATT_CHARAC_UUID);
@@ -395,7 +403,7 @@ static int register_attributes(void)
 	atval[1] = 0x83;
 	atval[2] = 0x06;
 	memcpy(atval + 3, char_weight_uuid, 16);
-	attrib_db_add(0x0682, &uuid, atval, 19);
+	attrib_db_add(0x0682, &uuid, ATT_ACCESS(ATT_READ, ATT_NONE), atval, 19);
 
 	/* Weight: characteristic value */
 	sdp_uuid128_create(&uuid, char_weight_uuid);
@@ -403,7 +411,7 @@ static int register_attributes(void)
 	atval[1] = 0x55;
 	atval[2] = 0x00;
 	atval[3] = 0x00;
-	attrib_db_add(0x0683, &uuid, atval, 4);
+	attrib_db_add(0x0683, &uuid, ATT_ACCESS(ATT_READ, ATT_NONE), atval, 4);
 
 	/* Weight: characteristic format */
 	sdp_uuid16_create(&uuid, GATT_CHARAC_FMT_UUID);
@@ -418,13 +426,14 @@ static int register_attributes(void)
 	u16 = htons(FMT_HANGING_UUID);
 	atval[6] = u16 >> 8;
 	atval[7] = u16;
-	attrib_db_add(0x0684, &uuid, atval, 8);
+	attrib_db_add(0x0684, &uuid, ATT_ACCESS(ATT_READ, ATT_NONE), atval, 8);
 
 	/* Weight: characteristic user description */
 	sdp_uuid16_create(&uuid, GATT_CHARAC_USER_DESC_UUID);
 	len = strlen(desc_weight);
 	strncpy((char *) atval, desc_weight, len);
-	attrib_db_add(0x0685, &uuid, atval, len);
+	attrib_db_add(0x0685, &uuid, ATT_ACCESS(ATT_READ, ATT_NONE), atval,
+									len);
 
 	return 0;
 }
diff --git a/src/attrib-server.c b/src/attrib-server.c
index 41c0ffc..4199355 100644
--- a/src/attrib-server.c
+++ b/src/attrib-server.c
@@ -760,7 +760,8 @@ void attrib_server_exit(void)
 		remove_record_from_server(sdp_handle);
 }
 
-int attrib_db_add(uint16_t handle, uuid_t *uuid, const uint8_t *value, int len)
+int attrib_db_add(uint16_t handle, uuid_t *uuid, uint8_t perms,
+						const uint8_t *value, int len)
 {
 	struct attribute *a;
 
@@ -769,6 +770,7 @@ int attrib_db_add(uint16_t handle, uuid_t *uuid, const uint8_t *value, int len)
 	a = g_malloc0(sizeof(struct attribute) + len);
 	a->handle = handle;
 	memcpy(&a->uuid, uuid, sizeof(uuid_t));
+	a->perms = perms;
 	a->len = len;
 	memcpy(a->data, value, len);
 
diff --git a/src/attrib-server.h b/src/attrib-server.h
index 4a0afa6..e74a134 100644
--- a/src/attrib-server.h
+++ b/src/attrib-server.h
@@ -25,7 +25,18 @@
 int attrib_server_init(void);
 void attrib_server_exit(void);
 
-int attrib_db_add(uint16_t handle, uuid_t *uuid, const uint8_t *value, int len);
+/* Access permissions */
+#define ATT_READ	1
+#define ATT_WRITE	2
+/* Authentication/Authorization permissions */
+#define ATT_NONE		0
+#define ATT_AUTHENTICATION	2
+#define ATT_AUTHORIZATION	4
+/* Build bit mask for permission checks */
+#define ATT_ACCESS(x, y)	(((x) << y) | (x))
+
+int attrib_db_add(uint16_t handle, uuid_t *uuid, uint8_t perms,
+						const uint8_t *value, int len);
 int attrib_db_update(uint16_t handle, uuid_t *uuid, const uint8_t *value,
 								int len);
 int attrib_db_del(uint16_t handle);
-- 
1.7.0.4


^ permalink raw reply related	[flat|nested] 7+ messages in thread

* [PATCH 2/3] Check attribute permissions in attribute server
  2010-12-01 16:13 [PATCH 0/3] Basic attribute permission support Anderson Lizardo
  2010-12-01 16:13 ` [PATCH 1/3] Initial attribute permission implementation Anderson Lizardo
@ 2010-12-01 16:13 ` Anderson Lizardo
  2010-12-01 16:13 ` [PATCH 3/3] Check authentication permissions on " Anderson Lizardo
  2 siblings, 0 replies; 7+ messages in thread
From: Anderson Lizardo @ 2010-12-01 16:13 UTC (permalink / raw)
  To: linux-bluetooth; +Cc: Bruna Moreira

From: Bruna Moreira <bruna.moreira@openbossa.org>

The attribute server must verify if the operation (read/write) is
permitted before running the request, and send "Read Not Permitted" or
"Write Not Permitted" error response to client if appropriate.
---
 TODO                |    7 -----
 src/attrib-server.c |   68 ++++++++++++++++++++++++++++++++++++++++++++++----
 2 files changed, 62 insertions(+), 13 deletions(-)

diff --git a/TODO b/TODO
index 49a9e76..32d3a61 100644
--- a/TODO
+++ b/TODO
@@ -136,13 +136,6 @@ ATT/GATT
   Priority: Low
   Complexity: C2
 
-- Attribute server shall implement attribute permission verification,
-  returning an error code if necessary. See Volume 3, Part F, 3.2.5
-  for more information.
-
-  Priority: Low
-  Complexity: C2
-
 - Implement Client Characteristic Configuration support in the attribute
   server to manage indications and notications. This is a per client attribute
   to control how the client wants to receive reports of changes in a given
diff --git a/src/attrib-server.c b/src/attrib-server.c
index 4199355..32357f0 100644
--- a/src/attrib-server.c
+++ b/src/attrib-server.c
@@ -138,6 +138,28 @@ static sdp_record_t *server_record_new(void)
 	return record;
 }
 
+static uint8_t att_check_perms(uint8_t opcode, uint8_t perms)
+{
+	switch (opcode) {
+	case ATT_OP_READ_BY_GROUP_REQ:
+	case ATT_OP_READ_BY_TYPE_REQ:
+	case ATT_OP_READ_REQ:
+	case ATT_OP_READ_BLOB_REQ:
+	case ATT_OP_READ_MULTI_REQ:
+		if (!(perms & ATT_ACCESS(ATT_READ, ATT_NONE)))
+			return ATT_ECODE_READ_NOT_PERM;
+		break;
+	case ATT_OP_PREP_WRITE_REQ:
+	case ATT_OP_WRITE_REQ:
+	case ATT_OP_WRITE_CMD:
+		if (!(perms & ATT_ACCESS(ATT_WRITE, ATT_NONE)))
+			return ATT_ECODE_WRITE_NOT_PERM;
+		break;
+	}
+
+	return 0;
+}
+
 static uint16_t read_by_group(uint16_t start, uint16_t end, uuid_t *uuid,
 							uint8_t *pdu, int len)
 {
@@ -146,6 +168,7 @@ static uint16_t read_by_group(uint16_t start, uint16_t end, uuid_t *uuid,
 	struct group_elem *cur, *old = NULL;
 	GSList *l, *groups;
 	uint16_t length, last_handle, last_size = 0;
+	uint8_t status;
 	int i;
 
 	if (start > end || start == 0x0000)
@@ -189,6 +212,14 @@ static uint16_t read_by_group(uint16_t start, uint16_t end, uuid_t *uuid,
 		if (last_size && (last_size != a->len))
 			break;
 
+		status = att_check_perms(ATT_OP_READ_BY_GROUP_REQ, a->perms);
+		if (status) {
+			g_slist_foreach(groups, (GFunc) g_free, NULL);
+			g_slist_free(groups);
+			return enc_error_resp(ATT_OP_READ_BY_GROUP_REQ,
+						a->handle, status, pdu, len);
+		}
+
 		cur = g_new0(struct group_elem, 1);
 		cur->handle = a->handle;
 		cur->data = a->data;
@@ -247,6 +278,7 @@ static uint16_t read_by_type(uint16_t start, uint16_t end, uuid_t *uuid,
 	GSList *l, *types;
 	struct attribute *a;
 	uint16_t num, length;
+	uint8_t status;
 	int i;
 
 	if (start > end || start == 0x0000)
@@ -265,6 +297,13 @@ static uint16_t read_by_type(uint16_t start, uint16_t end, uuid_t *uuid,
 		if (sdp_uuid_cmp(&a->uuid, uuid)  != 0)
 			continue;
 
+		status = att_check_perms(ATT_OP_READ_BY_TYPE_REQ, a->perms);
+		if (status) {
+			g_slist_free(types);
+			return enc_error_resp(ATT_OP_READ_BY_TYPE_REQ,
+						a->handle, status, pdu, len);
+		}
+
 		/* All elements must have the same length */
 		if (length == 0)
 			length = a->len;
@@ -472,6 +511,7 @@ static int attribute_cmp(gconstpointer a1, gconstpointer a2)
 static uint16_t read_value(uint16_t handle, uint8_t *pdu, int len)
 {
 	struct attribute *a;
+	uint8_t status;
 	GSList *l;
 	guint h = handle;
 
@@ -482,23 +522,41 @@ static uint16_t read_value(uint16_t handle, uint8_t *pdu, int len)
 
 	a = l->data;
 
+	status = att_check_perms(ATT_OP_READ_REQ, a->perms);
+	if (status)
+		return enc_error_resp(ATT_OP_READ_REQ, handle, status, pdu,
+									len);
+
 	return enc_read_resp(a->data, a->len, pdu, len);
 }
 
-static void write_value(uint16_t handle, const uint8_t *value, int vlen)
+static uint16_t write_value(uint16_t handle, const uint8_t *value, int vlen,
+							uint8_t *pdu, int len)
 {
 	struct attribute *a;
+	uint8_t status;
 	GSList *l;
 	guint h = handle;
 	uuid_t uuid;
 
 	l = g_slist_find_custom(database, GUINT_TO_POINTER(h), handle_cmp);
 	if (!l)
-		return;
+		return enc_error_resp(ATT_OP_WRITE_REQ, handle,
+				ATT_ECODE_INVALID_HANDLE, pdu, len);
 
 	a = l->data;
+
+	status = att_check_perms(ATT_OP_WRITE_REQ, a->perms);
+	if (status)
+		return enc_error_resp(ATT_OP_WRITE_REQ, handle, status, pdu,
+									len);
+
 	memcpy(&uuid, &a->uuid, sizeof(uuid_t));
 	attrib_db_update(handle, &uuid, value, vlen);
+
+	pdu[0] = ATT_OP_WRITE_RESP;
+
+	return sizeof(pdu[0]);
 }
 
 static uint16_t mtu_exchange(struct gatt_channel *channel, uint16_t mtu,
@@ -582,14 +640,12 @@ static void channel_handler(const uint8_t *ipdu, uint16_t len,
 			goto done;
 		}
 
-		write_value(start, value, vlen);
-		opdu[0] = ATT_OP_WRITE_RESP;
-		length = sizeof(opdu[0]);
+		length = write_value(start, value, vlen, opdu, channel->mtu);
 		break;
 	case ATT_OP_WRITE_CMD:
 		length = dec_write_cmd(ipdu, len, &start, value, &vlen);
 		if (length > 0)
-			write_value(start, value, vlen);
+			write_value(start, value, vlen, opdu, channel->mtu);
 		return;
 	case ATT_OP_FIND_BY_TYPE_REQ:
 		length = dec_find_by_type_req(ipdu, len, &start, &end,
-- 
1.7.0.4


^ permalink raw reply related	[flat|nested] 7+ messages in thread

* [PATCH 3/3] Check authentication permissions on attribute server
  2010-12-01 16:13 [PATCH 0/3] Basic attribute permission support Anderson Lizardo
  2010-12-01 16:13 ` [PATCH 1/3] Initial attribute permission implementation Anderson Lizardo
  2010-12-01 16:13 ` [PATCH 2/3] Check attribute permissions in attribute server Anderson Lizardo
@ 2010-12-01 16:13 ` Anderson Lizardo
  2 siblings, 0 replies; 7+ messages in thread
From: Anderson Lizardo @ 2010-12-01 16:13 UTC (permalink / raw)
  To: linux-bluetooth; +Cc: Anderson Lizardo

Attributes may require encryption for certain operations. This commit
adds checks to the attribute server which verify whether the current
connection is encrypted (currently done by checking the security level,
but may be changed later) and the attribute being accessed requires
authentication. If encryption requirements are not satisfied, the
"Insufficient Encryption" error is returned by the server.
---
 src/attrib-server.c |   76 +++++++++++++++++++++++++++++++++++++++-----------
 1 files changed, 59 insertions(+), 17 deletions(-)

diff --git a/src/attrib-server.c b/src/attrib-server.c
index 32357f0..fee5462 100644
--- a/src/attrib-server.c
+++ b/src/attrib-server.c
@@ -57,6 +57,7 @@ struct gatt_channel {
 	GAttrib *attrib;
 	guint mtu;
 	guint id;
+	uint8_t perms;
 };
 
 struct group_elem {
@@ -138,8 +139,11 @@ static sdp_record_t *server_record_new(void)
 	return record;
 }
 
-static uint8_t att_check_perms(uint8_t opcode, uint8_t perms)
+static uint8_t att_check_perms(struct gatt_channel *channel, uint8_t opcode,
+								uint8_t perms)
 {
+	uint8_t chp = channel->perms;
+
 	switch (opcode) {
 	case ATT_OP_READ_BY_GROUP_REQ:
 	case ATT_OP_READ_BY_TYPE_REQ:
@@ -148,20 +152,41 @@ static uint8_t att_check_perms(uint8_t opcode, uint8_t perms)
 	case ATT_OP_READ_MULTI_REQ:
 		if (!(perms & ATT_ACCESS(ATT_READ, ATT_NONE)))
 			return ATT_ECODE_READ_NOT_PERM;
+
+		if ((perms & ATT_ACCESS(ATT_READ, ATT_AUTHENTICATION)) !=
+				ATT_ACCESS(ATT_READ, ATT_AUTHENTICATION))
+			/* Attribute does not require authentication for read */
+			return 0;
+
+		if ((chp & ATT_ACCESS(ATT_READ, ATT_AUTHENTICATION)) !=
+				ATT_ACCESS(ATT_READ, ATT_AUTHENTICATION))
+			/* Connection is not encrypted */
+			return ATT_ECODE_INSUFF_AUTHEN;
 		break;
 	case ATT_OP_PREP_WRITE_REQ:
 	case ATT_OP_WRITE_REQ:
 	case ATT_OP_WRITE_CMD:
 		if (!(perms & ATT_ACCESS(ATT_WRITE, ATT_NONE)))
 			return ATT_ECODE_WRITE_NOT_PERM;
+
+		if ((perms & ATT_ACCESS(ATT_WRITE, ATT_AUTHENTICATION)) !=
+				ATT_ACCESS(ATT_WRITE, ATT_AUTHENTICATION))
+			/* Attribute does not require authentication for write */
+			return 0;
+
+		if ((chp & ATT_ACCESS(ATT_WRITE, ATT_AUTHENTICATION)) !=
+				ATT_ACCESS(ATT_WRITE, ATT_AUTHENTICATION))
+			/* Connection is not encrypted */
+			return ATT_ECODE_INSUFF_AUTHEN;
 		break;
 	}
 
 	return 0;
 }
 
-static uint16_t read_by_group(uint16_t start, uint16_t end, uuid_t *uuid,
-							uint8_t *pdu, int len)
+static uint16_t read_by_group(struct gatt_channel *channel, uint16_t start,
+						uint16_t end, uuid_t *uuid,
+						uint8_t *pdu, int len)
 {
 	struct att_data_list *adl;
 	struct attribute *a;
@@ -212,7 +237,8 @@ static uint16_t read_by_group(uint16_t start, uint16_t end, uuid_t *uuid,
 		if (last_size && (last_size != a->len))
 			break;
 
-		status = att_check_perms(ATT_OP_READ_BY_GROUP_REQ, a->perms);
+		status = att_check_perms(channel, ATT_OP_READ_BY_GROUP_REQ,
+								a->perms);
 		if (status) {
 			g_slist_foreach(groups, (GFunc) g_free, NULL);
 			g_slist_free(groups);
@@ -271,8 +297,9 @@ static uint16_t read_by_group(uint16_t start, uint16_t end, uuid_t *uuid,
 	return length;
 }
 
-static uint16_t read_by_type(uint16_t start, uint16_t end, uuid_t *uuid,
-							uint8_t *pdu, int len)
+static uint16_t read_by_type(struct gatt_channel *channel, uint16_t start,
+						uint16_t end, uuid_t *uuid,
+						uint8_t *pdu, int len)
 {
 	struct att_data_list *adl;
 	GSList *l, *types;
@@ -297,7 +324,8 @@ static uint16_t read_by_type(uint16_t start, uint16_t end, uuid_t *uuid,
 		if (sdp_uuid_cmp(&a->uuid, uuid)  != 0)
 			continue;
 
-		status = att_check_perms(ATT_OP_READ_BY_TYPE_REQ, a->perms);
+		status = att_check_perms(channel, ATT_OP_READ_BY_TYPE_REQ,
+								a->perms);
 		if (status) {
 			g_slist_free(types);
 			return enc_error_resp(ATT_OP_READ_BY_TYPE_REQ,
@@ -508,7 +536,8 @@ static int attribute_cmp(gconstpointer a1, gconstpointer a2)
 	return attrib1->handle - attrib2->handle;
 }
 
-static uint16_t read_value(uint16_t handle, uint8_t *pdu, int len)
+static uint16_t read_value(struct gatt_channel *channel, uint16_t handle,
+							uint8_t *pdu, int len)
 {
 	struct attribute *a;
 	uint8_t status;
@@ -522,7 +551,7 @@ static uint16_t read_value(uint16_t handle, uint8_t *pdu, int len)
 
 	a = l->data;
 
-	status = att_check_perms(ATT_OP_READ_REQ, a->perms);
+	status = att_check_perms(channel, ATT_OP_READ_REQ, a->perms);
 	if (status)
 		return enc_error_resp(ATT_OP_READ_REQ, handle, status, pdu,
 									len);
@@ -530,8 +559,9 @@ static uint16_t read_value(uint16_t handle, uint8_t *pdu, int len)
 	return enc_read_resp(a->data, a->len, pdu, len);
 }
 
-static uint16_t write_value(uint16_t handle, const uint8_t *value, int vlen,
-							uint8_t *pdu, int len)
+static uint16_t write_value(struct gatt_channel *channel, uint16_t handle,
+						const uint8_t *value, int vlen,
+						uint8_t *pdu, int len)
 {
 	struct attribute *a;
 	uint8_t status;
@@ -546,7 +576,7 @@ static uint16_t write_value(uint16_t handle, const uint8_t *value, int vlen,
 
 	a = l->data;
 
-	status = att_check_perms(ATT_OP_WRITE_REQ, a->perms);
+	status = att_check_perms(channel, ATT_OP_WRITE_REQ, a->perms);
 	if (status)
 		return enc_error_resp(ATT_OP_WRITE_REQ, handle, status, pdu,
 									len);
@@ -595,7 +625,8 @@ static void channel_handler(const uint8_t *ipdu, uint16_t len,
 			goto done;
 		}
 
-		length = read_by_group(start, end, &uuid, opdu, channel->mtu);
+		length = read_by_group(channel, start, end, &uuid, opdu,
+								channel->mtu);
 		break;
 	case ATT_OP_READ_BY_TYPE_REQ:
 		length = dec_read_by_type_req(ipdu, len, &start, &end, &uuid);
@@ -604,7 +635,8 @@ static void channel_handler(const uint8_t *ipdu, uint16_t len,
 			goto done;
 		}
 
-		length = read_by_type(start, end, &uuid, opdu, channel->mtu);
+		length = read_by_type(channel, start, end, &uuid, opdu,
+								channel->mtu);
 		break;
 	case ATT_OP_READ_REQ:
 		length = dec_read_req(ipdu, len, &start);
@@ -613,7 +645,7 @@ static void channel_handler(const uint8_t *ipdu, uint16_t len,
 			goto done;
 		}
 
-		length = read_value(start, opdu, channel->mtu);
+		length = read_value(channel, start, opdu, channel->mtu);
 		break;
 	case ATT_OP_MTU_REQ:
 		length = dec_mtu_req(ipdu, len, &mtu);
@@ -640,12 +672,14 @@ static void channel_handler(const uint8_t *ipdu, uint16_t len,
 			goto done;
 		}
 
-		length = write_value(start, value, vlen, opdu, channel->mtu);
+		length = write_value(channel, start, value, vlen, opdu,
+								channel->mtu);
 		break;
 	case ATT_OP_WRITE_CMD:
 		length = dec_write_cmd(ipdu, len, &start, value, &vlen);
 		if (length > 0)
-			write_value(start, value, vlen, opdu, channel->mtu);
+			write_value(channel, start, value, vlen, opdu,
+								channel->mtu);
 		return;
 	case ATT_OP_FIND_BY_TYPE_REQ:
 		length = dec_find_by_type_req(ipdu, len, &start, &end,
@@ -682,6 +716,7 @@ static void connect_event(GIOChannel *io, GError *err, void *user_data)
 {
 	struct gatt_channel *channel;
 	GError *gerr = NULL;
+	int sec_level;
 
 	if (err) {
 		error("%s", err->message);
@@ -693,6 +728,7 @@ static void connect_event(GIOChannel *io, GError *err, void *user_data)
 	bt_io_get(io, BT_IO_L2CAP, &gerr,
 			BT_IO_OPT_SOURCE_BDADDR, &channel->src,
 			BT_IO_OPT_DEST_BDADDR, &channel->dst,
+			BT_IO_OPT_SEC_LEVEL, &sec_level,
 			BT_IO_OPT_INVALID);
 	if (gerr) {
 		error("bt_io_get: %s", gerr->message);
@@ -705,6 +741,12 @@ static void connect_event(GIOChannel *io, GError *err, void *user_data)
 	channel->attrib = g_attrib_new(io);
 	channel->mtu = ATT_DEFAULT_MTU;
 
+	if (sec_level > BT_IO_SEC_LOW)
+		channel->perms = ATT_ACCESS(ATT_READ | ATT_WRITE,
+							ATT_AUTHENTICATION);
+	else
+		channel->perms = ATT_ACCESS(ATT_READ | ATT_WRITE, ATT_NONE);
+
 	channel->id = g_attrib_register(channel->attrib, GATTRIB_ALL_EVENTS,
 				channel_handler, channel, NULL);
 
-- 
1.7.0.4


^ permalink raw reply related	[flat|nested] 7+ messages in thread

* Re: [PATCH 1/3] Initial attribute permission implementation
  2010-12-01 16:13 ` [PATCH 1/3] Initial attribute permission implementation Anderson Lizardo
@ 2010-12-02 10:10   ` Johan Hedberg
  2010-12-02 13:33     ` Anderson Lizardo
  0 siblings, 1 reply; 7+ messages in thread
From: Johan Hedberg @ 2010-12-02 10:10 UTC (permalink / raw)
  To: Anderson Lizardo; +Cc: linux-bluetooth, Bruna Moreira

Hi,

On Wed, Dec 01, 2010, Anderson Lizardo wrote:
> +	attrib_db_add(0x0685, &uuid, ATT_ACCESS(ATT_READ, ATT_NONE), atval,
> +									len);
<snip>
> +/* Access permissions */
> +#define ATT_READ	1
> +#define ATT_WRITE	2
> +/* Authentication/Authorization permissions */
> +#define ATT_NONE		0
> +#define ATT_AUTHENTICATION	2
> +#define ATT_AUTHORIZATION	4
> +/* Build bit mask for permission checks */
> +#define ATT_ACCESS(x, y)	(((x) << y) | (x))

The last macro doesn't make any sense to me. Based the core spec it
seems that the values for the different permission types are
implementation specific and not exposed publicly (vol 2, part G, 2.5.1:
"Attribute Permissions is part of the Attribute that cannot be read from
or written to using the Attribute Protocol"). Why not make sure that
they are all orthogonal from the start instead of ssigning the same
value for authentication and writability like you do now. I.e. instead
you could have:

none		0
read		1
write		2
authentication	4
authorization	8

Then you can just or together whatever you want without having any
complex macros for producing the final permissions value. Alternatively
you could also have a simple enum and use set_bit/test_bit marcos (see
lib/hci_lib.h).

Johan

^ permalink raw reply	[flat|nested] 7+ messages in thread

* Re: [PATCH 1/3] Initial attribute permission implementation
  2010-12-02 10:10   ` Johan Hedberg
@ 2010-12-02 13:33     ` Anderson Lizardo
  2010-12-02 14:58       ` Johan Hedberg
  0 siblings, 1 reply; 7+ messages in thread
From: Anderson Lizardo @ 2010-12-02 13:33 UTC (permalink / raw)
  To: Anderson Lizardo, linux-bluetooth, Bruna Moreira

On Thu, Dec 2, 2010 at 6:10 AM, Johan Hedberg <johan.hedberg@gmail.com> wrote:
> Hi,
>
> On Wed, Dec 01, 2010, Anderson Lizardo wrote:
>> +     attrib_db_add(0x0685, &uuid, ATT_ACCESS(ATT_READ, ATT_NONE), atval,
>> +                                                                     len);
> <snip>
>> +/* Access permissions */
>> +#define ATT_READ     1
>> +#define ATT_WRITE    2
>> +/* Authentication/Authorization permissions */
>> +#define ATT_NONE             0
>> +#define ATT_AUTHENTICATION   2
>> +#define ATT_AUTHORIZATION    4
>> +/* Build bit mask for permission checks */
>> +#define ATT_ACCESS(x, y)     (((x) << y) | (x))
>
> The last macro doesn't make any sense to me. Based the core spec it
> seems that the values for the different permission types are
> implementation specific and not exposed publicly (vol 2, part G, 2.5.1:
> "Attribute Permissions is part of the Attribute that cannot be read from
> or written to using the Attribute Protocol").

Correct. The permission implementation is internal.

> Why not make sure that
> they are all orthogonal from the start instead of ssigning the same
> value for authentication and writability like you do now. I.e. instead
> you could have:
>
> none            0
> read            1
> write           2
> authentication  4
> authorization   8

This was the first attempt (as it seemed obvious). But see vol 3, part
F, section 4:

"For example, an attribute value may be allowed to be read by any device, but
only written by an authenticated device. An implementation should take this
into account, and not assume that just because it can read an attribute’s value,
it will also be able to write the value. [...]"

This makes it clear that the authentication/authorization permissions
are not orthogonal. Further on it is confirmed by the attribute
permissions of "Client Characteristic Configuration" (vol 3, part G,
3.3.3.4):

* Readable with no authentication or authorization.
* Writable with authentication and authorization defined by a higher
layer specification or is implementation specific.

The scheme implemented in this patch set is just a compacted version
of unix permissions using a 2-bit grouping (not sure if it will look
nice to read):

5 4 3 2 1 0  bit
| | | | | \_ Read
| | | | \___ Write
| | | \_____ Authenticated read
| | \_______ Authenticated write
| \_________ Authorized read
\___________ Authorized write

With an addition that, if the authenticated/authorized read/write bits
are set, the "none" read/write bits are set as well (I think the
reason is obvious).

> Then you can just or together whatever you want without having any
> complex macros for producing the final permissions value. Alternatively
> you could also have a simple enum and use set_bit/test_bit marcos (see
> lib/hci_lib.h).

If you have ideas to reuse the hci_lib.h macros and keep the semantics
defined in the spec at the same time, let us know.

>
> Johan
>

Regards,
-- 
Anderson Lizardo
OpenBossa Labs - INdT
Manaus - Brazil

^ permalink raw reply	[flat|nested] 7+ messages in thread

* Re: [PATCH 1/3] Initial attribute permission implementation
  2010-12-02 13:33     ` Anderson Lizardo
@ 2010-12-02 14:58       ` Johan Hedberg
  0 siblings, 0 replies; 7+ messages in thread
From: Johan Hedberg @ 2010-12-02 14:58 UTC (permalink / raw)
  To: Anderson Lizardo; +Cc: linux-bluetooth, Bruna Moreira

Hi Anderson,

On Thu, Dec 02, 2010, Anderson Lizardo wrote:
> > Why not make sure that they are all orthogonal from the start
> > instead of assigning the same value for authentication and
> > writability like you do now. I.e. instead you could have:
> >
> > none            0
> > read            1
> > write           2
> > authentication  4
> > authorization   8
> 
> This was the first attempt (as it seemed obvious). But see vol 3, part
> F, section 4:
> 
> "For example, an attribute value may be allowed to be read by any device, but
> only written by an authenticated device. An implementation should take this
> into account, and not assume that just because it can read an attribute’s value,
> it will also be able to write the value. [...]"
> 
> This makes it clear that the authentication/authorization permissions
> are not orthogonal. Further on it is confirmed by the attribute
> permissions of "Client Characteristic Configuration" (vol 3, part G,
> 3.3.3.4):
> 
> * Readable with no authentication or authorization.
> * Writable with authentication and authorization defined by a higher
> layer specification or is implementation specific.
> 
> The scheme implemented in this patch set is just a compacted version
> of unix permissions using a 2-bit grouping (not sure if it will look
> nice to read):
> 
> 5 4 3 2 1 0  bit
> | | | | | \_ Read
> | | | | \___ Write
> | | | \_____ Authenticated read
> | | \_______ Authenticated write
> | \_________ Authorized read
> \___________ Authorized write

Ok, now I understand what you were trying to accomplish. If you had put
all this into the commit message or as a code comment we'd have avoided
some confusion ;)

> With an addition that, if the authenticated/authorized read/write bits
> are set, the "none" read/write bits are set as well (I think the
> reason is obvious).

Wouldn't it be the other way around, i.e. if the "none" bits are set
(i.e.  no special authentication or authentication requirements) then
for higher security (authenticated or authorized) reads and writes would
also succeed.

Anyway, if the requirement checks are really supposed to be so flexible
and up to the profile implementation you will still have some gaps with
your scheme. It's not e.g. possible to say "allow write only if *both*
authenticated *and* authorized". So, for ultimate flexibility, I don't
think there's any other choice then have a profile specific callback
function that makes the decision. The callback would at least get the
attempted operation (read/write) as well as the current level of
security (authorized/authenticated) as parameters. Any thoughts on this?

Johan

^ permalink raw reply	[flat|nested] 7+ messages in thread

end of thread, other threads:[~2010-12-02 14:58 UTC | newest]

Thread overview: 7+ messages (download: mbox.gz follow: Atom feed
-- links below jump to the message on this page --
2010-12-01 16:13 [PATCH 0/3] Basic attribute permission support Anderson Lizardo
2010-12-01 16:13 ` [PATCH 1/3] Initial attribute permission implementation Anderson Lizardo
2010-12-02 10:10   ` Johan Hedberg
2010-12-02 13:33     ` Anderson Lizardo
2010-12-02 14:58       ` Johan Hedberg
2010-12-01 16:13 ` [PATCH 2/3] Check attribute permissions in attribute server Anderson Lizardo
2010-12-01 16:13 ` [PATCH 3/3] Check authentication permissions on " Anderson Lizardo

This is a public inbox, see mirroring instructions
for how to clone and mirror all data and code used for this inbox;
as well as URLs for NNTP newsgroup(s).