Line data Source code
1 : /*
2 : SSSD
3 :
4 : Authors:
5 : Stephen Gallagher <sgallagh@redhat.com>
6 :
7 : Copyright (C) 2012 Red Hat
8 :
9 : This program is free software; you can redistribute it and/or modify
10 : it under the terms of the GNU General Public License as published by
11 : the Free Software Foundation; either version 3 of the License, or
12 : (at your option) any later version.
13 :
14 : This program is distributed in the hope that it will be useful,
15 : but WITHOUT ANY WARRANTY; without even the implied warranty of
16 : MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
17 : GNU General Public License for more details.
18 :
19 : You should have received a copy of the GNU General Public License
20 : along with this program. If not, see <http://www.gnu.org/licenses/>.
21 : */
22 :
23 : #include "util/util.h"
24 : #include "db/sysdb.h"
25 : #include "db/sysdb_services.h"
26 : #include "providers/ldap/sdap_async_private.h"
27 : #include "providers/ldap/ldap_common.h"
28 :
29 : struct sdap_get_services_state {
30 : struct tevent_context *ev;
31 : struct sdap_options *opts;
32 : struct sdap_handle *sh;
33 : struct sss_domain_info *dom;
34 : struct sysdb_ctx *sysdb;
35 : const char **attrs;
36 : const char *base_filter;
37 : char *filter;
38 : int timeout;
39 : bool enumeration;
40 :
41 : char *higher_usn;
42 : struct sysdb_attrs **services;
43 : size_t count;
44 :
45 : size_t base_iter;
46 : struct sdap_search_base **search_bases;
47 : };
48 :
49 : static errno_t
50 : sdap_get_services_next_base(struct tevent_req *req);
51 : static void
52 : sdap_get_services_process(struct tevent_req *subreq);
53 : static errno_t
54 : sdap_save_services(TALLOC_CTX *memctx,
55 : struct sysdb_ctx *sysdb,
56 : struct sss_domain_info *dom,
57 : struct sdap_options *opts,
58 : struct sysdb_attrs **services,
59 : size_t num_services,
60 : char **_usn_value);
61 : static errno_t
62 : sdap_save_service(TALLOC_CTX *mem_ctx,
63 : struct sysdb_ctx *sysdb,
64 : struct sdap_options *opts,
65 : struct sss_domain_info *dom,
66 : struct sysdb_attrs *attrs,
67 : char **_usn_value,
68 : time_t now);
69 :
70 : struct tevent_req *
71 0 : sdap_get_services_send(TALLOC_CTX *memctx,
72 : struct tevent_context *ev,
73 : struct sss_domain_info *dom,
74 : struct sysdb_ctx *sysdb,
75 : struct sdap_options *opts,
76 : struct sdap_search_base **search_bases,
77 : struct sdap_handle *sh,
78 : const char **attrs,
79 : const char *filter,
80 : int timeout,
81 : bool enumeration)
82 : {
83 : errno_t ret;
84 : struct tevent_req *req;
85 : struct sdap_get_services_state *state;
86 :
87 0 : req = tevent_req_create(memctx, &state, struct sdap_get_services_state);
88 0 : if (!req) return NULL;
89 :
90 0 : state->ev = ev;
91 0 : state->opts = opts;
92 0 : state->dom = dom;
93 0 : state->sh = sh;
94 0 : state->sysdb = sysdb;
95 0 : state->attrs = attrs;
96 0 : state->higher_usn = NULL;
97 0 : state->services = NULL;
98 0 : state->count = 0;
99 0 : state->timeout = timeout;
100 0 : state->base_filter = filter;
101 0 : state->base_iter = 0;
102 0 : state->search_bases = search_bases;
103 0 : state->enumeration = enumeration;
104 :
105 0 : if (!state->search_bases) {
106 0 : DEBUG(SSSDBG_CRIT_FAILURE,
107 : "Services lookup request without a search base\n");
108 0 : ret = EINVAL;
109 0 : goto done;
110 : }
111 :
112 0 : ret = sdap_get_services_next_base(req);
113 :
114 : done:
115 0 : if (ret != EOK) {
116 0 : tevent_req_error(req, ret);
117 0 : tevent_req_post(req, state->ev);
118 : }
119 :
120 0 : return req;
121 : }
122 :
123 : static errno_t
124 0 : sdap_get_services_next_base(struct tevent_req *req)
125 : {
126 : struct tevent_req *subreq;
127 : struct sdap_get_services_state *state;
128 :
129 0 : state = tevent_req_data(req, struct sdap_get_services_state);
130 :
131 0 : talloc_zfree(state->filter);
132 0 : state->filter = sdap_combine_filters(state, state->base_filter,
133 0 : state->search_bases[state->base_iter]->filter);
134 0 : if (!state->filter) {
135 0 : return ENOMEM;
136 : }
137 :
138 0 : DEBUG(SSSDBG_TRACE_FUNC,
139 : "Searching for services with base [%s]\n",
140 : state->search_bases[state->base_iter]->basedn);
141 :
142 0 : subreq = sdap_get_generic_send(
143 : state, state->ev, state->opts, state->sh,
144 0 : state->search_bases[state->base_iter]->basedn,
145 0 : state->search_bases[state->base_iter]->scope,
146 0 : state->filter, state->attrs,
147 0 : state->opts->service_map, SDAP_OPTS_SERVICES,
148 : state->timeout,
149 0 : state->enumeration); /* If we're enumerating, we need paging */
150 0 : if (!subreq) {
151 0 : return ENOMEM;
152 : }
153 0 : tevent_req_set_callback(subreq, sdap_get_services_process, req);
154 :
155 0 : return EOK;
156 : }
157 :
158 : static void
159 0 : sdap_get_services_process(struct tevent_req *subreq)
160 : {
161 0 : struct tevent_req *req =
162 0 : tevent_req_callback_data(subreq, struct tevent_req);
163 0 : struct sdap_get_services_state *state =
164 0 : tevent_req_data(req, struct sdap_get_services_state);
165 : int ret;
166 : size_t count, i;
167 : struct sysdb_attrs **services;
168 0 : bool next_base = false;
169 :
170 0 : ret = sdap_get_generic_recv(subreq, state,
171 : &count, &services);
172 0 : talloc_zfree(subreq);
173 0 : if (ret) {
174 0 : tevent_req_error(req, ret);
175 0 : return;
176 : }
177 :
178 0 : DEBUG(SSSDBG_TRACE_FUNC,
179 : "Search for services, returned %zu results.\n",
180 : count);
181 :
182 0 : if (state->enumeration || count == 0) {
183 : /* No services found in this search or enumerating */
184 0 : next_base = true;
185 : }
186 :
187 : /* Add this batch of sevices to the list */
188 0 : if (count > 0) {
189 0 : state->services =
190 0 : talloc_realloc(state,
191 : state->services,
192 : struct sysdb_attrs *,
193 : state->count + count + 1);
194 0 : if (!state->services) {
195 0 : tevent_req_error(req, ENOMEM);
196 0 : return;
197 : }
198 :
199 : /* Copy the new services into the list
200 : */
201 0 : for (i = 0; i < count; i++) {
202 0 : state->services[state->count + i] =
203 0 : talloc_steal(state->services, services[i]);
204 : }
205 :
206 0 : state->count += count;
207 0 : state->services[state->count] = NULL;
208 : }
209 :
210 0 : if (next_base) {
211 0 : state->base_iter++;
212 0 : if (state->search_bases[state->base_iter]) {
213 : /* There are more search bases to try */
214 0 : ret = sdap_get_services_next_base(req);
215 0 : if (ret != EOK) {
216 0 : tevent_req_error(req, ret);
217 : }
218 0 : return;
219 : }
220 : }
221 :
222 : /* No more search bases
223 : * Return ENOENT if no services were found
224 : */
225 0 : if (state->count == 0) {
226 0 : tevent_req_error(req, ENOENT);
227 0 : return;
228 : }
229 :
230 0 : ret = sdap_save_services(state, state->sysdb,
231 : state->dom, state->opts,
232 : state->services, state->count,
233 : &state->higher_usn);
234 0 : if (ret) {
235 0 : DEBUG(SSSDBG_MINOR_FAILURE,
236 : "Failed to store services.\n");
237 0 : tevent_req_error(req, ret);
238 0 : return;
239 : }
240 :
241 0 : DEBUG(SSSDBG_TRACE_INTERNAL,
242 : "Saving %zu services - Done\n", state->count);
243 :
244 0 : tevent_req_done(req);
245 : }
246 :
247 : static errno_t
248 0 : sdap_save_services(TALLOC_CTX *mem_ctx,
249 : struct sysdb_ctx *sysdb,
250 : struct sss_domain_info *dom,
251 : struct sdap_options *opts,
252 : struct sysdb_attrs **services,
253 : size_t num_services,
254 : char **_usn_value)
255 : {
256 : errno_t ret, sret;
257 : time_t now;
258 : size_t i;
259 0 : bool in_transaction = false;
260 0 : char *higher_usn = NULL;
261 : char *usn_value;
262 : TALLOC_CTX *tmp_ctx;
263 :
264 0 : if (num_services == 0) {
265 : /* Nothing to do */
266 0 : return ENOENT;
267 : }
268 :
269 0 : tmp_ctx = talloc_new(NULL);
270 0 : if (!tmp_ctx) {
271 0 : return ENOMEM;
272 : }
273 :
274 0 : ret = sysdb_transaction_start(sysdb);
275 0 : if (ret != EOK) {
276 0 : DEBUG(SSSDBG_CRIT_FAILURE, "Failed to start transaction\n");
277 0 : goto done;
278 : }
279 :
280 0 : in_transaction = true;
281 :
282 0 : now = time(NULL);
283 0 : for (i = 0; i < num_services; i++) {
284 0 : usn_value = NULL;
285 :
286 0 : ret = sdap_save_service(tmp_ctx, sysdb, opts, dom,
287 0 : services[i],
288 : &usn_value, now);
289 :
290 : /* Do not fail completely on errors.
291 : * Just report the failure to save and go on */
292 0 : if (ret) {
293 0 : DEBUG(SSSDBG_MINOR_FAILURE,
294 : "Failed to store service %zu. Ignoring.\n", i);
295 : } else {
296 0 : DEBUG(SSSDBG_TRACE_INTERNAL,
297 : "Service [%zu/%zu] processed!\n", i, num_services);
298 : }
299 :
300 0 : if (usn_value) {
301 0 : if (higher_usn) {
302 0 : if ((strlen(usn_value) > strlen(higher_usn)) ||
303 0 : (strcmp(usn_value, higher_usn) > 0)) {
304 0 : talloc_zfree(higher_usn);
305 0 : higher_usn = usn_value;
306 : } else {
307 0 : talloc_zfree(usn_value);
308 : }
309 : } else {
310 0 : higher_usn = usn_value;
311 : }
312 : }
313 : }
314 :
315 0 : ret = sysdb_transaction_commit(sysdb);
316 0 : if (ret != EOK) {
317 0 : DEBUG(SSSDBG_CRIT_FAILURE,
318 : "Failed to commit transaction!\n");
319 0 : goto done;
320 : }
321 0 : in_transaction = false;
322 :
323 0 : if (_usn_value) {
324 0 : *_usn_value = talloc_steal(mem_ctx, higher_usn);
325 : }
326 :
327 : done:
328 0 : if (in_transaction) {
329 0 : sret = sysdb_transaction_cancel(sysdb);
330 0 : if (sret != EOK) {
331 0 : DEBUG(SSSDBG_CRIT_FAILURE,
332 : "Failed to cancel transaction!\n");
333 : }
334 : }
335 0 : talloc_free(tmp_ctx);
336 0 : return ret;
337 : }
338 :
339 : static errno_t
340 0 : sdap_save_service(TALLOC_CTX *mem_ctx,
341 : struct sysdb_ctx *sysdb,
342 : struct sdap_options *opts,
343 : struct sss_domain_info *dom,
344 : struct sysdb_attrs *attrs,
345 : char **_usn_value,
346 : time_t now)
347 : {
348 : errno_t ret;
349 0 : TALLOC_CTX *tmp_ctx = NULL;
350 : struct sysdb_attrs *svc_attrs;
351 : struct ldb_message_element *el;
352 0 : char *usn_value = NULL;
353 0 : const char *name = NULL;
354 : const char **aliases;
355 : const char **protocols;
356 : const char **cased_protocols;
357 : const char **store_protocols;
358 : char **missing;
359 : uint16_t port;
360 : uint64_t cache_timeout;
361 :
362 0 : DEBUG(SSSDBG_TRACE_ALL, "Saving service\n");
363 :
364 0 : tmp_ctx = talloc_new(NULL);
365 0 : if (!tmp_ctx) {
366 0 : ret = ENOMEM;
367 0 : goto done;
368 : }
369 :
370 0 : svc_attrs = sysdb_new_attrs(tmp_ctx);
371 0 : if (!svc_attrs) {
372 0 : ret = ENOMEM;
373 0 : goto done;
374 : }
375 :
376 : /* Identify the primary name of this services */
377 0 : ret = sysdb_attrs_primary_name(
378 : sysdb, attrs,
379 0 : opts->service_map[SDAP_AT_SERVICE_NAME].name,
380 : &name);
381 0 : if (ret != EOK) {
382 0 : DEBUG(SSSDBG_MINOR_FAILURE,
383 : "Could not determine the primary name of the service\n");
384 0 : goto done;
385 : }
386 :
387 0 : DEBUG(SSSDBG_TRACE_INTERNAL, "Primary name: [%s]\n", name);
388 :
389 :
390 : /* Handle any available aliases */
391 0 : ret = sysdb_attrs_get_aliases(tmp_ctx, attrs, name,
392 0 : !dom->case_sensitive,
393 0 : &aliases);
394 0 : if (ret != EOK) {
395 0 : DEBUG(SSSDBG_MINOR_FAILURE,
396 : "Failed to identify service aliases\n");
397 0 : goto done;
398 : }
399 :
400 : /* Get the port number */
401 0 : ret = sysdb_attrs_get_uint16_t(attrs, SYSDB_SVC_PORT, &port);
402 0 : if (ret != EOK) {
403 0 : DEBUG(SSSDBG_MINOR_FAILURE,
404 : "Failed to identify service port: [%s]\n",
405 : strerror(ret));
406 0 : goto done;
407 : }
408 :
409 : /* Get the protocols this service offers on that port */
410 0 : ret = sysdb_attrs_get_string_array(attrs, SYSDB_SVC_PROTO,
411 : tmp_ctx, &protocols);
412 0 : if (ret != EOK) {
413 0 : DEBUG(SSSDBG_MINOR_FAILURE,
414 : "Failed to identify service protocols: [%s]\n",
415 : strerror(ret));
416 0 : goto done;
417 : }
418 :
419 0 : if (dom->case_sensitive == false) {
420 : /* Don't perform the extra mallocs if not necessary */
421 0 : ret = sss_get_cased_name_list(tmp_ctx, protocols,
422 0 : dom->case_sensitive, &cased_protocols);
423 0 : if (ret != EOK) {
424 0 : DEBUG(SSSDBG_MINOR_FAILURE,
425 : "Failed to get case_sensitive protocols names: [%s]\n",
426 : strerror(ret));
427 0 : goto done;
428 : }
429 : }
430 :
431 0 : store_protocols = dom->case_sensitive ? protocols : cased_protocols;
432 :
433 : /* Get the USN value, if available */
434 0 : ret = sysdb_attrs_get_el(attrs,
435 0 : opts->service_map[SDAP_AT_SERVICE_USN].sys_name, &el);
436 0 : if (ret && ret != ENOENT) {
437 0 : DEBUG(SSSDBG_MINOR_FAILURE,
438 : "Failed to retrieve USN value: [%s]\n",
439 : strerror(ret));
440 0 : goto done;
441 : }
442 0 : if (ret == ENOENT || el->num_values == 0) {
443 0 : DEBUG(SSSDBG_TRACE_LIBS,
444 : "Original USN value is not available for [%s].\n",
445 : name);
446 : } else {
447 0 : ret = sysdb_attrs_add_string(svc_attrs,
448 0 : opts->service_map[SDAP_AT_SERVICE_USN].sys_name,
449 0 : (const char*)el->values[0].data);
450 0 : if (ret) {
451 0 : DEBUG(SSSDBG_MINOR_FAILURE,
452 : "Failed to add USN value: [%s]\n",
453 : strerror(ret));
454 0 : goto done;
455 : }
456 0 : usn_value = talloc_strdup(tmp_ctx, (const char*)el->values[0].data);
457 0 : if (!usn_value) {
458 0 : ret = ENOMEM;
459 0 : goto done;
460 : }
461 : }
462 :
463 : /* Make sure to remove any extra attributes from the sysdb
464 : * that have been removed from LDAP
465 : */
466 0 : ret = list_missing_attrs(svc_attrs, opts->service_map, SDAP_OPTS_SERVICES,
467 : attrs, &missing);
468 0 : if (ret != EOK) {
469 0 : DEBUG(SSSDBG_MINOR_FAILURE,
470 : "Failed to identify removed attributes: [%s]\n",
471 : strerror(ret));
472 0 : goto done;
473 : }
474 :
475 0 : cache_timeout = dom->service_timeout;
476 :
477 0 : ret = sysdb_store_service(dom, name, port, aliases, store_protocols,
478 : svc_attrs, missing, cache_timeout, now);
479 0 : if (ret != EOK) {
480 0 : DEBUG(SSSDBG_MINOR_FAILURE,
481 : "Failed to store service in the sysdb: [%s]\n",
482 : strerror(ret));
483 0 : goto done;
484 : }
485 :
486 0 : *_usn_value = talloc_steal(mem_ctx, usn_value);
487 :
488 : done:
489 0 : talloc_free(tmp_ctx);
490 0 : return ret;
491 : }
492 :
493 : errno_t
494 0 : sdap_get_services_recv(TALLOC_CTX *mem_ctx,
495 : struct tevent_req *req,
496 : char **usn_value)
497 : {
498 0 : struct sdap_get_services_state *state =
499 0 : tevent_req_data(req, struct sdap_get_services_state);
500 :
501 0 : TEVENT_REQ_RETURN_ON_ERROR(req);
502 :
503 0 : if (usn_value) {
504 0 : *usn_value = talloc_steal(mem_ctx, state->higher_usn);
505 : }
506 :
507 0 : return EOK;
508 : }
509 :
510 :
511 : /* Enumeration routines */
512 :
513 : struct enum_services_state {
514 : struct tevent_context *ev;
515 : struct sdap_id_ctx *id_ctx;
516 : struct sdap_id_op *op;
517 : struct sss_domain_info *domain;
518 : struct sysdb_ctx *sysdb;
519 :
520 : char *filter;
521 : const char **attrs;
522 : };
523 :
524 : static void
525 : enum_services_op_done(struct tevent_req *subreq);
526 :
527 : struct tevent_req *
528 0 : enum_services_send(TALLOC_CTX *memctx,
529 : struct tevent_context *ev,
530 : struct sdap_id_ctx *id_ctx,
531 : struct sdap_id_op *op,
532 : bool purge)
533 : {
534 : errno_t ret;
535 : struct tevent_req *req;
536 : struct tevent_req *subreq;
537 : struct enum_services_state *state;
538 :
539 0 : req = tevent_req_create(memctx, &state, struct enum_services_state);
540 0 : if (!req) return NULL;
541 :
542 0 : state->ev = ev;
543 0 : state->id_ctx = id_ctx;
544 0 : state->domain = id_ctx->be->domain;
545 0 : state->sysdb = id_ctx->be->domain->sysdb;
546 0 : state->op = op;
547 :
548 0 : if (id_ctx->srv_opts && id_ctx->srv_opts->max_service_value && !purge) {
549 0 : state->filter = talloc_asprintf(
550 : state,
551 : "(&(objectclass=%s)(%s=*)(%s=*)(%s=*)(%s>=%s)(!(%s=%s)))",
552 0 : id_ctx->opts->service_map[SDAP_OC_SERVICE].name,
553 0 : id_ctx->opts->service_map[SDAP_AT_SERVICE_NAME].name,
554 0 : id_ctx->opts->service_map[SDAP_AT_SERVICE_PORT].name,
555 0 : id_ctx->opts->service_map[SDAP_AT_SERVICE_PROTOCOL].name,
556 0 : id_ctx->opts->service_map[SDAP_AT_SERVICE_USN].name,
557 0 : id_ctx->srv_opts->max_service_value,
558 0 : id_ctx->opts->service_map[SDAP_AT_SERVICE_USN].name,
559 0 : id_ctx->srv_opts->max_service_value);
560 : } else {
561 0 : state->filter = talloc_asprintf(
562 : state,
563 : "(&(objectclass=%s)(%s=*)(%s=*)(%s=*))",
564 0 : id_ctx->opts->service_map[SDAP_OC_SERVICE].name,
565 0 : id_ctx->opts->service_map[SDAP_AT_SERVICE_NAME].name,
566 0 : id_ctx->opts->service_map[SDAP_AT_SERVICE_PORT].name,
567 0 : id_ctx->opts->service_map[SDAP_AT_SERVICE_PROTOCOL].name);
568 : }
569 0 : if (!state->filter) {
570 0 : DEBUG(SSSDBG_MINOR_FAILURE, "Failed to build base filter\n");
571 0 : ret = ENOMEM;
572 0 : goto fail;
573 : }
574 :
575 : /* TODO: handle attrs_type */
576 0 : ret = build_attrs_from_map(state, id_ctx->opts->service_map,
577 : SDAP_OPTS_SERVICES, NULL,
578 0 : &state->attrs, NULL);
579 0 : if (ret != EOK) goto fail;
580 :
581 0 : subreq = sdap_get_services_send(state, state->ev,
582 0 : state->domain, state->sysdb,
583 0 : state->id_ctx->opts,
584 0 : state->id_ctx->opts->sdom->service_search_bases,
585 0 : sdap_id_op_handle(state->op),
586 0 : state->attrs, state->filter,
587 0 : dp_opt_get_int(state->id_ctx->opts->basic,
588 : SDAP_SEARCH_TIMEOUT),
589 : true);
590 0 : if (!subreq) {
591 0 : ret = ENOMEM;
592 0 : goto fail;
593 : }
594 0 : tevent_req_set_callback(subreq, enum_services_op_done, req);
595 :
596 0 : return req;
597 :
598 : fail:
599 0 : tevent_req_error(req, ret);
600 0 : tevent_req_post(req, ev);
601 0 : return req;
602 : }
603 :
604 : static void
605 0 : enum_services_op_done(struct tevent_req *subreq)
606 : {
607 0 : struct tevent_req *req =
608 0 : tevent_req_callback_data(subreq, struct tevent_req);
609 0 : struct enum_services_state *state =
610 0 : tevent_req_data(req, struct enum_services_state);
611 : char *usn_value;
612 0 : char *endptr = NULL;
613 : unsigned usn_number;
614 : int ret;
615 :
616 0 : ret = sdap_get_services_recv(state, subreq, &usn_value);
617 0 : talloc_zfree(subreq);
618 0 : if (ret != EOK) {
619 0 : tevent_req_error(req, ret);
620 0 : return;
621 : }
622 :
623 0 : if (usn_value) {
624 0 : talloc_zfree(state->id_ctx->srv_opts->max_service_value);
625 0 : state->id_ctx->srv_opts->max_service_value =
626 0 : talloc_steal(state->id_ctx, usn_value);
627 :
628 0 : usn_number = strtoul(usn_value, &endptr, 10);
629 0 : if ((endptr == NULL || (*endptr == '\0' && endptr != usn_value))
630 0 : && (usn_number > state->id_ctx->srv_opts->last_usn)) {
631 0 : state->id_ctx->srv_opts->last_usn = usn_number;
632 : }
633 : }
634 :
635 0 : DEBUG(SSSDBG_FUNC_DATA, "Services higher USN value: [%s]\n",
636 : state->id_ctx->srv_opts->max_service_value);
637 :
638 0 : tevent_req_done(req);
639 : }
640 :
641 : errno_t
642 0 : enum_services_recv(struct tevent_req *req)
643 : {
644 0 : TEVENT_REQ_RETURN_ON_ERROR(req);
645 :
646 0 : return EOK;
647 : }
|