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