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 :
24 : #include <arpa/inet.h>
25 : #include "util/util.h"
26 : #include "responder/nss/nsssrv.h"
27 : #include "responder/nss/nsssrv_private.h"
28 : #include "responder/nss/nsssrv_services.h"
29 : #include "responder/common/negcache.h"
30 : #include "confdb/confdb.h"
31 : #include "db/sysdb.h"
32 : #include "db/sysdb_services.h"
33 :
34 : struct getserv_ctx {
35 : uint16_t port;
36 : struct tevent_context *ev;
37 : struct nss_dom_ctx *dctx;
38 :
39 : struct sss_domain_info **domains;
40 : size_t dom_idx;
41 :
42 : char *name;
43 : char *cased_name;
44 :
45 : char *proto;
46 : char *cased_proto;
47 : struct ldb_result *res;
48 : };
49 :
50 : static errno_t lookup_service_step(struct tevent_req *req);
51 : static void lookup_service_done(struct tevent_req *req);
52 :
53 : #define SVC_NAME_CASED (dom->case_sensitive ? state->name \
54 : : state->cased_name)
55 : #define SVC_PROTO_CASED (dom->case_sensitive ? state->proto \
56 : : state->cased_proto)
57 :
58 : /* Provider Lookup Logic:
59 : * Iterate through the available caches. If the cached entry is
60 : * present and not expired, return it immediately(*). If it is
61 : * present and expired, add it to a list of domains eligible to
62 : * be checked. If it is in the negative cache, skip over it and
63 : * do not add it to the eligible domain list.
64 : *
65 : * Once we have searched all of the caches, if the entry has not
66 : * been determined to be available, search all domains in order
67 : * to see if any of them contain the requested entry.
68 : *
69 : * (*) Optionally perform a midpoint cache refresh if appropriate.
70 : */
71 :
72 : static struct tevent_req *
73 0 : getserv_send(TALLOC_CTX *mem_ctx,
74 : struct tevent_context *ev,
75 : uint16_t port,
76 : const char *service_name,
77 : const char *service_protocol,
78 : struct nss_dom_ctx *dctx)
79 : {
80 : errno_t ret;
81 : struct tevent_req *req;
82 : struct tevent_req *subreq;
83 : struct getserv_ctx *state;
84 0 : struct nss_cmd_ctx *cmdctx = dctx->cmdctx;
85 0 : struct cli_ctx *cctx = cmdctx->cctx;
86 : struct sss_domain_info *dom;
87 0 : size_t num_domains = 0;
88 0 : size_t dom_idx = 0;
89 0 : struct nss_ctx *nctx =
90 0 : talloc_get_type(cctx->rctx->pvt_ctx, struct nss_ctx);
91 0 : time_t now = time(NULL);
92 : uint64_t lastUpdate;
93 : uint64_t cacheExpire;
94 : uint64_t midpoint_refresh;
95 :
96 0 : req = tevent_req_create(mem_ctx, &state, struct getserv_ctx);
97 0 : if (!req) return NULL;
98 0 : state->dctx = dctx;
99 :
100 0 : for (dom = cctx->rctx->domains; dom; dom = get_next_domain(dom, false)) {
101 0 : num_domains++;
102 : }
103 :
104 : /* Create an array of domains to check. To save resizes, we'll
105 : * assume that all will be checked
106 : */
107 0 : state->domains = talloc_zero_array(state,
108 : struct sss_domain_info *,
109 : num_domains + 1);
110 0 : if (!state->domains) {
111 0 : ret = ENOMEM;
112 0 : goto immediate;
113 : }
114 :
115 0 : state->port = port;
116 :
117 : /* Store both the case-sensitive and lowercased names
118 : * in the state object, to avoid recalculating the
119 : * lowercase in multiple domains.
120 : */
121 0 : if (service_protocol) {
122 0 : state->proto = talloc_strdup(state, service_protocol);
123 0 : if (!state->proto) {
124 0 : ret = ENOMEM;
125 0 : goto immediate;
126 : }
127 0 : state->cased_proto = sss_get_cased_name(state, service_protocol,
128 : false);
129 0 : if (!state->cased_proto) {
130 0 : ret = ENOMEM;
131 0 : goto immediate;
132 : }
133 : } else {
134 0 : state->proto = NULL;
135 0 : state->cased_proto = NULL;
136 : }
137 :
138 : /* If we're looking up by name */
139 0 : if (service_name) {
140 : /* Store both the case-sensitive and lowercased names
141 : * in the state object, to avoid recalculating the
142 : * lowercase in multiple domains.
143 : */
144 0 : state->name = talloc_strdup(state, service_name);
145 0 : if (!state->name) {
146 0 : ret = ENOMEM;
147 0 : goto immediate;
148 : }
149 :
150 0 : state->cased_name = sss_get_cased_name(state, service_name,
151 : false);
152 0 : if (!state->cased_name) {
153 0 : ret = ENOMEM;
154 0 : goto immediate;
155 : }
156 : }
157 :
158 0 : dom = cctx->rctx->domains;
159 0 : while(dom) {
160 : /* if it is a domainless search, skip domains that require fully
161 : * qualified names instead */
162 0 : while (dom && cmdctx->check_next && dom->fqnames) {
163 0 : dom = get_next_domain(dom, false);
164 : }
165 0 : if (!dom) break;
166 :
167 0 : if (dom->sysdb == NULL) {
168 0 : DEBUG(SSSDBG_CRIT_FAILURE,
169 : "Critical: Sysdb CTX not found for [%s]!\n", dom->name);
170 0 : ret = EINVAL;
171 0 : goto immediate;
172 : }
173 :
174 : /* If we're looking up by name */
175 0 : if (service_name) {
176 : /* Check the negative cache */
177 0 : ret = sss_ncache_check_service(nctx->ncache,
178 : nctx->neg_timeout,
179 : dom,
180 0 : SVC_NAME_CASED,
181 0 : SVC_PROTO_CASED);
182 : /* If negatively cached, return we didn't find it */
183 0 : if (ret == EEXIST) {
184 0 : DEBUG(SSSDBG_TRACE_FUNC,
185 : "Service [%s:%s] does not exist in [%s]! "
186 : "(negative cache)\n",
187 : SVC_NAME_CASED,
188 : SVC_PROTO_CASED ? SVC_PROTO_CASED : "<ANY>",
189 : dom->name);
190 :
191 : /* If this is a multi-domain search, try the next one */
192 0 : if (cmdctx->check_next) {
193 0 : dom = get_next_domain(dom, false);
194 : } else {
195 : /* This was a single-domain search.
196 : * exit the loop. Since it was negatively-
197 : * cached, don't add it to the eligible
198 : * domains list.
199 : */
200 0 : dom = NULL;
201 : }
202 :
203 0 : continue;
204 : }
205 :
206 : /* Check the cache */
207 0 : DEBUG(SSSDBG_TRACE_FUNC,
208 : "Checking cache for [%s:%s@%s]\n",
209 : SVC_NAME_CASED,
210 : SVC_PROTO_CASED ? SVC_PROTO_CASED : "<ANY>",
211 : dom->name);
212 :
213 0 : ret = sysdb_getservbyname(state, dom,
214 0 : SVC_NAME_CASED,
215 0 : SVC_PROTO_CASED,
216 0 : &state->res);
217 : } else { /* Looking up by port */
218 : /* Check the negative cache */
219 0 : ret = sss_ncache_check_service_port(nctx->ncache,
220 : nctx->neg_timeout,
221 : dom, port,
222 0 : SVC_PROTO_CASED);
223 : /* If negatively cached, return we didn't find it */
224 0 : if (ret == EEXIST) {
225 0 : DEBUG(SSSDBG_TRACE_FUNC,
226 : "Service [%"PRIu16":%s] does not exist in [%s]! "
227 : "(negative cache)\n",
228 : port,
229 : SVC_PROTO_CASED ? SVC_PROTO_CASED : "<ANY>",
230 : dom->name);
231 :
232 : /* If this is a multi-domain search, try the next one */
233 0 : if (cmdctx->check_next) {
234 0 : dom = get_next_domain(dom, false);
235 : } else {
236 : /* This was a single-domain search.
237 : * exit the loop. Since it was negatively-
238 : * cached, don't add it to the eligible
239 : * domains list.
240 : */
241 0 : dom = NULL;
242 : }
243 :
244 0 : continue;
245 : }
246 :
247 : /* Check the cache */
248 0 : DEBUG(SSSDBG_TRACE_FUNC,
249 : "Checking cache for [%"PRIu16":%s@%s]\n",
250 : port,
251 : SVC_PROTO_CASED ? SVC_PROTO_CASED : "<ANY>",
252 : dom->name);
253 :
254 0 : ret = sysdb_getservbyport(state, dom, port,
255 0 : SVC_PROTO_CASED,
256 0 : &state->res);
257 : }
258 0 : if (ret != EOK && ret != ENOENT) goto immediate;
259 :
260 0 : if (ret == ENOENT) {
261 : /* Not found in the cache. Add this domain to the
262 : * list of eligible domains to check the provider.
263 : */
264 0 : if (NEED_CHECK_PROVIDER(dom->provider)) {
265 0 : state->domains[dom_idx] = dom;
266 0 : dom_idx++;
267 : } else {
268 : /* No provider to check. Set the negative cache here */
269 0 : if (state->name) {
270 0 : ret = sss_ncache_set_service_name(nctx->ncache, false,
271 : dom,
272 0 : SVC_NAME_CASED,
273 0 : SVC_PROTO_CASED);
274 0 : if (ret != EOK) {
275 : /* Failure to set the negative cache is non-fatal.
276 : * We'll log an error and continue.
277 : */
278 0 : DEBUG(SSSDBG_MINOR_FAILURE,
279 : "Could not set negative cache for [%s][%s]\n",
280 : SVC_NAME_CASED, SVC_PROTO_CASED);
281 : }
282 : } else {
283 0 : ret = sss_ncache_set_service_port(nctx->ncache, false,
284 : dom,
285 0 : state->port,
286 0 : SVC_PROTO_CASED);
287 0 : if (ret != EOK) {
288 : /* Failure to set the negative cache is non-fatal.
289 : * We'll log an error and continue.
290 : */
291 0 : DEBUG(SSSDBG_MINOR_FAILURE,
292 : "Could not set negative cache for "
293 : "[%"PRIu16"][%s]\n",
294 : state->port, SVC_PROTO_CASED);
295 : }
296 : }
297 : }
298 :
299 : /* If this is a multi-domain search, try the next one */
300 0 : if (cmdctx->check_next) {
301 0 : dom = get_next_domain(dom, false);
302 : } else {
303 : /* This was a single-domain search.
304 : * exit the loop.
305 : */
306 0 : dom = NULL;
307 : }
308 0 : continue;
309 : }
310 :
311 : /* Found a result. Check its validity */
312 0 : if (state->res->count > 1) {
313 0 : DEBUG(SSSDBG_OP_FAILURE,
314 : "getservby* returned more than one result!\n");
315 0 : ret = ENOENT;
316 0 : goto immediate;
317 : }
318 :
319 0 : lastUpdate = ldb_msg_find_attr_as_uint64(state->res->msgs[0],
320 : SYSDB_LAST_UPDATE, 0);
321 :
322 0 : cacheExpire = ldb_msg_find_attr_as_uint64(state->res->msgs[0],
323 : SYSDB_CACHE_EXPIRE, 0);
324 :
325 0 : midpoint_refresh = 0;
326 0 : if(nctx->cache_refresh_percent) {
327 0 : midpoint_refresh = lastUpdate +
328 0 : (cacheExpire - lastUpdate)*nctx->cache_refresh_percent/100.0;
329 0 : if (midpoint_refresh - lastUpdate < 10) {
330 : /* If the percentage results in an expiration
331 : * less than ten seconds after the lastUpdate time,
332 : * that's too often we will simply set it to 10s
333 : */
334 0 : midpoint_refresh = lastUpdate+10;
335 : }
336 : }
337 :
338 0 : if (cacheExpire > now) {
339 : /* cache still valid */
340 :
341 0 : if (NEED_CHECK_PROVIDER(dom->provider)
342 0 : && midpoint_refresh
343 0 : && midpoint_refresh < now) {
344 : /* We're past the cache refresh timeout
345 : * We'll return the value from the cache, but we'll also
346 : * queue the cache entry for update out-of-band.
347 : */
348 0 : DEBUG(SSSDBG_TRACE_FUNC,
349 : "Performing midpoint cache update\n");
350 :
351 : /* Update the cache */
352 0 : subreq = sss_dp_get_account_send(cctx, cctx->rctx,
353 : dom, true,
354 : SSS_DP_SERVICES,
355 0 : SVC_NAME_CASED,
356 : port, NULL);
357 0 : if (!subreq) {
358 0 : DEBUG(SSSDBG_CRIT_FAILURE,
359 : "Out of memory sending out-of-band data provider "
360 : "request\n");
361 : /* This is non-fatal, so we'll continue here */
362 : }
363 : /* We don't need to listen for a reply, so we will free the
364 : * request here.
365 : */
366 0 : talloc_zfree(subreq);
367 : }
368 :
369 : /* The cache is valid. Return it */
370 0 : ret = EOK;
371 0 : goto immediate;
372 : } else {
373 : /* Cache is expired. Add this domain to the
374 : * list of eligible domains to check the provider.
375 : */
376 0 : if (NEED_CHECK_PROVIDER(dom->provider)) {
377 0 : state->domains[dom_idx] = dom;
378 0 : dom_idx++;
379 : }
380 :
381 : /* If this is a multi-domain search, try the next one */
382 0 : if (cmdctx->check_next) {
383 0 : dom = get_next_domain(dom, false);
384 : } else {
385 : /* This was a single-domain search.
386 : * exit the loop.
387 : */
388 0 : dom = NULL;
389 : }
390 : }
391 : }
392 :
393 : /* No valid cached entries found and
394 : * not found in negative caches.
395 : * Iterate through the domains and try
396 : * to look the data up.
397 : */
398 :
399 0 : state->dom_idx = 0;
400 0 : if (!state->domains[state->dom_idx]) {
401 : /* No domains to search. Return ENOENT */
402 0 : ret = ENOENT;
403 0 : goto immediate;
404 : }
405 :
406 0 : ret = lookup_service_step(req);
407 0 : if (ret != EOK) goto immediate;
408 :
409 0 : return req;
410 :
411 : immediate:
412 0 : if (ret == EOK) {
413 0 : tevent_req_done(req);
414 : } else {
415 0 : tevent_req_error(req, ret);
416 : }
417 0 : tevent_req_post(req, ev);
418 0 : return req;
419 : }
420 :
421 0 : static errno_t lookup_service_step(struct tevent_req *req)
422 : {
423 0 : struct getserv_ctx *state =
424 0 : tevent_req_data(req, struct getserv_ctx);
425 : struct tevent_req *subreq;
426 0 : struct cli_ctx *cctx = state->dctx->cmdctx->cctx;
427 0 : struct sss_domain_info *dom =
428 0 : state->domains[state->dom_idx];
429 :
430 : /* Update the cache */
431 0 : subreq = sss_dp_get_account_send(req,
432 : cctx->rctx,
433 : dom,
434 : true,
435 : SSS_DP_SERVICES,
436 0 : SVC_NAME_CASED,
437 0 : state->port,
438 0 : SVC_PROTO_CASED);
439 0 : if (!subreq) return ENOMEM;
440 0 : tevent_req_set_callback(subreq, lookup_service_done, req);
441 :
442 0 : return EOK;
443 : }
444 :
445 0 : static void lookup_service_done(struct tevent_req *subreq)
446 : {
447 : errno_t ret;
448 : dbus_uint16_t err_maj;
449 : dbus_uint32_t err_min;
450 : char *err_msg;
451 :
452 0 : struct tevent_req *req =
453 0 : tevent_req_callback_data(subreq, struct tevent_req);
454 0 : struct getserv_ctx *state =
455 0 : tevent_req_data(req, struct getserv_ctx);
456 0 : struct cli_ctx *cctx = state->dctx->cmdctx->cctx;
457 0 : struct nss_ctx *nctx =
458 0 : talloc_get_type(cctx->rctx->pvt_ctx, struct nss_ctx);
459 0 : struct sss_domain_info *dom = state->domains[state->dom_idx];
460 :
461 0 : ret = sss_dp_get_account_recv(state, subreq,
462 : &err_maj, &err_min,
463 : &err_msg);
464 0 : talloc_zfree(subreq);
465 0 : if (ret != EOK) {
466 0 : DEBUG(SSSDBG_OP_FAILURE,
467 : "Unable to get information from Data Provider\n"
468 : "dp_error: [%u], errno: [%u], error_msg: [%s]\n"
469 : "Will try to return what we have in cache\n",
470 : (unsigned int)err_maj, (unsigned int)err_min,
471 : err_msg ? err_msg : "none");
472 : }
473 :
474 : /* Recheck the cache after the lookup.
475 : * We can ignore the expiration values here, because
476 : * either we have just updated it or the provider is
477 : * offline. Either way, whatever is in the cache should
478 : * be returned, if it exists. Otherwise, move to the
479 : * next provider.
480 : */
481 0 : if (dom->sysdb == NULL) {
482 0 : DEBUG(SSSDBG_CRIT_FAILURE,
483 : "Critical: Sysdb CTX not found for [%s]!\n",
484 : dom->name);
485 0 : ret = EINVAL;
486 0 : goto done;
487 : }
488 :
489 0 : if (state->name) {
490 0 : DEBUG(SSSDBG_TRACE_FUNC,
491 : "Re-checking cache for [%s:%s@%s]\n",
492 : SVC_NAME_CASED,
493 : SVC_PROTO_CASED ? SVC_PROTO_CASED : "<ANY>",
494 : dom->name);
495 :
496 0 : ret = sysdb_getservbyname(state, dom,
497 0 : SVC_NAME_CASED,
498 0 : SVC_PROTO_CASED,
499 : &state->res);
500 : } else {
501 0 : DEBUG(SSSDBG_TRACE_FUNC,
502 : "Re-checking cache for [%"PRIu16":%s@%s]\n",
503 : state->port,
504 : SVC_PROTO_CASED ? SVC_PROTO_CASED : "<ANY>",
505 : dom->name);
506 :
507 0 : ret = sysdb_getservbyport(state, dom,
508 0 : state->port,
509 0 : SVC_PROTO_CASED,
510 : &state->res);
511 : }
512 :
513 0 : if (ret == ENOENT) {
514 : /* Nothing in the cache.
515 : * Set the negative cache
516 : */
517 0 : if (state->name) {
518 0 : ret = sss_ncache_set_service_name(nctx->ncache, false,
519 : dom,
520 0 : SVC_NAME_CASED,
521 0 : SVC_PROTO_CASED);
522 0 : if (ret != EOK) {
523 : /* Failure to set the negative cache is non-fatal.
524 : * We'll log an error and continue.
525 : */
526 0 : DEBUG(SSSDBG_MINOR_FAILURE,
527 : "Could not set negative cache for [%s][%s]\n",
528 : SVC_NAME_CASED, SVC_PROTO_CASED);
529 : }
530 : } else {
531 0 : ret = sss_ncache_set_service_port(nctx->ncache, false,
532 : dom,
533 0 : state->port,
534 0 : SVC_PROTO_CASED);
535 0 : if (ret != EOK) {
536 : /* Failure to set the negative cache is non-fatal.
537 : * We'll log an error and continue.
538 : */
539 0 : DEBUG(SSSDBG_MINOR_FAILURE,
540 : "Could not set negative cache for [%"PRIu16"][%s]\n",
541 : state->port, SVC_PROTO_CASED);
542 : }
543 : }
544 :
545 : /* Need to check other domains */
546 0 : state->dom_idx++;
547 0 : if (!state->domains[state->dom_idx]) {
548 : /* No more domains to search. Return ENOENT */
549 0 : ret = ENOENT;
550 0 : goto done;
551 : }
552 0 : ret = lookup_service_step(req);
553 0 : if (ret != EOK) goto done;
554 :
555 : /* Set EAGAIN so we will re-enter the mainloop */
556 0 : ret = EAGAIN;
557 : }
558 :
559 : done:
560 0 : if (ret == EOK) {
561 : /* Cache contained results. Return them */
562 0 : tevent_req_done(req);
563 0 : } else if (ret != EAGAIN) {
564 : /* An error occurred, fail the request */
565 0 : tevent_req_error(req, ret);
566 : }
567 :
568 : /* ret == EAGAIN: Reenter mainloop */
569 0 : return;
570 : }
571 :
572 : static errno_t
573 0 : getserv_recv(TALLOC_CTX *mem_ctx,
574 : struct tevent_req *req,
575 : struct ldb_result **_res)
576 : {
577 0 : struct getserv_ctx *state =
578 0 : tevent_req_data(req, struct getserv_ctx);
579 :
580 0 : TEVENT_REQ_RETURN_ON_ERROR(req);
581 :
582 0 : *_res = talloc_steal(mem_ctx, state->res);
583 :
584 0 : return EOK;
585 : }
586 :
587 : static errno_t
588 0 : fill_service(struct sss_packet *packet,
589 : struct sss_domain_info *dom,
590 : const char *protocol,
591 : struct ldb_message **msgs,
592 : unsigned int *count)
593 : {
594 : errno_t ret;
595 0 : unsigned int msg_count = *count;
596 : size_t rzero, rsize, aptr;
597 0 : unsigned int num = 0;
598 : unsigned int i, j;
599 : uint32_t num_aliases, written_aliases;
600 : struct ldb_message *msg;
601 : struct ldb_message_element *el;
602 0 : TALLOC_CTX *tmp_ctx = NULL;
603 : const char *orig_name;
604 : const char *orig_proto;
605 : struct sized_string cased_name;
606 : struct sized_string cased_proto;
607 : uint16_t port;
608 : char *tmpstr;
609 : uint8_t *body;
610 : size_t blen;
611 : struct sized_string alias;
612 :
613 : /* FIXME: Should we account for fully-qualified
614 : * service names?
615 : */
616 :
617 : /* first 2 fields (len and reserved), filled up later */
618 0 : ret = sss_packet_grow(packet, 2 * sizeof(uint32_t));
619 0 : if (ret != EOK) goto done;
620 :
621 0 : rzero = 2 * sizeof(uint32_t);
622 0 : rsize = 0;
623 :
624 0 : for (i = 0; i < msg_count; i++) {
625 0 : talloc_zfree(tmp_ctx);
626 0 : tmp_ctx = talloc_new(NULL);
627 0 : if (!tmp_ctx) return ENOMEM;
628 :
629 0 : msg = msgs[i];
630 :
631 : /* new service */
632 0 : if (!ldb_msg_check_string_attribute(msg, "objectClass",
633 : SYSDB_SVC_CLASS)) {
634 0 : DEBUG(SSSDBG_CRIT_FAILURE, "Wrong object (%s) found on stack!\n",
635 : ldb_dn_get_linearized(msg->dn));
636 0 : continue;
637 : }
638 :
639 : /* new result starts at end of previous result */
640 0 : rzero += rsize;
641 0 : rsize = 0;
642 :
643 : /* Get the service name */
644 0 : orig_name = ldb_msg_find_attr_as_string(msg, SYSDB_NAME, NULL);
645 0 : tmpstr = sss_get_cased_name(tmp_ctx, orig_name, dom->case_preserve);
646 0 : if (tmpstr == NULL) {
647 0 : DEBUG(SSSDBG_CRIT_FAILURE,
648 : "Could not identify service name, skipping\n");
649 0 : continue;
650 : }
651 0 : to_sized_string(&cased_name, tmpstr);
652 :
653 : /* Get the port */
654 0 : port = (uint16_t) ldb_msg_find_attr_as_uint(msg, SYSDB_SVC_PORT, 0);
655 0 : if (!port) {
656 0 : DEBUG(SSSDBG_CRIT_FAILURE,
657 : "No port for service [%s]. Skipping\n", tmpstr);
658 0 : continue;
659 : }
660 :
661 : /* Get the service protocol.
662 : * Use the requested protocol if present,
663 : * otherwise take the first protocol returned
664 : * by the sysdb.
665 : * If more than one is available, select the
666 : * first in the message.
667 : */
668 0 : if (protocol) {
669 0 : orig_proto = protocol;
670 : } else {
671 0 : el = ldb_msg_find_element(msg, SYSDB_SVC_PROTO);
672 0 : if (el->num_values == 0) {
673 0 : ret = EINVAL;
674 0 : num = 0;
675 0 : goto done;
676 : }
677 0 : orig_proto = (const char *)el->values[0].data;
678 : }
679 :
680 0 : tmpstr = sss_get_cased_name(tmp_ctx, orig_proto, dom->case_preserve);
681 0 : if (tmpstr == NULL) {
682 0 : DEBUG(SSSDBG_CRIT_FAILURE,
683 : "sss_get_cased_name failed, skipping\n");
684 0 : continue;
685 : }
686 0 : to_sized_string(&cased_proto, tmpstr);
687 :
688 0 : ret = sss_packet_grow(packet, 2 * sizeof(uint16_t)
689 : + sizeof(uint32_t)
690 0 : + cased_name.len
691 0 : + cased_proto.len);
692 0 : if (ret != EOK) {
693 0 : num = 0;
694 0 : goto done;
695 : }
696 0 : sss_packet_get_body(packet, &body, &blen);
697 :
698 : /* Store the port number */
699 0 : SAFEALIGN_SETMEM_UINT32(&body[rzero + rsize], (uint32_t)htons(port), &rsize);
700 :
701 : /* Get the aliases */
702 0 : el = ldb_msg_find_element(msg, SYSDB_NAME_ALIAS);
703 0 : if (!el) {
704 : /* No aliases for this user */
705 0 : num_aliases = 0;
706 : } else {
707 0 : num_aliases = el->num_values;
708 : }
709 :
710 : /* We'll store the alias count here */
711 0 : aptr = rzero+rsize;
712 0 : rsize += sizeof(uint32_t);
713 :
714 : /* Store the primary name */
715 0 : safealign_memcpy(&body[rzero + rsize],
716 0 : cased_name.str,
717 : cased_name.len,
718 : &rsize);
719 :
720 : /* Store the protocol */
721 0 : safealign_memcpy(&body[rzero + rsize],
722 0 : cased_proto.str,
723 : cased_proto.len,
724 : &rsize);
725 :
726 0 : written_aliases = 0;
727 0 : for (j = 0; j < num_aliases; j++) {
728 0 : if (sss_string_equal(dom->case_sensitive,
729 0 : (const char *)el->values[j].data,
730 : cased_name.str)) {
731 0 : continue;
732 : }
733 0 : to_sized_string(&alias, (const char *)el->values[j].data);
734 :
735 0 : ret = sss_packet_grow(packet, alias.len);
736 0 : if (ret != EOK) {
737 0 : num = 0;
738 0 : goto done;
739 : }
740 0 : sss_packet_get_body(packet, &body, &blen);
741 :
742 : /* Store the alias */
743 0 : safealign_memcpy(&body[rzero + rsize],
744 0 : alias.str,
745 : alias.len,
746 : &rsize);
747 :
748 0 : written_aliases++;
749 0 : talloc_zfree(tmpstr);
750 : }
751 :
752 : /* We must not advance rsize here, the data has already been
753 : * allocated and skipped earlier when aptr was assigned to.
754 : */
755 0 : SAFEALIGN_SETMEM_UINT32(&body[aptr], written_aliases, NULL);
756 :
757 0 : num++;
758 : }
759 :
760 0 : ret = EOK;
761 :
762 : done:
763 0 : talloc_free(tmp_ctx);
764 :
765 0 : if (ret != EOK ||num == 0) {
766 : /* if num is 0 most probably something went wrong,
767 : * reset packet and return ENOENT */
768 0 : sss_packet_set_size(packet, 0);
769 0 : return ENOENT;
770 : }
771 :
772 : /* num results */
773 0 : SAFEALIGN_COPY_UINT32(body, &num, NULL);
774 :
775 : /* reserved */
776 0 : SAFEALIGN_SETMEM_UINT32(body + sizeof(uint32_t), 0, NULL);
777 :
778 0 : return ret;
779 : }
780 : /*****************
781 : * getservbyname *
782 : *****************/
783 :
784 : errno_t parse_getservbyname(TALLOC_CTX *mem_ctx,
785 : uint8_t *body, size_t blen,
786 : struct sss_domain_info *domains,
787 : char *default_domain,
788 : char **domain_name,
789 : char **service_name,
790 : char **service_protocol);
791 :
792 : static void
793 : nss_cmd_getserv_done(struct tevent_req *req);
794 :
795 0 : int nss_cmd_getservbyname(struct cli_ctx *cctx)
796 : {
797 : errno_t ret;
798 : struct nss_cmd_ctx *cmdctx;
799 : struct nss_dom_ctx *dctx;
800 : char *domname;
801 : char *service_name;
802 : char *service_protocol;
803 : uint8_t *body;
804 : size_t blen;
805 : struct tevent_req *req;
806 :
807 0 : cmdctx = talloc_zero(cctx, struct nss_cmd_ctx);
808 0 : if (!cmdctx) return ENOMEM;
809 :
810 0 : cmdctx->cctx = cctx;
811 :
812 0 : dctx = talloc_zero(cmdctx, struct nss_dom_ctx);
813 0 : if (!dctx) {
814 0 : ret = ENOMEM;
815 0 : goto done;
816 : }
817 0 : dctx->cmdctx = cmdctx;
818 :
819 : /* get service name and protocol */
820 0 : sss_packet_get_body(cctx->creq->in, &body, &blen);
821 : /* if not terminated fail */
822 0 : if (body[blen -1] != '\0') {
823 0 : ret = EINVAL;
824 0 : goto done;
825 : }
826 :
827 0 : ret = parse_getservbyname(cmdctx, body, blen,
828 0 : cctx->rctx->domains,
829 0 : cctx->rctx->default_domain,
830 : &domname,
831 : &service_name,
832 : &service_protocol);
833 0 : if (ret != EOK) {
834 0 : DEBUG(SSSDBG_OP_FAILURE,
835 : "Could not parse request\n");
836 0 : goto done;
837 : }
838 :
839 0 : dctx->protocol = service_protocol;
840 :
841 0 : DEBUG(SSSDBG_TRACE_FUNC,
842 : "Requesting info for service [%s:%s] from [%s]\n",
843 : service_name,
844 : service_protocol ? service_protocol : "<ANY>",
845 : domname ? domname : "<ALL>");
846 :
847 0 : if (domname) {
848 0 : dctx->domain = responder_get_domain(cctx->rctx, domname);
849 0 : if (!dctx->domain) {
850 0 : ret = ENOENT;
851 0 : goto done;
852 : }
853 : } else {
854 : /* this is a multidomain search */
855 0 : dctx->domain = cctx->rctx->domains;
856 0 : cmdctx->check_next = true;
857 : }
858 :
859 : /* Identify if this backend requires a provider check */
860 0 : dctx->check_provider = NEED_CHECK_PROVIDER(dctx->domain->provider);
861 :
862 : /* Ok, find it! */
863 0 : req = getserv_send(cmdctx, cctx->ev, 0,
864 : service_name,
865 : service_protocol,
866 : dctx);
867 0 : if (!req) {
868 0 : ret = ENOMEM;
869 0 : goto done;
870 : }
871 0 : tevent_req_set_callback(req, nss_cmd_getserv_done, dctx);
872 :
873 : done:
874 0 : return nss_cmd_done(cmdctx, ret);
875 : }
876 :
877 0 : errno_t parse_getservbyname(TALLOC_CTX *mem_ctx,
878 : uint8_t *body, size_t blen,
879 : struct sss_domain_info *domains,
880 : char *default_domain,
881 : char **domain_name,
882 : char **service_name,
883 : char **service_protocol)
884 : {
885 : errno_t ret;
886 : size_t i, j, namelen;
887 : char *rawname;
888 : char *domname;
889 : char *svc_name;
890 : char *protocol;
891 : TALLOC_CTX *tmp_ctx;
892 :
893 0 : tmp_ctx = talloc_new(NULL);
894 0 : if (!tmp_ctx) return ENOMEM;
895 :
896 : /* The raw name is at most one character shorter
897 : * than the body length (if the protocol wasn't
898 : * specified). Since this is a common case, we'll
899 : * just assume the maximum memory size for the
900 : * rawname.
901 : */
902 0 : rawname = talloc_array(tmp_ctx, char, blen - 1);
903 0 : if (!rawname) {
904 0 : ret = ENOMEM;
905 0 : goto done;
906 : }
907 :
908 0 : i = j = 0;
909 :
910 : /* Copy in the service name */
911 0 : while (i < (blen - 1) && body[i]) {
912 0 : rawname[j] = body[i];
913 0 : i++;
914 0 : j++;
915 : }
916 0 : if (body[i] != '\0') {
917 : /* blen - 1 was reached without hitting
918 : * a NULL-terminator. No protocol field
919 : * is possible.
920 : */
921 0 : ret = EINVAL;
922 0 : goto done;
923 : }
924 0 : rawname[j] = '\0';
925 :
926 0 : i++;
927 0 : namelen = i;
928 0 : j = 0;
929 :
930 : /* Copy in the protocol */
931 0 : if (body[i] == '\0') {
932 : /* Zero-length protocol
933 : * Just set the protocol to NULL
934 : */
935 0 : protocol = NULL;
936 : } else {
937 : /* The protocol must be no longer than the remaining
938 : * body space, after the name was copied.
939 : */
940 0 : protocol = talloc_array(tmp_ctx, char, blen - i);
941 0 : if (!protocol) {
942 0 : ret = ENOMEM;
943 0 : goto done;
944 : }
945 :
946 0 : while (i < blen && body[i]) {
947 0 : protocol[j] = body[i];
948 0 : i++;
949 0 : j++;
950 : }
951 0 : if (body[i] != '\0') {
952 : /* blen was reached without hitting
953 : * a NULL-terminator.
954 : */
955 0 : ret = EINVAL;
956 0 : goto done;
957 : }
958 :
959 0 : protocol[j] = '\0';
960 :
961 0 : if (j != blen - namelen - 1) {
962 0 : DEBUG(SSSDBG_MINOR_FAILURE,
963 : "Body longer than the name and protocol\n");
964 0 : ret = EINVAL;
965 0 : goto done;
966 : }
967 : }
968 :
969 0 : ret = sss_parse_name_for_domains(tmp_ctx, domains, default_domain, rawname,
970 : &domname, &svc_name);
971 0 : if (ret != EOK) {
972 0 : DEBUG(SSSDBG_MINOR_FAILURE,
973 : "Could not split name and domain of [%s]\n",
974 : rawname);
975 0 : goto done;
976 : }
977 :
978 0 : *domain_name = talloc_steal(mem_ctx, domname);
979 0 : *service_name = talloc_steal(mem_ctx, svc_name);
980 0 : *service_protocol = talloc_steal(mem_ctx, protocol);
981 :
982 0 : ret = EOK;
983 :
984 : done:
985 0 : talloc_free(tmp_ctx);
986 0 : return ret;
987 : }
988 :
989 : static void
990 0 : nss_cmd_getserv_done(struct tevent_req *req)
991 : {
992 : errno_t ret, reqret;
993 : unsigned int i;
994 :
995 0 : struct nss_dom_ctx *dctx =
996 0 : tevent_req_callback_data(req, struct nss_dom_ctx);
997 0 : struct nss_cmd_ctx *cmdctx = dctx->cmdctx;
998 :
999 0 : reqret = getserv_recv(dctx, req, &dctx->res);
1000 0 : talloc_zfree(req);
1001 0 : if (reqret != EOK && reqret != ENOENT) {
1002 0 : DEBUG(SSSDBG_OP_FAILURE,
1003 : "getservbyname failed\n");
1004 0 : nss_cmd_done(cmdctx, reqret);
1005 0 : return;
1006 : }
1007 :
1008 : /* Either we succeeded or no domains were eligible */
1009 0 : ret = sss_packet_new(cmdctx->cctx->creq, 0,
1010 0 : sss_packet_get_cmd(cmdctx->cctx->creq->in),
1011 0 : &cmdctx->cctx->creq->out);
1012 0 : if (ret == EOK) {
1013 0 : if (reqret == ENOENT) {
1014 : /* Notify the caller that this entry wasn't found */
1015 0 : ret = sss_cmd_empty_packet(cmdctx->cctx->creq->out);
1016 : } else {
1017 0 : i = dctx->res->count;
1018 0 : ret = fill_service(cmdctx->cctx->creq->out,
1019 : dctx->domain,
1020 : dctx->protocol,
1021 0 : dctx->res->msgs,
1022 : &i);
1023 : }
1024 0 : if (ret != EOK) {
1025 0 : DEBUG(SSSDBG_OP_FAILURE,
1026 : "Could not create response packet: [%s]\n",
1027 : strerror(ret));
1028 : }
1029 :
1030 0 : sss_cmd_done(cmdctx->cctx, cmdctx);
1031 0 : return;
1032 : }
1033 :
1034 0 : DEBUG(SSSDBG_OP_FAILURE, "Error creating packet\n");
1035 : }
1036 :
1037 0 : errno_t parse_getservbyport(TALLOC_CTX *mem_ctx,
1038 : uint8_t *body, size_t blen,
1039 : uint16_t *service_port,
1040 : char **service_protocol)
1041 : {
1042 : errno_t ret;
1043 : size_t i, j;
1044 : size_t port_and_padding_len;
1045 : uint16_t c, port;
1046 : char *protocol;
1047 0 : TALLOC_CTX *tmp_ctx = talloc_new(NULL);
1048 0 : if (!tmp_ctx) return ENOMEM;
1049 :
1050 : /* Copy in the port */
1051 0 : SAFEALIGN_COPY_UINT16(&c, body, NULL);
1052 0 : port = ntohs(c);
1053 :
1054 0 : port_and_padding_len = 2 * sizeof(uint16_t) + sizeof(uint32_t);
1055 0 : i = port_and_padding_len;
1056 0 : j = 0;
1057 :
1058 : /* Copy in the protocol */
1059 0 : if (body[i] == '\0') {
1060 : /* Zero-length protocol
1061 : * Just set the protocol to NULL
1062 : */
1063 0 : protocol = NULL;
1064 : } else {
1065 : /* The protocol must be no longer than the remaining
1066 : * body space.
1067 : */
1068 0 : protocol = talloc_array(tmp_ctx, char, blen - i);
1069 0 : if (!protocol) {
1070 0 : ret = ENOMEM;
1071 0 : goto done;
1072 : }
1073 :
1074 0 : while (i < blen && body[i]) {
1075 0 : protocol[j] = body[i];
1076 0 : i++;
1077 0 : j++;
1078 : }
1079 0 : if (body[i] != '\0') {
1080 : /* blen was reached without hitting
1081 : * a NULL-terminator.
1082 : */
1083 0 : ret = EINVAL;
1084 0 : goto done;
1085 : }
1086 :
1087 0 : protocol[j] = '\0';
1088 :
1089 0 : if (j != blen - port_and_padding_len - 1) {
1090 0 : DEBUG(SSSDBG_MINOR_FAILURE,
1091 : "Body longer than the name and protocol\n");
1092 0 : ret = EINVAL;
1093 0 : goto done;
1094 : }
1095 : }
1096 :
1097 0 : *service_port = port;
1098 0 : *service_protocol = talloc_steal(mem_ctx, protocol);
1099 :
1100 0 : ret = EOK;
1101 :
1102 : done:
1103 0 : talloc_free(tmp_ctx);
1104 0 : return ret;
1105 : }
1106 :
1107 : /*****************
1108 : * getservbyport *
1109 : *****************/
1110 0 : int nss_cmd_getservbyport(struct cli_ctx *cctx)
1111 : {
1112 : errno_t ret;
1113 : struct nss_cmd_ctx *cmdctx;
1114 : struct nss_dom_ctx *dctx;
1115 : uint16_t port;
1116 : char *service_protocol;
1117 : uint8_t *body;
1118 : size_t blen;
1119 : struct tevent_req *req;
1120 :
1121 0 : cmdctx = talloc_zero(cctx, struct nss_cmd_ctx);
1122 0 : if (!cmdctx) return ENOMEM;
1123 :
1124 0 : cmdctx->cctx = cctx;
1125 :
1126 0 : dctx = talloc_zero(cmdctx, struct nss_dom_ctx);
1127 0 : if (!dctx) {
1128 0 : ret = ENOMEM;
1129 0 : goto done;
1130 : }
1131 0 : dctx->cmdctx = cmdctx;
1132 :
1133 : /* get service port and protocol */
1134 0 : sss_packet_get_body(cctx->creq->in, &body, &blen);
1135 : /* if not terminated fail */
1136 0 : if (body[blen -1] != '\0') {
1137 0 : ret = EINVAL;
1138 0 : goto done;
1139 : }
1140 :
1141 0 : ret = parse_getservbyport(cmdctx, body, blen,
1142 : &port,
1143 : &service_protocol);
1144 0 : if (ret != EOK) {
1145 0 : DEBUG(SSSDBG_OP_FAILURE,
1146 : "Could not parse request\n");
1147 0 : goto done;
1148 : }
1149 :
1150 0 : dctx->protocol = service_protocol;
1151 :
1152 0 : DEBUG(SSSDBG_TRACE_FUNC,
1153 : "Requesting info for service on port [%"PRIu16"/%s]\n",
1154 : port, service_protocol ? service_protocol : "<ANY>");
1155 :
1156 : /* All port lookups are multidomain searches */
1157 0 : dctx->domain = cctx->rctx->domains;
1158 0 : cmdctx->check_next = true;
1159 :
1160 : /* Identify if this backend requires a provider check */
1161 0 : dctx->check_provider = NEED_CHECK_PROVIDER(dctx->domain->provider);
1162 :
1163 : /* Ok, find it! */
1164 0 : req = getserv_send(cmdctx, cctx->ev, port,
1165 : NULL, service_protocol, dctx);
1166 0 : if (!req) {
1167 0 : ret = ENOMEM;
1168 0 : goto done;
1169 : }
1170 0 : tevent_req_set_callback(req, nss_cmd_getserv_done, dctx);
1171 :
1172 : done:
1173 0 : return nss_cmd_done(cmdctx, ret);
1174 : }
1175 :
1176 : struct setservent_ctx {
1177 : struct cli_ctx *cctx;
1178 : struct nss_ctx *nctx;
1179 : struct nss_dom_ctx *dctx;
1180 : struct getent_ctx *getent_ctx;
1181 : };
1182 :
1183 : static errno_t
1184 : setservent_step(struct setent_step_ctx *step_ctx);
1185 : static void
1186 : setservent_step_done(struct tevent_req *req);
1187 :
1188 : static struct tevent_req *
1189 : lookup_servent_send(TALLOC_CTX *mem_ctx,
1190 : struct resp_ctx *rctx,
1191 : struct sss_domain_info *dom);
1192 :
1193 : static struct tevent_req *
1194 0 : setservent_send(TALLOC_CTX *mem_ctx, struct cli_ctx *cctx)
1195 : {
1196 : errno_t ret;
1197 : unsigned int num_domains;
1198 : struct tevent_req *req;
1199 : struct setservent_ctx *state;
1200 : struct sss_domain_info *dom;
1201 : struct setent_step_ctx *step_ctx;
1202 0 : struct nss_ctx *nctx =
1203 0 : talloc_get_type(cctx->rctx->pvt_ctx, struct nss_ctx);
1204 :
1205 0 : DEBUG(SSSDBG_TRACE_FUNC, "Received setservent request\n");
1206 :
1207 : /* Reset the read pointers */
1208 0 : cctx->svc_dom_idx = 0;
1209 0 : cctx->svcent_cur = 0;
1210 :
1211 0 : req = tevent_req_create(mem_ctx, &state, struct setservent_ctx);
1212 0 : if (!req) return NULL;
1213 :
1214 0 : state->nctx = nctx;
1215 0 : state->cctx = cctx;
1216 0 : state->dctx = talloc_zero(state, struct nss_dom_ctx);
1217 0 : if (!state->dctx) {
1218 0 : ret = ENOMEM;
1219 0 : goto immediate;
1220 : }
1221 0 : state->dctx->domain = cctx->rctx->domains;
1222 :
1223 : /* Is the result context already available */
1224 0 : if (state->nctx->svcctx) {
1225 0 : if (state->nctx->svcctx->ready) {
1226 : /* All of the necessary data is in place
1227 : * We can return now, getservent requests will work at this point
1228 : */
1229 0 : ret = EOK;
1230 0 : goto immediate;
1231 : }
1232 : else {
1233 : /* Object is still being constructed
1234 : * Register for notification when it's
1235 : * ready.
1236 : */
1237 0 : ret = nss_setent_add_ref(state, state->nctx->svcctx, req);
1238 0 : if (ret != EOK) goto immediate;
1239 : }
1240 0 : return req;
1241 : }
1242 :
1243 : /* Create a new result context
1244 : * We are creating it on the nss_ctx so that it doesn't
1245 : * go away if the original request does. We will delete
1246 : * it when the refcount goes to zero;
1247 : */
1248 0 : state->nctx->svcctx = talloc_zero(nctx, struct getent_ctx);
1249 0 : if (!state->nctx->svcctx) {
1250 0 : ret = ENOMEM;
1251 0 : goto immediate;
1252 : }
1253 0 : state->getent_ctx = nctx->svcctx;
1254 :
1255 : /* Assume that all domains will have results (to avoid having
1256 : * to reallocate later
1257 : */
1258 0 : num_domains = 0;
1259 0 : for (dom = state->cctx->rctx->domains;
1260 : dom;
1261 0 : dom = get_next_domain(dom, false)) {
1262 0 : num_domains++;
1263 : }
1264 :
1265 0 : state->nctx->svcctx->doms = talloc_zero_array(state->nctx->svcctx,
1266 : struct dom_ctx,
1267 : num_domains);
1268 0 : if (!state->nctx->svcctx->doms) {
1269 0 : ret = ENOMEM;
1270 0 : goto immediate;
1271 : }
1272 :
1273 : /* Add a callback reference for ourselves */
1274 0 : ret = nss_setent_add_ref(state, state->nctx->svcctx, req);
1275 0 : if (ret != EOK) {
1276 0 : goto immediate;
1277 : }
1278 :
1279 : /* ok, start the searches */
1280 0 : step_ctx = talloc_zero(state->getent_ctx, struct setent_step_ctx);
1281 0 : if (!step_ctx) {
1282 0 : ret = ENOMEM;
1283 0 : goto immediate;
1284 : }
1285 :
1286 : /* Steal the dom_ctx onto the step_ctx so it doesn't go out of scope if
1287 : * this request is canceled while other requests are in-progress.
1288 : */
1289 0 : step_ctx->dctx = talloc_steal(step_ctx, state->dctx);
1290 0 : step_ctx->nctx = state->nctx;
1291 0 : step_ctx->getent_ctx = state->getent_ctx;
1292 0 : step_ctx->rctx = cctx->rctx;
1293 0 : step_ctx->cctx = cctx;
1294 0 : step_ctx->returned_to_mainloop = false;
1295 :
1296 0 : while (step_ctx->dctx->domain) {
1297 : /* There are more domains to check */
1298 0 : ret = setservent_step(step_ctx);
1299 0 : if (ret == EOK) {
1300 : /* Re-enter the mainloop */
1301 0 : return req;
1302 : }
1303 :
1304 0 : DEBUG(SSSDBG_CRIT_FAILURE,
1305 : "Error [%s] requesting info from domain [%s]. Skipping.\n",
1306 : strerror(ret), step_ctx->dctx->domain->name);
1307 :
1308 0 : step_ctx->dctx->domain = get_next_domain(step_ctx->dctx->domain, false);
1309 : }
1310 :
1311 : /* All domains failed */
1312 0 : ret = EIO;
1313 :
1314 : immediate:
1315 0 : if (ret == EOK) {
1316 0 : tevent_req_done(req);
1317 : } else {
1318 0 : tevent_req_error(req, ret);
1319 : }
1320 0 : tevent_req_post(req, cctx->rctx->ev);
1321 0 : return req;
1322 : }
1323 :
1324 : static errno_t
1325 0 : setservent_step(struct setent_step_ctx *step_ctx)
1326 : {
1327 : struct tevent_req *req;
1328 :
1329 0 : req = lookup_servent_send(step_ctx,
1330 : step_ctx->rctx,
1331 0 : step_ctx->dctx->domain);
1332 0 : if (!req) {
1333 0 : return ENOMEM;
1334 : }
1335 0 : tevent_req_set_callback(req, setservent_step_done, step_ctx);
1336 :
1337 0 : return EOK;
1338 : }
1339 :
1340 : struct lookup_servent_ctx {
1341 : struct resp_ctx *rctx;
1342 : struct sss_domain_info *dom;
1343 : struct ldb_result *res;
1344 : };
1345 :
1346 : static void
1347 : lookup_servent_done(struct tevent_req *subreq);
1348 :
1349 : static void
1350 : setservent_finalize(struct setent_step_ctx *step_ctx);
1351 :
1352 : static struct tevent_req *
1353 0 : lookup_servent_send(TALLOC_CTX *mem_ctx,
1354 : struct resp_ctx *rctx,
1355 : struct sss_domain_info *dom)
1356 : {
1357 : errno_t ret;
1358 : struct tevent_req *req;
1359 : struct tevent_req *subreq;
1360 : struct lookup_servent_ctx *state;
1361 : struct sysdb_ctx *sysdb;
1362 :
1363 0 : req = tevent_req_create(mem_ctx, &state, struct lookup_servent_ctx);
1364 0 : if (!req) return NULL;
1365 :
1366 0 : state->rctx = rctx;
1367 0 : state->dom = dom;
1368 :
1369 0 : if (!dom->enumerate) {
1370 0 : ret = ENOENT;
1371 0 : goto immediate;
1372 : }
1373 :
1374 0 : if (!(NEED_CHECK_PROVIDER(dom->name))) {
1375 : /* No provider check required. Just ask the
1376 : * sysdb.
1377 : */
1378 0 : sysdb = dom->sysdb;
1379 0 : if (sysdb == NULL) {
1380 0 : DEBUG(SSSDBG_FATAL_FAILURE,
1381 : "Sysdb CTX not found for [%s]!\n", dom->name);
1382 0 : ret = EINVAL;
1383 0 : goto immediate;
1384 : }
1385 :
1386 0 : ret = sysdb_enumservent(state, dom, &state->res);
1387 : /* Whatever the result, we're done, so report it */
1388 0 : goto immediate;
1389 : }
1390 :
1391 : /* We need to ask the provider for an enumeration */
1392 : /* Update the cache */
1393 0 : subreq = sss_dp_get_account_send(req, rctx, state->dom,
1394 : true, SSS_DP_SERVICES,
1395 : NULL, 0, NULL);
1396 0 : if (!subreq) {
1397 0 : ret = ENOMEM;
1398 0 : goto immediate;
1399 : }
1400 0 : tevent_req_set_callback(subreq, lookup_servent_done, req);
1401 :
1402 0 : return req;
1403 :
1404 : immediate:
1405 0 : if (ret == EOK) {
1406 0 : tevent_req_done(req);
1407 : } else {
1408 0 : tevent_req_error(req, ENOENT);
1409 : }
1410 0 : tevent_req_post(req, rctx->ev);
1411 0 : return req;
1412 : }
1413 :
1414 : static void
1415 0 : lookup_servent_done(struct tevent_req *subreq)
1416 : {
1417 : errno_t ret;
1418 : dbus_uint16_t dp_err;
1419 : dbus_uint32_t dp_ret;
1420 : char *err_msg;
1421 : struct sysdb_ctx *sysdb;
1422 0 : struct tevent_req *req =
1423 0 : tevent_req_callback_data(subreq, struct tevent_req);
1424 0 : struct lookup_servent_ctx *state =
1425 0 : tevent_req_data(req, struct lookup_servent_ctx);
1426 :
1427 0 : ret = sss_dp_get_account_recv(state, subreq,
1428 : &dp_err, &dp_ret, &err_msg);
1429 0 : talloc_zfree(subreq);
1430 0 : if (ret != EOK) {
1431 0 : DEBUG(SSSDBG_OP_FAILURE,
1432 : "Unable to get information from Data Provider\n"
1433 : "dp_error: [%u], errno: [%u], error_msg: [%s]\n"
1434 : "Will try to return what we have in cache\n",
1435 : (unsigned int)dp_err, (unsigned int)dp_ret,
1436 : err_msg ? err_msg : "none");
1437 : }
1438 :
1439 : /* Check the cache now */
1440 0 : sysdb = state->dom->sysdb;
1441 0 : if (sysdb == NULL) {
1442 0 : DEBUG(SSSDBG_FATAL_FAILURE,
1443 : "Sysdb CTX not found for [%s]!\n", state->dom->name);
1444 0 : ret = EINVAL;
1445 0 : goto done;
1446 : }
1447 :
1448 0 : ret = sysdb_enumservent(state, state->dom, &state->res);
1449 : /* Whatever the result, we're done, so report it */
1450 :
1451 : done:
1452 0 : if (ret == EOK) {
1453 0 : tevent_req_done(req);
1454 : } else {
1455 0 : tevent_req_error(req, ret);
1456 : }
1457 0 : }
1458 :
1459 : static errno_t
1460 0 : lookup_servent_recv(TALLOC_CTX *mem_ctx,
1461 : struct tevent_req *req,
1462 : struct ldb_result **res)
1463 : {
1464 0 : struct lookup_servent_ctx *state =
1465 0 : tevent_req_data(req, struct lookup_servent_ctx);
1466 :
1467 0 : TEVENT_REQ_RETURN_ON_ERROR(req);
1468 :
1469 0 : *res = talloc_steal(mem_ctx, state->res);
1470 0 : return EOK;
1471 : }
1472 :
1473 : static void
1474 0 : setservent_step_done(struct tevent_req *req)
1475 : {
1476 : errno_t ret;
1477 0 : struct ldb_result *res = NULL;
1478 0 : struct setent_step_ctx *step_ctx =
1479 0 : tevent_req_callback_data(req, struct setent_step_ctx);
1480 0 : struct nss_dom_ctx *dctx = step_ctx->dctx;
1481 0 : struct getent_ctx *svcctx = step_ctx->getent_ctx;
1482 :
1483 :
1484 0 : ret = lookup_servent_recv(step_ctx, req, &res);
1485 0 : talloc_zfree(req);
1486 0 : if (ret == ENOENT) {
1487 0 : DEBUG(SSSDBG_TRACE_FUNC,
1488 : "Domain [%s] returned no results\n", dctx->domain->name);
1489 0 : } else if (ret != EOK) {
1490 0 : DEBUG(SSSDBG_CRIT_FAILURE,
1491 : "Error [%s] while retrieving info from domain [%s]. "
1492 : "Skipping.\n", strerror(ret), dctx->domain->name);
1493 : /* Continue on */
1494 : } else {
1495 : /* Got some results
1496 : * Add the retrieved results to the list
1497 : */
1498 0 : svcctx->doms[svcctx->num].domain = dctx->domain;
1499 0 : svcctx->doms[svcctx->num].res = talloc_steal(svcctx->doms, res);
1500 0 : svcctx->num++;
1501 : }
1502 :
1503 0 : step_ctx->dctx->domain = get_next_domain(step_ctx->dctx->domain, false);
1504 :
1505 0 : while (step_ctx->dctx->domain) {
1506 : /* There are more domains to check */
1507 0 : ret = setservent_step(step_ctx);
1508 0 : if (ret == EOK) {
1509 : /* Re-enter the mainloop */
1510 0 : return;
1511 : }
1512 :
1513 0 : DEBUG(SSSDBG_CRIT_FAILURE,
1514 : "Error [%s] requesting info from domain [%s]. Skipping.\n",
1515 : strerror(ret), step_ctx->dctx->domain->name);
1516 :
1517 0 : step_ctx->dctx->domain = get_next_domain(step_ctx->dctx->domain, false);
1518 : }
1519 :
1520 : /* All domains have been checked */
1521 0 : setservent_finalize(step_ctx);
1522 : }
1523 :
1524 : static void
1525 : setservent_result_timeout(struct tevent_context *ev,
1526 : struct tevent_timer *te,
1527 : struct timeval current_time,
1528 : void *pvt);
1529 :
1530 : static void
1531 0 : setservent_finalize(struct setent_step_ctx *step_ctx)
1532 : {
1533 0 : struct nss_ctx *nctx = step_ctx->nctx;
1534 0 : struct resp_ctx *rctx = step_ctx->rctx;
1535 : struct timeval tv;
1536 : struct tevent_timer *te;
1537 :
1538 : /* We've finished all our lookups
1539 : * The result object is now safe to read.
1540 : */
1541 0 : nctx->svcctx->ready = true;
1542 :
1543 : /* Set up a lifetime timer for this result object
1544 : * We don't want this result object to outlive the
1545 : * enum cache refresh timeout
1546 : */
1547 0 : tv = tevent_timeval_current_ofs(nctx->enum_cache_timeout, 0);
1548 0 : te = tevent_add_timer(rctx->ev, nctx->svcctx, tv,
1549 : setservent_result_timeout, nctx);
1550 0 : if (!te) {
1551 0 : DEBUG(SSSDBG_CRIT_FAILURE,
1552 : "Could not set up life timer for setservent result object. "
1553 : "Entries may become stale.\n");
1554 : }
1555 :
1556 0 : nss_setent_notify_done(nctx->svcctx);
1557 0 : }
1558 :
1559 : static void
1560 0 : setservent_result_timeout(struct tevent_context *ev,
1561 : struct tevent_timer *te,
1562 : struct timeval current_time,
1563 : void *pvt)
1564 : {
1565 0 : struct nss_ctx *nctx = talloc_get_type(pvt, struct nss_ctx);
1566 :
1567 0 : DEBUG(SSSDBG_TRACE_FUNC,
1568 : "setservent result object has expired. Cleaning up.\n");
1569 :
1570 : /* Free the service enumeration context.
1571 : * If additional getservent requests come in, they will invoke
1572 : * an implicit setservent and refresh the result object.
1573 : */
1574 0 : talloc_zfree(nctx->svcctx);
1575 0 : }
1576 :
1577 : static errno_t
1578 0 : setservent_recv(struct tevent_req *req)
1579 : {
1580 0 : TEVENT_REQ_RETURN_ON_ERROR(req);
1581 0 : return EOK;
1582 : }
1583 :
1584 : static void
1585 : nss_cmd_setservent_done(struct tevent_req *req);
1586 :
1587 : int
1588 0 : nss_cmd_setservent(struct cli_ctx *cctx)
1589 : {
1590 : struct nss_cmd_ctx *cmdctx;
1591 : struct tevent_req *req;
1592 0 : errno_t ret = EOK;
1593 :
1594 0 : cmdctx = talloc_zero(cctx, struct nss_cmd_ctx);
1595 0 : if (!cmdctx) {
1596 0 : return ENOMEM;
1597 : }
1598 0 : cmdctx->cctx = cctx;
1599 :
1600 0 : req = setservent_send(cmdctx, cctx);
1601 0 : if (!req) {
1602 0 : DEBUG(SSSDBG_CRIT_FAILURE,
1603 : "Fatal error calling nss_cmd_setservent_send\n");
1604 0 : ret = EIO;
1605 0 : goto done;
1606 : }
1607 0 : tevent_req_set_callback(req, nss_cmd_setservent_done, cmdctx);
1608 :
1609 : done:
1610 0 : return nss_cmd_done(cmdctx, ret);
1611 : }
1612 :
1613 : static void
1614 0 : nss_cmd_setservent_done(struct tevent_req *req)
1615 : {
1616 : errno_t ret;
1617 0 : struct nss_cmd_ctx *cmdctx =
1618 0 : tevent_req_callback_data(req, struct nss_cmd_ctx);
1619 :
1620 0 : ret = setservent_recv(req);
1621 0 : talloc_zfree(req);
1622 0 : if (ret == EOK || ret == ENOENT) {
1623 : /* Either we succeeded or no domains
1624 : * were eligible.
1625 : * Return an acknowledgment
1626 : */
1627 0 : ret = sss_packet_new(cmdctx->cctx->creq, 0,
1628 0 : sss_packet_get_cmd(cmdctx->cctx->creq->in),
1629 0 : &cmdctx->cctx->creq->out);
1630 0 : if (ret == EOK) {
1631 0 : sss_cmd_done(cmdctx->cctx, cmdctx);
1632 0 : return;
1633 : }
1634 : }
1635 :
1636 : /* Something bad happened.
1637 : * Return an error
1638 : */
1639 0 : nss_cmd_done(cmdctx, ret);
1640 : }
1641 :
1642 : static void
1643 : nss_cmd_implicit_setservent_done(struct tevent_req *req);
1644 :
1645 : static errno_t
1646 : nss_cmd_getservent_immediate(struct nss_cmd_ctx *cmdctx);
1647 :
1648 : static errno_t
1649 : retservent(struct cli_ctx *cctx, int num);
1650 :
1651 0 : int nss_cmd_getservent(struct cli_ctx *cctx)
1652 : {
1653 : struct nss_ctx *nctx;
1654 : struct nss_cmd_ctx *cmdctx;
1655 : struct tevent_req *req;
1656 :
1657 0 : DEBUG(SSSDBG_TRACE_FUNC,
1658 : "Requesting info for all services\n");
1659 :
1660 0 : cmdctx = talloc_zero(cctx, struct nss_cmd_ctx);
1661 0 : if (!cmdctx) {
1662 0 : return ENOMEM;
1663 : }
1664 0 : cmdctx->cctx = cctx;
1665 :
1666 : /* Save the current index and cursor locations
1667 : * If we end up calling setservent implicitly, because the response object
1668 : * expired and has to be recreated, we want to resume from the same
1669 : * location.
1670 : */
1671 0 : cmdctx->saved_dom_idx = cctx->svc_dom_idx;
1672 0 : cmdctx->saved_cur = cctx->svcent_cur;
1673 :
1674 0 : nctx = talloc_get_type(cctx->rctx->pvt_ctx, struct nss_ctx);
1675 0 : if(!nctx->svcctx || !nctx->svcctx->ready) {
1676 : /* Make sure we invoke setservent if it hasn't been run or is still
1677 : * processing from another client
1678 : */
1679 0 : req = setservent_send(cmdctx, cctx);
1680 0 : if (!req) {
1681 0 : return EIO;
1682 : }
1683 0 : tevent_req_set_callback(req,
1684 : nss_cmd_implicit_setservent_done,
1685 : cmdctx);
1686 0 : return EOK;
1687 : }
1688 :
1689 0 : return nss_cmd_getservent_immediate(cmdctx);
1690 : }
1691 :
1692 : static void
1693 0 : nss_cmd_implicit_setservent_done(struct tevent_req *req)
1694 : {
1695 : errno_t ret;
1696 0 : struct nss_cmd_ctx *cmdctx =
1697 0 : tevent_req_callback_data(req, struct nss_cmd_ctx);
1698 :
1699 0 : ret = setservent_recv(req);
1700 0 : talloc_zfree(req);
1701 :
1702 : /* ENOENT is acceptable, as it just means that there were no entries
1703 : * to be returned. This will be handled gracefully in retservent
1704 : * later.
1705 : */
1706 0 : if (ret != EOK && ret != ENOENT) {
1707 0 : DEBUG(SSSDBG_CRIT_FAILURE,
1708 : "Implicit setservent failed with unexpected error [%d][%s]\n",
1709 : ret, strerror(ret));
1710 0 : NSS_CMD_FATAL_ERROR(cmdctx);
1711 : }
1712 :
1713 : /* Restore the saved index and cursor locations */
1714 0 : cmdctx->cctx->svc_dom_idx = cmdctx->saved_dom_idx;
1715 0 : cmdctx->cctx->svcent_cur = cmdctx->saved_cur;
1716 :
1717 0 : ret = nss_cmd_getservent_immediate(cmdctx);
1718 0 : if (ret != EOK) {
1719 0 : DEBUG(SSSDBG_CRIT_FAILURE,
1720 : "Immediate retrieval failed with unexpected error "
1721 : "[%d][%s]\n", ret, strerror(ret));
1722 0 : NSS_CMD_FATAL_ERROR(cmdctx);
1723 : }
1724 : }
1725 :
1726 : static errno_t
1727 0 : nss_cmd_getservent_immediate(struct nss_cmd_ctx *cmdctx)
1728 : {
1729 0 : struct cli_ctx *cctx = cmdctx->cctx;
1730 : uint8_t *body;
1731 : size_t blen;
1732 : uint32_t num;
1733 : int ret;
1734 :
1735 : /* get max num of entries to return in one call */
1736 0 : sss_packet_get_body(cctx->creq->in, &body, &blen);
1737 0 : if (blen != sizeof(uint32_t)) {
1738 0 : return EINVAL;
1739 : }
1740 0 : SAFEALIGN_COPY_UINT32(&num, body, NULL);
1741 :
1742 : /* create response packet */
1743 0 : ret = sss_packet_new(cctx->creq, 0,
1744 0 : sss_packet_get_cmd(cctx->creq->in),
1745 0 : &cctx->creq->out);
1746 0 : if (ret != EOK) {
1747 0 : return ret;
1748 : }
1749 :
1750 0 : ret = retservent(cctx, num);
1751 :
1752 0 : sss_packet_set_error(cctx->creq->out, ret);
1753 0 : sss_cmd_done(cctx, cmdctx);
1754 :
1755 0 : return EOK;
1756 : }
1757 :
1758 : static errno_t
1759 0 : retservent(struct cli_ctx *cctx, int num)
1760 : {
1761 : struct nss_ctx *nctx;
1762 : struct getent_ctx *svcctx;
1763 0 : struct ldb_message **msgs = NULL;
1764 0 : struct dom_ctx *pdom = NULL;
1765 0 : unsigned int n = 0;
1766 0 : int ret = ENOENT;
1767 :
1768 0 : nctx = talloc_get_type(cctx->rctx->pvt_ctx, struct nss_ctx);
1769 0 : if (!nctx->svcctx) goto none;
1770 :
1771 0 : svcctx = nctx->svcctx;
1772 :
1773 0 : while (ret == ENOENT) {
1774 0 : if (cctx->svc_dom_idx >= svcctx->num) break;
1775 :
1776 0 : pdom = &svcctx->doms[cctx->svc_dom_idx];
1777 :
1778 0 : n = pdom->res->count - cctx->svcent_cur;
1779 0 : if (n <= 0 && (cctx->svc_dom_idx+1 < svcctx->num)) {
1780 0 : cctx->svc_dom_idx++;
1781 0 : pdom = &svcctx->doms[cctx->svc_dom_idx];
1782 0 : n = pdom->res->count;
1783 0 : cctx->svcent_cur = 0;
1784 : }
1785 :
1786 0 : if (!n) break;
1787 :
1788 0 : if (n > num) n = num;
1789 :
1790 0 : msgs = &(pdom->res->msgs[cctx->svcent_cur]);
1791 :
1792 0 : ret = fill_service(cctx->creq->out,
1793 : pdom->domain,
1794 : NULL, msgs,
1795 : &n);
1796 :
1797 0 : cctx->svcent_cur += n;
1798 : }
1799 :
1800 : none:
1801 0 : if (ret == ENOENT) {
1802 0 : ret = sss_cmd_empty_packet(cctx->creq->out);
1803 : }
1804 0 : return ret;
1805 : }
1806 :
1807 0 : int nss_cmd_endservent(struct cli_ctx *cctx)
1808 : {
1809 : struct nss_ctx *nctx;
1810 : int ret;
1811 :
1812 0 : DEBUG(SSSDBG_TRACE_FUNC,
1813 : "Terminating request info for all accounts\n");
1814 :
1815 0 : nctx = talloc_get_type(cctx->rctx->pvt_ctx, struct nss_ctx);
1816 :
1817 : /* create response packet */
1818 0 : ret = sss_packet_new(cctx->creq, 0,
1819 0 : sss_packet_get_cmd(cctx->creq->in),
1820 0 : &cctx->creq->out);
1821 :
1822 0 : if (ret != EOK) {
1823 0 : return ret;
1824 : }
1825 0 : if (nctx->svcctx == NULL) goto done;
1826 :
1827 : /* Reset the indices so that subsequent requests start at zero */
1828 0 : cctx->svc_dom_idx = 0;
1829 0 : cctx->svcent_cur = 0;
1830 :
1831 : done:
1832 0 : sss_cmd_done(cctx, NULL);
1833 0 : return EOK;
1834 : }
|