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_get_id_specific_filter(state,
133 : state->base_filter,
134 0 : state->search_bases[state->base_iter]->filter);
135 0 : if (!state->filter) {
136 0 : return ENOMEM;
137 : }
138 :
139 0 : DEBUG(SSSDBG_TRACE_FUNC,
140 : "Searching for services with base [%s]\n",
141 : state->search_bases[state->base_iter]->basedn);
142 :
143 0 : subreq = sdap_get_generic_send(
144 : state, state->ev, state->opts, state->sh,
145 0 : state->search_bases[state->base_iter]->basedn,
146 0 : state->search_bases[state->base_iter]->scope,
147 0 : state->filter, state->attrs,
148 0 : state->opts->service_map, SDAP_OPTS_SERVICES,
149 : state->timeout,
150 0 : state->enumeration); /* If we're enumerating, we need paging */
151 0 : if (!subreq) {
152 0 : return ENOMEM;
153 : }
154 0 : tevent_req_set_callback(subreq, sdap_get_services_process, req);
155 :
156 0 : return EOK;
157 : }
158 :
159 : static void
160 0 : sdap_get_services_process(struct tevent_req *subreq)
161 : {
162 0 : struct tevent_req *req =
163 0 : tevent_req_callback_data(subreq, struct tevent_req);
164 0 : struct sdap_get_services_state *state =
165 0 : tevent_req_data(req, struct sdap_get_services_state);
166 : int ret;
167 : size_t count, i;
168 : struct sysdb_attrs **services;
169 0 : bool next_base = false;
170 :
171 0 : ret = sdap_get_generic_recv(subreq, state,
172 : &count, &services);
173 0 : talloc_zfree(subreq);
174 0 : if (ret) {
175 0 : tevent_req_error(req, ret);
176 0 : return;
177 : }
178 :
179 0 : DEBUG(SSSDBG_TRACE_FUNC,
180 : "Search for services, returned %zu results.\n",
181 : count);
182 :
183 0 : if (state->enumeration || count == 0) {
184 : /* No services found in this search or enumerating */
185 0 : next_base = true;
186 : }
187 :
188 : /* Add this batch of sevices to the list */
189 0 : if (count > 0) {
190 0 : state->services =
191 0 : talloc_realloc(state,
192 : state->services,
193 : struct sysdb_attrs *,
194 : state->count + count + 1);
195 0 : if (!state->services) {
196 0 : tevent_req_error(req, ENOMEM);
197 0 : return;
198 : }
199 :
200 : /* Copy the new services into the list
201 : */
202 0 : for (i = 0; i < count; i++) {
203 0 : state->services[state->count + i] =
204 0 : talloc_steal(state->services, services[i]);
205 : }
206 :
207 0 : state->count += count;
208 0 : state->services[state->count] = NULL;
209 : }
210 :
211 0 : if (next_base) {
212 0 : state->base_iter++;
213 0 : if (state->search_bases[state->base_iter]) {
214 : /* There are more search bases to try */
215 0 : ret = sdap_get_services_next_base(req);
216 0 : if (ret != EOK) {
217 0 : tevent_req_error(req, ret);
218 : }
219 0 : return;
220 : }
221 : }
222 :
223 : /* No more search bases
224 : * Return ENOENT if no services were found
225 : */
226 0 : if (state->count == 0) {
227 0 : tevent_req_error(req, ENOENT);
228 0 : return;
229 : }
230 :
231 0 : ret = sdap_save_services(state, state->sysdb,
232 : state->dom, state->opts,
233 : state->services, state->count,
234 : &state->higher_usn);
235 0 : if (ret) {
236 0 : DEBUG(SSSDBG_MINOR_FAILURE,
237 : "Failed to store services.\n");
238 0 : tevent_req_error(req, ret);
239 0 : return;
240 : }
241 :
242 0 : DEBUG(SSSDBG_TRACE_INTERNAL,
243 : "Saving %zu services - Done\n", state->count);
244 :
245 0 : tevent_req_done(req);
246 : }
247 :
248 : static errno_t
249 0 : sdap_save_services(TALLOC_CTX *mem_ctx,
250 : struct sysdb_ctx *sysdb,
251 : struct sss_domain_info *dom,
252 : struct sdap_options *opts,
253 : struct sysdb_attrs **services,
254 : size_t num_services,
255 : char **_usn_value)
256 : {
257 : errno_t ret, sret;
258 : time_t now;
259 : size_t i;
260 0 : bool in_transaction = false;
261 0 : char *higher_usn = NULL;
262 : char *usn_value;
263 : TALLOC_CTX *tmp_ctx;
264 :
265 0 : if (num_services == 0) {
266 : /* Nothing to do */
267 0 : return ENOENT;
268 : }
269 :
270 0 : tmp_ctx = talloc_new(NULL);
271 0 : if (!tmp_ctx) {
272 0 : return ENOMEM;
273 : }
274 :
275 0 : ret = sysdb_transaction_start(sysdb);
276 0 : if (ret != EOK) {
277 0 : DEBUG(SSSDBG_CRIT_FAILURE, "Failed to start transaction\n");
278 0 : goto done;
279 : }
280 :
281 0 : in_transaction = true;
282 :
283 0 : now = time(NULL);
284 0 : for (i = 0; i < num_services; i++) {
285 0 : usn_value = NULL;
286 :
287 0 : ret = sdap_save_service(tmp_ctx, sysdb, opts, dom,
288 0 : services[i],
289 : &usn_value, now);
290 :
291 : /* Do not fail completely on errors.
292 : * Just report the failure to save and go on */
293 0 : if (ret) {
294 0 : DEBUG(SSSDBG_MINOR_FAILURE,
295 : "Failed to store service %zu. Ignoring.\n", i);
296 : } else {
297 0 : DEBUG(SSSDBG_TRACE_INTERNAL,
298 : "Service [%zu/%zu] processed!\n", i, num_services);
299 : }
300 :
301 0 : if (usn_value) {
302 0 : if (higher_usn) {
303 0 : if ((strlen(usn_value) > strlen(higher_usn)) ||
304 0 : (strcmp(usn_value, higher_usn) > 0)) {
305 0 : talloc_zfree(higher_usn);
306 0 : higher_usn = usn_value;
307 : } else {
308 0 : talloc_zfree(usn_value);
309 : }
310 : } else {
311 0 : higher_usn = usn_value;
312 : }
313 : }
314 : }
315 :
316 0 : ret = sysdb_transaction_commit(sysdb);
317 0 : if (ret != EOK) {
318 0 : DEBUG(SSSDBG_CRIT_FAILURE,
319 : "Failed to commit transaction!\n");
320 0 : goto done;
321 : }
322 0 : in_transaction = false;
323 :
324 0 : if (_usn_value) {
325 0 : *_usn_value = talloc_steal(mem_ctx, higher_usn);
326 : }
327 :
328 : done:
329 0 : if (in_transaction) {
330 0 : sret = sysdb_transaction_cancel(sysdb);
331 0 : if (sret != EOK) {
332 0 : DEBUG(SSSDBG_CRIT_FAILURE,
333 : "Failed to cancel transaction!\n");
334 : }
335 : }
336 0 : talloc_free(tmp_ctx);
337 0 : return ret;
338 : }
339 :
340 : static errno_t
341 0 : sdap_save_service(TALLOC_CTX *mem_ctx,
342 : struct sysdb_ctx *sysdb,
343 : struct sdap_options *opts,
344 : struct sss_domain_info *dom,
345 : struct sysdb_attrs *attrs,
346 : char **_usn_value,
347 : time_t now)
348 : {
349 : errno_t ret;
350 0 : TALLOC_CTX *tmp_ctx = NULL;
351 : struct sysdb_attrs *svc_attrs;
352 : struct ldb_message_element *el;
353 0 : char *usn_value = NULL;
354 0 : const char *name = NULL;
355 : const char **aliases;
356 : const char **protocols;
357 : const char **cased_protocols;
358 : const char **store_protocols;
359 : char **missing;
360 : uint16_t port;
361 : uint64_t cache_timeout;
362 :
363 0 : DEBUG(SSSDBG_TRACE_ALL, "Saving service\n");
364 :
365 0 : tmp_ctx = talloc_new(NULL);
366 0 : if (!tmp_ctx) {
367 0 : ret = ENOMEM;
368 0 : goto done;
369 : }
370 :
371 0 : svc_attrs = sysdb_new_attrs(tmp_ctx);
372 0 : if (!svc_attrs) {
373 0 : ret = ENOMEM;
374 0 : goto done;
375 : }
376 :
377 : /* Identify the primary name of this services */
378 0 : ret = sysdb_attrs_primary_name(
379 : sysdb, attrs,
380 0 : opts->service_map[SDAP_AT_SERVICE_NAME].name,
381 : &name);
382 0 : if (ret != EOK) {
383 0 : DEBUG(SSSDBG_MINOR_FAILURE,
384 : "Could not determine the primary name of the service\n");
385 0 : goto done;
386 : }
387 :
388 0 : DEBUG(SSSDBG_TRACE_INTERNAL, "Primary name: [%s]\n", name);
389 :
390 :
391 : /* Handle any available aliases */
392 0 : ret = sysdb_attrs_get_aliases(tmp_ctx, attrs, name,
393 0 : !dom->case_sensitive,
394 : &aliases);
395 0 : if (ret != EOK) {
396 0 : DEBUG(SSSDBG_MINOR_FAILURE,
397 : "Failed to identify service aliases\n");
398 0 : goto done;
399 : }
400 :
401 : /* Get the port number */
402 0 : ret = sysdb_attrs_get_uint16_t(attrs, SYSDB_SVC_PORT, &port);
403 0 : if (ret != EOK) {
404 0 : DEBUG(SSSDBG_MINOR_FAILURE,
405 : "Failed to identify service port: [%s]\n",
406 : strerror(ret));
407 0 : goto done;
408 : }
409 :
410 : /* Get the protocols this service offers on that port */
411 0 : ret = sysdb_attrs_get_string_array(attrs, SYSDB_SVC_PROTO,
412 : tmp_ctx, &protocols);
413 0 : if (ret != EOK) {
414 0 : DEBUG(SSSDBG_MINOR_FAILURE,
415 : "Failed to identify service protocols: [%s]\n",
416 : strerror(ret));
417 0 : goto done;
418 : }
419 :
420 0 : if (dom->case_sensitive == false) {
421 : /* Don't perform the extra mallocs if not necessary */
422 0 : ret = sss_get_cased_name_list(tmp_ctx, protocols,
423 0 : dom->case_sensitive, &cased_protocols);
424 0 : if (ret != EOK) {
425 0 : DEBUG(SSSDBG_MINOR_FAILURE,
426 : "Failed to get case_sensitive protocols names: [%s]\n",
427 : strerror(ret));
428 0 : goto done;
429 : }
430 : }
431 :
432 0 : store_protocols = dom->case_sensitive ? protocols : cased_protocols;
433 :
434 : /* Get the USN value, if available */
435 0 : ret = sysdb_attrs_get_el(attrs,
436 0 : opts->service_map[SDAP_AT_SERVICE_USN].sys_name, &el);
437 0 : if (ret && ret != ENOENT) {
438 0 : DEBUG(SSSDBG_MINOR_FAILURE,
439 : "Failed to retrieve USN value: [%s]\n",
440 : strerror(ret));
441 0 : goto done;
442 : }
443 0 : if (ret == ENOENT || el->num_values == 0) {
444 0 : DEBUG(SSSDBG_TRACE_LIBS,
445 : "Original USN value is not available for [%s].\n",
446 : name);
447 : } else {
448 0 : ret = sysdb_attrs_add_string(svc_attrs,
449 0 : opts->service_map[SDAP_AT_SERVICE_USN].sys_name,
450 0 : (const char*)el->values[0].data);
451 0 : if (ret) {
452 0 : DEBUG(SSSDBG_MINOR_FAILURE,
453 : "Failed to add USN value: [%s]\n",
454 : strerror(ret));
455 0 : goto done;
456 : }
457 0 : usn_value = talloc_strdup(tmp_ctx, (const char*)el->values[0].data);
458 0 : if (!usn_value) {
459 0 : ret = ENOMEM;
460 0 : goto done;
461 : }
462 : }
463 :
464 : /* Make sure to remove any extra attributes from the sysdb
465 : * that have been removed from LDAP
466 : */
467 0 : ret = list_missing_attrs(svc_attrs, opts->service_map, SDAP_OPTS_SERVICES,
468 : attrs, &missing);
469 0 : if (ret != EOK) {
470 0 : DEBUG(SSSDBG_MINOR_FAILURE,
471 : "Failed to identify removed attributes: [%s]\n",
472 : strerror(ret));
473 0 : goto done;
474 : }
475 :
476 0 : cache_timeout = dom->service_timeout;
477 :
478 0 : ret = sysdb_store_service(dom, name, port, aliases, store_protocols,
479 : svc_attrs, missing, cache_timeout, now);
480 0 : if (ret != EOK) {
481 0 : DEBUG(SSSDBG_MINOR_FAILURE,
482 : "Failed to store service in the sysdb: [%s]\n",
483 : strerror(ret));
484 0 : goto done;
485 : }
486 :
487 0 : *_usn_value = talloc_steal(mem_ctx, usn_value);
488 :
489 : done:
490 0 : talloc_free(tmp_ctx);
491 0 : return ret;
492 : }
493 :
494 : errno_t
495 0 : sdap_get_services_recv(TALLOC_CTX *mem_ctx,
496 : struct tevent_req *req,
497 : char **usn_value)
498 : {
499 0 : struct sdap_get_services_state *state =
500 0 : tevent_req_data(req, struct sdap_get_services_state);
501 :
502 0 : TEVENT_REQ_RETURN_ON_ERROR(req);
503 :
504 0 : if (usn_value) {
505 0 : *usn_value = talloc_steal(mem_ctx, state->higher_usn);
506 : }
507 :
508 0 : return EOK;
509 : }
510 :
511 :
512 : /* Enumeration routines */
513 :
514 : struct enum_services_state {
515 : struct tevent_context *ev;
516 : struct sdap_id_ctx *id_ctx;
517 : struct sdap_id_op *op;
518 : struct sss_domain_info *domain;
519 : struct sysdb_ctx *sysdb;
520 :
521 : char *filter;
522 : const char **attrs;
523 : };
524 :
525 : static void
526 : enum_services_op_done(struct tevent_req *subreq);
527 :
528 : struct tevent_req *
529 0 : enum_services_send(TALLOC_CTX *memctx,
530 : struct tevent_context *ev,
531 : struct sdap_id_ctx *id_ctx,
532 : struct sdap_id_op *op,
533 : bool purge)
534 : {
535 : errno_t ret;
536 : struct tevent_req *req;
537 : struct tevent_req *subreq;
538 : struct enum_services_state *state;
539 :
540 0 : req = tevent_req_create(memctx, &state, struct enum_services_state);
541 0 : if (!req) return NULL;
542 :
543 0 : state->ev = ev;
544 0 : state->id_ctx = id_ctx;
545 0 : state->domain = id_ctx->be->domain;
546 0 : state->sysdb = id_ctx->be->domain->sysdb;
547 0 : state->op = op;
548 :
549 0 : if (id_ctx->srv_opts && id_ctx->srv_opts->max_service_value && !purge) {
550 0 : state->filter = talloc_asprintf(
551 : state,
552 : "(&(objectclass=%s)(%s=*)(%s=*)(%s=*)(%s>=%s)(!(%s=%s)))",
553 0 : id_ctx->opts->service_map[SDAP_OC_SERVICE].name,
554 0 : id_ctx->opts->service_map[SDAP_AT_SERVICE_NAME].name,
555 0 : id_ctx->opts->service_map[SDAP_AT_SERVICE_PORT].name,
556 0 : id_ctx->opts->service_map[SDAP_AT_SERVICE_PROTOCOL].name,
557 0 : id_ctx->opts->service_map[SDAP_AT_SERVICE_USN].name,
558 0 : id_ctx->srv_opts->max_service_value,
559 0 : id_ctx->opts->service_map[SDAP_AT_SERVICE_USN].name,
560 0 : id_ctx->srv_opts->max_service_value);
561 : } else {
562 0 : state->filter = talloc_asprintf(
563 : state,
564 : "(&(objectclass=%s)(%s=*)(%s=*)(%s=*))",
565 0 : id_ctx->opts->service_map[SDAP_OC_SERVICE].name,
566 0 : id_ctx->opts->service_map[SDAP_AT_SERVICE_NAME].name,
567 0 : id_ctx->opts->service_map[SDAP_AT_SERVICE_PORT].name,
568 0 : id_ctx->opts->service_map[SDAP_AT_SERVICE_PROTOCOL].name);
569 : }
570 0 : if (!state->filter) {
571 0 : DEBUG(SSSDBG_MINOR_FAILURE, "Failed to build base filter\n");
572 0 : ret = ENOMEM;
573 0 : goto fail;
574 : }
575 :
576 : /* TODO: handle attrs_type */
577 0 : ret = build_attrs_from_map(state, id_ctx->opts->service_map,
578 : SDAP_OPTS_SERVICES, NULL,
579 0 : &state->attrs, NULL);
580 0 : if (ret != EOK) goto fail;
581 :
582 0 : subreq = sdap_get_services_send(state, state->ev,
583 0 : state->domain, state->sysdb,
584 0 : state->id_ctx->opts,
585 0 : state->id_ctx->opts->sdom->service_search_bases,
586 0 : sdap_id_op_handle(state->op),
587 0 : state->attrs, state->filter,
588 0 : dp_opt_get_int(state->id_ctx->opts->basic,
589 : SDAP_SEARCH_TIMEOUT),
590 : true);
591 0 : if (!subreq) {
592 0 : ret = ENOMEM;
593 0 : goto fail;
594 : }
595 0 : tevent_req_set_callback(subreq, enum_services_op_done, req);
596 :
597 0 : return req;
598 :
599 : fail:
600 0 : tevent_req_error(req, ret);
601 0 : tevent_req_post(req, ev);
602 0 : return req;
603 : }
604 :
605 : static void
606 0 : enum_services_op_done(struct tevent_req *subreq)
607 : {
608 0 : struct tevent_req *req =
609 0 : tevent_req_callback_data(subreq, struct tevent_req);
610 0 : struct enum_services_state *state =
611 0 : tevent_req_data(req, struct enum_services_state);
612 : char *usn_value;
613 0 : char *endptr = NULL;
614 : unsigned usn_number;
615 : int ret;
616 :
617 0 : ret = sdap_get_services_recv(state, subreq, &usn_value);
618 0 : talloc_zfree(subreq);
619 0 : if (ret != EOK) {
620 0 : tevent_req_error(req, ret);
621 0 : return;
622 : }
623 :
624 0 : if (usn_value) {
625 0 : talloc_zfree(state->id_ctx->srv_opts->max_service_value);
626 0 : state->id_ctx->srv_opts->max_service_value =
627 0 : talloc_steal(state->id_ctx, usn_value);
628 :
629 0 : usn_number = strtoul(usn_value, &endptr, 10);
630 0 : if ((endptr == NULL || (*endptr == '\0' && endptr != usn_value))
631 0 : && (usn_number > state->id_ctx->srv_opts->last_usn)) {
632 0 : state->id_ctx->srv_opts->last_usn = usn_number;
633 : }
634 : }
635 :
636 0 : DEBUG(SSSDBG_FUNC_DATA, "Services higher USN value: [%s]\n",
637 : state->id_ctx->srv_opts->max_service_value);
638 :
639 0 : tevent_req_done(req);
640 : }
641 :
642 : errno_t
643 0 : enum_services_recv(struct tevent_req *req)
644 : {
645 0 : TEVENT_REQ_RETURN_ON_ERROR(req);
646 :
647 0 : return EOK;
648 : }
|