summaryrefslogtreecommitdiff
path: root/gobex
diff options
context:
space:
mode:
authorLuiz Augusto von Dentz <luiz.von.dentz@intel.com>2014-03-28 15:13:28 +0200
committerLuiz Augusto von Dentz <luiz.von.dentz@intel.com>2014-03-31 17:30:59 +0300
commit774aae64e8746fda9206164b08a352a708cb8aaa (patch)
tree6fedfa5214c35dc697a284a3084f6ba69336584d /gobex
parentab22fce6bb008088618839fdd33bb8a138f29117 (diff)
downloadbluez-774aae64e8746fda9206164b08a352a708cb8aaa.tar.gz
gobex: Respond to authentication challenge
This implements authentication response as defined in OBEX specification since it is a mandatory feature for many Bluetooth profiles.
Diffstat (limited to 'gobex')
-rw-r--r--gobex/gobex.c127
1 files changed, 113 insertions, 14 deletions
diff --git a/gobex/gobex.c b/gobex/gobex.c
index ca1594111..880ed9159 100644
--- a/gobex/gobex.c
+++ b/gobex/gobex.c
@@ -44,6 +44,13 @@
#define CONNID_INVALID 0xffffffff
+/* Challenge request */
+#define NONCE_TAG 0x00
+#define NONCE_LEN 16
+
+/* Challenge response */
+#define DIGEST_TAG 0x00
+
guint gobex_debug = 0;
struct srm_config {
@@ -85,6 +92,7 @@ struct _GObex {
guint16 tx_mtu;
guint32 conn_id;
+ GObexApparam *authchal;
GQueue *tx_queue;
@@ -106,6 +114,7 @@ struct pending_pkt {
gpointer rsp_data;
gboolean cancelled;
gboolean suspended;
+ gboolean authenticating;
};
struct req_handler {
@@ -546,26 +555,84 @@ static void init_connect_data(GObex *obex, struct connect_data *data)
memcpy(&data->mtu, &u16, sizeof(u16));
}
+static guint8 *digest_response(const guint8 *nonce)
+{
+ GChecksum *md5;
+ guint8 *result;
+ gsize size;
+
+ result = g_new0(guint8, NONCE_LEN);
+
+ md5 = g_checksum_new(G_CHECKSUM_MD5);
+ if (md5 == NULL)
+ return result;
+
+ g_checksum_update(md5, nonce, NONCE_LEN);
+ g_checksum_update(md5, (guint8 *) ":BlueZ", 6);
+
+ size = NONCE_LEN;
+ g_checksum_get_digest(md5, result, &size);
+
+ g_checksum_free(md5);
+
+ return result;
+}
+
+static void prepare_auth_rsp(GObex *obex, GObexPacket *rsp)
+{
+ GObexHeader *hdr;
+ GObexApparam *authrsp;
+ const guint8 *nonce;
+ guint8 *result;
+ gsize len;
+
+ /* Check if client is already responding to authentication challenge */
+ hdr = g_obex_packet_get_header(rsp, G_OBEX_HDR_AUTHRESP);
+ if (hdr)
+ goto done;
+
+ if (!g_obex_apparam_get_bytes(obex->authchal, NONCE_TAG, &nonce, &len))
+ goto done;
+
+ if (len != NONCE_LEN)
+ goto done;
+
+ result = digest_response(nonce);
+ authrsp = g_obex_apparam_set_bytes(NULL, DIGEST_TAG, result, NONCE_LEN);
+
+ hdr = g_obex_header_new_tag(G_OBEX_HDR_AUTHRESP, authrsp);
+ g_obex_packet_add_header(rsp, hdr);
+
+ g_obex_apparam_free(authrsp);
+
+done:
+ g_obex_apparam_free(obex->authchal);
+ obex->authchal = NULL;
+}
+
static void prepare_connect_rsp(GObex *obex, GObexPacket *rsp)
{
- GObexHeader *connid;
+ GObexHeader *hdr;
struct connect_data data;
static guint32 next_connid = 1;
init_connect_data(obex, &data);
g_obex_packet_set_data(rsp, &data, sizeof(data), G_OBEX_DATA_COPY);
- connid = g_obex_packet_get_header(rsp, G_OBEX_HDR_CONNECTION);
- if (connid != NULL) {
- g_obex_header_get_uint32(connid, &obex->conn_id);
- return;
+ hdr = g_obex_packet_get_header(rsp, G_OBEX_HDR_CONNECTION);
+ if (hdr) {
+ g_obex_header_get_uint32(hdr, &obex->conn_id);
+ goto done;
}
obex->conn_id = next_connid++;
- connid = g_obex_header_new_uint32(G_OBEX_HDR_CONNECTION,
- obex->conn_id);
- g_obex_packet_prepend_header(rsp, connid);
+ hdr = g_obex_header_new_uint32(G_OBEX_HDR_CONNECTION, obex->conn_id);
+ g_obex_packet_prepend_header(rsp, hdr);
+
+done:
+ if (obex->authchal)
+ prepare_auth_rsp(obex, rsp);
}
static void prepare_srm_rsp(GObex *obex, GObexPacket *pkt)
@@ -936,10 +1003,27 @@ done:
return ret;
}
+static void auth_challenge(GObex *obex)
+{
+ struct pending_pkt *p = obex->pending_req;
+
+ if (p->authenticating)
+ return;
+
+ p->authenticating = TRUE;
+
+ prepare_auth_rsp(obex, p->pkt);
+
+ /* Remove it as pending and add it back to the queue so it gets sent
+ * again */
+ obex->pending_req = NULL;
+ g_obex_send_internal(obex, p, NULL);
+}
+
static void parse_connect_data(GObex *obex, GObexPacket *pkt)
{
const struct connect_data *data;
- GObexHeader *connid;
+ GObexHeader *hdr;
guint16 u16;
size_t data_len;
@@ -954,9 +1038,13 @@ static void parse_connect_data(GObex *obex, GObexPacket *pkt)
obex->tx_mtu = obex->io_tx_mtu;
obex->tx_buf = g_realloc(obex->tx_buf, obex->tx_mtu);
- connid = g_obex_packet_get_header(pkt, G_OBEX_HDR_CONNECTION);
- if (connid != NULL)
- g_obex_header_get_uint32(connid, &obex->conn_id);
+ hdr = g_obex_packet_get_header(pkt, G_OBEX_HDR_CONNECTION);
+ if (hdr)
+ g_obex_header_get_uint32(hdr, &obex->conn_id);
+
+ hdr = g_obex_packet_get_header(pkt, G_OBEX_HDR_AUTHCHAL);
+ if (hdr)
+ obex->authchal = g_obex_header_get_apparam(hdr);
}
static gboolean parse_response(GObex *obex, GObexPacket *rsp)
@@ -968,8 +1056,11 @@ static gboolean parse_response(GObex *obex, GObexPacket *rsp)
rspcode = g_obex_packet_get_operation(rsp, &final);
opcode = g_obex_packet_get_operation(p->pkt, NULL);
- if (opcode == G_OBEX_OP_CONNECT)
+ if (opcode == G_OBEX_OP_CONNECT) {
parse_connect_data(obex, rsp);
+ if (rspcode == G_OBEX_RSP_UNAUTHORIZED && obex->authchal)
+ auth_challenge(obex);
+ }
setup_srm(obex, rsp, FALSE);
@@ -993,12 +1084,17 @@ static gboolean parse_response(GObex *obex, GObexPacket *rsp)
static void handle_response(GObex *obex, GError *err, GObexPacket *rsp)
{
- struct pending_pkt *p = obex->pending_req;
+ struct pending_pkt *p;
gboolean disconn = err ? TRUE : FALSE, final_rsp = TRUE;
if (rsp != NULL)
final_rsp = parse_response(obex, rsp);
+ if (!obex->pending_req)
+ return;
+
+ p = obex->pending_req;
+
if (p->cancelled)
err = g_error_new(G_OBEX_ERROR, G_OBEX_ERROR_CANCELLED,
"The operation was cancelled");
@@ -1420,6 +1516,9 @@ void g_obex_unref(GObex *obex)
if (obex->pending_req)
pending_pkt_free(obex->pending_req);
+ if (obex->authchal)
+ g_obex_apparam_free(obex->authchal);
+
g_free(obex);
}