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
38static char *passkey_value = NULL;
39static int passkey_delay = 0;
40static int do_reject = 0;
41
42static volatile sig_atomic_t __io_canceled = 0;
43static volatile sig_atomic_t __io_terminated = 0;
44
45static void sig_term(int sig)
46{
47	__io_canceled = 1;
48}
49
50static 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
76static 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
112send:
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
122static 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
161send:
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
171static 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
203send:
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
213static 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
239send:
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
249static 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
276static 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
306static 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
333static const DBusObjectPathVTable agent_table = {
334	.message_function = agent_message,
335};
336
337static 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
377static 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
417static 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
452static 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
504static 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
562static 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
571static 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
581int 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