Line data Source code
1 : /*
2 : SSSD
3 :
4 : proxy_id.c
5 :
6 : Authors:
7 : Stephen Gallagher <sgallagh@redhat.com>
8 :
9 : Copyright (C) 2010 Red Hat
10 :
11 : This program is free software; you can redistribute it and/or modify
12 : it under the terms of the GNU General Public License as published by
13 : the Free Software Foundation; either version 3 of the License, or
14 : (at your option) any later version.
15 :
16 : This program is distributed in the hope that it will be useful,
17 : but WITHOUT ANY WARRANTY; without even the implied warranty of
18 : MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
19 : GNU General Public License for more details.
20 :
21 : You should have received a copy of the GNU General Public License
22 : along with this program. If not, see <http://www.gnu.org/licenses/>.
23 : */
24 :
25 : #include "config.h"
26 :
27 : #include "util/sss_format.h"
28 : #include "util/strtonum.h"
29 : #include "providers/proxy/proxy.h"
30 :
31 : /* =Getpwnam-wrapper======================================================*/
32 :
33 : static int save_user(struct sss_domain_info *domain,
34 : bool lowercase, struct passwd *pwd, const char *real_name,
35 : const char *alias, uint64_t cache_timeout);
36 :
37 : static int
38 : handle_getpw_result(enum nss_status status, struct passwd *pwd,
39 : struct sss_domain_info *dom, bool *del_user);
40 :
41 : static int
42 : delete_user(struct sss_domain_info *domain,
43 : const char *name, uid_t uid);
44 :
45 0 : static int get_pw_name(struct proxy_id_ctx *ctx,
46 : struct sss_domain_info *dom,
47 : const char *name)
48 : {
49 : TALLOC_CTX *tmpctx;
50 : struct passwd *pwd;
51 : enum nss_status status;
52 : char *buffer;
53 : size_t buflen;
54 : int ret;
55 : uid_t uid;
56 : bool del_user;
57 0 : struct ldb_result *cached_pwd = NULL;
58 0 : const char *real_name = NULL;
59 :
60 0 : DEBUG(SSSDBG_TRACE_FUNC, "Searching user by name (%s)\n", name);
61 :
62 0 : tmpctx = talloc_new(NULL);
63 0 : if (!tmpctx) {
64 0 : return ENOMEM;
65 : }
66 :
67 0 : pwd = talloc_zero(tmpctx, struct passwd);
68 0 : if (!pwd) {
69 0 : ret = ENOMEM;
70 0 : goto done;
71 : }
72 :
73 0 : buflen = DEFAULT_BUFSIZE;
74 0 : buffer = talloc_size(tmpctx, buflen);
75 0 : if (!buffer) {
76 0 : ret = ENOMEM;
77 0 : goto done;
78 : }
79 :
80 : /* FIXME: should we move this call outside the transaction to keep the
81 : * transaction as short as possible ? */
82 0 : status = ctx->ops.getpwnam_r(name, pwd, buffer, buflen, &ret);
83 0 : ret = handle_getpw_result(status, pwd, dom, &del_user);
84 0 : if (ret) {
85 0 : DEBUG(SSSDBG_OP_FAILURE,
86 : "getpwnam failed [%d]: %s\n", ret, strerror(ret));
87 0 : goto done;
88 : }
89 :
90 0 : if (del_user) {
91 0 : ret = delete_user(dom, name, 0);
92 0 : goto done;
93 : }
94 :
95 0 : uid = pwd->pw_uid;
96 :
97 : /* Canonicalize the username in case it was actually an alias */
98 :
99 0 : if (ctx->fast_alias == true) {
100 0 : ret = sysdb_getpwuid(tmpctx, dom, uid, &cached_pwd);
101 0 : if (ret != EOK) {
102 : /* Non-fatal, attempt to canonicalize online */
103 0 : DEBUG(SSSDBG_TRACE_FUNC, "Request to cache failed [%d]: %s\n",
104 : ret, strerror(ret));
105 : }
106 :
107 0 : if (ret == EOK && cached_pwd->count == 1) {
108 0 : real_name = ldb_msg_find_attr_as_string(cached_pwd->msgs[0],
109 : SYSDB_NAME, NULL);
110 0 : if (!real_name) {
111 0 : DEBUG(SSSDBG_MINOR_FAILURE, "Cached user has no name?\n");
112 : }
113 : }
114 : }
115 :
116 0 : if (real_name == NULL) {
117 0 : memset(buffer, 0, buflen);
118 :
119 0 : status = ctx->ops.getpwuid_r(uid, pwd, buffer, buflen, &ret);
120 0 : ret = handle_getpw_result(status, pwd, dom, &del_user);
121 0 : if (ret) {
122 0 : DEBUG(SSSDBG_OP_FAILURE,
123 : "getpwuid failed [%d]: %s\n", ret, strerror(ret));
124 0 : goto done;
125 : }
126 :
127 0 : real_name = pwd->pw_name;
128 : }
129 :
130 0 : if (del_user) {
131 0 : ret = delete_user(dom, name, uid);
132 0 : goto done;
133 : }
134 :
135 : /* Both lookups went fine, we can save the user now */
136 0 : ret = save_user(dom, !dom->case_sensitive, pwd,
137 0 : real_name, name, dom->user_timeout);
138 :
139 : done:
140 0 : talloc_zfree(tmpctx);
141 0 : if (ret) {
142 0 : DEBUG(SSSDBG_OP_FAILURE,
143 : "proxy -> getpwnam_r failed for '%s' <%d>: %s\n",
144 : name, ret, strerror(ret));
145 : }
146 0 : return ret;
147 : }
148 :
149 : static int
150 0 : handle_getpw_result(enum nss_status status, struct passwd *pwd,
151 : struct sss_domain_info *dom, bool *del_user)
152 : {
153 0 : int ret = EOK;
154 :
155 0 : if (!del_user) {
156 0 : return EINVAL;
157 : }
158 0 : *del_user = false;
159 :
160 0 : switch (status) {
161 : case NSS_STATUS_NOTFOUND:
162 :
163 0 : DEBUG(SSSDBG_MINOR_FAILURE, "User not found.\n");
164 0 : *del_user = true;
165 0 : break;
166 :
167 : case NSS_STATUS_SUCCESS:
168 :
169 0 : DEBUG(SSSDBG_TRACE_FUNC, "User found: (%s, %"SPRIuid", %"SPRIgid")\n",
170 : pwd->pw_name, pwd->pw_uid, pwd->pw_gid);
171 :
172 : /* uid=0 or gid=0 are invalid values */
173 : /* also check that the id is in the valid range for this domain */
174 0 : if (OUT_OF_ID_RANGE(pwd->pw_uid, dom->id_min, dom->id_max) ||
175 0 : OUT_OF_ID_RANGE(pwd->pw_gid, dom->id_min, dom->id_max)) {
176 :
177 0 : DEBUG(SSSDBG_MINOR_FAILURE,
178 : "User filtered out! (id out of range)\n");
179 0 : *del_user = true;
180 0 : break;
181 : }
182 0 : break;
183 :
184 : case NSS_STATUS_UNAVAIL:
185 0 : DEBUG(SSSDBG_MINOR_FAILURE,
186 : "Remote back end is not available. Entering offline mode\n");
187 0 : ret = ENXIO;
188 0 : break;
189 :
190 : default:
191 0 : DEBUG(SSSDBG_OP_FAILURE, "Unknown return code %d\n", status);
192 0 : ret = EIO;
193 0 : break;
194 : }
195 :
196 0 : return ret;
197 : }
198 :
199 : static int
200 0 : delete_user(struct sss_domain_info *domain,
201 : const char *name, uid_t uid)
202 : {
203 0 : int ret = EOK;
204 :
205 0 : DEBUG(SSSDBG_TRACE_FUNC,
206 : "User %s does not exist (or is invalid) on remote server,"
207 : " deleting!\n", name);
208 0 : ret = sysdb_delete_user(domain, name, uid);
209 0 : if (ret == ENOENT) {
210 0 : ret = EOK;
211 : }
212 :
213 0 : return ret;
214 : }
215 :
216 0 : static int save_user(struct sss_domain_info *domain,
217 : bool lowercase, struct passwd *pwd, const char *real_name,
218 : const char *alias, uint64_t cache_timeout)
219 : {
220 : const char *shell;
221 : const char *gecos;
222 0 : struct sysdb_attrs *attrs = NULL;
223 : errno_t ret;
224 : const char *cased_alias;
225 0 : const char *lc_pw_name = NULL;
226 :
227 0 : if (pwd->pw_shell && pwd->pw_shell[0] != '\0') {
228 0 : shell = pwd->pw_shell;
229 : } else {
230 0 : shell = NULL;
231 : }
232 :
233 0 : if (pwd->pw_gecos && pwd->pw_gecos[0] != '\0') {
234 0 : gecos = pwd->pw_gecos;
235 : } else {
236 0 : gecos = NULL;
237 : }
238 :
239 0 : if (lowercase || alias) {
240 0 : attrs = sysdb_new_attrs(NULL);
241 0 : if (!attrs) {
242 0 : DEBUG(SSSDBG_CRIT_FAILURE, "Allocation error ?!\n");
243 0 : ret = ENOMEM;
244 0 : goto done;
245 : }
246 : }
247 :
248 0 : if (lowercase) {
249 0 : lc_pw_name = sss_tc_utf8_str_tolower(attrs, pwd->pw_name);
250 0 : if (lc_pw_name == NULL) {
251 0 : DEBUG(SSSDBG_OP_FAILURE, "Cannot convert name to lowercase.\n");
252 0 : ret = ENOMEM;
253 0 : goto done;
254 : }
255 :
256 0 : ret = sysdb_attrs_add_string(attrs, SYSDB_NAME_ALIAS, lc_pw_name);
257 0 : if (ret) {
258 0 : DEBUG(SSSDBG_OP_FAILURE, "Could not add name alias\n");
259 0 : ret = ENOMEM;
260 0 : goto done;
261 : }
262 :
263 : }
264 :
265 0 : if (alias) {
266 0 : cased_alias = sss_get_cased_name(attrs, alias, !lowercase);
267 0 : if (!cased_alias) {
268 0 : ret = ENOMEM;
269 0 : goto done;
270 : }
271 :
272 : /* Add the alias only if it differs from lowercased pw_name */
273 0 : if (lc_pw_name == NULL || strcmp(cased_alias, lc_pw_name) != 0) {
274 0 : ret = sysdb_attrs_add_string(attrs, SYSDB_NAME_ALIAS, cased_alias);
275 0 : if (ret) {
276 0 : DEBUG(SSSDBG_OP_FAILURE, "Could not add name alias\n");
277 0 : goto done;
278 : }
279 : }
280 : }
281 :
282 0 : ret = sysdb_store_user(domain,
283 : real_name,
284 0 : pwd->pw_passwd,
285 : pwd->pw_uid,
286 : pwd->pw_gid,
287 : gecos,
288 0 : pwd->pw_dir,
289 : shell,
290 : NULL,
291 : attrs,
292 : NULL,
293 : cache_timeout,
294 : 0);
295 0 : if (ret) {
296 0 : DEBUG(SSSDBG_OP_FAILURE, "Could not add user to cache\n");
297 0 : goto done;
298 : }
299 :
300 : done:
301 0 : talloc_zfree(attrs);
302 0 : return ret;
303 : }
304 :
305 : /* =Getpwuid-wrapper======================================================*/
306 :
307 0 : static int get_pw_uid(struct proxy_id_ctx *ctx,
308 : struct sss_domain_info *dom,
309 : uid_t uid)
310 : {
311 : TALLOC_CTX *tmpctx;
312 : struct passwd *pwd;
313 : enum nss_status status;
314 : char *buffer;
315 : size_t buflen;
316 0 : bool del_user = false;
317 : int ret;
318 :
319 0 : DEBUG(SSSDBG_TRACE_FUNC, "Searching user by uid (%"SPRIuid")\n", uid);
320 :
321 0 : tmpctx = talloc_new(NULL);
322 0 : if (!tmpctx) {
323 0 : return ENOMEM;
324 : }
325 :
326 0 : pwd = talloc_zero(tmpctx, struct passwd);
327 0 : if (!pwd) {
328 0 : ret = ENOMEM;
329 0 : goto done;
330 : }
331 :
332 0 : buflen = DEFAULT_BUFSIZE;
333 0 : buffer = talloc_size(tmpctx, buflen);
334 0 : if (!buffer) {
335 0 : ret = ENOMEM;
336 0 : goto done;
337 : }
338 :
339 0 : status = ctx->ops.getpwuid_r(uid, pwd, buffer, buflen, &ret);
340 0 : ret = handle_getpw_result(status, pwd, dom, &del_user);
341 0 : if (ret) {
342 0 : DEBUG(SSSDBG_OP_FAILURE,
343 : "getpwuid failed [%d]: %s\n", ret, strerror(ret));
344 0 : goto done;
345 : }
346 :
347 0 : if (del_user) {
348 0 : ret = delete_user(dom, NULL, uid);
349 0 : goto done;
350 : }
351 :
352 0 : ret = save_user(dom, !dom->case_sensitive, pwd,
353 0 : pwd->pw_name, NULL, dom->user_timeout);
354 :
355 : done:
356 0 : talloc_zfree(tmpctx);
357 0 : if (ret) {
358 0 : DEBUG(SSSDBG_CRIT_FAILURE,
359 : "proxy -> getpwuid_r failed for '%"SPRIuid"' <%d>: %s\n",
360 : uid, ret, strerror(ret));
361 : }
362 0 : return ret;
363 : }
364 :
365 : /* =Getpwent-wrapper======================================================*/
366 :
367 0 : static int enum_users(TALLOC_CTX *mem_ctx,
368 : struct proxy_id_ctx *ctx,
369 : struct sysdb_ctx *sysdb,
370 : struct sss_domain_info *dom)
371 : {
372 : TALLOC_CTX *tmpctx;
373 0 : bool in_transaction = false;
374 : struct passwd *pwd;
375 : enum nss_status status;
376 : size_t buflen;
377 : char *buffer;
378 : char *newbuf;
379 : int ret;
380 : errno_t sret;
381 : bool again;
382 :
383 0 : DEBUG(SSSDBG_TRACE_LIBS, "Enumerating users\n");
384 :
385 0 : tmpctx = talloc_new(mem_ctx);
386 0 : if (!tmpctx) {
387 0 : return ENOMEM;
388 : }
389 :
390 0 : pwd = talloc_zero(tmpctx, struct passwd);
391 0 : if (!pwd) {
392 0 : ret = ENOMEM;
393 0 : goto done;
394 : }
395 :
396 0 : buflen = DEFAULT_BUFSIZE;
397 0 : buffer = talloc_size(tmpctx, buflen);
398 0 : if (!buffer) {
399 0 : ret = ENOMEM;
400 0 : goto done;
401 : }
402 :
403 0 : ret = sysdb_transaction_start(sysdb);
404 0 : if (ret) {
405 0 : DEBUG(SSSDBG_CRIT_FAILURE, "Failed to start transaction\n");
406 0 : goto done;
407 : }
408 0 : in_transaction = true;
409 :
410 0 : status = ctx->ops.setpwent();
411 0 : if (status != NSS_STATUS_SUCCESS) {
412 0 : ret = EIO;
413 0 : goto done;
414 : }
415 :
416 : do {
417 0 : again = false;
418 :
419 : /* always zero out the pwd structure */
420 0 : memset(pwd, 0, sizeof(struct passwd));
421 :
422 : /* get entry */
423 0 : status = ctx->ops.getpwent_r(pwd, buffer, buflen, &ret);
424 :
425 0 : switch (status) {
426 : case NSS_STATUS_TRYAGAIN:
427 : /* buffer too small ? */
428 0 : if (buflen < MAX_BUF_SIZE) {
429 0 : buflen *= 2;
430 : }
431 0 : if (buflen > MAX_BUF_SIZE) {
432 0 : buflen = MAX_BUF_SIZE;
433 : }
434 0 : newbuf = talloc_realloc_size(tmpctx, buffer, buflen);
435 0 : if (!newbuf) {
436 0 : ret = ENOMEM;
437 0 : goto done;
438 : }
439 0 : buffer = newbuf;
440 0 : again = true;
441 0 : break;
442 :
443 : case NSS_STATUS_NOTFOUND:
444 :
445 : /* we are done here */
446 0 : DEBUG(SSSDBG_TRACE_LIBS, "Enumeration completed.\n");
447 :
448 0 : ret = sysdb_transaction_commit(sysdb);
449 0 : if (ret != EOK) {
450 0 : DEBUG(SSSDBG_CRIT_FAILURE, "Failed to commit transaction\n");
451 0 : goto done;
452 : }
453 0 : in_transaction = false;
454 0 : break;
455 :
456 : case NSS_STATUS_SUCCESS:
457 :
458 0 : DEBUG(SSSDBG_TRACE_LIBS,
459 : "User found (%s, %"SPRIuid", %"SPRIgid")\n",
460 : pwd->pw_name, pwd->pw_uid, pwd->pw_gid);
461 :
462 : /* uid=0 or gid=0 are invalid values */
463 : /* also check that the id is in the valid range for this domain
464 : */
465 0 : if (OUT_OF_ID_RANGE(pwd->pw_uid, dom->id_min, dom->id_max) ||
466 0 : OUT_OF_ID_RANGE(pwd->pw_gid, dom->id_min, dom->id_max)) {
467 :
468 0 : DEBUG(SSSDBG_OP_FAILURE, "User [%s] filtered out! (id out"
469 : " of range)\n", pwd->pw_name);
470 :
471 0 : again = true;
472 0 : break;
473 : }
474 :
475 0 : ret = save_user(dom, !dom->case_sensitive, pwd,
476 0 : pwd->pw_name, NULL, dom->user_timeout);
477 0 : if (ret) {
478 : /* Do not fail completely on errors.
479 : * Just report the failure to save and go on */
480 0 : DEBUG(SSSDBG_OP_FAILURE, "Failed to store user %s."
481 : " Ignoring.\n", pwd->pw_name);
482 : }
483 0 : again = true;
484 0 : break;
485 :
486 : case NSS_STATUS_UNAVAIL:
487 : /* "remote" backend unavailable. Enter offline mode */
488 0 : ret = ENXIO;
489 0 : break;
490 :
491 : default:
492 0 : ret = EIO;
493 0 : DEBUG(SSSDBG_OP_FAILURE, "proxy -> getpwent_r failed (%d)[%s]"
494 : "\n", ret, strerror(ret));
495 0 : break;
496 : }
497 0 : } while (again);
498 :
499 : done:
500 0 : talloc_zfree(tmpctx);
501 0 : if (in_transaction) {
502 0 : sret = sysdb_transaction_cancel(sysdb);
503 0 : if (sret != EOK) {
504 0 : DEBUG(SSSDBG_CRIT_FAILURE, "Failed to cancel transaction\n");
505 : }
506 : }
507 0 : ctx->ops.endpwent();
508 0 : return ret;
509 : }
510 :
511 : /* =Save-group-utilities=================================================*/
512 : #define DEBUG_GR_MEM(level, grp) \
513 : do { \
514 : if (DEBUG_IS_SET(level)) { \
515 : if (!grp->gr_mem || !grp->gr_mem[0]) { \
516 : DEBUG(level, "Group %s has no members!\n", \
517 : grp->gr_name); \
518 : } else { \
519 : int i = 0; \
520 : while (grp->gr_mem[i]) { \
521 : /* count */ \
522 : i++; \
523 : } \
524 : DEBUG(level, "Group %s has %d members!\n", \
525 : grp->gr_name, i); \
526 : } \
527 : } \
528 : } while(0)
529 :
530 :
531 : static errno_t proxy_process_missing_users(struct sysdb_ctx *sysdb,
532 : struct sss_domain_info *domain,
533 : struct sysdb_attrs *group_attrs,
534 : struct group *grp,
535 : time_t now);
536 0 : static int save_group(struct sysdb_ctx *sysdb, struct sss_domain_info *dom,
537 : struct group *grp, const char *real_name,
538 : const char *alias, uint64_t cache_timeout)
539 : {
540 : errno_t ret, sret;
541 0 : struct sysdb_attrs *attrs = NULL;
542 : const char *cased_alias;
543 0 : const char *lc_gr_name = NULL;
544 : TALLOC_CTX *tmp_ctx;
545 0 : time_t now = time(NULL);
546 0 : bool in_transaction = false;
547 :
548 0 : tmp_ctx = talloc_new(NULL);
549 0 : if (!tmp_ctx) {
550 0 : return ENOMEM;
551 : }
552 :
553 0 : DEBUG_GR_MEM(SSSDBG_TRACE_LIBS, grp);
554 :
555 0 : ret = sysdb_transaction_start(sysdb);
556 0 : if (ret != EOK) {
557 0 : DEBUG(SSSDBG_CRIT_FAILURE, "Failed to start transaction\n");
558 0 : goto done;
559 : }
560 0 : in_transaction = true;
561 :
562 0 : if (grp->gr_mem && grp->gr_mem[0]) {
563 0 : attrs = sysdb_new_attrs(tmp_ctx);
564 0 : if (!attrs) {
565 0 : DEBUG(SSSDBG_CRIT_FAILURE, "Allocation error ?!\n");
566 0 : ret = ENOMEM;
567 0 : goto done;
568 : }
569 :
570 0 : ret = sysdb_attrs_users_from_str_list(
571 0 : attrs, SYSDB_MEMBER, dom->name,
572 0 : (const char *const *)grp->gr_mem);
573 0 : if (ret) {
574 0 : DEBUG(SSSDBG_OP_FAILURE, "Could not add group members\n");
575 0 : goto done;
576 : }
577 :
578 : /* Create ghost users */
579 0 : ret = proxy_process_missing_users(sysdb, dom, attrs, grp, now);
580 0 : if (ret != EOK) {
581 0 : DEBUG(SSSDBG_OP_FAILURE, "Could not add missing members\n");
582 0 : goto done;
583 : }
584 : }
585 :
586 0 : if (dom->case_sensitive == false || alias) {
587 0 : if (!attrs) {
588 0 : attrs = sysdb_new_attrs(tmp_ctx);
589 0 : if (!attrs) {
590 0 : DEBUG(SSSDBG_CRIT_FAILURE, "Allocation error ?!\n");
591 0 : ret = ENOMEM;
592 0 : goto done;
593 : }
594 : }
595 : }
596 :
597 0 : if (dom->case_sensitive == false) {
598 0 : lc_gr_name = sss_tc_utf8_str_tolower(attrs, grp->gr_name);
599 0 : if (lc_gr_name == NULL) {
600 0 : DEBUG(SSSDBG_OP_FAILURE, "Cannot convert name to lowercase.\n");
601 0 : ret = ENOMEM;
602 0 : goto done;
603 : }
604 :
605 0 : ret = sysdb_attrs_add_string(attrs, SYSDB_NAME_ALIAS, lc_gr_name);
606 0 : if (ret != EOK) {
607 0 : goto done;
608 : }
609 : }
610 :
611 0 : if (alias) {
612 0 : cased_alias = sss_get_cased_name(attrs, alias, dom->case_sensitive);
613 0 : if (!cased_alias) {
614 0 : ret = ENOMEM;
615 0 : DEBUG(SSSDBG_OP_FAILURE, "Could not add name alias\n");
616 0 : goto done;
617 : }
618 :
619 0 : if (lc_gr_name == NULL || strcmp(cased_alias, lc_gr_name)) {
620 0 : ret = sysdb_attrs_add_string(attrs, SYSDB_NAME_ALIAS, cased_alias);
621 0 : if (ret) {
622 0 : DEBUG(SSSDBG_OP_FAILURE, "Could not add name alias\n");
623 0 : goto done;
624 : }
625 : }
626 : }
627 :
628 0 : ret = sysdb_store_group(dom,
629 : real_name,
630 : grp->gr_gid,
631 : attrs,
632 : cache_timeout,
633 : now);
634 0 : if (ret) {
635 0 : DEBUG(SSSDBG_OP_FAILURE, "Could not add group to cache\n");
636 0 : goto done;
637 : }
638 :
639 0 : ret = sysdb_transaction_commit(sysdb);
640 0 : if (ret != EOK) {
641 0 : DEBUG(SSSDBG_CRIT_FAILURE,
642 : "Could not commit transaction: [%s]\n",
643 : strerror(ret));
644 0 : goto done;
645 : }
646 0 : in_transaction = false;
647 :
648 : done:
649 0 : if (in_transaction) {
650 0 : sret = sysdb_transaction_cancel(sysdb);
651 0 : if (sret != EOK) {
652 0 : DEBUG(SSSDBG_CRIT_FAILURE, "Could not cancel transaction\n");
653 : }
654 : }
655 0 : talloc_free(tmp_ctx);
656 0 : return ret;
657 : }
658 :
659 0 : static errno_t proxy_process_missing_users(struct sysdb_ctx *sysdb,
660 : struct sss_domain_info *domain,
661 : struct sysdb_attrs *group_attrs,
662 : struct group *grp,
663 : time_t now)
664 : {
665 : errno_t ret;
666 : size_t i;
667 0 : TALLOC_CTX *tmp_ctx = NULL;
668 : struct ldb_message *msg;
669 :
670 0 : if (!sysdb || !grp) return EINVAL;
671 :
672 0 : tmp_ctx = talloc_new(NULL);
673 0 : if (!tmp_ctx) return ENOMEM;
674 :
675 0 : for (i = 0; grp->gr_mem[i]; i++) {
676 0 : ret = sysdb_search_user_by_name(tmp_ctx, domain, grp->gr_mem[i],
677 : NULL, &msg);
678 0 : if (ret == EOK) {
679 : /* Member already exists in the cache */
680 0 : DEBUG(SSSDBG_TRACE_INTERNAL,
681 : "Member [%s] already cached\n", grp->gr_mem[i]);
682 : /* clean up */
683 0 : talloc_zfree(msg);
684 0 : continue;
685 0 : } else if (ret == ENOENT) {
686 : /* No entry for this user. Create a ghost user */
687 0 : DEBUG(SSSDBG_TRACE_LIBS,
688 : "Member [%s] not cached, creating ghost user entry\n",
689 : grp->gr_mem[i]);
690 :
691 0 : ret = sysdb_attrs_add_string(group_attrs, SYSDB_GHOST, grp->gr_mem[i]);
692 0 : if (ret != EOK) {
693 0 : DEBUG(SSSDBG_MINOR_FAILURE,
694 : "Cannot store ghost user entry: [%d]: %s\n",
695 : ret, strerror(ret));
696 0 : goto done;
697 : }
698 : } else {
699 : /* Unexpected error */
700 0 : DEBUG(SSSDBG_MINOR_FAILURE,
701 : "Error searching cache for user [%s]: [%s]\n",
702 : grp->gr_mem[i], strerror(ret));
703 0 : goto done;
704 : }
705 : }
706 :
707 0 : ret = EOK;
708 : done:
709 0 : talloc_free(tmp_ctx);
710 0 : return ret;
711 : }
712 :
713 : /* =Getgrnam-wrapper======================================================*/
714 : static char *
715 0 : grow_group_buffer(TALLOC_CTX *mem_ctx,
716 : char **buffer, size_t *buflen)
717 : {
718 : char *newbuf;
719 :
720 0 : if (*buflen == 0) {
721 0 : *buflen = DEFAULT_BUFSIZE;
722 : }
723 0 : if (*buflen < MAX_BUF_SIZE) {
724 0 : *buflen *= 2;
725 : }
726 0 : if (*buflen > MAX_BUF_SIZE) {
727 0 : *buflen = MAX_BUF_SIZE;
728 : }
729 :
730 0 : newbuf = talloc_realloc_size(mem_ctx, *buffer, *buflen);
731 0 : if (!newbuf) {
732 0 : return NULL;
733 : }
734 0 : *buffer = newbuf;
735 :
736 0 : return *buffer;
737 : }
738 :
739 : static errno_t
740 0 : handle_getgr_result(enum nss_status status, struct group *grp,
741 : struct sss_domain_info *dom,
742 : bool *delete_group)
743 : {
744 0 : switch (status) {
745 : case NSS_STATUS_TRYAGAIN:
746 0 : DEBUG(SSSDBG_MINOR_FAILURE, "Buffer too small\n");
747 0 : return EAGAIN;
748 :
749 : case NSS_STATUS_NOTFOUND:
750 0 : DEBUG(SSSDBG_MINOR_FAILURE, "Group not found.\n");
751 0 : *delete_group = true;
752 0 : break;
753 :
754 : case NSS_STATUS_SUCCESS:
755 0 : DEBUG(SSSDBG_FUNC_DATA, "Group found: (%s, %"SPRIgid")\n",
756 : grp->gr_name, grp->gr_gid);
757 :
758 : /* gid=0 is an invalid value */
759 : /* also check that the id is in the valid range for this domain */
760 0 : if (OUT_OF_ID_RANGE(grp->gr_gid, dom->id_min, dom->id_max)) {
761 0 : DEBUG(SSSDBG_MINOR_FAILURE,
762 : "Group filtered out! (id out of range)\n");
763 0 : *delete_group = true;
764 0 : break;
765 : }
766 0 : break;
767 :
768 : case NSS_STATUS_UNAVAIL:
769 0 : DEBUG(SSSDBG_MINOR_FAILURE,
770 : "Remote back end is not available. Entering offline mode\n");
771 0 : return ENXIO;
772 :
773 : default:
774 0 : DEBUG(SSSDBG_OP_FAILURE, "Unknown return code %d\n", status);
775 0 : return EIO;
776 : }
777 :
778 0 : return EOK;
779 : }
780 :
781 0 : static int get_gr_name(struct proxy_id_ctx *ctx,
782 : struct sysdb_ctx *sysdb,
783 : struct sss_domain_info *dom,
784 : const char *name)
785 : {
786 : TALLOC_CTX *tmpctx;
787 : struct group *grp;
788 : enum nss_status status;
789 0 : char *buffer = 0;
790 0 : size_t buflen = 0;
791 0 : bool delete_group = false;
792 : int ret;
793 : gid_t gid;
794 0 : struct ldb_result *cached_grp = NULL;
795 0 : const char *real_name = NULL;
796 :
797 0 : DEBUG(SSSDBG_FUNC_DATA, "Searching group by name (%s)\n", name);
798 :
799 0 : tmpctx = talloc_new(NULL);
800 0 : if (!tmpctx) {
801 0 : return ENOMEM;
802 : }
803 :
804 0 : grp = talloc(tmpctx, struct group);
805 0 : if (!grp) {
806 0 : ret = ENOMEM;
807 0 : DEBUG(SSSDBG_CRIT_FAILURE,
808 : "proxy -> getgrnam_r failed for '%s': [%d] %s\n",
809 : name, ret, strerror(ret));
810 0 : goto done;
811 : }
812 :
813 : do {
814 : /* always zero out the grp structure */
815 0 : memset(grp, 0, sizeof(struct group));
816 0 : buffer = grow_group_buffer(tmpctx, &buffer, &buflen);
817 0 : if (!buffer) {
818 0 : ret = ENOMEM;
819 0 : goto done;
820 : }
821 :
822 0 : status = ctx->ops.getgrnam_r(name, grp, buffer, buflen, &ret);
823 :
824 0 : ret = handle_getgr_result(status, grp, dom, &delete_group);
825 0 : } while (ret == EAGAIN);
826 :
827 0 : if (ret != EOK) {
828 0 : DEBUG(SSSDBG_OP_FAILURE,
829 : "getgrnam failed [%d]: %s\n", ret, strerror(ret));
830 0 : goto done;
831 : }
832 :
833 0 : gid = grp->gr_gid;
834 :
835 : /* Canonicalize the group name in case it was actually an alias */
836 0 : if (ctx->fast_alias == true) {
837 0 : ret = sysdb_getgrgid(tmpctx, dom, gid, &cached_grp);
838 0 : if (ret != EOK) {
839 : /* Non-fatal, attempt to canonicalize online */
840 0 : DEBUG(SSSDBG_TRACE_FUNC, "Request to cache failed [%d]: %s\n",
841 : ret, strerror(ret));
842 : }
843 :
844 0 : if (ret == EOK && cached_grp->count == 1) {
845 0 : real_name = ldb_msg_find_attr_as_string(cached_grp->msgs[0],
846 : SYSDB_NAME, NULL);
847 0 : if (!real_name) {
848 0 : DEBUG(SSSDBG_MINOR_FAILURE, "Cached group has no name?\n");
849 : }
850 : }
851 : }
852 :
853 0 : if (real_name == NULL) {
854 0 : talloc_zfree(buffer);
855 0 : buflen = 0;
856 :
857 : do {
858 0 : memset(grp, 0, sizeof(struct group));
859 0 : buffer = grow_group_buffer(tmpctx, &buffer, &buflen);
860 0 : if (!buffer) {
861 0 : ret = ENOMEM;
862 0 : goto done;
863 : }
864 :
865 0 : status = ctx->ops.getgrgid_r(gid, grp, buffer, buflen, &ret);
866 :
867 0 : ret = handle_getgr_result(status, grp, dom, &delete_group);
868 0 : } while (ret == EAGAIN);
869 :
870 0 : if (ret != EOK) {
871 0 : DEBUG(SSSDBG_OP_FAILURE,
872 : "getgrgid failed [%d]: %s\n", ret, strerror(ret));
873 0 : goto done;
874 : }
875 :
876 0 : real_name = grp->gr_name;
877 : }
878 :
879 0 : if (delete_group) {
880 0 : DEBUG(SSSDBG_TRACE_FUNC,
881 : "Group %s does not exist (or is invalid) on remote server,"
882 : " deleting!\n", name);
883 :
884 0 : ret = sysdb_delete_group(dom, NULL, gid);
885 0 : if (ret == ENOENT) {
886 0 : ret = EOK;
887 : }
888 0 : goto done;
889 : }
890 :
891 0 : ret = save_group(sysdb, dom, grp, real_name, name, dom->group_timeout);
892 0 : if (ret) {
893 0 : DEBUG(SSSDBG_OP_FAILURE,
894 : "Cannot save group [%d]: %s\n", ret, strerror(ret));
895 0 : goto done;
896 : }
897 :
898 : done:
899 0 : talloc_zfree(tmpctx);
900 0 : if (ret) {
901 0 : DEBUG(SSSDBG_OP_FAILURE,
902 : "proxy -> getgrnam_r failed for '%s' <%d>: %s\n",
903 : name, ret, strerror(ret));
904 : }
905 0 : return ret;
906 : }
907 :
908 : /* =Getgrgid-wrapper======================================================*/
909 0 : static int get_gr_gid(TALLOC_CTX *mem_ctx,
910 : struct proxy_id_ctx *ctx,
911 : struct sysdb_ctx *sysdb,
912 : struct sss_domain_info *dom,
913 : gid_t gid,
914 : time_t now)
915 : {
916 : TALLOC_CTX *tmpctx;
917 : struct group *grp;
918 : enum nss_status status;
919 0 : char *buffer = NULL;
920 0 : size_t buflen = 0;
921 0 : bool delete_group = false;
922 : int ret;
923 :
924 0 : DEBUG(SSSDBG_TRACE_FUNC, "Searching group by gid (%"SPRIgid")\n", gid);
925 :
926 0 : tmpctx = talloc_new(mem_ctx);
927 0 : if (!tmpctx) {
928 0 : return ENOMEM;
929 : }
930 :
931 0 : grp = talloc(tmpctx, struct group);
932 0 : if (!grp) {
933 0 : ret = ENOMEM;
934 0 : goto done;
935 : }
936 :
937 : do {
938 : /* always zero out the grp structure */
939 0 : memset(grp, 0, sizeof(struct group));
940 0 : buffer = grow_group_buffer(tmpctx, &buffer, &buflen);
941 0 : if (!buffer) {
942 0 : ret = ENOMEM;
943 0 : goto done;
944 : }
945 :
946 0 : status = ctx->ops.getgrgid_r(gid, grp, buffer, buflen, &ret);
947 :
948 0 : ret = handle_getgr_result(status, grp, dom, &delete_group);
949 0 : } while (ret == EAGAIN);
950 :
951 0 : if (ret != EOK) {
952 0 : DEBUG(SSSDBG_OP_FAILURE,
953 : "getgrgid failed [%d]: %s\n", ret, strerror(ret));
954 0 : goto done;
955 : }
956 :
957 0 : if (delete_group) {
958 0 : DEBUG(SSSDBG_TRACE_FUNC,
959 : "Group %"SPRIgid" does not exist (or is invalid) on remote "
960 : "server, deleting!\n", gid);
961 :
962 0 : ret = sysdb_delete_group(dom, NULL, gid);
963 0 : if (ret == ENOENT) {
964 0 : ret = EOK;
965 : }
966 0 : goto done;
967 : }
968 :
969 0 : ret = save_group(sysdb, dom, grp, grp->gr_name, NULL, dom->group_timeout);
970 0 : if (ret) {
971 0 : DEBUG(SSSDBG_OP_FAILURE,
972 : "Cannot save user [%d]: %s\n", ret, strerror(ret));
973 0 : goto done;
974 : }
975 :
976 : done:
977 0 : talloc_zfree(tmpctx);
978 0 : if (ret) {
979 0 : DEBUG(SSSDBG_OP_FAILURE,
980 : "proxy -> getgrgid_r failed for '%"SPRIgid"' <%d>: %s\n",
981 : gid, ret, strerror(ret));
982 : }
983 0 : return ret;
984 : }
985 :
986 : /* =Getgrent-wrapper======================================================*/
987 :
988 0 : static int enum_groups(TALLOC_CTX *mem_ctx,
989 : struct proxy_id_ctx *ctx,
990 : struct sysdb_ctx *sysdb,
991 : struct sss_domain_info *dom)
992 : {
993 : TALLOC_CTX *tmpctx;
994 0 : bool in_transaction = false;
995 : struct group *grp;
996 : enum nss_status status;
997 : size_t buflen;
998 : char *buffer;
999 : char *newbuf;
1000 : int ret;
1001 : errno_t sret;
1002 : bool again;
1003 :
1004 0 : DEBUG(SSSDBG_TRACE_LIBS, "Enumerating groups\n");
1005 :
1006 0 : tmpctx = talloc_new(mem_ctx);
1007 0 : if (!tmpctx) {
1008 0 : return ENOMEM;
1009 : }
1010 :
1011 0 : grp = talloc(tmpctx, struct group);
1012 0 : if (!grp) {
1013 0 : ret = ENOMEM;
1014 0 : goto done;
1015 : }
1016 :
1017 0 : buflen = DEFAULT_BUFSIZE;
1018 0 : buffer = talloc_size(tmpctx, buflen);
1019 0 : if (!buffer) {
1020 0 : ret = ENOMEM;
1021 0 : goto done;
1022 : }
1023 :
1024 0 : ret = sysdb_transaction_start(sysdb);
1025 0 : if (ret) {
1026 0 : DEBUG(SSSDBG_CRIT_FAILURE, "Failed to start transaction\n");
1027 0 : goto done;
1028 : }
1029 0 : in_transaction = true;
1030 :
1031 0 : status = ctx->ops.setgrent();
1032 0 : if (status != NSS_STATUS_SUCCESS) {
1033 0 : ret = EIO;
1034 0 : goto done;
1035 : }
1036 :
1037 : do {
1038 0 : again = false;
1039 :
1040 : /* always zero out the grp structure */
1041 0 : memset(grp, 0, sizeof(struct group));
1042 :
1043 : /* get entry */
1044 0 : status = ctx->ops.getgrent_r(grp, buffer, buflen, &ret);
1045 :
1046 0 : switch (status) {
1047 : case NSS_STATUS_TRYAGAIN:
1048 : /* buffer too small ? */
1049 0 : if (buflen < MAX_BUF_SIZE) {
1050 0 : buflen *= 2;
1051 : }
1052 0 : if (buflen > MAX_BUF_SIZE) {
1053 0 : buflen = MAX_BUF_SIZE;
1054 : }
1055 0 : newbuf = talloc_realloc_size(tmpctx, buffer, buflen);
1056 0 : if (!newbuf) {
1057 0 : ret = ENOMEM;
1058 0 : goto done;
1059 : }
1060 0 : buffer = newbuf;
1061 0 : again = true;
1062 0 : break;
1063 :
1064 : case NSS_STATUS_NOTFOUND:
1065 :
1066 : /* we are done here */
1067 0 : DEBUG(SSSDBG_TRACE_LIBS, "Enumeration completed.\n");
1068 :
1069 0 : ret = sysdb_transaction_commit(sysdb);
1070 0 : if (ret != EOK) {
1071 0 : DEBUG(SSSDBG_CRIT_FAILURE, "Failed to commit transaction\n");
1072 0 : goto done;
1073 : }
1074 0 : in_transaction = false;
1075 0 : break;
1076 :
1077 : case NSS_STATUS_SUCCESS:
1078 :
1079 0 : DEBUG(SSSDBG_OP_FAILURE, "Group found (%s, %"SPRIgid")\n",
1080 : grp->gr_name, grp->gr_gid);
1081 :
1082 : /* gid=0 is an invalid value */
1083 : /* also check that the id is in the valid range for this domain
1084 : */
1085 0 : if (OUT_OF_ID_RANGE(grp->gr_gid, dom->id_min, dom->id_max)) {
1086 :
1087 0 : DEBUG(SSSDBG_OP_FAILURE, "Group [%s] filtered out! (id"
1088 : "out of range)\n", grp->gr_name);
1089 :
1090 0 : again = true;
1091 0 : break;
1092 : }
1093 :
1094 0 : ret = save_group(sysdb, dom, grp, grp->gr_name,
1095 0 : NULL, dom->group_timeout);
1096 0 : if (ret) {
1097 : /* Do not fail completely on errors.
1098 : * Just report the failure to save and go on */
1099 0 : DEBUG(SSSDBG_OP_FAILURE, "Failed to store group."
1100 : "Ignoring\n");
1101 : }
1102 0 : again = true;
1103 0 : break;
1104 :
1105 : case NSS_STATUS_UNAVAIL:
1106 : /* "remote" backend unavailable. Enter offline mode */
1107 0 : ret = ENXIO;
1108 0 : break;
1109 :
1110 : default:
1111 0 : ret = EIO;
1112 0 : DEBUG(SSSDBG_OP_FAILURE, "proxy -> getgrent_r failed (%d)[%s]"
1113 : "\n", ret, strerror(ret));
1114 0 : break;
1115 : }
1116 0 : } while (again);
1117 :
1118 : done:
1119 0 : talloc_zfree(tmpctx);
1120 0 : if (in_transaction) {
1121 0 : sret = sysdb_transaction_cancel(sysdb);
1122 0 : if (sret != EOK) {
1123 0 : DEBUG(SSSDBG_CRIT_FAILURE, "Failed to cancel transaction\n");
1124 : }
1125 : }
1126 0 : ctx->ops.endgrent();
1127 0 : return ret;
1128 : }
1129 :
1130 :
1131 : /* =Initgroups-wrapper====================================================*/
1132 :
1133 : static int get_initgr_groups_process(TALLOC_CTX *memctx,
1134 : struct proxy_id_ctx *ctx,
1135 : struct sysdb_ctx *sysdb,
1136 : struct sss_domain_info *dom,
1137 : struct passwd *pwd);
1138 :
1139 0 : static int get_initgr(TALLOC_CTX *mem_ctx,
1140 : struct proxy_id_ctx *ctx,
1141 : struct sysdb_ctx *sysdb,
1142 : struct sss_domain_info *dom,
1143 : const char *name)
1144 : {
1145 : TALLOC_CTX *tmpctx;
1146 0 : bool in_transaction = false;
1147 : struct passwd *pwd;
1148 : enum nss_status status;
1149 : char *buffer;
1150 : size_t buflen;
1151 : int ret;
1152 : errno_t sret;
1153 : bool del_user;
1154 : uid_t uid;
1155 0 : struct ldb_result *cached_pwd = NULL;
1156 0 : const char *real_name = NULL;
1157 :
1158 0 : tmpctx = talloc_new(mem_ctx);
1159 0 : if (!tmpctx) {
1160 0 : return ENOMEM;
1161 : }
1162 :
1163 0 : pwd = talloc_zero(tmpctx, struct passwd);
1164 0 : if (!pwd) {
1165 0 : ret = ENOMEM;
1166 0 : goto fail;
1167 : }
1168 :
1169 0 : buflen = DEFAULT_BUFSIZE;
1170 0 : buffer = talloc_size(tmpctx, buflen);
1171 0 : if (!buffer) {
1172 0 : ret = ENOMEM;
1173 0 : goto fail;
1174 : }
1175 :
1176 0 : ret = sysdb_transaction_start(sysdb);
1177 0 : if (ret) {
1178 0 : DEBUG(SSSDBG_CRIT_FAILURE, "Failed to start transaction\n");
1179 0 : goto fail;
1180 : }
1181 0 : in_transaction = true;
1182 :
1183 : /* FIXME: should we move this call outside the transaction to keep the
1184 : * transaction as short as possible ? */
1185 0 : status = ctx->ops.getpwnam_r(name, pwd, buffer, buflen, &ret);
1186 0 : ret = handle_getpw_result(status, pwd, dom, &del_user);
1187 0 : if (ret) {
1188 0 : DEBUG(SSSDBG_OP_FAILURE,
1189 : "getpwnam failed [%d]: %s\n", ret, strerror(ret));
1190 0 : goto fail;
1191 : }
1192 :
1193 0 : if (del_user) {
1194 0 : ret = delete_user(dom, name, 0);
1195 0 : if (ret) {
1196 0 : DEBUG(SSSDBG_OP_FAILURE, "Could not delete user\n");
1197 0 : goto fail;
1198 : }
1199 0 : goto done;
1200 : }
1201 :
1202 0 : uid = pwd->pw_uid;
1203 0 : memset(buffer, 0, buflen);
1204 :
1205 : /* Canonicalize the username in case it was actually an alias */
1206 0 : if (ctx->fast_alias == true) {
1207 0 : ret = sysdb_getpwuid(tmpctx, dom, uid, &cached_pwd);
1208 0 : if (ret != EOK) {
1209 : /* Non-fatal, attempt to canonicalize online */
1210 0 : DEBUG(SSSDBG_TRACE_FUNC, "Request to cache failed [%d]: %s\n",
1211 : ret, strerror(ret));
1212 : }
1213 :
1214 0 : if (ret == EOK && cached_pwd->count == 1) {
1215 0 : real_name = ldb_msg_find_attr_as_string(cached_pwd->msgs[0],
1216 : SYSDB_NAME, NULL);
1217 0 : if (!real_name) {
1218 0 : DEBUG(SSSDBG_MINOR_FAILURE, "Cached user has no name?\n");
1219 : }
1220 : }
1221 : }
1222 :
1223 0 : if (real_name == NULL) {
1224 0 : memset(buffer, 0, buflen);
1225 :
1226 0 : status = ctx->ops.getpwuid_r(uid, pwd, buffer, buflen, &ret);
1227 0 : ret = handle_getpw_result(status, pwd, dom, &del_user);
1228 0 : if (ret) {
1229 0 : DEBUG(SSSDBG_OP_FAILURE,
1230 : "getpwuid failed [%d]: %s\n", ret, strerror(ret));
1231 0 : goto done;
1232 : }
1233 :
1234 0 : real_name = pwd->pw_name;
1235 : }
1236 :
1237 0 : if (del_user) {
1238 0 : ret = delete_user(dom, name, uid);
1239 0 : if (ret) {
1240 0 : DEBUG(SSSDBG_OP_FAILURE, "Could not delete user\n");
1241 0 : goto fail;
1242 : }
1243 0 : goto done;
1244 : }
1245 :
1246 0 : ret = save_user(dom, !dom->case_sensitive, pwd,
1247 0 : real_name, name, dom->user_timeout);
1248 0 : if (ret) {
1249 0 : DEBUG(SSSDBG_OP_FAILURE, "Could not save user\n");
1250 0 : goto fail;
1251 : }
1252 :
1253 0 : ret = get_initgr_groups_process(tmpctx, ctx, sysdb, dom, pwd);
1254 0 : if (ret != EOK) {
1255 0 : DEBUG(SSSDBG_OP_FAILURE, "Could not process initgroups\n");
1256 0 : goto fail;
1257 : }
1258 :
1259 : done:
1260 0 : ret = sysdb_transaction_commit(sysdb);
1261 0 : if (ret) {
1262 0 : DEBUG(SSSDBG_OP_FAILURE, "Failed to commit transaction\n");
1263 0 : goto fail;
1264 : }
1265 0 : in_transaction = false;
1266 :
1267 : fail:
1268 0 : talloc_zfree(tmpctx);
1269 0 : if (in_transaction) {
1270 0 : sret = sysdb_transaction_cancel(sysdb);
1271 0 : if (sret != EOK) {
1272 0 : DEBUG(SSSDBG_CRIT_FAILURE, "Failed to cancel transaction\n");
1273 : }
1274 : }
1275 0 : return ret;
1276 : }
1277 :
1278 0 : static int get_initgr_groups_process(TALLOC_CTX *memctx,
1279 : struct proxy_id_ctx *ctx,
1280 : struct sysdb_ctx *sysdb,
1281 : struct sss_domain_info *dom,
1282 : struct passwd *pwd)
1283 : {
1284 : enum nss_status status;
1285 : long int limit;
1286 : long int size;
1287 : long int num;
1288 : long int num_gids;
1289 : gid_t *gids;
1290 : int ret;
1291 : int i;
1292 : time_t now;
1293 :
1294 0 : num_gids = 0;
1295 0 : limit = 4096;
1296 0 : num = 4096;
1297 0 : size = num*sizeof(gid_t);
1298 0 : gids = talloc_size(memctx, size);
1299 0 : if (!gids) {
1300 0 : return ENOMEM;
1301 : }
1302 :
1303 : /* nss modules may skip the primary group when we pass it in so always add
1304 : * it in advance */
1305 0 : gids[0] = pwd->pw_gid;
1306 0 : num_gids++;
1307 :
1308 : /* FIXME: should we move this call outside the transaction to keep the
1309 : * transaction as short as possible ? */
1310 : do {
1311 0 : status = ctx->ops.initgroups_dyn(pwd->pw_name, pwd->pw_gid, &num_gids,
1312 : &num, &gids, limit, &ret);
1313 :
1314 0 : if (status == NSS_STATUS_TRYAGAIN) {
1315 : /* buffer too small ? */
1316 0 : if (size < MAX_BUF_SIZE) {
1317 0 : num *= 2;
1318 0 : size = num*sizeof(gid_t);
1319 : }
1320 0 : if (size > MAX_BUF_SIZE) {
1321 0 : size = MAX_BUF_SIZE;
1322 0 : num = size/sizeof(gid_t);
1323 : }
1324 0 : limit = num;
1325 0 : gids = talloc_realloc_size(memctx, gids, size);
1326 0 : if (!gids) {
1327 0 : return ENOMEM;
1328 : }
1329 : }
1330 0 : } while(status == NSS_STATUS_TRYAGAIN);
1331 :
1332 0 : switch (status) {
1333 : case NSS_STATUS_NOTFOUND:
1334 0 : DEBUG(SSSDBG_FUNC_DATA, "The initgroups call returned 'NOTFOUND'. "
1335 : "Assume the user is only member of its "
1336 : "primary group (%"SPRIgid")\n", pwd->pw_gid);
1337 : /* fall through */
1338 : case NSS_STATUS_SUCCESS:
1339 0 : DEBUG(SSSDBG_CONF_SETTINGS, "User [%s] appears to be member of %lu "
1340 : "groups\n", pwd->pw_name, num_gids);
1341 :
1342 0 : now = time(NULL);
1343 0 : for (i = 0; i < num_gids; i++) {
1344 0 : ret = get_gr_gid(memctx, ctx, sysdb, dom, gids[i], now);
1345 0 : if (ret) {
1346 0 : return ret;
1347 : }
1348 : }
1349 0 : ret = EOK;
1350 :
1351 0 : break;
1352 :
1353 : default:
1354 0 : DEBUG(SSSDBG_OP_FAILURE, "proxy -> initgroups_dyn failed (%d)[%s]\n",
1355 : ret, strerror(ret));
1356 0 : ret = EIO;
1357 0 : break;
1358 : }
1359 :
1360 0 : return ret;
1361 : }
1362 :
1363 : /* =Proxy_Id-Functions====================================================*/
1364 :
1365 : static struct dp_reply_std
1366 0 : proxy_account_info(TALLOC_CTX *mem_ctx,
1367 : struct proxy_id_ctx *ctx,
1368 : struct be_acct_req *data,
1369 : struct be_ctx *be_ctx,
1370 : struct sss_domain_info *domain)
1371 : {
1372 : struct dp_reply_std reply;
1373 : struct sysdb_ctx *sysdb;
1374 : uid_t uid;
1375 : gid_t gid;
1376 : errno_t ret;
1377 : char *endptr;
1378 :
1379 0 : sysdb = domain->sysdb;
1380 :
1381 : /* For now we support only core attrs. */
1382 0 : if (data->attr_type != BE_ATTR_CORE) {
1383 0 : dp_reply_std_set(&reply, DP_ERR_FATAL, EINVAL, "Invalid attr type");
1384 0 : return reply;
1385 : }
1386 :
1387 : /* Proxy provider does not support security ID lookups. */
1388 0 : if (data->filter_type == BE_FILTER_SECID) {
1389 0 : dp_reply_std_set(&reply, DP_ERR_FATAL, ENOSYS,
1390 : "Security lookups are not supported");
1391 0 : return reply;
1392 : }
1393 :
1394 0 : switch (data->entry_type & BE_REQ_TYPE_MASK) {
1395 : case BE_REQ_USER: /* user */
1396 0 : switch (data->filter_type) {
1397 : case BE_FILTER_ENUM:
1398 0 : ret = enum_users(mem_ctx, ctx, sysdb, domain);
1399 0 : break;
1400 :
1401 : case BE_FILTER_NAME:
1402 0 : ret = get_pw_name(ctx, domain, data->filter_value);
1403 0 : break;
1404 :
1405 : case BE_FILTER_IDNUM:
1406 0 : uid = (uid_t) strtouint32(data->filter_value, &endptr, 10);
1407 0 : if (errno || *endptr || (data->filter_value == endptr)) {
1408 0 : dp_reply_std_set(&reply, DP_ERR_FATAL, EINVAL,
1409 : "Invalid attr type");
1410 0 : return reply;
1411 : }
1412 0 : ret = get_pw_uid(ctx, domain, uid);
1413 0 : break;
1414 : default:
1415 0 : dp_reply_std_set(&reply, DP_ERR_FATAL, EINVAL,
1416 : "Invalid filter type");
1417 0 : return reply;
1418 : }
1419 0 : break;
1420 :
1421 : case BE_REQ_GROUP: /* group */
1422 0 : switch (data->filter_type) {
1423 : case BE_FILTER_ENUM:
1424 0 : ret = enum_groups(mem_ctx, ctx, sysdb, domain);
1425 0 : break;
1426 : case BE_FILTER_NAME:
1427 0 : ret = get_gr_name(ctx, sysdb, domain, data->filter_value);
1428 0 : break;
1429 : case BE_FILTER_IDNUM:
1430 0 : gid = (gid_t) strtouint32(data->filter_value, &endptr, 10);
1431 0 : if (errno || *endptr || (data->filter_value == endptr)) {
1432 0 : dp_reply_std_set(&reply, DP_ERR_FATAL, EINVAL,
1433 : "Invalid attr type");
1434 0 : return reply;
1435 : }
1436 0 : ret = get_gr_gid(mem_ctx, ctx, sysdb, domain, gid, 0);
1437 0 : break;
1438 : default:
1439 0 : dp_reply_std_set(&reply, DP_ERR_FATAL, EINVAL,
1440 : "Invalid filter type");
1441 0 : return reply;
1442 : }
1443 0 : break;
1444 :
1445 : case BE_REQ_INITGROUPS: /* init groups for user */
1446 0 : if (data->filter_type != BE_FILTER_NAME) {
1447 0 : dp_reply_std_set(&reply, DP_ERR_FATAL, EINVAL,
1448 : "Invalid filter type");
1449 0 : return reply;
1450 : }
1451 0 : if (ctx->ops.initgroups_dyn == NULL) {
1452 0 : dp_reply_std_set(&reply, DP_ERR_FATAL, ENODEV,
1453 : "Initgroups call not supported");
1454 0 : return reply;
1455 : }
1456 0 : ret = get_initgr(mem_ctx, ctx, sysdb, domain, data->filter_value);
1457 0 : break;
1458 :
1459 : case BE_REQ_NETGROUP:
1460 0 : if (data->filter_type != BE_FILTER_NAME) {
1461 0 : dp_reply_std_set(&reply, DP_ERR_FATAL, EINVAL,
1462 : "Invalid filter type");
1463 0 : return reply;
1464 : }
1465 0 : if (ctx->ops.setnetgrent == NULL || ctx->ops.getnetgrent_r == NULL ||
1466 0 : ctx->ops.endnetgrent == NULL) {
1467 0 : dp_reply_std_set(&reply, DP_ERR_FATAL, ENODEV,
1468 : "Netgroups are not supported");
1469 0 : return reply;
1470 : }
1471 :
1472 0 : ret = get_netgroup(ctx, domain, data->filter_value);
1473 0 : break;
1474 :
1475 : case BE_REQ_SERVICES:
1476 0 : switch (data->filter_type) {
1477 : case BE_FILTER_NAME:
1478 0 : if (ctx->ops.getservbyname_r == NULL) {
1479 0 : dp_reply_std_set(&reply, DP_ERR_FATAL, ENODEV,
1480 : "Services are not supported");
1481 0 : return reply;
1482 : }
1483 0 : ret = get_serv_byname(ctx, domain,
1484 : data->filter_value,
1485 : data->extra_value);
1486 0 : break;
1487 : case BE_FILTER_IDNUM:
1488 0 : if (ctx->ops.getservbyport_r == NULL) {
1489 0 : dp_reply_std_set(&reply, DP_ERR_FATAL, ENODEV,
1490 : "Services are not supported");
1491 0 : return reply;
1492 : }
1493 0 : ret = get_serv_byport(ctx, domain,
1494 : data->filter_value,
1495 : data->extra_value);
1496 0 : break;
1497 : case BE_FILTER_ENUM:
1498 0 : if (!ctx->ops.setservent
1499 0 : || !ctx->ops.getservent_r
1500 0 : || !ctx->ops.endservent) {
1501 0 : dp_reply_std_set(&reply, DP_ERR_FATAL, ENODEV,
1502 : "Services are not supported");
1503 0 : return reply;
1504 : }
1505 0 : ret = enum_services(ctx, sysdb, domain);
1506 0 : break;
1507 : default:
1508 0 : dp_reply_std_set(&reply, DP_ERR_FATAL, EINVAL,
1509 : "Invalid filter type");
1510 0 : return reply;
1511 : }
1512 0 : break;
1513 :
1514 : default: /*fail*/
1515 0 : dp_reply_std_set(&reply, DP_ERR_FATAL, EINVAL,
1516 : "Invalid filter type");
1517 0 : return reply;
1518 : }
1519 :
1520 0 : if (ret) {
1521 0 : if (ret == ENXIO) {
1522 0 : DEBUG(SSSDBG_OP_FAILURE,
1523 : "proxy returned UNAVAIL error, going offline!\n");
1524 0 : be_mark_offline(be_ctx);
1525 : }
1526 :
1527 0 : dp_reply_std_set(&reply, DP_ERR_FATAL, ret, NULL);
1528 0 : return reply;
1529 : }
1530 :
1531 0 : dp_reply_std_set(&reply, DP_ERR_OK, EOK, NULL);
1532 0 : return reply;
1533 : }
1534 :
1535 : struct proxy_account_info_handler_state {
1536 : struct dp_reply_std reply;
1537 : };
1538 :
1539 : struct tevent_req *
1540 0 : proxy_account_info_handler_send(TALLOC_CTX *mem_ctx,
1541 : struct proxy_id_ctx *id_ctx,
1542 : struct be_acct_req *data,
1543 : struct dp_req_params *params)
1544 : {
1545 : struct proxy_account_info_handler_state *state;
1546 : struct tevent_req *req;
1547 :
1548 0 : req = tevent_req_create(mem_ctx, &state,
1549 : struct proxy_account_info_handler_state);
1550 0 : if (req == NULL) {
1551 0 : DEBUG(SSSDBG_CRIT_FAILURE, "tevent_req_create() failed\n");
1552 0 : return NULL;
1553 : }
1554 :
1555 0 : state->reply = proxy_account_info(state, id_ctx, data, params->be_ctx,
1556 0 : params->be_ctx->domain);
1557 :
1558 : /* TODO For backward compatibility we always return EOK to DP now. */
1559 0 : tevent_req_done(req);
1560 0 : tevent_req_post(req, params->ev);
1561 :
1562 0 : return req;
1563 : }
1564 :
1565 0 : errno_t proxy_account_info_handler_recv(TALLOC_CTX *mem_ctx,
1566 : struct tevent_req *req,
1567 : struct dp_reply_std *data)
1568 : {
1569 0 : struct proxy_account_info_handler_state *state = NULL;
1570 :
1571 0 : state = tevent_req_data(req, struct proxy_account_info_handler_state);
1572 :
1573 0 : TEVENT_REQ_RETURN_ON_ERROR(req);
1574 :
1575 0 : *data = state->reply;
1576 :
1577 0 : return EOK;
1578 : }
|