mesh: Clear prov capabilities structure before reading
[platform/upstream/bluez.git] / mesh / agent.c
1 /*
2  *
3  *  BlueZ - Bluetooth protocol stack for Linux
4  *
5  *  Copyright (C) 2018-2019  Intel Corporation. All rights reserved.
6  *
7  *
8  *  This library is free software; you can redistribute it and/or
9  *  modify it under the terms of the GNU Lesser General Public
10  *  License as published by the Free Software Foundation; either
11  *  version 2.1 of the License, or (at your option) any later version.
12  *
13  *  This library 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 GNU
16  *  Lesser General Public License for more details.
17  *
18  */
19
20 #ifdef HAVE_CONFIG_H
21 #include <config.h>
22 #endif
23
24 #include <ell/ell.h>
25
26 #include "mesh/mesh.h"
27 #include "mesh/error.h"
28 #include "mesh/dbus.h"
29 #include "mesh/agent.h"
30
31 typedef enum {
32         MESH_AGENT_REQUEST_BLINK,
33         MESH_AGENT_REQUEST_BEEP,
34         MESH_AGENT_REQUEST_VIBRATE,
35         MESH_AGENT_REQUEST_OUT_NUMERIC,
36         MESH_AGENT_REQUEST_OUT_ALPHA,
37         MESH_AGENT_REQUEST_PUSH,
38         MESH_AGENT_REQUEST_TWIST,
39         MESH_AGENT_REQUEST_IN_NUMERIC,
40         MESH_AGENT_REQUEST_IN_ALPHA,
41         MESH_AGENT_REQUEST_STATIC_OOB,
42         MESH_AGENT_REQUEST_PRIVATE_KEY,
43         MESH_AGENT_REQUEST_PUBLIC_KEY,
44         MESH_AGENT_REQUEST_CAPABILITIES,
45 } agent_request_type_t;
46
47 struct agent_request {
48         agent_request_type_t type;
49         struct l_dbus_message *msg;
50         void *cb;
51         void *user_data;
52 };
53
54 struct mesh_agent {
55         char *path;
56         char *owner;
57         struct mesh_agent_prov_caps caps;
58         struct agent_request *req;
59 };
60
61 struct prov_action {
62         const char *action;
63         uint16_t output;
64         uint16_t input;
65         uint8_t size;
66 };
67
68 struct oob_info {
69         const char *oob;
70         uint16_t mask;
71 };
72
73 static struct prov_action cap_table[] = {
74         {"blink", 0x0001, 0x0000, 1},
75         {"beep", 0x0002, 0x0000, 1},
76         {"vibrate", 0x0004, 0x0000, 1},
77         {"out-numeric", 0x0008, 0x0000, 8},
78         {"out-alpha", 0x0010, 0x0000, 8},
79         {"push", 0x0000, 0x0001, 1},
80         {"twist", 0x0000, 0x0002, 1},
81         {"in-numeric", 0x0000, 0x0004, 8},
82         {"in-alpha", 0x0000, 0x0008, 8}
83 };
84
85 static struct oob_info oob_table[] = {
86         {"other", 0x0001},
87         {"uri", 0x0002},
88         {"machine-code-2d", 0x0004},
89         {"barcode", 0x0008},
90         {"nfc", 0x0010},
91         {"number", 0x0020},
92         {"string", 0x0040},
93         {"on-box", 0x0800},
94         {"in-box", 0x1000},
95         {"on-paper", 0x2000},
96         {"in-manual", 0x4000},
97         {"on-device", 0x8000}
98 };
99
100 static struct l_queue *agents;
101
102 static bool simple_match(const void *a, const void *b)
103 {
104         return a == b;
105 }
106
107 static void parse_prov_caps(struct mesh_agent_prov_caps *caps,
108                                 struct l_dbus_message_iter *property)
109 {
110         struct l_dbus_message_iter iter_caps;
111         const char *str;
112         uint32_t i;
113
114         if (!l_dbus_message_iter_get_variant(property, "as", &iter_caps))
115                 return;
116
117         while (l_dbus_message_iter_next_entry(&iter_caps, &str)) {
118                 for (i = 0; i < L_ARRAY_SIZE(cap_table); i++) {
119                         if (strcmp(str, cap_table[i].action))
120                                 continue;
121
122                         caps->output_action |= cap_table[i].output;
123                         if (cap_table[i].output &&
124                                         caps->output_size < cap_table[i].size)
125                                 caps->output_size = cap_table[i].size;
126
127                         caps->input_action |= cap_table[i].input;
128                         if (cap_table[i].input &&
129                                         caps->input_size < cap_table[i].size)
130                                 caps->input_size = cap_table[i].size;
131
132                         break;
133                 }
134
135                 if (!strcmp(str, "public-oob"))
136                         caps->pub_type = 1;
137                 else if (!strcmp(str, "static-oob"))
138                         caps->static_type = 1;
139         }
140
141 }
142
143 static void parse_oob_info(struct mesh_agent_prov_caps *caps,
144                                 struct l_dbus_message_iter *property)
145 {
146         struct l_dbus_message_iter iter_oob;
147         uint32_t i;
148         const char *str;
149
150         if (!l_dbus_message_iter_get_variant(property, "as", &iter_oob))
151                 return;
152
153         while (l_dbus_message_iter_next_entry(&iter_oob, &str)) {
154                 for (i = 0; i < L_ARRAY_SIZE(oob_table); i++) {
155                         if (strcmp(str, oob_table[i].oob))
156                                 continue;
157                         caps->oob_info |= oob_table[i].mask;
158                 }
159         }
160 }
161
162 static void parse_properties(struct mesh_agent *agent,
163                                         struct l_dbus_message_iter *properties)
164 {
165         const char *key, *uri_string;
166         struct l_dbus_message_iter variant;
167
168         memset(&agent->caps, 0, sizeof(agent->caps));
169
170         while (l_dbus_message_iter_next_entry(properties, &key, &variant)) {
171                 if (!strcmp(key, "Capabilities")) {
172                         parse_prov_caps(&agent->caps, &variant);
173                 } else if (!strcmp(key, "URI")) {
174                         l_dbus_message_iter_get_variant(&variant, "s",
175                                                                 &uri_string);
176                         /* TODO: compute hash */
177                 } else if (!strcmp(key, "OutOfBandInfo")) {
178                         parse_oob_info(&agent->caps, &variant);
179                 }
180         }
181 }
182
183 static void agent_free(void *agent_data)
184 {
185         struct mesh_agent *agent = agent_data;
186         int err;
187         mesh_agent_cb_t simple_cb;
188         mesh_agent_key_cb_t key_cb;
189         mesh_agent_number_cb_t number_cb;
190
191         if (!l_queue_find(agents, simple_match, agent))
192                 return;
193
194         err = MESH_ERROR_DOES_NOT_EXIST;
195
196         if (agent->req && agent->req->cb) {
197                 struct agent_request *req = agent->req;
198
199                 switch (req->type) {
200                 case MESH_AGENT_REQUEST_PUSH:
201                 case MESH_AGENT_REQUEST_TWIST:
202                 case MESH_AGENT_REQUEST_IN_NUMERIC:
203                         number_cb = req->cb;
204                         number_cb(req->user_data, err, 0);
205                         break;
206                 case MESH_AGENT_REQUEST_IN_ALPHA:
207                 case MESH_AGENT_REQUEST_STATIC_OOB:
208                 case MESH_AGENT_REQUEST_PRIVATE_KEY:
209                 case MESH_AGENT_REQUEST_PUBLIC_KEY:
210                         key_cb = req->cb;
211                         key_cb(req->user_data, err, NULL, 0);
212                         break;
213                 case MESH_AGENT_REQUEST_BLINK:
214                 case MESH_AGENT_REQUEST_BEEP:
215                 case MESH_AGENT_REQUEST_VIBRATE:
216                 case MESH_AGENT_REQUEST_OUT_NUMERIC:
217                 case MESH_AGENT_REQUEST_OUT_ALPHA:
218                 case MESH_AGENT_REQUEST_CAPABILITIES:
219                         simple_cb = agent->req->cb;
220                         simple_cb(req->user_data, err);
221                 default:
222                         break;
223                 }
224
225                 l_dbus_message_unref(req->msg);
226                 l_free(req);
227         }
228
229         l_free(agent->path);
230         l_free(agent->owner);
231 }
232
233 void mesh_agent_remove(struct mesh_agent *agent)
234 {
235         if (!agent || !l_queue_find(agents, simple_match, agent))
236                 return;
237
238         agent_free(agent);
239         l_queue_remove(agents, agent);
240 }
241
242 void mesh_agent_cleanup(void)
243 {
244         if (!agents)
245                 return;
246
247         l_queue_destroy(agents, agent_free);
248
249 }
250
251 void mesh_agent_init(void)
252 {
253         if (!agents)
254                 agents = l_queue_new();
255 }
256
257 struct mesh_agent *mesh_agent_create(const char *path, const char *owner,
258                                         struct l_dbus_message_iter *properties)
259 {
260         struct mesh_agent *agent;
261
262         agent = l_new(struct mesh_agent, 1);
263         agent->owner = l_strdup(owner);
264         agent->path = l_strdup(path);
265
266         parse_properties(agent, properties);
267
268         l_queue_push_tail(agents, agent);
269
270         return agent;
271 }
272
273 struct mesh_agent_prov_caps *mesh_agent_get_caps(struct mesh_agent *agent)
274 {
275         if (!agent || !l_queue_find(agents, simple_match, agent))
276                 return NULL;
277
278         return &agent->caps;
279 }
280
281 static struct agent_request *create_request(agent_request_type_t type,
282                                                 void *cb, void *data)
283 {
284         struct agent_request *req;
285
286         req = l_new(struct agent_request, 1);
287
288         req->type = type;
289         req->cb = cb;
290         req->user_data = data;
291
292         return req;
293 }
294
295 static int get_reply_error(struct l_dbus_message *reply)
296 {
297         const char *name, *desc;
298
299         if (l_dbus_message_is_error(reply)) {
300
301                 l_dbus_message_get_error(reply, &name, &desc);
302                 l_error("Agent failed (%s), %s", name, desc);
303                 return MESH_ERROR_FAILED;
304         }
305
306         return MESH_ERROR_NONE;
307 }
308
309 static void properties_reply(struct l_dbus_message *reply, void *user_data)
310 {
311         struct mesh_agent *agent = user_data;
312         struct agent_request *req;
313         mesh_agent_cb_t cb;
314         struct l_dbus_message_iter properties;
315         int err;
316
317         if (!l_queue_find(agents, simple_match, agent) || !agent->req)
318                 return;
319
320         req = agent->req;
321
322         err = get_reply_error(reply);
323
324         if (err != MESH_ERROR_NONE)
325                 goto fail;
326
327         if (!l_dbus_message_get_arguments(reply, "a{sv}", &properties)) {
328                 err = MESH_ERROR_FAILED;
329                 goto fail;
330         }
331
332         parse_properties(agent, &properties);
333 fail:
334         if (req->cb) {
335                 cb = req->cb;
336                 cb(req->user_data, err);
337         }
338
339         l_dbus_message_unref(req->msg);
340         l_free(req);
341         agent->req = NULL;
342 }
343
344 void mesh_agent_refresh(struct mesh_agent *agent, mesh_agent_cb_t cb,
345                                                         void *user_data)
346 {
347         struct l_dbus *dbus = dbus_get_bus();
348         struct l_dbus_message *msg;
349         struct l_dbus_message_builder *builder;
350
351         agent->req = create_request(MESH_AGENT_REQUEST_CAPABILITIES, (void *)cb,
352                                                                 user_data);
353
354         msg = l_dbus_message_new_method_call(dbus, agent->owner, agent->path,
355                                                 L_DBUS_INTERFACE_PROPERTIES,
356                                                 "GetAll");
357
358         builder = l_dbus_message_builder_new(msg);
359         l_dbus_message_builder_append_basic(builder, 's',
360                                                 MESH_PROVISION_AGENT_INTERFACE);
361         l_dbus_message_builder_finalize(builder);
362         l_dbus_message_builder_destroy(builder);
363
364         l_dbus_send_with_reply(dbus_get_bus(), msg, properties_reply, agent,
365                                                                         NULL);
366
367         agent->req->msg = l_dbus_message_ref(msg);
368 }
369
370
371 static void simple_reply(struct l_dbus_message *reply, void *user_data)
372 {
373         struct mesh_agent *agent = user_data;
374         struct agent_request *req;
375         mesh_agent_cb_t cb;
376         int err;
377
378         if (!l_queue_find(agents, simple_match, agent) || !agent->req)
379                 return;
380
381         req = agent->req;
382
383         err = get_reply_error(reply);
384
385         l_dbus_message_unref(req->msg);
386
387         if (req->cb) {
388                 cb = req->cb;
389                 cb(req->user_data, err);
390         }
391
392         l_free(req);
393         agent->req = NULL;
394 }
395
396 static void numeric_reply(struct l_dbus_message *reply, void *user_data)
397 {
398         struct mesh_agent *agent = user_data;
399         struct agent_request *req;
400         mesh_agent_number_cb_t cb;
401         uint32_t count;
402         int err;
403
404         if (!l_queue_find(agents, simple_match, agent) || !agent->req)
405                 return;
406
407         req = agent->req;
408
409         err = get_reply_error(reply);
410
411         count = 0;
412
413         if (err == MESH_ERROR_NONE) {
414                 if (!l_dbus_message_get_arguments(reply, "u", &count)) {
415                         l_error("Failed to retrieve numeric input");
416                         err = MESH_ERROR_FAILED;
417                 }
418         }
419
420         l_dbus_message_unref(req->msg);
421
422         if (req->cb) {
423                 cb = req->cb;
424                 cb(req->user_data, err, count);
425         }
426
427         l_free(req);
428         agent->req = NULL;
429 }
430
431 static void key_reply(struct l_dbus_message *reply, void *user_data)
432 {
433         struct mesh_agent *agent = user_data;
434         struct agent_request *req;
435         mesh_agent_key_cb_t cb;
436         struct l_dbus_message_iter iter_array;
437         uint32_t n = 0, expected_len = 0;
438         uint8_t *buf;
439         int err;
440
441         if (!l_queue_find(agents, simple_match, agent) || !agent->req)
442                 return;
443
444         req = agent->req;
445
446         err = get_reply_error(reply);
447
448         if (err != MESH_ERROR_NONE)
449                 goto done;
450
451         if (!l_dbus_message_get_arguments(reply, "ay", &iter_array)) {
452                 l_error("Failed to retrieve key input");
453                 err = MESH_ERROR_FAILED;
454                 goto done;
455         }
456
457         if (!l_dbus_message_iter_get_fixed_array(&iter_array, &buf, &n)) {
458                 l_error("Failed to retrieve key input");
459                 err = MESH_ERROR_FAILED;
460                 goto done;
461         }
462
463         if (req->type == MESH_AGENT_REQUEST_PRIVATE_KEY)
464                 expected_len = 32;
465         else if (req->type == MESH_AGENT_REQUEST_PUBLIC_KEY)
466                 expected_len = 64;
467         else
468                 expected_len = 16;
469
470         if (n != expected_len) {
471                 l_error("Bad response length: %u (need %u)", n, expected_len);
472                 err = MESH_ERROR_FAILED;
473                 n = 0;
474         }
475
476 done:
477         if (req->cb) {
478                 cb = req->cb;
479                 cb(req->user_data, err, buf, n);
480         }
481
482         l_dbus_message_unref(req->msg);
483
484         l_free(req);
485         agent->req = NULL;
486 }
487
488 static int output_request(struct mesh_agent *agent, const char *action,
489                                         agent_request_type_t type, uint32_t cnt,
490                                         void *cb, void *user_data)
491 {
492         struct l_dbus *dbus = dbus_get_bus();
493         struct l_dbus_message *msg;
494         struct l_dbus_message_builder *builder;
495
496         if (!l_queue_find(agents, simple_match, agent))
497                 return MESH_ERROR_DOES_NOT_EXIST;
498
499         if (agent->req)
500                 return MESH_ERROR_BUSY;
501
502         agent->req = create_request(type, cb, user_data);
503         msg = l_dbus_message_new_method_call(dbus, agent->owner, agent->path,
504                                                 MESH_PROVISION_AGENT_INTERFACE,
505                                                 "DisplayNumeric");
506
507         builder = l_dbus_message_builder_new(msg);
508         l_dbus_message_builder_append_basic(builder, 's', action);
509         l_dbus_message_builder_append_basic(builder, 'u', &cnt);
510         l_dbus_message_builder_finalize(builder);
511         l_dbus_message_builder_destroy(builder);
512
513         l_debug("Send DisplayNumeric request to %s %s",
514                                                 agent->owner, agent->path);
515
516         l_dbus_send_with_reply(dbus_get_bus(), msg, simple_reply, agent,
517                                                                         NULL);
518
519         agent->req->msg = l_dbus_message_ref(msg);
520
521         return MESH_ERROR_NONE;
522 }
523
524 static int prompt_input(struct mesh_agent *agent, const char *action,
525                                         agent_request_type_t type, bool numeric,
526                                         void *cb, void *user_data)
527 {
528         struct l_dbus *dbus = dbus_get_bus();
529         struct l_dbus_message *msg;
530         struct l_dbus_message_builder *builder;
531         const char *method_name;
532         l_dbus_message_func_t reply_cb;
533
534         if (!l_queue_find(agents, simple_match, agent))
535                 return MESH_ERROR_DOES_NOT_EXIST;
536
537         if (agent->req)
538                 return MESH_ERROR_BUSY;
539
540         agent->req = create_request(type, cb, user_data);
541
542         method_name = numeric ? "PromptNumeric" : "PromptStatic";
543
544         msg = l_dbus_message_new_method_call(dbus, agent->owner,
545                                                 agent->path,
546                                                 MESH_PROVISION_AGENT_INTERFACE,
547                                                 method_name);
548
549         builder = l_dbus_message_builder_new(msg);
550         l_dbus_message_builder_append_basic(builder, 's', action);
551         l_dbus_message_builder_finalize(builder);
552         l_dbus_message_builder_destroy(builder);
553
554         l_debug("Send \"%s\" input request to %s %s", action,
555                                                 agent->owner, agent->path);
556
557         reply_cb = numeric ? numeric_reply : key_reply;
558
559         l_dbus_send_with_reply(dbus_get_bus(), msg, reply_cb, agent, NULL);
560
561         agent->req->msg = l_dbus_message_ref(msg);
562
563         return MESH_ERROR_NONE;
564 }
565
566 static int request_key(struct mesh_agent *agent,
567                                         agent_request_type_t type,
568                                         void *cb, void *user_data)
569 {
570         struct l_dbus *dbus = dbus_get_bus();
571         struct l_dbus_message *msg;
572         const char *method_name;
573
574         if (!l_queue_find(agents, simple_match, agent))
575                 return MESH_ERROR_DOES_NOT_EXIST;
576
577         if (agent->req)
578                 return MESH_ERROR_BUSY;
579
580         agent->req = create_request(type, cb, user_data);
581
582         method_name = (type == MESH_AGENT_REQUEST_PRIVATE_KEY) ?
583                                                 "PrivateKey" : "PublicKey";
584
585         msg = l_dbus_message_new_method_call(dbus, agent->owner,
586                                                 agent->path,
587                                                 MESH_PROVISION_AGENT_INTERFACE,
588                                                 method_name);
589
590         l_dbus_message_set_arguments(msg, "");
591
592         l_debug("Send key request to %s %s", agent->owner, agent->path);
593
594         l_dbus_send_with_reply(dbus_get_bus(), msg, key_reply, agent, NULL);
595
596         agent->req->msg = l_dbus_message_ref(msg);
597
598         return MESH_ERROR_NONE;
599 }
600
601 int mesh_agent_display_string(struct mesh_agent *agent, const char *str,
602                                 mesh_agent_cb_t cb, void *user_data)
603 {
604         struct l_dbus *dbus = dbus_get_bus();
605         struct l_dbus_message *msg;
606         struct l_dbus_message_builder *builder;
607
608         if (!l_queue_find(agents, simple_match, agent))
609                 return MESH_ERROR_DOES_NOT_EXIST;
610
611         if (agent->req)
612                 return MESH_ERROR_BUSY;
613
614         agent->req = create_request(MESH_AGENT_REQUEST_OUT_ALPHA,
615                                                                 cb, user_data);
616         msg = l_dbus_message_new_method_call(dbus, agent->owner, agent->path,
617                                                 MESH_PROVISION_AGENT_INTERFACE,
618                                                 "DisplayString");
619
620         builder = l_dbus_message_builder_new(msg);
621         l_dbus_message_builder_append_basic(builder, 's', str);
622         l_dbus_message_builder_finalize(builder);
623         l_dbus_message_builder_destroy(builder);
624
625         l_debug("Send DisplayString request to %s %s",
626                                                 agent->owner, agent->path);
627
628         l_dbus_send_with_reply(dbus_get_bus(), msg, simple_reply, agent,
629                                                                         NULL);
630
631         agent->req->msg = l_dbus_message_ref(msg);
632
633         return MESH_ERROR_NONE;
634
635 }
636
637 int mesh_agent_display_number(struct mesh_agent *agent, bool initiator,
638                                         uint8_t action, uint32_t count,
639                                         mesh_agent_cb_t cb, void *user_data)
640 {
641         const char *str_type;
642         agent_request_type_t type;
643
644         type = action;
645
646         if (initiator)
647                 type = action + MESH_AGENT_REQUEST_PUSH;
648
649         if (type >= L_ARRAY_SIZE(cap_table))
650                 return MESH_ERROR_INVALID_ARGS;
651
652         str_type = cap_table[type].action;
653
654         return output_request(agent, str_type, type, count, cb, user_data);
655 }
656
657 int mesh_agent_prompt_number(struct mesh_agent *agent, bool initiator,
658                                                 uint8_t action,
659                                                 mesh_agent_number_cb_t cb,
660                                                 void *user_data)
661 {
662         const char *str_type;
663         agent_request_type_t type;
664
665         type = action;
666
667         if (!initiator)
668                 type = action + MESH_AGENT_REQUEST_PUSH;
669
670         if (type >= L_ARRAY_SIZE(cap_table))
671                 return MESH_ERROR_INVALID_ARGS;
672
673         str_type = cap_table[type].action;
674
675         return prompt_input(agent, str_type, type, true, cb, user_data);
676 }
677
678 int mesh_agent_prompt_alpha(struct mesh_agent *agent, bool initiator,
679                                         mesh_agent_key_cb_t cb, void *user_data)
680 {
681         if (initiator)
682                 return prompt_input(agent,
683                                 cap_table[MESH_AGENT_REQUEST_OUT_ALPHA].action,
684                                 MESH_AGENT_REQUEST_OUT_ALPHA, false, cb,
685                                 user_data);
686         else
687                 return prompt_input(agent,
688                                 cap_table[MESH_AGENT_REQUEST_IN_ALPHA].action,
689                                 MESH_AGENT_REQUEST_IN_ALPHA, false, cb,
690                                 user_data);
691 }
692
693 int mesh_agent_request_static(struct mesh_agent *agent, mesh_agent_key_cb_t cb,
694                                                                 void *user_data)
695 {
696         return prompt_input(agent, "static-oob", MESH_AGENT_REQUEST_STATIC_OOB,
697                                                         false, cb, user_data);
698 }
699
700 int mesh_agent_request_private_key(struct mesh_agent *agent,
701                                 mesh_agent_key_cb_t cb, void *user_data)
702 {
703         return request_key(agent, MESH_AGENT_REQUEST_PRIVATE_KEY, cb,
704                                                                 user_data);
705
706 }
707
708 int mesh_agent_request_public_key(struct mesh_agent *agent,
709                                 mesh_agent_key_cb_t cb, void *user_data)
710 {
711         return request_key(agent, MESH_AGENT_REQUEST_PUBLIC_KEY, cb,
712                                                                 user_data);
713 }
714
715 void mesh_agent_cancel(struct mesh_agent *agent)
716 {
717         struct l_dbus *dbus = dbus_get_bus();
718         struct l_dbus_message *msg;
719
720         if (!l_queue_find(agents, simple_match, agent))
721                 return;
722
723         msg = l_dbus_message_new_method_call(dbus, agent->owner, agent->path,
724                                                 MESH_PROVISION_AGENT_INTERFACE,
725                                                 "Cancel");
726
727         l_dbus_message_set_arguments(msg, "");
728
729         l_dbus_send(dbus, msg);
730 }