Line data Source code
1 : /*
2 : SSSD
3 :
4 : NSS Responder
5 :
6 : Copyright (C) Simo Sorce <ssorce@redhat.com> 2008
7 :
8 : This program is free software; you can redistribute it and/or modify
9 : it under the terms of the GNU General Public License as published by
10 : the Free Software Foundation; either version 3 of the License, or
11 : (at your option) any later version.
12 :
13 : This program is distributed in the hope that it will be useful,
14 : but WITHOUT ANY WARRANTY; without even the implied warranty of
15 : MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
16 : GNU General Public License for more details.
17 :
18 : You should have received a copy of the GNU General Public License
19 : along with this program. If not, see <http://www.gnu.org/licenses/>.
20 : */
21 :
22 : #include "util/util.h"
23 : #include "util/sss_nss.h"
24 : #include "util/sss_cli_cmd.h"
25 : #include "responder/nss/nsssrv.h"
26 : #include "responder/nss/nsssrv_private.h"
27 : #include "responder/nss/nsssrv_netgroup.h"
28 : #include "responder/nss/nsssrv_services.h"
29 : #include "responder/nss/nsssrv_mmap_cache.h"
30 : #include "responder/common/negcache.h"
31 : #include "providers/data_provider.h"
32 : #include "confdb/confdb.h"
33 : #include "db/sysdb.h"
34 : #include "sss_client/idmap/sss_nss_idmap.h"
35 : #include <time.h>
36 :
37 4 : static int nss_cmd_send_error(struct nss_cmd_ctx *cmdctx, int err)
38 : {
39 4 : return sss_cmd_send_error(cmdctx->cctx, err);
40 : }
41 :
42 12 : static int nss_cmd_send_empty(struct nss_cmd_ctx *cmdctx)
43 : {
44 12 : struct cli_ctx *cctx = cmdctx->cctx;
45 12 : return sss_cmd_send_empty(cctx, cmdctx);
46 : }
47 :
48 105 : int nss_cmd_done(struct nss_cmd_ctx *cmdctx, int ret)
49 : {
50 105 : switch (ret) {
51 : case EOK:
52 : /* all fine, just return here */
53 37 : break;
54 :
55 : case ENOENT:
56 12 : ret = nss_cmd_send_empty(cmdctx);
57 12 : if (ret) {
58 0 : return EFAULT;
59 : }
60 12 : break;
61 :
62 : case EAGAIN:
63 : /* async processing, just return here */
64 52 : break;
65 :
66 : case EFAULT:
67 : /* very bad error */
68 0 : return EFAULT;
69 :
70 : default:
71 4 : ret = nss_cmd_send_error(cmdctx, ret);
72 4 : if (ret) {
73 0 : return EFAULT;
74 : }
75 4 : sss_cmd_done(cmdctx->cctx, cmdctx);
76 4 : break;
77 : }
78 :
79 105 : return EOK;
80 : }
81 :
82 : /***************************
83 : * Enumeration procedures *
84 : ***************************/
85 0 : errno_t nss_setent_add_ref(TALLOC_CTX *memctx,
86 : struct getent_ctx *getent_ctx,
87 : struct tevent_req *req)
88 : {
89 0 : return setent_add_ref(memctx, getent_ctx, &getent_ctx->reqs, req);
90 : }
91 :
92 0 : void nss_setent_notify_error(struct getent_ctx *getent_ctx, errno_t ret)
93 : {
94 0 : return setent_notify(&getent_ctx->reqs, ret);
95 : }
96 :
97 0 : void nss_setent_notify_done(struct getent_ctx *getent_ctx)
98 : {
99 0 : return setent_notify_done(&getent_ctx->reqs);
100 : }
101 :
102 : struct setent_ctx {
103 : struct cli_ctx *client;
104 : struct nss_ctx *nctx;
105 : struct nss_dom_ctx *dctx;
106 : struct getent_ctx *getent_ctx;
107 : };
108 :
109 32 : static int nss_reset_negcache(struct resp_ctx *rctx)
110 : {
111 : struct nss_ctx *nss_ctx;
112 :
113 32 : nss_ctx = talloc_get_type(rctx->pvt_ctx, struct nss_ctx);
114 32 : if (nss_ctx == NULL) {
115 0 : return EIO;
116 : }
117 :
118 32 : return sss_ncache_reset_repopulate_permanent(rctx, nss_ctx->ncache);
119 : }
120 :
121 : /****************************************************************************
122 : * PASSWD db related functions
123 : ***************************************************************************/
124 :
125 0 : void nss_update_pw_memcache(struct nss_ctx *nctx)
126 : {
127 : struct sss_domain_info *dom;
128 : struct ldb_result *res;
129 : uint64_t exp;
130 : struct sized_string key;
131 : const char *id;
132 : time_t now;
133 : int ret;
134 : int i;
135 :
136 0 : now = time(NULL);
137 :
138 0 : for (dom = nctx->rctx->domains; dom; dom = get_next_domain(dom, false)) {
139 0 : ret = sysdb_enumpwent_with_views(nctx, dom, &res);
140 0 : if (ret != EOK) {
141 0 : DEBUG(SSSDBG_CRIT_FAILURE,
142 : "Failed to enumerate users for domain [%s]\n", dom->name);
143 0 : continue;
144 : }
145 :
146 0 : for (i = 0; i < res->count; i++) {
147 0 : exp = ldb_msg_find_attr_as_uint64(res->msgs[i],
148 : SYSDB_CACHE_EXPIRE, 0);
149 0 : if (exp >= now) {
150 0 : continue;
151 : }
152 :
153 : /* names require more manipulation (build up fqname conditionally),
154 : * but uidNumber is unique and always resolvable too, so we use
155 : * that to update the cache, as it points to the same entry */
156 0 : id = sss_view_ldb_msg_find_attr_as_string(dom, res->msgs[i],
157 : SYSDB_UIDNUM, NULL);
158 0 : if (!id) {
159 0 : DEBUG(SSSDBG_CRIT_FAILURE,
160 : "Failed to find uidNumber in %s.\n",
161 : ldb_dn_get_linearized(res->msgs[i]->dn));
162 0 : continue;
163 : }
164 0 : to_sized_string(&key, id);
165 :
166 0 : ret = sss_mmap_cache_pw_invalidate(nctx->pwd_mc_ctx, &key);
167 0 : if (ret != EOK && ret != ENOENT) {
168 0 : DEBUG(SSSDBG_CRIT_FAILURE,
169 : "Internal failure in memory cache code: %d [%s]\n",
170 : ret, strerror(ret));
171 : }
172 :
173 0 : ret = sss_mmap_cache_pw_invalidate(nctx->initgr_mc_ctx, &key);
174 0 : if (ret != EOK && ret != ENOENT) {
175 0 : DEBUG(SSSDBG_CRIT_FAILURE,
176 : "Internal failure in memory cache code: %d [%s]\n",
177 : ret, strerror(ret));
178 : }
179 : }
180 :
181 0 : talloc_zfree(res);
182 : }
183 0 : }
184 :
185 12 : static gid_t get_gid_override(struct ldb_message *msg,
186 : struct sss_domain_info *dom)
187 : {
188 24 : return dom->override_gid ?
189 : dom->override_gid :
190 12 : ldb_msg_find_attr_as_uint64(msg, SYSDB_GIDNUM, 0);
191 : }
192 :
193 12 : static const char *get_homedir_override(TALLOC_CTX *mem_ctx,
194 : struct ldb_message *msg,
195 : struct nss_ctx *nctx,
196 : struct sss_domain_info *dom,
197 : struct sss_nss_homedir_ctx *homedir_ctx)
198 : {
199 : const char *homedir;
200 12 : const char *orig_name = homedir_ctx->username;
201 : errno_t ret;
202 :
203 12 : homedir = sss_view_ldb_msg_find_attr_as_string(dom, msg, SYSDB_HOMEDIR,
204 : NULL);
205 12 : homedir_ctx->original = homedir;
206 :
207 : /* Subdomain users store FQDN in their name attribute */
208 12 : ret = sss_parse_name_const(mem_ctx, dom->names, orig_name,
209 : NULL, &homedir_ctx->username);
210 12 : if (ret != EOK) {
211 0 : DEBUG(SSSDBG_MINOR_FAILURE, "Could not parse [%s] into "
212 : "name-value components.\n", orig_name);
213 0 : return NULL;
214 : }
215 :
216 : /* Check to see which homedir_prefix to use. */
217 12 : if (dom->homedir_substr != NULL) {
218 0 : homedir_ctx->config_homedir_substr = dom->homedir_substr;
219 12 : } else if (nctx->homedir_substr != NULL) {
220 0 : homedir_ctx->config_homedir_substr = nctx->homedir_substr;
221 : }
222 :
223 : /* Check whether we are unconditionally overriding the server
224 : * for home directory locations.
225 : */
226 12 : if (dom->override_homedir) {
227 0 : return expand_homedir_template(mem_ctx, dom->override_homedir,
228 : homedir_ctx);
229 12 : } else if (nctx->override_homedir) {
230 0 : return expand_homedir_template(mem_ctx, nctx->override_homedir,
231 : homedir_ctx);
232 : }
233 :
234 12 : if (!homedir || *homedir == '\0') {
235 : /* In the case of a NULL or empty homedir, check to see if
236 : * we have a fallback homedir to use.
237 : */
238 0 : if (dom->fallback_homedir) {
239 0 : return expand_homedir_template(mem_ctx, dom->fallback_homedir,
240 : homedir_ctx);
241 0 : } else if (nctx->fallback_homedir) {
242 0 : return expand_homedir_template(mem_ctx, nctx->fallback_homedir,
243 : homedir_ctx);
244 : }
245 : }
246 :
247 : /* Provider can also return template, try to expand it.*/
248 12 : return expand_homedir_template(mem_ctx, homedir, homedir_ctx);
249 : }
250 :
251 12 : static const char *get_shell_override(TALLOC_CTX *mem_ctx,
252 : struct ldb_message *msg,
253 : struct nss_ctx *nctx,
254 : struct sss_domain_info *dom)
255 : {
256 : const char *user_shell;
257 : int i;
258 :
259 : /* Check whether we are unconditionally overriding the server
260 : * for the login shell.
261 : */
262 12 : if (dom->override_shell) {
263 0 : return dom->override_shell;
264 12 : } else if (nctx->override_shell) {
265 0 : return nctx->override_shell;
266 : }
267 :
268 12 : user_shell = sss_view_ldb_msg_find_attr_as_string(dom, msg, SYSDB_SHELL,
269 : NULL);
270 12 : if (!user_shell) {
271 : /* Check whether there is a default shell specified */
272 0 : if (dom->default_shell) {
273 0 : return talloc_strdup(mem_ctx, dom->default_shell);
274 0 : } else if (nctx->default_shell) {
275 0 : return talloc_strdup(mem_ctx, nctx->default_shell);
276 : }
277 0 : return NULL;
278 : }
279 12 : if (!nctx->allowed_shells && !nctx->vetoed_shells) return talloc_strdup(mem_ctx, user_shell);
280 :
281 0 : if (nctx->vetoed_shells) {
282 0 : for (i=0; nctx->vetoed_shells[i]; i++) {
283 0 : if (strcmp(nctx->vetoed_shells[i], user_shell) == 0) {
284 0 : DEBUG(SSSDBG_FUNC_DATA, "The shell '%s' is vetoed. "
285 : "Using fallback\n", user_shell);
286 0 : return talloc_strdup(mem_ctx, nctx->shell_fallback);
287 : }
288 : }
289 : }
290 :
291 0 : if (nctx->etc_shells) {
292 0 : for (i=0; nctx->etc_shells[i]; i++) {
293 0 : if (strcmp(user_shell, nctx->etc_shells[i]) == 0) {
294 0 : DEBUG(SSSDBG_TRACE_ALL, "Shell %s found in /etc/shells\n",
295 : nctx->etc_shells[i]);
296 0 : break;
297 : }
298 : }
299 :
300 0 : if (nctx->etc_shells[i]) {
301 0 : DEBUG(SSSDBG_TRACE_ALL, "Using original shell '%s'\n", user_shell);
302 0 : return talloc_strdup(mem_ctx, user_shell);
303 : }
304 : }
305 :
306 0 : if (nctx->allowed_shells) {
307 0 : if (strcmp(nctx->allowed_shells[0], "*") == 0) {
308 0 : DEBUG(SSSDBG_FUNC_DATA,
309 : "The shell '%s' is allowed but does not exist. "
310 : "Using fallback\n", user_shell);
311 0 : return talloc_strdup(mem_ctx, nctx->shell_fallback);
312 : } else {
313 0 : for (i=0; nctx->allowed_shells[i]; i++) {
314 0 : if (strcmp(nctx->allowed_shells[i], user_shell) == 0) {
315 0 : DEBUG(SSSDBG_FUNC_DATA,
316 : "The shell '%s' is allowed but does not exist. "
317 : "Using fallback\n", user_shell);
318 0 : return talloc_strdup(mem_ctx, nctx->shell_fallback);
319 : }
320 : }
321 : }
322 : }
323 :
324 0 : DEBUG(SSSDBG_FUNC_DATA,
325 : "The shell '%s' is not allowed and does not exist.\n",
326 : user_shell);
327 0 : return talloc_strdup(mem_ctx, NOLOGIN_SHELL);
328 : }
329 :
330 12 : static int fill_pwent(struct sss_packet *packet,
331 : struct sss_domain_info *dom,
332 : struct nss_ctx *nctx,
333 : bool filter_users, bool pw_mmap_cache,
334 : struct ldb_message **msgs,
335 : int *count)
336 : {
337 : struct ldb_message *msg;
338 : uint8_t *body;
339 : const char *upn;
340 : const char *tmpstr;
341 : const char *orig_name;
342 : struct sized_string name;
343 : struct sized_string gecos;
344 : struct sized_string homedir;
345 : struct sized_string shell;
346 : struct sized_string pwfield;
347 : struct sized_string fullname;
348 : uint32_t uid;
349 : uint32_t gid;
350 : size_t rsize, rp, blen;
351 12 : int fq_len = 0;
352 : int i, ret, num;
353 12 : bool add_domain = (!IS_SUBDOMAIN(dom) && dom->fqnames);
354 12 : const char *domain = dom->name;
355 12 : bool packet_initialized = false;
356 : int ncret;
357 12 : TALLOC_CTX *tmp_ctx = NULL;
358 : struct sss_nss_homedir_ctx homedir_ctx;
359 :
360 12 : to_sized_string(&pwfield, nctx->pwfield);
361 :
362 12 : rp = 2*sizeof(uint32_t);
363 :
364 12 : num = 0;
365 24 : for (i = 0; i < *count; i++) {
366 12 : talloc_zfree(tmp_ctx);
367 12 : tmp_ctx = talloc_new(NULL);
368 :
369 12 : msg = msgs[i];
370 :
371 12 : upn = ldb_msg_find_attr_as_string(msg, SYSDB_UPN, NULL);
372 :
373 12 : if (DOM_HAS_VIEWS(dom)) {
374 0 : orig_name = ldb_msg_find_attr_as_string(msg,
375 : OVERRIDE_PREFIX SYSDB_NAME,
376 : NULL);
377 0 : if (orig_name != NULL && IS_SUBDOMAIN(dom)) {
378 : /* Override names are not fully qualified */
379 0 : add_domain = true;
380 : }
381 :
382 0 : gid = ldb_msg_find_attr_as_uint64(msg,
383 : OVERRIDE_PREFIX SYSDB_GIDNUM, 0);
384 : } else {
385 12 : orig_name = NULL;
386 12 : gid = 0;
387 : }
388 :
389 12 : if (orig_name == NULL) {
390 12 : orig_name = ldb_msg_find_attr_as_string(msg,
391 : SYSDB_DEFAULT_OVERRIDE_NAME,
392 : NULL);
393 12 : if (orig_name == NULL) {
394 12 : orig_name = ldb_msg_find_attr_as_string(msg, SYSDB_NAME, NULL);
395 : }
396 : }
397 :
398 12 : uid = sss_view_ldb_msg_find_attr_as_uint64(dom, msg, SYSDB_UIDNUM, 0);
399 :
400 12 : if (gid == 0) {
401 12 : gid = get_gid_override(msg, dom);
402 : }
403 :
404 12 : if (!orig_name || !uid || !gid) {
405 0 : DEBUG(SSSDBG_OP_FAILURE, "Incomplete user object for %s[%llu]! Skipping\n",
406 : orig_name?orig_name:"<NULL>", (unsigned long long int)uid);
407 0 : continue;
408 : }
409 :
410 12 : if (filter_users) {
411 3 : ncret = sss_ncache_check_user(nctx->ncache,
412 : nctx->neg_timeout,
413 : dom, orig_name);
414 3 : if (ncret == EEXIST) {
415 0 : DEBUG(SSSDBG_TRACE_FUNC,
416 : "User [%s@%s] filtered out! (negative cache)\n",
417 : orig_name, domain);
418 0 : continue;
419 : }
420 : }
421 :
422 12 : if (!packet_initialized) {
423 : /* first 2 fields (len and reserved), filled up later */
424 12 : ret = sss_packet_grow(packet, 2*sizeof(uint32_t));
425 12 : if (ret != EOK) return ret;
426 12 : packet_initialized = true;
427 : }
428 :
429 12 : tmpstr = sss_get_cased_name(tmp_ctx, orig_name, dom->case_preserve);
430 12 : if (tmpstr == NULL) {
431 0 : DEBUG(SSSDBG_CRIT_FAILURE,
432 : "sss_get_cased_name failed, skipping\n");
433 0 : continue;
434 : }
435 :
436 12 : tmpstr = sss_replace_space(tmp_ctx, tmpstr,
437 12 : nctx->rctx->override_space);
438 12 : if (tmpstr == NULL) {
439 0 : DEBUG(SSSDBG_CRIT_FAILURE,
440 : "sss_replace_space failed, skipping\n");
441 0 : continue;
442 : }
443 :
444 12 : to_sized_string(&name, tmpstr);
445 :
446 12 : tmpstr = sss_view_ldb_msg_find_attr_as_string(dom, msg, SYSDB_GECOS,
447 : NULL);
448 12 : if (!tmpstr) {
449 0 : to_sized_string(&gecos, "");
450 : } else {
451 12 : to_sized_string(&gecos, tmpstr);
452 : }
453 :
454 12 : ZERO_STRUCT(homedir_ctx);
455 :
456 12 : homedir_ctx.username = name.str;
457 12 : homedir_ctx.uid = uid;
458 12 : homedir_ctx.domain = dom->name;
459 12 : homedir_ctx.upn = upn;
460 :
461 12 : tmpstr = get_homedir_override(tmp_ctx, msg, nctx, dom, &homedir_ctx);
462 12 : if (!tmpstr) {
463 0 : to_sized_string(&homedir, "/");
464 : } else {
465 12 : to_sized_string(&homedir, tmpstr);
466 : }
467 :
468 12 : tmpstr = get_shell_override(tmp_ctx, msg, nctx, dom);
469 12 : if (!tmpstr) {
470 0 : to_sized_string(&shell, "");
471 : } else {
472 12 : to_sized_string(&shell, tmpstr);
473 : }
474 :
475 36 : rsize = 2 * sizeof(uint32_t) + name.len + gecos.len +
476 24 : homedir.len + shell.len + pwfield.len;
477 :
478 12 : if (add_domain) {
479 2 : fq_len = sss_fqname(NULL, 0, dom->names, dom, name.str);
480 2 : if (fq_len >= 0) {
481 2 : fq_len += 1;
482 2 : rsize -= name.len;
483 2 : rsize += fq_len;
484 : } else {
485 0 : fq_len = 0;
486 : }
487 : }
488 :
489 12 : ret = sss_packet_grow(packet, rsize);
490 12 : if (ret != EOK) {
491 0 : num = 0;
492 0 : goto done;
493 : }
494 12 : sss_packet_get_body(packet, &body, &blen);
495 :
496 12 : SAFEALIGN_SET_UINT32(&body[rp], uid, &rp);
497 12 : SAFEALIGN_SET_UINT32(&body[rp], gid, &rp);
498 :
499 12 : if (add_domain) {
500 2 : ret = sss_fqname((char *) &body[rp], fq_len, dom->names, dom, name.str);
501 2 : if (ret < 0 || ret != fq_len - 1) {
502 0 : DEBUG(SSSDBG_CRIT_FAILURE,
503 : "Failed to generate a fully qualified name for user "
504 : "[%s] in [%s]! Skipping user.\n", name.str, domain);
505 0 : continue;
506 : }
507 : } else {
508 10 : memcpy(&body[rp], name.str, name.len);
509 : }
510 12 : to_sized_string(&fullname, (const char *)&body[rp]);
511 12 : rp += fullname.len;
512 :
513 12 : memcpy(&body[rp], pwfield.str, pwfield.len);
514 12 : rp += pwfield.len;
515 12 : memcpy(&body[rp], gecos.str, gecos.len);
516 12 : rp += gecos.len;
517 12 : memcpy(&body[rp], homedir.str, homedir.len);
518 12 : rp += homedir.len;
519 12 : memcpy(&body[rp], shell.str, shell.len);
520 12 : rp += shell.len;
521 :
522 12 : num++;
523 :
524 12 : if (pw_mmap_cache && nctx->pwd_mc_ctx) {
525 0 : ret = sss_mmap_cache_pw_store(&nctx->pwd_mc_ctx,
526 : &fullname, &pwfield,
527 : uid, gid,
528 : &gecos, &homedir, &shell);
529 0 : if (ret != EOK && ret != ENOMEM) {
530 0 : DEBUG(SSSDBG_CRIT_FAILURE,
531 : "Failed to store user %s(%s) in mmap cache!\n",
532 : name.str, domain);
533 : }
534 : }
535 : }
536 12 : talloc_zfree(tmp_ctx);
537 :
538 : done:
539 12 : *count = i;
540 :
541 : /* if there are no results just return ENOENT,
542 : * let the caller decide if this is the last packet or not */
543 12 : if (!packet_initialized) return ENOENT;
544 :
545 12 : sss_packet_get_body(packet, &body, &blen);
546 12 : SAFEALIGN_COPY_UINT32(body, &num, NULL); /* num results */
547 12 : SAFEALIGN_SETMEM_UINT32(body + sizeof(uint32_t), 0, NULL); /* reserved */
548 :
549 12 : return EOK;
550 : }
551 :
552 12 : static int nss_cmd_getpw_send_reply(struct nss_dom_ctx *dctx, bool filter)
553 : {
554 12 : struct nss_cmd_ctx *cmdctx = dctx->cmdctx;
555 12 : struct cli_ctx *cctx = cmdctx->cctx;
556 : struct nss_ctx *nctx;
557 : int ret;
558 : int i;
559 :
560 12 : nctx = talloc_get_type(cctx->rctx->pvt_ctx, struct nss_ctx);
561 :
562 24 : ret = sss_packet_new(cctx->creq, 0,
563 12 : sss_packet_get_cmd(cctx->creq->in),
564 12 : &cctx->creq->out);
565 12 : if (ret != EOK) {
566 0 : return EFAULT;
567 : }
568 12 : i = dctx->res->count;
569 :
570 12 : ret = fill_pwent(cctx->creq->out,
571 : dctx->domain,
572 : nctx, filter, true,
573 12 : dctx->res->msgs, &i);
574 12 : if (ret) {
575 0 : return ret;
576 : }
577 12 : sss_packet_set_error(cctx->creq->out, EOK);
578 12 : sss_cmd_done(cctx, cmdctx);
579 12 : return EOK;
580 : }
581 :
582 : /* Currently only refreshing expired netgroups is supported. */
583 : static bool
584 28 : is_refreshed_on_bg(enum sss_dp_acct_type req_type,
585 : enum sss_dp_acct_type refresh_expired_interval)
586 : {
587 28 : if (refresh_expired_interval == 0) {
588 28 : return false;
589 : }
590 :
591 0 : switch (req_type) {
592 : case SSS_DP_NETGR:
593 0 : return true;
594 : default:
595 0 : return false;
596 : }
597 :
598 : return false;
599 : }
600 :
601 : static void nsssrv_dp_send_acct_req_done(struct tevent_req *req);
602 :
603 37 : static void get_dp_name_and_id(TALLOC_CTX *mem_ctx,
604 : struct sss_domain_info *dom,
605 : enum sss_dp_acct_type req_type,
606 : const char *opt_name,
607 : uint32_t opt_id,
608 : const char **_name,
609 : uint32_t *_id)
610 : {
611 : TALLOC_CTX *tmp_ctx;
612 37 : struct ldb_result *res = NULL;
613 : const char *attr;
614 : const char *name;
615 : uint32_t id;
616 : errno_t ret;
617 :
618 : /* First set the same values to make things easier. */
619 37 : *_name = opt_name;
620 37 : *_id = opt_id;
621 :
622 37 : if (!DOM_HAS_VIEWS(dom) || !is_local_view(dom->view_name)) {
623 37 : DEBUG(SSSDBG_TRACE_FUNC, "Not a LOCAL view, continuing with "
624 : "provided values.\n");
625 74 : return;
626 : }
627 :
628 0 : tmp_ctx = talloc_new(NULL);
629 0 : if (tmp_ctx == NULL) {
630 0 : DEBUG(SSSDBG_CRIT_FAILURE, "talloc_new() failed\n");
631 0 : return;
632 : }
633 :
634 0 : if (opt_name != NULL) {
635 0 : switch (req_type) {
636 : case SSS_DP_USER:
637 : case SSS_DP_INITGROUPS:
638 0 : ret = sysdb_getpwnam_with_views(tmp_ctx, dom, opt_name, &res);
639 0 : if (ret != EOK) {
640 0 : DEBUG(SSSDBG_CONF_SETTINGS,
641 : "sysdb_getpwnam_with_views() failed [%d]: %s\n",
642 : ret, sss_strerror(ret));
643 0 : goto done;
644 : }
645 0 : break;
646 : case SSS_DP_GROUP:
647 0 : ret = sysdb_getgrnam_with_views(tmp_ctx, dom, opt_name, &res);
648 0 : if (ret != EOK) {
649 0 : DEBUG(SSSDBG_CONF_SETTINGS,
650 : "sysdb_getgrnam_with_views() failed [%d]: %s\n",
651 : ret, sss_strerror(ret));
652 0 : goto done;
653 : }
654 0 : break;
655 : default:
656 0 : goto done;
657 : }
658 :
659 0 : if (res == NULL || res->count != 1) {
660 : /* This should not happen with LOCAL view and overridden value. */
661 0 : DEBUG(SSSDBG_TRACE_FUNC, "Entry is missing?! Continuing with "
662 : "provided values.\n");
663 0 : goto done;
664 : }
665 :
666 0 : name = ldb_msg_find_attr_as_string(res->msgs[0], SYSDB_NAME, NULL);
667 0 : if (name == NULL) {
668 0 : DEBUG(SSSDBG_CRIT_FAILURE, "Bug: name cannot be NULL\n");
669 0 : goto done;
670 : }
671 :
672 0 : *_name = talloc_steal(mem_ctx, name);
673 0 : } else if (opt_id != 0) {
674 0 : switch (req_type) {
675 : case SSS_DP_USER:
676 0 : ret = sysdb_getpwuid_with_views(tmp_ctx, dom, opt_id, &res);
677 0 : if (ret != EOK) {
678 0 : DEBUG(SSSDBG_CONF_SETTINGS,
679 : "sysdb_getpwuid_with_views() failed [%d]: %s\n",
680 : ret, sss_strerror(ret));
681 0 : goto done;
682 : }
683 :
684 0 : attr = SYSDB_UIDNUM;
685 0 : break;
686 : case SSS_DP_GROUP:
687 0 : ret = sysdb_getgrgid_with_views(tmp_ctx, dom, opt_id, &res);
688 0 : if (ret != EOK) {
689 0 : DEBUG(SSSDBG_CONF_SETTINGS,
690 : "sysdb_getgrgid_with_views() failed [%d]: %s\n",
691 : ret, sss_strerror(ret));
692 0 : goto done;
693 : }
694 :
695 0 : attr = SYSDB_GIDNUM;
696 0 : break;
697 : default:
698 0 : goto done;
699 : }
700 :
701 0 : if (res == NULL || res->count != 1) {
702 : /* This should not happen with LOCAL view and overridden value. */
703 0 : DEBUG(SSSDBG_TRACE_FUNC, "Entry is missing?! Continuing with "
704 : "provided values.\n");
705 0 : goto done;
706 : }
707 :
708 0 : id = ldb_msg_find_attr_as_uint64(res->msgs[0], attr, 0);
709 0 : if (id == 0) {
710 0 : DEBUG(SSSDBG_CRIT_FAILURE, "Bug: id cannot be 0\n");
711 0 : goto done;
712 : }
713 :
714 0 : *_id = id;
715 : }
716 :
717 : done:
718 0 : talloc_free(tmp_ctx);
719 : }
720 :
721 : /* FIXME: do not check res->count, but get in a msgs and check in parent */
722 37 : errno_t check_cache(struct nss_dom_ctx *dctx,
723 : struct nss_ctx *nctx,
724 : struct ldb_result *res,
725 : enum sss_dp_acct_type req_type,
726 : const char *opt_name,
727 : uint32_t opt_id,
728 : const char *extra,
729 : sss_dp_callback_t callback,
730 : void *pvt)
731 : {
732 : errno_t ret;
733 37 : struct nss_cmd_ctx *cmdctx = dctx->cmdctx;
734 37 : struct cli_ctx *cctx = cmdctx->cctx;
735 37 : struct tevent_req *req = NULL;
736 37 : struct dp_callback_ctx *cb_ctx = NULL;
737 37 : uint64_t cacheExpire = 0;
738 37 : const char *name = opt_name;
739 37 : uint32_t id = opt_id;
740 :
741 : /* when searching for a user or netgroup, more than one reply is a
742 : * db error
743 : */
744 55 : if ((req_type == SSS_DP_USER || req_type == SSS_DP_NETGR) &&
745 18 : (res->count > 1)) {
746 0 : DEBUG(SSSDBG_CRIT_FAILURE,
747 : "getpwXXX call returned more than one result! DB Corrupted?\n");
748 0 : return ENOENT;
749 : }
750 :
751 : /* In case of local view we have to always contant DP with the original
752 : * name or id. */
753 37 : get_dp_name_and_id(dctx->cmdctx, dctx->domain, req_type, opt_name, opt_id,
754 : &name, &id);
755 :
756 : /* if we have any reply let's check cache validity, but ignore netgroups
757 : * if refresh_expired_interval is set (which implies that another method
758 : * is used to refresh netgroups)
759 : */
760 37 : if (res->count > 0) {
761 28 : if (is_refreshed_on_bg(req_type,
762 28 : dctx->domain->refresh_expired_interval)) {
763 0 : ret = EOK;
764 : } else {
765 28 : if (req_type == SSS_DP_INITGROUPS) {
766 4 : cacheExpire = ldb_msg_find_attr_as_uint64(res->msgs[0],
767 : SYSDB_INITGR_EXPIRE,
768 : 0);
769 : } else {
770 24 : cacheExpire = ldb_msg_find_attr_as_uint64(res->msgs[0],
771 : SYSDB_CACHE_EXPIRE,
772 : 0);
773 : }
774 :
775 : /* if we have any reply let's check cache validity */
776 28 : ret = sss_cmd_check_cache(res->msgs[0],
777 : nctx->cache_refresh_percent,
778 : cacheExpire);
779 : }
780 28 : if (ret == EOK) {
781 23 : DEBUG(SSSDBG_TRACE_FUNC, "Cached entry is valid, returning..\n");
782 23 : return EOK;
783 5 : } else if (ret != EAGAIN && ret != ENOENT) {
784 0 : DEBUG(SSSDBG_CRIT_FAILURE, "Error checking cache: %d\n", ret);
785 0 : goto error;
786 : }
787 : } else {
788 : /* No replies */
789 9 : ret = ENOENT;
790 : }
791 :
792 : /* EAGAIN (off band) or ENOENT (cache miss) -> check cache */
793 14 : if (ret == EAGAIN) {
794 : /* No callback required
795 : * This was an out-of-band update. We'll return EOK
796 : * so the calling function can return the cached entry
797 : * immediately.
798 : */
799 0 : DEBUG(SSSDBG_TRACE_FUNC,
800 : "Performing midpoint cache update on [%s]\n", name);
801 :
802 0 : req = sss_dp_get_account_send(cctx, cctx->rctx, dctx->domain, true,
803 : req_type, name, id, extra);
804 0 : if (!req) {
805 0 : DEBUG(SSSDBG_CRIT_FAILURE,
806 : "Out of memory sending out-of-band data provider "
807 : "request\n");
808 : /* This is non-fatal, so we'll continue here */
809 : } else {
810 0 : DEBUG(SSSDBG_TRACE_FUNC, "Updating cache out-of-band\n");
811 : }
812 :
813 : /* We don't need to listen for a reply, so we will free the
814 : * request here.
815 : */
816 0 : talloc_zfree(req);
817 :
818 : } else {
819 : /* This is a cache miss. Or the cache is expired.
820 : * We need to get the updated user information before returning it.
821 : */
822 :
823 : /* dont loop forever :-) */
824 14 : dctx->check_provider = false;
825 :
826 : /* keep around current data in case backend is offline */
827 14 : if (res->count) {
828 5 : dctx->res = talloc_steal(dctx, res);
829 : }
830 :
831 14 : req = sss_dp_get_account_send(cctx, cctx->rctx, dctx->domain, true,
832 : req_type, name, id, extra);
833 14 : if (!req) {
834 0 : DEBUG(SSSDBG_CRIT_FAILURE,
835 : "Out of memory sending data provider request\n");
836 0 : ret = ENOMEM;
837 0 : goto error;
838 : }
839 :
840 14 : cb_ctx = talloc_zero(dctx, struct dp_callback_ctx);
841 14 : if(!cb_ctx) {
842 0 : talloc_zfree(req);
843 0 : ret = ENOMEM;
844 0 : goto error;
845 : }
846 14 : cb_ctx->callback = callback;
847 14 : cb_ctx->ptr = pvt;
848 14 : cb_ctx->cctx = dctx->cmdctx->cctx;
849 14 : cb_ctx->mem_ctx = dctx;
850 :
851 14 : tevent_req_set_callback(req, nsssrv_dp_send_acct_req_done, cb_ctx);
852 :
853 14 : return EAGAIN;
854 : }
855 :
856 0 : return EOK;
857 :
858 : error:
859 0 : ret = nss_cmd_send_error(cmdctx, ret);
860 0 : if (ret != EOK) {
861 0 : NSS_CMD_FATAL_ERROR_CODE(cctx, ret);
862 : }
863 0 : sss_cmd_done(cctx, cmdctx);
864 0 : return EOK;
865 : }
866 :
867 14 : static void nsssrv_dp_send_acct_req_done(struct tevent_req *req)
868 : {
869 14 : struct dp_callback_ctx *cb_ctx =
870 14 : tevent_req_callback_data(req, struct dp_callback_ctx);
871 :
872 : errno_t ret;
873 : dbus_uint16_t err_maj;
874 : dbus_uint32_t err_min;
875 : char *err_msg;
876 :
877 14 : ret = sss_dp_get_account_recv(cb_ctx->mem_ctx, req,
878 : &err_maj, &err_min,
879 : &err_msg);
880 14 : talloc_zfree(req);
881 14 : if (ret != EOK) {
882 0 : NSS_CMD_FATAL_ERROR(cb_ctx->cctx);
883 : }
884 :
885 14 : cb_ctx->callback(err_maj, err_min, err_msg, cb_ctx->ptr);
886 : }
887 :
888 4 : static int delete_entry_from_memcache(struct sss_domain_info *dom, char *name,
889 : struct sss_mc_ctx *mc_ctx,
890 : enum sss_mc_type type)
891 : {
892 4 : TALLOC_CTX *tmp_ctx = NULL;
893 : struct sized_string delete_name;
894 4 : char *fqdn = NULL;
895 : int ret;
896 :
897 4 : tmp_ctx = talloc_new(NULL);
898 4 : if (tmp_ctx == NULL) {
899 0 : DEBUG(SSSDBG_CRIT_FAILURE, "Out of memory.\n");
900 0 : return ENOMEM;
901 : }
902 :
903 4 : if (dom->fqnames) {
904 0 : fqdn = sss_tc_fqname(tmp_ctx, dom->names, dom, name);
905 0 : if (fqdn == NULL) {
906 0 : DEBUG(SSSDBG_CRIT_FAILURE, "Out of memory.\n");
907 0 : ret = ENOMEM;
908 0 : goto done;
909 : }
910 0 : to_sized_string(&delete_name, fqdn);
911 : } else {
912 4 : to_sized_string(&delete_name, name);
913 : }
914 :
915 4 : switch (type) {
916 : case SSS_MC_PASSWD:
917 2 : ret = sss_mmap_cache_pw_invalidate(mc_ctx, &delete_name);
918 2 : if (ret != EOK && ret != ENOENT) {
919 2 : DEBUG(SSSDBG_CRIT_FAILURE,
920 : "Internal failure in memory cache code: %d [%s]\n",
921 : ret, strerror(ret));
922 2 : goto done;
923 : }
924 0 : break;
925 : case SSS_MC_GROUP:
926 0 : ret = sss_mmap_cache_gr_invalidate(mc_ctx, &delete_name);
927 0 : if (ret != EOK && ret != ENOENT) {
928 0 : DEBUG(SSSDBG_CRIT_FAILURE,
929 : "Internal failure in memory cache code: %d [%s]\n",
930 : ret, strerror(ret));
931 0 : goto done;
932 : }
933 0 : break;
934 : case SSS_MC_INITGROUPS:
935 2 : ret = sss_mmap_cache_initgr_invalidate(mc_ctx, &delete_name);
936 2 : if (ret != EOK && ret != ENOENT) {
937 2 : DEBUG(SSSDBG_CRIT_FAILURE,
938 : "Internal failure in memory cache code: %d [%s]\n",
939 : ret, strerror(ret));
940 2 : goto done;
941 : }
942 0 : break;
943 : default:
944 0 : ret = EINVAL;
945 0 : goto done;
946 : }
947 :
948 0 : ret = EOK;
949 : done:
950 4 : talloc_free(tmp_ctx);
951 4 : return ret;
952 :
953 : }
954 :
955 : static void nss_cmd_getby_dp_callback(uint16_t err_maj, uint32_t err_min,
956 : const char *err_msg, void *ptr);
957 :
958 : /* search for a user.
959 : * Returns:
960 : * ENOENT, if user is definitely not found
961 : * EAGAIN, if user is being fetched from backend via async operations
962 : * EOK, if found
963 : * anything else on a fatal error
964 : */
965 :
966 18 : static int nss_cmd_getpwnam_search(struct nss_dom_ctx *dctx)
967 : {
968 18 : struct nss_cmd_ctx *cmdctx = dctx->cmdctx;
969 18 : struct sss_domain_info *dom = dctx->domain;
970 18 : struct cli_ctx *cctx = cmdctx->cctx;
971 18 : char *name = NULL;
972 : struct nss_ctx *nctx;
973 : int ret;
974 : static const char *user_attrs[] = SYSDB_PW_ATTRS;
975 : struct ldb_message *msg;
976 18 : const char *extra_flag = NULL;
977 :
978 18 : nctx = talloc_get_type(cctx->rctx->pvt_ctx, struct nss_ctx);
979 :
980 18 : while (dom) {
981 : /* if it is a domainless search, skip domains that require fully
982 : * qualified names instead */
983 36 : while (dom && cmdctx->check_next && dom->fqnames
984 0 : && !cmdctx->name_is_upn) {
985 0 : dom = get_next_domain(dom, false);
986 : }
987 :
988 18 : if (!dom) break;
989 :
990 18 : if (dom != dctx->domain) {
991 : /* make sure we reset the check_provider flag when we check
992 : * a new domain */
993 0 : dctx->check_provider = NEED_CHECK_PROVIDER(dom->provider);
994 : }
995 :
996 : /* make sure to update the dctx if we changed domain */
997 18 : dctx->domain = dom;
998 :
999 18 : talloc_free(name);
1000 18 : name = sss_get_cased_name(cmdctx, cmdctx->name, dom->case_sensitive);
1001 18 : if (!name) return ENOMEM;
1002 :
1003 18 : name = sss_reverse_replace_space(dctx, name,
1004 18 : nctx->rctx->override_space);
1005 18 : if (name == NULL) {
1006 0 : DEBUG(SSSDBG_CRIT_FAILURE,
1007 : "sss_reverse_replace_space failed\n");
1008 0 : return ENOMEM;
1009 : }
1010 :
1011 : /* verify this user has not yet been negatively cached,
1012 : * or has been permanently filtered */
1013 18 : ret = sss_ncache_check_user(nctx->ncache, nctx->neg_timeout,
1014 : dom, name);
1015 :
1016 : /* if neg cached, return we didn't find it */
1017 18 : if (ret == EEXIST) {
1018 3 : DEBUG(SSSDBG_TRACE_FUNC,
1019 : "User [%s] does not exist in [%s]! (negative cache)\n",
1020 : name, dom->name);
1021 : /* if a multidomain search, try with next */
1022 3 : if (cmdctx->check_next) {
1023 3 : if (cmdctx->name_is_upn) {
1024 2 : dom = get_next_domain(dom, true);
1025 : } else {
1026 1 : dom = get_next_domain(dom, false);
1027 : }
1028 3 : continue;
1029 : }
1030 : /* There are no further domains or this was a
1031 : * fully-qualified user request.
1032 : */
1033 0 : return ENOENT;
1034 : }
1035 :
1036 15 : DEBUG(SSSDBG_CONF_SETTINGS,
1037 : "Requesting info for [%s@%s]\n", name, dom->name);
1038 :
1039 15 : if (dom->sysdb == NULL) {
1040 0 : DEBUG(SSSDBG_FATAL_FAILURE,
1041 : "Fatal: Sysdb CTX not found for this domain!\n");
1042 0 : return EIO;
1043 : }
1044 :
1045 15 : if (cmdctx->name_is_upn) {
1046 3 : ret = sysdb_search_user_by_upn(cmdctx, dom, name, user_attrs, &msg);
1047 3 : if (ret != EOK && ret != ENOENT) {
1048 0 : DEBUG(SSSDBG_OP_FAILURE, "sysdb_search_user_by_upn failed.\n");
1049 0 : return ret;
1050 : }
1051 :
1052 3 : dctx->res = talloc_zero(cmdctx, struct ldb_result);
1053 3 : if (dctx->res == NULL) {
1054 0 : DEBUG(SSSDBG_OP_FAILURE, "talloc_zero failed.\n");
1055 0 : return ENOMEM;
1056 : }
1057 :
1058 3 : if (ret == ENOENT) {
1059 2 : dctx->res->count = 0;
1060 2 : dctx->res->msgs = NULL;
1061 2 : ret = EOK;
1062 : } else {
1063 1 : dctx->res->count = 1;
1064 1 : dctx->res->msgs = talloc_array(dctx->res,
1065 : struct ldb_message *, 1);
1066 1 : if (dctx->res->msgs == NULL) {
1067 0 : DEBUG(SSSDBG_OP_FAILURE, "talloc_array failed.\n");
1068 0 : return ENOMEM;
1069 : }
1070 :
1071 1 : dctx->res->msgs[0] = talloc_steal(dctx->res->msgs, msg);
1072 : }
1073 : } else {
1074 12 : ret = sysdb_getpwnam_with_views(cmdctx, dom, name, &dctx->res);
1075 : }
1076 15 : if (ret != EOK) {
1077 0 : DEBUG(SSSDBG_CRIT_FAILURE,
1078 : "Failed to make request to our cache!\n");
1079 0 : return EIO;
1080 : }
1081 :
1082 15 : if (dctx->res->count > 1) {
1083 0 : DEBUG(SSSDBG_FATAL_FAILURE,
1084 : "getpwnam call returned more than one result !?!\n");
1085 0 : sss_log(SSS_LOG_ERR,
1086 : "More users have the same name [%s@%s] in SSSD cache. "
1087 : "SSSD will not work correctly.\n",
1088 : name, dom->name);
1089 0 : return ENOENT;
1090 : }
1091 :
1092 15 : if (dctx->res->count == 0 && !dctx->check_provider) {
1093 : /* set negative cache only if not result of cache check */
1094 2 : ret = sss_ncache_set_user(nctx->ncache, false, dom, name);
1095 2 : if (ret != EOK) {
1096 0 : DEBUG(SSSDBG_MINOR_FAILURE, "Cannot set negcache for %s@%s\n",
1097 : name, dom->name);
1098 : }
1099 :
1100 : /* if a multidomain search, try with next */
1101 2 : if (cmdctx->check_next) {
1102 2 : if (cmdctx->name_is_upn) {
1103 1 : dom = get_next_domain(dom, true);
1104 : } else {
1105 1 : dom = get_next_domain(dom, false);
1106 : }
1107 2 : if (dom) continue;
1108 : }
1109 :
1110 2 : DEBUG(SSSDBG_OP_FAILURE, "No results for getpwnam call\n");
1111 :
1112 : /* User not found in ldb -> delete user from memory cache. */
1113 2 : ret = delete_entry_from_memcache(dctx->domain, name,
1114 : nctx->pwd_mc_ctx, SSS_MC_PASSWD);
1115 2 : if (ret != EOK) {
1116 2 : DEBUG(SSSDBG_MINOR_FAILURE,
1117 : "Deleting user from memcache failed.\n");
1118 : }
1119 :
1120 2 : ret = delete_entry_from_memcache(dctx->domain, name,
1121 : nctx->initgr_mc_ctx,
1122 : SSS_MC_INITGROUPS);
1123 2 : if (ret != EOK) {
1124 2 : DEBUG(SSSDBG_MINOR_FAILURE,
1125 : "Deleting user from memcache failed.\n");
1126 : }
1127 :
1128 2 : return ENOENT;
1129 : }
1130 :
1131 : /* if this is a caching provider (or if we haven't checked the cache
1132 : * yet) then verify that the cache is uptodate */
1133 13 : if (dctx->check_provider) {
1134 :
1135 11 : if (cmdctx->name_is_upn) {
1136 2 : extra_flag = EXTRA_NAME_IS_UPN;
1137 9 : } else if (DOM_HAS_VIEWS(dom) && (dctx->res->count == 0
1138 0 : || ldb_msg_find_attr_as_string(dctx->res->msgs[0],
1139 : OVERRIDE_PREFIX SYSDB_NAME,
1140 : NULL) != NULL)) {
1141 0 : extra_flag = EXTRA_INPUT_MAYBE_WITH_VIEW;
1142 : } else {
1143 9 : extra_flag = NULL;
1144 : }
1145 :
1146 11 : ret = check_cache(dctx, nctx, dctx->res, SSS_DP_USER, name, 0,
1147 : extra_flag, nss_cmd_getby_dp_callback, dctx);
1148 11 : if (ret != EOK) {
1149 : /* Anything but EOK means we should reenter the mainloop
1150 : * because we may be refreshing the cache
1151 : */
1152 4 : return ret;
1153 : }
1154 : }
1155 :
1156 : /* One result found */
1157 9 : DEBUG(SSSDBG_TRACE_FUNC,
1158 : "Returning info for user [%s@%s]\n", name, dom->name);
1159 :
1160 9 : return EOK;
1161 : }
1162 :
1163 3 : DEBUG(SSSDBG_MINOR_FAILURE,
1164 : "No matching domain found for [%s], fail!\n", cmdctx->name);
1165 3 : return ENOENT;
1166 : }
1167 :
1168 : static int nss_cmd_getgrnam_search(struct nss_dom_ctx *dctx);
1169 : static int nss_cmd_getgr_send_reply(struct nss_dom_ctx *dctx, bool filter);
1170 : static int nss_cmd_initgroups_search(struct nss_dom_ctx *dctx);
1171 : static int nss_cmd_initgr_send_reply(struct nss_dom_ctx *dctx);
1172 : static int nss_cmd_getpwuid_search(struct nss_dom_ctx *dctx);
1173 : static int nss_cmd_getgrgid_search(struct nss_dom_ctx *dctx);
1174 : static errno_t nss_cmd_getbysid_search(struct nss_dom_ctx *dctx);
1175 : static errno_t nss_cmd_getbysid_send_reply(struct nss_dom_ctx *dctx);
1176 : static errno_t nss_cmd_getsidby_search(struct nss_dom_ctx *dctx);
1177 :
1178 8 : static int nss_cmd_assume_upn(struct nss_dom_ctx *dctx)
1179 : {
1180 : int ret;
1181 :
1182 8 : if (dctx->domain == NULL) {
1183 6 : dctx->domain = dctx->cmdctx->cctx->rctx->domains;
1184 6 : dctx->check_provider = NEED_CHECK_PROVIDER(dctx->domain->provider);
1185 6 : dctx->cmdctx->check_next = true;
1186 6 : dctx->cmdctx->name = talloc_strdup(dctx->cmdctx, dctx->rawname);
1187 6 : dctx->cmdctx->name_is_upn = true;
1188 6 : if (dctx->cmdctx->name == NULL) {
1189 0 : DEBUG(SSSDBG_OP_FAILURE, "talloc_strdup failed.\n");
1190 0 : return ENOMEM;
1191 : }
1192 : }
1193 :
1194 8 : switch (dctx->cmdctx->cmd) {
1195 : case SSS_NSS_GETPWNAM:
1196 4 : ret = nss_cmd_getpwnam_search(dctx);
1197 4 : if (ret == EOK) {
1198 1 : ret = nss_cmd_getpw_send_reply(dctx, false);
1199 : }
1200 4 : break;
1201 : case SSS_NSS_INITGR:
1202 4 : ret = nss_cmd_initgroups_search(dctx);
1203 4 : if (ret == EOK) {
1204 1 : ret = nss_cmd_initgr_send_reply(dctx);
1205 : }
1206 4 : break;
1207 : default:
1208 0 : DEBUG(SSSDBG_CRIT_FAILURE, "Invalid command [%d][%s].\n",
1209 : dctx->cmdctx->cmd, sss_cmd2str(dctx->cmdctx->cmd));
1210 0 : ret = EINVAL;
1211 : }
1212 :
1213 8 : return ret;
1214 : }
1215 :
1216 14 : static void nss_cmd_getby_dp_callback(uint16_t err_maj, uint32_t err_min,
1217 : const char *err_msg, void *ptr)
1218 : {
1219 14 : struct nss_dom_ctx *dctx = talloc_get_type(ptr, struct nss_dom_ctx);
1220 14 : struct nss_cmd_ctx *cmdctx = dctx->cmdctx;
1221 14 : struct cli_ctx *cctx = cmdctx->cctx;
1222 : int ret;
1223 : bool check_subdomains;
1224 14 : struct nss_ctx *nctx = talloc_get_type(cctx->rctx->pvt_ctx, struct nss_ctx);
1225 :
1226 14 : if (err_maj) {
1227 0 : DEBUG(SSSDBG_OP_FAILURE,
1228 : "Unable to get information from Data Provider\n"
1229 : "Error: %u, %u, %s\n"
1230 : "Will try to return what we have in cache\n",
1231 : (unsigned int)err_maj, (unsigned int)err_min, err_msg);
1232 :
1233 0 : if ((dctx->res && dctx->res->count == 1) ||
1234 0 : (dctx->cmdctx->cmd == SSS_NSS_INITGR &&
1235 0 : dctx->res && dctx->res->count != 0)) {
1236 0 : switch (dctx->cmdctx->cmd) {
1237 : case SSS_NSS_GETPWNAM:
1238 0 : ret = nss_cmd_getpw_send_reply(dctx, false);
1239 0 : break;
1240 : case SSS_NSS_GETGRNAM:
1241 0 : ret = nss_cmd_getgr_send_reply(dctx, false);
1242 0 : break;
1243 : case SSS_NSS_INITGR:
1244 0 : ret = nss_cmd_initgr_send_reply(dctx);
1245 0 : break;
1246 : case SSS_NSS_GETPWUID:
1247 0 : ret = nss_cmd_getpw_send_reply(dctx, true);
1248 0 : break;
1249 : case SSS_NSS_GETGRGID:
1250 0 : ret = nss_cmd_getgr_send_reply(dctx, true);
1251 0 : break;
1252 : case SSS_NSS_GETNAMEBYSID:
1253 : case SSS_NSS_GETIDBYSID:
1254 : case SSS_NSS_GETSIDBYNAME:
1255 : case SSS_NSS_GETORIGBYNAME:
1256 : case SSS_NSS_GETSIDBYID:
1257 0 : ret = nss_cmd_getbysid_send_reply(dctx);
1258 0 : break;
1259 : default:
1260 0 : DEBUG(SSSDBG_CRIT_FAILURE, "Invalid command [%d][%s].\n",
1261 : dctx->cmdctx->cmd, sss_cmd2str(dctx->cmdctx->cmd));
1262 0 : ret = EINVAL;
1263 : }
1264 0 : goto done;
1265 : }
1266 :
1267 : /* Since subdomain users and groups are fully qualified they are
1268 : * typically not subject of multi-domain searches. But since POSIX
1269 : * ID do not contain a domain name we have to decend to subdomains
1270 : * here. */
1271 0 : switch (dctx->cmdctx->cmd) {
1272 : case SSS_NSS_GETPWUID:
1273 0 : ret = sss_ncache_set_uid(nctx->ncache, false, dctx->domain,
1274 : cmdctx->id);
1275 0 : if (ret != EOK) {
1276 0 : DEBUG(SSSDBG_MINOR_FAILURE,
1277 : "Cannot set negative cache for UID %"PRIu32"\n",
1278 : cmdctx->id);
1279 : }
1280 0 : check_subdomains = true;
1281 0 : break;
1282 : case SSS_NSS_GETGRGID:
1283 0 : ret = sss_ncache_set_gid(nctx->ncache, false, dctx->domain,
1284 : cmdctx->id);
1285 0 : if (ret != EOK) {
1286 0 : DEBUG(SSSDBG_MINOR_FAILURE,
1287 : "Cannot set negative cache for GID %"PRIu32"\n",
1288 : cmdctx->id);
1289 : }
1290 0 : check_subdomains = true;
1291 0 : break;
1292 : case SSS_NSS_GETSIDBYID:
1293 0 : ret = sss_ncache_set_uid(nctx->ncache, false, dctx->domain,
1294 : cmdctx->id);
1295 0 : if (ret != EOK) {
1296 0 : DEBUG(SSSDBG_MINOR_FAILURE,
1297 : "Cannot set negative cache for UID %"PRIu32"\n",
1298 : cmdctx->id);
1299 : }
1300 0 : ret = sss_ncache_set_gid(nctx->ncache, false, dctx->domain,
1301 : cmdctx->id);
1302 0 : if (ret != EOK) {
1303 0 : DEBUG(SSSDBG_MINOR_FAILURE,
1304 : "Cannot set negative cache for GID %"PRIu32"\n",
1305 : cmdctx->id);
1306 : }
1307 0 : check_subdomains = true;
1308 0 : break;
1309 : default:
1310 0 : check_subdomains = false;
1311 : }
1312 :
1313 : /* no previous results, just loop to next domain if possible */
1314 0 : if (cmdctx->check_next &&
1315 0 : get_next_domain(dctx->domain, check_subdomains)) {
1316 0 : dctx->domain = get_next_domain(dctx->domain, check_subdomains);
1317 0 : dctx->check_provider = NEED_CHECK_PROVIDER(dctx->domain->provider);
1318 : } else {
1319 : /* nothing available */
1320 0 : ret = ENOENT;
1321 0 : goto done;
1322 : }
1323 : }
1324 :
1325 : /* ok the backend returned, search to see if we have updated results */
1326 14 : switch (dctx->cmdctx->cmd) {
1327 : case SSS_NSS_GETPWNAM:
1328 4 : ret = nss_cmd_getpwnam_search(dctx);
1329 4 : if (ret == EOK) {
1330 : /* we have results to return */
1331 2 : ret = nss_cmd_getpw_send_reply(dctx, false);
1332 : }
1333 4 : break;
1334 : case SSS_NSS_GETGRNAM:
1335 0 : ret = nss_cmd_getgrnam_search(dctx);
1336 0 : if (ret == EOK) {
1337 : /* we have results to return */
1338 0 : ret = nss_cmd_getgr_send_reply(dctx, false);
1339 : }
1340 0 : break;
1341 : case SSS_NSS_INITGR:
1342 5 : ret = nss_cmd_initgroups_search(dctx);
1343 5 : if (ret == EOK) {
1344 : /* we have results to return */
1345 3 : ret = nss_cmd_initgr_send_reply(dctx);
1346 : }
1347 5 : break;
1348 : case SSS_NSS_GETPWUID:
1349 3 : ret = nss_cmd_getpwuid_search(dctx);
1350 3 : if (ret == EOK) {
1351 : /* we have results to return */
1352 2 : ret = nss_cmd_getpw_send_reply(dctx, true);
1353 : }
1354 3 : break;
1355 : case SSS_NSS_GETGRGID:
1356 0 : ret = nss_cmd_getgrgid_search(dctx);
1357 0 : if (ret == EOK) {
1358 : /* we have results to return */
1359 0 : ret = nss_cmd_getgr_send_reply(dctx, true);
1360 : }
1361 0 : break;
1362 : case SSS_NSS_GETNAMEBYSID:
1363 : case SSS_NSS_GETIDBYSID:
1364 2 : ret = nss_cmd_getbysid_search(dctx);
1365 2 : if (ret == EOK) {
1366 1 : ret = nss_cmd_getbysid_send_reply(dctx);
1367 : }
1368 2 : break;
1369 : case SSS_NSS_GETSIDBYNAME:
1370 : case SSS_NSS_GETORIGBYNAME:
1371 0 : ret = nss_cmd_getsidby_search(dctx);
1372 0 : if (ret == EOK) {
1373 0 : ret = nss_cmd_getbysid_send_reply(dctx);
1374 : }
1375 0 : break;
1376 : case SSS_NSS_GETSIDBYID:
1377 0 : ret = nss_cmd_getsidby_search(dctx);
1378 0 : if (ret == EOK) {
1379 0 : ret = nss_cmd_getbysid_send_reply(dctx);
1380 : }
1381 0 : break;
1382 : default:
1383 0 : DEBUG(SSSDBG_CRIT_FAILURE, "Invalid command [%d][%s].\n",
1384 : dctx->cmdctx->cmd, sss_cmd2str(dctx->cmdctx->cmd));
1385 0 : ret = EINVAL;
1386 : }
1387 :
1388 : done:
1389 14 : if (ret == ENOENT
1390 6 : && (dctx->cmdctx->cmd == SSS_NSS_GETPWNAM
1391 4 : || dctx->cmdctx->cmd == SSS_NSS_INITGR)
1392 4 : && dctx->rawname != NULL && strchr(dctx->rawname, '@') != NULL) {
1393 : /* assume Kerberos principal */
1394 2 : ret = nss_cmd_assume_upn(dctx);
1395 : }
1396 :
1397 14 : ret = nss_cmd_done(cmdctx, ret);
1398 14 : if (ret) {
1399 0 : NSS_CMD_FATAL_ERROR(cctx);
1400 : }
1401 : }
1402 :
1403 6 : static int nss_check_name_of_well_known_sid(struct nss_cmd_ctx *cmdctx,
1404 : const char *full_name)
1405 : {
1406 6 : char *wk_name = NULL;
1407 6 : char *wk_dom_name = NULL;
1408 : const char *wk_sid;
1409 : int ret;
1410 : struct sized_string sid;
1411 : uint8_t *body;
1412 : size_t blen;
1413 : struct cli_ctx *cctx;
1414 : struct nss_ctx *nss_ctx;
1415 6 : size_t pctr = 0;
1416 :
1417 6 : nss_ctx = talloc_get_type(cmdctx->cctx->rctx->pvt_ctx, struct nss_ctx);
1418 6 : ret = sss_parse_name(cmdctx, nss_ctx->global_names, full_name,
1419 : &wk_dom_name, &wk_name);
1420 6 : if (ret != EOK) {
1421 0 : DEBUG(SSSDBG_OP_FAILURE, "sss_parse_name failed.\n");
1422 0 : return ret;
1423 : }
1424 :
1425 6 : if (wk_dom_name == NULL || wk_name == NULL) {
1426 0 : DEBUG(SSSDBG_OP_FAILURE,
1427 : "Unable to split [%s] in name and domain part. " \
1428 : "Skipping check for well-known name.\n", full_name);
1429 :
1430 0 : return ENOENT;
1431 : }
1432 :
1433 6 : ret = name_to_well_known_sid(wk_dom_name, wk_name, &wk_sid);
1434 6 : talloc_free(wk_dom_name);
1435 6 : talloc_free(wk_name);
1436 6 : if (ret != EOK) {
1437 2 : DEBUG(SSSDBG_TRACE_ALL, "Name [%s] is not the name of a " \
1438 : "Well-Known SID.\n", full_name);
1439 2 : return ret;
1440 : }
1441 :
1442 4 : to_sized_string(&sid, wk_sid);
1443 :
1444 4 : cctx = cmdctx->cctx;
1445 8 : ret = sss_packet_new(cctx->creq, sid.len + 3 * sizeof(uint32_t),
1446 4 : sss_packet_get_cmd(cctx->creq->in),
1447 4 : &cctx->creq->out);
1448 4 : if (ret != EOK) {
1449 0 : return ENOMEM;
1450 : }
1451 :
1452 4 : sss_packet_get_body(cctx->creq->out, &body, &blen);
1453 4 : SAFEALIGN_SETMEM_UINT32(body, 1, &pctr); /* num results */
1454 4 : SAFEALIGN_SETMEM_UINT32(body + pctr, 0, &pctr); /* reserved */
1455 4 : SAFEALIGN_SETMEM_UINT32(body + pctr, SSS_ID_TYPE_GID, &pctr);
1456 4 : memcpy(&body[pctr], sid.str, sid.len);
1457 :
1458 4 : sss_packet_set_error(cctx->creq->out, EOK);
1459 4 : sss_cmd_done(cctx, cmdctx);
1460 4 : return EOK;
1461 : }
1462 :
1463 : static int nss_cmd_getbynam(enum sss_cli_command cmd, struct cli_ctx *cctx);
1464 : static void nss_cmd_getbynam_done(struct tevent_req *req);
1465 13 : static int nss_cmd_getpwnam(struct cli_ctx *cctx)
1466 : {
1467 13 : return nss_cmd_getbynam(SSS_NSS_GETPWNAM, cctx);
1468 : }
1469 :
1470 40 : static int nss_cmd_getbynam(enum sss_cli_command cmd, struct cli_ctx *cctx)
1471 : {
1472 :
1473 : struct tevent_req *req;
1474 : struct nss_cmd_ctx *cmdctx;
1475 : struct nss_dom_ctx *dctx;
1476 : const char *rawname;
1477 : char *domname;
1478 : uint8_t *body;
1479 : size_t blen;
1480 : int ret;
1481 :
1482 40 : switch(cmd) {
1483 : case SSS_NSS_GETPWNAM:
1484 : case SSS_NSS_GETGRNAM:
1485 : case SSS_NSS_INITGR:
1486 : case SSS_NSS_GETSIDBYNAME:
1487 : case SSS_NSS_GETORIGBYNAME:
1488 40 : break;
1489 : default:
1490 0 : DEBUG(SSSDBG_CRIT_FAILURE, "Invalid command type [%d][%s].\n",
1491 : cmd, sss_cmd2str(cmd));
1492 0 : return EINVAL;
1493 : }
1494 :
1495 40 : cmdctx = talloc_zero(cctx, struct nss_cmd_ctx);
1496 40 : if (!cmdctx) {
1497 0 : return ENOMEM;
1498 : }
1499 40 : cmdctx->cctx = cctx;
1500 40 : cmdctx->cmd = cmd;
1501 :
1502 40 : dctx = talloc_zero(cmdctx, struct nss_dom_ctx);
1503 40 : if (!dctx) {
1504 0 : ret = ENOMEM;
1505 0 : goto done;
1506 : }
1507 40 : dctx->cmdctx = cmdctx;
1508 :
1509 : /* get user name to query */
1510 40 : sss_packet_get_body(cctx->creq->in, &body, &blen);
1511 :
1512 : /* if not terminated fail */
1513 40 : if (body[blen -1] != '\0') {
1514 0 : ret = EINVAL;
1515 0 : goto done;
1516 : }
1517 :
1518 : /* If the body isn't valid UTF-8, fail */
1519 40 : if (!sss_utf8_check(body, blen -1)) {
1520 0 : ret = EINVAL;
1521 0 : goto done;
1522 : }
1523 :
1524 40 : rawname = (const char *)body;
1525 40 : dctx->mc_name = rawname;
1526 :
1527 40 : DEBUG(SSSDBG_TRACE_FUNC, "Running command [%d][%s] with input [%s].\n",
1528 : cmd, sss_cmd2str(dctx->cmdctx->cmd), rawname);
1529 :
1530 40 : if (dctx->cmdctx->cmd == SSS_NSS_GETSIDBYNAME) {
1531 6 : ret = nss_check_name_of_well_known_sid(cmdctx, rawname);
1532 6 : if (ret != ENOENT) {
1533 6 : if (ret == EOK) {
1534 4 : DEBUG(SSSDBG_TRACE_ALL, "Name [%s] is the name of a " \
1535 : "Well-Known SID.\n", rawname);
1536 : } else {
1537 2 : DEBUG(SSSDBG_OP_FAILURE,
1538 : "nss_check_name_of_well_known_sid failed.\n");
1539 : }
1540 6 : goto done;
1541 : }
1542 : }
1543 :
1544 : /* We need to attach to subdomain request, if the first one is not
1545 : * finished yet. We may not be able to lookup object in AD otherwise. */
1546 34 : if (cctx->rctx->get_domains_last_call.tv_sec == 0) {
1547 34 : req = sss_dp_get_domains_send(cctx->rctx, cctx->rctx, true, NULL);
1548 34 : if (req == NULL) {
1549 0 : ret = ENOMEM;
1550 : } else {
1551 34 : dctx->rawname = rawname;
1552 34 : tevent_req_set_callback(req, nss_cmd_getbynam_done, dctx);
1553 34 : ret = EAGAIN;
1554 : }
1555 34 : goto done;
1556 : }
1557 :
1558 0 : domname = NULL;
1559 0 : ret = sss_parse_name_for_domains(cmdctx, cctx->rctx->domains,
1560 0 : cctx->rctx->default_domain, rawname,
1561 : &domname, &cmdctx->name);
1562 0 : if (ret == EAGAIN) {
1563 0 : req = sss_dp_get_domains_send(cctx->rctx, cctx->rctx, true, domname);
1564 0 : if (req == NULL) {
1565 0 : ret = ENOMEM;
1566 : } else {
1567 0 : dctx->rawname = rawname;
1568 0 : tevent_req_set_callback(req, nss_cmd_getbynam_done, dctx);
1569 0 : ret = EAGAIN;
1570 : }
1571 0 : goto done;
1572 0 : } else if (ret != EOK) {
1573 0 : DEBUG(SSSDBG_OP_FAILURE, "Invalid name received [%s]\n", rawname);
1574 0 : ret = ENOENT;
1575 0 : goto done;
1576 : }
1577 :
1578 0 : DEBUG(SSSDBG_CONF_SETTINGS, "Requesting info for [%s] from [%s]\n",
1579 : cmdctx->name, domname?domname:"<ALL>");
1580 :
1581 0 : if (domname) {
1582 0 : dctx->domain = responder_get_domain(cctx->rctx, domname);
1583 0 : if (!dctx->domain) {
1584 0 : ret = ENOENT;
1585 0 : goto done;
1586 : }
1587 : } else {
1588 : /* this is a multidomain search */
1589 0 : dctx->rawname = rawname;
1590 0 : dctx->domain = cctx->rctx->domains;
1591 0 : cmdctx->check_next = true;
1592 0 : if (cctx->rctx->get_domains_last_call.tv_sec == 0) {
1593 0 : req = sss_dp_get_domains_send(cctx->rctx, cctx->rctx, false, NULL);
1594 0 : if (req == NULL) {
1595 0 : ret = ENOMEM;
1596 : } else {
1597 0 : tevent_req_set_callback(req, nss_cmd_getbynam_done, dctx);
1598 0 : ret = EAGAIN;
1599 : }
1600 0 : goto done;
1601 : }
1602 : }
1603 :
1604 0 : dctx->check_provider = NEED_CHECK_PROVIDER(dctx->domain->provider);
1605 :
1606 : /* ok, find it ! */
1607 0 : switch (dctx->cmdctx->cmd) {
1608 : case SSS_NSS_GETPWNAM:
1609 0 : ret = nss_cmd_getpwnam_search(dctx);
1610 0 : if (ret == EOK) {
1611 : /* we have results to return */
1612 0 : ret = nss_cmd_getpw_send_reply(dctx, false);
1613 0 : } else if (ret == ENOENT
1614 0 : && dctx->rawname != NULL
1615 0 : && strchr(dctx->rawname, '@') != NULL) {
1616 : /* assume Kerberos principal */
1617 0 : ret = nss_cmd_assume_upn(dctx);
1618 : }
1619 0 : break;
1620 : case SSS_NSS_GETGRNAM:
1621 0 : ret = nss_cmd_getgrnam_search(dctx);
1622 0 : if (ret == EOK) {
1623 : /* we have results to return */
1624 0 : ret = nss_cmd_getgr_send_reply(dctx, false);
1625 : }
1626 0 : break;
1627 : case SSS_NSS_INITGR:
1628 0 : ret = nss_cmd_initgroups_search(dctx);
1629 0 : if (ret == EOK) {
1630 : /* we have results to return */
1631 0 : ret = nss_cmd_initgr_send_reply(dctx);
1632 0 : } else if (ret == ENOENT
1633 0 : && dctx->rawname != NULL
1634 0 : && strchr(dctx->rawname, '@') != NULL) {
1635 : /* assume Kerberos principal */
1636 0 : ret = nss_cmd_assume_upn(dctx);
1637 : }
1638 0 : break;
1639 : case SSS_NSS_GETSIDBYNAME:
1640 : case SSS_NSS_GETORIGBYNAME:
1641 0 : ret = nss_cmd_getsidby_search(dctx);
1642 0 : if (ret == EOK) {
1643 0 : ret = nss_cmd_getbysid_send_reply(dctx);
1644 : }
1645 0 : break;
1646 : default:
1647 0 : DEBUG(SSSDBG_CRIT_FAILURE, "Invalid command [%d][%s].\n",
1648 : dctx->cmdctx->cmd, sss_cmd2str(dctx->cmdctx->cmd));
1649 0 : ret = EINVAL;
1650 : }
1651 :
1652 : done:
1653 40 : return nss_cmd_done(cmdctx, ret);
1654 : }
1655 :
1656 34 : static void nss_cmd_getbynam_done(struct tevent_req *req)
1657 : {
1658 34 : struct nss_dom_ctx *dctx = tevent_req_callback_data(req, struct nss_dom_ctx);
1659 34 : struct nss_cmd_ctx *cmdctx = dctx->cmdctx;
1660 34 : struct cli_ctx *cctx = cmdctx->cctx;
1661 34 : char *domname = NULL;
1662 34 : const char *rawname = dctx->rawname;
1663 : errno_t ret;
1664 :
1665 34 : ret = sss_dp_get_domains_recv(req);
1666 34 : talloc_free(req);
1667 34 : if (ret != EOK) {
1668 0 : goto done;
1669 : }
1670 :
1671 68 : ret = sss_parse_name_for_domains(cmdctx, cctx->rctx->domains,
1672 34 : cctx->rctx->default_domain, rawname,
1673 : &domname, &cmdctx->name);
1674 34 : if (ret == EAGAIN && (dctx->cmdctx->cmd == SSS_NSS_GETPWNAM
1675 3 : || dctx->cmdctx->cmd == SSS_NSS_INITGR)) {
1676 : /* assume Kerberos principal */
1677 6 : ret = nss_cmd_assume_upn(dctx);
1678 6 : goto done;
1679 28 : } else if (ret != EOK) {
1680 0 : DEBUG(SSSDBG_OP_FAILURE, "Invalid name received [%s]\n", rawname);
1681 0 : ret = ENOENT;
1682 0 : goto done;
1683 : }
1684 :
1685 28 : ret = nss_reset_negcache(cctx->rctx);
1686 28 : if (ret != EOK) {
1687 0 : DEBUG(SSSDBG_MINOR_FAILURE, "Cannot reset negcache records\n");
1688 : /* Not fatal */
1689 : }
1690 :
1691 28 : DEBUG(SSSDBG_TRACE_FUNC, "Requesting info for [%s] from [%s]\n",
1692 : cmdctx->name, domname?domname:"<ALL>");
1693 :
1694 28 : if (domname) {
1695 6 : dctx->domain = responder_get_domain(cctx->rctx, domname);
1696 6 : if (dctx->domain == NULL) {
1697 0 : ret = ENOENT;
1698 0 : goto done;
1699 : }
1700 : } else {
1701 : /* this is a multidomain search */
1702 22 : dctx->domain = cctx->rctx->domains;
1703 22 : cmdctx->check_next = true;
1704 : }
1705 :
1706 28 : dctx->check_provider = NEED_CHECK_PROVIDER(dctx->domain->provider);
1707 :
1708 : /* ok, find it ! */
1709 28 : switch (dctx->cmdctx->cmd) {
1710 : case SSS_NSS_GETPWNAM:
1711 10 : ret = nss_cmd_getpwnam_search(dctx);
1712 10 : if (ret == EOK) {
1713 : /* we have results to return */
1714 6 : ret = nss_cmd_getpw_send_reply(dctx, false);
1715 4 : } else if (ret == ENOENT
1716 1 : && dctx->rawname != NULL
1717 1 : && strchr(dctx->rawname, '@') != NULL) {
1718 : /* assume Kerberos principal */
1719 0 : ret = nss_cmd_assume_upn(dctx);
1720 : }
1721 10 : break;
1722 : case SSS_NSS_GETGRNAM:
1723 9 : ret = nss_cmd_getgrnam_search(dctx);
1724 9 : if (ret == EOK) {
1725 : /* we have results to return */
1726 9 : ret = nss_cmd_getgr_send_reply(dctx, false);
1727 : }
1728 9 : break;
1729 : case SSS_NSS_INITGR:
1730 6 : ret = nss_cmd_initgroups_search(dctx);
1731 6 : if (ret == EOK) {
1732 : /* we have results to return */
1733 1 : ret = nss_cmd_initgr_send_reply(dctx);
1734 5 : } else if (ret == ENOENT
1735 1 : && dctx->rawname != NULL
1736 1 : && strchr(dctx->rawname, '@') != NULL) {
1737 : /* assume Kerberos principal */
1738 0 : ret = nss_cmd_assume_upn(dctx);
1739 : }
1740 6 : break;
1741 : case SSS_NSS_GETSIDBYNAME:
1742 : case SSS_NSS_GETORIGBYNAME:
1743 3 : ret = nss_cmd_getsidby_search(dctx);
1744 3 : if (ret == EOK) {
1745 3 : ret = nss_cmd_getbysid_send_reply(dctx);
1746 : }
1747 3 : break;
1748 : default:
1749 0 : DEBUG(SSSDBG_CRIT_FAILURE, "Invalid command [%d][%s].\n",
1750 : dctx->cmdctx->cmd, sss_cmd2str(dctx->cmdctx->cmd));
1751 0 : ret = EINVAL;
1752 : }
1753 :
1754 : done:
1755 34 : nss_cmd_done(cmdctx, ret);
1756 34 : }
1757 :
1758 : /* search for a uid.
1759 : * Returns:
1760 : * ENOENT, if uid is definitely not found
1761 : * EAGAIN, if uid is being fetched from backend via async operations
1762 : * EOK, if found
1763 : * anything else on a fatal error
1764 : */
1765 :
1766 7 : static int nss_cmd_getpwuid_search(struct nss_dom_ctx *dctx)
1767 : {
1768 7 : struct nss_cmd_ctx *cmdctx = dctx->cmdctx;
1769 7 : struct sss_domain_info *dom = dctx->domain;
1770 7 : struct cli_ctx *cctx = cmdctx->cctx;
1771 : struct nss_ctx *nctx;
1772 : int ret;
1773 : int err;
1774 7 : const char *extra_flag = NULL;
1775 :
1776 7 : nctx = talloc_get_type(cctx->rctx->pvt_ctx, struct nss_ctx);
1777 :
1778 7 : while (dom) {
1779 :
1780 : /* check that the uid is valid for this domain */
1781 14 : if ((dom->id_min && (cmdctx->id < dom->id_min)) ||
1782 7 : (dom->id_max && (cmdctx->id > dom->id_max))) {
1783 0 : DEBUG(SSSDBG_CONF_SETTINGS,
1784 : "Uid [%"PRIu32"] does not exist in domain [%s]! "
1785 : "(id out of range)\n",
1786 : cmdctx->id, dom->name);
1787 0 : if (cmdctx->check_next) {
1788 0 : dom = get_next_domain(dom, true);
1789 0 : continue;
1790 : }
1791 0 : ret = ENOENT;
1792 0 : goto done;
1793 : }
1794 :
1795 7 : if (dom != dctx->domain) {
1796 : /* make sure we reset the check_provider flag when we check
1797 : * a new domain */
1798 0 : dctx->check_provider = NEED_CHECK_PROVIDER(dom->provider);
1799 : }
1800 :
1801 : /* make sure to update the dctx if we changed domain */
1802 7 : dctx->domain = dom;
1803 :
1804 7 : DEBUG(SSSDBG_CONF_SETTINGS,
1805 : "Requesting info for [%"PRIu32"@%s]\n", cmdctx->id, dom->name);
1806 :
1807 7 : if (dom->sysdb == NULL) {
1808 0 : DEBUG(SSSDBG_FATAL_FAILURE,
1809 : "Fatal: Sysdb CTX not found for this domain!\n");
1810 0 : ret = EIO;
1811 0 : goto done;
1812 : }
1813 :
1814 7 : ret = sysdb_getpwuid_with_views(cmdctx, dom, cmdctx->id, &dctx->res);
1815 7 : if (ret != EOK) {
1816 0 : DEBUG(SSSDBG_CRIT_FAILURE,
1817 : "Failed to make request to our cache!\n");
1818 0 : ret = EIO;
1819 0 : goto done;
1820 : }
1821 :
1822 7 : if (dctx->res->count > 1) {
1823 0 : DEBUG(SSSDBG_FATAL_FAILURE,
1824 : "getpwuid call returned more than one result !?!\n");
1825 0 : sss_log(SSS_LOG_ERR,
1826 : "More users have the same UID [%"PRIu32"] in directory "
1827 : "server. SSSD will not work correctly.\n", cmdctx->id);
1828 0 : ret = ENOENT;
1829 0 : goto done;
1830 : }
1831 :
1832 7 : if (dctx->res->count == 0 && !dctx->check_provider) {
1833 : /* if a multidomain search, try with next */
1834 1 : if (cmdctx->check_next) {
1835 1 : dom = get_next_domain(dom, true);
1836 1 : continue;
1837 : }
1838 :
1839 : /* set negative cache only if not result of cache check */
1840 0 : DEBUG(SSSDBG_MINOR_FAILURE, "No results for getpwuid call\n");
1841 0 : ret = ENOENT;
1842 0 : goto done;
1843 : }
1844 :
1845 : /* if this is a caching provider (or if we haven't checked the cache
1846 : * yet) then verify that the cache is uptodate */
1847 6 : if (dctx->check_provider) {
1848 :
1849 4 : if (DOM_HAS_VIEWS(dom) && (dctx->res->count == 0
1850 0 : || ldb_msg_find_attr_as_uint64(dctx->res->msgs[0],
1851 : OVERRIDE_PREFIX SYSDB_UIDNUM,
1852 : 0) != 0)) {
1853 0 : extra_flag = EXTRA_INPUT_MAYBE_WITH_VIEW;
1854 : } else {
1855 4 : extra_flag = NULL;
1856 : }
1857 :
1858 4 : ret = check_cache(dctx, nctx, dctx->res, SSS_DP_USER, NULL,
1859 : cmdctx->id, extra_flag, nss_cmd_getby_dp_callback,
1860 : dctx);
1861 4 : if (ret != EOK) {
1862 : /* Anything but EOK means we should reenter the mainloop
1863 : * because we may be refreshing the cache
1864 : */
1865 3 : goto done;
1866 : }
1867 : }
1868 :
1869 : /* One result found */
1870 3 : DEBUG(SSSDBG_TRACE_FUNC,
1871 : "Returning info for uid [%"PRIu32"@%s]\n", cmdctx->id, dom->name);
1872 :
1873 3 : ret = EOK;
1874 3 : goto done;
1875 : }
1876 :
1877 : /* All domains were tried and none had the entry. */
1878 1 : ret = ENOENT;
1879 : done:
1880 7 : if (ret == ENOENT) {
1881 : /* The entry was not found, need to set result in negative cache */
1882 1 : err = sss_ncache_set_uid(nctx->ncache, false, NULL, cmdctx->id);
1883 1 : if (err != EOK) {
1884 0 : DEBUG(SSSDBG_MINOR_FAILURE,
1885 : "Cannot set negative cache for UID %"PRIu32"\n", cmdctx->id);
1886 : }
1887 : }
1888 :
1889 7 : DEBUG(SSSDBG_MINOR_FAILURE, "No matching domain found for [%"PRIu32"]\n", cmdctx->id);
1890 7 : return ret;
1891 : }
1892 :
1893 : static int nss_cmd_getgrgid_search(struct nss_dom_ctx *dctx);
1894 : static int nss_cmd_getbyid(enum sss_cli_command cmd, struct cli_ctx *cctx);
1895 : static void nss_cmd_getbyid_done(struct tevent_req *req);
1896 5 : static int nss_cmd_getpwuid(struct cli_ctx *cctx)
1897 : {
1898 5 : return nss_cmd_getbyid(SSS_NSS_GETPWUID, cctx);
1899 : }
1900 :
1901 5 : static int nss_cmd_getbyid(enum sss_cli_command cmd, struct cli_ctx *cctx)
1902 : {
1903 : struct nss_cmd_ctx *cmdctx;
1904 : struct nss_dom_ctx *dctx;
1905 : struct nss_ctx *nctx;
1906 : uint8_t *body;
1907 : size_t blen;
1908 : int ret;
1909 : struct tevent_req *req;
1910 :
1911 5 : switch (cmd) {
1912 : case SSS_NSS_GETPWUID:
1913 : case SSS_NSS_GETGRGID:
1914 : case SSS_NSS_GETSIDBYID:
1915 5 : break;
1916 : default:
1917 0 : DEBUG(SSSDBG_CRIT_FAILURE, "Invalid command type [%d][%s].\n",
1918 : cmd, sss_cmd2str(cmd));
1919 0 : return EINVAL;
1920 : }
1921 :
1922 5 : nctx = talloc_get_type(cctx->rctx->pvt_ctx, struct nss_ctx);
1923 :
1924 5 : cmdctx = talloc_zero(cctx, struct nss_cmd_ctx);
1925 5 : if (!cmdctx) {
1926 0 : return ENOMEM;
1927 : }
1928 5 : cmdctx->cctx = cctx;
1929 5 : cmdctx->cmd = cmd;
1930 :
1931 5 : dctx = talloc_zero(cmdctx, struct nss_dom_ctx);
1932 5 : if (!dctx) {
1933 0 : ret = ENOMEM;
1934 0 : goto done;
1935 : }
1936 5 : dctx->cmdctx = cmdctx;
1937 :
1938 : /* get id to query */
1939 5 : sss_packet_get_body(cctx->creq->in, &body, &blen);
1940 :
1941 5 : if (blen != sizeof(uint32_t)) {
1942 0 : ret = EINVAL;
1943 0 : goto done;
1944 : }
1945 5 : SAFEALIGN_COPY_UINT32(&cmdctx->id, body, NULL);
1946 :
1947 5 : DEBUG(SSSDBG_TRACE_FUNC, "Running command [%d][%s] with id [%"PRIu32"].\n",
1948 : dctx->cmdctx->cmd, sss_cmd2str(dctx->cmdctx->cmd), cmdctx->id);
1949 :
1950 5 : switch(dctx->cmdctx->cmd) {
1951 : case SSS_NSS_GETPWUID:
1952 5 : ret = sss_ncache_check_uid(nctx->ncache, nctx->neg_timeout, NULL,
1953 : cmdctx->id);
1954 5 : if (ret == EEXIST) {
1955 1 : DEBUG(SSSDBG_TRACE_FUNC,
1956 : "Uid [%"PRIu32"] does not exist! (negative cache)\n",
1957 : cmdctx->id);
1958 1 : ret = ENOENT;
1959 1 : goto done;
1960 : }
1961 4 : break;
1962 : case SSS_NSS_GETGRGID:
1963 0 : ret = sss_ncache_check_gid(nctx->ncache, nctx->neg_timeout, NULL,
1964 : cmdctx->id);
1965 0 : if (ret == EEXIST) {
1966 0 : DEBUG(SSSDBG_TRACE_FUNC,
1967 : "Gid [%"PRIu32"] does not exist! (negative cache)\n",
1968 : cmdctx->id);
1969 0 : ret = ENOENT;
1970 0 : goto done;
1971 : }
1972 0 : break;
1973 : case SSS_NSS_GETSIDBYID:
1974 0 : ret = sss_ncache_check_uid(nctx->ncache, nctx->neg_timeout, NULL,
1975 : cmdctx->id);
1976 0 : if (ret != EEXIST) {
1977 0 : ret = sss_ncache_check_gid(nctx->ncache, nctx->neg_timeout,
1978 : NULL, cmdctx->id);
1979 : }
1980 0 : if (ret == EEXIST) {
1981 0 : DEBUG(SSSDBG_TRACE_FUNC,
1982 : "Id [%"PRIu32"] does not exist! (negative cache)\n",
1983 : cmdctx->id);
1984 0 : ret = ENOENT;
1985 0 : goto done;
1986 : }
1987 0 : break;
1988 : default:
1989 0 : DEBUG(SSSDBG_CRIT_FAILURE, "Invalid command [%d][%s].\n",
1990 : dctx->cmdctx->cmd, sss_cmd2str(dctx->cmdctx->cmd));
1991 0 : ret = EINVAL;
1992 0 : goto done;
1993 : }
1994 :
1995 : /* id searches are always multidomain */
1996 4 : dctx->domain = cctx->rctx->domains;
1997 4 : cmdctx->check_next = true;
1998 :
1999 4 : dctx->check_provider = NEED_CHECK_PROVIDER(dctx->domain->provider);
2000 :
2001 4 : if (cctx->rctx->get_domains_last_call.tv_sec == 0) {
2002 4 : req = sss_dp_get_domains_send(cctx->rctx, cctx->rctx, false, NULL);
2003 4 : if (req == NULL) {
2004 0 : ret = ENOMEM;
2005 : } else {
2006 4 : tevent_req_set_callback(req, nss_cmd_getbyid_done, dctx);
2007 4 : ret = EAGAIN;
2008 : }
2009 4 : goto done;
2010 : }
2011 :
2012 : /* ok, find it ! */
2013 0 : switch(dctx->cmdctx->cmd) {
2014 : case SSS_NSS_GETPWUID:
2015 0 : ret = nss_cmd_getpwuid_search(dctx);
2016 0 : if (ret == EOK) {
2017 : /* we have results to return */
2018 0 : ret = nss_cmd_getpw_send_reply(dctx, true);
2019 : }
2020 0 : break;
2021 : case SSS_NSS_GETGRGID:
2022 0 : ret = nss_cmd_getgrgid_search(dctx);
2023 0 : if (ret == EOK) {
2024 : /* we have results to return */
2025 0 : ret = nss_cmd_getgr_send_reply(dctx, true);
2026 : }
2027 0 : break;
2028 : case SSS_NSS_GETSIDBYID:
2029 0 : ret = nss_cmd_getsidby_search(dctx);
2030 0 : if (ret == EOK) {
2031 0 : ret = nss_cmd_getbysid_send_reply(dctx);
2032 : }
2033 0 : break;
2034 : default:
2035 0 : DEBUG(SSSDBG_CRIT_FAILURE, "Invalid command [%d][%s].\n",
2036 : dctx->cmdctx->cmd, sss_cmd2str(dctx->cmdctx->cmd));
2037 0 : ret = EINVAL;
2038 : }
2039 :
2040 : done:
2041 5 : return nss_cmd_done(cmdctx, ret);
2042 : }
2043 :
2044 4 : static void nss_cmd_getbyid_done(struct tevent_req *req)
2045 : {
2046 4 : struct nss_dom_ctx *dctx = tevent_req_callback_data(req, struct nss_dom_ctx);
2047 4 : struct nss_cmd_ctx *cmdctx = dctx->cmdctx;
2048 : errno_t ret;
2049 :
2050 4 : ret = sss_dp_get_domains_recv(req);
2051 4 : talloc_free(req);
2052 4 : if (ret != EOK) {
2053 0 : goto done;
2054 : }
2055 :
2056 4 : ret = nss_reset_negcache(cmdctx->cctx->rctx);
2057 4 : if (ret != EOK) {
2058 0 : DEBUG(SSSDBG_MINOR_FAILURE, "Cannot reset negcache records\n");
2059 : /* Not fatal */
2060 : }
2061 :
2062 : /* ok, find it ! */
2063 4 : switch(dctx->cmdctx->cmd) {
2064 : case SSS_NSS_GETPWUID:
2065 4 : ret = nss_cmd_getpwuid_search(dctx);
2066 4 : if (ret == EOK) {
2067 : /* we have results to return */
2068 1 : ret = nss_cmd_getpw_send_reply(dctx, true);
2069 : }
2070 4 : break;
2071 : case SSS_NSS_GETGRGID:
2072 0 : ret = nss_cmd_getgrgid_search(dctx);
2073 0 : if (ret == EOK) {
2074 : /* we have results to return */
2075 0 : ret = nss_cmd_getgr_send_reply(dctx, true);
2076 : }
2077 0 : break;
2078 : case SSS_NSS_GETNAMEBYSID:
2079 : case SSS_NSS_GETIDBYSID:
2080 0 : ret = responder_get_domain_by_id(cmdctx->cctx->rctx, cmdctx->secid,
2081 : &dctx->domain);
2082 0 : if (ret != EOK) {
2083 0 : DEBUG(SSSDBG_OP_FAILURE, "Cannot find domain for SID [%s].\n",
2084 : cmdctx->secid);
2085 0 : ret = ENOENT;
2086 0 : goto done;
2087 : }
2088 :
2089 0 : dctx->check_provider = NEED_CHECK_PROVIDER(dctx->domain->provider);
2090 :
2091 0 : ret = nss_cmd_getbysid_search(dctx);
2092 0 : if (ret == EOK) {
2093 0 : ret = nss_cmd_getbysid_send_reply(dctx);
2094 : }
2095 0 : break;
2096 : case SSS_NSS_GETSIDBYID:
2097 0 : ret = nss_cmd_getsidby_search(dctx);
2098 0 : if (ret == EOK) {
2099 0 : ret = nss_cmd_getbysid_send_reply(dctx);
2100 : }
2101 0 : break;
2102 : default:
2103 0 : DEBUG(SSSDBG_CRIT_FAILURE, "Invalid command [%d][%s].\n",
2104 : dctx->cmdctx->cmd, sss_cmd2str(dctx->cmdctx->cmd));
2105 0 : ret = EINVAL;
2106 : }
2107 :
2108 : done:
2109 4 : nss_cmd_done(cmdctx, ret);
2110 4 : }
2111 :
2112 : /* to keep it simple at this stage we are retrieving the
2113 : * full enumeration again for each request for each process
2114 : * and we also block on setpwent() for the full time needed
2115 : * to retrieve the data. And endpwent() frees all the data.
2116 : * Next steps are:
2117 : * - use an nsssrv wide cache with data already structured
2118 : * so that it can be immediately returned (see nscd way)
2119 : * - use mutexes so that setpwent() can return immediately
2120 : * even if the data is still being fetched
2121 : * - make getpwent() wait on the mutex
2122 : *
2123 : * Alternatively:
2124 : * - use a smarter search mechanism that keeps track of the
2125 : * last user searched and return the next X users doing
2126 : * an alphabetic sort and starting from the user following
2127 : * the last returned user.
2128 : */
2129 : static int nss_cmd_getpwent_immediate(struct nss_cmd_ctx *cmdctx);
2130 : struct tevent_req * nss_cmd_setpwent_send(TALLOC_CTX *mem_ctx,
2131 : struct cli_ctx *client);
2132 : static void nss_cmd_setpwent_done(struct tevent_req *req);
2133 0 : static int nss_cmd_setpwent(struct cli_ctx *cctx)
2134 : {
2135 : struct nss_cmd_ctx *cmdctx;
2136 : struct tevent_req *req;
2137 0 : errno_t ret = EOK;
2138 :
2139 0 : cmdctx = talloc_zero(cctx, struct nss_cmd_ctx);
2140 0 : if (!cmdctx) {
2141 0 : return ENOMEM;
2142 : }
2143 0 : cmdctx->cctx = cctx;
2144 :
2145 0 : req = nss_cmd_setpwent_send(cmdctx, cctx);
2146 0 : if (!req) {
2147 0 : DEBUG(SSSDBG_FATAL_FAILURE,
2148 : "Fatal error calling nss_cmd_setpwent_send\n");
2149 0 : ret = EIO;
2150 0 : goto done;
2151 : }
2152 0 : tevent_req_set_callback(req, nss_cmd_setpwent_done, cmdctx);
2153 :
2154 : done:
2155 0 : return nss_cmd_done(cmdctx, ret);
2156 : }
2157 :
2158 : static errno_t nss_cmd_setpwent_step(struct setent_step_ctx *step_ctx);
2159 0 : struct tevent_req *nss_cmd_setpwent_send(TALLOC_CTX *mem_ctx,
2160 : struct cli_ctx *client)
2161 : {
2162 : errno_t ret;
2163 : struct nss_ctx *nctx;
2164 : struct tevent_req *req;
2165 : struct setent_ctx *state;
2166 : struct sss_domain_info *dom;
2167 : struct setent_step_ctx *step_ctx;
2168 :
2169 0 : DEBUG(SSSDBG_CONF_SETTINGS, "Received setpwent request\n");
2170 0 : nctx = talloc_get_type(client->rctx->pvt_ctx, struct nss_ctx);
2171 :
2172 : /* Reset the read pointers */
2173 0 : client->pwent_dom_idx = 0;
2174 0 : client->pwent_cur = 0;
2175 :
2176 0 : req = tevent_req_create(mem_ctx, &state, struct setent_ctx);
2177 0 : if (!req) {
2178 0 : DEBUG(SSSDBG_FATAL_FAILURE,
2179 : "Could not create tevent request for setpwent\n");
2180 0 : return NULL;
2181 : }
2182 :
2183 0 : state->nctx = nctx;
2184 0 : state->client = client;
2185 :
2186 0 : state->dctx = talloc_zero(state, struct nss_dom_ctx);
2187 0 : if (!state->dctx) {
2188 0 : ret = ENOMEM;
2189 0 : goto error;
2190 : }
2191 :
2192 : /* check if enumeration is enabled in any domain */
2193 0 : for (dom = client->rctx->domains; dom; dom = get_next_domain(dom, true)) {
2194 0 : if (dom->enumerate == true) break;
2195 : }
2196 0 : state->dctx->domain = dom;
2197 :
2198 0 : if (state->dctx->domain == NULL) {
2199 0 : DEBUG(SSSDBG_OP_FAILURE, "Enumeration disabled on all domains!\n");
2200 0 : ret = ENOENT;
2201 0 : goto error;
2202 : }
2203 :
2204 0 : state->dctx->check_provider =
2205 0 : NEED_CHECK_PROVIDER(state->dctx->domain->provider);
2206 :
2207 : /* Is the result context already available */
2208 0 : if (state->nctx->pctx) {
2209 0 : if (state->nctx->pctx->ready) {
2210 : /* All of the necessary data is in place
2211 : * We can return now, getpwent requests will work at this point
2212 : */
2213 0 : tevent_req_done(req);
2214 0 : tevent_req_post(req, state->nctx->rctx->ev);
2215 : }
2216 : else {
2217 : /* Object is still being constructed
2218 : * Register for notification when it's
2219 : * ready.
2220 : */
2221 0 : ret = nss_setent_add_ref(state, state->nctx->pctx, req);
2222 0 : if (ret != EOK) {
2223 0 : talloc_free(req);
2224 0 : return NULL;
2225 : }
2226 : }
2227 0 : return req;
2228 : }
2229 :
2230 : /* Create a new result context
2231 : * We are creating it on the nss_ctx so that it doesn't
2232 : * go away if the original request does. We will delete
2233 : * it when the refcount goes to zero;
2234 : */
2235 0 : state->nctx->pctx = talloc_zero(nctx, struct getent_ctx);
2236 0 : if (!state->nctx->pctx) {
2237 0 : ret = ENOMEM;
2238 0 : goto error;
2239 : }
2240 0 : state->getent_ctx = nctx->pctx;
2241 :
2242 : /* Add a callback reference for ourselves */
2243 0 : ret = nss_setent_add_ref(state, state->nctx->pctx, req);
2244 0 : if (ret) goto error;
2245 :
2246 : /* ok, start the searches */
2247 0 : step_ctx = talloc_zero(state->getent_ctx, struct setent_step_ctx);
2248 0 : if (!step_ctx) {
2249 0 : ret = ENOMEM;
2250 0 : goto error;
2251 : }
2252 :
2253 : /* Steal the dom_ctx onto the step_ctx so it doesn't go out of scope if
2254 : * this request is canceled while other requests are in-progress.
2255 : */
2256 0 : step_ctx->dctx = talloc_steal(step_ctx, state->dctx);
2257 0 : step_ctx->nctx = state->nctx;
2258 0 : step_ctx->getent_ctx = state->getent_ctx;
2259 0 : step_ctx->rctx = client->rctx;
2260 0 : step_ctx->cctx = client;
2261 0 : step_ctx->returned_to_mainloop = false;
2262 :
2263 0 : ret = nss_cmd_setpwent_step(step_ctx);
2264 0 : if (ret != EOK && ret != EAGAIN) goto error;
2265 :
2266 0 : if (ret == EOK) {
2267 0 : tevent_req_post(req, client->rctx->ev);
2268 : }
2269 :
2270 0 : return req;
2271 :
2272 : error:
2273 0 : tevent_req_error(req, ret);
2274 0 : tevent_req_post(req, client->rctx->ev);
2275 0 : return req;
2276 : }
2277 :
2278 : static void nss_cmd_setpwent_dp_callback(uint16_t err_maj, uint32_t err_min,
2279 : const char *err_msg, void *ptr);
2280 : static void setpwent_result_timeout(struct tevent_context *ev,
2281 : struct tevent_timer *te,
2282 : struct timeval current_time,
2283 : void *pvt);
2284 :
2285 : /* nss_cmd_setpwent_step returns
2286 : * EOK if everything is done and the request needs to be posted explicitly
2287 : * EAGAIN if the caller can safely return to the main loop
2288 : */
2289 0 : static errno_t nss_cmd_setpwent_step(struct setent_step_ctx *step_ctx)
2290 : {
2291 : errno_t ret;
2292 0 : struct sss_domain_info *dom = step_ctx->dctx->domain;
2293 0 : struct resp_ctx *rctx = step_ctx->rctx;
2294 0 : struct nss_dom_ctx *dctx = step_ctx->dctx;
2295 0 : struct getent_ctx *pctx = step_ctx->getent_ctx;
2296 0 : struct nss_ctx *nctx = step_ctx->nctx;
2297 : struct ldb_result *res;
2298 : struct timeval tv;
2299 : struct tevent_timer *te;
2300 : struct tevent_req *dpreq;
2301 : struct dp_callback_ctx *cb_ctx;
2302 :
2303 0 : while (dom) {
2304 0 : while (dom && dom->enumerate == false) {
2305 0 : dom = get_next_domain(dom, true);
2306 : }
2307 :
2308 0 : if (!dom) break;
2309 :
2310 0 : if (dom != dctx->domain) {
2311 : /* make sure we reset the check_provider flag when we check
2312 : * a new domain */
2313 0 : dctx->check_provider = NEED_CHECK_PROVIDER(dom->provider);
2314 : }
2315 :
2316 : /* make sure to update the dctx if we changed domain */
2317 0 : dctx->domain = dom;
2318 :
2319 0 : DEBUG(SSSDBG_TRACE_FUNC,
2320 : "Requesting info for domain [%s]\n", dom->name);
2321 :
2322 0 : if (dom->sysdb == NULL) {
2323 0 : DEBUG(SSSDBG_FATAL_FAILURE,
2324 : "Fatal: Sysdb CTX not found for this domain!\n");
2325 0 : return EIO;
2326 : }
2327 :
2328 : /* if this is a caching provider (or if we haven't checked the cache
2329 : * yet) then verify that the cache is uptodate */
2330 0 : if (dctx->check_provider) {
2331 0 : step_ctx->returned_to_mainloop = true;
2332 : /* Only do this once per provider */
2333 0 : dctx->check_provider = false;
2334 :
2335 0 : dpreq = sss_dp_get_account_send(step_ctx, rctx, dctx->domain, true,
2336 : SSS_DP_USER, NULL, 0, NULL);
2337 0 : if (!dpreq) {
2338 0 : DEBUG(SSSDBG_MINOR_FAILURE,
2339 : "Enum Cache refresh for domain [%s] failed."
2340 : " Trying to return what we have in cache!\n",
2341 : dom->name);
2342 : } else {
2343 0 : cb_ctx = talloc_zero(step_ctx, struct dp_callback_ctx);
2344 0 : if(!cb_ctx) {
2345 0 : talloc_zfree(dpreq);
2346 0 : return ENOMEM;
2347 : }
2348 :
2349 0 : cb_ctx->callback = nss_cmd_setpwent_dp_callback;
2350 0 : cb_ctx->ptr = step_ctx;
2351 0 : cb_ctx->cctx = step_ctx->cctx;
2352 0 : cb_ctx->mem_ctx = step_ctx;
2353 :
2354 0 : tevent_req_set_callback(dpreq, nsssrv_dp_send_acct_req_done, cb_ctx);
2355 :
2356 0 : return EAGAIN;
2357 : }
2358 : }
2359 :
2360 0 : ret = sysdb_enumpwent_with_views(dctx, dom, &res);
2361 0 : if (ret != EOK) {
2362 0 : DEBUG(SSSDBG_CRIT_FAILURE,
2363 : "Enum from cache failed, skipping domain [%s]\n",
2364 : dom->name);
2365 0 : dom = get_next_domain(dom, true);
2366 0 : continue;
2367 : }
2368 :
2369 0 : if (res->count == 0) {
2370 0 : DEBUG(SSSDBG_CONF_SETTINGS,
2371 : "Domain [%s] has no users, skipping.\n", dom->name);
2372 0 : dom = get_next_domain(dom, true);
2373 0 : continue;
2374 : }
2375 :
2376 0 : nctx->pctx->doms = talloc_realloc(pctx, pctx->doms,
2377 : struct dom_ctx, pctx->num +1);
2378 0 : if (!pctx->doms) {
2379 0 : talloc_free(pctx);
2380 0 : nctx->pctx = NULL;
2381 0 : return ENOMEM;
2382 : }
2383 :
2384 0 : nctx->pctx->doms[pctx->num].domain = dctx->domain;
2385 0 : nctx->pctx->doms[pctx->num].res = talloc_steal(pctx->doms, res);
2386 :
2387 0 : nctx->pctx->num++;
2388 :
2389 : /* do not reply until all domain searches are done */
2390 0 : dom = get_next_domain(dom, true);
2391 : }
2392 :
2393 : /* We've finished all our lookups
2394 : * The result object is now safe to read.
2395 : */
2396 0 : nctx->pctx->ready = true;
2397 :
2398 : /* Set up a lifetime timer for this result object
2399 : * We don't want this result object to outlive the
2400 : * enum cache refresh timeout
2401 : */
2402 0 : tv = tevent_timeval_current_ofs(nctx->enum_cache_timeout, 0);
2403 0 : te = tevent_add_timer(rctx->ev, nctx->pctx, tv,
2404 : setpwent_result_timeout, nctx);
2405 0 : if (!te) {
2406 0 : DEBUG(SSSDBG_FATAL_FAILURE,
2407 : "Could not set up life timer for setpwent result object. "
2408 : "Entries may become stale.\n");
2409 : }
2410 :
2411 : /* Notify the waiting clients */
2412 0 : nss_setent_notify_done(nctx->pctx);
2413 :
2414 0 : if (step_ctx->returned_to_mainloop) {
2415 0 : return EAGAIN;
2416 : } else {
2417 0 : return EOK;
2418 : }
2419 : }
2420 :
2421 0 : static void setpwent_result_timeout(struct tevent_context *ev,
2422 : struct tevent_timer *te,
2423 : struct timeval current_time,
2424 : void *pvt)
2425 : {
2426 0 : struct nss_ctx *nctx = talloc_get_type(pvt, struct nss_ctx);
2427 :
2428 0 : DEBUG(SSSDBG_CRIT_FAILURE,
2429 : "setpwent result object has expired. Cleaning up.\n");
2430 :
2431 : /* Free the passwd enumeration context.
2432 : * If additional getpwent requests come in, they will invoke
2433 : * an implicit setpwent and refresh the result object.
2434 : */
2435 0 : talloc_zfree(nctx->pctx);
2436 0 : }
2437 :
2438 0 : static void nss_cmd_setpwent_dp_callback(uint16_t err_maj, uint32_t err_min,
2439 : const char *err_msg, void *ptr)
2440 : {
2441 0 : struct setent_step_ctx *step_ctx =
2442 : talloc_get_type(ptr, struct setent_step_ctx);
2443 : int ret;
2444 :
2445 0 : if (err_maj) {
2446 0 : DEBUG(SSSDBG_OP_FAILURE,
2447 : "Unable to get information from Data Provider\n"
2448 : "Error: %u, %u, %s\n"
2449 : "Will try to return what we have in cache\n",
2450 : (unsigned int)err_maj, (unsigned int)err_min, err_msg);
2451 : }
2452 :
2453 0 : ret = nss_cmd_setpwent_step(step_ctx);
2454 0 : if (ret != EOK && ret != EAGAIN) {
2455 : /* Notify any waiting processes of failure */
2456 0 : nss_setent_notify_error(step_ctx->nctx->pctx, ret);
2457 : }
2458 0 : }
2459 :
2460 0 : static errno_t nss_cmd_setpwent_recv(struct tevent_req *req)
2461 : {
2462 0 : TEVENT_REQ_RETURN_ON_ERROR(req);
2463 0 : return EOK;
2464 : }
2465 :
2466 0 : static void nss_cmd_setpwent_done(struct tevent_req *req)
2467 : {
2468 : errno_t ret;
2469 0 : struct nss_cmd_ctx *cmdctx =
2470 0 : tevent_req_callback_data(req, struct nss_cmd_ctx);
2471 :
2472 0 : ret = nss_cmd_setpwent_recv(req);
2473 0 : talloc_zfree(req);
2474 0 : if (ret == EOK || ret == ENOENT) {
2475 : /* Either we succeeded or no domains were eligible */
2476 0 : ret = sss_packet_new(cmdctx->cctx->creq, 0,
2477 0 : sss_packet_get_cmd(cmdctx->cctx->creq->in),
2478 0 : &cmdctx->cctx->creq->out);
2479 0 : if (ret == EOK) {
2480 0 : sss_cmd_done(cmdctx->cctx, cmdctx);
2481 0 : return;
2482 : }
2483 : }
2484 :
2485 : /* Something bad happened */
2486 0 : nss_cmd_done(cmdctx, ret);
2487 : }
2488 :
2489 : static void nss_cmd_implicit_setpwent_done(struct tevent_req *req);
2490 0 : static int nss_cmd_getpwent(struct cli_ctx *cctx)
2491 : {
2492 : struct nss_ctx *nctx;
2493 : struct nss_cmd_ctx *cmdctx;
2494 : struct tevent_req *req;
2495 :
2496 0 : DEBUG(SSSDBG_CONF_SETTINGS, "Requesting info for all accounts\n");
2497 :
2498 0 : cmdctx = talloc_zero(cctx, struct nss_cmd_ctx);
2499 0 : if (!cmdctx) {
2500 0 : return ENOMEM;
2501 : }
2502 0 : cmdctx->cctx = cctx;
2503 :
2504 : /* Save the current index and cursor locations
2505 : * If we end up calling setpwent implicitly, because the response object
2506 : * expired and has to be recreated, we want to resume from the same
2507 : * location.
2508 : */
2509 0 : cmdctx->saved_dom_idx = cctx->pwent_dom_idx;
2510 0 : cmdctx->saved_cur = cctx->pwent_cur;
2511 :
2512 0 : nctx = talloc_get_type(cctx->rctx->pvt_ctx, struct nss_ctx);
2513 0 : if(!nctx->pctx || !nctx->pctx->ready) {
2514 : /* Make sure we invoke setpwent if it hasn't been run or is still
2515 : * processing from another client
2516 : */
2517 0 : req = nss_cmd_setpwent_send(cctx, cctx);
2518 0 : if (!req) {
2519 0 : return EIO;
2520 : }
2521 0 : tevent_req_set_callback(req, nss_cmd_implicit_setpwent_done, cmdctx);
2522 0 : return EOK;
2523 : }
2524 :
2525 0 : return nss_cmd_getpwent_immediate(cmdctx);
2526 : }
2527 :
2528 : static int nss_cmd_retpwent(struct cli_ctx *cctx, int num);
2529 0 : static int nss_cmd_getpwent_immediate(struct nss_cmd_ctx *cmdctx)
2530 : {
2531 0 : struct cli_ctx *cctx = cmdctx->cctx;
2532 : uint8_t *body;
2533 : size_t blen;
2534 : uint32_t num;
2535 : int ret;
2536 :
2537 : /* get max num of entries to return in one call */
2538 0 : sss_packet_get_body(cctx->creq->in, &body, &blen);
2539 0 : if (blen != sizeof(uint32_t)) {
2540 0 : return EINVAL;
2541 : }
2542 0 : SAFEALIGN_COPY_UINT32(&num, body, NULL);
2543 :
2544 : /* create response packet */
2545 0 : ret = sss_packet_new(cctx->creq, 0,
2546 0 : sss_packet_get_cmd(cctx->creq->in),
2547 0 : &cctx->creq->out);
2548 0 : if (ret != EOK) {
2549 0 : return ret;
2550 : }
2551 :
2552 0 : ret = nss_cmd_retpwent(cctx, num);
2553 :
2554 0 : sss_packet_set_error(cctx->creq->out, ret);
2555 0 : sss_cmd_done(cctx, cmdctx);
2556 :
2557 0 : return EOK;
2558 : }
2559 :
2560 0 : static int nss_cmd_retpwent(struct cli_ctx *cctx, int num)
2561 : {
2562 : struct nss_ctx *nctx;
2563 : struct getent_ctx *pctx;
2564 0 : struct ldb_message **msgs = NULL;
2565 0 : struct dom_ctx *pdom = NULL;
2566 0 : int n = 0;
2567 0 : int ret = ENOENT;
2568 :
2569 0 : nctx = talloc_get_type(cctx->rctx->pvt_ctx, struct nss_ctx);
2570 0 : if (!nctx->pctx) goto none;
2571 :
2572 0 : pctx = nctx->pctx;
2573 :
2574 0 : while (ret == ENOENT) {
2575 0 : if (cctx->pwent_dom_idx >= pctx->num) break;
2576 :
2577 0 : pdom = &pctx->doms[cctx->pwent_dom_idx];
2578 :
2579 0 : n = pdom->res->count - cctx->pwent_cur;
2580 0 : if (n <= 0 && (cctx->pwent_dom_idx+1 < pctx->num)) {
2581 0 : cctx->pwent_dom_idx++;
2582 0 : pdom = &pctx->doms[cctx->pwent_dom_idx];
2583 0 : n = pdom->res->count;
2584 0 : cctx->pwent_cur = 0;
2585 : }
2586 :
2587 0 : if (!n) break;
2588 :
2589 0 : if (n < 0) {
2590 0 : DEBUG(SSSDBG_CRIT_FAILURE, "BUG: Negative difference"
2591 : "[%d - %d = %d]\n", pdom->res->count, cctx->pwent_cur, n);
2592 0 : DEBUG(SSSDBG_CRIT_FAILURE, "Domain: %d (total %d)\n",
2593 : cctx->pwent_dom_idx, pctx->num);
2594 0 : break;
2595 : }
2596 :
2597 0 : if (n > num) n = num;
2598 :
2599 0 : msgs = &(pdom->res->msgs[cctx->pwent_cur]);
2600 :
2601 0 : ret = fill_pwent(cctx->creq->out, pdom->domain, nctx,
2602 : true, false, msgs, &n);
2603 :
2604 0 : cctx->pwent_cur += n;
2605 : }
2606 :
2607 : none:
2608 0 : if (ret == ENOENT) {
2609 0 : ret = sss_cmd_empty_packet(cctx->creq->out);
2610 : }
2611 0 : return ret;
2612 : }
2613 :
2614 0 : static void nss_cmd_implicit_setpwent_done(struct tevent_req *req)
2615 : {
2616 : errno_t ret;
2617 0 : struct nss_cmd_ctx *cmdctx =
2618 0 : tevent_req_callback_data(req, struct nss_cmd_ctx);
2619 :
2620 0 : ret = nss_cmd_setpwent_recv(req);
2621 0 : talloc_zfree(req);
2622 :
2623 : /* ENOENT is acceptable, as it just means that there were no entries
2624 : * to be returned. This will be handled gracefully in nss_cmd_retpwent
2625 : * later.
2626 : */
2627 0 : if (ret != EOK && ret != ENOENT) {
2628 0 : DEBUG(SSSDBG_FATAL_FAILURE,
2629 : "Implicit setpwent failed with unexpected error [%d][%s]\n",
2630 : ret, strerror(ret));
2631 0 : NSS_CMD_FATAL_ERROR(cmdctx);
2632 : }
2633 :
2634 : /* Restore the saved index and cursor locations */
2635 0 : cmdctx->cctx->pwent_dom_idx = cmdctx->saved_dom_idx;
2636 0 : cmdctx->cctx->pwent_cur = cmdctx->saved_cur;
2637 :
2638 0 : ret = nss_cmd_getpwent_immediate(cmdctx);
2639 0 : if (ret != EOK) {
2640 0 : DEBUG(SSSDBG_FATAL_FAILURE,
2641 : "Immediate retrieval failed with unexpected error "
2642 : "[%d][%s]\n", ret, strerror(ret));
2643 0 : NSS_CMD_FATAL_ERROR(cmdctx);
2644 : }
2645 : }
2646 :
2647 0 : static int nss_cmd_endpwent(struct cli_ctx *cctx)
2648 : {
2649 : struct nss_ctx *nctx;
2650 : int ret;
2651 :
2652 0 : DEBUG(SSSDBG_CONF_SETTINGS, "Terminating request info for all accounts\n");
2653 :
2654 0 : nctx = talloc_get_type(cctx->rctx->pvt_ctx, struct nss_ctx);
2655 :
2656 : /* create response packet */
2657 0 : ret = sss_packet_new(cctx->creq, 0,
2658 0 : sss_packet_get_cmd(cctx->creq->in),
2659 0 : &cctx->creq->out);
2660 :
2661 0 : if (ret != EOK) {
2662 0 : return ret;
2663 : }
2664 0 : if (nctx->pctx == NULL) goto done;
2665 :
2666 : /* Reset the indices so that subsequent requests start at zero */
2667 0 : cctx->pwent_dom_idx = 0;
2668 0 : cctx->pwent_cur = 0;
2669 :
2670 : done:
2671 0 : sss_cmd_done(cctx, NULL);
2672 0 : return EOK;
2673 : }
2674 :
2675 : /****************************************************************************
2676 : * GROUP db related functions
2677 : ***************************************************************************/
2678 :
2679 0 : void nss_update_gr_memcache(struct nss_ctx *nctx)
2680 : {
2681 : struct sss_domain_info *dom;
2682 : struct ldb_result *res;
2683 : uint64_t exp;
2684 : struct sized_string key;
2685 : const char *id;
2686 : time_t now;
2687 : int ret;
2688 : int i;
2689 :
2690 0 : now = time(NULL);
2691 :
2692 0 : for (dom = nctx->rctx->domains; dom; dom = get_next_domain(dom, false)) {
2693 0 : ret = sysdb_enumgrent_with_views(nctx, dom, &res);
2694 0 : if (ret != EOK) {
2695 0 : DEBUG(SSSDBG_CRIT_FAILURE,
2696 : "Failed to enumerate users for domain [%s]\n", dom->name);
2697 0 : continue;
2698 : }
2699 :
2700 0 : for (i = 0; i < res->count; i++) {
2701 0 : exp = ldb_msg_find_attr_as_uint64(res->msgs[i],
2702 : SYSDB_CACHE_EXPIRE, 0);
2703 0 : if (exp >= now) {
2704 0 : continue;
2705 : }
2706 :
2707 : /* names require more manipulation (build up fqname conditionally),
2708 : * but uidNumber is unique and always resolvable too, so we use
2709 : * that to update the cache, as it points to the same entry */
2710 0 : id = sss_view_ldb_msg_find_attr_as_string(dom, res->msgs[i],
2711 : SYSDB_GIDNUM, NULL);
2712 0 : if (!id) {
2713 0 : DEBUG(SSSDBG_CRIT_FAILURE,
2714 : "Failed to find gidNumber in %s.\n",
2715 : ldb_dn_get_linearized(res->msgs[i]->dn));
2716 0 : continue;
2717 : }
2718 0 : to_sized_string(&key, id);
2719 :
2720 0 : ret = sss_mmap_cache_gr_invalidate(nctx->grp_mc_ctx, &key);
2721 0 : if (ret != EOK && ret != ENOENT) {
2722 0 : DEBUG(SSSDBG_CRIT_FAILURE,
2723 : "Internal failure in memory cache code: %d [%s]\n",
2724 : ret, strerror(ret));
2725 : }
2726 : }
2727 0 : talloc_zfree(res);
2728 : }
2729 0 : }
2730 :
2731 : #define GID_ROFFSET 0
2732 : #define MNUM_ROFFSET sizeof(uint32_t)
2733 : #define STRS_ROFFSET 2*sizeof(uint32_t)
2734 :
2735 15 : static int parse_member(TALLOC_CTX *mem_ctx, struct sss_domain_info *group_dom,
2736 : const char *member, struct sss_domain_info **_member_dom,
2737 : struct sized_string *_name, bool *_add_domain)
2738 : {
2739 : errno_t ret;
2740 : char *username;
2741 : char *domname;
2742 : const char *use_member;
2743 : struct sss_domain_info *member_dom;
2744 : bool add_domain;
2745 :
2746 15 : ret = sss_parse_name(mem_ctx, group_dom->names, member, &domname, &username);
2747 15 : if (ret != EOK) {
2748 0 : DEBUG(SSSDBG_MINOR_FAILURE, "Could not parse [%s] into "
2749 : "name-value components.\n", member);
2750 0 : return ret;
2751 : }
2752 :
2753 15 : add_domain = (!IS_SUBDOMAIN(group_dom) && group_dom->fqnames);
2754 15 : use_member = member;
2755 15 : member_dom = group_dom;
2756 :
2757 15 : if (IS_SUBDOMAIN(group_dom) == false && domname != NULL) {
2758 : /* The group is stored in the parent domain, but the member comes from.
2759 : * a subdomain. No need to add the domain component, it's already
2760 : * present in the memberuid/ghost attribute
2761 : */
2762 2 : add_domain = false;
2763 : }
2764 :
2765 15 : if (IS_SUBDOMAIN(group_dom) == true && domname == NULL) {
2766 : /* The group is stored in a subdomain, but the member comes
2767 : * from the parent domain. Need to add the domain component
2768 : * of the parent domain
2769 : */
2770 1 : add_domain = true;
2771 1 : use_member = username;
2772 1 : member_dom = group_dom->parent;
2773 : }
2774 :
2775 15 : to_sized_string(_name, use_member);
2776 15 : *_add_domain = add_domain;
2777 15 : *_member_dom = member_dom;
2778 15 : return EOK;
2779 : }
2780 :
2781 6 : static int fill_members(struct sss_packet *packet,
2782 : struct sss_domain_info *dom,
2783 : struct nss_ctx *nctx,
2784 : struct ldb_message_element *el,
2785 : size_t *_rzero,
2786 : size_t *_rsize,
2787 : int *_memnum)
2788 : {
2789 6 : int i, ret = EOK;
2790 6 : int memnum = *_memnum;
2791 6 : size_t rzero= *_rzero;
2792 6 : size_t rsize = *_rsize;
2793 : const char *tmpstr;
2794 : struct sized_string name;
2795 6 : TALLOC_CTX *tmp_ctx = NULL;
2796 :
2797 6 : int nlen = 0;
2798 :
2799 : uint8_t *body;
2800 : size_t blen;
2801 :
2802 6 : const char *domain = dom->name;
2803 : bool add_domain;
2804 : struct sss_domain_info *member_dom;
2805 :
2806 6 : tmp_ctx = talloc_new(NULL);
2807 6 : if (tmp_ctx == NULL) {
2808 0 : return ENOMEM;
2809 : }
2810 :
2811 6 : sss_packet_get_body(packet, &body, &blen);
2812 21 : for (i = 0; i < el->num_values; i++) {
2813 15 : tmpstr = sss_get_cased_name(tmp_ctx, (char *)el->values[i].data,
2814 15 : dom->case_preserve);
2815 15 : if (tmpstr == NULL) {
2816 0 : DEBUG(SSSDBG_CRIT_FAILURE,
2817 : "sss_get_cased_name failed, skipping\n");
2818 0 : continue;
2819 : }
2820 :
2821 15 : tmpstr = sss_replace_space(tmp_ctx, tmpstr,
2822 15 : nctx->rctx->override_space);
2823 15 : if (tmpstr == NULL) {
2824 0 : DEBUG(SSSDBG_CRIT_FAILURE,
2825 : "sss_replace_space failed\n");
2826 0 : ret = ENOMEM;
2827 0 : goto done;
2828 : }
2829 :
2830 15 : if (nctx->filter_users_in_groups) {
2831 0 : ret = sss_ncache_check_user(nctx->ncache,
2832 : nctx->neg_timeout,
2833 : dom, tmpstr);
2834 0 : if (ret == EEXIST) {
2835 0 : DEBUG(SSSDBG_TRACE_FUNC,
2836 : "Group [%s] member [%s@%s] filtered out!"
2837 : " (negative cache)\n",
2838 : (char *)&body[rzero+STRS_ROFFSET], tmpstr, domain);
2839 0 : continue;
2840 : }
2841 : }
2842 :
2843 15 : ret = parse_member(tmp_ctx, dom, tmpstr, &member_dom, &name, &add_domain);
2844 15 : if (ret != EOK) {
2845 0 : DEBUG(SSSDBG_MINOR_FAILURE,
2846 : "Could not process member %s, skipping\n", tmpstr);
2847 0 : continue;
2848 : }
2849 :
2850 15 : if (add_domain) {
2851 5 : nlen = sss_fqname(NULL, 0, member_dom->names, member_dom,
2852 : name.str);
2853 5 : if (nlen >= 0) {
2854 5 : nlen += 1;
2855 : } else {
2856 : /* Other failures caught below */
2857 0 : nlen = 0;
2858 : }
2859 : } else {
2860 10 : nlen = name.len;
2861 : }
2862 :
2863 15 : ret = sss_packet_grow(packet, nlen);
2864 15 : if (ret != EOK) {
2865 0 : goto done;
2866 : }
2867 15 : sss_packet_get_body(packet, &body, &blen);
2868 :
2869 15 : if (add_domain) {
2870 10 : ret = sss_fqname((char *)&body[rzero + rsize], nlen,
2871 5 : member_dom->names, member_dom, name.str);
2872 5 : if (ret < 0 || ret != nlen - 1) {
2873 0 : DEBUG(SSSDBG_OP_FAILURE, "Failed to generate a fully qualified name"
2874 : " for member [%s@%s] of group [%s]!"
2875 : " Skipping\n", name.str, domain,
2876 : (char *)&body[rzero+STRS_ROFFSET]);
2877 : /* reclaim space */
2878 0 : ret = sss_packet_shrink(packet, nlen);
2879 0 : if (ret != EOK) {
2880 0 : goto done;
2881 : }
2882 0 : continue;
2883 : }
2884 :
2885 : } else {
2886 10 : memcpy(&body[rzero + rsize], name.str, name.len);
2887 : }
2888 :
2889 15 : rsize += nlen;
2890 15 : memnum++;
2891 : }
2892 :
2893 6 : ret = 0;
2894 :
2895 : done:
2896 6 : *_memnum = memnum;
2897 6 : *_rzero = rzero;
2898 6 : *_rsize = rsize;
2899 6 : talloc_zfree(tmp_ctx);
2900 6 : return ret;
2901 : }
2902 :
2903 9 : static int fill_grent(struct sss_packet *packet,
2904 : struct sss_domain_info *dom,
2905 : struct nss_ctx *nctx,
2906 : bool filter_groups, bool gr_mmap_cache,
2907 : struct ldb_message **msgs,
2908 : int *count)
2909 : {
2910 : struct ldb_message *msg;
2911 : struct ldb_message_element *el;
2912 : uint8_t *body;
2913 : size_t blen;
2914 : uint32_t gid;
2915 : const char *tmpstr;
2916 9 : const char *orig_name = NULL;
2917 : struct sized_string name;
2918 : struct sized_string pwfield;
2919 : struct sized_string fullname;
2920 9 : int fq_len = 0;
2921 9 : int i = 0;
2922 : int ret, num, memnum;
2923 : size_t rzero, rsize;
2924 9 : bool add_domain = (!IS_SUBDOMAIN(dom) && dom->fqnames);
2925 9 : const char *domain = dom->name;
2926 9 : TALLOC_CTX *tmp_ctx = NULL;
2927 :
2928 9 : to_sized_string(&pwfield, nctx->pwfield);
2929 :
2930 9 : num = 0;
2931 :
2932 : /* first 2 fields (len and reserved), filled up later */
2933 9 : ret = sss_packet_grow(packet, 2*sizeof(uint32_t));
2934 9 : if (ret != EOK) {
2935 0 : goto done;
2936 : }
2937 9 : sss_packet_get_body(packet, &body, &blen);
2938 9 : rzero = 2*sizeof(uint32_t);
2939 9 : rsize = 0;
2940 :
2941 18 : for (i = 0; i < *count; i++) {
2942 9 : talloc_zfree(tmp_ctx);
2943 9 : tmp_ctx = talloc_new(NULL);
2944 9 : msg = msgs[i];
2945 :
2946 : /* new group */
2947 9 : if (!ldb_msg_check_string_attribute(msg, "objectClass",
2948 : SYSDB_GROUP_CLASS)) {
2949 0 : DEBUG(SSSDBG_CRIT_FAILURE, "Wrong object (%s) found on stack!\n",
2950 : ldb_dn_get_linearized(msg->dn));
2951 0 : continue;
2952 : }
2953 :
2954 : /* new result starts at end of previous result */
2955 9 : rzero += rsize;
2956 9 : rsize = 0;
2957 :
2958 : /* find group name/gid */
2959 :
2960 : /* start with an empty name for each iteration */
2961 9 : orig_name = NULL;
2962 9 : if (DOM_HAS_VIEWS(dom)) {
2963 0 : orig_name = ldb_msg_find_attr_as_string(msg,
2964 : OVERRIDE_PREFIX SYSDB_NAME,
2965 : NULL);
2966 0 : if (orig_name != NULL && IS_SUBDOMAIN(dom)) {
2967 : /* Override names are not fully qualified */
2968 0 : add_domain = true;
2969 : }
2970 : }
2971 9 : if (orig_name == NULL) {
2972 9 : orig_name = ldb_msg_find_attr_as_string(msg,
2973 : SYSDB_DEFAULT_OVERRIDE_NAME,
2974 : NULL);
2975 9 : if (orig_name == NULL) {
2976 9 : orig_name = ldb_msg_find_attr_as_string(msg, SYSDB_NAME, NULL);
2977 : }
2978 : }
2979 :
2980 9 : gid = sss_view_ldb_msg_find_attr_as_uint64(dom, msg, SYSDB_GIDNUM, 0);
2981 9 : if (!orig_name || !gid) {
2982 0 : DEBUG(SSSDBG_OP_FAILURE,
2983 : "Incomplete group object for %s[%llu]! Skipping\n",
2984 : orig_name?orig_name:"<NULL>", (unsigned long long int)gid);
2985 0 : continue;
2986 : }
2987 :
2988 9 : if (filter_groups) {
2989 0 : ret = sss_ncache_check_group(nctx->ncache,
2990 : nctx->neg_timeout, dom, orig_name);
2991 0 : if (ret == EEXIST) {
2992 0 : DEBUG(SSSDBG_TRACE_FUNC,
2993 : "Group [%s@%s] filtered out! (negative cache)\n",
2994 : orig_name, domain);
2995 0 : continue;
2996 : }
2997 : }
2998 :
2999 9 : tmpstr = sss_get_cased_name(tmp_ctx, orig_name, dom->case_preserve);
3000 9 : if (tmpstr == NULL) {
3001 0 : DEBUG(SSSDBG_CRIT_FAILURE,
3002 : "sss_get_cased_name failed, skipping\n");
3003 0 : continue;
3004 : }
3005 :
3006 9 : tmpstr = sss_replace_space(tmp_ctx, tmpstr,
3007 9 : nctx->rctx->override_space);
3008 9 : if (tmpstr == NULL) {
3009 0 : DEBUG(SSSDBG_CRIT_FAILURE,
3010 : "sss_replace_space failed, skipping\n");
3011 0 : continue;
3012 : }
3013 :
3014 9 : to_sized_string(&name, tmpstr);
3015 :
3016 : /* fill in gid and name and set pointer for number of members */
3017 9 : rsize = STRS_ROFFSET + name.len + pwfield.len; /* name\0x\0 */
3018 :
3019 9 : if (add_domain) {
3020 2 : fq_len = sss_fqname(NULL, 0, dom->names, dom, name.str);
3021 2 : if (fq_len >= 0) {
3022 2 : fq_len += 1;
3023 2 : rsize -= name.len;
3024 2 : rsize += fq_len;
3025 : } else {
3026 : /* Other failures caught below */
3027 0 : fq_len = 0;
3028 : }
3029 : }
3030 :
3031 9 : ret = sss_packet_grow(packet, rsize);
3032 9 : if (ret != EOK) {
3033 0 : num = 0;
3034 0 : goto done;
3035 : }
3036 9 : sss_packet_get_body(packet, &body, &blen);
3037 :
3038 : /* 0-3: 32bit number gid */
3039 9 : SAFEALIGN_SET_UINT32(&body[rzero+GID_ROFFSET], gid, NULL);
3040 :
3041 : /* 4-7: 32bit unsigned number of members */
3042 9 : SAFEALIGN_SET_UINT32(&body[rzero+MNUM_ROFFSET], 0, NULL);
3043 :
3044 : /* 8-X: sequence of strings (name, passwd, mem..) */
3045 9 : if (add_domain) {
3046 2 : ret = sss_fqname((char *)&body[rzero+STRS_ROFFSET], fq_len,
3047 : dom->names, dom, name.str);
3048 2 : if (ret < 0 || ret != fq_len - 1) {
3049 0 : DEBUG(SSSDBG_CRIT_FAILURE,
3050 : "Failed to generate a fully qualified name for"
3051 : " group [%s] in [%s]! Skipping\n", name.str, domain);
3052 : /* reclaim space */
3053 0 : ret = sss_packet_shrink(packet, rsize);
3054 0 : if (ret != EOK) {
3055 0 : num = 0;
3056 0 : goto done;
3057 : }
3058 0 : rsize = 0;
3059 0 : continue;
3060 : }
3061 : } else {
3062 7 : memcpy(&body[rzero+STRS_ROFFSET], name.str, name.len);
3063 : }
3064 9 : to_sized_string(&fullname, (const char *)&body[rzero+STRS_ROFFSET]);
3065 :
3066 : /* group passwd field */
3067 18 : memcpy(&body[rzero+STRS_ROFFSET + fullname.len],
3068 9 : pwfield.str, pwfield.len);
3069 :
3070 9 : memnum = 0;
3071 9 : if (!dom->ignore_group_members) {
3072 9 : el = sss_view_ldb_msg_find_element(dom, msg, SYSDB_MEMBERUID);
3073 9 : if (el) {
3074 6 : ret = fill_members(packet, dom, nctx, el, &rzero, &rsize,
3075 : &memnum);
3076 6 : if (ret != EOK) {
3077 0 : num = 0;
3078 0 : goto done;
3079 : }
3080 6 : sss_packet_get_body(packet, &body, &blen);
3081 : }
3082 9 : el = ldb_msg_find_element(msg, SYSDB_GHOST);
3083 9 : if (el) {
3084 0 : if (DOM_HAS_VIEWS(dom) && !is_local_view(dom->view_name)
3085 0 : && el->num_values != 0) {
3086 0 : DEBUG(SSSDBG_CRIT_FAILURE,
3087 : "Domain has a view [%s] but group [%s] still has " \
3088 : "ghost members.\n", dom->view_name, orig_name);
3089 0 : num = 0;
3090 0 : goto done;
3091 : }
3092 0 : ret = fill_members(packet, dom, nctx, el, &rzero, &rsize,
3093 : &memnum);
3094 0 : if (ret != EOK) {
3095 0 : num = 0;
3096 0 : goto done;
3097 : }
3098 0 : sss_packet_get_body(packet, &body, &blen);
3099 : }
3100 : }
3101 9 : if (memnum) {
3102 : /* set num of members */
3103 6 : SAFEALIGN_SET_UINT32(&body[rzero+MNUM_ROFFSET], memnum, NULL);
3104 : }
3105 :
3106 9 : num++;
3107 :
3108 9 : if (gr_mmap_cache && nctx->grp_mc_ctx) {
3109 : /* body was reallocated, so fullname might be pointing to
3110 : * where body used to be, not where it is */
3111 0 : to_sized_string(&fullname, (const char *)&body[rzero+STRS_ROFFSET]);
3112 0 : ret = sss_mmap_cache_gr_store(&nctx->grp_mc_ctx,
3113 : &fullname, &pwfield, gid, memnum,
3114 0 : (char *)&body[rzero] + STRS_ROFFSET +
3115 0 : fullname.len + pwfield.len,
3116 0 : rsize - STRS_ROFFSET -
3117 0 : fullname.len - pwfield.len);
3118 0 : if (ret != EOK && ret != ENOMEM) {
3119 0 : DEBUG(SSSDBG_OP_FAILURE,
3120 : "Failed to store group %s(%s) in mmap cache!\n",
3121 : name.str, domain);
3122 : }
3123 : }
3124 :
3125 9 : continue;
3126 : }
3127 9 : talloc_zfree(tmp_ctx);
3128 :
3129 : done:
3130 9 : *count = i;
3131 :
3132 9 : if (num == 0) {
3133 : /* if num is 0 most probably something went wrong,
3134 : * reset packet and return ENOENT */
3135 0 : ret = sss_packet_set_size(packet, 0);
3136 0 : if (ret != EOK) return ret;
3137 0 : return ENOENT;
3138 : }
3139 :
3140 9 : SAFEALIGN_COPY_UINT32(body, &num, NULL); /* num results */
3141 9 : SAFEALIGN_SETMEM_UINT32(body + sizeof(uint32_t), 0, NULL); /* reserved */
3142 :
3143 9 : return EOK;
3144 : }
3145 :
3146 9 : static int nss_cmd_getgr_send_reply(struct nss_dom_ctx *dctx, bool filter)
3147 : {
3148 9 : struct nss_cmd_ctx *cmdctx = dctx->cmdctx;
3149 9 : struct cli_ctx *cctx = cmdctx->cctx;
3150 : struct nss_ctx *nctx;
3151 : int ret;
3152 : int i;
3153 :
3154 9 : nctx = talloc_get_type(cctx->rctx->pvt_ctx, struct nss_ctx);
3155 :
3156 18 : ret = sss_packet_new(cctx->creq, 0,
3157 9 : sss_packet_get_cmd(cctx->creq->in),
3158 9 : &cctx->creq->out);
3159 9 : if (ret != EOK) {
3160 0 : return EFAULT;
3161 : }
3162 9 : i = dctx->res->count;
3163 9 : ret = fill_grent(cctx->creq->out,
3164 : dctx->domain,
3165 : nctx, filter, true,
3166 9 : dctx->res->msgs, &i);
3167 9 : if (ret) {
3168 0 : return ret;
3169 : }
3170 9 : sss_packet_set_error(cctx->creq->out, EOK);
3171 9 : sss_cmd_done(cctx, cmdctx);
3172 9 : return EOK;
3173 : }
3174 :
3175 : /* search for a group.
3176 : * Returns:
3177 : * ENOENT, if group is definitely not found
3178 : * EAGAIN, if group is being fetched from backend via async operations
3179 : * EOK, if found
3180 : * anything else on a fatal error
3181 : */
3182 :
3183 9 : static int nss_cmd_getgrnam_search(struct nss_dom_ctx *dctx)
3184 : {
3185 9 : struct nss_cmd_ctx *cmdctx = dctx->cmdctx;
3186 9 : struct sss_domain_info *dom = dctx->domain;
3187 9 : struct cli_ctx *cctx = cmdctx->cctx;
3188 9 : char *name = NULL;
3189 : struct nss_ctx *nctx;
3190 : int ret;
3191 9 : const char *extra_flag = NULL;
3192 :
3193 9 : nctx = talloc_get_type(cctx->rctx->pvt_ctx, struct nss_ctx);
3194 :
3195 9 : while (dom) {
3196 : /* if it is a domainless search, skip domains that require fully
3197 : * qualified names instead */
3198 18 : while (dom && cmdctx->check_next && dom->fqnames) {
3199 0 : dom = get_next_domain(dom, false);
3200 : }
3201 :
3202 9 : if (!dom) break;
3203 :
3204 9 : if (dom != dctx->domain) {
3205 : /* make sure we reset the check_provider flag when we check
3206 : * a new domain */
3207 0 : dctx->check_provider = NEED_CHECK_PROVIDER(dom->provider);
3208 : }
3209 :
3210 : /* make sure to update the dctx if we changed domain */
3211 9 : dctx->domain = dom;
3212 :
3213 9 : talloc_free(name);
3214 9 : name = sss_get_cased_name(dctx, cmdctx->name, dom->case_sensitive);
3215 9 : if (!name) return ENOMEM;
3216 :
3217 9 : name = sss_reverse_replace_space(dctx, name,
3218 9 : nctx->rctx->override_space);
3219 9 : if (name == NULL) {
3220 0 : DEBUG(SSSDBG_CRIT_FAILURE,
3221 : "sss_reverse_replace_space failed\n");
3222 0 : return ENOMEM;
3223 : }
3224 :
3225 : /* verify this group has not yet been negatively cached,
3226 : * or has been permanently filtered */
3227 9 : ret = sss_ncache_check_group(nctx->ncache, nctx->neg_timeout,
3228 : dom, name);
3229 :
3230 : /* if neg cached, return we didn't find it */
3231 9 : if (ret == EEXIST) {
3232 0 : DEBUG(SSSDBG_TRACE_FUNC,
3233 : "Group [%s] does not exist in [%s]! (negative cache)\n",
3234 : name, dom->name);
3235 : /* if a multidomain search, try with next */
3236 0 : if (cmdctx->check_next) {
3237 0 : dom = get_next_domain(dom, false);
3238 0 : continue;
3239 : }
3240 : /* There are no further domains or this was a
3241 : * fully-qualified user request.
3242 : */
3243 0 : return ENOENT;
3244 : }
3245 :
3246 9 : DEBUG(SSSDBG_CONF_SETTINGS,
3247 : "Requesting info for [%s@%s]\n", name, dom->name);
3248 :
3249 9 : if (dom->sysdb == NULL) {
3250 0 : DEBUG(SSSDBG_FATAL_FAILURE,
3251 : "Fatal: Sysdb CTX not found for this domain!\n");
3252 0 : return EIO;
3253 : }
3254 :
3255 9 : ret = sysdb_getgrnam_with_views(cmdctx, dom, name, &dctx->res);
3256 9 : if (ret != EOK) {
3257 0 : DEBUG(SSSDBG_CRIT_FAILURE,
3258 : "Failed to make request to our cache!\n");
3259 0 : return EIO;
3260 : }
3261 :
3262 9 : if (dctx->res->count > 1) {
3263 0 : DEBUG(SSSDBG_FATAL_FAILURE,
3264 : "getgrnam call returned more than one result !?!\n");
3265 0 : sss_log(SSS_LOG_ERR,
3266 : "More groups have the same name [%s@%s] in SSSD cache. "
3267 : "SSSD will not work correctly.\n",
3268 : name, dom->name);
3269 0 : return ENOENT;
3270 : }
3271 :
3272 9 : if (dctx->res->count == 0 && !dctx->check_provider) {
3273 : /* set negative cache only if not result of cache check */
3274 0 : ret = sss_ncache_set_group(nctx->ncache, false, dom, name);
3275 0 : if (ret != EOK) {
3276 0 : DEBUG(SSSDBG_MINOR_FAILURE, "Cannot set negcache for %s@%s\n",
3277 : name, dom->name);
3278 : }
3279 :
3280 : /* if a multidomain search, try with next */
3281 0 : if (cmdctx->check_next) {
3282 0 : dom = get_next_domain(dom, false);
3283 0 : if (dom) continue;
3284 : }
3285 :
3286 0 : DEBUG(SSSDBG_OP_FAILURE, "No results for getgrnam call\n");
3287 :
3288 : /* Group not found in ldb -> delete group from memory cache. */
3289 0 : ret = delete_entry_from_memcache(dctx->domain, name,
3290 : nctx->grp_mc_ctx, SSS_MC_GROUP);
3291 0 : if (ret != EOK) {
3292 0 : DEBUG(SSSDBG_MINOR_FAILURE,
3293 : "Deleting group from memcache failed.\n");
3294 : }
3295 :
3296 :
3297 0 : return ENOENT;
3298 : }
3299 :
3300 : /* if this is a caching provider (or if we haven't checked the cache
3301 : * yet) then verify that the cache is uptodate */
3302 9 : if (dctx->check_provider) {
3303 :
3304 9 : if (DOM_HAS_VIEWS(dom) && (dctx->res->count == 0
3305 0 : || ldb_msg_find_attr_as_string(dctx->res->msgs[0],
3306 : OVERRIDE_PREFIX SYSDB_NAME,
3307 : NULL) != NULL)) {
3308 0 : extra_flag = EXTRA_INPUT_MAYBE_WITH_VIEW;
3309 : } else {
3310 9 : extra_flag = NULL;
3311 : }
3312 :
3313 9 : ret = check_cache(dctx, nctx, dctx->res, SSS_DP_GROUP, name, 0,
3314 : extra_flag, nss_cmd_getby_dp_callback, dctx);
3315 9 : if (ret != EOK) {
3316 : /* Anything but EOK means we should reenter the mainloop
3317 : * because we may be refreshing the cache
3318 : */
3319 0 : return ret;
3320 : }
3321 : }
3322 :
3323 : /* One result found */
3324 9 : DEBUG(SSSDBG_TRACE_FUNC,
3325 : "Returning info for group [%s@%s]\n", name, dom->name);
3326 :
3327 9 : return EOK;
3328 : }
3329 :
3330 0 : DEBUG(SSSDBG_MINOR_FAILURE,
3331 : "No matching domain found for [%s], fail!\n", cmdctx->name);
3332 0 : return ENOENT;
3333 : }
3334 :
3335 9 : static int nss_cmd_getgrnam(struct cli_ctx *cctx)
3336 : {
3337 9 : return nss_cmd_getbynam(SSS_NSS_GETGRNAM, cctx);
3338 : }
3339 :
3340 : /* search for a gid.
3341 : * Returns:
3342 : * ENOENT, if gid is definitely not found
3343 : * EAGAIN, if gid is being fetched from backend via async operations
3344 : * EOK, if found
3345 : * anything else on a fatal error
3346 : */
3347 :
3348 0 : static int nss_cmd_getgrgid_search(struct nss_dom_ctx *dctx)
3349 : {
3350 0 : struct nss_cmd_ctx *cmdctx = dctx->cmdctx;
3351 0 : struct sss_domain_info *dom = dctx->domain;
3352 0 : struct cli_ctx *cctx = cmdctx->cctx;
3353 : struct nss_ctx *nctx;
3354 : int ret;
3355 : int err;
3356 0 : const char *extra_flag = NULL;
3357 :
3358 0 : nctx = talloc_get_type(cctx->rctx->pvt_ctx, struct nss_ctx);
3359 :
3360 0 : while (dom) {
3361 :
3362 : /* check that the gid is valid for this domain */
3363 0 : if ((dom->id_min && (cmdctx->id < dom->id_min)) ||
3364 0 : (dom->id_max && (cmdctx->id > dom->id_max))) {
3365 0 : DEBUG(SSSDBG_CONF_SETTINGS,
3366 : "Gid [%"PRIu32"] does not exist in domain [%s]! "
3367 : "(id out of range)\n",
3368 : cmdctx->id, dom->name);
3369 0 : if (cmdctx->check_next) {
3370 0 : dom = get_next_domain(dom, true);
3371 0 : continue;
3372 : }
3373 0 : ret = ENOENT;
3374 0 : goto done;
3375 : }
3376 :
3377 0 : if (dom != dctx->domain) {
3378 : /* make sure we reset the check_provider flag when we check
3379 : * a new domain */
3380 0 : dctx->check_provider = NEED_CHECK_PROVIDER(dom->provider);
3381 : }
3382 :
3383 : /* make sure to update the dctx if we changed domain */
3384 0 : dctx->domain = dom;
3385 :
3386 0 : DEBUG(SSSDBG_CONF_SETTINGS,
3387 : "Requesting info for [%"PRIu32"@%s]\n", cmdctx->id, dom->name);
3388 :
3389 0 : if (dom->sysdb == NULL) {
3390 0 : DEBUG(SSSDBG_FATAL_FAILURE,
3391 : "Fatal: Sysdb CTX not found for this domain!\n");
3392 0 : ret = EIO;
3393 0 : goto done;
3394 : }
3395 :
3396 0 : ret = sysdb_getgrgid_with_views(cmdctx, dom, cmdctx->id, &dctx->res);
3397 0 : if (ret != EOK) {
3398 0 : DEBUG(SSSDBG_CRIT_FAILURE,
3399 : "Failed to make request to our cache!\n");
3400 0 : ret = EIO;
3401 0 : goto done;
3402 : }
3403 :
3404 0 : if (dctx->res->count > 1) {
3405 0 : DEBUG(SSSDBG_FATAL_FAILURE,
3406 : "getgrgid call returned more than one result !?!\n");
3407 0 : sss_log(SSS_LOG_ERR,
3408 : "More groups have the same GID [%"PRIu32"] in directory "
3409 : "server. SSSD will not work correctly.\n", cmdctx->id);
3410 0 : ret = ENOENT;
3411 0 : goto done;
3412 : }
3413 :
3414 0 : if (dctx->res->count == 0 && !dctx->check_provider) {
3415 : /* if a multidomain search, try with next */
3416 0 : if (cmdctx->check_next) {
3417 0 : dom = get_next_domain(dom, true);
3418 0 : continue;
3419 : }
3420 :
3421 : /* set negative cache only if not result of cache check */
3422 0 : DEBUG(SSSDBG_MINOR_FAILURE, "No results for getgrgid call\n");
3423 0 : ret = ENOENT;
3424 0 : goto done;
3425 : }
3426 :
3427 : /* if this is a caching provider (or if we haven't checked the cache
3428 : * yet) then verify that the cache is uptodate */
3429 0 : if (dctx->check_provider) {
3430 :
3431 0 : if (DOM_HAS_VIEWS(dom) && (dctx->res->count == 0
3432 0 : || ldb_msg_find_attr_as_uint64(dctx->res->msgs[0],
3433 : OVERRIDE_PREFIX SYSDB_GIDNUM,
3434 : 0) != 0)) {
3435 0 : extra_flag = EXTRA_INPUT_MAYBE_WITH_VIEW;
3436 : } else {
3437 0 : extra_flag = NULL;
3438 : }
3439 :
3440 0 : ret = check_cache(dctx, nctx, dctx->res, SSS_DP_GROUP, NULL,
3441 : cmdctx->id, extra_flag, nss_cmd_getby_dp_callback,
3442 : dctx);
3443 0 : if (ret != EOK) {
3444 : /* Anything but EOK means we should reenter the mainloop
3445 : * because we may be refreshing the cache
3446 : */
3447 0 : goto done;
3448 : }
3449 : }
3450 :
3451 : /* One result found */
3452 0 : DEBUG(SSSDBG_TRACE_FUNC,
3453 : "Returning info for gid [%"PRIu32"@%s]\n", cmdctx->id, dom->name);
3454 :
3455 : /* Success. Break from the loop and return EOK */
3456 0 : ret = EOK;
3457 0 : goto done;
3458 : }
3459 :
3460 : /* All domains were tried and none had the entry. */
3461 0 : ret = ENOENT;
3462 : done:
3463 0 : if (ret == ENOENT) {
3464 : /* The entry was not found, need to set result in negative cache */
3465 0 : err = sss_ncache_set_gid(nctx->ncache, false, NULL, cmdctx->id);
3466 0 : if (err != EOK) {
3467 0 : DEBUG(SSSDBG_MINOR_FAILURE,
3468 : "Cannot set negative cache for GID %"PRIu32"\n", cmdctx->id);
3469 : }
3470 : }
3471 :
3472 0 : DEBUG(SSSDBG_MINOR_FAILURE, "No matching domain found for [%"PRIu32"]\n", cmdctx->id);
3473 0 : return ret;
3474 : }
3475 :
3476 0 : static int nss_cmd_getgrgid(struct cli_ctx *cctx)
3477 : {
3478 0 : return nss_cmd_getbyid(SSS_NSS_GETGRGID, cctx);
3479 : }
3480 :
3481 : /* to keep it simple at this stage we are retrieving the
3482 : * full enumeration again for each request for each process
3483 : * and we also block on setgrent() for the full time needed
3484 : * to retrieve the data. And endgrent() frees all the data.
3485 : * Next steps are:
3486 : * - use and nsssrv wide cache with data already structured
3487 : * so that it can be immediately returned (see nscd way)
3488 : * - use mutexes so that setgrent() can return immediately
3489 : * even if the data is still being fetched
3490 : * - make getgrent() wait on the mutex
3491 : */
3492 : struct tevent_req *nss_cmd_setgrent_send(TALLOC_CTX *mem_ctx,
3493 : struct cli_ctx *client);
3494 : static void nss_cmd_setgrent_done(struct tevent_req *req);
3495 0 : static int nss_cmd_setgrent(struct cli_ctx *cctx)
3496 : {
3497 : struct nss_cmd_ctx *cmdctx;
3498 : struct tevent_req *req;
3499 0 : errno_t ret = EOK;
3500 :
3501 0 : cmdctx = talloc_zero(cctx, struct nss_cmd_ctx);
3502 0 : if (!cmdctx) {
3503 0 : return ENOMEM;
3504 : }
3505 0 : cmdctx->cctx = cctx;
3506 :
3507 0 : req = nss_cmd_setgrent_send(cmdctx, cctx);
3508 0 : if (!req) {
3509 0 : DEBUG(SSSDBG_FATAL_FAILURE,
3510 : "Fatal error calling nss_cmd_setgrent_send\n");
3511 0 : ret = EIO;
3512 0 : goto done;
3513 : }
3514 0 : tevent_req_set_callback(req, nss_cmd_setgrent_done, cmdctx);
3515 :
3516 : done:
3517 0 : return nss_cmd_done(cmdctx, ret);
3518 : }
3519 :
3520 : static errno_t nss_cmd_setgrent_step(struct setent_step_ctx *step_ctx);
3521 0 : struct tevent_req *nss_cmd_setgrent_send(TALLOC_CTX *mem_ctx,
3522 : struct cli_ctx *client)
3523 : {
3524 : errno_t ret;
3525 : struct nss_ctx *nctx;
3526 : struct tevent_req *req;
3527 : struct setent_ctx *state;
3528 : struct sss_domain_info *dom;
3529 : struct setent_step_ctx *step_ctx;
3530 :
3531 0 : DEBUG(SSSDBG_CONF_SETTINGS, "Received setgrent request\n");
3532 0 : nctx = talloc_get_type(client->rctx->pvt_ctx, struct nss_ctx);
3533 :
3534 : /* Reset the read pointers */
3535 0 : client->grent_dom_idx = 0;
3536 0 : client->grent_cur = 0;
3537 :
3538 0 : req = tevent_req_create(mem_ctx, &state, struct setent_ctx);
3539 0 : if (!req) {
3540 0 : DEBUG(SSSDBG_FATAL_FAILURE,
3541 : "Could not create tevent request for setgrent\n");
3542 0 : return NULL;
3543 : }
3544 :
3545 0 : state->nctx = nctx;
3546 0 : state->client = client;
3547 :
3548 0 : state->dctx = talloc_zero(state, struct nss_dom_ctx);
3549 0 : if (!state->dctx) {
3550 0 : ret = ENOMEM;
3551 0 : goto error;
3552 : }
3553 :
3554 : /* check if enumeration is enabled in any domain */
3555 0 : for (dom = client->rctx->domains; dom; dom = get_next_domain(dom, true)) {
3556 0 : if (dom->enumerate == true) break;
3557 : }
3558 0 : state->dctx->domain = dom;
3559 :
3560 0 : if (state->dctx->domain == NULL) {
3561 0 : DEBUG(SSSDBG_OP_FAILURE, "Enumeration disabled on all domains!\n");
3562 0 : ret = ENOENT;
3563 0 : goto error;
3564 : }
3565 :
3566 0 : state->dctx->check_provider =
3567 0 : NEED_CHECK_PROVIDER(state->dctx->domain->provider);
3568 :
3569 : /* Is the result context already available */
3570 0 : if (state->nctx->gctx) {
3571 0 : if (state->nctx->gctx->ready) {
3572 : /* All of the necessary data is in place
3573 : * We can return now, getgrent requests will work at this point
3574 : */
3575 0 : tevent_req_done(req);
3576 0 : tevent_req_post(req, state->nctx->rctx->ev);
3577 : }
3578 : else {
3579 : /* Object is still being constructed
3580 : * Register for notification when it's
3581 : * ready.
3582 : */
3583 0 : ret = nss_setent_add_ref(state, state->nctx->gctx, req);
3584 0 : if (ret != EOK) {
3585 0 : talloc_free(req);
3586 0 : return NULL;
3587 : }
3588 : }
3589 0 : return req;
3590 : }
3591 :
3592 : /* Create a new result context
3593 : * We are creating it on the nss_ctx so that it doesn't
3594 : * go away if the original request does. We will delete
3595 : * it when the refcount goes to zero;
3596 : */
3597 0 : state->nctx->gctx = talloc_zero(nctx, struct getent_ctx);
3598 0 : if (!state->nctx->gctx) {
3599 0 : ret = ENOMEM;
3600 0 : goto error;
3601 : }
3602 0 : state->getent_ctx = nctx->gctx;
3603 :
3604 : /* Add a callback reference for ourselves */
3605 0 : ret = nss_setent_add_ref(state, state->nctx->gctx, req);
3606 0 : if (ret) goto error;
3607 :
3608 : /* ok, start the searches */
3609 0 : step_ctx = talloc_zero(state->getent_ctx, struct setent_step_ctx);
3610 0 : if (!step_ctx) {
3611 0 : ret = ENOMEM;
3612 0 : goto error;
3613 : }
3614 :
3615 : /* Steal the dom_ctx onto the step_ctx so it doesn't go out of scope if
3616 : * this request is canceled while other requests are in-progress.
3617 : */
3618 0 : step_ctx->dctx = talloc_steal(step_ctx, state->dctx);
3619 0 : step_ctx->nctx = state->nctx;
3620 0 : step_ctx->getent_ctx = state->getent_ctx;
3621 0 : step_ctx->rctx = client->rctx;
3622 0 : step_ctx->cctx = client;
3623 0 : step_ctx->returned_to_mainloop = false;
3624 :
3625 0 : ret = nss_cmd_setgrent_step(step_ctx);
3626 0 : if (ret != EOK && ret != EAGAIN) goto error;
3627 :
3628 0 : if (ret == EOK) {
3629 0 : tevent_req_post(req, client->rctx->ev);
3630 : }
3631 :
3632 0 : return req;
3633 :
3634 : error:
3635 0 : tevent_req_error(req, ret);
3636 0 : tevent_req_post(req, client->rctx->ev);
3637 0 : return req;
3638 : }
3639 :
3640 : static void nss_cmd_setgrent_dp_callback(uint16_t err_maj, uint32_t err_min,
3641 : const char *err_msg, void *ptr);
3642 : static void setgrent_result_timeout(struct tevent_context *ev,
3643 : struct tevent_timer *te,
3644 : struct timeval current_time,
3645 : void *pvt);
3646 :
3647 : /* nss_cmd_setgrent_step returns
3648 : * EOK if everything is done and the request needs to be posted explicitly
3649 : * EAGAIN if the caller can safely return to the main loop
3650 : */
3651 0 : static errno_t nss_cmd_setgrent_step(struct setent_step_ctx *step_ctx)
3652 : {
3653 : errno_t ret;
3654 0 : struct sss_domain_info *dom = step_ctx->dctx->domain;
3655 0 : struct resp_ctx *rctx = step_ctx->rctx;
3656 0 : struct nss_dom_ctx *dctx = step_ctx->dctx;
3657 0 : struct getent_ctx *gctx = step_ctx->getent_ctx;
3658 0 : struct nss_ctx *nctx = step_ctx->nctx;
3659 : struct ldb_result *res;
3660 : struct timeval tv;
3661 : struct tevent_timer *te;
3662 : struct tevent_req *dpreq;
3663 : struct dp_callback_ctx *cb_ctx;
3664 :
3665 0 : while (dom) {
3666 0 : while (dom && dom->enumerate == false) {
3667 0 : dom = get_next_domain(dom, true);
3668 : }
3669 :
3670 0 : if (!dom) break;
3671 :
3672 0 : if (dom != dctx->domain) {
3673 : /* make sure we reset the check_provider flag when we check
3674 : * a new domain */
3675 0 : dctx->check_provider = NEED_CHECK_PROVIDER(dom->provider);
3676 : }
3677 :
3678 : /* make sure to update the dctx if we changed domain */
3679 0 : dctx->domain = dom;
3680 :
3681 0 : DEBUG(SSSDBG_TRACE_FUNC,
3682 : "Requesting info for domain [%s]\n", dom->name);
3683 :
3684 0 : if (dom->sysdb == NULL) {
3685 0 : DEBUG(SSSDBG_FATAL_FAILURE,
3686 : "Fatal: Sysdb CTX not found for this domain!\n");
3687 0 : return EIO;
3688 : }
3689 :
3690 : /* if this is a caching provider (or if we haven't checked the cache
3691 : * yet) then verify that the cache is uptodate */
3692 0 : if (dctx->check_provider) {
3693 0 : step_ctx->returned_to_mainloop = true;
3694 : /* Only do this once per provider */
3695 0 : dctx->check_provider = false;
3696 :
3697 0 : dpreq = sss_dp_get_account_send(step_ctx, rctx, dctx->domain, true,
3698 : SSS_DP_GROUP, NULL, 0, NULL);
3699 0 : if (!dpreq) {
3700 0 : DEBUG(SSSDBG_MINOR_FAILURE,
3701 : "Enum Cache refresh for domain [%s] failed."
3702 : " Trying to return what we have in cache!\n",
3703 : dom->name);
3704 : } else {
3705 0 : cb_ctx = talloc_zero(step_ctx, struct dp_callback_ctx);
3706 0 : if(!cb_ctx) {
3707 0 : talloc_zfree(dpreq);
3708 0 : return ENOMEM;
3709 : }
3710 :
3711 0 : cb_ctx->callback = nss_cmd_setgrent_dp_callback;
3712 0 : cb_ctx->ptr = step_ctx;
3713 0 : cb_ctx->cctx = step_ctx->cctx;
3714 0 : cb_ctx->mem_ctx = step_ctx;
3715 :
3716 0 : tevent_req_set_callback(dpreq, nsssrv_dp_send_acct_req_done, cb_ctx);
3717 :
3718 0 : return EAGAIN;
3719 : }
3720 : }
3721 :
3722 0 : ret = sysdb_enumgrent_with_views(dctx, dom, &res);
3723 0 : if (ret != EOK) {
3724 0 : DEBUG(SSSDBG_CRIT_FAILURE,
3725 : "Enum from cache failed, skipping domain [%s]\n",
3726 : dom->name);
3727 0 : dom = get_next_domain(dom, true);
3728 0 : continue;
3729 : }
3730 :
3731 0 : if (res->count == 0) {
3732 0 : DEBUG(SSSDBG_CONF_SETTINGS,
3733 : "Domain [%s] has no groups, skipping.\n", dom->name);
3734 0 : dom = get_next_domain(dom, true);
3735 0 : continue;
3736 : }
3737 :
3738 0 : nctx->gctx->doms = talloc_realloc(gctx, gctx->doms,
3739 : struct dom_ctx, gctx->num +1);
3740 0 : if (!gctx->doms) {
3741 0 : talloc_free(gctx);
3742 0 : nctx->gctx = NULL;
3743 0 : return ENOMEM;
3744 : }
3745 :
3746 0 : nctx->gctx->doms[gctx->num].domain = dctx->domain;
3747 0 : nctx->gctx->doms[gctx->num].res = talloc_steal(gctx->doms, res);
3748 :
3749 0 : nctx->gctx->num++;
3750 :
3751 : /* do not reply until all domain searches are done */
3752 0 : dom = get_next_domain(dom, true);
3753 : }
3754 :
3755 : /* We've finished all our lookups
3756 : * The result object is now safe to read.
3757 : */
3758 0 : nctx->gctx->ready = true;
3759 :
3760 : /* Set up a lifetime timer for this result object
3761 : * We don't want this result object to outlive the
3762 : * enum cache refresh timeout
3763 : */
3764 0 : tv = tevent_timeval_current_ofs(nctx->enum_cache_timeout, 0);
3765 0 : te = tevent_add_timer(rctx->ev, nctx->gctx, tv,
3766 : setgrent_result_timeout, nctx);
3767 0 : if (!te) {
3768 0 : DEBUG(SSSDBG_FATAL_FAILURE,
3769 : "Could not set up life timer for setgrent result object. "
3770 : "Entries may become stale.\n");
3771 : }
3772 :
3773 : /* Notify the waiting clients */
3774 0 : nss_setent_notify_done(nctx->gctx);
3775 :
3776 0 : if (step_ctx->returned_to_mainloop) {
3777 0 : return EAGAIN;
3778 : } else {
3779 0 : return EOK;
3780 : }
3781 :
3782 : }
3783 :
3784 0 : static void setgrent_result_timeout(struct tevent_context *ev,
3785 : struct tevent_timer *te,
3786 : struct timeval current_time,
3787 : void *pvt)
3788 : {
3789 0 : struct nss_ctx *nctx = talloc_get_type(pvt, struct nss_ctx);
3790 :
3791 0 : DEBUG(SSSDBG_CRIT_FAILURE,
3792 : "setgrent result object has expired. Cleaning up.\n");
3793 :
3794 : /* Free the group enumeration context.
3795 : * If additional getgrent requests come in, they will invoke
3796 : * an implicit setgrent and refresh the result object.
3797 : */
3798 0 : talloc_zfree(nctx->gctx);
3799 0 : }
3800 :
3801 0 : static void nss_cmd_setgrent_dp_callback(uint16_t err_maj, uint32_t err_min,
3802 : const char *err_msg, void *ptr)
3803 : {
3804 0 : struct setent_step_ctx *step_ctx =
3805 : talloc_get_type(ptr, struct setent_step_ctx);
3806 : int ret;
3807 :
3808 0 : if (err_maj) {
3809 0 : DEBUG(SSSDBG_OP_FAILURE,
3810 : "Unable to get information from Data Provider\n"
3811 : "Error: %u, %u, %s\n"
3812 : "Will try to return what we have in cache\n",
3813 : (unsigned int)err_maj, (unsigned int)err_min, err_msg);
3814 : }
3815 :
3816 0 : ret = nss_cmd_setgrent_step(step_ctx);
3817 0 : if (ret != EOK && ret != EAGAIN) {
3818 : /* Notify any waiting processes of failure */
3819 0 : nss_setent_notify_error(step_ctx->nctx->gctx, ret);
3820 : }
3821 0 : }
3822 :
3823 0 : static errno_t nss_cmd_setgrent_recv(struct tevent_req *req)
3824 : {
3825 0 : TEVENT_REQ_RETURN_ON_ERROR(req);
3826 0 : return EOK;
3827 : }
3828 :
3829 0 : static void nss_cmd_setgrent_done(struct tevent_req *req)
3830 : {
3831 : errno_t ret;
3832 0 : struct nss_cmd_ctx *cmdctx =
3833 0 : tevent_req_callback_data(req, struct nss_cmd_ctx);
3834 :
3835 0 : ret = nss_cmd_setgrent_recv(req);
3836 0 : talloc_zfree(req);
3837 0 : if (ret == EOK || ret == ENOENT) {
3838 : /* Either we succeeded or no domains were eligible */
3839 0 : ret = sss_packet_new(cmdctx->cctx->creq, 0,
3840 0 : sss_packet_get_cmd(cmdctx->cctx->creq->in),
3841 0 : &cmdctx->cctx->creq->out);
3842 0 : if (ret == EOK) {
3843 0 : sss_cmd_done(cmdctx->cctx, cmdctx);
3844 0 : return;
3845 : }
3846 : }
3847 :
3848 : /* Something bad happened */
3849 0 : nss_cmd_done(cmdctx, ret);
3850 : }
3851 :
3852 0 : static int nss_cmd_retgrent(struct cli_ctx *cctx, int num)
3853 : {
3854 : struct nss_ctx *nctx;
3855 : struct getent_ctx *gctx;
3856 0 : struct ldb_message **msgs = NULL;
3857 0 : struct dom_ctx *gdom = NULL;
3858 0 : int n = 0;
3859 0 : int ret = ENOENT;
3860 :
3861 0 : nctx = talloc_get_type(cctx->rctx->pvt_ctx, struct nss_ctx);
3862 0 : if (!nctx->gctx) goto none;
3863 :
3864 0 : gctx = nctx->gctx;
3865 :
3866 0 : while (ret == ENOENT) {
3867 0 : if (cctx->grent_dom_idx >= gctx->num) break;
3868 :
3869 0 : gdom = &gctx->doms[cctx->grent_dom_idx];
3870 :
3871 0 : n = gdom->res->count - cctx->grent_cur;
3872 0 : if (n <= 0 && (cctx->grent_dom_idx+1 < gctx->num)) {
3873 0 : cctx->grent_dom_idx++;
3874 0 : gdom = &gctx->doms[cctx->grent_dom_idx];
3875 0 : n = gdom->res->count;
3876 0 : cctx->grent_cur = 0;
3877 : }
3878 :
3879 0 : if (!n) break;
3880 :
3881 0 : if (n > num) n = num;
3882 :
3883 0 : msgs = &(gdom->res->msgs[cctx->grent_cur]);
3884 :
3885 0 : ret = fill_grent(cctx->creq->out,
3886 : gdom->domain,
3887 : nctx, true, false, msgs, &n);
3888 :
3889 0 : cctx->grent_cur += n;
3890 : }
3891 :
3892 : none:
3893 0 : if (ret == ENOENT) {
3894 0 : ret = sss_cmd_empty_packet(cctx->creq->out);
3895 : }
3896 0 : return ret;
3897 : }
3898 :
3899 0 : static int nss_cmd_getgrent_immediate(struct nss_cmd_ctx *cmdctx)
3900 : {
3901 0 : struct cli_ctx *cctx = cmdctx->cctx;
3902 : uint8_t *body;
3903 : size_t blen;
3904 : uint32_t num;
3905 : int ret;
3906 :
3907 : /* get max num of entries to return in one call */
3908 0 : sss_packet_get_body(cctx->creq->in, &body, &blen);
3909 0 : if (blen != sizeof(uint32_t)) {
3910 0 : return EINVAL;
3911 : }
3912 0 : SAFEALIGN_COPY_UINT32(&num, body, NULL);
3913 :
3914 : /* create response packet */
3915 0 : ret = sss_packet_new(cctx->creq, 0,
3916 0 : sss_packet_get_cmd(cctx->creq->in),
3917 0 : &cctx->creq->out);
3918 0 : if (ret != EOK) {
3919 0 : return ret;
3920 : }
3921 :
3922 0 : ret = nss_cmd_retgrent(cctx, num);
3923 :
3924 0 : sss_packet_set_error(cctx->creq->out, ret);
3925 0 : sss_cmd_done(cctx, cmdctx);
3926 :
3927 0 : return EOK;
3928 : }
3929 :
3930 : static void nss_cmd_implicit_setgrent_done(struct tevent_req *req);
3931 0 : static int nss_cmd_getgrent(struct cli_ctx *cctx)
3932 : {
3933 : struct nss_ctx *nctx;
3934 : struct nss_cmd_ctx *cmdctx;
3935 : struct tevent_req *req;
3936 :
3937 0 : DEBUG(SSSDBG_CONF_SETTINGS, "Requesting info for all groups\n");
3938 :
3939 0 : cmdctx = talloc_zero(cctx, struct nss_cmd_ctx);
3940 0 : if (!cmdctx) {
3941 0 : return ENOMEM;
3942 : }
3943 0 : cmdctx->cctx = cctx;
3944 :
3945 : /* Save the current index and cursor locations
3946 : * If we end up calling setgrent implicitly, because the response object
3947 : * expired and has to be recreated, we want to resume from the same
3948 : * location.
3949 : */
3950 0 : cmdctx->saved_dom_idx = cctx->grent_dom_idx;
3951 0 : cmdctx->saved_cur = cctx->grent_cur;
3952 :
3953 0 : nctx = talloc_get_type(cctx->rctx->pvt_ctx, struct nss_ctx);
3954 0 : if(!nctx->gctx || !nctx->gctx->ready) {
3955 : /* Make sure we invoke setgrent if it hasn't been run or is still
3956 : * processing from another client
3957 : */
3958 0 : req = nss_cmd_setgrent_send(cctx, cctx);
3959 0 : if (!req) {
3960 0 : return EIO;
3961 : }
3962 0 : tevent_req_set_callback(req, nss_cmd_implicit_setgrent_done, cmdctx);
3963 0 : return EOK;
3964 : }
3965 :
3966 0 : return nss_cmd_getgrent_immediate(cmdctx);
3967 : }
3968 :
3969 0 : static void nss_cmd_implicit_setgrent_done(struct tevent_req *req)
3970 : {
3971 : errno_t ret;
3972 0 : struct nss_cmd_ctx *cmdctx =
3973 0 : tevent_req_callback_data(req, struct nss_cmd_ctx);
3974 :
3975 0 : ret = nss_cmd_setgrent_recv(req);
3976 0 : talloc_zfree(req);
3977 :
3978 : /* ENOENT is acceptable, as it just means that there were no entries
3979 : * to be returned. This will be handled gracefully in nss_cmd_retpwent
3980 : * later.
3981 : */
3982 0 : if (ret != EOK && ret != ENOENT) {
3983 0 : DEBUG(SSSDBG_FATAL_FAILURE,
3984 : "Implicit setgrent failed with unexpected error [%d][%s]\n",
3985 : ret, strerror(ret));
3986 0 : NSS_CMD_FATAL_ERROR(cmdctx);
3987 : }
3988 :
3989 : /* Restore the saved index and cursor locations */
3990 0 : cmdctx->cctx->grent_dom_idx = cmdctx->saved_dom_idx;
3991 0 : cmdctx->cctx->grent_cur = cmdctx->saved_cur;
3992 :
3993 0 : ret = nss_cmd_getgrent_immediate(cmdctx);
3994 0 : if (ret != EOK) {
3995 0 : DEBUG(SSSDBG_FATAL_FAILURE,
3996 : "Immediate retrieval failed with unexpected error "
3997 : "[%d][%s]\n", ret, strerror(ret));
3998 0 : NSS_CMD_FATAL_ERROR(cmdctx);
3999 : }
4000 : }
4001 :
4002 0 : static int nss_cmd_endgrent(struct cli_ctx *cctx)
4003 : {
4004 : struct nss_ctx *nctx;
4005 : int ret;
4006 :
4007 0 : DEBUG(SSSDBG_CONF_SETTINGS, "Terminating request info for all groups\n");
4008 :
4009 0 : nctx = talloc_get_type(cctx->rctx->pvt_ctx, struct nss_ctx);
4010 :
4011 : /* create response packet */
4012 0 : ret = sss_packet_new(cctx->creq, 0,
4013 0 : sss_packet_get_cmd(cctx->creq->in),
4014 0 : &cctx->creq->out);
4015 :
4016 0 : if (ret != EOK) {
4017 0 : return ret;
4018 : }
4019 0 : if (nctx->gctx == NULL) goto done;
4020 :
4021 : /* Reset the indices so that subsequent requests start at zero */
4022 0 : cctx->grent_dom_idx = 0;
4023 0 : cctx->grent_cur = 0;
4024 :
4025 : done:
4026 0 : sss_cmd_done(cctx, NULL);
4027 0 : return EOK;
4028 : }
4029 :
4030 0 : void nss_update_initgr_memcache(struct nss_ctx *nctx,
4031 : const char *name, const char *domain,
4032 : int gnum, uint32_t *groups)
4033 0 : {
4034 0 : TALLOC_CTX *tmp_ctx = NULL;
4035 : struct sss_domain_info *dom;
4036 : struct ldb_result *res;
4037 : struct sized_string delete_name;
4038 0 : bool changed = false;
4039 : uint32_t id;
4040 0 : uint32_t gids[gnum];
4041 : int ret;
4042 : int i, j;
4043 :
4044 0 : for (dom = nctx->rctx->domains; dom; dom = get_next_domain(dom, false)) {
4045 0 : if (strcasecmp(dom->name, domain) == 0) {
4046 0 : break;
4047 : }
4048 : }
4049 :
4050 0 : if (dom == NULL) {
4051 0 : DEBUG(SSSDBG_OP_FAILURE,
4052 : "Unknown domain (%s) requested by provider\n", domain);
4053 0 : return;
4054 : }
4055 :
4056 0 : tmp_ctx = talloc_new(NULL);
4057 :
4058 0 : ret = sysdb_initgroups(tmp_ctx, dom, name, &res);
4059 0 : if (ret != EOK && ret != ENOENT) {
4060 0 : DEBUG(SSSDBG_CRIT_FAILURE,
4061 : "Failed to make request to our cache! [%d][%s]\n",
4062 : ret, strerror(ret));
4063 0 : goto done;
4064 : }
4065 :
4066 : /* copy, we need the original intact in case we need to invalidate
4067 : * all the original groups */
4068 0 : memcpy(gids, groups, gnum * sizeof(uint32_t));
4069 :
4070 0 : if (ret == ENOENT || res->count == 0) {
4071 : /* The user is gone. Invalidate the mc record */
4072 0 : to_sized_string(&delete_name, name);
4073 0 : ret = sss_mmap_cache_pw_invalidate(nctx->pwd_mc_ctx, &delete_name);
4074 0 : if (ret != EOK && ret != ENOENT) {
4075 0 : DEBUG(SSSDBG_CRIT_FAILURE,
4076 : "Internal failure in memory cache code: %d [%s]\n",
4077 : ret, strerror(ret));
4078 : }
4079 :
4080 : /* Also invalidate his groups */
4081 0 : changed = true;
4082 : } else {
4083 : /* we skip the first entry, it's the user itself */
4084 0 : for (i = 0; i < res->count; i++) {
4085 0 : id = ldb_msg_find_attr_as_uint(res->msgs[i], SYSDB_GIDNUM, 0);
4086 0 : if (id == 0) {
4087 : /* probably non-posix group, skip */
4088 0 : continue;
4089 : }
4090 0 : for (j = 0; j < gnum; j++) {
4091 0 : if (gids[j] == id) {
4092 0 : gids[j] = 0;
4093 0 : break;
4094 : }
4095 : }
4096 0 : if (j >= gnum) {
4097 : /* we couldn't find a match, this means the groups have
4098 : * changed after the refresh */
4099 0 : changed = true;
4100 0 : break;
4101 : }
4102 : }
4103 :
4104 0 : if (!changed) {
4105 0 : for (j = 0; j < gnum; j++) {
4106 0 : if (gids[j] != 0) {
4107 : /* we found an un-cleared groups, this means the groups
4108 : * have changed after the refresh (some got deleted) */
4109 0 : changed = true;
4110 0 : break;
4111 : }
4112 : }
4113 : }
4114 : }
4115 :
4116 0 : if (changed) {
4117 0 : char *fq_name = sss_tc_fqname(tmp_ctx, dom->names, dom, name);
4118 0 : if (!fq_name) {
4119 0 : DEBUG(SSSDBG_CRIT_FAILURE,
4120 : "Could not create fq name\n");
4121 0 : goto done;
4122 : }
4123 :
4124 0 : for (i = 0; i < gnum; i++) {
4125 0 : id = groups[i];
4126 :
4127 0 : ret = sss_mmap_cache_gr_invalidate_gid(nctx->grp_mc_ctx, id);
4128 0 : if (ret != EOK && ret != ENOENT) {
4129 0 : DEBUG(SSSDBG_CRIT_FAILURE,
4130 : "Internal failure in memory cache code: %d [%s]\n",
4131 : ret, strerror(ret));
4132 : }
4133 : }
4134 :
4135 0 : to_sized_string(&delete_name, fq_name);
4136 0 : ret = sss_mmap_cache_initgr_invalidate(nctx->initgr_mc_ctx,
4137 : &delete_name);
4138 0 : if (ret != EOK && ret != ENOENT) {
4139 0 : DEBUG(SSSDBG_CRIT_FAILURE,
4140 : "Internal failure in memory cache code: %d [%s]\n",
4141 : ret, strerror(ret));
4142 : }
4143 : }
4144 :
4145 : done:
4146 0 : talloc_free(tmp_ctx);
4147 : }
4148 :
4149 : /* FIXME: what about mpg, should we return the user's GID ? */
4150 : /* FIXME: should we filter out GIDs ? */
4151 5 : static int fill_initgr(struct sss_packet *packet,
4152 : struct sss_domain_info *dom,
4153 : struct ldb_result *res,
4154 : struct nss_ctx *nctx,
4155 : const char *mc_name,
4156 : const char *name)
4157 : {
4158 : uint8_t *body;
4159 : size_t blen;
4160 : gid_t gid;
4161 : int ret, i;
4162 : uint32_t num;
4163 : size_t bindex;
4164 5 : int skipped = 0;
4165 : const char *posix;
4166 : gid_t orig_primary_gid;
4167 : struct sized_string rawname;
4168 : uint8_t *gids;
4169 :
4170 5 : if (res->count == 0) {
4171 0 : return ENOENT;
4172 : }
4173 :
4174 : /* one less, the first one is the user entry */
4175 5 : num = res->count -1;
4176 :
4177 5 : ret = sss_packet_grow(packet, (2 + res->count) * sizeof(uint32_t));
4178 5 : if (ret != EOK) {
4179 0 : return ret;
4180 : }
4181 5 : sss_packet_get_body(packet, &body, &blen);
4182 :
4183 5 : orig_primary_gid = sss_view_ldb_msg_find_attr_as_uint64(dom, res->msgs[0],
4184 : SYSDB_PRIMARY_GROUP_GIDNUM,
4185 : 0);
4186 :
4187 : /* If the GID of the original primary group is available but equal to the
4188 : * current primary GID it must not be added. */
4189 5 : if (orig_primary_gid != 0) {
4190 0 : gid = sss_view_ldb_msg_find_attr_as_uint64(dom, res->msgs[0],
4191 : SYSDB_GIDNUM, 0);
4192 :
4193 0 : if (orig_primary_gid == gid) {
4194 0 : orig_primary_gid = 0;
4195 : }
4196 : }
4197 :
4198 : /* 0-3: 32bit unsigned number of results
4199 : * 4-7: 32bit unsigned (reserved/padding) */
4200 5 : bindex = 2 * sizeof(uint32_t);
4201 5 : gids = body + bindex;
4202 :
4203 : /* skip first entry, it's the user entry */
4204 15 : for (i = 0; i < num; i++) {
4205 10 : gid = sss_view_ldb_msg_find_attr_as_uint64(dom, res->msgs[i + 1],
4206 : SYSDB_GIDNUM, 0);
4207 10 : posix = ldb_msg_find_attr_as_string(res->msgs[i + 1],
4208 : SYSDB_POSIX, NULL);
4209 10 : if (!gid) {
4210 0 : if (posix && strcmp(posix, "FALSE") == 0) {
4211 0 : skipped++;
4212 0 : continue;
4213 : } else {
4214 0 : DEBUG(SSSDBG_CRIT_FAILURE,
4215 : "Incomplete group object for initgroups! Aborting\n");
4216 0 : return EFAULT;
4217 : }
4218 : }
4219 10 : SAFEALIGN_COPY_UINT32(body + bindex, &gid, &bindex);
4220 :
4221 : /* do not add the GID of the original primary group is the user is
4222 : * already and explicit member of the group. */
4223 10 : if (orig_primary_gid == gid) {
4224 0 : orig_primary_gid = 0;
4225 : }
4226 : }
4227 :
4228 5 : if (orig_primary_gid != 0) {
4229 0 : SAFEALIGN_COPY_UINT32(body + bindex, &orig_primary_gid, &bindex);
4230 0 : num++;
4231 : }
4232 :
4233 5 : SAFEALIGN_SETMEM_UINT32(body, num - skipped, NULL); /* num results */
4234 5 : SAFEALIGN_SETMEM_UINT32(body + sizeof(uint32_t), 0, NULL); /* reserved */
4235 5 : blen = bindex;
4236 5 : ret = sss_packet_set_size(packet, blen);
4237 5 : if (ret != EOK) {
4238 0 : DEBUG(SSSDBG_OP_FAILURE,
4239 : "Could not set packet size to value:%zu\n", blen);
4240 0 : return ret;
4241 : }
4242 :
4243 5 : if (nctx->initgr_mc_ctx) {
4244 : struct sized_string unique_name;
4245 0 : char *fq_name = sss_tc_fqname(packet, dom->names, dom, name);
4246 0 : if (!fq_name) {
4247 0 : DEBUG(SSSDBG_CRIT_FAILURE,
4248 : "Could not create fq name\n");
4249 0 : return ENOMEM;
4250 : }
4251 :
4252 0 : to_sized_string(&rawname, mc_name);
4253 0 : to_sized_string(&unique_name, fq_name);
4254 0 : ret = sss_mmap_cache_initgr_store(&nctx->initgr_mc_ctx, &rawname,
4255 : &unique_name, num - skipped, gids);
4256 0 : if (ret != EOK && ret != ENOMEM) {
4257 0 : DEBUG(SSSDBG_CRIT_FAILURE,
4258 : "Failed to store user %s(%s) in mmap cache!\n",
4259 : rawname.str, dom->name);
4260 : }
4261 : }
4262 :
4263 5 : return EOK;
4264 : }
4265 :
4266 5 : static int nss_cmd_initgr_send_reply(struct nss_dom_ctx *dctx)
4267 : {
4268 5 : struct nss_cmd_ctx *cmdctx = dctx->cmdctx;
4269 5 : struct cli_ctx *cctx = cmdctx->cctx;
4270 : struct nss_ctx *nctx;
4271 : int ret;
4272 :
4273 5 : nctx = talloc_get_type(cctx->rctx->pvt_ctx, struct nss_ctx);
4274 :
4275 10 : ret = sss_packet_new(cctx->creq, 0,
4276 5 : sss_packet_get_cmd(cctx->creq->in),
4277 5 : &cctx->creq->out);
4278 5 : if (ret != EOK) {
4279 0 : return EFAULT;
4280 : }
4281 :
4282 5 : ret = fill_initgr(cctx->creq->out, dctx->domain, dctx->res, nctx,
4283 : dctx->mc_name, cmdctx->normalized_name);
4284 5 : if (ret) {
4285 0 : return ret;
4286 : }
4287 5 : sss_packet_set_error(cctx->creq->out, EOK);
4288 5 : sss_cmd_done(cctx, cmdctx);
4289 5 : return EOK;
4290 : }
4291 :
4292 15 : static int nss_cmd_initgroups_search(struct nss_dom_ctx *dctx)
4293 : {
4294 15 : struct nss_cmd_ctx *cmdctx = dctx->cmdctx;
4295 15 : struct sss_domain_info *dom = dctx->domain;
4296 15 : struct cli_ctx *cctx = cmdctx->cctx;
4297 15 : char *name = NULL;
4298 : struct nss_ctx *nctx;
4299 : int ret;
4300 : static const char *user_attrs[] = SYSDB_PW_ATTRS;
4301 : struct ldb_message *msg;
4302 : const char *sysdb_name;
4303 : size_t c;
4304 15 : const char *extra_flag = NULL;
4305 :
4306 15 : nctx = talloc_get_type(cctx->rctx->pvt_ctx, struct nss_ctx);
4307 :
4308 15 : while (dom) {
4309 : /* if it is a domainless search, skip domains that require fully
4310 : * qualified names instead */
4311 30 : while (dom && cmdctx->check_next && dom->fqnames
4312 0 : && !cmdctx->name_is_upn) {
4313 0 : dom = get_next_domain(dom, false);
4314 : }
4315 :
4316 15 : if (!dom) break;
4317 :
4318 15 : if (dom != dctx->domain) {
4319 : /* make sure we reset the check_provider flag when we check
4320 : * a new domain */
4321 0 : dctx->check_provider = NEED_CHECK_PROVIDER(dom->provider);
4322 : }
4323 :
4324 : /* make sure to update the dctx if we changed domain */
4325 15 : dctx->domain = dom;
4326 :
4327 15 : talloc_zfree(cmdctx->normalized_name);
4328 15 : name = sss_get_cased_name(dctx, cmdctx->name, dom->case_sensitive);
4329 15 : if (!name) return ENOMEM;
4330 :
4331 15 : name = sss_reverse_replace_space(cmdctx, name,
4332 15 : nctx->rctx->override_space);
4333 : /* save name so it can be used in initgr reply */
4334 15 : cmdctx->normalized_name = name;
4335 15 : if (name == NULL) {
4336 0 : DEBUG(SSSDBG_CRIT_FAILURE,
4337 : "sss_reverse_replace_space failed\n");
4338 0 : return ENOMEM;
4339 : }
4340 :
4341 : /* verify this user has not yet been negatively cached,
4342 : * or has been permanently filtered */
4343 15 : ret = sss_ncache_check_user(nctx->ncache, nctx->neg_timeout,
4344 : dom, name);
4345 :
4346 : /* if neg cached, return we didn't find it */
4347 15 : if (ret == EEXIST) {
4348 3 : DEBUG(SSSDBG_TRACE_FUNC,
4349 : "User [%s] does not exist in [%s]! (negative cache)\n",
4350 : name, dom->name);
4351 : /* if a multidomain search, try with next */
4352 3 : if (cmdctx->check_next) {
4353 3 : dom = get_next_domain(dom, false);
4354 3 : continue;
4355 : }
4356 : /* There are no further domains or this was a
4357 : * fully-qualified user request.
4358 : */
4359 0 : return ENOENT;
4360 : }
4361 :
4362 12 : DEBUG(SSSDBG_CONF_SETTINGS,
4363 : "Requesting info for [%s@%s]\n", name, dom->name);
4364 :
4365 12 : if (dom->sysdb == NULL) {
4366 0 : DEBUG(SSSDBG_FATAL_FAILURE,
4367 : "Fatal: Sysdb CTX not found for this domain!\n");
4368 0 : return EIO;
4369 : }
4370 :
4371 12 : if (cmdctx->name_is_upn) {
4372 3 : ret = sysdb_search_user_by_upn(cmdctx, dom, name, user_attrs, &msg);
4373 3 : if (ret == ENOENT) {
4374 2 : dctx->res = talloc_zero(cmdctx, struct ldb_result);
4375 2 : if (dctx->res == NULL) {
4376 0 : DEBUG(SSSDBG_OP_FAILURE, "talloc_zero failed.\n");
4377 0 : return ENOMEM;
4378 : }
4379 :
4380 2 : dctx->res->count = 0;
4381 2 : dctx->res->msgs = NULL;
4382 2 : ret = EOK;
4383 1 : } else if (ret != EOK) {
4384 0 : DEBUG(SSSDBG_OP_FAILURE, "sysdb_search_user_by_upn failed.\n");
4385 0 : return ret;
4386 : } else {
4387 1 : sysdb_name = ldb_msg_find_attr_as_string(msg, SYSDB_NAME, NULL);
4388 1 : if (sysdb_name == NULL) {
4389 0 : DEBUG(SSSDBG_OP_FAILURE,
4390 : "Sysdb entry does not have a name.\n");
4391 0 : return EINVAL;
4392 : }
4393 :
4394 1 : ret = sysdb_initgroups(cmdctx, dom, sysdb_name, &dctx->res);
4395 1 : if (ret == EOK && DOM_HAS_VIEWS(dom)) {
4396 0 : for (c = 0; c < dctx->res->count; c++) {
4397 0 : ret = sysdb_add_overrides_to_object(dom, dctx->res->msgs[c],
4398 : NULL, NULL);
4399 0 : if (ret != EOK) {
4400 0 : DEBUG(SSSDBG_OP_FAILURE,
4401 : "sysdb_add_overrides_to_object failed.\n");
4402 0 : return ret;
4403 : }
4404 : }
4405 : }
4406 : }
4407 : } else {
4408 9 : ret = sysdb_initgroups_with_views(cmdctx, dom, name, &dctx->res);
4409 : }
4410 12 : if (ret != EOK) {
4411 0 : DEBUG(SSSDBG_CRIT_FAILURE,
4412 : "Failed to make request to our cache! [%d][%s]\n",
4413 : ret, strerror(ret));
4414 0 : return EIO;
4415 : }
4416 :
4417 12 : if (dctx->res->count == 0 && !dctx->check_provider) {
4418 : /* set negative cache only if not result of cache check */
4419 2 : ret = sss_ncache_set_user(nctx->ncache, false, dom, name);
4420 2 : if (ret != EOK) {
4421 0 : DEBUG(SSSDBG_MINOR_FAILURE, "Cannot set negcache for %s@%s\n",
4422 : name, dom->name);
4423 : }
4424 :
4425 : /* if a multidomain search, try with next */
4426 2 : if (cmdctx->check_next) {
4427 2 : dom = get_next_domain(dom, false);
4428 2 : if (dom) continue;
4429 : }
4430 :
4431 2 : DEBUG(SSSDBG_OP_FAILURE, "No results for initgroups call\n");
4432 :
4433 2 : return ENOENT;
4434 : }
4435 :
4436 : /* if this is a caching provider (or if we haven't checked the cache
4437 : * yet) then verify that the cache is uptodate */
4438 10 : if (dctx->check_provider) {
4439 :
4440 7 : if (cmdctx->name_is_upn) {
4441 2 : extra_flag = EXTRA_NAME_IS_UPN;
4442 5 : } else if (DOM_HAS_VIEWS(dom) && (dctx->res->count == 0
4443 0 : || ldb_msg_find_attr_as_string(dctx->res->msgs[0],
4444 : OVERRIDE_PREFIX SYSDB_NAME,
4445 : NULL) != NULL)) {
4446 0 : extra_flag = EXTRA_INPUT_MAYBE_WITH_VIEW;
4447 : } else {
4448 5 : extra_flag = NULL;
4449 : }
4450 :
4451 7 : ret = check_cache(dctx, nctx, dctx->res, SSS_DP_INITGROUPS, name, 0,
4452 : extra_flag, nss_cmd_getby_dp_callback, dctx);
4453 7 : if (ret != EOK) {
4454 : /* Anything but EOK means we should reenter the mainloop
4455 : * because we may be refreshing the cache
4456 : */
4457 5 : return ret;
4458 : }
4459 : }
4460 :
4461 5 : DEBUG(SSSDBG_TRACE_FUNC,
4462 : "Initgroups for [%s@%s] completed\n", name, dom->name);
4463 5 : return EOK;
4464 : }
4465 :
4466 3 : DEBUG(SSSDBG_MINOR_FAILURE,
4467 : "No matching domain found for [%s], fail!\n", cmdctx->name);
4468 3 : return ENOENT;
4469 : }
4470 :
4471 : /* for now, if we are online, try to always query the backend */
4472 9 : static int nss_cmd_initgroups(struct cli_ctx *cctx)
4473 : {
4474 9 : return nss_cmd_getbynam(SSS_NSS_INITGR, cctx);
4475 : }
4476 :
4477 3 : static errno_t nss_cmd_getsidby_search(struct nss_dom_ctx *dctx)
4478 : {
4479 3 : struct nss_cmd_ctx *cmdctx = dctx->cmdctx;
4480 3 : struct sss_domain_info *dom = dctx->domain;
4481 3 : struct cli_ctx *cctx = cmdctx->cctx;
4482 : struct sysdb_ctx *sysdb;
4483 : struct nss_ctx *nctx;
4484 : int ret;
4485 : int err;
4486 3 : const char *default_attrs[] = {SYSDB_NAME, SYSDB_OBJECTCLASS, SYSDB_SID_STR,
4487 : ORIGINALAD_PREFIX SYSDB_NAME,
4488 : ORIGINALAD_PREFIX SYSDB_UIDNUM,
4489 : ORIGINALAD_PREFIX SYSDB_GIDNUM,
4490 : ORIGINALAD_PREFIX SYSDB_GECOS,
4491 : ORIGINALAD_PREFIX SYSDB_HOMEDIR,
4492 : ORIGINALAD_PREFIX SYSDB_SHELL,
4493 : SYSDB_UPN,
4494 : SYSDB_DEFAULT_OVERRIDE_NAME,
4495 : SYSDB_AD_ACCOUNT_EXPIRES,
4496 : SYSDB_AD_USER_ACCOUNT_CONTROL,
4497 : SYSDB_SSH_PUBKEY,
4498 : SYSDB_ORIG_DN,
4499 : SYSDB_ORIG_MEMBEROF,
4500 : SYSDB_DEFAULT_ATTRS, NULL};
4501 : const char **attrs;
4502 3 : bool user_found = false;
4503 3 : bool group_found = false;
4504 3 : struct ldb_message *msg = NULL;
4505 3 : char *sysdb_name = NULL;
4506 3 : char *name = NULL;
4507 : char *req_name;
4508 : uint32_t req_id;
4509 : enum sss_dp_acct_type req_type;
4510 :
4511 3 : nctx = talloc_get_type(cctx->rctx->pvt_ctx, struct nss_ctx);
4512 :
4513 3 : while (dom) {
4514 :
4515 3 : if (cmdctx->cmd == SSS_NSS_GETSIDBYID) {
4516 : /* check that the uid is valid for this domain */
4517 0 : if ((dom->id_min && (cmdctx->id < dom->id_min)) ||
4518 0 : (dom->id_max && (cmdctx->id > dom->id_max))) {
4519 0 : DEBUG(SSSDBG_TRACE_FUNC,
4520 : "Uid [%"PRIu32"] does not exist in domain [%s]! "
4521 : "(id out of range)\n",
4522 : cmdctx->id, dom->name);
4523 0 : if (cmdctx->check_next) {
4524 0 : dom = get_next_domain(dom, true);
4525 0 : continue;
4526 : }
4527 0 : ret = ENOENT;
4528 0 : goto done;
4529 : }
4530 : } else {
4531 : /* if it is a domainless search, skip domains that require fully
4532 : * qualified names instead */
4533 6 : while (dom && cmdctx->check_next && dom->fqnames) {
4534 0 : dom = get_next_domain(dom, false);
4535 : }
4536 :
4537 3 : if (!dom) break;
4538 : }
4539 :
4540 3 : if (dom != dctx->domain) {
4541 : /* make sure we reset the check_provider flag when we check
4542 : * a new domain */
4543 0 : dctx->check_provider = NEED_CHECK_PROVIDER(dom->provider);
4544 : }
4545 :
4546 : /* make sure to update the dctx if we changed domain */
4547 3 : dctx->domain = dom;
4548 :
4549 3 : if (cmdctx->cmd == SSS_NSS_GETSIDBYID) {
4550 0 : DEBUG(SSSDBG_TRACE_FUNC, "Requesting info for [%"PRIu32"@%s]\n",
4551 : cmdctx->id, dom->name);
4552 :
4553 0 : ret = sss_ncache_check_uid(nctx->ncache, nctx->neg_timeout, dom,
4554 : cmdctx->id);
4555 0 : if (ret == EEXIST) {
4556 0 : ret = sss_ncache_check_gid(nctx->ncache, nctx->neg_timeout, dom,
4557 : cmdctx->id);
4558 0 : if (ret == EEXIST) {
4559 0 : DEBUG(SSSDBG_TRACE_FUNC,
4560 : "ID [%"PRIu32"] does not exist in [%s]! (negative cache)\n",
4561 : cmdctx->id, dom->name);
4562 : /* if a multidomain search, try with next, including
4563 : * sub-domains */
4564 0 : if (cmdctx->check_next) {
4565 0 : dom = get_next_domain(dom, true);
4566 0 : continue;
4567 : }
4568 : /* There are no further domains. */
4569 0 : ret = ENOENT;
4570 0 : goto done;
4571 : }
4572 : }
4573 :
4574 : } else {
4575 3 : talloc_free(name);
4576 3 : talloc_zfree(sysdb_name);
4577 :
4578 3 : name = sss_get_cased_name(cmdctx, cmdctx->name, dom->case_sensitive);
4579 3 : if (name == NULL) {
4580 0 : DEBUG(SSSDBG_OP_FAILURE, "sss_get_cased_name failed.\n");
4581 0 : ret = ENOMEM;
4582 0 : goto done;
4583 : }
4584 :
4585 3 : name = sss_reverse_replace_space(dctx, name,
4586 3 : nctx->rctx->override_space);
4587 3 : if (name == NULL) {
4588 0 : DEBUG(SSSDBG_CRIT_FAILURE,
4589 : "sss_reverse_replace_space failed\n");
4590 0 : ret = ENOMEM;
4591 0 : goto done;
4592 : }
4593 :
4594 : /* For subdomains a fully qualified name is needed for
4595 : * sysdb_search_user_by_name and sysdb_search_group_by_name. */
4596 3 : if (IS_SUBDOMAIN(dom)) {
4597 0 : sysdb_name = sss_tc_fqname(cmdctx, dom->names, dom, name);
4598 0 : if (sysdb_name == NULL) {
4599 0 : DEBUG(SSSDBG_OP_FAILURE, "talloc_asprintf failed.\n");
4600 0 : ret = ENOMEM;
4601 0 : goto done;
4602 : }
4603 : }
4604 :
4605 :
4606 : /* verify this name has not yet been negatively cached, as user
4607 : * and groupm, or has been permanently filtered */
4608 3 : ret = sss_ncache_check_user(nctx->ncache, nctx->neg_timeout,
4609 : dom, name);
4610 :
4611 3 : if (ret == EEXIST) {
4612 0 : ret = sss_ncache_check_group(nctx->ncache, nctx->neg_timeout,
4613 : dom, name);
4614 0 : if (ret == EEXIST) {
4615 : /* if neg cached, return we didn't find it */
4616 0 : DEBUG(SSSDBG_TRACE_FUNC,
4617 : "SID [%s] does not exist in [%s]! (negative cache)\n",
4618 : name, dom->name);
4619 : /* if a multidomain search, try with next */
4620 0 : if (cmdctx->check_next) {
4621 0 : dom = get_next_domain(dom, false);
4622 0 : continue;
4623 : }
4624 : /* There are no further domains or this was a
4625 : * fully-qualified user request.
4626 : */
4627 0 : ret = ENOENT;
4628 0 : goto done;
4629 : }
4630 : }
4631 :
4632 3 : DEBUG(SSSDBG_TRACE_FUNC, "Requesting info for [%s@%s]\n",
4633 : name, dom->name);
4634 : }
4635 :
4636 :
4637 3 : sysdb = dom->sysdb;
4638 3 : if (sysdb == NULL) {
4639 0 : DEBUG(SSSDBG_FATAL_FAILURE,
4640 : "Fatal: Sysdb CTX not found for this domain!\n");
4641 0 : ret = EIO;
4642 0 : goto done;
4643 : }
4644 :
4645 3 : attrs = default_attrs;
4646 3 : if (cmdctx->cmd == SSS_NSS_GETORIGBYNAME
4647 3 : && nctx->extra_attributes != NULL) {
4648 2 : ret = add_strings_lists(cmdctx, default_attrs,
4649 : nctx->extra_attributes, false,
4650 : discard_const(&attrs));
4651 2 : if (ret != EOK) {
4652 0 : DEBUG(SSSDBG_OP_FAILURE, "add_strings_lists failed.\n");
4653 0 : goto done;
4654 : }
4655 : }
4656 :
4657 3 : if (cmdctx->cmd == SSS_NSS_GETSIDBYID) {
4658 0 : ret = sysdb_search_user_by_uid(cmdctx, dom, cmdctx->id, attrs,
4659 : &msg);
4660 0 : if (ret != EOK && ret != ENOENT) {
4661 0 : DEBUG(SSSDBG_CRIT_FAILURE,
4662 : "Failed to make request to our cache!\n");
4663 0 : ret = EIO;
4664 0 : goto done;
4665 : }
4666 :
4667 0 : if (ret == EOK) {
4668 0 : user_found = true;
4669 : } else {
4670 0 : talloc_free(msg);
4671 0 : ret = sysdb_search_group_by_gid(cmdctx, dom, cmdctx->id, attrs,
4672 : &msg);
4673 0 : if (ret != EOK && ret != ENOENT) {
4674 0 : DEBUG(SSSDBG_CRIT_FAILURE,
4675 : "Failed to make request to our cache!\n");
4676 0 : ret = EIO;
4677 0 : goto done;
4678 : }
4679 :
4680 0 : if (ret == EOK) {
4681 0 : group_found = true;
4682 : }
4683 : }
4684 : } else {
4685 3 : ret = sysdb_search_user_by_name(cmdctx, dom,
4686 : sysdb_name ? sysdb_name : name,
4687 : attrs, &msg);
4688 3 : if (ret != EOK && ret != ENOENT) {
4689 0 : DEBUG(SSSDBG_CRIT_FAILURE,
4690 : "Failed to make request to our cache!\n");
4691 0 : ret = EIO;
4692 0 : goto done;
4693 : }
4694 :
4695 3 : if (ret == EOK) {
4696 3 : user_found = true;
4697 : } else {
4698 0 : talloc_free(msg);
4699 0 : ret = sysdb_search_group_by_name(cmdctx, dom,
4700 : sysdb_name ? sysdb_name : name,
4701 : attrs, &msg);
4702 0 : if (ret != EOK && ret != ENOENT) {
4703 0 : DEBUG(SSSDBG_CRIT_FAILURE,
4704 : "Failed to make request to our cache!\n");
4705 0 : ret = EIO;
4706 0 : goto done;
4707 : }
4708 :
4709 0 : if (ret == EOK) {
4710 0 : group_found = true;
4711 : }
4712 : }
4713 : }
4714 :
4715 3 : dctx->res = talloc_zero(cmdctx, struct ldb_result);
4716 3 : if (dctx->res == NULL) {
4717 0 : DEBUG(SSSDBG_OP_FAILURE, "talloc_zero failed.\n");
4718 0 : ret = ENOMEM;
4719 0 : goto done;
4720 : }
4721 :
4722 3 : if (user_found || group_found) {
4723 3 : dctx->res->count = 1;
4724 3 : dctx->res->msgs = talloc_array(dctx->res, struct ldb_message *, 1);
4725 3 : if (dctx->res->msgs == NULL) {
4726 0 : DEBUG(SSSDBG_OP_FAILURE, "talloc_array failed.\n");
4727 0 : ret = ENOMEM;
4728 0 : goto done;
4729 : }
4730 3 : dctx->res->msgs[0] = talloc_steal(dctx->res, msg);
4731 : }
4732 :
4733 3 : if (dctx->res->count == 0 && !dctx->check_provider) {
4734 0 : if (cmdctx->cmd == SSS_NSS_GETSIDBYNAME
4735 0 : || cmdctx->cmd == SSS_NSS_GETORIGBYNAME) {
4736 0 : ret = sss_ncache_set_user(nctx->ncache, false, dom, name);
4737 0 : if (ret != EOK) {
4738 0 : DEBUG(SSSDBG_MINOR_FAILURE,
4739 : "Cannot set negcache for %s@%s\n", name, dom->name);
4740 : }
4741 :
4742 0 : ret = sss_ncache_set_group(nctx->ncache, false, dom, name);
4743 0 : if (ret != EOK) {
4744 0 : DEBUG(SSSDBG_MINOR_FAILURE,
4745 : "Cannot set negcache for %s@%s\n", name, dom->name);
4746 : }
4747 : }
4748 : /* if a multidomain search, try with next */
4749 0 : if (cmdctx->check_next) {
4750 0 : dom = get_next_domain(dom, true);
4751 0 : continue;
4752 : }
4753 :
4754 0 : DEBUG(SSSDBG_OP_FAILURE, "No matching user or group found.\n");
4755 0 : ret = ENOENT;
4756 0 : goto done;
4757 : }
4758 :
4759 : /* if this is a caching provider (or if we haven't checked the cache
4760 : * yet) then verify that the cache is uptodate */
4761 3 : if (dctx->check_provider) {
4762 3 : if (cmdctx->cmd == SSS_NSS_GETSIDBYID) {
4763 0 : req_name = NULL;
4764 0 : req_id = cmdctx->id;
4765 : } else {
4766 3 : req_name = name;
4767 3 : req_id = 0;
4768 : }
4769 3 : if (user_found) {
4770 3 : req_type = SSS_DP_USER;
4771 0 : } else if (group_found) {
4772 0 : req_type = SSS_DP_GROUP;
4773 : } else {
4774 0 : req_type = SSS_DP_USER_AND_GROUP;
4775 : }
4776 :
4777 3 : ret = check_cache(dctx, nctx, dctx->res,
4778 : req_type, req_name, req_id, NULL,
4779 : nss_cmd_getby_dp_callback,
4780 : dctx);
4781 3 : if (ret != EOK) {
4782 : /* Anything but EOK means we should reenter the mainloop
4783 : * because we may be refreshing the cache
4784 : */
4785 0 : goto done;
4786 : }
4787 : }
4788 :
4789 : /* One result found */
4790 3 : if (cmdctx->cmd == SSS_NSS_GETSIDBYID) {
4791 0 : DEBUG(SSSDBG_TRACE_FUNC, "Returning info for id [%"PRIu32"@%s]\n",
4792 : cmdctx->id, dom->name);
4793 : } else {
4794 3 : DEBUG(SSSDBG_TRACE_FUNC, "Returning info for user/group [%s@%s]\n",
4795 : name, dom->name);
4796 : }
4797 :
4798 : /* Success. Break from the loop and return EOK */
4799 3 : ret = EOK;
4800 3 : goto done;
4801 : }
4802 :
4803 : /* All domains were tried and none had the entry. */
4804 0 : ret = ENOENT;
4805 : done:
4806 3 : if (ret == ENOENT) {
4807 : /* The entry was not found, need to set result in negative cache */
4808 0 : if (cmdctx->cmd == SSS_NSS_GETSIDBYID) {
4809 0 : DEBUG(SSSDBG_MINOR_FAILURE,
4810 : "No matching domain found for [%"PRIu32"], fail!\n", cmdctx->id);
4811 0 : err = sss_ncache_set_uid(nctx->ncache, false, NULL, cmdctx->id);
4812 0 : if (err != EOK) {
4813 0 : DEBUG(SSSDBG_MINOR_FAILURE,
4814 : "Cannot set negative cache for UID %"PRIu32"\n", cmdctx->id);
4815 : }
4816 :
4817 0 : err = sss_ncache_set_gid(nctx->ncache, false, NULL, cmdctx->id);
4818 0 : if (err != EOK) {
4819 0 : DEBUG(SSSDBG_MINOR_FAILURE,
4820 : "Cannot set negative cache for GID %"PRIu32"\n", cmdctx->id);
4821 : }
4822 : } else {
4823 0 : DEBUG(SSSDBG_MINOR_FAILURE,
4824 : "No matching domain found for [%s], fail!\n", cmdctx->name);
4825 : }
4826 : }
4827 3 : return ret;
4828 : }
4829 :
4830 6 : static errno_t nss_cmd_getbysid_search(struct nss_dom_ctx *dctx)
4831 : {
4832 6 : struct nss_cmd_ctx *cmdctx = dctx->cmdctx;
4833 6 : struct sss_domain_info *dom = dctx->domain;
4834 6 : struct cli_ctx *cctx = cmdctx->cctx;
4835 : struct sysdb_ctx *sysdb;
4836 : struct nss_ctx *nctx;
4837 : int ret;
4838 :
4839 6 : nctx = talloc_get_type(cctx->rctx->pvt_ctx, struct nss_ctx);
4840 :
4841 6 : DEBUG(SSSDBG_TRACE_FUNC, "Requesting info for [%s@%s]\n", cmdctx->secid,
4842 : dom->name);
4843 :
4844 6 : sysdb = dom->sysdb;
4845 6 : if (sysdb == NULL) {
4846 0 : DEBUG(SSSDBG_FATAL_FAILURE, "Fatal: Sysdb CTX not found for this " \
4847 : "domain!\n");
4848 0 : return EIO;
4849 : }
4850 :
4851 : /* verify this user has not yet been negatively cached,
4852 : * or has been permanently filtered */
4853 6 : ret = sss_ncache_check_sid(nctx->ncache, nctx->neg_timeout, cmdctx->secid);
4854 6 : if (ret == EEXIST) {
4855 1 : DEBUG(SSSDBG_TRACE_FUNC,
4856 : "SID [%s] does not exist! (negative cache)\n", cmdctx->secid);
4857 1 : return ENOENT;
4858 : }
4859 :
4860 5 : ret = sysdb_search_object_by_sid(cmdctx, dom, cmdctx->secid, NULL,
4861 : &dctx->res);
4862 5 : if (ret == ENOENT) {
4863 2 : if (!dctx->check_provider) {
4864 1 : DEBUG(SSSDBG_OP_FAILURE, "No results for getbysid call.\n");
4865 :
4866 : /* set negative cache only if not result of cache check */
4867 1 : ret = sss_ncache_set_sid(nctx->ncache, false, cmdctx->secid);
4868 1 : if (ret != EOK) {
4869 0 : DEBUG(SSSDBG_MINOR_FAILURE,
4870 : "Cannot set negative cache for %s\n", cmdctx->secid);
4871 : }
4872 :
4873 1 : return ENOENT;
4874 : }
4875 :
4876 1 : dctx->res = talloc_zero(cmdctx, struct ldb_result);
4877 1 : if (dctx->res == NULL) {
4878 0 : DEBUG(SSSDBG_OP_FAILURE, "talloc_zero failed.\n");
4879 0 : return ENOMEM;
4880 : }
4881 : /* Fall through and call the backend */
4882 3 : } else if (ret != EOK) {
4883 0 : DEBUG(SSSDBG_CRIT_FAILURE, "Failed to make request to our cache!\n");
4884 0 : return EIO;
4885 : }
4886 :
4887 4 : if (dctx->res->count > 1) {
4888 0 : DEBUG(SSSDBG_FATAL_FAILURE, "getbysid call returned more than one " \
4889 : "result !?!\n");
4890 0 : return ENOENT;
4891 : }
4892 :
4893 : /* if this is a caching provider (or if we haven't checked the cache
4894 : * yet) then verify that the cache is uptodate */
4895 4 : if (dctx->check_provider) {
4896 3 : ret = check_cache(dctx, nctx, dctx->res,
4897 3 : SSS_DP_SECID, cmdctx->secid, 0, NULL,
4898 : nss_cmd_getby_dp_callback,
4899 : dctx);
4900 3 : if (ret != EOK) {
4901 : /* Anything but EOK means we should reenter the mainloop
4902 : * because we may be refreshing the cache
4903 : */
4904 2 : return ret;
4905 : }
4906 : }
4907 :
4908 : /* One result found */
4909 2 : DEBUG(SSSDBG_TRACE_FUNC, "Returning info for sid [%s@%s]\n", cmdctx->secid,
4910 : dom->name);
4911 :
4912 2 : return EOK;
4913 : }
4914 :
4915 5 : static errno_t find_sss_id_type(struct ldb_message *msg,
4916 : bool mpg,
4917 : enum sss_id_type *id_type)
4918 : {
4919 : size_t c;
4920 : struct ldb_message_element *el;
4921 5 : struct ldb_val *val = NULL;
4922 :
4923 5 : el = ldb_msg_find_element(msg, SYSDB_OBJECTCLASS);
4924 5 : if (el == NULL) {
4925 0 : DEBUG(SSSDBG_OP_FAILURE, "Objectclass attribute not found.\n");
4926 0 : return EINVAL;
4927 : }
4928 :
4929 5 : for (c = 0; c < el->num_values; c++) {
4930 5 : val = &(el->values[c]);
4931 10 : if (strncasecmp(SYSDB_USER_CLASS,
4932 5 : (char *)val->data, val->length) == 0) {
4933 5 : break;
4934 : }
4935 : }
4936 :
4937 5 : if (c == el->num_values) {
4938 0 : *id_type = SSS_ID_TYPE_GID;
4939 : } else {
4940 5 : if (mpg) {
4941 0 : *id_type = SSS_ID_TYPE_BOTH;
4942 : } else {
4943 5 : *id_type = SSS_ID_TYPE_UID;
4944 : }
4945 : }
4946 :
4947 5 : return EOK;
4948 : }
4949 :
4950 0 : static errno_t fill_sid(struct sss_packet *packet,
4951 : enum sss_id_type id_type,
4952 : struct ldb_message *msg)
4953 : {
4954 : int ret;
4955 : const char *sid_str;
4956 : struct sized_string sid;
4957 : uint8_t *body;
4958 : size_t blen;
4959 0 : size_t pctr = 0;
4960 :
4961 0 : sid_str = ldb_msg_find_attr_as_string(msg, SYSDB_SID_STR, NULL);
4962 0 : if (sid_str == NULL) {
4963 0 : DEBUG(SSSDBG_CRIT_FAILURE, "Missing SID.\n");
4964 0 : return EINVAL;
4965 : }
4966 :
4967 0 : to_sized_string(&sid, sid_str);
4968 :
4969 0 : ret = sss_packet_grow(packet, sid.len + 3* sizeof(uint32_t));
4970 0 : if (ret != EOK) {
4971 0 : DEBUG(SSSDBG_OP_FAILURE, "sss_packet_grow failed.\n");
4972 0 : return ret;
4973 : }
4974 :
4975 0 : sss_packet_get_body(packet, &body, &blen);
4976 0 : SAFEALIGN_SETMEM_UINT32(body, 1, &pctr); /* Num results */
4977 0 : SAFEALIGN_SETMEM_UINT32(body + pctr, 0, &pctr); /* reserved */
4978 0 : SAFEALIGN_COPY_UINT32(body + pctr, &id_type, &pctr);
4979 0 : memcpy(&body[pctr], sid.str, sid.len);
4980 :
4981 0 : return EOK;
4982 : }
4983 :
4984 5 : static errno_t process_attr_list(TALLOC_CTX *mem_ctx, struct ldb_message *msg,
4985 : const char **attr_list,
4986 : struct sized_string **_keys,
4987 : struct sized_string **_vals,
4988 : size_t *array_size, size_t *sum, size_t *found)
4989 : {
4990 : size_t c;
4991 : size_t d;
4992 : struct sized_string *keys;
4993 : struct sized_string *vals;
4994 : struct ldb_val *val;
4995 : struct ldb_message_element *el;
4996 :
4997 5 : keys = *_keys;
4998 5 : vals = *_vals;
4999 :
5000 51 : for (c = 0; attr_list[c] != NULL; c++) {
5001 46 : el = ldb_msg_find_element(msg, attr_list[c]);
5002 46 : if (el != NULL && el->num_values > 0) {
5003 12 : if (el->num_values > 1) {
5004 1 : *array_size += el->num_values;
5005 1 : keys = talloc_realloc(mem_ctx, keys, struct sized_string,
5006 : *array_size);
5007 1 : vals = talloc_realloc(mem_ctx, vals, struct sized_string,
5008 : *array_size);
5009 1 : if (keys == NULL || vals == NULL) {
5010 0 : DEBUG(SSSDBG_OP_FAILURE, "talloc_array failed.\n");
5011 0 : return ENOMEM;
5012 : }
5013 : }
5014 26 : for (d = 0; d < el->num_values; d++) {
5015 14 : to_sized_string(&keys[*found], attr_list[c]);
5016 14 : *sum += keys[*found].len;
5017 14 : val = &(el->values[d]);
5018 14 : if (val == NULL || val->data == NULL
5019 14 : || val->data[val->length] != '\0') {
5020 0 : DEBUG(SSSDBG_CRIT_FAILURE,
5021 : "Unexpected attribute value found for [%s].\n",
5022 : attr_list[c]);
5023 0 : return EINVAL;
5024 : }
5025 14 : to_sized_string(&vals[*found], (const char *)val->data);
5026 14 : *sum += vals[*found].len;
5027 :
5028 14 : (*found)++;
5029 : }
5030 : }
5031 : }
5032 :
5033 5 : *_keys = keys;
5034 5 : *_vals = vals;
5035 :
5036 5 : return EOK;
5037 : }
5038 :
5039 3 : static errno_t fill_orig(struct sss_packet *packet,
5040 : struct resp_ctx *rctx,
5041 : enum sss_id_type id_type,
5042 : struct ldb_message *msg)
5043 : {
5044 : int ret;
5045 : TALLOC_CTX *tmp_ctx;
5046 : uint8_t *body;
5047 : size_t blen;
5048 3 : size_t pctr = 0;
5049 : size_t c;
5050 : size_t sum;
5051 : size_t found;
5052 : size_t array_size;
5053 3 : size_t extra_attrs_count = 0;
5054 3 : const char **extra_attrs_list = NULL;
5055 3 : const char *orig_attr_list[] = {SYSDB_SID_STR,
5056 : ORIGINALAD_PREFIX SYSDB_NAME,
5057 : ORIGINALAD_PREFIX SYSDB_UIDNUM,
5058 : ORIGINALAD_PREFIX SYSDB_GIDNUM,
5059 : ORIGINALAD_PREFIX SYSDB_HOMEDIR,
5060 : ORIGINALAD_PREFIX SYSDB_GECOS,
5061 : ORIGINALAD_PREFIX SYSDB_SHELL,
5062 : SYSDB_UPN,
5063 : SYSDB_DEFAULT_OVERRIDE_NAME,
5064 : SYSDB_AD_ACCOUNT_EXPIRES,
5065 : SYSDB_AD_USER_ACCOUNT_CONTROL,
5066 : SYSDB_SSH_PUBKEY,
5067 : SYSDB_ORIG_DN,
5068 : SYSDB_ORIG_MEMBEROF,
5069 : NULL};
5070 : struct sized_string *keys;
5071 : struct sized_string *vals;
5072 : struct nss_ctx *nctx;
5073 :
5074 3 : tmp_ctx = talloc_new(NULL);
5075 3 : if (tmp_ctx == NULL) {
5076 0 : DEBUG(SSSDBG_OP_FAILURE, "talloc_new failed.\n");
5077 0 : return ENOMEM;
5078 : }
5079 :
5080 3 : nctx = talloc_get_type(rctx->pvt_ctx, struct nss_ctx);
5081 3 : if (nctx->extra_attributes != NULL) {
5082 2 : extra_attrs_list = nctx->extra_attributes;
5083 8 : for(extra_attrs_count = 0;
5084 6 : extra_attrs_list[extra_attrs_count] != NULL;
5085 4 : extra_attrs_count++);
5086 : }
5087 :
5088 3 : array_size = sizeof(orig_attr_list) + extra_attrs_count;
5089 3 : keys = talloc_array(tmp_ctx, struct sized_string, array_size);
5090 3 : vals = talloc_array(tmp_ctx, struct sized_string, array_size);
5091 3 : if (keys == NULL || vals == NULL) {
5092 0 : DEBUG(SSSDBG_OP_FAILURE, "talloc_array failed.\n");
5093 0 : ret = ENOMEM;
5094 0 : goto done;
5095 : }
5096 :
5097 3 : sum = 0;
5098 3 : found = 0;
5099 :
5100 3 : ret = process_attr_list(tmp_ctx, msg, orig_attr_list, &keys, &vals,
5101 : &array_size, &sum, &found);
5102 3 : if (ret != EOK) {
5103 0 : DEBUG(SSSDBG_OP_FAILURE, "process_attr_list failed.\n");
5104 0 : goto done;
5105 : }
5106 :
5107 3 : if (extra_attrs_count != 0) {
5108 2 : ret = process_attr_list(tmp_ctx, msg, extra_attrs_list, &keys, &vals,
5109 : &array_size, &sum, &found);
5110 2 : if (ret != EOK) {
5111 0 : DEBUG(SSSDBG_OP_FAILURE, "process_attr_list failed.\n");
5112 0 : goto done;
5113 : }
5114 : }
5115 :
5116 3 : ret = sss_packet_grow(packet, sum + 3 * sizeof(uint32_t));
5117 3 : if (ret != EOK) {
5118 0 : DEBUG(SSSDBG_OP_FAILURE, "sss_packet_grow failed.\n");
5119 0 : goto done;
5120 : }
5121 :
5122 3 : sss_packet_get_body(packet, &body, &blen);
5123 3 : SAFEALIGN_SETMEM_UINT32(body, 1, &pctr); /* Num results */
5124 3 : SAFEALIGN_SETMEM_UINT32(body + pctr, 0, &pctr); /* reserved */
5125 3 : SAFEALIGN_COPY_UINT32(body + pctr, &id_type, &pctr);
5126 17 : for (c = 0; c < found; c++) {
5127 14 : memcpy(&body[pctr], keys[c].str, keys[c].len);
5128 14 : pctr+= keys[c].len;
5129 14 : memcpy(&body[pctr], vals[c].str, vals[c].len);
5130 14 : pctr+= vals[c].len;
5131 : }
5132 :
5133 3 : ret = EOK;
5134 :
5135 : done:
5136 3 : talloc_free(tmp_ctx);
5137 :
5138 3 : return ret;
5139 : }
5140 :
5141 2 : static errno_t fill_name(struct sss_packet *packet,
5142 : struct sss_domain_info *dom,
5143 : enum sss_id_type id_type,
5144 : bool apply_no_view,
5145 : struct ldb_message *msg)
5146 : {
5147 : int ret;
5148 2 : TALLOC_CTX *tmp_ctx = NULL;
5149 2 : const char *orig_name = NULL;
5150 : const char *cased_name;
5151 : const char *fq_name;
5152 : struct sized_string name;
5153 2 : bool add_domain = (!IS_SUBDOMAIN(dom) && dom->fqnames);
5154 : uint8_t *body;
5155 : size_t blen;
5156 2 : size_t pctr = 0;
5157 :
5158 2 : if (apply_no_view) {
5159 2 : orig_name = ldb_msg_find_attr_as_string(msg,
5160 : ORIGINALAD_PREFIX SYSDB_NAME,
5161 : NULL);
5162 : } else {
5163 0 : if (DOM_HAS_VIEWS(dom)) {
5164 0 : orig_name = ldb_msg_find_attr_as_string(msg,
5165 : OVERRIDE_PREFIX SYSDB_NAME,
5166 : NULL);
5167 0 : if (orig_name != NULL && IS_SUBDOMAIN(dom)) {
5168 : /* Override names are un-qualified */
5169 0 : add_domain = true;
5170 : }
5171 : }
5172 : }
5173 :
5174 2 : if (orig_name == NULL) {
5175 2 : orig_name = ldb_msg_find_attr_as_string(msg, SYSDB_NAME, NULL);
5176 : }
5177 2 : if (orig_name == NULL) {
5178 0 : DEBUG(SSSDBG_CRIT_FAILURE, "Missing name.\n");
5179 0 : return EINVAL;
5180 : }
5181 :
5182 2 : tmp_ctx = talloc_new(NULL);
5183 2 : if (tmp_ctx == NULL) {
5184 0 : DEBUG(SSSDBG_OP_FAILURE, "talloc_new failed.\n");
5185 0 : return ENOMEM;
5186 : }
5187 :
5188 2 : cased_name= sss_get_cased_name(tmp_ctx, orig_name, dom->case_sensitive);
5189 2 : if (cased_name == NULL) {
5190 0 : DEBUG(SSSDBG_OP_FAILURE, "sss_get_cased_name failed.\n");
5191 0 : ret = ENOMEM;
5192 0 : goto done;
5193 : }
5194 :
5195 2 : if (add_domain) {
5196 0 : fq_name = sss_tc_fqname(tmp_ctx, dom->names, dom, cased_name);
5197 0 : if (fq_name == NULL) {
5198 0 : DEBUG(SSSDBG_OP_FAILURE, "talloc_asprintf failed.\n");
5199 0 : ret = ENOMEM;
5200 0 : goto done;
5201 : }
5202 0 : to_sized_string(&name, fq_name);
5203 : } else {
5204 2 : to_sized_string(&name, cased_name);
5205 : }
5206 :
5207 2 : ret = sss_packet_grow(packet, name.len + 3 * sizeof(uint32_t));
5208 2 : if (ret != EOK) {
5209 0 : DEBUG(SSSDBG_OP_FAILURE, "sss_packet_grow failed.\n");
5210 0 : goto done;
5211 : }
5212 :
5213 2 : sss_packet_get_body(packet, &body, &blen);
5214 2 : SAFEALIGN_SETMEM_UINT32(body, 1, &pctr); /* Num results */
5215 2 : SAFEALIGN_SETMEM_UINT32(body + pctr, 0, &pctr); /* reserved */
5216 2 : SAFEALIGN_COPY_UINT32(body + pctr, &id_type, &pctr);
5217 2 : memcpy(&body[pctr], name.str, name.len);
5218 :
5219 :
5220 2 : ret = EOK;
5221 :
5222 : done:
5223 2 : talloc_free(tmp_ctx);
5224 :
5225 2 : return ret;
5226 : }
5227 :
5228 0 : static errno_t fill_id(struct sss_packet *packet,
5229 : enum sss_id_type id_type,
5230 : struct ldb_message *msg)
5231 : {
5232 : int ret;
5233 : uint8_t *body;
5234 : size_t blen;
5235 0 : size_t pctr = 0;
5236 : uint64_t tmp_id;
5237 : uint32_t id;
5238 :
5239 0 : if (id_type == SSS_ID_TYPE_GID) {
5240 0 : tmp_id = ldb_msg_find_attr_as_uint64(msg, SYSDB_GIDNUM, 0);
5241 : } else {
5242 0 : tmp_id = ldb_msg_find_attr_as_uint64(msg, SYSDB_UIDNUM, 0);
5243 : }
5244 :
5245 0 : if (tmp_id == 0 || tmp_id >= UINT32_MAX) {
5246 0 : DEBUG(SSSDBG_CRIT_FAILURE, "Invalid POSIX ID.\n");
5247 0 : return EINVAL;
5248 : }
5249 0 : id = (uint32_t) tmp_id;
5250 :
5251 0 : ret = sss_packet_grow(packet, 4 * sizeof(uint32_t));
5252 0 : if (ret != EOK) {
5253 0 : DEBUG(SSSDBG_OP_FAILURE, "sss_packet_grow failed.\n");
5254 0 : return ret;
5255 : }
5256 :
5257 0 : sss_packet_get_body(packet, &body, &blen);
5258 0 : SAFEALIGN_SETMEM_UINT32(body, 1, &pctr); /* Num results */
5259 0 : SAFEALIGN_SETMEM_UINT32(body + pctr, 0, &pctr); /* reserved */
5260 0 : SAFEALIGN_COPY_UINT32(body + pctr, &id_type, &pctr);
5261 0 : SAFEALIGN_COPY_UINT32(body + pctr, &id, &pctr);
5262 :
5263 0 : return EOK;
5264 : }
5265 :
5266 5 : static errno_t nss_cmd_getbysid_send_reply(struct nss_dom_ctx *dctx)
5267 : {
5268 5 : struct nss_cmd_ctx *cmdctx = dctx->cmdctx;
5269 5 : struct cli_ctx *cctx = cmdctx->cctx;
5270 : int ret;
5271 : enum sss_id_type id_type;
5272 :
5273 5 : if (dctx->res->count > 1) {
5274 0 : return EINVAL;
5275 5 : } else if (dctx->res->count == 0) {
5276 0 : return ENOENT;
5277 : }
5278 :
5279 10 : ret = sss_packet_new(cctx->creq, 0,
5280 5 : sss_packet_get_cmd(cctx->creq->in),
5281 5 : &cctx->creq->out);
5282 5 : if (ret != EOK) {
5283 0 : return EFAULT;
5284 : }
5285 :
5286 5 : ret = find_sss_id_type(dctx->res->msgs[0], dctx->domain->mpg, &id_type);
5287 5 : if (ret != 0) {
5288 0 : DEBUG(SSSDBG_OP_FAILURE, "find_sss_id_type failed.\n");
5289 0 : return ret;
5290 : }
5291 :
5292 5 : switch(cmdctx->cmd) {
5293 : case SSS_NSS_GETNAMEBYSID:
5294 2 : ret = fill_name(cctx->creq->out,
5295 : dctx->domain,
5296 : id_type,
5297 : true,
5298 2 : dctx->res->msgs[0]);
5299 2 : break;
5300 : case SSS_NSS_GETIDBYSID:
5301 0 : ret = fill_id(cctx->creq->out, id_type, dctx->res->msgs[0]);
5302 0 : break;
5303 : case SSS_NSS_GETSIDBYNAME:
5304 : case SSS_NSS_GETSIDBYID:
5305 0 : ret = fill_sid(cctx->creq->out, id_type, dctx->res->msgs[0]);
5306 0 : break;
5307 : case SSS_NSS_GETORIGBYNAME:
5308 3 : ret = fill_orig(cctx->creq->out, cctx->rctx, id_type,
5309 3 : dctx->res->msgs[0]);
5310 3 : break;
5311 : default:
5312 0 : DEBUG(SSSDBG_CRIT_FAILURE, "Unsupported request type.\n");
5313 0 : return EINVAL;
5314 : }
5315 5 : if (ret != EOK) {
5316 0 : return ret;
5317 : }
5318 :
5319 5 : sss_packet_set_error(cctx->creq->out, EOK);
5320 5 : sss_cmd_done(cctx, cmdctx);
5321 5 : return EOK;
5322 : }
5323 :
5324 8 : static int nss_check_well_known_sid(struct nss_cmd_ctx *cmdctx)
5325 : {
5326 : const char *wk_name;
5327 : const char *wk_dom_name;
5328 : int ret;
5329 8 : char *fq_name = NULL;
5330 : struct sized_string name;
5331 : uint8_t *body;
5332 : size_t blen;
5333 : struct cli_ctx *cctx;
5334 : struct nss_ctx *nss_ctx;
5335 8 : size_t pctr = 0;
5336 :
5337 8 : ret = well_known_sid_to_name(cmdctx->secid, &wk_dom_name, &wk_name);
5338 8 : if (ret != EOK) {
5339 5 : DEBUG(SSSDBG_TRACE_ALL, "SID [%s] is not a Well-Known SID.\n",
5340 : cmdctx->secid);
5341 5 : return ret;
5342 : }
5343 :
5344 3 : if (cmdctx->cmd != SSS_NSS_GETNAMEBYSID) {
5345 1 : DEBUG(SSSDBG_TRACE_ALL,
5346 : "Well-Known SIDs can only be translated to names.\n");
5347 1 : return EINVAL;
5348 : }
5349 :
5350 2 : if (wk_dom_name != NULL) {
5351 2 : nss_ctx = talloc_get_type(cmdctx->cctx->rctx->pvt_ctx, struct nss_ctx);
5352 2 : fq_name = sss_tc_fqname2(cmdctx, nss_ctx->global_names,
5353 : wk_dom_name, wk_dom_name, wk_name);
5354 2 : if (fq_name == NULL) {
5355 0 : DEBUG(SSSDBG_OP_FAILURE, "sss_tc_fqname2 failed.\n");
5356 0 : return ENOMEM;
5357 : }
5358 2 : to_sized_string(&name, fq_name);
5359 : } else {
5360 0 : to_sized_string(&name, wk_name);
5361 : }
5362 :
5363 2 : cctx = cmdctx->cctx;
5364 4 : ret = sss_packet_new(cctx->creq, name.len + 3 * sizeof(uint32_t),
5365 2 : sss_packet_get_cmd(cctx->creq->in),
5366 2 : &cctx->creq->out);
5367 2 : if (ret != EOK) {
5368 0 : talloc_free(fq_name);
5369 0 : return ENOMEM;
5370 : }
5371 :
5372 2 : sss_packet_get_body(cctx->creq->out, &body, &blen);
5373 2 : SAFEALIGN_SETMEM_UINT32(body, 1, &pctr); /* num results */
5374 2 : SAFEALIGN_SETMEM_UINT32(body + pctr, 0, &pctr); /* reserved */
5375 2 : SAFEALIGN_SETMEM_UINT32(body + pctr, SSS_ID_TYPE_GID, &pctr);
5376 2 : memcpy(&body[pctr], name.str, name.len);
5377 :
5378 2 : sss_packet_set_error(cctx->creq->out, EOK);
5379 2 : sss_cmd_done(cctx, cmdctx);
5380 2 : return EOK;
5381 : }
5382 :
5383 8 : static int nss_cmd_getbysid(enum sss_cli_command cmd, struct cli_ctx *cctx)
5384 : {
5385 :
5386 : struct tevent_req *req;
5387 : struct nss_cmd_ctx *cmdctx;
5388 : struct nss_dom_ctx *dctx;
5389 : const char *sid_str;
5390 : uint8_t *body;
5391 : size_t blen;
5392 : int ret;
5393 : struct nss_ctx *nctx;
5394 : enum idmap_error_code err;
5395 8 : uint8_t *bin_sid = NULL;
5396 : size_t bin_sid_length;
5397 :
5398 8 : if (cmd != SSS_NSS_GETNAMEBYSID && cmd != SSS_NSS_GETIDBYSID) {
5399 0 : DEBUG(SSSDBG_CRIT_FAILURE, "Invalid command type [%d][%s].\n",
5400 : cmd, sss_cmd2str(cmd));
5401 0 : return EINVAL;
5402 : }
5403 :
5404 8 : cmdctx = talloc_zero(cctx, struct nss_cmd_ctx);
5405 8 : if (!cmdctx) {
5406 0 : return ENOMEM;
5407 : }
5408 8 : cmdctx->cctx = cctx;
5409 8 : cmdctx->cmd = cmd;
5410 :
5411 8 : dctx = talloc_zero(cmdctx, struct nss_dom_ctx);
5412 8 : if (!dctx) {
5413 0 : ret = ENOMEM;
5414 0 : goto done;
5415 : }
5416 8 : dctx->cmdctx = cmdctx;
5417 :
5418 : /* get SID to query */
5419 8 : sss_packet_get_body(cctx->creq->in, &body, &blen);
5420 :
5421 : /* if not terminated fail */
5422 8 : if (body[blen -1] != '\0') {
5423 0 : ret = EINVAL;
5424 0 : goto done;
5425 : }
5426 :
5427 8 : sid_str = (const char *) body;
5428 :
5429 8 : nctx = talloc_get_type(cctx->rctx->pvt_ctx, struct nss_ctx);
5430 :
5431 : /* If the body isn't a SID, fail */
5432 8 : err = sss_idmap_sid_to_bin_sid(nctx->idmap_ctx, sid_str,
5433 : &bin_sid, &bin_sid_length);
5434 8 : sss_idmap_free_bin_sid(nctx->idmap_ctx, bin_sid);
5435 8 : if (err != IDMAP_SUCCESS) {
5436 0 : DEBUG(SSSDBG_OP_FAILURE, "sss_idmap_sid_to_bin_sid failed for [%s].\n",
5437 : body);
5438 0 : ret = EINVAL;
5439 0 : goto done;
5440 : }
5441 :
5442 8 : DEBUG(SSSDBG_TRACE_FUNC, "Running command [%d][%s] with SID [%s].\n",
5443 : dctx->cmdctx->cmd, sss_cmd2str(dctx->cmdctx->cmd), sid_str);
5444 :
5445 8 : cmdctx->secid = talloc_strdup(cmdctx, sid_str);
5446 8 : if (cmdctx->secid == NULL) {
5447 0 : DEBUG(SSSDBG_OP_FAILURE, "talloc_strdup failed.\n");
5448 0 : ret = ENOMEM;
5449 0 : goto done;
5450 : }
5451 :
5452 8 : ret = nss_check_well_known_sid(cmdctx);
5453 8 : if (ret != ENOENT) {
5454 4 : if (ret == EOK) {
5455 2 : DEBUG(SSSDBG_TRACE_ALL, "SID [%s] is a Well-Known SID.\n",
5456 : cmdctx->secid);
5457 : } else {
5458 2 : DEBUG(SSSDBG_OP_FAILURE, "nss_check_well_known_sid failed.\n");
5459 : }
5460 4 : goto done;
5461 : }
5462 :
5463 4 : ret = responder_get_domain_by_id(cctx->rctx, cmdctx->secid, &dctx->domain);
5464 4 : if (ret == EAGAIN || ret == ENOENT) {
5465 0 : req = sss_dp_get_domains_send(cctx->rctx, cctx->rctx, true, NULL);
5466 0 : if (req == NULL) {
5467 0 : ret = ENOMEM;
5468 : } else {
5469 0 : dctx->rawname = sid_str;
5470 0 : tevent_req_set_callback(req, nss_cmd_getbyid_done, dctx);
5471 0 : ret = EAGAIN;
5472 : }
5473 0 : goto done;
5474 4 : } else if (ret != EOK) {
5475 0 : DEBUG(SSSDBG_OP_FAILURE, "responder_get_domain_by_id failed.\n");
5476 0 : goto done;
5477 : }
5478 :
5479 4 : DEBUG(SSSDBG_CONF_SETTINGS, "Requesting info for [%s] from [%s]\n",
5480 : cmdctx->secid, dctx->domain->name);
5481 :
5482 4 : dctx->check_provider = NEED_CHECK_PROVIDER(dctx->domain->provider);
5483 :
5484 : /* ok, find it ! */
5485 4 : ret = nss_cmd_getbysid_search(dctx);
5486 4 : if (ret == EOK) {
5487 1 : ret = nss_cmd_getbysid_send_reply(dctx);
5488 : }
5489 :
5490 : done:
5491 8 : return nss_cmd_done(cmdctx, ret);
5492 : }
5493 :
5494 6 : static int nss_cmd_getsidbyname(struct cli_ctx *cctx)
5495 : {
5496 6 : return nss_cmd_getbynam(SSS_NSS_GETSIDBYNAME, cctx);
5497 : }
5498 :
5499 0 : static int nss_cmd_getsidbyid(struct cli_ctx *cctx)
5500 : {
5501 0 : return nss_cmd_getbyid(SSS_NSS_GETSIDBYID, cctx);
5502 : }
5503 :
5504 7 : static int nss_cmd_getnamebysid(struct cli_ctx *cctx)
5505 : {
5506 7 : return nss_cmd_getbysid(SSS_NSS_GETNAMEBYSID, cctx);
5507 : }
5508 :
5509 1 : static int nss_cmd_getidbysid(struct cli_ctx *cctx)
5510 : {
5511 1 : return nss_cmd_getbysid(SSS_NSS_GETIDBYSID, cctx);
5512 : }
5513 :
5514 3 : static int nss_cmd_getorigbyname(struct cli_ctx *cctx)
5515 : {
5516 3 : return nss_cmd_getbynam(SSS_NSS_GETORIGBYNAME, cctx);
5517 : }
5518 :
5519 0 : struct cli_protocol_version *register_cli_protocol_version(void)
5520 : {
5521 : static struct cli_protocol_version nss_cli_protocol_version[] = {
5522 : {1, "2008-09-05", "initial version, \\0 terminated strings"},
5523 : {0, NULL, NULL}
5524 : };
5525 :
5526 0 : return nss_cli_protocol_version;
5527 : }
5528 :
5529 : static struct sss_cmd_table nss_cmds[] = {
5530 : {SSS_GET_VERSION, sss_cmd_get_version},
5531 : {SSS_NSS_GETPWNAM, nss_cmd_getpwnam},
5532 : {SSS_NSS_GETPWUID, nss_cmd_getpwuid},
5533 : {SSS_NSS_SETPWENT, nss_cmd_setpwent},
5534 : {SSS_NSS_GETPWENT, nss_cmd_getpwent},
5535 : {SSS_NSS_ENDPWENT, nss_cmd_endpwent},
5536 : {SSS_NSS_GETGRNAM, nss_cmd_getgrnam},
5537 : {SSS_NSS_GETGRGID, nss_cmd_getgrgid},
5538 : {SSS_NSS_SETGRENT, nss_cmd_setgrent},
5539 : {SSS_NSS_GETGRENT, nss_cmd_getgrent},
5540 : {SSS_NSS_ENDGRENT, nss_cmd_endgrent},
5541 : {SSS_NSS_INITGR, nss_cmd_initgroups},
5542 : {SSS_NSS_SETNETGRENT, nss_cmd_setnetgrent},
5543 : {SSS_NSS_GETNETGRENT, nss_cmd_getnetgrent},
5544 : {SSS_NSS_ENDNETGRENT, nss_cmd_endnetgrent},
5545 : {SSS_NSS_GETSERVBYNAME, nss_cmd_getservbyname},
5546 : {SSS_NSS_GETSERVBYPORT, nss_cmd_getservbyport},
5547 : {SSS_NSS_SETSERVENT, nss_cmd_setservent},
5548 : {SSS_NSS_GETSERVENT, nss_cmd_getservent},
5549 : {SSS_NSS_ENDSERVENT, nss_cmd_endservent},
5550 : {SSS_NSS_GETSIDBYNAME, nss_cmd_getsidbyname},
5551 : {SSS_NSS_GETSIDBYID, nss_cmd_getsidbyid},
5552 : {SSS_NSS_GETNAMEBYSID, nss_cmd_getnamebysid},
5553 : {SSS_NSS_GETIDBYSID, nss_cmd_getidbysid},
5554 : {SSS_NSS_GETORIGBYNAME, nss_cmd_getorigbyname},
5555 : {SSS_CLI_NULL, NULL}
5556 : };
5557 :
5558 44 : struct sss_cmd_table *get_nss_cmds(void) {
5559 44 : return nss_cmds;
5560 : }
|