3 * btpasskey 1.0 Copyright (c) 2007 Victor Wagner
5 * Based on example passkey-agent.c program from
7 * BlueZ - Bluetooth protocol stack for Linux
9 * Copyright (C) 2005-2006 Marcel Holtmann <marcel@holtmann.org>
12 * This program is free software; you can redistribute it and/or modify
13 * it under the terms of the GNU General Public License as published by
14 * the Free Software Foundation; either version 2 of the License, or
15 * (at your option) any later version.
17 * This program is distributed in the hope that it will be useful,
18 * but WITHOUT ANY WARRANTY; without even the implied warranty of
19 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
20 * GNU General Public License for more details.
22 * You should have received a copy of the GNU General Public License
23 * along with this program; if not, write to the Free Software
24 * Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA
39 #include <dbus/dbus.h>
41 #define INTERFACE "org.bluez.Security"
42 char* resolve_bluetooth_name(DBusConnection *conn,const char *adapter,const char *btaddress)
45 DBusPendingCall *pending;
47 static char namebuf[256];
49 msg = dbus_message_new_method_call("org.bluez",
50 adapter, "org.bluez.Adapter","GetRemoteName");
53 fprintf(stderr,"dbus_message_new_method_call error\n");
56 dbus_message_append_args(msg,DBUS_TYPE_STRING,&btaddress,DBUS_TYPE_INVALID);
58 if (!dbus_connection_send_with_reply(conn, msg, &pending,-1)
61 fprintf(stderr,"Error sending message to system bus\n");
64 dbus_connection_flush(conn);
65 dbus_message_unref(msg);
66 dbus_pending_call_block(pending);
67 msg = dbus_pending_call_steal_reply(pending);
70 fprintf(stderr,"Error getting call result\n");
73 dbus_pending_call_unref(pending);
74 dbus_error_init(&err);
75 if (!dbus_message_get_args(msg,&err,DBUS_TYPE_STRING,&name,DBUS_TYPE_INVALID))
77 fprintf(stderr,"Error parsing name request result\n");
80 if (!dbus_error_is_set(&err))
86 fprintf(stderr,"DBus error %s: %s\n",err.name,
90 dbus_error_free(&err);
91 dbus_message_unref(msg);
97 static volatile sig_atomic_t __io_canceled = 0;
98 static volatile sig_atomic_t __io_terminated = 0;
100 static void sig_term(int sig)
105 static DBusHandlerResult agent_filter(DBusConnection *conn,
106 DBusMessage *msg, void *data)
108 const char *name, *old, *new;
110 if (!dbus_message_is_signal(msg, DBUS_INTERFACE_DBUS, "NameOwnerChanged"))
111 return DBUS_HANDLER_RESULT_NOT_YET_HANDLED;
113 if (!dbus_message_get_args(msg, NULL,
114 DBUS_TYPE_STRING, &name, DBUS_TYPE_STRING, &old,
115 DBUS_TYPE_STRING, &new, DBUS_TYPE_INVALID)) {
116 fprintf(stderr, "Invalid arguments for NameOwnerChanged signal");
117 return DBUS_HANDLER_RESULT_NOT_YET_HANDLED;
120 if (!strcmp(name, "org.bluez") && *new == '\0') {
121 fprintf(stderr, "Passkey service has been terminated\n");
125 return DBUS_HANDLER_RESULT_NOT_YET_HANDLED;
128 static DBusHandlerResult request_message(DBusConnection *conn,
129 DBusMessage *msg, void *data)
132 const char *path, *address;
133 char passbuf[24], *c,*dname;
134 char *passkey = passbuf;
138 if (!dbus_message_get_args(msg, NULL, DBUS_TYPE_STRING, &path,
139 DBUS_TYPE_STRING, &address, DBUS_TYPE_INVALID)) {
140 fprintf(stderr, "Invalid arguments for passkey Request method");
141 return DBUS_HANDLER_RESULT_NOT_YET_HANDLED;
143 dname = resolve_bluetooth_name(conn,path,address);
144 if (!dname) dname = "";
146 printf("Pass key requested for device \"%s\" (%s)\nEnter passkey: ",dname,address);
147 fgets(passkey,sizeof(passbuf),stdin);
148 for (c=passkey;*c && *c!='\n'; c++) {
150 printf("Invalid passkey. Retry, please\n"); break;
154 if (!*c && c>passkey) ok=1;
155 if (c==passkey) return DBUS_HANDLER_RESULT_NOT_YET_HANDLED;
157 reply = dbus_message_new_method_return(msg);
159 fprintf(stderr, "Can't create reply message\n");
160 return DBUS_HANDLER_RESULT_NOT_YET_HANDLED;
163 dbus_message_append_args(reply, DBUS_TYPE_STRING, &passkey,
166 dbus_connection_send(conn, reply, NULL);
168 dbus_connection_flush(conn);
170 dbus_message_unref(reply);
172 return DBUS_HANDLER_RESULT_HANDLED;
175 static DBusHandlerResult release_message(DBusConnection *conn,
176 DBusMessage *msg, void *data)
180 if (!dbus_message_get_args(msg, NULL, DBUS_TYPE_INVALID)) {
181 fprintf(stderr, "Invalid arguments for passkey Release method");
182 return DBUS_HANDLER_RESULT_NOT_YET_HANDLED;
185 reply = dbus_message_new_method_return(msg);
187 fprintf(stderr, "Can't create reply message\n");
188 return DBUS_HANDLER_RESULT_NOT_YET_HANDLED;
191 dbus_message_append_args(reply, DBUS_TYPE_INVALID);
193 dbus_connection_send(conn, reply, NULL);
195 dbus_connection_flush(conn);
197 dbus_message_unref(reply);
200 fprintf(stderr, "Passkey service has been released\n");
204 return DBUS_HANDLER_RESULT_HANDLED;
207 static DBusHandlerResult agent_message(DBusConnection *conn,
208 DBusMessage *msg, void *data)
210 if (dbus_message_is_method_call(msg, "org.bluez.PasskeyAgent", "Request"))
211 return request_message(conn, msg, data);
213 if (dbus_message_is_method_call(msg, "org.bluez.PasskeyAgent", "Release"))
214 return release_message(conn, msg, data);
216 return DBUS_HANDLER_RESULT_NOT_YET_HANDLED;
219 static const DBusObjectPathVTable agent_table = {
220 .message_function = agent_message,
223 static int register_agent(DBusConnection *conn, const char *agent_path)
225 DBusMessage *msg, *reply;
227 const char *path, *method;
229 if (!dbus_connection_register_object_path(conn, agent_path,
230 &agent_table, NULL)) {
231 fprintf(stderr, "Can't register path object path for agent\n");
236 method = "RegisterDefaultPasskeyAgent";
237 msg = dbus_message_new_method_call("org.bluez", path, INTERFACE, method);
239 fprintf(stderr, "Can't allocate new method call\n");
243 dbus_message_append_args(msg, DBUS_TYPE_STRING, &agent_path,
246 dbus_error_init(&err);
248 reply = dbus_connection_send_with_reply_and_block(conn, msg, -1, &err);
250 dbus_message_unref(msg);
253 fprintf(stderr, "Can't register passkey agent\n");
254 if (dbus_error_is_set(&err)) {
255 fprintf(stderr, "%s\n", err.message);
256 dbus_error_free(&err);
261 dbus_message_unref(reply);
263 dbus_connection_flush(conn);
268 static int unregister_agent(DBusConnection *conn, const char *agent_path)
270 DBusMessage *msg, *reply;
272 const char *path, *method;
275 method = "UnregisterDefaultPasskeyAgent";
277 msg = dbus_message_new_method_call("org.bluez", path, INTERFACE, method);
279 fprintf(stderr, "Can't allocate new method call\n");
280 dbus_connection_unref(conn);
284 dbus_message_append_args(msg, DBUS_TYPE_STRING, &agent_path,
287 dbus_error_init(&err);
289 reply = dbus_connection_send_with_reply_and_block(conn, msg, -1, &err);
291 dbus_message_unref(msg);
294 fprintf(stderr, "Can't unregister passkey agent\n");
295 if (dbus_error_is_set(&err)) {
296 fprintf(stderr, "%s\n", err.message);
297 dbus_error_free(&err);
302 dbus_message_unref(reply);
304 dbus_connection_flush(conn);
306 dbus_connection_unregister_object_path(conn, agent_path);
310 /* Gets default bluetooth adapter name */
311 char *get_default_adapter(DBusConnection *conn)
313 DBusMessage *msg, *reply;
317 msg = dbus_message_new_method_call("org.bluez", "/org/bluez","org.bluez.Manager", "DefaultAdapter");
318 dbus_error_init(&err);
319 reply = dbus_connection_send_with_reply_and_block(conn, msg, -1, &err);
320 dbus_message_get_args(reply,&err,DBUS_TYPE_STRING,&result,DBUS_TYPE_INVALID);
321 if (dbus_error_is_set(&err))
323 fprintf(stderr,"Error getting default adapter: %s\n",err.message);
324 dbus_error_free(&err);
327 dbus_message_unref(msg);
328 result=strdup(result);
329 dbus_message_unref(reply);
334 /* Finishes pending call without retriving anything */
335 static void finish_pending_call(DBusPendingCall *pending, void *user_data)
337 dbus_pending_call_unref(pending);
339 /*Calls specified method of org.bluez.Adapter interface with one or zero
340 * string arguments. If wait for reply is specified, waits, and if
341 * method is not void,
342 * returns string result
344 char *adapter_call(DBusConnection *conn, char *adapter,const char *method,
345 const char *argument, int wait_reply) {
346 DBusMessage *msg, *reply;
349 msg = dbus_message_new_method_call("org.bluez", adapter,"org.bluez.Adapter", method);
351 dbus_message_append_args(msg, DBUS_TYPE_STRING, &argument,
355 dbus_error_init(&err);
356 reply = dbus_connection_send_with_reply_and_block(conn, msg, -1, &err);
357 dbus_message_get_args(reply,&err,DBUS_TYPE_STRING,&result,DBUS_TYPE_INVALID);
359 result=strdup(result);
360 dbus_message_unref(reply);
362 DBusPendingCall *pending;
363 dbus_connection_send_with_reply(conn,msg,&pending,-1);
364 dbus_pending_call_set_notify(pending,finish_pending_call,
368 dbus_message_unref(msg);
372 static void usage(const char *progname)
374 printf("Bluetooth passkey agent ver %s\n\n", VERSION);
380 "\t -p, --path agent-path - set D-Bus object path for agent\n"
381 "\t -d, --discoverable - switch default adapter into discoverable\n"
382 "\t\tmode while waiting for pairing request\n"
383 "\t -P, --pair address - initiate pairing with specified address\n"
384 "\t -r, --remove address - remove pairing with specified device and exit\n");
387 static struct option main_options[] = {
388 { "path", 1, 0, 'p' },
389 { "help", 0, 0, 'h' },
390 { "version", 0, 0, 'V'},
391 { "discoverable",0,0,'d' },
397 int main(int argc, char *argv[])
400 DBusConnection *conn;
401 char match_string[128], default_path[128], *agent_path = NULL;
402 char *saved_mode=NULL;
403 char *default_adapter=NULL,*pair_address=NULL, *remove_bonding=NULL;
406 snprintf(default_path, sizeof(default_path),
407 "/org/bluez/passkey_agent_%d", getpid());
409 while ((opt = getopt_long(argc, argv, "p:r:P:hVd", main_options, NULL)) != EOF) {
412 if (optarg[0] != '/') {
413 fprintf(stderr, "Invalid path\n");
416 agent_path = strdup(optarg);
418 case 'd': set_mode=1;
420 case 'P': pair_address=optarg;
422 case 'r' : remove_bonding=optarg;
429 printf("btpasskey " VERSION " Copyright (c) 2007-2008, Victor Wagner\n");
432 fprintf(stderr,"Invalid option -%c. Use %s --help\n",
442 agent_path = strdup(default_path);
444 conn = dbus_bus_get(DBUS_BUS_SYSTEM, NULL);
446 fprintf(stderr, "Can't get on system bus");
449 if (set_mode || pair_address|| remove_bonding)
451 /* With these options we need default adapter to interact */
452 default_adapter = get_default_adapter(conn);
456 adapter_call(conn,default_adapter,"RemoveBonding",remove_bonding,0);
457 dbus_connection_unref(conn);
461 if (register_agent(conn, agent_path) < 0) {
462 dbus_connection_unref(conn);
466 if (!dbus_connection_add_filter(conn, agent_filter, NULL, NULL))
467 fprintf(stderr, "Can't add signal filter");
469 snprintf(match_string, sizeof(match_string),
470 "interface=%s,member=NameOwnerChanged,arg0=%s",
471 DBUS_INTERFACE_DBUS, "org.bluez");
473 dbus_bus_add_match(conn, match_string, NULL);
475 memset(&sa, 0, sizeof(sa));
476 sa.sa_flags = SA_NOCLDSTOP;
477 sa.sa_handler = sig_term;
478 sigaction(SIGTERM, &sa, NULL);
479 sigaction(SIGINT, &sa, NULL);
480 sigaction(SIGPIPE, &sa, NULL);
481 setvbuf(stdin,NULL,_IONBF,0);
482 setvbuf(stdout,NULL,_IONBF,0);
483 if (isatty(fileno(stdout)))
484 printf("Waiting for passkey request. Press Ctrl-C to interrupt\n");
486 saved_mode=adapter_call(conn,default_adapter,"GetMode",NULL,1);
487 if (strcmp(saved_mode,"discoverable")==0)
488 { /* Already in discoverable state nothing to do*/
494 adapter_call(conn,default_adapter,"SetMode","discoverable",1);
498 adapter_call(conn,default_adapter,"CreateBonding", pair_address,0);
500 while (!__io_canceled && !__io_terminated) {
501 if (dbus_connection_read_write_dispatch(conn, 100) != TRUE)
505 if (!__io_terminated)
506 unregister_agent(conn, agent_path);
509 adapter_call(conn,default_adapter,"SetMode",saved_mode,1);
512 dbus_connection_unref(conn);
513 if (default_adapter) free(default_adapter);