1 /*
2 *
3 * BlueZ - Bluetooth protocol stack for Linux
4 *
5 * Copyright (C) 2004-2010 Marcel Holtmann <marcel@holtmann.org>
6 *
7 *
8 * This program is free software; you can redistribute it and/or modify
9 * it under the terms of the GNU General Public License as published by
10 * the Free Software Foundation; either version 2 of the License, or
11 * (at your option) any later version.
12 *
13 * This program is distributed in the hope that it will be useful,
14 * but WITHOUT ANY WARRANTY; without even the implied warranty of
15 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
16 * GNU General Public License for more details.
17 *
18 * You should have received a copy of the GNU General Public License
19 * along with this program; if not, write to the Free Software
20 * Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA
21 *
22 */
23
24 #ifdef HAVE_CONFIG_H
25 #include <config.h>
26 #endif
27
28 #include <stdio.h>
29 #include <errno.h>
30 #include <unistd.h>
31 #include <stdlib.h>
32 #include <signal.h>
33 #include <getopt.h>
34 #include <string.h>
35
36 #include <dbus/dbus.h>
37
38 static char *passkey_value = NULL;
39 static int passkey_delay = 0;
40 static int do_reject = 0;
41
42 static volatile sig_atomic_t __io_canceled = 0;
43 static volatile sig_atomic_t __io_terminated = 0;
44
sig_term(int sig)45 static void sig_term(int sig)
46 {
47 __io_canceled = 1;
48 }
49
agent_filter(DBusConnection * conn,DBusMessage * msg,void * data)50 static DBusHandlerResult agent_filter(DBusConnection *conn,
51 DBusMessage *msg, void *data)
52 {
53 const char *name, *old, *new;
54
55 if (!dbus_message_is_signal(msg, DBUS_INTERFACE_DBUS,
56 "NameOwnerChanged"))
57 return DBUS_HANDLER_RESULT_NOT_YET_HANDLED;
58
59 if (!dbus_message_get_args(msg, NULL,
60 DBUS_TYPE_STRING, &name,
61 DBUS_TYPE_STRING, &old,
62 DBUS_TYPE_STRING, &new,
63 DBUS_TYPE_INVALID)) {
64 fprintf(stderr, "Invalid arguments for NameOwnerChanged signal");
65 return DBUS_HANDLER_RESULT_NOT_YET_HANDLED;
66 }
67
68 if (!strcmp(name, "org.bluez") && *new == '\0') {
69 fprintf(stderr, "Agent has been terminated\n");
70 __io_terminated = 1;
71 }
72
73 return DBUS_HANDLER_RESULT_NOT_YET_HANDLED;
74 }
75
request_pincode_message(DBusConnection * conn,DBusMessage * msg,void * data)76 static DBusHandlerResult request_pincode_message(DBusConnection *conn,
77 DBusMessage *msg, void *data)
78 {
79 DBusMessage *reply;
80 const char *path;
81
82 if (!passkey_value)
83 return DBUS_HANDLER_RESULT_NOT_YET_HANDLED;
84
85 if (!dbus_message_get_args(msg, NULL, DBUS_TYPE_OBJECT_PATH, &path,
86 DBUS_TYPE_INVALID)) {
87 fprintf(stderr, "Invalid arguments for RequestPinCode method");
88 return DBUS_HANDLER_RESULT_NOT_YET_HANDLED;
89 }
90
91 if (do_reject) {
92 reply = dbus_message_new_error(msg, "org.bluez.Error.Rejected", "");
93 goto send;
94 }
95
96 reply = dbus_message_new_method_return(msg);
97 if (!reply) {
98 fprintf(stderr, "Can't create reply message\n");
99 return DBUS_HANDLER_RESULT_NEED_MEMORY;
100 }
101
102 printf("Pincode request for device %s\n", path);
103
104 if (passkey_delay) {
105 printf("Waiting for %d seconds\n", passkey_delay);
106 sleep(passkey_delay);
107 }
108
109 dbus_message_append_args(reply, DBUS_TYPE_STRING, &passkey_value,
110 DBUS_TYPE_INVALID);
111
112 send:
113 dbus_connection_send(conn, reply, NULL);
114
115 dbus_connection_flush(conn);
116
117 dbus_message_unref(reply);
118
119 return DBUS_HANDLER_RESULT_HANDLED;
120 }
121
request_passkey_message(DBusConnection * conn,DBusMessage * msg,void * data)122 static DBusHandlerResult request_passkey_message(DBusConnection *conn,
123 DBusMessage *msg, void *data)
124 {
125 DBusMessage *reply;
126 const char *path;
127 unsigned int passkey;
128
129 if (!passkey_value)
130 return DBUS_HANDLER_RESULT_NOT_YET_HANDLED;
131
132 if (!dbus_message_get_args(msg, NULL, DBUS_TYPE_OBJECT_PATH, &path,
133 DBUS_TYPE_INVALID)) {
134 fprintf(stderr, "Invalid arguments for RequestPasskey method");
135 return DBUS_HANDLER_RESULT_NOT_YET_HANDLED;
136 }
137
138 if (do_reject) {
139 reply = dbus_message_new_error(msg, "org.bluez.Error.Rejected", "");
140 goto send;
141 }
142
143 reply = dbus_message_new_method_return(msg);
144 if (!reply) {
145 fprintf(stderr, "Can't create reply message\n");
146 return DBUS_HANDLER_RESULT_NEED_MEMORY;
147 }
148
149 printf("Passkey request for device %s\n", path);
150
151 if (passkey_delay) {
152 printf("Waiting for %d seconds\n", passkey_delay);
153 sleep(passkey_delay);
154 }
155
156 passkey = strtoul(passkey_value, NULL, 10);
157
158 dbus_message_append_args(reply, DBUS_TYPE_UINT32, &passkey,
159 DBUS_TYPE_INVALID);
160
161 send:
162 dbus_connection_send(conn, reply, NULL);
163
164 dbus_connection_flush(conn);
165
166 dbus_message_unref(reply);
167
168 return DBUS_HANDLER_RESULT_HANDLED;
169 }
170
request_confirmation_message(DBusConnection * conn,DBusMessage * msg,void * data)171 static DBusHandlerResult request_confirmation_message(DBusConnection *conn,
172 DBusMessage *msg, void *data)
173 {
174 DBusMessage *reply;
175 const char *path;
176 unsigned int passkey;
177
178 if (!dbus_message_get_args(msg, NULL, DBUS_TYPE_OBJECT_PATH, &path,
179 DBUS_TYPE_UINT32, &passkey,
180 DBUS_TYPE_INVALID)) {
181 fprintf(stderr, "Invalid arguments for RequestPasskey method");
182 return DBUS_HANDLER_RESULT_NOT_YET_HANDLED;
183 }
184
185 if (do_reject) {
186 reply = dbus_message_new_error(msg, "org.bluez.Error.Rejected", "");
187 goto send;
188 }
189
190 reply = dbus_message_new_method_return(msg);
191 if (!reply) {
192 fprintf(stderr, "Can't create reply message\n");
193 return DBUS_HANDLER_RESULT_NEED_MEMORY;
194 }
195
196 printf("Confirmation request of %u for device %s\n", passkey, path);
197
198 if (passkey_delay) {
199 printf("Waiting for %d seconds\n", passkey_delay);
200 sleep(passkey_delay);
201 }
202
203 send:
204 dbus_connection_send(conn, reply, NULL);
205
206 dbus_connection_flush(conn);
207
208 dbus_message_unref(reply);
209
210 return DBUS_HANDLER_RESULT_HANDLED;
211 }
212
authorize_message(DBusConnection * conn,DBusMessage * msg,void * data)213 static DBusHandlerResult authorize_message(DBusConnection *conn,
214 DBusMessage *msg, void *data)
215 {
216 DBusMessage *reply;
217 const char *path, *uuid;
218
219 if (!dbus_message_get_args(msg, NULL, DBUS_TYPE_OBJECT_PATH, &path,
220 DBUS_TYPE_STRING, &uuid,
221 DBUS_TYPE_INVALID)) {
222 fprintf(stderr, "Invalid arguments for Authorize method");
223 return DBUS_HANDLER_RESULT_NOT_YET_HANDLED;
224 }
225
226 if (do_reject) {
227 reply = dbus_message_new_error(msg, "org.bluez.Error.Rejected", "");
228 goto send;
229 }
230
231 reply = dbus_message_new_method_return(msg);
232 if (!reply) {
233 fprintf(stderr, "Can't create reply message\n");
234 return DBUS_HANDLER_RESULT_NEED_MEMORY;
235 }
236
237 printf("Authorizing request for %s\n", path);
238
239 send:
240 dbus_connection_send(conn, reply, NULL);
241
242 dbus_connection_flush(conn);
243
244 dbus_message_unref(reply);
245
246 return DBUS_HANDLER_RESULT_HANDLED;
247 }
248
cancel_message(DBusConnection * conn,DBusMessage * msg,void * data)249 static DBusHandlerResult cancel_message(DBusConnection *conn,
250 DBusMessage *msg, void *data)
251 {
252 DBusMessage *reply;
253
254 if (!dbus_message_get_args(msg, NULL, DBUS_TYPE_INVALID)) {
255 fprintf(stderr, "Invalid arguments for passkey Confirm method");
256 return DBUS_HANDLER_RESULT_NOT_YET_HANDLED;
257 }
258
259 printf("Request canceled\n");
260
261 reply = dbus_message_new_method_return(msg);
262 if (!reply) {
263 fprintf(stderr, "Can't create reply message\n");
264 return DBUS_HANDLER_RESULT_NEED_MEMORY;
265 }
266
267 dbus_connection_send(conn, reply, NULL);
268
269 dbus_connection_flush(conn);
270
271 dbus_message_unref(reply);
272
273 return DBUS_HANDLER_RESULT_HANDLED;
274 }
275
release_message(DBusConnection * conn,DBusMessage * msg,void * data)276 static DBusHandlerResult release_message(DBusConnection *conn,
277 DBusMessage *msg, void *data)
278 {
279 DBusMessage *reply;
280
281 if (!dbus_message_get_args(msg, NULL, DBUS_TYPE_INVALID)) {
282 fprintf(stderr, "Invalid arguments for Release method");
283 return DBUS_HANDLER_RESULT_NOT_YET_HANDLED;
284 }
285
286 if (!__io_canceled)
287 fprintf(stderr, "Agent has been released\n");
288
289 __io_terminated = 1;
290
291 reply = dbus_message_new_method_return(msg);
292 if (!reply) {
293 fprintf(stderr, "Can't create reply message\n");
294 return DBUS_HANDLER_RESULT_NEED_MEMORY;
295 }
296
297 dbus_connection_send(conn, reply, NULL);
298
299 dbus_connection_flush(conn);
300
301 dbus_message_unref(reply);
302
303 return DBUS_HANDLER_RESULT_HANDLED;
304 }
305
agent_message(DBusConnection * conn,DBusMessage * msg,void * data)306 static DBusHandlerResult agent_message(DBusConnection *conn,
307 DBusMessage *msg, void *data)
308 {
309 if (dbus_message_is_method_call(msg, "org.bluez.Agent",
310 "RequestPinCode"))
311 return request_pincode_message(conn, msg, data);
312
313 if (dbus_message_is_method_call(msg, "org.bluez.Agent",
314 "RequestPasskey"))
315 return request_passkey_message(conn, msg, data);
316
317 if (dbus_message_is_method_call(msg, "org.bluez.Agent",
318 "RequestConfirmation"))
319 return request_confirmation_message(conn, msg, data);
320
321 if (dbus_message_is_method_call(msg, "org.bluez.Agent", "Authorize"))
322 return authorize_message(conn, msg, data);
323
324 if (dbus_message_is_method_call(msg, "org.bluez.Agent", "Cancel"))
325 return cancel_message(conn, msg, data);
326
327 if (dbus_message_is_method_call(msg, "org.bluez.Agent", "Release"))
328 return release_message(conn, msg, data);
329
330 return DBUS_HANDLER_RESULT_NOT_YET_HANDLED;
331 }
332
333 static const DBusObjectPathVTable agent_table = {
334 .message_function = agent_message,
335 };
336
register_agent(DBusConnection * conn,const char * adapter_path,const char * agent_path,const char * capabilities)337 static int register_agent(DBusConnection *conn, const char *adapter_path,
338 const char *agent_path,
339 const char *capabilities)
340 {
341 DBusMessage *msg, *reply;
342 DBusError err;
343
344 msg = dbus_message_new_method_call("org.bluez", adapter_path,
345 "org.bluez.Adapter", "RegisterAgent");
346 if (!msg) {
347 fprintf(stderr, "Can't allocate new method call\n");
348 return -1;
349 }
350
351 dbus_message_append_args(msg, DBUS_TYPE_OBJECT_PATH, &agent_path,
352 DBUS_TYPE_STRING, &capabilities,
353 DBUS_TYPE_INVALID);
354
355 dbus_error_init(&err);
356
357 reply = dbus_connection_send_with_reply_and_block(conn, msg, -1, &err);
358
359 dbus_message_unref(msg);
360
361 if (!reply) {
362 fprintf(stderr, "Can't register agent\n");
363 if (dbus_error_is_set(&err)) {
364 fprintf(stderr, "%s\n", err.message);
365 dbus_error_free(&err);
366 }
367 return -1;
368 }
369
370 dbus_message_unref(reply);
371
372 dbus_connection_flush(conn);
373
374 return 0;
375 }
376
unregister_agent(DBusConnection * conn,const char * adapter_path,const char * agent_path)377 static int unregister_agent(DBusConnection *conn, const char *adapter_path,
378 const char *agent_path)
379 {
380 DBusMessage *msg, *reply;
381 DBusError err;
382
383 msg = dbus_message_new_method_call("org.bluez", adapter_path,
384 "org.bluez.Adapter", "UnregisterAgent");
385 if (!msg) {
386 fprintf(stderr, "Can't allocate new method call\n");
387 return -1;
388 }
389
390 dbus_message_append_args(msg, DBUS_TYPE_OBJECT_PATH, &agent_path,
391 DBUS_TYPE_INVALID);
392
393 dbus_error_init(&err);
394
395 reply = dbus_connection_send_with_reply_and_block(conn, msg, -1, &err);
396
397 dbus_message_unref(msg);
398
399 if (!reply) {
400 fprintf(stderr, "Can't unregister agent\n");
401 if (dbus_error_is_set(&err)) {
402 fprintf(stderr, "%s\n", err.message);
403 dbus_error_free(&err);
404 }
405 return -1;
406 }
407
408 dbus_message_unref(reply);
409
410 dbus_connection_flush(conn);
411
412 dbus_connection_unregister_object_path(conn, agent_path);
413
414 return 0;
415 }
416
create_paired_device(DBusConnection * conn,const char * adapter_path,const char * agent_path,const char * capabilities,const char * device)417 static int create_paired_device(DBusConnection *conn, const char *adapter_path,
418 const char *agent_path,
419 const char *capabilities,
420 const char *device)
421 {
422 dbus_bool_t success;
423 DBusMessage *msg;
424
425 msg = dbus_message_new_method_call("org.bluez", adapter_path,
426 "org.bluez.Adapter",
427 "CreatePairedDevice");
428 if (!msg) {
429 fprintf(stderr, "Can't allocate new method call\n");
430 return -1;
431 }
432
433 dbus_message_append_args(msg, DBUS_TYPE_STRING, &device,
434 DBUS_TYPE_OBJECT_PATH, &agent_path,
435 DBUS_TYPE_STRING, &capabilities,
436 DBUS_TYPE_INVALID);
437
438 success = dbus_connection_send(conn, msg, NULL);
439
440 dbus_message_unref(msg);
441
442 if (!success) {
443 fprintf(stderr, "Not enough memory for message send\n");
444 return -1;
445 }
446
447 dbus_connection_flush(conn);
448
449 return 0;
450 }
451
get_default_adapter_path(DBusConnection * conn)452 static char *get_default_adapter_path(DBusConnection *conn)
453 {
454 DBusMessage *msg, *reply;
455 DBusError err;
456 const char *reply_path;
457 char *path;
458
459 msg = dbus_message_new_method_call("org.bluez", "/",
460 "org.bluez.Manager", "DefaultAdapter");
461
462 if (!msg) {
463 fprintf(stderr, "Can't allocate new method call\n");
464 return NULL;
465 }
466
467 dbus_error_init(&err);
468
469 reply = dbus_connection_send_with_reply_and_block(conn, msg, -1, &err);
470
471 dbus_message_unref(msg);
472
473 if (!reply) {
474 fprintf(stderr,
475 "Can't get default adapter\n");
476 if (dbus_error_is_set(&err)) {
477 fprintf(stderr, "%s\n", err.message);
478 dbus_error_free(&err);
479 }
480 return NULL;
481 }
482
483 if (!dbus_message_get_args(reply, &err,
484 DBUS_TYPE_OBJECT_PATH, &reply_path,
485 DBUS_TYPE_INVALID)) {
486 fprintf(stderr,
487 "Can't get reply arguments\n");
488 if (dbus_error_is_set(&err)) {
489 fprintf(stderr, "%s\n", err.message);
490 dbus_error_free(&err);
491 }
492 return NULL;
493 }
494
495 path = strdup(reply_path);
496
497 dbus_message_unref(reply);
498
499 dbus_connection_flush(conn);
500
501 return path;
502 }
503
get_adapter_path(DBusConnection * conn,const char * adapter)504 static char *get_adapter_path(DBusConnection *conn, const char *adapter)
505 {
506 DBusMessage *msg, *reply;
507 DBusError err;
508 const char *reply_path;
509 char *path;
510
511 if (!adapter)
512 return get_default_adapter_path(conn);
513
514 msg = dbus_message_new_method_call("org.bluez", "/",
515 "org.bluez.Manager", "FindAdapter");
516
517 if (!msg) {
518 fprintf(stderr, "Can't allocate new method call\n");
519 return NULL;
520 }
521
522 dbus_message_append_args(msg, DBUS_TYPE_STRING, &adapter,
523 DBUS_TYPE_INVALID);
524
525 dbus_error_init(&err);
526
527 reply = dbus_connection_send_with_reply_and_block(conn, msg, -1, &err);
528
529 dbus_message_unref(msg);
530
531 if (!reply) {
532 fprintf(stderr,
533 "Can't find adapter %s\n", adapter);
534 if (dbus_error_is_set(&err)) {
535 fprintf(stderr, "%s\n", err.message);
536 dbus_error_free(&err);
537 }
538 return NULL;
539 }
540
541 if (!dbus_message_get_args(reply, &err,
542 DBUS_TYPE_OBJECT_PATH, &reply_path,
543 DBUS_TYPE_INVALID)) {
544 fprintf(stderr,
545 "Can't get reply arguments\n");
546 if (dbus_error_is_set(&err)) {
547 fprintf(stderr, "%s\n", err.message);
548 dbus_error_free(&err);
549 }
550 return NULL;
551 }
552
553 path = strdup(reply_path);
554
555 dbus_message_unref(reply);
556
557 dbus_connection_flush(conn);
558
559 return path;
560 }
561
usage(void)562 static void usage(void)
563 {
564 printf("Bluetooth agent ver %s\n\n", VERSION);
565
566 printf("Usage:\n"
567 "\tagent [--adapter adapter-path] [--path agent-path] <passkey> [<device>]\n"
568 "\n");
569 }
570
571 static struct option main_options[] = {
572 { "adapter", 1, 0, 'a' },
573 { "path", 1, 0, 'p' },
574 { "capabilites",1, 0, 'c' },
575 { "delay", 1, 0, 'd' },
576 { "reject", 0, 0, 'r' },
577 { "help", 0, 0, 'h' },
578 { 0, 0, 0, 0 }
579 };
580
main(int argc,char * argv[])581 int main(int argc, char *argv[])
582 {
583 const char *capabilities = "DisplayYesNo";
584 struct sigaction sa;
585 DBusConnection *conn;
586 char match_string[128], default_path[128], *adapter_id = NULL;
587 char *adapter_path = NULL, *agent_path = NULL, *device = NULL;
588 int opt;
589
590 snprintf(default_path, sizeof(default_path),
591 "/org/bluez/agent_%d", getpid());
592
593 while ((opt = getopt_long(argc, argv, "+a:p:c:d:rh", main_options, NULL)) != EOF) {
594 switch(opt) {
595 case 'a':
596 adapter_id = optarg;
597 break;
598 case 'p':
599 if (optarg[0] != '/') {
600 fprintf(stderr, "Invalid path\n");
601 exit(1);
602 }
603 agent_path = strdup(optarg);
604 break;
605 case 'c':
606 capabilities = optarg;
607 break;
608 case 'd':
609 passkey_delay = atoi(optarg);
610 break;
611 case 'r':
612 do_reject = 1;
613 break;
614 case 'h':
615 usage();
616 exit(0);
617 default:
618 exit(1);
619 }
620 }
621
622 argc -= optind;
623 argv += optind;
624 optind = 0;
625
626 if (argc < 1) {
627 usage();
628 exit(1);
629 }
630
631 passkey_value = strdup(argv[0]);
632
633 if (argc > 1)
634 device = strdup(argv[1]);
635
636 if (!agent_path)
637 agent_path = strdup(default_path);
638
639 conn = dbus_bus_get(DBUS_BUS_SYSTEM, NULL);
640 if (!conn) {
641 fprintf(stderr, "Can't get on system bus");
642 exit(1);
643 }
644
645 adapter_path = get_adapter_path(conn, adapter_id);
646 if (!adapter_path)
647 exit(1);
648
649 if (!dbus_connection_register_object_path(conn, agent_path,
650 &agent_table, NULL)) {
651 fprintf(stderr, "Can't register object path for agent\n");
652 exit(1);
653 }
654
655 if (device) {
656 if (create_paired_device(conn, adapter_path, agent_path,
657 capabilities, device) < 0) {
658 dbus_connection_unref(conn);
659 exit(1);
660 }
661 } else {
662 if (register_agent(conn, adapter_path, agent_path,
663 capabilities) < 0) {
664 dbus_connection_unref(conn);
665 exit(1);
666 }
667 }
668
669 if (!dbus_connection_add_filter(conn, agent_filter, NULL, NULL))
670 fprintf(stderr, "Can't add signal filter");
671
672 snprintf(match_string, sizeof(match_string),
673 "interface=%s,member=NameOwnerChanged,arg0=%s",
674 DBUS_INTERFACE_DBUS, "org.bluez");
675
676 dbus_bus_add_match(conn, match_string, NULL);
677
678 memset(&sa, 0, sizeof(sa));
679 sa.sa_flags = SA_NOCLDSTOP;
680 sa.sa_handler = sig_term;
681 sigaction(SIGTERM, &sa, NULL);
682 sigaction(SIGINT, &sa, NULL);
683
684 while (!__io_canceled && !__io_terminated) {
685 if (dbus_connection_read_write_dispatch(conn, 500) != TRUE)
686 break;
687 }
688
689 if (!__io_terminated && !device)
690 unregister_agent(conn, adapter_path, agent_path);
691
692 free(adapter_path);
693 free(agent_path);
694
695 free(passkey_value);
696
697 dbus_connection_unref(conn);
698
699 return 0;
700 }
701