Line data Source code
1 : /*
2 : SSSD
3 :
4 : PAC Responder
5 :
6 : Copyright (C) Sumit Bose <sbose@redhat.com> 2012
7 : Jan Zeleny <jzeleny@redhat.com> 2012
8 :
9 : This program is free software; you can redistribute it and/or modify
10 : it under the terms of the GNU General Public License as published by
11 : the Free Software Foundation; either version 3 of the License, or
12 : (at your option) any later version.
13 :
14 : This program is distributed in the hope that it will be useful,
15 : but WITHOUT ANY WARRANTY; without even the implied warranty of
16 : MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
17 : GNU General Public License for more details.
18 :
19 : You should have received a copy of the GNU General Public License
20 : along with this program. If not, see <http://www.gnu.org/licenses/>.
21 : */
22 :
23 : #include "util/util.h"
24 : #include "responder/pac/pacsrv.h"
25 : #include "confdb/confdb.h"
26 :
27 0 : static errno_t pac_cmd_done(struct cli_ctx *cctx, int cmd_ret)
28 : {
29 : int ret;
30 :
31 0 : if (cmd_ret == EAGAIN) {
32 : /* async processing, just return here */
33 0 : return EOK;
34 : }
35 :
36 0 : ret = sss_packet_new(cctx->creq, 0, sss_packet_get_cmd(cctx->creq->in),
37 0 : &cctx->creq->out);
38 0 : if (ret != EOK) {
39 0 : DEBUG(SSSDBG_OP_FAILURE, "sss_packet_new failed [%d][%s].\n",
40 : ret, strerror(ret));
41 0 : return ret;
42 : }
43 :
44 0 : sss_packet_set_error(cctx->creq->out, cmd_ret);
45 :
46 0 : sss_cmd_done(cctx, NULL);
47 :
48 0 : return EOK;
49 : }
50 :
51 : struct pac_req_ctx {
52 : struct cli_ctx *cctx;
53 : struct pac_ctx *pac_ctx;
54 : const char *domain_name;
55 : const char *user_name;
56 : struct sss_domain_info *dom;
57 :
58 : struct PAC_LOGON_INFO *logon_info;
59 : struct dom_sid2 *domain_sid;
60 :
61 : size_t del_grp_count;
62 : struct grp_info *del_grp_list;
63 :
64 : size_t add_sid_count;
65 : char **add_sids;
66 :
67 : hash_table_t *sid_table;
68 : char *user_sid_str;
69 : char *user_dom_sid_str;
70 : char *primary_group_sid_str;
71 : };
72 :
73 : static errno_t pac_resolve_sids_next(struct pac_req_ctx *pr_ctx);
74 : static void pac_lookup_sids_done(struct tevent_req *req);
75 : static struct tevent_req *pac_lookup_sids_send(TALLOC_CTX *mem_ctx,
76 : struct tevent_context *ev,
77 : struct pac_req_ctx *pr_ctx,
78 : struct pac_ctx *pac_ctx,
79 : hash_table_t *sid_table);
80 : static errno_t pac_lookup_sids_recv(struct tevent_req *req);
81 : static void pac_add_user_next(struct pac_req_ctx *pr_ctx);
82 : static void pac_get_domains_done(struct tevent_req *req);
83 : static errno_t pac_user_get_grp_info(TALLOC_CTX *mem_ctx,
84 : struct pac_req_ctx *pr_ctx,
85 : size_t *_del_grp_count,
86 : struct grp_info **_del_grp_list,
87 : size_t *_add_sid_count,
88 : char ***_add_sids);
89 : static errno_t save_pac_user(struct pac_req_ctx *pr_ctx);
90 : static void pac_get_group_done(struct tevent_req *subreq);
91 : static errno_t pac_save_memberships_next(struct tevent_req *req);
92 : static errno_t pac_store_membership(struct pac_req_ctx *pr_ctx,
93 : struct ldb_dn *user_dn,
94 : const char *grp_sid_str,
95 : struct sss_domain_info *grp_dom);
96 : struct tevent_req *pac_save_memberships_send(struct pac_req_ctx *pr_ctx);
97 : static void pac_save_memberships_done(struct tevent_req *req);
98 :
99 :
100 0 : static errno_t pac_add_pac_user(struct cli_ctx *cctx)
101 : {
102 : int ret;
103 : uint8_t *body;
104 : size_t blen;
105 : struct pac_req_ctx *pr_ctx;
106 : struct tevent_req *req;
107 : enum idmap_error_code err;
108 :
109 0 : sss_packet_get_body(cctx->creq->in, &body, &blen);
110 :
111 0 : pr_ctx = talloc_zero(cctx, struct pac_req_ctx);
112 0 : if (pr_ctx == NULL) {
113 0 : DEBUG(SSSDBG_OP_FAILURE, "talloc_new failed.\n");
114 0 : return ENOMEM;
115 : }
116 :
117 0 : pr_ctx->cctx = cctx;
118 :
119 0 : pr_ctx->pac_ctx = talloc_get_type(cctx->rctx->pvt_ctx, struct pac_ctx);
120 0 : if (pr_ctx->pac_ctx == NULL) {
121 0 : DEBUG(SSSDBG_FATAL_FAILURE, "Cannot find pac responder context.\n");
122 0 : return EINVAL;
123 : }
124 :
125 0 : ret = get_data_from_pac(pr_ctx, body, blen,
126 : &pr_ctx->logon_info);
127 0 : if (ret != EOK) {
128 0 : DEBUG(SSSDBG_OP_FAILURE, "get_data_from_pac failed.\n");
129 0 : goto done;
130 : }
131 :
132 0 : pr_ctx->domain_name = pr_ctx->logon_info->info3.base.logon_domain.string;
133 0 : if (pr_ctx->domain_name == NULL) {
134 0 : DEBUG(SSSDBG_FATAL_FAILURE, "No domain name in PAC\n");
135 0 : ret = EINVAL;
136 0 : goto done;
137 : }
138 :
139 0 : if (pr_ctx->logon_info->info3.base.account_name.string == NULL) {
140 0 : ret = EINVAL;
141 0 : DEBUG(SSSDBG_FATAL_FAILURE, "Missing account name in PAC.\n");
142 0 : goto done;
143 : }
144 :
145 : /* To be compatible with winbind based lookups we have to use lower case
146 : * names only, effectively making the domain case-insenvitive. */
147 0 : pr_ctx->user_name = sss_tc_utf8_str_tolower(pr_ctx,
148 0 : pr_ctx->logon_info->info3.base.account_name.string);
149 0 : if (pr_ctx->user_name == NULL) {
150 0 : ret = ENOMEM;
151 0 : DEBUG(SSSDBG_FATAL_FAILURE, "sss_tc_utf8_str_tolower failed.\n");
152 0 : goto done;
153 : }
154 :
155 0 : err = sss_idmap_smb_sid_to_sid(pr_ctx->pac_ctx->idmap_ctx,
156 0 : pr_ctx->logon_info->info3.base.domain_sid,
157 : &pr_ctx->user_dom_sid_str);
158 0 : if (err != IDMAP_SUCCESS) {
159 0 : DEBUG(SSSDBG_OP_FAILURE, "sss_idmap_smb_sid_to_sid failed.\n");
160 0 : ret = EFAULT;
161 0 : goto done;
162 : }
163 :
164 0 : talloc_steal(pr_ctx, pr_ctx->user_dom_sid_str);
165 :
166 0 : ret = responder_get_domain_by_id(cctx->rctx, pr_ctx->user_dom_sid_str,
167 : &pr_ctx->dom);
168 0 : if (ret == EAGAIN || ret == ENOENT) {
169 0 : req = sss_dp_get_domains_send(cctx->rctx, cctx->rctx, true,
170 : pr_ctx->domain_name);
171 0 : if (req == NULL) {
172 0 : ret = ENOMEM;
173 : } else {
174 0 : tevent_req_set_callback(req, pac_get_domains_done, pr_ctx);
175 0 : ret = EAGAIN;
176 : }
177 0 : goto done;
178 0 : } else if (ret != EOK) {
179 0 : DEBUG(SSSDBG_OP_FAILURE, "responder_get_domain_by_id failed.\n");
180 0 : goto done;
181 : }
182 :
183 0 : ret = pac_resolve_sids_next(pr_ctx);
184 :
185 : done:
186 0 : if (ret != EAGAIN) {
187 0 : talloc_free(pr_ctx);
188 : }
189 0 : return pac_cmd_done(cctx, ret);
190 : }
191 :
192 0 : static void pac_get_domains_done(struct tevent_req *req)
193 : {
194 0 : struct pac_req_ctx *pr_ctx = tevent_req_callback_data(req,
195 : struct pac_req_ctx);
196 0 : struct cli_ctx *cctx = pr_ctx->cctx;
197 : int ret;
198 :
199 0 : ret = sss_dp_get_domains_recv(req);
200 0 : talloc_free(req);
201 0 : if (ret != EOK) {
202 0 : goto done;
203 : }
204 :
205 0 : ret = responder_get_domain_by_id(cctx->rctx, pr_ctx->user_dom_sid_str,
206 : &pr_ctx->dom);
207 0 : if (ret != EOK) {
208 0 : DEBUG(SSSDBG_OP_FAILURE, "Corresponding domain [%s] has not been "
209 : "found\n", pr_ctx->user_dom_sid_str);
210 0 : ret = ENOENT;
211 0 : goto done;
212 : }
213 :
214 0 : ret = pac_resolve_sids_next(pr_ctx);
215 :
216 : done:
217 0 : if (ret != EAGAIN) {
218 0 : talloc_free(pr_ctx);
219 : }
220 0 : pac_cmd_done(cctx, ret);
221 0 : }
222 :
223 0 : static errno_t pac_resolve_sids_next(struct pac_req_ctx *pr_ctx)
224 : {
225 : int ret;
226 : struct tevent_req *req;
227 :
228 0 : ret = get_sids_from_pac(pr_ctx, pr_ctx->pac_ctx, pr_ctx->logon_info,
229 : &pr_ctx->user_sid_str,
230 : &pr_ctx->primary_group_sid_str,
231 : &pr_ctx->sid_table);
232 0 : if (ret != 0) {
233 0 : DEBUG(SSSDBG_OP_FAILURE, "get_sids_from_pac failed.\n");
234 0 : return ret;
235 : }
236 :
237 0 : req = pac_lookup_sids_send(pr_ctx, pr_ctx->cctx->ev, pr_ctx,
238 : pr_ctx->pac_ctx, pr_ctx->sid_table);
239 0 : if (req == NULL) {
240 0 : DEBUG(SSSDBG_OP_FAILURE, "pac_lookup_sids_send failed.\n");
241 0 : return ENOMEM;
242 : }
243 :
244 0 : tevent_req_set_callback(req, pac_lookup_sids_done, pr_ctx);
245 :
246 0 : ret = EAGAIN;
247 :
248 :
249 0 : return ret;
250 : }
251 :
252 0 : static void pac_lookup_sids_done(struct tevent_req *req)
253 : {
254 0 : struct pac_req_ctx *pr_ctx = tevent_req_callback_data(req, struct pac_req_ctx);
255 0 : struct cli_ctx *cctx = pr_ctx->cctx;
256 : errno_t ret;
257 : unsigned long count;
258 : hash_entry_t *entries;
259 : hash_key_t key;
260 : hash_value_t value;
261 : size_t c;
262 : struct sss_domain_info *dom;
263 : uint64_t id;
264 : struct ldb_result *msg;
265 :
266 0 : ret = pac_lookup_sids_recv(req);
267 0 : talloc_zfree(req);
268 :
269 0 : if (ret != EOK) {
270 0 : talloc_free(pr_ctx);
271 0 : pac_cmd_done(cctx, ret);
272 0 : return;
273 : }
274 :
275 0 : key.type = HASH_KEY_STRING;
276 0 : value.type = HASH_VALUE_ULONG;
277 :
278 0 : ret = hash_entries(pr_ctx->sid_table, &count, &entries);
279 0 : if (ret != HASH_SUCCESS) {
280 0 : DEBUG(SSSDBG_OP_FAILURE, "hash_entries failed [%d][%s].\n",
281 : ret, hash_error_string(ret));
282 0 : talloc_free(pr_ctx);
283 0 : pac_cmd_done(cctx, ret);
284 0 : return;
285 : }
286 :
287 0 : for (c = 0; c < count; c++) {
288 0 : if (entries[c].value.ul == 0) {
289 0 : ret =responder_get_domain_by_id(cctx->rctx,
290 0 : entries[c].key.str, &dom);
291 0 : if (ret != EOK) {
292 0 : DEBUG(SSSDBG_OP_FAILURE, "No domain found for SID [%s].\n",
293 : entries[c].key.str);
294 0 : continue;
295 : }
296 :
297 0 : msg = NULL;
298 0 : ret = sysdb_search_object_by_sid(pr_ctx, dom, entries[c].key.str,
299 : NULL, &msg);
300 0 : if (ret == ENOENT) {
301 0 : DEBUG(SSSDBG_OP_FAILURE, "No entry found for SID [%s].\n",
302 : entries[c].key.str);
303 0 : continue;
304 0 : } else if (ret != EOK) {
305 0 : DEBUG(SSSDBG_OP_FAILURE,
306 : "sysdb_search_object_by_sid failed.\n");
307 0 : continue;
308 : }
309 :
310 0 : if (msg->count > 1) {
311 0 : DEBUG(SSSDBG_CRIT_FAILURE, "More then one result returned " \
312 : "for SID [%s].\n",
313 : entries[c].key.str);
314 0 : talloc_free(msg);
315 0 : pac_cmd_done(cctx, EINVAL);
316 0 : return;
317 : }
318 :
319 0 : id = ldb_msg_find_attr_as_uint64(msg->msgs[0],
320 : SYSDB_UIDNUM, 0);
321 0 : if (id == 0) {
322 0 : id = ldb_msg_find_attr_as_uint64(msg->msgs[0],
323 : SYSDB_GIDNUM, 0);
324 : }
325 :
326 0 : if (id == 0) {
327 0 : DEBUG(SSSDBG_OP_FAILURE, "No ID found in entry.\n");
328 0 : talloc_free(msg);
329 0 : continue;
330 : }
331 :
332 0 : key.str = entries[c].key.str;
333 0 : value.ul = id;
334 :
335 0 : ret = hash_enter(pr_ctx->sid_table, &key, &value);
336 0 : if (ret != HASH_SUCCESS) {
337 0 : DEBUG(SSSDBG_OP_FAILURE, "hash_enter failed [%d][%s].\n",
338 : ret, hash_error_string(ret));
339 0 : continue;
340 : }
341 0 : talloc_free(msg);
342 : }
343 : }
344 :
345 0 : pac_add_user_next(pr_ctx);
346 : }
347 :
348 0 : static void pac_add_user_next(struct pac_req_ctx *pr_ctx)
349 : {
350 : int ret;
351 : struct tevent_req *req;
352 0 : struct cli_ctx *cctx = pr_ctx->cctx;
353 :
354 0 : ret = save_pac_user(pr_ctx);
355 0 : if (ret != EOK) {
356 0 : DEBUG(SSSDBG_OP_FAILURE, "save_pac_user failed.\n");
357 0 : goto done;
358 : }
359 :
360 0 : ret = pac_user_get_grp_info(pr_ctx, pr_ctx, &pr_ctx->del_grp_count,
361 : &pr_ctx->del_grp_list,
362 : &pr_ctx->add_sid_count, &pr_ctx->add_sids);
363 0 : if (ret != EOK) {
364 0 : DEBUG(SSSDBG_OP_FAILURE, "pac_user_get_grp_info failed.\n");
365 0 : goto done;
366 : }
367 :
368 0 : req = pac_save_memberships_send(pr_ctx);
369 0 : if (req == NULL) {
370 0 : ret = ENOMEM;
371 0 : goto done;
372 : }
373 :
374 0 : tevent_req_set_callback(req, pac_save_memberships_done, pr_ctx);
375 :
376 0 : ret = EAGAIN;
377 :
378 : done:
379 0 : if (ret != EAGAIN) {
380 0 : talloc_free(pr_ctx);
381 : }
382 0 : pac_cmd_done(cctx, ret);
383 0 : }
384 :
385 0 : static errno_t pac_user_get_grp_info(TALLOC_CTX *mem_ctx,
386 : struct pac_req_ctx *pr_ctx,
387 : size_t *_del_grp_count,
388 : struct grp_info **_del_grp_list,
389 : size_t *_add_sid_count,
390 : char ***_add_sids)
391 : {
392 : struct sysdb_ctx *sysdb;
393 : int ret;
394 0 : TALLOC_CTX *tmp_ctx = NULL;
395 0 : struct ldb_result *res = NULL;
396 : size_t c;
397 : const char *tmp_str;
398 :
399 0 : size_t add_sid_count = 0;
400 0 : char **add_sids = NULL;
401 :
402 : size_t del_idx;
403 0 : size_t del_grp_count = 0;
404 0 : struct grp_info *del_grp_list = NULL;
405 :
406 : const char *cur_sid;
407 : hash_key_t key;
408 : hash_value_t value;
409 :
410 0 : struct hash_iter_context_t *iter = NULL;
411 : hash_entry_t *entry;
412 :
413 0 : sysdb = pr_ctx->dom->sysdb;
414 0 : if (sysdb == NULL) {
415 0 : ret = EINVAL;
416 0 : DEBUG(SSSDBG_FATAL_FAILURE, "Fatal: Sysdb CTX not found for this domain!\n");
417 0 : goto done;
418 : }
419 :
420 0 : tmp_ctx = talloc_new(NULL);
421 0 : if (tmp_ctx == NULL) {
422 0 : ret = ENOMEM;
423 0 : DEBUG(SSSDBG_OP_FAILURE, "talloc_new failed.\n");
424 0 : goto done;
425 : }
426 :
427 0 : ret = sysdb_initgroups(tmp_ctx, pr_ctx->dom, pr_ctx->user_name, &res);
428 0 : if (ret != EOK) {
429 0 : DEBUG(SSSDBG_OP_FAILURE, "sysdb_initgroups failed.\n");
430 0 : goto done;
431 : }
432 :
433 0 : if (res->count == 0) {
434 0 : DEBUG(SSSDBG_OP_FAILURE, "sysdb_initgroups did not found [%s].\n",
435 : pr_ctx->user_name);
436 0 : ret = ENOENT;
437 0 : goto done;
438 : }
439 :
440 : /* First result is the user entry then the groups follow */
441 0 : if (res->count > 1) {
442 0 : del_grp_count = res->count - 1;
443 0 : del_grp_list = talloc_zero_array(tmp_ctx, struct grp_info,
444 : del_grp_count);
445 0 : if (del_grp_list == NULL) {
446 0 : DEBUG(SSSDBG_OP_FAILURE, "talloc_array failed.\n");
447 0 : ret = ENOMEM;
448 0 : goto done;
449 : }
450 0 : del_idx = 0;
451 :
452 0 : key.type = HASH_KEY_STRING;
453 :
454 0 : for (c = 0; c < (res->count - 1); c++) {
455 0 : cur_sid = ldb_msg_find_attr_as_string(res->msgs[c + 1],
456 : SYSDB_SID_STR, NULL);
457 0 : if (cur_sid == NULL) {
458 0 : DEBUG(SSSDBG_OP_FAILURE, "Missing SID in group entry.\n");
459 0 : ret = EINVAL;
460 0 : goto done;
461 : }
462 :
463 0 : key.str = discard_const(cur_sid);
464 0 : ret = hash_lookup(pr_ctx->sid_table, &key, &value);
465 0 : if (ret == HASH_SUCCESS) {
466 0 : DEBUG(SSSDBG_TRACE_ALL, "User [%s] already member of group " \
467 : "with SID [%s].\n",
468 : pr_ctx->user_name, cur_sid);
469 :
470 0 : ret = hash_delete(pr_ctx->sid_table, &key);
471 0 : if (ret != HASH_SUCCESS) {
472 0 : DEBUG(SSSDBG_OP_FAILURE, "Failed to remove hash entry.\n");
473 0 : ret = EIO;
474 0 : goto done;
475 : }
476 0 : } else if (ret == HASH_ERROR_KEY_NOT_FOUND) {
477 0 : DEBUG(SSSDBG_TRACE_INTERNAL, "Group with SID [%s] is not in " \
478 : "the PAC anymore, membership " \
479 : "must be removed.\n", cur_sid);
480 :
481 0 : tmp_str = ldb_msg_find_attr_as_string(res->msgs[c + 1],
482 : SYSDB_ORIG_DN, NULL);
483 0 : if (tmp_str != NULL) {
484 0 : del_grp_list[del_idx].orig_dn = talloc_strdup(del_grp_list,
485 : tmp_str);
486 0 : if (del_grp_list[del_idx].orig_dn == NULL) {
487 0 : DEBUG(SSSDBG_OP_FAILURE, "talloc_strdup failed.\n");
488 0 : ret = ENOMEM;
489 0 : goto done;
490 : }
491 : }
492 :
493 0 : del_grp_list[del_idx].dn = ldb_dn_copy(del_grp_list,
494 0 : res->msgs[c + 1]->dn);
495 0 : if (del_grp_list[del_idx].dn == NULL) {
496 0 : DEBUG(SSSDBG_OP_FAILURE, "ldb_dn_copy failed.\n");
497 0 : ret = ENOMEM;
498 0 : goto done;
499 : }
500 :
501 0 : del_idx++;
502 : }
503 : }
504 0 : del_grp_count = del_idx;
505 : }
506 :
507 0 : add_sid_count = hash_count(pr_ctx->sid_table);
508 0 : if (add_sid_count > 0) {
509 0 : add_sids = talloc_array(tmp_ctx, char *, add_sid_count);
510 0 : if (add_sids == NULL) {
511 0 : DEBUG(SSSDBG_OP_FAILURE, "talloc_array failed.\n");
512 0 : ret = ENOMEM;
513 0 : goto done;
514 : }
515 :
516 0 : iter = new_hash_iter_context(pr_ctx->sid_table);
517 0 : c = 0;
518 0 : while ((entry = iter->next(iter)) != NULL) {
519 0 : if (strcmp(entry->key.str, pr_ctx->user_sid_str) != 0) {
520 0 : add_sids[c] = talloc_strdup(add_sids, entry->key.str);
521 0 : if (add_sids[c] == NULL) {
522 0 : DEBUG(SSSDBG_OP_FAILURE, "talloc_strdup failed.\n");
523 0 : ret = ENOMEM;
524 0 : goto done;
525 : }
526 0 : DEBUG(SSSDBG_TRACE_ALL, "SID [%s] added to add_sids " \
527 : "list.\n", entry->key.str);
528 0 : c++;
529 : }
530 : }
531 0 : add_sid_count = c;
532 : }
533 :
534 :
535 0 : *_del_grp_count = del_grp_count;
536 0 : *_del_grp_list = talloc_steal(mem_ctx, del_grp_list);
537 0 : *_add_sid_count = add_sid_count;
538 0 : *_add_sids = talloc_steal(mem_ctx, add_sids);
539 :
540 0 : ret = EOK;
541 :
542 : done:
543 0 : talloc_free(iter);
544 0 : talloc_free(tmp_ctx);
545 :
546 0 : return ret;
547 : }
548 :
549 0 : static errno_t save_pac_user(struct pac_req_ctx *pr_ctx)
550 : {
551 : struct sysdb_ctx *sysdb;
552 : int ret;
553 0 : const char *attrs[] = {SYSDB_NAME, SYSDB_NAME_ALIAS, SYSDB_UIDNUM,
554 : SYSDB_GIDNUM, SYSDB_GECOS, SYSDB_HOMEDIR,
555 : SYSDB_SHELL, SYSDB_ORIG_DN, SYSDB_CACHEDPWD, NULL};
556 : struct ldb_message *msg;
557 0 : struct passwd *pwd = NULL;
558 0 : TALLOC_CTX *tmp_ctx = NULL;
559 0 : struct sysdb_attrs *user_attrs = NULL;
560 :
561 0 : sysdb = pr_ctx->dom->sysdb;
562 0 : if (sysdb == NULL) {
563 0 : ret = EINVAL;
564 0 : DEBUG(SSSDBG_FATAL_FAILURE, "Fatal: Sysdb CTX not found for this domain!\n");
565 0 : goto done;
566 : }
567 :
568 0 : tmp_ctx = talloc_new(NULL);
569 0 : if (tmp_ctx == NULL) {
570 0 : ret = ENOMEM;
571 0 : DEBUG(SSSDBG_OP_FAILURE, "talloc_new failed.\n");
572 0 : goto done;
573 : }
574 :
575 0 : ret = get_pwd_from_pac(tmp_ctx, pr_ctx->dom, pr_ctx->user_sid_str,
576 : pr_ctx->primary_group_sid_str, pr_ctx->sid_table,
577 : pr_ctx->logon_info, &pwd, &user_attrs);
578 0 : if (ret != EOK) {
579 0 : DEBUG(SSSDBG_OP_FAILURE, "get_pwd_from_pac failed.\n");
580 0 : goto done;
581 : }
582 :
583 0 : ret = sysdb_search_user_by_uid(tmp_ctx, pr_ctx->dom, pwd->pw_uid, attrs,
584 : &msg);
585 0 : if (ret == ENOENT) {
586 0 : if (pwd->pw_gid == 0 && !pr_ctx->dom->mpg) {
587 0 : DEBUG(SSSDBG_CRIT_FAILURE, "Primary group RID from the PAC "
588 : "cannot be translated into a GID for "
589 : "user [%s]. Typically this happens "
590 : "when UIDs and GIDs are read from AD "
591 : "and the primary AD group does not "
592 : "have a GID assigned. Make sure the "
593 : "user is created by the ID provider "
594 : "before GSSAPI based authentication "
595 : "is used in this case.\n",
596 : pwd->pw_name);
597 0 : ret = EINVAL;
598 0 : goto done;
599 : }
600 :
601 0 : ret = sysdb_store_user(pr_ctx->dom, pwd->pw_name, NULL,
602 0 : pwd->pw_uid, pwd->pw_gid, pwd->pw_gecos,
603 0 : pwd->pw_dir,
604 0 : pwd->pw_shell, NULL, user_attrs, NULL,
605 0 : pr_ctx->dom->user_timeout, 0);
606 0 : if (ret != EOK) {
607 0 : DEBUG(SSSDBG_OP_FAILURE, "sysdb_store_user failed [%d][%s].\n",
608 : ret, strerror(ret));
609 0 : goto done;
610 : }
611 0 : } else if (ret != EOK && ret != ENOENT) {
612 0 : DEBUG(SSSDBG_OP_FAILURE, "sysdb_search_user_by_id failed.\n");
613 0 : goto done;
614 : }
615 :
616 0 : ret = EOK;
617 :
618 : done:
619 0 : talloc_free(tmp_ctx);
620 0 : return ret;
621 : }
622 :
623 : struct pac_save_memberships_state {
624 : size_t sid_iter;
625 : struct ldb_dn *user_dn;
626 :
627 : struct pac_req_ctx *pr_ctx;
628 : };
629 :
630 : static errno_t
631 : pac_save_memberships_delete(struct pac_save_memberships_state *state);
632 :
633 0 : struct tevent_req *pac_save_memberships_send(struct pac_req_ctx *pr_ctx)
634 : {
635 : struct pac_save_memberships_state *state;
636 0 : struct sss_domain_info *dom = pr_ctx->dom;
637 : struct tevent_req *req;
638 : errno_t ret;
639 0 : char *dom_name = NULL;
640 : struct ldb_message *msg;
641 :
642 0 : req = tevent_req_create(pr_ctx, &state, struct pac_save_memberships_state);
643 0 : if (req == NULL) {
644 0 : return NULL;
645 : }
646 :
647 0 : state->sid_iter = 0;
648 :
649 0 : dom_name = sss_get_domain_name(state, pr_ctx->user_name, dom);
650 0 : if (dom_name == NULL) {
651 0 : DEBUG(SSSDBG_OP_FAILURE, "talloc_sprintf failed.\n");
652 0 : ret = ENOMEM;
653 0 : goto done;
654 : }
655 :
656 0 : ret = sysdb_search_user_by_name(state, dom, dom_name, NULL, &msg);
657 0 : if (ret != EOK) {
658 0 : DEBUG(SSSDBG_OP_FAILURE, "sysdb_search_user_by_name failed " \
659 : "[%d][%s].\n", ret, strerror(ret));
660 0 : goto done;
661 : }
662 :
663 0 : state->user_dn = msg->dn;
664 0 : state->pr_ctx = pr_ctx;
665 :
666 0 : ret = pac_save_memberships_delete(state);
667 0 : if (ret != EOK) {
668 0 : DEBUG(SSSDBG_OP_FAILURE, "pac_save_memberships_delete failed.\n");
669 0 : goto done;
670 : }
671 :
672 0 : ret = pac_save_memberships_next(req);
673 0 : if (ret == EOK) {
674 0 : tevent_req_done(req);
675 0 : tevent_req_post(req, pr_ctx->cctx->ev);
676 : }
677 :
678 : done:
679 0 : talloc_free(dom_name);
680 0 : if (ret != EOK && ret != EAGAIN) {
681 0 : tevent_req_error(req, ret);
682 0 : tevent_req_post(req, pr_ctx->cctx->ev);
683 : }
684 :
685 0 : return req;
686 : }
687 :
688 : static errno_t
689 0 : pac_save_memberships_delete(struct pac_save_memberships_state *state)
690 : {
691 : int ret;
692 : int sret;
693 : size_t c;
694 : struct pac_req_ctx *pr_ctx;
695 0 : bool in_transaction = false;
696 : TALLOC_CTX *tmp_ctx;
697 0 : struct sysdb_attrs *user_attrs = NULL;
698 :
699 0 : pr_ctx = state->pr_ctx;
700 :
701 0 : if (pr_ctx->del_grp_count == 0) {
702 0 : return EOK;
703 : }
704 :
705 0 : if (pr_ctx->del_grp_list == NULL) {
706 0 : DEBUG(SSSDBG_OP_FAILURE, "Missing group list.\n");
707 0 : return EINVAL;
708 : }
709 :
710 0 : tmp_ctx = talloc_new(NULL);
711 0 : if (tmp_ctx == NULL) {
712 0 : DEBUG(SSSDBG_OP_FAILURE, "talloc_new failed.\n");
713 0 : return ENOMEM;
714 : }
715 :
716 0 : ret = sysdb_transaction_start(pr_ctx->dom->sysdb);
717 0 : if (ret != EOK) {
718 0 : DEBUG(SSSDBG_OP_FAILURE, "sysdb_transaction_start failed.\n");
719 0 : goto done;
720 : }
721 0 : in_transaction = true;
722 :
723 0 : for (c = 0; c < pr_ctx->del_grp_count; c++) {
724 : /* If there is a failure for one group we still try to remove the
725 : * remaining groups. */
726 0 : ret = sysdb_mod_group_member(pr_ctx->dom, state->user_dn,
727 0 : pr_ctx->del_grp_list[c].dn,
728 : LDB_FLAG_MOD_DELETE);
729 0 : if (ret != EOK) {
730 0 : DEBUG(SSSDBG_OP_FAILURE, "sysdb_mod_group_member failed for " \
731 : "user [%s] and group[%s].\n",
732 : ldb_dn_get_linearized(state->user_dn),
733 : ldb_dn_get_linearized(
734 : pr_ctx->del_grp_list[c].dn));
735 0 : continue;
736 : }
737 :
738 0 : if (pr_ctx->del_grp_list[c].orig_dn != NULL) {
739 0 : user_attrs = sysdb_new_attrs(tmp_ctx);
740 0 : if (user_attrs == NULL) {
741 0 : DEBUG(SSSDBG_OP_FAILURE, "sysdb_new_attrs failed.\n");
742 0 : continue;
743 : }
744 :
745 0 : ret = sysdb_attrs_add_string(user_attrs, SYSDB_ORIG_MEMBEROF,
746 0 : pr_ctx->del_grp_list[c].orig_dn);
747 0 : if (ret != EOK) {
748 0 : DEBUG(SSSDBG_OP_FAILURE, "sysdb_attrs_add_string failed.\n");
749 0 : continue;
750 : }
751 :
752 0 : ret = sysdb_set_entry_attr(pr_ctx->dom->sysdb, state->user_dn, user_attrs,
753 : LDB_FLAG_MOD_DELETE);
754 0 : if (ret != EOK) {
755 0 : DEBUG(SSSDBG_OP_FAILURE, "sysdb_set_entry_attr failed.\n");
756 0 : continue;
757 : }
758 0 : talloc_free(user_attrs);
759 : }
760 : }
761 :
762 0 : ret = sysdb_transaction_commit(pr_ctx->dom->sysdb);
763 0 : if (ret != EOK) {
764 0 : DEBUG(SSSDBG_OP_FAILURE, "sysdb_transaction_commit failed.\n");
765 0 : goto done;
766 : }
767 0 : in_transaction = false;
768 :
769 0 : ret = EOK;
770 : done:
771 0 : if (in_transaction) {
772 0 : sret = sysdb_transaction_cancel(pr_ctx->dom->sysdb);
773 0 : if (sret != EOK) {
774 0 : DEBUG(SSSDBG_OP_FAILURE, "sysdb_transaction_cancel failed.\n");
775 : }
776 : }
777 :
778 0 : talloc_free(tmp_ctx);
779 :
780 0 : return ret;
781 : }
782 :
783 0 : static errno_t pac_save_memberships_next(struct tevent_req *req)
784 : {
785 : errno_t ret;
786 : char *sid;
787 : struct sss_domain_info *grp_dom;
788 : struct tevent_req *subreq;
789 : struct pac_save_memberships_state *state;
790 : struct pac_req_ctx *pr_ctx;
791 :
792 0 : state = tevent_req_data(req, struct pac_save_memberships_state);
793 0 : pr_ctx = state->pr_ctx;
794 :
795 0 : if (pr_ctx->add_sid_count == 0) {
796 0 : return EOK;
797 : }
798 :
799 0 : if (pr_ctx->add_sids == NULL) {
800 0 : DEBUG(SSSDBG_OP_FAILURE, "Missing list of SIDs.\n");
801 0 : return EINVAL;
802 : }
803 :
804 0 : while (state->sid_iter < pr_ctx->add_sid_count) {
805 0 : sid = pr_ctx->add_sids[state->sid_iter];
806 0 : ret = responder_get_domain_by_id(pr_ctx->pac_ctx->rctx, sid, &grp_dom);
807 0 : if (ret != EOK) {
808 0 : DEBUG(SSSDBG_MINOR_FAILURE, "responder_get_domain_by_id failed, " \
809 : "will try next group\n");
810 0 : state->sid_iter++;
811 0 : continue;
812 : }
813 :
814 0 : ret = pac_store_membership(state->pr_ctx, state->user_dn, sid, grp_dom);
815 0 : if (ret == EOK) {
816 0 : state->sid_iter++;
817 0 : continue;
818 0 : } else if (ret == ENOENT) {
819 0 : subreq = sss_dp_get_account_send(state, pr_ctx->cctx->rctx,
820 : grp_dom, true,
821 : SSS_DP_SECID, sid, 0, NULL);
822 0 : if (subreq == NULL) {
823 0 : ret = ENOMEM;
824 0 : goto done;
825 : }
826 0 : tevent_req_set_callback(subreq, pac_get_group_done, req);
827 :
828 0 : return EAGAIN;
829 : } else {
830 0 : DEBUG(SSSDBG_OP_FAILURE, "pac_store_membership failed, "
831 : "trying next group.\n");
832 0 : state->sid_iter++;
833 0 : continue;
834 : }
835 : }
836 :
837 0 : ret = EOK;
838 : done:
839 0 : return ret;
840 : }
841 :
842 0 : static void pac_get_group_done(struct tevent_req *subreq)
843 : {
844 : struct tevent_req *req;
845 : struct pac_save_memberships_state *state;
846 :
847 0 : req = tevent_req_callback_data(subreq, struct tevent_req);
848 0 : state = tevent_req_data(req, struct pac_save_memberships_state);
849 :
850 : errno_t ret;
851 : dbus_uint16_t err_maj;
852 : dbus_uint32_t err_min;
853 : char *err_msg;
854 : char *sid;
855 : struct sss_domain_info *grp_dom;
856 0 : struct pac_req_ctx *pr_ctx = state->pr_ctx;
857 :
858 0 : ret = sss_dp_get_account_recv(req, subreq,
859 : &err_maj, &err_min,
860 : &err_msg);
861 0 : talloc_zfree(subreq);
862 0 : talloc_zfree(err_msg);
863 0 : if (ret != EOK) {
864 0 : goto error;
865 : }
866 :
867 0 : sid = pr_ctx->add_sids[state->sid_iter];
868 0 : ret = responder_get_domain_by_id(pr_ctx->pac_ctx->rctx,sid, &grp_dom);
869 0 : if (ret != EOK) {
870 0 : DEBUG(SSSDBG_OP_FAILURE, "responder_get_domain_by_id failed.\n");
871 0 : goto error;
872 : }
873 :
874 0 : ret = pac_store_membership(state->pr_ctx, state->user_dn, sid, grp_dom);
875 0 : if (ret != EOK) {
876 0 : DEBUG(SSSDBG_OP_FAILURE, "pac_store_membership failed, "
877 : "trying next group.\n");
878 : }
879 0 : state->sid_iter++;
880 :
881 0 : ret = pac_save_memberships_next(req);
882 0 : if (ret == EOK) {
883 0 : tevent_req_done(req);
884 0 : } else if (ret != EAGAIN) {
885 0 : goto error;
886 : }
887 :
888 0 : return;
889 :
890 : error:
891 0 : tevent_req_error(req, ret);
892 : }
893 :
894 : static errno_t
895 0 : pac_store_membership(struct pac_req_ctx *pr_ctx,
896 : struct ldb_dn *user_dn,
897 : const char *grp_sid_str,
898 : struct sss_domain_info *grp_dom)
899 : {
900 : TALLOC_CTX *tmp_ctx;
901 : struct sysdb_attrs *user_attrs;
902 : struct ldb_result *group;
903 : errno_t ret;
904 : const char *orig_group_dn;
905 0 : const char *group_attrs[] = { SYSDB_ORIG_DN, SYSDB_OBJECTCLASS, NULL };
906 : const char *oc;
907 :
908 0 : tmp_ctx = talloc_new(NULL);
909 0 : if (tmp_ctx == NULL) {
910 0 : return ENOMEM;
911 : }
912 :
913 0 : ret = sysdb_search_object_by_sid(tmp_ctx, grp_dom, grp_sid_str,
914 : group_attrs, &group);
915 0 : if (ret == ENOENT) {
916 0 : DEBUG(SSSDBG_OP_FAILURE, "Unexpected number of groups returned.\n");
917 0 : goto done;
918 0 : } else if (ret != EOK) {
919 0 : DEBUG(SSSDBG_TRACE_INTERNAL,
920 : "sysdb_search_object_by_sid for SID [%s] failed [%d][%s].\n",
921 : grp_sid_str, ret, strerror(ret));
922 0 : goto done;
923 : }
924 :
925 0 : if (group->count != 1) {
926 0 : DEBUG(SSSDBG_OP_FAILURE, "Unexpected number of groups returned.\n");
927 0 : ret = EINVAL;
928 0 : goto done;
929 : }
930 :
931 0 : oc = ldb_msg_find_attr_as_string(group->msgs[0], SYSDB_OBJECTCLASS, NULL);
932 0 : if (oc == NULL || strcmp(oc, SYSDB_GROUP_CLASS) != 0) {
933 0 : DEBUG(SSSDBG_OP_FAILURE, "Return object does not have group " \
934 : "objectclass.\n");
935 0 : ret = EINVAL;
936 0 : goto done;
937 : }
938 :
939 0 : DEBUG(SSSDBG_TRACE_ALL, "Adding user [%s] to group [%s][%s].\n",
940 : ldb_dn_get_linearized(user_dn), grp_sid_str,
941 : ldb_dn_get_linearized(group->msgs[0]->dn));
942 0 : ret = sysdb_mod_group_member(grp_dom, user_dn, group->msgs[0]->dn,
943 : LDB_FLAG_MOD_ADD);
944 0 : if (ret != EOK) {
945 0 : DEBUG(SSSDBG_OP_FAILURE, "sysdb_mod_group_member failed user [%s] " \
946 : "group [%s].\n",
947 : ldb_dn_get_linearized(user_dn),
948 : ldb_dn_get_linearized(group->msgs[0]->dn));
949 0 : goto done;
950 : }
951 :
952 0 : orig_group_dn = ldb_msg_find_attr_as_string(group->msgs[0], SYSDB_ORIG_DN,
953 : NULL);
954 0 : if (orig_group_dn != NULL) {
955 0 : DEBUG(SSSDBG_TRACE_ALL, "Adding original group DN [%s] to user [%s].\n",
956 : orig_group_dn,
957 : ldb_dn_get_linearized(user_dn));
958 0 : user_attrs = sysdb_new_attrs(tmp_ctx);
959 0 : if (user_attrs == NULL) {
960 0 : DEBUG(SSSDBG_OP_FAILURE, "sysdb_new_attrs failed.\n");
961 0 : ret = ENOMEM;
962 0 : goto done;
963 : }
964 :
965 0 : ret = sysdb_attrs_add_string(user_attrs, SYSDB_ORIG_MEMBEROF,
966 : orig_group_dn);
967 0 : if (ret != EOK) {
968 0 : DEBUG(SSSDBG_OP_FAILURE, "sysdb_attrs_add_string failed.\n");
969 0 : goto done;
970 : }
971 :
972 0 : ret = sysdb_set_entry_attr(pr_ctx->dom->sysdb, user_dn, user_attrs,
973 : LDB_FLAG_MOD_ADD);
974 0 : if (ret != EOK) {
975 0 : DEBUG(SSSDBG_OP_FAILURE, "sysdb_set_entry_attr failed.\n");
976 0 : goto done;
977 : }
978 : } else {
979 0 : DEBUG(SSSDBG_MINOR_FAILURE, "Original DN not available for group " \
980 : "[%s][%s].\n", grp_sid_str,
981 : ldb_dn_get_linearized(group->msgs[0]->dn));
982 : }
983 :
984 : done:
985 0 : talloc_free(tmp_ctx);
986 0 : return ret;
987 : }
988 :
989 0 : static errno_t pac_save_memberships_recv(struct tevent_req *subreq)
990 : {
991 0 : TEVENT_REQ_RETURN_ON_ERROR(subreq);
992 :
993 0 : return EOK;
994 : }
995 :
996 0 : static void pac_save_memberships_done(struct tevent_req *req)
997 : {
998 0 : struct pac_req_ctx *pr_ctx = tevent_req_callback_data(req, struct pac_req_ctx);
999 0 : struct cli_ctx *cctx = pr_ctx->cctx;
1000 : errno_t ret;
1001 :
1002 0 : ret = pac_save_memberships_recv(req);
1003 0 : talloc_zfree(req);
1004 :
1005 0 : talloc_free(pr_ctx);
1006 0 : pac_cmd_done(cctx, ret);
1007 0 : }
1008 :
1009 : struct pac_lookup_sids_state {
1010 : struct pac_ctx *pac_ctx;
1011 : struct pac_req_ctx *pr_ctx;
1012 : hash_table_t *sid_table;
1013 : struct hash_iter_context_t *iter;
1014 : };
1015 :
1016 : static errno_t pac_lookup_sids_next(struct tevent_req *req);
1017 : static void pac_lookup_sids_next_done(struct tevent_req *subreq);
1018 :
1019 0 : static struct tevent_req *pac_lookup_sids_send(TALLOC_CTX *mem_ctx,
1020 : struct tevent_context *ev,
1021 : struct pac_req_ctx *pr_ctx,
1022 : struct pac_ctx *pac_ctx,
1023 : hash_table_t *sid_table)
1024 : {
1025 : struct tevent_req *req;
1026 : struct pac_lookup_sids_state *state;
1027 : int ret;
1028 :
1029 0 : req = tevent_req_create(mem_ctx, &state, struct pac_lookup_sids_state);
1030 0 : if (req == NULL) {
1031 0 : return NULL;
1032 : }
1033 :
1034 0 : state->pac_ctx = pac_ctx;
1035 0 : state->pr_ctx = pr_ctx;
1036 0 : state->sid_table = sid_table;
1037 0 : state->iter = talloc_steal(state, new_hash_iter_context(state->sid_table));
1038 :
1039 0 : ret = pac_lookup_sids_next(req);
1040 :
1041 0 : if (ret != EAGAIN) {
1042 0 : if (ret == EOK) {
1043 0 : tevent_req_done(req);
1044 : } else {
1045 0 : tevent_req_error(req, ret);
1046 : }
1047 0 : tevent_req_post(req, ev);
1048 : }
1049 :
1050 0 : return req;
1051 : }
1052 :
1053 :
1054 0 : static errno_t pac_lookup_sids_next(struct tevent_req *req)
1055 : {
1056 : struct pac_lookup_sids_state *state;
1057 0 : state = tevent_req_data(req, struct pac_lookup_sids_state);
1058 : hash_entry_t *entry;
1059 : struct tevent_req *subreq;
1060 : struct sss_domain_info *dom;
1061 : int ret;
1062 :
1063 0 : while ((entry = state->iter->next(state->iter)) != NULL) {
1064 0 : if (entry->value.ul == 0) {
1065 0 : ret = responder_get_domain_by_id(state->pac_ctx->rctx,
1066 0 : entry->key.str, &dom);
1067 0 : if (ret == EOK && dom != NULL) {
1068 0 : subreq = sss_dp_get_account_send(state,
1069 0 : state->pr_ctx->cctx->rctx,
1070 : dom, true,
1071 0 : SSS_DP_SECID, entry->key.str,
1072 : 0, NULL);
1073 0 : if (subreq == NULL) {
1074 0 : return ENOMEM;
1075 : }
1076 0 : tevent_req_set_callback(subreq, pac_lookup_sids_next_done, req);
1077 0 : return EAGAIN;
1078 : }
1079 : }
1080 : }
1081 :
1082 0 : return EOK;
1083 : }
1084 :
1085 0 : static void pac_lookup_sids_next_done(struct tevent_req *subreq)
1086 : {
1087 : struct tevent_req *req;
1088 :
1089 0 : req = tevent_req_callback_data(subreq, struct tevent_req);
1090 :
1091 : errno_t ret;
1092 : dbus_uint16_t err_maj;
1093 : dbus_uint32_t err_min;
1094 : char *err_msg;
1095 :
1096 0 : ret = sss_dp_get_account_recv(req, subreq,
1097 : &err_maj, &err_min,
1098 : &err_msg);
1099 0 : if (ret != EOK) {
1100 0 : DEBUG(SSSDBG_OP_FAILURE,
1101 : "Unable to get information from Data Provider\n"
1102 : "dp_error: [%u], errno: [%u], error_msg: [%s]\n",
1103 : (unsigned int)err_maj, (unsigned int)err_min,
1104 : err_msg ? err_msg : "none");
1105 : }
1106 :
1107 0 : talloc_zfree(subreq);
1108 0 : talloc_zfree(err_msg);
1109 : /* Errors during individual lookups are ignored. */
1110 :
1111 0 : ret = pac_lookup_sids_next(req);
1112 0 : if (ret == EOK) {
1113 0 : tevent_req_done(req);
1114 0 : } else if (ret != EAGAIN) {
1115 0 : tevent_req_error(req, ret);
1116 : }
1117 :
1118 0 : return;
1119 : }
1120 :
1121 0 : static errno_t pac_lookup_sids_recv(struct tevent_req *req)
1122 : {
1123 0 : TEVENT_REQ_RETURN_ON_ERROR(req);
1124 :
1125 0 : return EOK;
1126 : }
1127 :
1128 0 : struct cli_protocol_version *register_cli_protocol_version(void)
1129 : {
1130 : static struct cli_protocol_version pac_cli_protocol_version[] = {
1131 : {1, "2011-04-12", "initial version"},
1132 : {0, NULL, NULL}
1133 : };
1134 :
1135 0 : return pac_cli_protocol_version;
1136 : }
1137 :
1138 : static struct sss_cmd_table pac_cmds[] = {
1139 : {SSS_GET_VERSION, sss_cmd_get_version},
1140 : {SSS_PAC_ADD_PAC_USER, pac_add_pac_user},
1141 : {SSS_CLI_NULL, NULL}
1142 : };
1143 :
1144 0 : struct sss_cmd_table *get_pac_cmds(void) {
1145 0 : return pac_cmds;
1146 : }
|