]> www.wagner.pp.ru Git - oss/btcli.git/blob - btpasskey/btpasskey.c
fixed typo
[oss/btcli.git] / btpasskey / btpasskey.c
1 /*
2  *
3  *  btpasskey 1.0 Copyright (c) 2007 Victor Wagner
4  *
5  *  Based on example passkey-agent.c program from
6  *
7  *  BlueZ - Bluetooth protocol stack for Linux
8  *
9  *  Copyright (C) 2005-2006  Marcel Holtmann <marcel@holtmann.org>
10  *
11  *
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.
16  *
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.
21  *
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
25  *
26  */
27 #ifdef HAVE_CONFIG_H
28 #include <config.h>
29 #endif
30
31 #include <stdio.h>
32 #include <errno.h>
33 #include <unistd.h>
34 #include <stdlib.h>
35 #include <signal.h>
36 #include <getopt.h>
37 #include <string.h>
38
39 #include <dbus/dbus.h>
40
41 #define INTERFACE "org.bluez.Security"
42 char*  resolve_bluetooth_name(DBusConnection *conn,const char *adapter,const char *btaddress)  
43 {
44         DBusMessage *msg;
45         DBusPendingCall *pending;
46         char *name;
47         static char namebuf[256];
48         DBusError err;
49         msg = dbus_message_new_method_call("org.bluez",
50         adapter, "org.bluez.Adapter","GetRemoteName");
51         if (NULL==msg) 
52         {
53                 fprintf(stderr,"dbus_message_new_method_call error\n");
54                 exit(1);
55         }
56         dbus_message_append_args(msg,DBUS_TYPE_STRING,&btaddress,DBUS_TYPE_INVALID);
57
58         if (!dbus_connection_send_with_reply(conn, msg, &pending,-1)
59          || NULL==pending) 
60         {
61                 fprintf(stderr,"Error sending message to system bus\n");
62                 exit(1);
63         }
64         dbus_connection_flush(conn);
65         dbus_message_unref(msg);
66         dbus_pending_call_block(pending);
67         msg = dbus_pending_call_steal_reply(pending);
68         if (NULL == msg)
69         {
70                 fprintf(stderr,"Error getting call result\n");
71                 exit(1);
72         }
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)) 
76         {
77                 fprintf(stderr,"Error parsing name request result\n");
78                 exit(1);
79         }
80         if (!dbus_error_is_set(&err)) 
81         {
82                 strcpy(namebuf,name);
83         }
84         else 
85         {
86                 fprintf(stderr,"DBus error %s: %s\n",err.name,
87                 err.message);
88                 namebuf[0]=0;
89         }
90         dbus_error_free(&err);
91         dbus_message_unref(msg);
92         return namebuf;
93            
94                 
95 }
96
97 static volatile sig_atomic_t __io_canceled = 0;
98 static volatile sig_atomic_t __io_terminated = 0;
99
100 static void sig_term(int sig)
101 {
102         __io_canceled = 1;
103 }
104
105 static DBusHandlerResult agent_filter(DBusConnection *conn,
106                                                 DBusMessage *msg, void *data)
107 {
108         const char *name, *old, *new;
109
110         if (!dbus_message_is_signal(msg, DBUS_INTERFACE_DBUS, "NameOwnerChanged"))
111                 return DBUS_HANDLER_RESULT_NOT_YET_HANDLED;
112
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;
118         }
119
120         if (!strcmp(name, "org.bluez") && *new == '\0') {
121                 fprintf(stderr, "Passkey service has been terminated\n");
122                 __io_terminated = 1;
123         }
124
125         return DBUS_HANDLER_RESULT_NOT_YET_HANDLED;
126 }
127
128 static DBusHandlerResult request_message(DBusConnection *conn,
129                                                 DBusMessage *msg, void *data)
130 {
131         DBusMessage *reply;
132         const char *path, *address;
133         char passbuf[24], *c,*dname; 
134         char *passkey = passbuf;
135         int ok=0;
136         
137
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;
142         }
143         dname = resolve_bluetooth_name(conn,path,address);
144         if (!dname) dname = "";
145         while (!ok) {
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++) {
149                         if (!isdigit(*c)) {
150                                 printf("Invalid passkey. Retry, please\n");                             break;
151                         }       
152                 }       
153                 if (*c=='\n') *c=0;
154                 if (!*c && c>passkey) ok=1;
155                 if (c==passkey) return DBUS_HANDLER_RESULT_NOT_YET_HANDLED;
156         }
157         reply = dbus_message_new_method_return(msg);
158         if (!reply) {
159                 fprintf(stderr, "Can't create reply message\n");
160                 return DBUS_HANDLER_RESULT_NOT_YET_HANDLED;
161         }
162
163         dbus_message_append_args(reply, DBUS_TYPE_STRING, &passkey,
164                                         DBUS_TYPE_INVALID);
165
166         dbus_connection_send(conn, reply, NULL);
167
168         dbus_connection_flush(conn);
169
170         dbus_message_unref(reply);
171
172         return DBUS_HANDLER_RESULT_HANDLED;
173 }
174
175 static DBusHandlerResult release_message(DBusConnection *conn,
176                                                 DBusMessage *msg, void *data)
177 {
178         DBusMessage *reply;
179
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;
183         }
184
185         reply = dbus_message_new_method_return(msg);
186         if (!reply) {
187                 fprintf(stderr, "Can't create reply message\n");
188                 return DBUS_HANDLER_RESULT_NOT_YET_HANDLED;
189         }
190
191         dbus_message_append_args(reply, DBUS_TYPE_INVALID);
192
193         dbus_connection_send(conn, reply, NULL);
194
195         dbus_connection_flush(conn);
196
197         dbus_message_unref(reply);
198
199         if (!__io_canceled)
200                 fprintf(stderr, "Passkey service has been released\n");
201
202         __io_terminated = 1;
203
204         return DBUS_HANDLER_RESULT_HANDLED;
205 }
206
207 static DBusHandlerResult agent_message(DBusConnection *conn,
208                                                 DBusMessage *msg, void *data)
209 {
210         if (dbus_message_is_method_call(msg, "org.bluez.PasskeyAgent", "Request"))
211                 return request_message(conn, msg, data);
212
213         if (dbus_message_is_method_call(msg, "org.bluez.PasskeyAgent", "Release"))
214                 return release_message(conn, msg, data);
215
216         return DBUS_HANDLER_RESULT_NOT_YET_HANDLED;
217 }
218
219 static const DBusObjectPathVTable agent_table = {
220         .message_function = agent_message,
221 };
222
223 static int register_agent(DBusConnection *conn, const char *agent_path)
224 {
225         DBusMessage *msg, *reply;
226         DBusError err;
227         const char *path, *method;
228
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");
232                 return -1;
233         }
234
235         path = "/org/bluez";
236         method = "RegisterDefaultPasskeyAgent";
237         msg = dbus_message_new_method_call("org.bluez", path, INTERFACE, method);
238         if (!msg) {
239                 fprintf(stderr, "Can't allocate new method call\n");
240                 return -1;
241         }
242
243         dbus_message_append_args(msg, DBUS_TYPE_STRING, &agent_path,
244                                                         DBUS_TYPE_INVALID);
245
246         dbus_error_init(&err);
247
248         reply = dbus_connection_send_with_reply_and_block(conn, msg, -1, &err);
249
250         dbus_message_unref(msg);
251
252         if (!reply) {
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);
257                 }
258                 return -1;
259         }
260
261         dbus_message_unref(reply);
262
263         dbus_connection_flush(conn);
264
265         return 0;
266 }
267
268 static int unregister_agent(DBusConnection *conn, const char *agent_path)
269 {
270         DBusMessage *msg, *reply;
271         DBusError err;
272         const char *path, *method;
273
274         path = "/org/bluez";
275         method = "UnregisterDefaultPasskeyAgent";
276
277         msg = dbus_message_new_method_call("org.bluez", path, INTERFACE, method);
278         if (!msg) {
279                 fprintf(stderr, "Can't allocate new method call\n");
280                 dbus_connection_unref(conn);
281                 exit(1);
282         }
283
284         dbus_message_append_args(msg, DBUS_TYPE_STRING, &agent_path,
285                                                         DBUS_TYPE_INVALID);
286
287         dbus_error_init(&err);
288
289         reply = dbus_connection_send_with_reply_and_block(conn, msg, -1, &err);
290
291         dbus_message_unref(msg);
292
293         if (!reply) {
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);
298                 }
299                 return -1;
300         }
301
302         dbus_message_unref(reply);
303
304         dbus_connection_flush(conn);
305
306         dbus_connection_unregister_object_path(conn, agent_path);
307
308         return 0;
309 }
310 /* Gets default bluetooth adapter name */
311 char *get_default_adapter(DBusConnection *conn)
312 {
313         DBusMessage *msg, *reply;
314         char *result=NULL;
315         DBusError err;
316
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))
322         {
323                 fprintf(stderr,"Error getting default adapter: %s\n",err.message);
324                 dbus_error_free(&err);
325                 exit(1);
326         }       
327         dbus_message_unref(msg);
328         result=strdup(result);
329         dbus_message_unref(reply);
330         return result;
331 }
332
333
334 /* Finishes pending call without retriving anything */
335 static void finish_pending_call(DBusPendingCall *pending, void *user_data)
336 {
337         dbus_pending_call_unref(pending);
338 }
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
343  */
344 char *adapter_call(DBusConnection *conn, char *adapter,const char *method,
345    const char *argument, int wait_reply) {
346         DBusMessage *msg, *reply;
347         char *result=NULL;
348         DBusError err;
349         msg = dbus_message_new_method_call("org.bluez", adapter,"org.bluez.Adapter", method);
350         if (argument !=NULL) 
351                 dbus_message_append_args(msg, DBUS_TYPE_STRING, &argument,
352                         DBUS_TYPE_INVALID);
353                                                         
354         if (wait_reply) {
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);
358                 if (result) 
359                         result=strdup(result);
360                 dbus_message_unref(reply);
361         } else {
362                 DBusPendingCall *pending;
363                 dbus_connection_send_with_reply(conn,msg,&pending,-1);
364                 dbus_pending_call_set_notify(pending,finish_pending_call,
365                         NULL,NULL);
366                         
367         }
368         dbus_message_unref(msg);
369         return result;
370 }
371
372 static void usage(const char *progname)
373 {
374         printf("Bluetooth passkey agent ver %s\n\n", VERSION);
375
376         printf("Usage:\n"
377                 "\t%s [options]\n"
378                 "\n",progname);
379         printf("Options:\n"
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");
385 }
386
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' },
392         { "pair",1,0,'P' },
393         { "remove",1,0,'r'},
394         { 0, 0, 0, 0 }
395 };
396
397 int main(int argc, char *argv[])
398 {
399         struct sigaction sa;
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;
404         int opt,set_mode=0;
405
406         snprintf(default_path, sizeof(default_path),
407                                 "/org/bluez/passkey_agent_%d", getpid());
408
409         while ((opt = getopt_long(argc, argv, "p:r:P:hVd", main_options, NULL)) != EOF) {
410                 switch(opt) {
411                 case 'p':
412                         if (optarg[0] != '/') {
413                                 fprintf(stderr, "Invalid path\n");
414                                 exit(1);
415                         }
416                         agent_path = strdup(optarg);
417                         break;
418                 case 'd': set_mode=1;
419                         break;
420                 case 'P': pair_address=optarg;
421                         break;
422                 case 'r' : remove_bonding=optarg;       
423                         break;
424                 case 'h':
425                         usage(argv[0]);
426
427                         exit(0);
428                 case 'V':
429                         printf("btpasskey " VERSION " Copyright (c) 2007-2008, Victor Wagner\n");
430                         exit(0);
431                 default:
432                         fprintf(stderr,"Invalid option -%c. Use %s --help\n",
433                         opt,argv[0]);
434
435                         exit(1);
436                 }
437         }
438
439
440
441         if (!agent_path)
442                 agent_path = strdup(default_path);
443
444         conn = dbus_bus_get(DBUS_BUS_SYSTEM, NULL);
445         if (!conn) {
446                 fprintf(stderr, "Can't get on system bus");
447                 exit(1);
448         }
449         if (set_mode || pair_address|| remove_bonding)
450         {
451         /* With these options we need default adapter to interact */
452                 default_adapter = get_default_adapter(conn);
453         }       
454         if (remove_bonding) 
455         {
456                 adapter_call(conn,default_adapter,"RemoveBonding",remove_bonding,0);
457                 dbus_connection_unref(conn);
458                 exit(0);
459         }
460
461         if (register_agent(conn, agent_path) < 0) {
462                 dbus_connection_unref(conn);
463                 exit(1);
464         }
465
466         if (!dbus_connection_add_filter(conn, agent_filter, NULL, NULL))
467                 fprintf(stderr, "Can't add signal filter");
468
469         snprintf(match_string, sizeof(match_string),
470                         "interface=%s,member=NameOwnerChanged,arg0=%s",
471                                         DBUS_INTERFACE_DBUS, "org.bluez");
472
473         dbus_bus_add_match(conn, match_string, NULL);
474
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");
485         if (set_mode) {
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*/
489                         free(saved_mode);
490                         saved_mode=NULL;
491                  }
492                  else
493                  {
494                     adapter_call(conn,default_adapter,"SetMode","discoverable",1);
495                  }
496         }
497         if (pair_address) {
498                 adapter_call(conn,default_adapter,"CreateBonding", pair_address,0);
499         }
500         while (!__io_canceled && !__io_terminated) {
501                 if (dbus_connection_read_write_dispatch(conn, 100) != TRUE)
502                         break;
503         }
504
505         if (!__io_terminated)
506                 unregister_agent(conn, agent_path);
507
508         if (saved_mode) {
509                 adapter_call(conn,default_adapter,"SetMode",saved_mode,1);
510                 free(saved_mode);
511         }       
512         dbus_connection_unref(conn);
513         if (default_adapter) free(default_adapter);
514
515         return 0;
516 }