Line data Source code
1 : /*
2 : SSSD
3 :
4 : Kerberos 5 Backend Module -- Utilities
5 :
6 : Authors:
7 : Sumit Bose <sbose@redhat.com>
8 :
9 : Copyright (C) 2009 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 : #include <string.h>
25 : #include <stdlib.h>
26 : #include <libgen.h>
27 :
28 : #include "providers/krb5/krb5_utils.h"
29 : #include "providers/krb5/krb5_ccache.h"
30 : #include "providers/krb5/krb5_auth.h"
31 : #include "src/util/find_uid.h"
32 : #include "util/util.h"
33 :
34 0 : errno_t find_or_guess_upn(TALLOC_CTX *mem_ctx, struct ldb_message *msg,
35 : struct krb5_ctx *krb5_ctx,
36 : struct sss_domain_info *dom, const char *user,
37 : const char *user_dom, char **_upn)
38 : {
39 0 : const char *upn = NULL;
40 : int ret;
41 :
42 0 : if (krb5_ctx == NULL || dom == NULL || user == NULL || _upn == NULL) {
43 0 : return EINVAL;
44 : }
45 :
46 0 : if (msg != NULL) {
47 0 : upn = ldb_msg_find_attr_as_string(msg, SYSDB_CANONICAL_UPN, NULL);
48 0 : if (upn != NULL) {
49 0 : ret = EOK;
50 0 : goto done;
51 : }
52 :
53 0 : upn = ldb_msg_find_attr_as_string(msg, SYSDB_UPN, NULL);
54 0 : if (upn != NULL) {
55 0 : ret = EOK;
56 0 : goto done;
57 : }
58 : }
59 :
60 0 : ret = krb5_get_simple_upn(mem_ctx, krb5_ctx, dom, user,
61 : user_dom, _upn);
62 0 : if (ret != EOK) {
63 0 : DEBUG(SSSDBG_OP_FAILURE, "krb5_get_simple_upn failed.\n");
64 0 : return ret;
65 : }
66 :
67 : done:
68 0 : if (ret == EOK && upn != NULL) {
69 0 : *_upn = talloc_strdup(mem_ctx, upn);
70 0 : if (*_upn == NULL) {
71 0 : DEBUG(SSSDBG_OP_FAILURE, "talloc_strdup failed.\n");
72 0 : return ENOMEM;
73 : }
74 : }
75 :
76 0 : return ret;
77 : }
78 :
79 0 : errno_t check_if_cached_upn_needs_update(struct sysdb_ctx *sysdb,
80 : struct sss_domain_info *domain,
81 : const char *user,
82 : const char *upn)
83 : {
84 : TALLOC_CTX *tmp_ctx;
85 : int ret;
86 : int sret;
87 0 : const char *attrs[] = {SYSDB_UPN, SYSDB_CANONICAL_UPN, NULL};
88 : struct sysdb_attrs *new_attrs;
89 : struct ldb_result *res;
90 0 : bool in_transaction = false;
91 : const char *cached_upn;
92 : const char *cached_canonical_upn;
93 :
94 0 : if (sysdb == NULL || user == NULL || upn == NULL) {
95 0 : return EINVAL;
96 : }
97 :
98 0 : tmp_ctx = talloc_new(NULL);
99 0 : if (tmp_ctx == NULL) {
100 0 : DEBUG(SSSDBG_OP_FAILURE, "talloc_new failed.\n");
101 0 : return ENOMEM;
102 : }
103 :
104 0 : ret = sysdb_get_user_attr(tmp_ctx, domain, user, attrs, &res);
105 0 : if (ret != EOK) {
106 0 : DEBUG(SSSDBG_OP_FAILURE, "sysdb_get_user_attr failed.\n");
107 0 : goto done;
108 : }
109 :
110 0 : if (res->count != 1) {
111 0 : DEBUG(SSSDBG_OP_FAILURE, "[%d] user objects for name [%s] found, " \
112 : "expected 1.\n", res->count, user);
113 0 : ret = EINVAL;
114 0 : goto done;
115 : }
116 :
117 0 : cached_upn = ldb_msg_find_attr_as_string(res->msgs[0], SYSDB_UPN, NULL);
118 :
119 0 : if (cached_upn != NULL && strcmp(cached_upn, upn) == 0) {
120 0 : DEBUG(SSSDBG_TRACE_ALL, "Cached UPN and new one match, "
121 : "nothing to do.\n");
122 0 : ret = EOK;
123 0 : goto done;
124 : }
125 :
126 0 : cached_canonical_upn = ldb_msg_find_attr_as_string(res->msgs[0],
127 : SYSDB_CANONICAL_UPN,
128 : NULL);
129 :
130 0 : if (cached_canonical_upn != NULL
131 0 : && strcmp(cached_canonical_upn, upn) == 0) {
132 0 : DEBUG(SSSDBG_TRACE_ALL, "Cached canonical UPN and new one match, "
133 : "nothing to do.\n");
134 0 : ret = EOK;
135 0 : goto done;
136 : }
137 :
138 0 : DEBUG(SSSDBG_TRACE_LIBS, "Replacing canonical UPN [%s] with [%s] " \
139 : "for user [%s].\n",
140 : cached_canonical_upn == NULL ?
141 : "empty" : cached_canonical_upn,
142 : upn, user);
143 :
144 0 : new_attrs = sysdb_new_attrs(tmp_ctx);
145 0 : if (new_attrs == NULL) {
146 0 : DEBUG(SSSDBG_OP_FAILURE, "sysdb_new_attrs failed.\n");
147 0 : ret = ENOMEM;
148 0 : goto done;
149 : }
150 :
151 0 : ret = sysdb_attrs_add_string(new_attrs, SYSDB_CANONICAL_UPN, upn);
152 0 : if (ret != EOK) {
153 0 : DEBUG(SSSDBG_OP_FAILURE, "sysdb_attrs_add_string failed.\n");
154 0 : goto done;
155 : }
156 :
157 0 : ret = sysdb_transaction_start(sysdb);
158 0 : if (ret != EOK) {
159 0 : DEBUG(SSSDBG_OP_FAILURE,
160 : "Error %d starting transaction (%s)\n", ret, strerror(ret));
161 0 : goto done;
162 : }
163 0 : in_transaction = true;
164 :
165 0 : ret = sysdb_set_entry_attr(sysdb, res->msgs[0]->dn, new_attrs,
166 : cached_canonical_upn == NULL ? SYSDB_MOD_ADD :
167 : SYSDB_MOD_REP);
168 0 : if (ret != EOK) {
169 0 : DEBUG(SSSDBG_OP_FAILURE, "sysdb_set_entry_attr failed [%d][%s].\n",
170 : ret, strerror(ret));
171 0 : goto done;
172 : }
173 :
174 0 : ret = sysdb_transaction_commit(sysdb);
175 0 : if (ret != EOK) {
176 0 : DEBUG(SSSDBG_OP_FAILURE, "Failed to commit transaction!\n");
177 0 : goto done;
178 : }
179 0 : in_transaction = false;
180 :
181 0 : ret = EOK;
182 :
183 : done:
184 0 : if (in_transaction) {
185 0 : sret = sysdb_transaction_cancel(sysdb);
186 0 : if (sret != EOK) {
187 0 : DEBUG(SSSDBG_CRIT_FAILURE, "Failed to cancel transaction\n");
188 : }
189 : }
190 :
191 0 : talloc_free(tmp_ctx);
192 :
193 0 : return ret;
194 : }
195 :
196 : #define S_EXP_UID "{uid}"
197 : #define L_EXP_UID (sizeof(S_EXP_UID) - 1)
198 : #define S_EXP_USERID "{USERID}"
199 : #define L_EXP_USERID (sizeof(S_EXP_USERID) - 1)
200 : #define S_EXP_EUID "{euid}"
201 : #define L_EXP_EUID (sizeof(S_EXP_EUID) - 1)
202 : #define S_EXP_USERNAME "{username}"
203 : #define L_EXP_USERNAME (sizeof(S_EXP_USERNAME) - 1)
204 :
205 : static errno_t
206 4 : check_ccache_re(const char *filename, pcre *illegal_re)
207 : {
208 : errno_t ret;
209 :
210 4 : ret = pcre_exec(illegal_re, NULL, filename, strlen(filename),
211 : 0, 0, NULL, 0);
212 4 : if (ret == 0) {
213 4 : DEBUG(SSSDBG_OP_FAILURE,
214 : "Illegal pattern in ccache directory name [%s].\n", filename);
215 4 : return EINVAL;
216 0 : } else if (ret == PCRE_ERROR_NOMATCH) {
217 0 : DEBUG(SSSDBG_TRACE_LIBS,
218 : "Ccache directory name [%s] does not contain "
219 : "illegal patterns.\n", filename);
220 0 : return EOK;
221 : }
222 :
223 0 : DEBUG(SSSDBG_CRIT_FAILURE, "pcre_exec failed [%d].\n", ret);
224 0 : return EFAULT;
225 : }
226 :
227 41 : char *expand_ccname_template(TALLOC_CTX *mem_ctx, struct krb5child_req *kr,
228 : const char *template, pcre *illegal_re,
229 : bool file_mode, bool case_sensitive)
230 : {
231 : char *copy;
232 : char *p;
233 : char *n;
234 41 : char *result = NULL;
235 : char *dummy;
236 : char *name;
237 41 : char *res = NULL;
238 : const char *cache_dir_tmpl;
239 41 : TALLOC_CTX *tmp_ctx = NULL;
240 : char action;
241 : bool rerun;
242 : int ret;
243 :
244 41 : if (template == NULL) {
245 1 : DEBUG(SSSDBG_CRIT_FAILURE, "Missing template.\n");
246 1 : return NULL;
247 : }
248 :
249 40 : tmp_ctx = talloc_new(NULL);
250 40 : if (!tmp_ctx) return NULL;
251 :
252 40 : copy = talloc_strdup(tmp_ctx, template);
253 40 : if (copy == NULL) {
254 0 : DEBUG(SSSDBG_CRIT_FAILURE, "talloc_strdup failed.\n");
255 0 : goto done;
256 : }
257 :
258 40 : result = talloc_strdup(tmp_ctx, "");
259 40 : if (result == NULL) {
260 0 : DEBUG(SSSDBG_CRIT_FAILURE, "talloc_strdup failed.\n");
261 0 : goto done;
262 : }
263 :
264 40 : p = copy;
265 114 : while ( (n = strchr(p, '%')) != NULL) {
266 41 : *n = '\0';
267 41 : n++;
268 41 : if ( *n == '\0' ) {
269 0 : DEBUG(SSSDBG_CRIT_FAILURE,
270 : "format error, single %% at the end of the template.\n");
271 0 : goto done;
272 : }
273 :
274 41 : rerun = true;
275 41 : action = *n;
276 120 : while (rerun) {
277 45 : rerun = false;
278 45 : switch (action) {
279 : case 'u':
280 9 : if (kr->pd->user == NULL) {
281 0 : DEBUG(SSSDBG_CRIT_FAILURE,
282 : "Cannot expand user name template "
283 : "because user name is empty.\n");
284 0 : goto done;
285 : }
286 9 : name = sss_get_cased_name(tmp_ctx, kr->pd->user,
287 : case_sensitive);
288 9 : if (!name) {
289 0 : DEBUG(SSSDBG_CRIT_FAILURE,
290 : "sss_get_cased_name failed\n");
291 0 : goto done;
292 : }
293 :
294 9 : result = talloc_asprintf_append(result, "%s%s", p,
295 : name);
296 9 : break;
297 : case 'U':
298 7 : if (kr->uid <= 0) {
299 0 : DEBUG(SSSDBG_CRIT_FAILURE, "Cannot expand uid template "
300 : "because uid is invalid.\n");
301 0 : goto done;
302 : }
303 7 : result = talloc_asprintf_append(result, "%s%"SPRIuid, p,
304 : kr->uid);
305 7 : break;
306 : case 'p':
307 2 : if (kr->upn == NULL) {
308 0 : DEBUG(SSSDBG_CRIT_FAILURE,
309 : "Cannot expand user principal name template "
310 : "because upn is empty.\n");
311 0 : goto done;
312 : }
313 2 : result = talloc_asprintf_append(result, "%s%s", p, kr->upn);
314 2 : break;
315 : case '%':
316 2 : result = talloc_asprintf_append(result, "%s%%", p);
317 2 : break;
318 : case 'r':
319 2 : dummy = dp_opt_get_string(kr->krb5_ctx->opts, KRB5_REALM);
320 2 : if (dummy == NULL) {
321 0 : DEBUG(SSSDBG_CRIT_FAILURE, "Missing kerberos realm.\n");
322 0 : goto done;
323 : }
324 2 : result = talloc_asprintf_append(result, "%s%s", p, dummy);
325 2 : break;
326 : case 'h':
327 2 : if (kr->homedir == NULL) {
328 0 : DEBUG(SSSDBG_CRIT_FAILURE,
329 : "Cannot expand home directory template "
330 : "because the path is not available.\n");
331 0 : goto done;
332 : }
333 2 : result = talloc_asprintf_append(result, "%s%s", p, kr->homedir);
334 2 : break;
335 : case 'd':
336 12 : if (file_mode) {
337 11 : cache_dir_tmpl = dp_opt_get_string(kr->krb5_ctx->opts,
338 : KRB5_CCACHEDIR);
339 11 : if (cache_dir_tmpl == NULL) {
340 0 : DEBUG(SSSDBG_CRIT_FAILURE,
341 : "Missing credential cache directory.\n");
342 0 : goto done;
343 : }
344 :
345 11 : dummy = expand_ccname_template(tmp_ctx, kr, cache_dir_tmpl,
346 : illegal_re, false, case_sensitive);
347 11 : if (dummy == NULL) {
348 3 : DEBUG(SSSDBG_CRIT_FAILURE,
349 : "Expanding credential cache directory "
350 : "template failed.\n");
351 3 : goto done;
352 : }
353 8 : result = talloc_asprintf_append(result, "%s%s", p, dummy);
354 8 : talloc_zfree(dummy);
355 : } else {
356 1 : DEBUG(SSSDBG_CRIT_FAILURE,
357 : "'%%d' is not allowed in this template.\n");
358 1 : goto done;
359 : }
360 8 : break;
361 : case 'P':
362 2 : if (!file_mode) {
363 1 : DEBUG(SSSDBG_CRIT_FAILURE,
364 : "'%%P' is not allowed in this template.\n");
365 1 : goto done;
366 : }
367 1 : if (kr->pd->cli_pid == 0) {
368 0 : DEBUG(SSSDBG_CRIT_FAILURE, "Cannot expand PID template "
369 : "because PID is not available.\n");
370 0 : goto done;
371 : }
372 1 : result = talloc_asprintf_append(result, "%s%d", p,
373 1 : kr->pd->cli_pid);
374 1 : break;
375 :
376 : /* Additional syntax from krb5.conf default_ccache_name */
377 : case '{':
378 5 : if (strncmp(n , S_EXP_UID, L_EXP_UID) == 0) {
379 1 : action = 'U';
380 1 : n += L_EXP_UID - 1;
381 1 : rerun = true;
382 1 : continue;
383 4 : } else if (strncmp(n , S_EXP_USERID, L_EXP_USERID) == 0) {
384 1 : action = 'U';
385 1 : n += L_EXP_USERID - 1;
386 1 : rerun = true;
387 1 : continue;
388 3 : } else if (strncmp(n , S_EXP_EUID, L_EXP_EUID) == 0) {
389 : /* SSSD does not distinguish between uid and euid,
390 : * so we treat both the same way */
391 1 : action = 'U';
392 1 : n += L_EXP_EUID - 1;
393 1 : rerun = true;
394 1 : continue;
395 2 : } else if (strncmp(n , S_EXP_USERNAME, L_EXP_USERNAME) == 0) {
396 1 : action = 'u';
397 1 : n += L_EXP_USERNAME - 1;
398 1 : rerun = true;
399 1 : continue;
400 : } else {
401 : /* ignore any expansion variable we do not understand and
402 : * let libkrb5 hndle it or fail */
403 1 : name = n;
404 1 : n = strchr(name, '}');
405 1 : if (!n) {
406 0 : DEBUG(SSSDBG_CRIT_FAILURE,
407 : "Invalid substitution sequence in cache "
408 : "template. Missing closing '}' in [%s].\n",
409 : template);
410 0 : goto done;
411 : }
412 1 : result = talloc_asprintf_append(result, "%s%%%.*s", p,
413 1 : (int)(n - name + 1), name);
414 : }
415 1 : break;
416 : default:
417 2 : DEBUG(SSSDBG_CRIT_FAILURE,
418 : "format error, unknown template [%%%c].\n", *n);
419 2 : goto done;
420 : }
421 : }
422 :
423 34 : if (result == NULL) {
424 0 : DEBUG(SSSDBG_CRIT_FAILURE, "talloc_asprintf_append failed.\n");
425 0 : goto done;
426 : }
427 :
428 34 : p = n + 1;
429 : }
430 :
431 33 : result = talloc_asprintf_append(result, "%s", p);
432 33 : if (result == NULL) {
433 0 : DEBUG(SSSDBG_CRIT_FAILURE, "talloc_asprintf_append failed.\n");
434 0 : goto done;
435 : }
436 :
437 33 : if (illegal_re != NULL) {
438 4 : ret = check_ccache_re(result, illegal_re);
439 4 : if (ret != EOK) {
440 4 : goto done;
441 : }
442 : }
443 :
444 29 : res = talloc_move(mem_ctx, &result);
445 : done:
446 40 : talloc_zfree(tmp_ctx);
447 40 : return res;
448 : }
449 :
450 0 : errno_t get_domain_or_subdomain(struct be_ctx *be_ctx,
451 : char *domain_name,
452 : struct sss_domain_info **dom)
453 : {
454 :
455 0 : if (domain_name != NULL &&
456 0 : strcasecmp(domain_name, be_ctx->domain->name) != 0) {
457 0 : *dom = find_domain_by_name(be_ctx->domain, domain_name, true);
458 0 : if (*dom == NULL) {
459 0 : DEBUG(SSSDBG_OP_FAILURE, "find_domain_by_name failed.\n");
460 0 : return ENOMEM;
461 : }
462 : } else {
463 0 : *dom = be_ctx->domain;
464 : }
465 :
466 0 : return EOK;
467 : }
468 :
469 12 : static errno_t split_tuple(TALLOC_CTX *mem_ctx, const char *tuple,
470 : const char **_first, const char **_second)
471 : {
472 : errno_t ret;
473 : char **list;
474 : int n;
475 :
476 12 : ret = split_on_separator(mem_ctx, tuple, ':', true, true, &list, &n);
477 :
478 12 : if (ret != EOK) {
479 0 : DEBUG(SSSDBG_MINOR_FAILURE,
480 : "split_on_separator failed - %s:[%d]\n",
481 : sss_strerror(ret), ret);
482 0 : goto done;
483 12 : } else if (n != 2) {
484 6 : DEBUG(SSSDBG_MINOR_FAILURE,
485 : "split_on_separator failed - Expected format is: "
486 : "'username:primary' but got: '%s'.\n", tuple);
487 6 : ret = EINVAL;
488 6 : goto done;
489 : }
490 :
491 6 : *_first = list[0];
492 6 : *_second = list[1];
493 :
494 : done:
495 12 : return ret;
496 : }
497 :
498 : static errno_t
499 8 : fill_name_to_primary_map(TALLOC_CTX *mem_ctx, char **map,
500 : struct map_id_name_to_krb_primary *name_to_primary,
501 : size_t size)
502 : {
503 : int i;
504 : errno_t ret;
505 :
506 14 : for (i = 0; i < size; i++) {
507 24 : ret = split_tuple(mem_ctx, map[i],
508 12 : &name_to_primary[i].id_name,
509 12 : &name_to_primary[i].krb_primary);
510 12 : if (ret != EOK) {
511 6 : DEBUG(SSSDBG_MINOR_FAILURE,
512 : "split_tuple failed - %s:[%d]\n", sss_strerror(ret), ret);
513 6 : goto done;
514 : }
515 : }
516 :
517 2 : ret = EOK;
518 :
519 : done:
520 8 : return ret;
521 : }
522 :
523 : errno_t
524 12 : parse_krb5_map_user(TALLOC_CTX *mem_ctx, const char *krb5_map_user,
525 : struct map_id_name_to_krb_primary **_name_to_primary)
526 : {
527 : int size;
528 : char **map;
529 : errno_t ret;
530 : TALLOC_CTX *tmp_ctx;
531 : struct map_id_name_to_krb_primary *name_to_primary;
532 :
533 12 : tmp_ctx = talloc_new(NULL);
534 12 : if (tmp_ctx == NULL) {
535 0 : ret = ENOMEM;
536 0 : goto done;
537 : }
538 :
539 12 : if (krb5_map_user == NULL || strlen(krb5_map_user) == 0) {
540 2 : DEBUG(SSSDBG_FUNC_DATA, "Warning: krb5_map_user is empty!\n");
541 2 : size = 0;
542 : } else {
543 10 : ret = split_on_separator(tmp_ctx, krb5_map_user, ',', true, true,
544 : &map, &size);
545 10 : if (ret != EOK) {
546 0 : DEBUG(SSSDBG_OP_FAILURE, "Failed to parse krb5_map_user!\n");
547 0 : goto done;
548 : }
549 : }
550 :
551 12 : name_to_primary = talloc_zero_array(tmp_ctx,
552 : struct map_id_name_to_krb_primary,
553 : size + 1);
554 12 : if (name_to_primary == NULL) {
555 0 : ret = ENOMEM;
556 0 : goto done;
557 : }
558 : /* sentinel */
559 12 : name_to_primary[size].id_name = NULL;
560 12 : name_to_primary[size].krb_primary = NULL;
561 :
562 12 : if (size > 0) {
563 8 : ret = fill_name_to_primary_map(name_to_primary, map, name_to_primary,
564 : size);
565 8 : if (ret != EOK) {
566 6 : DEBUG(SSSDBG_MINOR_FAILURE,
567 : "fill_name_to_primary_map failed: %s:[%d]\n",
568 : sss_strerror(ret), ret);
569 6 : goto done;
570 : }
571 : }
572 :
573 6 : ret = EOK;
574 :
575 : done:
576 12 : if (ret == EOK) {
577 6 : *_name_to_primary = talloc_steal(mem_ctx, name_to_primary);
578 : }
579 12 : talloc_free(tmp_ctx);
580 12 : return ret;
581 : }
|