Line data Source code
1 : /*
2 : Authors:
3 : Pavel Březina <pbrezina@redhat.com>
4 :
5 : Copyright (C) 2014 Red Hat
6 :
7 : This program is free software; you can redistribute it and/or modify
8 : it under the terms of the GNU General Public License as published by
9 : the Free Software Foundation; either version 3 of the License, or
10 : (at your option) any later version.
11 :
12 : This program is distributed in the hope that it will be useful,
13 : but WITHOUT ANY WARRANTY; without even the implied warranty of
14 : MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
15 : GNU General Public License for more details.
16 :
17 : You should have received a copy of the GNU General Public License
18 : along with this program. If not, see <http://www.gnu.org/licenses/>.
19 : */
20 :
21 : #include <talloc.h>
22 : #include <dbus/dbus.h>
23 : #include <dhash.h>
24 :
25 : #include "util/util.h"
26 : #include "sbus/sssd_dbus.h"
27 : #include "sbus/sssd_dbus_meta.h"
28 : #include "sbus/sssd_dbus_private.h"
29 :
30 : static struct sbus_interface *
31 0 : sbus_iface_list_lookup(struct sbus_interface_list *list,
32 : const char *iface)
33 : {
34 : struct sbus_interface_list *item;
35 :
36 0 : DLIST_FOR_EACH(item, list) {
37 0 : if (strcmp(item->interface->vtable->meta->name, iface) == 0) {
38 0 : return item->interface;
39 : }
40 : }
41 :
42 0 : return NULL;
43 : }
44 :
45 : static errno_t
46 0 : sbus_iface_list_copy(TALLOC_CTX *mem_ctx,
47 : struct sbus_interface_list *list,
48 : struct sbus_interface_list **_copy)
49 : {
50 : TALLOC_CTX *list_ctx;
51 0 : struct sbus_interface_list *new_list = NULL;
52 : struct sbus_interface_list *new_item;
53 : struct sbus_interface_list *item;
54 : errno_t ret;
55 :
56 0 : if (list == NULL) {
57 0 : *_copy = NULL;
58 0 : return EOK;
59 : }
60 :
61 0 : list_ctx = talloc_new(mem_ctx);
62 0 : if (list_ctx == NULL) {
63 0 : return ENOMEM;
64 : }
65 :
66 0 : DLIST_FOR_EACH(item, list) {
67 0 : if (sbus_iface_list_lookup(new_list,
68 0 : item->interface->vtable->meta->name) != NULL) {
69 : /* already in list */
70 0 : continue;
71 : }
72 :
73 0 : new_item = talloc_zero(list_ctx, struct sbus_interface_list);
74 0 : if (new_item == NULL) {
75 0 : ret = ENOMEM;
76 0 : goto done;
77 : }
78 :
79 0 : new_item->interface = item->interface;
80 0 : DLIST_ADD(new_list, new_item);
81 : }
82 :
83 0 : *_copy = new_list;
84 0 : ret = EOK;
85 :
86 : done:
87 0 : if (ret != EOK) {
88 0 : talloc_free(list_ctx);
89 : }
90 :
91 0 : return ret;
92 : }
93 :
94 : /**
95 : * Object paths that represent all objects under the path:
96 : * /org/object/path/~* (without tilda)
97 : */
98 0 : static bool sbus_opath_is_subtree(const char *path)
99 : {
100 : size_t len;
101 :
102 0 : len = strlen(path);
103 :
104 0 : if (len < 2) {
105 0 : return false;
106 : }
107 :
108 0 : return path[len - 2] == '/' && path[len - 1] == '*';
109 : }
110 :
111 : /**
112 : * If the path represents a subtree object path, this function will
113 : * remove /~* from the end.
114 : */
115 0 : static char *sbus_opath_get_base_path(TALLOC_CTX *mem_ctx,
116 : const char *object_path)
117 : {
118 : char *tree_path;
119 : size_t len;
120 :
121 0 : tree_path = talloc_strdup(mem_ctx, object_path);
122 0 : if (tree_path == NULL) {
123 0 : return NULL;
124 : }
125 :
126 0 : if (!sbus_opath_is_subtree(tree_path)) {
127 0 : return tree_path;
128 : }
129 :
130 : /* replace / only if it is not a root path (only slash) */
131 0 : len = strlen(tree_path);
132 0 : tree_path[len - 1] = '\0';
133 0 : tree_path[len - 2] = (len - 2 != 0) ? '\0' : '/';
134 :
135 0 : return tree_path;
136 : }
137 :
138 0 : static char *sbus_opath_parent_subtree(TALLOC_CTX *mem_ctx,
139 : const char *path)
140 : {
141 : char *subtree;
142 : char *slash;
143 :
144 : /* first remove /~* from the end, stop when we have reached the root i.e.
145 : * subtree == "/" */
146 0 : subtree = sbus_opath_get_base_path(mem_ctx, path);
147 0 : if (subtree == NULL || subtree[1] == '\0') {
148 0 : return NULL;
149 : }
150 :
151 : /* Find the first separator and replace the part with asterisk. */
152 0 : slash = strrchr(subtree, '/');
153 0 : if (slash == NULL) {
154 : /* we cannot continue up */
155 0 : talloc_free(subtree);
156 0 : return NULL;
157 : }
158 :
159 0 : if (*(slash + 1) == '\0') {
160 : /* this object path is invalid since it cannot end with slash */
161 0 : DEBUG(SSSDBG_CRIT_FAILURE, "Invalid object path '%s'?\n", path);
162 0 : talloc_free(subtree);
163 0 : return NULL;
164 : }
165 :
166 : /* because object path cannot end with / there is enough space for
167 : * asterisk and terminating zero */
168 0 : *(slash + 1) = '*';
169 0 : *(slash + 2) = '\0';
170 :
171 0 : return subtree;
172 : }
173 :
174 : /**
175 : * The following path related functions are based on similar code in
176 : * storaged, just tailored to use talloc instead of glib
177 : */
178 : char *
179 7 : sbus_opath_escape_part(TALLOC_CTX *mem_ctx,
180 : const char *object_path_part)
181 : {
182 : size_t n;
183 7 : char *safe_path = NULL;
184 7 : TALLOC_CTX *tmp_ctx = NULL;
185 :
186 : /* The path must be valid */
187 7 : if (object_path_part == NULL) {
188 1 : return NULL;
189 : }
190 :
191 6 : tmp_ctx = talloc_new(NULL);
192 6 : if (tmp_ctx == NULL) {
193 0 : return NULL;
194 : }
195 :
196 6 : safe_path = talloc_strdup(tmp_ctx, "");
197 6 : if (safe_path == NULL) {
198 0 : goto done;
199 : }
200 :
201 : /* Special case for an empty string */
202 6 : if (strcmp(object_path_part, "") == 0) {
203 : /* the for loop would just fall through */
204 1 : safe_path = talloc_asprintf_append_buffer(safe_path, "_");
205 1 : if (safe_path == NULL) {
206 0 : goto done;
207 : }
208 : }
209 :
210 61 : for (n = 0; object_path_part[n]; n++) {
211 55 : int c = object_path_part[n];
212 : /* D-Bus spec says:
213 : * *
214 : * * Each element must only contain the ASCII characters
215 : * "[A-Z][a-z][0-9]_"
216 : * */
217 55 : if ((c >= 'A' && c <= 'Z')
218 55 : || (c >= 'a' && c <= 'z')
219 4 : || (c >= '0' && c <= '9')) {
220 51 : safe_path = talloc_asprintf_append_buffer(safe_path, "%c", c);
221 102 : if (safe_path == NULL) {
222 0 : goto done;
223 : }
224 : } else {
225 4 : safe_path = talloc_asprintf_append_buffer(safe_path, "_%02x", c);
226 4 : if (safe_path == NULL) {
227 0 : goto done;
228 : }
229 : }
230 : }
231 :
232 6 : safe_path = talloc_steal(mem_ctx, safe_path);
233 :
234 : done:
235 6 : talloc_free(tmp_ctx);
236 6 : return safe_path;
237 : }
238 :
239 12 : static inline int unhexchar(char c)
240 : {
241 12 : if (c >= '0' && c <= '9') {
242 6 : return c - '0';
243 : }
244 :
245 6 : if (c >= 'a' && c <= 'f') {
246 6 : return c - 'a' + 10;
247 : }
248 :
249 0 : if (c >= 'A' && c <= 'F') {
250 0 : return c - 'A' + 10;
251 : }
252 :
253 0 : return -1;
254 : }
255 :
256 : char *
257 20 : sbus_opath_unescape_part(TALLOC_CTX *mem_ctx,
258 : const char *object_path_part)
259 : {
260 : char *safe_path;
261 : const char *p;
262 : int a, b, c;
263 20 : TALLOC_CTX *tmp_ctx = NULL;
264 :
265 20 : tmp_ctx = talloc_new(NULL);
266 20 : if (tmp_ctx == NULL) {
267 0 : return NULL;
268 : }
269 :
270 20 : safe_path = talloc_strdup(tmp_ctx, "");
271 20 : if (safe_path == NULL) {
272 0 : goto done;
273 : }
274 :
275 : /* Special case for the empty string */
276 20 : if (strcmp(object_path_part, "_") == 0) {
277 1 : safe_path = talloc_steal(mem_ctx, safe_path);
278 1 : goto done;
279 : }
280 :
281 157 : for (p = object_path_part; *p; p++) {
282 139 : if (*p == '_') {
283 : /* There must be at least two more chars after underscore */
284 7 : if (p[1] == '\0' || p[2] == '\0') {
285 1 : safe_path = NULL;
286 1 : goto done;
287 : }
288 :
289 6 : if ((a = unhexchar(p[1])) < 0
290 6 : || (b = unhexchar(p[2])) < 0) {
291 : /* Invalid escape code, let's take it literal then */
292 0 : c = '_';
293 : } else {
294 6 : c = ((a << 4) | b);
295 6 : p += 2;
296 : }
297 : } else {
298 132 : c = *p;
299 : }
300 :
301 138 : safe_path = talloc_asprintf_append_buffer(safe_path, "%c", c);
302 138 : if (safe_path == NULL) {
303 0 : goto done;
304 : }
305 : }
306 :
307 18 : safe_path = talloc_steal(mem_ctx, safe_path);
308 :
309 : done:
310 20 : talloc_free(tmp_ctx);
311 20 : return safe_path;
312 : }
313 :
314 : char *
315 2 : _sbus_opath_compose(TALLOC_CTX *mem_ctx,
316 : const char *base,
317 : const char *part, ...)
318 : {
319 : char *safe_part;
320 2 : char *path = NULL;
321 : va_list va;
322 :
323 2 : if (base == NULL) {
324 0 : DEBUG(SSSDBG_OP_FAILURE, "Wrong object path base!\n");
325 0 : return NULL;
326 : }
327 :
328 2 : path = talloc_strdup(mem_ctx, base);
329 2 : if (path == NULL) return NULL;
330 :
331 2 : va_start(va, part);
332 6 : while (part != NULL) {
333 2 : safe_part = sbus_opath_escape_part(mem_ctx, part);
334 2 : if (safe_part == NULL) {
335 0 : DEBUG(SSSDBG_OP_FAILURE, "Could not add [%s] to objpath\n", part);
336 0 : goto fail;
337 : }
338 :
339 2 : path = talloc_asprintf_append(path, "/%s", safe_part);
340 2 : talloc_free(safe_part);
341 2 : if (path == NULL) {
342 0 : goto fail;
343 : }
344 :
345 2 : part = va_arg(va, const char *);
346 : }
347 2 : va_end(va);
348 :
349 2 : return path;
350 :
351 : fail:
352 0 : va_end(va);
353 0 : talloc_free(path);
354 0 : return NULL;
355 : }
356 :
357 : errno_t
358 7 : sbus_opath_decompose(TALLOC_CTX *mem_ctx,
359 : const char *object_path,
360 : const char *prefix,
361 : char ***_components,
362 : size_t *_len)
363 : {
364 : TALLOC_CTX *tmp_ctx;
365 : const char *path;
366 : char **decomposed;
367 : char **unescaped;
368 : errno_t ret;
369 : int len;
370 : int i;
371 :
372 7 : tmp_ctx = talloc_new(NULL);
373 7 : if (tmp_ctx == NULL) {
374 0 : return ENOMEM;
375 : }
376 :
377 : /* Strip prefix from the path. */
378 7 : if (prefix != NULL) {
379 3 : path = sbus_opath_strip_prefix(object_path, prefix);
380 3 : if (path == NULL) {
381 1 : ret = ERR_SBUS_INVALID_PATH;
382 1 : goto done;
383 : }
384 : } else {
385 4 : path = object_path;
386 : }
387 :
388 : /* Split the string using / as delimiter. */
389 6 : split_on_separator(tmp_ctx, path, '/', true, true, &decomposed, &len);
390 :
391 : /* Unescape parts. */
392 6 : unescaped = talloc_zero_array(tmp_ctx, char *, len + 1);
393 6 : if (unescaped == NULL) {
394 0 : ret = ENOMEM;
395 0 : goto done;
396 : }
397 :
398 19 : for (i = 0; i < len; i++) {
399 13 : unescaped[i] = sbus_opath_unescape_part(unescaped, decomposed[i]);
400 13 : if (unescaped[i] == NULL) {
401 0 : ret = ENOMEM;
402 0 : goto done;
403 : }
404 : }
405 :
406 6 : if (_components != NULL) {
407 6 : *_components = talloc_steal(mem_ctx, unescaped);
408 : }
409 :
410 6 : if (_len != NULL) {
411 6 : *_len = len;
412 : }
413 :
414 6 : ret = EOK;
415 :
416 : done:
417 7 : talloc_free(tmp_ctx);
418 7 : return ret;
419 : }
420 :
421 : errno_t
422 2 : sbus_opath_decompose_exact(TALLOC_CTX *mem_ctx,
423 : const char *object_path,
424 : const char *prefix,
425 : size_t expected,
426 : char ***_components)
427 : {
428 : char **components;
429 : size_t len;
430 : errno_t ret;
431 :
432 2 : ret = sbus_opath_decompose(mem_ctx, object_path, prefix,
433 : &components, &len);
434 2 : if (ret != EOK) {
435 0 : return ret;
436 : }
437 :
438 2 : if (len != expected) {
439 1 : talloc_free(components);
440 1 : return ERR_SBUS_INVALID_PATH;
441 : }
442 :
443 1 : if (_components != NULL) {
444 1 : *_components = components;
445 : }
446 :
447 1 : return EOK;
448 : }
449 :
450 : const char *
451 9 : sbus_opath_strip_prefix(const char *object_path,
452 : const char *prefix)
453 : {
454 9 : if (strncmp(object_path, prefix, strlen(prefix)) == 0) {
455 6 : return object_path + strlen(prefix);
456 : }
457 :
458 3 : return NULL;
459 : }
460 :
461 : char *
462 4 : sbus_opath_get_object_name(TALLOC_CTX *mem_ctx,
463 : const char *object_path,
464 : const char *base_path)
465 : {
466 : const char *name;
467 :
468 4 : name = sbus_opath_strip_prefix(object_path, base_path);
469 4 : if (name == NULL || name[0] == '\0') {
470 2 : return NULL;
471 : }
472 :
473 : /* if base_path did not end with / */
474 2 : if (name[0] == '/') {
475 1 : name = name + 1;
476 : }
477 :
478 2 : return sbus_opath_unescape_part(mem_ctx, name);
479 : }
480 :
481 : static void
482 0 : sbus_opath_hash_delete_cb(hash_entry_t *item,
483 : hash_destroy_enum deltype,
484 : void *pvt)
485 : {
486 : struct sbus_connection *conn;
487 : char *path;
488 :
489 0 : conn = talloc_get_type(pvt, struct sbus_connection);
490 0 : path = sbus_opath_get_base_path(NULL, item->key.str);
491 :
492 0 : dbus_connection_unregister_object_path(conn->dbus.conn, path);
493 0 : }
494 :
495 : errno_t
496 0 : sbus_opath_hash_init(TALLOC_CTX *mem_ctx,
497 : struct sbus_connection *conn,
498 : hash_table_t **_table)
499 : {
500 0 : return sss_hash_create_ex(mem_ctx, 10, _table, 0, 0, 0, 0,
501 : sbus_opath_hash_delete_cb, conn);
502 : }
503 :
504 : static errno_t
505 0 : sbus_opath_hash_add_iface(hash_table_t *table,
506 : const char *object_path,
507 : struct sbus_interface *iface,
508 : bool *_path_known)
509 : {
510 0 : TALLOC_CTX *tmp_ctx = NULL;
511 0 : struct sbus_interface_list *list = NULL;
512 0 : struct sbus_interface_list *item = NULL;
513 0 : const char *iface_name = iface->vtable->meta->name;
514 : hash_key_t key;
515 : hash_value_t value;
516 : bool path_known;
517 : errno_t ret;
518 : int hret;
519 :
520 0 : tmp_ctx = talloc_new(NULL);
521 0 : if (tmp_ctx == NULL) {
522 0 : return ENOMEM;
523 : }
524 :
525 0 : DEBUG(SSSDBG_TRACE_FUNC, "Registering interface %s with path %s\n",
526 : iface_name, object_path);
527 :
528 : /* create new list item */
529 :
530 0 : item = talloc_zero(tmp_ctx, struct sbus_interface_list);
531 0 : if (item == NULL) {
532 0 : return ENOMEM;
533 : }
534 :
535 0 : item->interface = iface;
536 :
537 : /* first lookup existing list in hash table */
538 :
539 0 : key.type = HASH_KEY_STRING;
540 0 : key.str = talloc_strdup(tmp_ctx, object_path);
541 0 : if (key.str == NULL) {
542 0 : ret = ENOMEM;
543 0 : goto done;
544 : }
545 :
546 0 : hret = hash_lookup(table, &key, &value);
547 0 : if (hret == HASH_SUCCESS) {
548 : /* This object path has already some interface registered. We will
549 : * check for existence of the interface currently being added and
550 : * add it if missing. */
551 :
552 0 : path_known = true;
553 :
554 0 : list = talloc_get_type(value.ptr, struct sbus_interface_list);
555 0 : if (sbus_iface_list_lookup(list, iface_name) != NULL) {
556 0 : DEBUG(SSSDBG_MINOR_FAILURE, "Trying to register the same interface"
557 : " twice: iface=%s, opath=%s\n", iface_name, object_path);
558 0 : ret = EEXIST;
559 0 : goto done;
560 : }
561 :
562 0 : DLIST_ADD_END(list, item, struct sbus_interface_list *);
563 0 : ret = EOK;
564 0 : goto done;
565 0 : } else if (hret != HASH_ERROR_KEY_NOT_FOUND) {
566 0 : ret = EIO;
567 0 : goto done;
568 : }
569 :
570 : /* otherwise create new hash entry and new list */
571 :
572 0 : path_known = false;
573 0 : list = item;
574 :
575 0 : value.type = HASH_VALUE_PTR;
576 0 : value.ptr = list;
577 :
578 0 : hret = hash_enter(table, &key, &value);
579 0 : if (hret != HASH_SUCCESS) {
580 0 : ret = EIO;
581 0 : goto done;
582 : }
583 :
584 0 : talloc_steal(table, key.str);
585 0 : ret = EOK;
586 :
587 : done:
588 0 : if (ret == EOK) {
589 0 : talloc_steal(item, iface);
590 0 : talloc_steal(table, item);
591 0 : *_path_known = path_known;
592 : }
593 :
594 0 : talloc_free(tmp_ctx);
595 0 : return ret;
596 : }
597 :
598 : static bool
599 0 : sbus_opath_hash_has_path(hash_table_t *table,
600 : const char *object_path)
601 : {
602 : hash_key_t key;
603 :
604 0 : key.type = HASH_KEY_STRING;
605 0 : key.str = discard_const(object_path);
606 :
607 0 : return hash_has_key(table, &key);
608 : }
609 :
610 : /**
611 : * First @object_path is looked up in @table, if it is not found it steps up
612 : * in the path hierarchy and try to lookup the parent node. This continues
613 : * until the root is reached.
614 : */
615 : struct sbus_interface *
616 0 : sbus_opath_hash_lookup_iface(hash_table_t *table,
617 : const char *object_path,
618 : const char *iface_name)
619 : {
620 0 : TALLOC_CTX *tmp_ctx = NULL;
621 0 : struct sbus_interface_list *list = NULL;
622 0 : struct sbus_interface *iface = NULL;
623 0 : char *lookup_path = NULL;
624 : hash_key_t key;
625 : hash_value_t value;
626 : int hret;
627 :
628 0 : tmp_ctx = talloc_new(NULL);
629 0 : if (tmp_ctx == NULL) {
630 0 : return NULL;
631 : }
632 :
633 0 : lookup_path = talloc_strdup(tmp_ctx, object_path);
634 0 : if (lookup_path == NULL) {
635 0 : goto done;
636 : }
637 :
638 0 : while (lookup_path != NULL) {
639 0 : key.type = HASH_KEY_STRING;
640 0 : key.str = lookup_path;
641 :
642 0 : hret = hash_lookup(table, &key, &value);
643 0 : if (hret == HASH_SUCCESS) {
644 0 : list = talloc_get_type(value.ptr, struct sbus_interface_list);
645 0 : iface = sbus_iface_list_lookup(list, iface_name);
646 0 : if (iface != NULL) {
647 0 : goto done;
648 : }
649 0 : } else if (hret != HASH_ERROR_KEY_NOT_FOUND) {
650 0 : DEBUG(SSSDBG_OP_FAILURE,
651 : "Unable to search hash table: hret=%d\n", hret);
652 0 : iface = NULL;
653 0 : goto done;
654 : }
655 :
656 : /* we will not free lookup path since it is freed with tmp_ctx
657 : * and the object paths are supposed to be small */
658 0 : lookup_path = sbus_opath_parent_subtree(tmp_ctx, lookup_path);
659 : }
660 :
661 : done:
662 0 : talloc_free(tmp_ctx);
663 0 : return iface;
664 : }
665 :
666 : /**
667 : * Acquire list of all interfaces that are supported on given object path.
668 : */
669 : errno_t
670 0 : sbus_opath_hash_lookup_supported(TALLOC_CTX *mem_ctx,
671 : hash_table_t *table,
672 : const char *object_path,
673 : struct sbus_interface_list **_list)
674 : {
675 0 : TALLOC_CTX *tmp_ctx = NULL;
676 0 : TALLOC_CTX *list_ctx = NULL;
677 0 : struct sbus_interface_list *copy = NULL;
678 0 : struct sbus_interface_list *list = NULL;
679 0 : char *lookup_path = NULL;
680 : hash_key_t key;
681 : hash_value_t value;
682 : errno_t ret;
683 : int hret;
684 :
685 0 : tmp_ctx = talloc_new(NULL);
686 0 : if (tmp_ctx == NULL) {
687 0 : return ENOMEM;
688 : }
689 :
690 0 : list_ctx = talloc_new(tmp_ctx);
691 0 : if (list_ctx == NULL) {
692 0 : ret = ENOMEM;
693 0 : goto done;
694 : }
695 :
696 0 : lookup_path = talloc_strdup(tmp_ctx, object_path);
697 0 : if (lookup_path == NULL) {
698 0 : ret = ENOMEM;
699 0 : goto done;
700 : }
701 :
702 0 : while (lookup_path != NULL) {
703 0 : key.type = HASH_KEY_STRING;
704 0 : key.str = lookup_path;
705 :
706 0 : hret = hash_lookup(table, &key, &value);
707 0 : if (hret == HASH_SUCCESS) {
708 0 : ret = sbus_iface_list_copy(list_ctx, value.ptr, ©);
709 0 : if (ret != EOK) {
710 0 : goto done;
711 : }
712 :
713 0 : DLIST_CONCATENATE(list, copy, struct sbus_interface_list *);
714 0 : } else if (hret != HASH_ERROR_KEY_NOT_FOUND) {
715 0 : DEBUG(SSSDBG_OP_FAILURE,
716 : "Unable to search hash table: hret=%d\n", hret);
717 0 : ret = EIO;
718 0 : goto done;
719 : }
720 :
721 : /* we will not free lookup path since it is freed with tmp_ctx
722 : * and the object paths are supposed to be small */
723 0 : lookup_path = sbus_opath_parent_subtree(tmp_ctx, lookup_path);
724 : }
725 :
726 0 : talloc_steal(mem_ctx, list_ctx);
727 0 : *_list = list;
728 0 : ret = EOK;
729 :
730 : done:
731 0 : talloc_free(tmp_ctx);
732 0 : return ret;
733 : }
734 :
735 : errno_t
736 0 : sbus_nodes_hash_init(TALLOC_CTX *mem_ctx,
737 : struct sbus_connection *conn,
738 : hash_table_t **_table)
739 : {
740 0 : return sss_hash_create_ex(mem_ctx, 10, _table, 0, 0, 0, 0,
741 : NULL, conn);
742 : }
743 :
744 : struct sbus_nodes_data {
745 : sbus_nodes_fn nodes_fn;
746 : void *handler_data;
747 : };
748 :
749 : static errno_t
750 0 : sbus_nodes_hash_add(hash_table_t *table,
751 : const char *object_path,
752 : sbus_nodes_fn nodes_fn,
753 : void *handler_data)
754 : {
755 : TALLOC_CTX *tmp_ctx;
756 : struct sbus_nodes_data *data;
757 : hash_key_t key;
758 : hash_value_t value;
759 : errno_t ret;
760 : bool has_key;
761 : int hret;
762 :
763 0 : tmp_ctx = talloc_new(NULL);
764 0 : if (tmp_ctx == NULL) {
765 0 : return ENOMEM;
766 : }
767 :
768 0 : key.type = HASH_KEY_STRING;
769 0 : key.str = talloc_strdup(tmp_ctx, object_path);
770 0 : if (key.str == NULL) {
771 0 : return ENOMEM;
772 : }
773 :
774 0 : has_key = hash_has_key(table, &key);
775 0 : if (has_key) {
776 0 : ret = EEXIST;
777 0 : goto done;
778 : }
779 :
780 0 : data = talloc_zero(tmp_ctx, struct sbus_nodes_data);
781 0 : if (data == NULL) {
782 0 : ret = ENOMEM;
783 0 : goto done;
784 : }
785 :
786 0 : data->handler_data = handler_data;
787 0 : data->nodes_fn = nodes_fn;
788 :
789 0 : value.type = HASH_VALUE_PTR;
790 0 : value.ptr = data;
791 :
792 0 : hret = hash_enter(table, &key, &value);
793 0 : if (hret != HASH_SUCCESS) {
794 0 : ret = EIO;
795 0 : goto done;
796 : }
797 :
798 0 : talloc_steal(table, key.str);
799 0 : talloc_steal(table, data);
800 :
801 0 : ret = EOK;
802 :
803 : done:
804 0 : talloc_free(tmp_ctx);
805 0 : return ret;
806 : }
807 :
808 : const char **
809 0 : sbus_nodes_hash_lookup(TALLOC_CTX *mem_ctx,
810 : hash_table_t *table,
811 : const char *object_path)
812 : {
813 : struct sbus_nodes_data *data;
814 : hash_key_t key;
815 : hash_value_t value;
816 : int hret;
817 :
818 0 : key.type = HASH_KEY_STRING;
819 0 : key.str = discard_const(object_path);
820 :
821 0 : hret = hash_lookup(table, &key, &value);
822 0 : if (hret == HASH_ERROR_KEY_NOT_FOUND) {
823 0 : return NULL;
824 0 : } else if (hret != HASH_SUCCESS) {
825 0 : DEBUG(SSSDBG_OP_FAILURE,
826 : "Unable to search hash table: hret=%d\n", hret);
827 0 : return NULL;
828 : }
829 :
830 0 : data = talloc_get_type(value.ptr, struct sbus_nodes_data);
831 :
832 0 : return data->nodes_fn(mem_ctx, object_path, data->handler_data);
833 : }
834 :
835 : static struct sbus_interface *
836 0 : sbus_new_interface(TALLOC_CTX *mem_ctx,
837 : const char *object_path,
838 : struct sbus_vtable *iface_vtable,
839 : void *handler_data)
840 : {
841 : struct sbus_interface *intf;
842 :
843 0 : intf = talloc_zero(mem_ctx, struct sbus_interface);
844 0 : if (intf == NULL) {
845 0 : DEBUG(SSSDBG_FATAL_FAILURE, "Cannot allocate a new sbus_interface.\n");
846 0 : return NULL;
847 : }
848 :
849 0 : intf->path = talloc_strdup(intf, object_path);
850 0 : if (intf->path == NULL) {
851 0 : DEBUG(SSSDBG_FATAL_FAILURE, "Cannot duplicate object path.\n");
852 0 : talloc_free(intf);
853 0 : return NULL;
854 : }
855 :
856 0 : intf->vtable = iface_vtable;
857 0 : intf->handler_data = handler_data;
858 0 : return intf;
859 : }
860 :
861 : static DBusHandlerResult
862 : sbus_message_handler(DBusConnection *dbus_conn,
863 : DBusMessage *message,
864 : void *user_data);
865 :
866 : static errno_t
867 0 : sbus_conn_register_path(struct sbus_connection *conn,
868 : const char *path)
869 : {
870 : static DBusObjectPathVTable vtable = {NULL, sbus_message_handler,
871 : NULL, NULL, NULL, NULL};
872 : DBusError error;
873 0 : char *reg_path = NULL;
874 : dbus_bool_t dbret;
875 :
876 0 : DEBUG(SSSDBG_TRACE_FUNC, "Registering object path %s with D-Bus "
877 : "connection\n", path);
878 :
879 0 : if (sbus_opath_is_subtree(path)) {
880 0 : reg_path = sbus_opath_get_base_path(conn, path);
881 0 : if (reg_path == NULL) {
882 0 : return ENOMEM;
883 : }
884 :
885 : /* D-Bus does not allow to have both object path and fallback
886 : * registered. Since we handle the real message handlers ourselves
887 : * we will register fallback only in this case. */
888 0 : if (sbus_opath_hash_has_path(conn->managed_paths, reg_path)) {
889 0 : dbus_connection_unregister_object_path(conn->dbus.conn, reg_path);
890 : }
891 :
892 0 : dbret = dbus_connection_register_fallback(conn->dbus.conn, reg_path,
893 : &vtable, conn);
894 0 : talloc_free(reg_path);
895 : } else {
896 0 : dbus_error_init(&error);
897 :
898 0 : dbret = dbus_connection_try_register_object_path(conn->dbus.conn, path,
899 : &vtable, conn, &error);
900 :
901 0 : if (dbus_error_is_set(&error) &&
902 0 : strcmp(error.name, DBUS_ERROR_OBJECT_PATH_IN_USE) == 0) {
903 : /* A fallback is probably already registered. Just return. */
904 0 : dbus_error_free(&error);
905 0 : return EOK;
906 : }
907 : }
908 :
909 0 : if (!dbret) {
910 0 : DEBUG(SSSDBG_FATAL_FAILURE, "Unable to register object path "
911 : "%s with D-Bus connection.\n", path);
912 0 : return ENOMEM;
913 : }
914 :
915 0 : return EOK;
916 : }
917 :
918 : errno_t
919 0 : sbus_conn_register_iface(struct sbus_connection *conn,
920 : struct sbus_vtable *iface_vtable,
921 : const char *object_path,
922 : void *handler_data)
923 : {
924 0 : struct sbus_interface *iface = NULL;
925 : bool path_known;
926 : errno_t ret;
927 :
928 0 : if (conn == NULL || iface_vtable == NULL || object_path == NULL) {
929 0 : return EINVAL;
930 : }
931 :
932 0 : iface = sbus_new_interface(conn, object_path, iface_vtable, handler_data);
933 0 : if (iface == NULL) {
934 0 : return ENOMEM;
935 : }
936 :
937 0 : ret = sbus_opath_hash_add_iface(conn->managed_paths, object_path, iface,
938 : &path_known);
939 0 : if (ret != EOK) {
940 0 : talloc_free(iface);
941 0 : return ret;
942 : }
943 :
944 0 : if (path_known) {
945 : /* this object path is already registered */
946 0 : return EOK;
947 : }
948 :
949 : /* if ret != EOK we will still leave iface in the table, since
950 : * we probably don't have enough memory to remove it correctly anyway */
951 :
952 0 : ret = sbus_conn_register_path(conn, object_path);
953 0 : if (ret != EOK) {
954 0 : return ret;
955 : }
956 :
957 : /* register standard interfaces with this object path as well */
958 0 : ret = sbus_conn_register_iface(conn, sbus_properties_vtable(),
959 : object_path, conn);
960 0 : if (ret != EOK) {
961 0 : return ret;
962 : }
963 :
964 0 : ret = sbus_conn_register_iface(conn, sbus_introspect_vtable(),
965 : object_path, conn);
966 0 : if (ret != EOK) {
967 0 : return ret;
968 : }
969 :
970 0 : return ret;
971 : }
972 :
973 : errno_t
974 0 : sbus_conn_register_iface_map(struct sbus_connection *conn,
975 : struct sbus_iface_map *map,
976 : void *pvt)
977 : {
978 : errno_t ret;
979 : int i;
980 :
981 0 : for (i = 0; map[i].path != NULL; i++) {
982 0 : ret = sbus_conn_register_iface(conn, map[i].vtable, map[i].path, pvt);
983 0 : if (ret != EOK) {
984 0 : return ret;
985 : }
986 : }
987 :
988 0 : return EOK;
989 : }
990 :
991 : void
992 0 : sbus_conn_register_nodes(struct sbus_connection *conn,
993 : const char *path,
994 : sbus_nodes_fn nodes_fn,
995 : void *data)
996 : {
997 : errno_t ret;
998 :
999 0 : ret = sbus_nodes_hash_add(conn->nodes_fns, path, nodes_fn, data);
1000 0 : if (ret != EOK && ret != EEXIST) {
1001 0 : DEBUG(SSSDBG_MINOR_FAILURE, "Unable to register node function with "
1002 : "%s. Introspection may not work correctly.\n", path);
1003 : }
1004 0 : }
1005 :
1006 : errno_t
1007 0 : sbus_conn_reregister_paths(struct sbus_connection *conn)
1008 : {
1009 0 : hash_key_t *keys = NULL;
1010 : unsigned long count;
1011 : unsigned long i;
1012 : errno_t ret;
1013 : int hret;
1014 :
1015 0 : hret = hash_keys(conn->managed_paths, &count, &keys);
1016 0 : if (hret != HASH_SUCCESS) {
1017 0 : ret = ENOMEM;
1018 0 : goto done;
1019 : }
1020 :
1021 0 : for (i = 0; i < count; i++) {
1022 0 : ret = sbus_conn_register_path(conn, keys[i].str);
1023 0 : if (ret != EOK) {
1024 0 : goto done;
1025 : }
1026 : }
1027 :
1028 0 : ret = EOK;
1029 :
1030 : done:
1031 0 : talloc_free(keys);
1032 0 : return ret;
1033 : }
1034 :
1035 : static void
1036 : sbus_message_handler_got_caller_id(struct tevent_req *req);
1037 :
1038 : static DBusHandlerResult
1039 0 : sbus_message_handler(DBusConnection *dbus_conn,
1040 : DBusMessage *message,
1041 : void *handler_data)
1042 : {
1043 : struct tevent_req *req;
1044 : struct sbus_connection *conn;
1045 : struct sbus_interface *iface;
1046 : struct sbus_request *sbus_req;
1047 : const struct sbus_method_meta *method;
1048 : const char *iface_name;
1049 : const char *method_name;
1050 : const char *path;
1051 : const char *sender;
1052 :
1053 0 : conn = talloc_get_type(handler_data, struct sbus_connection);
1054 :
1055 : /* header information */
1056 0 : iface_name = dbus_message_get_interface(message);
1057 0 : method_name = dbus_message_get_member(message);
1058 0 : path = dbus_message_get_path(message);
1059 0 : sender = dbus_message_get_sender(message);
1060 :
1061 0 : DEBUG(SSSDBG_TRACE_INTERNAL, "Received SBUS method %s.%s on path %s\n",
1062 : iface_name, method_name, path);
1063 :
1064 : /* try to find the interface */
1065 0 : iface = sbus_opath_hash_lookup_iface(conn->managed_paths,
1066 : path, iface_name);
1067 0 : if (iface == NULL) {
1068 0 : goto fail;
1069 : }
1070 :
1071 0 : method = sbus_meta_find_method(iface->vtable->meta, method_name);
1072 0 : if (method == NULL || method->vtable_offset == 0) {
1073 : goto fail;
1074 : }
1075 :
1076 : /* we have a valid handler, create D-Bus request */
1077 0 : sbus_req = sbus_new_request(conn, iface, message);
1078 0 : if (sbus_req == NULL) {
1079 0 : return DBUS_HANDLER_RESULT_NEED_MEMORY;
1080 : }
1081 :
1082 0 : sbus_req->method = method;
1083 :
1084 : /* now get the sender ID */
1085 0 : req = sbus_get_sender_id_send(sbus_req, conn->ev, conn, sender);
1086 0 : if (req == NULL) {
1087 0 : talloc_free(sbus_req);
1088 0 : return DBUS_HANDLER_RESULT_NEED_MEMORY;
1089 : }
1090 0 : tevent_req_set_callback(req, sbus_message_handler_got_caller_id, sbus_req);
1091 :
1092 0 : return DBUS_HANDLER_RESULT_HANDLED;
1093 :
1094 : fail: ;
1095 : DBusMessage *reply;
1096 :
1097 0 : DEBUG(SSSDBG_CRIT_FAILURE, "No matching handler found for method %s.%s "
1098 : "on path %s\n", iface_name, method_name, path);
1099 :
1100 0 : reply = dbus_message_new_error(message, DBUS_ERROR_UNKNOWN_METHOD, NULL);
1101 0 : sbus_conn_send_reply(conn, reply);
1102 :
1103 0 : return DBUS_HANDLER_RESULT_HANDLED;
1104 : }
1105 :
1106 : static void
1107 0 : sbus_message_handler_got_caller_id(struct tevent_req *req)
1108 : {
1109 : struct sbus_request *sbus_req;
1110 : const struct sbus_method_meta *method;
1111 : sbus_msg_handler_fn handler;
1112 : sbus_method_invoker_fn invoker;
1113 : void *pvt;
1114 : DBusError *error;
1115 : errno_t ret;
1116 :
1117 0 : sbus_req = tevent_req_callback_data(req, struct sbus_request);
1118 0 : method = sbus_req->method;
1119 :
1120 0 : ret = sbus_get_sender_id_recv(req, &sbus_req->client);
1121 0 : if (ret != EOK) {
1122 0 : error = sbus_error_new(sbus_req, DBUS_ERROR_FAILED, "Failed to "
1123 : "resolve caller's ID: %s\n", sss_strerror(ret));
1124 0 : sbus_request_fail_and_finish(sbus_req, error);
1125 0 : return;
1126 : }
1127 :
1128 0 : handler = VTABLE_FUNC(sbus_req->intf->vtable, method->vtable_offset);
1129 0 : invoker = method->invoker;
1130 0 : pvt = sbus_req->intf->handler_data;
1131 :
1132 0 : sbus_request_invoke_or_finish(sbus_req, handler, pvt, invoker);
1133 0 : return;
1134 : }
|