• Home
  • Line#
  • Scopes#
  • Navigate#
  • Raw
  • Download
1 /*
2  *
3  *  BlueZ - Bluetooth protocol stack for Linux
4  *
5  *  Copyright (C) 2006-2008  Nokia Corporation
6  *  Copyright (C) 2004-2008  Marcel Holtmann <marcel@holtmann.org>
7  *
8  *
9  *  This program is free software; you can redistribute it and/or modify
10  *  it under the terms of the GNU General Public License as published by
11  *  the Free Software Foundation; either version 2 of the License, or
12  *  (at your option) any later version.
13  *
14  *  This program is distributed in the hope that it will be useful,
15  *  but WITHOUT ANY WARRANTY; without even the implied warranty of
16  *  MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
17  *  GNU General Public License for more details.
18  *
19  *  You should have received a copy of the GNU General Public License
20  *  along with this program; if not, write to the Free Software
21  *  Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA  02110-1301  USA
22  *
23  */
24 
25 #ifdef HAVE_CONFIG_H
26 #include <config.h>
27 #endif
28 
29 #include <stdio.h>
30 #include <errno.h>
31 #include <stdlib.h>
32 #include <sys/socket.h>
33 #include <sys/ioctl.h>
34 
35 #include <bluetooth/bluetooth.h>
36 #include <bluetooth/hci.h>
37 #include <bluetooth/hci_lib.h>
38 #include <bluetooth/sdp.h>
39 
40 #include <glib.h>
41 #include <dbus/dbus.h>
42 #include <gdbus.h>
43 
44 #include "hcid.h"
45 #include "dbus-common.h"
46 #include "dbus-service.h"
47 #include "dbus-error.h"
48 #include "error.h"
49 #include "adapter.h"
50 #include "dbus-hci.h"
51 #include "device.h"
52 #include "agent.h"
53 
54 #define REQUEST_TIMEOUT (60 * 1000)		/* 60 seconds */
55 #define AGENT_TIMEOUT (10 * 60 * 1000)		/* 10 minutes */
56 
57 typedef enum {
58 	AGENT_REQUEST_PASSKEY,
59 	AGENT_REQUEST_CONFIRMATION,
60 	AGENT_REQUEST_PINCODE,
61 	AGENT_REQUEST_AUTHORIZE,
62 	AGENT_REQUEST_CONFIRM_MODE
63 } agent_request_type_t;
64 
65 struct agent {
66 	struct adapter *adapter;
67 	char *name;
68 	char *path;
69 	uint8_t capability;
70 	struct agent_request *request;
71 	int exited;
72 	agent_remove_cb remove_cb;
73 	void *remove_cb_data;
74 	guint listener_id;
75 };
76 
77 struct agent_request {
78 	agent_request_type_t type;
79 	struct agent *agent;
80 	DBusPendingCall *call;
81 	void *cb;
82 	void *user_data;
83 };
84 
85 static DBusConnection *connection = NULL;
86 
agent_release(struct agent * agent)87 static void agent_release(struct agent *agent)
88 {
89 	DBusMessage *message;
90 
91 	debug("Releasing agent %s, %s", agent->name, agent->path);
92 
93 	if (agent->request)
94 		agent_cancel(agent);
95 
96 	message = dbus_message_new_method_call(agent->name, agent->path,
97 			"org.bluez.Agent", "Release");
98 	if (message == NULL) {
99 		error("Couldn't allocate D-Bus message");
100 		return;
101 	}
102 
103 	dbus_message_set_no_reply(message, TRUE);
104 
105 	dbus_connection_send(connection, message, NULL);
106 
107 	dbus_message_unref(message);
108 }
109 
send_cancel_request(struct agent_request * req)110 static int send_cancel_request(struct agent_request *req)
111 {
112 	DBusMessage *message;
113 
114 	message = dbus_message_new_method_call(req->agent->name, req->agent->path,
115 						"org.bluez.Agent", "Cancel");
116 	if (message == NULL) {
117 		error("Couldn't allocate D-Bus message");
118 		return -ENOMEM;
119 	}
120 
121 	dbus_message_set_no_reply(message, TRUE);
122 
123 	dbus_connection_send(connection, message, NULL);
124 
125 	dbus_message_unref(message);
126 
127 	return 0;
128 }
129 
agent_request_free(struct agent_request * req)130 static void agent_request_free(struct agent_request *req)
131 {
132 	if (req->call)
133 		dbus_pending_call_unref(req->call);
134 	if (req->agent && req->agent->request)
135 		req->agent->request = NULL;
136 	g_free(req);
137 }
138 
agent_exited(void * user_data)139 static void agent_exited(void *user_data)
140 {
141 	struct agent *agent = user_data;
142 
143 	debug("Agent exited without calling Unregister");
144 
145 	agent_destroy(agent, TRUE);
146 }
147 
agent_free(struct agent * agent)148 static void agent_free(struct agent *agent)
149 {
150 	if (!agent)
151 		return;
152 
153 	if (agent->remove_cb)
154 		agent->remove_cb(agent, agent->remove_cb_data);
155 
156 	if (agent->request) {
157 		DBusError err;
158 		agent_pincode_cb pincode_cb;
159 		agent_cb cb;
160 
161 		dbus_error_init(&err);
162 		dbus_set_error_const(&err, "org.bluez.Error.Failed", "Canceled");
163 
164 		switch (agent->request->type) {
165 		case AGENT_REQUEST_PINCODE:
166 			pincode_cb = agent->request->cb;
167 			pincode_cb(agent, &err, NULL, agent->request->user_data);
168 			break;
169 		default:
170 			cb = agent->request->cb;
171 			cb(agent, &err, agent->request->user_data);
172 		}
173 
174 		dbus_error_free(&err);
175 
176 		agent_cancel(agent);
177 	}
178 
179 	if (!agent->exited) {
180 		g_dbus_remove_watch(connection, agent->listener_id);
181 		agent_release(agent);
182 	}
183 
184 	g_free(agent->name);
185 	g_free(agent->path);
186 
187 	g_free(agent);
188 }
189 
agent_create(struct adapter * adapter,const char * name,const char * path,uint8_t capability,agent_remove_cb cb,void * remove_cb_data)190 struct agent *agent_create(struct adapter *adapter, const char *name,
191 				const char *path, uint8_t capability,
192 				agent_remove_cb cb, void *remove_cb_data)
193 {
194 	struct agent *agent;
195 
196 	if (adapter->agent && g_str_equal(adapter->agent->name, name))
197 		return NULL;
198 
199 	agent = g_new0(struct agent, 1);
200 
201 	agent->adapter = adapter;
202 	agent->name = g_strdup(name);
203 	agent->path = g_strdup(path);
204 	agent->capability = capability;
205 	agent->remove_cb = cb;
206 	agent->remove_cb_data = remove_cb_data;
207 
208 	agent->listener_id = g_dbus_add_disconnect_watch(connection, name,
209 							agent_exited, agent,
210 							NULL);
211 
212 	return agent;
213 }
214 
agent_destroy(struct agent * agent,gboolean exited)215 int agent_destroy(struct agent *agent, gboolean exited)
216 {
217 	agent->exited = exited;
218 	agent_free(agent);
219 	return 0;
220 }
221 
agent_request_new(struct agent * agent,agent_request_type_t type,void * cb,void * user_data)222 static struct agent_request *agent_request_new(struct agent *agent,
223 						agent_request_type_t type,
224 						void *cb,
225 						void *user_data)
226 {
227 	struct agent_request *req;
228 
229 	req = g_new0(struct agent_request, 1);
230 
231 	req->agent = agent;
232 	req->type = type;
233 	req->cb = cb;
234 	req->user_data = user_data;
235 
236 	return req;
237 }
238 
agent_cancel(struct agent * agent)239 int agent_cancel(struct agent *agent)
240 {
241 	if (!agent->request)
242 		return -EINVAL;
243 
244 	if (agent->request->call)
245 		dbus_pending_call_cancel(agent->request->call);
246 
247 	if (!agent->exited)
248 		send_cancel_request(agent->request);
249 
250 	agent_request_free(agent->request);
251 	agent->request = NULL;
252 
253 	return 0;
254 }
255 
agent_call_authorize(struct agent * agent,const char * device_path,const char * uuid)256 static DBusPendingCall *agent_call_authorize(struct agent *agent,
257 						const char *device_path,
258 						const char *uuid)
259 {
260 	DBusMessage *message;
261 	DBusPendingCall *call;
262 
263 	message = dbus_message_new_method_call(agent->name, agent->path,
264 				"org.bluez.Agent", "Authorize");
265 	if (!message) {
266 		error("Couldn't allocate D-Bus message");
267 		return NULL;
268 	}
269 
270 	dbus_message_append_args(message,
271 				DBUS_TYPE_OBJECT_PATH, &device_path,
272 				DBUS_TYPE_STRING, &uuid,
273 				DBUS_TYPE_INVALID);
274 
275 	if (dbus_connection_send_with_reply(connection, message,
276 					&call, REQUEST_TIMEOUT) == FALSE) {
277 		error("D-Bus send failed");
278 		dbus_message_unref(message);
279 		return NULL;
280 	}
281 
282 	dbus_message_unref(message);
283 	return call;
284 }
285 
simple_agent_reply(DBusPendingCall * call,void * user_data)286 static void simple_agent_reply(DBusPendingCall *call, void *user_data)
287 {
288 	struct agent_request *req = user_data;
289 	struct agent *agent = req->agent;
290 	DBusMessage *message;
291 	DBusError err;
292 	agent_cb cb = req->cb;
293 
294 	/* steal_reply will always return non-NULL since the callback
295 	 * is only called after a reply has been received */
296 	message = dbus_pending_call_steal_reply(call);
297 
298 	dbus_error_init(&err);
299 	if (dbus_set_error_from_message(&err, message)) {
300 
301 		error("Agent replied with an error: %s, %s",
302 				err.name, err.message);
303 
304 		cb(agent, &err, req->user_data);
305 		dbus_error_free(&err);
306 		goto done;
307 	}
308 
309 	dbus_error_init(&err);
310 	if (!dbus_message_get_args(message, &err, DBUS_TYPE_INVALID)) {
311 		error("Wrong reply signature: %s", err.message);
312 		cb(agent, &err, req->user_data);
313 		dbus_error_free(&err);
314 		goto done;
315 	}
316 
317 	cb(agent, NULL, req->user_data);
318 done:
319 	dbus_message_unref(message);
320 
321 	agent->request = NULL;
322 	agent_request_free(req);
323 }
324 
agent_authorize(struct agent * agent,const char * path,const char * uuid,agent_cb cb,void * user_data)325 int agent_authorize(struct agent *agent,
326 			const char *path,
327 			const char *uuid,
328 			agent_cb cb,
329 			void *user_data)
330 {
331 	struct agent_request *req;
332 
333 	if (agent->request)
334 		return -EBUSY;
335 
336 	req = agent_request_new(agent, AGENT_REQUEST_AUTHORIZE, cb, user_data);
337 
338 	req->call = agent_call_authorize(agent, path, uuid);
339 	if (!req->call) {
340 		agent_request_free(req);
341 		return DBUS_HANDLER_RESULT_NEED_MEMORY;
342 	}
343 
344 	dbus_pending_call_set_notify(req->call, simple_agent_reply, req, NULL);
345 	agent->request = req;
346 
347 	debug("authorize request was sent for %s", path);
348 
349 	return 0;
350 }
351 
pincode_request_new(struct agent * agent,const char * device_path,dbus_bool_t numeric)352 static DBusPendingCall *pincode_request_new(struct agent *agent,
353 						const char *device_path,
354 						dbus_bool_t numeric)
355 {
356 	DBusMessage *message;
357 	DBusPendingCall *call;
358 
359 	message = dbus_message_new_method_call(agent->name, agent->path,
360 					"org.bluez.Agent", "RequestPinCode");
361 	if (message == NULL) {
362 		error("Couldn't allocate D-Bus message");
363 		return NULL;
364 	}
365 
366 	dbus_message_append_args(message, DBUS_TYPE_OBJECT_PATH, &device_path,
367 					DBUS_TYPE_INVALID);
368 
369 	if (dbus_connection_send_with_reply(connection, message,
370 					&call, REQUEST_TIMEOUT) == FALSE) {
371 		error("D-Bus send failed");
372 		dbus_message_unref(message);
373 		return NULL;
374 	}
375 
376 	dbus_message_unref(message);
377 	return call;
378 }
379 
pincode_reply(DBusPendingCall * call,void * user_data)380 static void pincode_reply(DBusPendingCall *call, void *user_data)
381 {
382 	struct agent_request *req = user_data;
383 	struct agent *agent = req->agent;
384 	struct adapter *adapter = agent->adapter;
385 	agent_pincode_cb cb = req->cb;
386 	DBusMessage *message;
387 	DBusError err;
388 	bdaddr_t sba;
389 	size_t len;
390 	char *pin;
391 
392 	/* steal_reply will always return non-NULL since the callback
393 	 * is only called after a reply has been received */
394 	message = dbus_pending_call_steal_reply(call);
395 
396 	dbus_error_init(&err);
397 	if (dbus_set_error_from_message(&err, message)) {
398 		error("Agent replied with an error: %s, %s",
399 				err.name, err.message);
400 
401 		cb(agent, &err, NULL, req->user_data);
402 		dbus_error_free(&err);
403 		goto done;
404 	}
405 
406 	dbus_error_init(&err);
407 	if (!dbus_message_get_args(message, &err,
408 				DBUS_TYPE_STRING, &pin,
409 				DBUS_TYPE_INVALID)) {
410 		error("Wrong passkey reply signature: %s", err.message);
411 		cb(agent, &err, NULL, req->user_data);
412 		dbus_error_free(&err);
413 		goto done;
414 	}
415 
416 	len = strlen(pin);
417 
418 	dbus_error_init(&err);
419 	if (len > 16 || len < 1) {
420 		error("Invalid passkey length from handler");
421 		dbus_set_error_const(&err, "org.bluez.Error.InvalidArgs",
422 					"Invalid passkey length");
423 		cb(agent, &err, NULL, req->user_data);
424 		dbus_error_free(&err);
425 		goto done;
426 	}
427 
428 	str2ba(adapter->address, &sba);
429 
430 	set_pin_length(&sba, len);
431 
432 	cb(agent, NULL, pin, req->user_data);
433 
434 done:
435 	if (message)
436 		dbus_message_unref(message);
437 
438 	dbus_pending_call_cancel(req->call);
439 	agent_request_free(req);
440 }
441 
agent_request_pincode(struct agent * agent,struct device * device,agent_pincode_cb cb,void * user_data)442 int agent_request_pincode(struct agent *agent, struct device *device,
443 				agent_pincode_cb cb, void *user_data)
444 {
445 	struct agent_request *req;
446 
447 	if (agent->request)
448 		return -EBUSY;
449 
450 	req = agent_request_new(agent, AGENT_REQUEST_PINCODE, cb, user_data);
451 
452 	req->call = pincode_request_new(agent, device->path, FALSE);
453 	if (!req->call)
454 		goto failed;
455 
456 	dbus_pending_call_set_notify(req->call, pincode_reply, req, NULL);
457 
458 	agent->request = req;
459 
460 	return 0;
461 
462 failed:
463 	g_free(req);
464 	return -1;
465 }
466 
confirm_mode_change_request_new(struct agent * agent,const char * mode)467 static DBusPendingCall *confirm_mode_change_request_new(struct agent *agent,
468 							const char *mode)
469 {
470 	DBusMessage *message;
471 	DBusPendingCall *call;
472 
473 	message = dbus_message_new_method_call(agent->name, agent->path,
474 				"org.bluez.Agent", "ConfirmModeChange");
475 	if (message == NULL) {
476 		error("Couldn't allocate D-Bus message");
477 		return NULL;
478 	}
479 
480 	dbus_message_append_args(message,
481 				DBUS_TYPE_STRING, &mode,
482 				DBUS_TYPE_INVALID);
483 
484 	if (dbus_connection_send_with_reply(connection, message,
485 					&call, REQUEST_TIMEOUT) == FALSE) {
486 		error("D-Bus send failed");
487 		dbus_message_unref(message);
488 		return NULL;
489 	}
490 
491 	dbus_message_unref(message);
492 
493 	return call;
494 }
495 
agent_confirm_mode_change(struct agent * agent,const char * new_mode,agent_cb cb,void * user_data)496 int agent_confirm_mode_change(struct agent *agent, const char *new_mode,
497 				agent_cb cb, void *user_data)
498 {
499 	struct agent_request *req;
500 
501 	if (agent->request)
502 		return -EBUSY;
503 
504 	debug("Calling Agent.ConfirmModeChange: name=%s, path=%s, mode=%s",
505 			agent->name, agent->path, new_mode);
506 
507 	req = agent_request_new(agent, AGENT_REQUEST_CONFIRM_MODE,
508 				cb, user_data);
509 
510 	req->call = confirm_mode_change_request_new(agent, new_mode);
511 	if (!req->call)
512 		goto failed;
513 
514 	dbus_pending_call_set_notify(req->call, simple_agent_reply, req, NULL);
515 
516 	agent->request = req;
517 
518 	return 0;
519 
520 failed:
521 	agent_request_free(req);
522 	return -1;
523 }
524 
passkey_request_new(struct agent * agent,const char * device_path)525 static DBusPendingCall *passkey_request_new(struct agent *agent,
526 						const char *device_path)
527 {
528 	DBusMessage *message;
529 	DBusPendingCall *call;
530 
531 	message = dbus_message_new_method_call(agent->name, agent->path,
532 					"org.bluez.Agent", "RequestPasskey");
533 	if (message == NULL) {
534 		error("Couldn't allocate D-Bus message");
535 		return NULL;
536 	}
537 
538 	dbus_message_append_args(message, DBUS_TYPE_OBJECT_PATH, &device_path,
539 					DBUS_TYPE_INVALID);
540 
541 	if (dbus_connection_send_with_reply(connection, message,
542 					&call, REQUEST_TIMEOUT) == FALSE) {
543 		error("D-Bus send failed");
544 		dbus_message_unref(message);
545 		return NULL;
546 	}
547 
548 	dbus_message_unref(message);
549 	return call;
550 }
551 
passkey_reply(DBusPendingCall * call,void * user_data)552 static void passkey_reply(DBusPendingCall *call, void *user_data)
553 {
554 	struct agent_request *req = user_data;
555 	struct agent *agent = req->agent;
556 	agent_passkey_cb cb = req->cb;
557 	DBusMessage *message;
558 	DBusError err;
559 	uint32_t passkey;
560 
561 	/* steal_reply will always return non-NULL since the callback
562 	 * is only called after a reply has been received */
563 	message = dbus_pending_call_steal_reply(call);
564 
565 	dbus_error_init(&err);
566 	if (dbus_set_error_from_message(&err, message)) {
567 		error("Agent replied with an error: %s, %s",
568 				err.name, err.message);
569 		cb(agent, &err, 0, req->user_data);
570 		dbus_error_free(&err);
571 		goto done;
572 	}
573 
574 	dbus_error_init(&err);
575 	if (!dbus_message_get_args(message, &err,
576 				DBUS_TYPE_UINT32, &passkey,
577 				DBUS_TYPE_INVALID)) {
578 		error("Wrong passkey reply signature: %s", err.message);
579 		cb(agent, &err, 0, req->user_data);
580 		dbus_error_free(&err);
581 		goto done;
582 	}
583 
584 	cb(agent, NULL, passkey, req->user_data);
585 
586 done:
587 	if (message)
588 		dbus_message_unref(message);
589 
590 	dbus_pending_call_cancel(req->call);
591 	agent_request_free(req);
592 }
593 
agent_request_passkey(struct agent * agent,struct device * device,agent_passkey_cb cb,void * user_data)594 int agent_request_passkey(struct agent *agent, struct device *device,
595 				agent_passkey_cb cb, void *user_data)
596 {
597 	struct agent_request *req;
598 
599 	if (agent->request)
600 		return -EBUSY;
601 
602 	debug("Calling Agent.RequestPasskey: name=%s, path=%s",
603 			agent->name, agent->path);
604 
605 	req = agent_request_new(agent, AGENT_REQUEST_PASSKEY, cb, user_data);
606 
607 	req->call = passkey_request_new(agent, device->path);
608 	if (!req->call)
609 		goto failed;
610 
611 	dbus_pending_call_set_notify(req->call, passkey_reply, req, NULL);
612 
613 	agent->request = req;
614 
615 	return 0;
616 
617 failed:
618 	agent_request_free(req);
619 	return -1;
620 }
621 
confirmation_request_new(struct agent * agent,const char * device_path,uint32_t passkey)622 static DBusPendingCall *confirmation_request_new(struct agent *agent,
623 						const char *device_path,
624 						uint32_t passkey)
625 {
626 	DBusMessage *message;
627 	DBusPendingCall *call;
628 
629 	message = dbus_message_new_method_call(agent->name, agent->path,
630 				"org.bluez.Agent", "RequestConfirmation");
631 	if (message == NULL) {
632 		error("Couldn't allocate D-Bus message");
633 		return NULL;
634 	}
635 
636 	dbus_message_append_args(message,
637 				DBUS_TYPE_OBJECT_PATH, &device_path,
638 				DBUS_TYPE_UINT32, &passkey,
639 				DBUS_TYPE_INVALID);
640 
641 	if (dbus_connection_send_with_reply(connection, message,
642 					&call, REQUEST_TIMEOUT) == FALSE) {
643 		error("D-Bus send failed");
644 		dbus_message_unref(message);
645 		return NULL;
646 	}
647 
648 	dbus_message_unref(message);
649 
650 	return call;
651 }
652 
agent_request_confirmation(struct agent * agent,struct device * device,uint32_t passkey,agent_cb cb,void * user_data)653 int agent_request_confirmation(struct agent *agent, struct device *device,
654 				uint32_t passkey, agent_cb cb,
655 				void *user_data)
656 {
657 	struct agent_request *req;
658 
659 	if (agent->request)
660 		return -EBUSY;
661 
662 	debug("Calling Agent.RequestConfirmation: name=%s, path=%s, passkey=%06u",
663 			agent->name, agent->path, passkey);
664 
665 	req = agent_request_new(agent, AGENT_REQUEST_CONFIRMATION, cb,
666 				user_data);
667 
668 	req->call = confirmation_request_new(agent, device->path, passkey);
669 	if (!req->call)
670 		goto failed;
671 
672 	dbus_pending_call_set_notify(req->call, simple_agent_reply, req, NULL);
673 
674 	agent->request = req;
675 
676 	return 0;
677 
678 failed:
679 	agent_request_free(req);
680 	return -1;
681 }
682 
agent_display_passkey(struct agent * agent,struct device * device,uint32_t passkey)683 int agent_display_passkey(struct agent *agent, struct device *device,
684 				uint32_t passkey)
685 {
686 	DBusMessage *message;
687 
688 	message = dbus_message_new_method_call(agent->name, agent->path,
689 				"org.bluez.Agent", "DisplayPasskey");
690 	if (!message) {
691 		error("Couldn't allocate D-Bus message");
692 		return -1;
693 	}
694 
695 	dbus_message_append_args(message,
696 				DBUS_TYPE_OBJECT_PATH, &device->path,
697 				DBUS_TYPE_UINT32, &passkey,
698 				DBUS_TYPE_INVALID);
699 
700 	if (!g_dbus_send_message(connection, message)) {
701 		error("D-Bus send failed");
702 		dbus_message_unref(message);
703 		return -1;
704 	}
705 
706 	return 0;
707 }
708 
agent_get_io_capability(struct agent * agent)709 uint8_t agent_get_io_capability(struct agent *agent)
710 {
711 	return agent->capability;
712 }
713 
agent_matches(struct agent * agent,const char * name,const char * path)714 gboolean agent_matches(struct agent *agent, const char *name, const char *path)
715 {
716 	if (g_str_equal(agent->name, name) && g_str_equal(agent->path, path))
717 		return TRUE;
718 
719 	return FALSE;
720 }
721 
agent_exit(void)722 void agent_exit(void)
723 {
724 	dbus_connection_unref(connection);
725 }
726 
agent_init(void)727 void agent_init(void)
728 {
729 	connection = dbus_bus_get(DBUS_BUS_SYSTEM, NULL);
730 }
731