• Home
  • Line#
  • Scopes#
  • Navigate#
  • Raw
  • Download
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