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