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