diff options
| author | Claudio Takahasi <claudio.takahasi@openbossa.org> | 2007-03-27 21:34:13 +0000 | 
|---|---|---|
| committer | Claudio Takahasi <claudio.takahasi@openbossa.org> | 2007-03-27 21:34:13 +0000 | 
| commit | 556bafad380353f745f8c0a54b9753967e75a1c2 (patch) | |
| tree | 69fd32bd45028cf11d09df21c65c681aea7969e4 | |
| parent | 580d52c4b07c5e44b17e7d0189e7d84f4a7667fb (diff) | |
network: Added authorization for incomming connections
| -rw-r--r-- | network/server.c | 277 | 
1 files changed, 265 insertions, 12 deletions
| diff --git a/network/server.c b/network/server.c index 1a02bb50..e33e7133 100644 --- a/network/server.c +++ b/network/server.c @@ -25,6 +25,7 @@  #include <config.h>  #endif +#include <stdio.h>  #include <unistd.h>  #include <stdlib.h>  #include <errno.h> @@ -36,6 +37,8 @@  #include <bluetooth/sdp.h>  #include <bluetooth/sdp_lib.h> +#include <netinet/in.h> +  #include <glib.h>  #include "logging.h" @@ -47,17 +50,40 @@  #include "common.h"  #include "server.h" +/* Pending Authorization */ +struct pending_auth { +	char				*addr;	/* Bluetooth Address */ +	GIOChannel			*io;	/* BNEP connection setup io channel */ +}; + +/* Main server structure */  struct network_server { -	char		*iface;		/* Routing interface */ -	char		*name;		/* Server service name */ -	char		*path; 		/* D-Bus path */ -	dbus_bool_t	secure; -	uint32_t	record_id;	/* Service record id */ -	uint16_t	id;		/* Service class identifier */ -	GIOChannel	*io;		/* GIOChannel when listening */ +	char			*iface;		/* Routing interface */ +	char			*name;		/* Server service name */ +	char			*path; 		/* D-Bus path */ +	dbus_bool_t		secure; +	uint32_t		record_id;	/* Service record id */ +	uint16_t		id;		/* Service class identifier */ +	GIOChannel		*io;		/* GIOChannel when listening */ +	DBusConnection 		*conn;		/* D-Bus connection */ +	struct pending_auth	*pauth;		/* Pending incomming connection/authorization */  }; -void add_lang_attr(sdp_record_t *r) +static char netdev[16] = "bnep%d"; + +static void pending_auth_free(struct pending_auth *pauth) +{ +	if (!pauth) +		return; +	if (pauth->addr) +		g_free(pauth->addr); +	/* FIXME: Is it necessary close the BNEP socket? */ +	if (pauth->io) +		g_io_channel_unref(pauth->io); +	g_free(pauth); +} + +static void add_lang_attr(sdp_record_t *r)  {  	sdp_lang_attr_t base_lang;  	sdp_list_t *langs = 0; @@ -187,11 +213,228 @@ static int create_server_record(sdp_buf_t *buf, uint16_t id)  	return ret;  } +static int send_bnep_ctrl_rsp(GIOChannel *chan, uint16_t response) +{ +	struct bnep_control_rsp rsp; +	GIOError gerr; +	gsize n; + +	rsp.type = BNEP_CONTROL; +	rsp.ctrl = BNEP_SETUP_CONN_RSP; +	rsp.resp = htons(response); + +	gerr = g_io_channel_write(chan, (gchar *)&rsp, sizeof(rsp), &n); + +	return -gerr; +} + +static void authorization_callback(DBusPendingCall *pcall, void *data) +{ +	struct network_server *ns = data; +	DBusMessage *reply = dbus_pending_call_steal_reply(pcall); +	DBusError derr; +	uint16_t response; + +	if (!ns->pauth) +		goto failed; + +	dbus_error_init(&derr); +	if (dbus_set_error_from_message(&derr, reply)) { +		error("Access denied: %s", derr.message); +		response = BNEP_CONN_NOT_ALLOWED; +	} else { +		char devname[16]; +		int sk; + +		response = BNEP_SUCCESS; + +		memset(devname, 0, 16); +		strncpy(devname, netdev, 16); + +		/* FIXME: Is it the correct order? */ +		sk = g_io_channel_unix_get_fd(ns->pauth->io); +		bnep_connadd(sk, BNEP_SVC_PANU, devname); + +		info("Authorization succedded. New connection: %s", devname); + +		/* FIXME: send the D-Bus message to notify the new bnep iface */ +	} + +	send_bnep_ctrl_rsp(ns->pauth->io, response); + +	pending_auth_free(ns->pauth); +	ns->pauth = NULL; + +failed: +	dbus_message_unref(reply); +	dbus_pending_call_unref(pcall); +} + +static int authorize_connection(struct network_server *ns) +{ +	DBusMessage *msg; +	DBusPendingCall *pending; +	const char *uuid = ""; /* FIXME: */ + +	msg = dbus_message_new_method_call("org.bluez", "/org/bluez", +				"org.bluez.Database", "RequestAuthorization"); +	if (!msg) { +		error("Unable to allocat new RequestAuthorization method call"); +		return -ENOMEM; +	} + +	debug("Requesting authorization for %s UUID:%s", ns->pauth->addr, uuid); + +	dbus_message_append_args(msg, +			DBUS_TYPE_STRING, &ns->pauth->addr, +			DBUS_TYPE_STRING, &uuid, +			DBUS_TYPE_INVALID); + +	if (dbus_connection_send_with_reply(ns->conn, msg, &pending, -1) == FALSE) { +		error("Sending of authorization request failed"); +		return -EACCES; +	} + +	dbus_pending_call_set_notify(pending, authorization_callback, ns, NULL); +	dbus_message_unref(msg); + +	return 0; +} + +static gboolean connect_setup_event(GIOChannel *chan, +					GIOCondition cond, gpointer data) +{ +	struct network_server *ns = data; +	struct bnep_setup_conn_req *req; +	unsigned char pkt[BNEP_MTU]; +	gsize n; +	GIOError gerr; +	uint8_t *pservice; +	uint16_t role, response; + +	if (cond & G_IO_NVAL) +		return FALSE; + +	if (cond & (G_IO_ERR | G_IO_HUP)) { +		error("Hangup or error on L2CAP socket"); +		/* FIXME: Cancel the pending authorization if applied */ +		return FALSE; +	} + +	/* FIXME: Missing address setup connection request retries */ + +	gerr = g_io_channel_read(chan, (gchar *)pkt, sizeof(pkt) - 1, &n); +	if (gerr != G_IO_ERROR_NONE) +		return FALSE; + +	if (n < sizeof(*req)) { +		error("Invalid BNEP packet size"); +		return FALSE; +	} + +	req = (void *)pkt; +	if (req->type != BNEP_CONTROL || req->ctrl != BNEP_SETUP_CONN_REQ) { +		error("Invalid BNEP control packet content"); +		return FALSE; +	} + +	/*  +	 * FIXME: According to BNEP SPEC the UUID size can be +	 * 2-16 bytes. Currently only 2 bytes size is supported +	 */ +	if (req->uuid_size != 2) { +		response = BNEP_CONN_INVALID_SVC;  +		goto reply; +	} + +	pservice = req->service; +	/* Getting destination service: considering 2 bytes size */ +	role = ntohs(bt_get_unaligned((uint16_t *) pservice)); + +	pservice += req->uuid_size; +	/* Getting source service: considering 2 bytes size */ +	role = ntohs(bt_get_unaligned((uint16_t *) pservice)); + +	if (role != BNEP_SVC_PANU) { +		response = BNEP_CONN_INVALID_SRC; +		goto reply; +	} + +	/* Wait authorization before reply success */ +	if (authorize_connection(ns) < 0) { +		response = BNEP_CONN_NOT_ALLOWED; +		goto reply; + +	} + +	return TRUE; +reply: +	send_bnep_ctrl_rsp(chan, response); +	return FALSE; +} + +static void connect_setup_destroy(gpointer data) +{ +	struct network_server *ns = data; + +	if (ns->pauth) { +		pending_auth_free(ns->pauth); +		ns->pauth = NULL; +	} +} +  static gboolean connect_event(GIOChannel *chan,  				GIOCondition cond, gpointer data)  { -	info("FIXME: Connect event"); -	return FALSE; +	struct network_server *ns = data; +	struct sockaddr_l2 addr; +	socklen_t addrlen; +	char peer[18]; +	bdaddr_t dst; +	unsigned short psm; +	int sk, nsk; + +	if (cond & G_IO_NVAL) +		return FALSE; + +	if (cond & (G_IO_ERR | G_IO_HUP)) { +		error("Hangup or error on L2CAP socket PSM 15"); +		/* FIXME: Notify the userspace? */ +		return FALSE; +	} + +	sk = g_io_channel_unix_get_fd(chan); + +	memset(&addr, 0, sizeof(addr)); +	addrlen = sizeof(addr); + +	nsk = accept(sk, (struct sockaddr *) &addr, &addrlen); +	if (nsk < 0) +		return TRUE; + +	bacpy(&dst, &addr.l2_bdaddr); +	psm = btohs(addr.l2_psm); + +	/* FIXME: Maybe keep a list of connected devices */ + +	ba2str(&dst, peer); +	info("Incoming connection from:%s on PSM %d", peer, psm); + +	/* FIXME: HOW handle multiple incomming connections? */ + +	/* Setting the pending incomming connection setup */ +	ns->pauth = g_new0(struct pending_auth, 1); +	ns->pauth->addr = g_strdup(peer); +	ns->pauth->io = g_io_channel_unix_new(nsk); + +	g_io_channel_set_close_on_unref(ns->pauth->io, FALSE); + +	/* New watch for BNEP setup */ +	g_io_add_watch_full(ns->pauth->io, G_PRIORITY_DEFAULT, +		G_IO_IN | G_IO_HUP | G_IO_ERR | G_IO_NVAL, +		connect_setup_event, ns, &connect_setup_destroy); + +	return TRUE;  }  static int l2cap_listen(struct network_server *ns) @@ -248,7 +491,7 @@ static int l2cap_listen(struct network_server *ns)  		goto fail;  	} -	if (listen(sk, 10) < 0) { +	if (listen(sk, 1) < 0) {  		err = errno;  		error("Listen failed. %s(%d)", strerror(err), err);  		goto fail; @@ -257,7 +500,8 @@ static int l2cap_listen(struct network_server *ns)  	ns->io = g_io_channel_unix_new(sk);  	g_io_channel_set_close_on_unref(ns->io, TRUE); -	g_io_add_watch(ns->io, G_IO_IN, connect_event, NULL); +	g_io_add_watch(ns->io, G_IO_IN | G_IO_HUP | G_IO_ERR | G_IO_NVAL, +							connect_event, ns);  	return 0;  fail: @@ -559,6 +803,14 @@ static void server_free(struct network_server *ns)  	if (ns->path)  		g_free(ns->path); +	if (ns->conn) +		dbus_connection_unref(ns->conn); + +	if (ns->io) +		g_io_channel_unref(ns->io); + +	/* FIXME: Missing release/free all bnepX interfaces */ +  	g_free(ns);  } @@ -598,6 +850,7 @@ int server_register(DBusConnection *conn, const char *path, uint16_t id)  	ns->path = g_strdup(path);  	ns->id = id; +	ns->conn = dbus_connection_ref(conn);  	info("Registered server path:%s", ns->path);  	return 0; | 
