Line data Source code
1 : /*
2 : SSSD
3 :
4 : Kerberos Provider Common Functions
5 :
6 : Authors:
7 : Sumit Bose <sbose@redhat.com>
8 :
9 : Copyright (C) 2008-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 <sys/types.h>
25 : #include <sys/stat.h>
26 : #include <unistd.h>
27 : #include <netdb.h>
28 : #include <arpa/inet.h>
29 : #include <ctype.h>
30 :
31 : #include "providers/backend.h"
32 : #include "providers/krb5/krb5_common.h"
33 : #include "providers/krb5/krb5_opts.h"
34 : #include "providers/krb5/krb5_utils.h"
35 :
36 : #ifdef HAVE_KRB5_CC_COLLECTION
37 : /* krb5 profile functions */
38 : #include <profile.h>
39 : #endif
40 :
41 0 : errno_t check_and_export_lifetime(struct dp_option *opts, const int opt_id,
42 : const char *env_name)
43 : {
44 : int ret;
45 : char *str;
46 : krb5_deltat lifetime;
47 0 : bool free_str = false;
48 :
49 0 : str = dp_opt_get_string(opts, opt_id);
50 0 : if (str == NULL || *str == '\0') {
51 0 : DEBUG(SSSDBG_FUNC_DATA, "No lifetime configured.\n");
52 0 : return EOK;
53 : }
54 :
55 0 : if (isdigit(str[strlen(str)-1])) {
56 0 : str = talloc_asprintf(opts, "%ss", str);
57 0 : if (str == NULL) {
58 0 : DEBUG(SSSDBG_CRIT_FAILURE, "talloc_asprintf failed\n");
59 0 : return ENOMEM;
60 : }
61 0 : free_str = true;
62 :
63 0 : ret = dp_opt_set_string(opts, opt_id, str);
64 0 : if (ret != EOK) {
65 0 : DEBUG(SSSDBG_CRIT_FAILURE, "dp_opt_set_string failed\n");
66 0 : goto done;
67 : }
68 : }
69 :
70 0 : ret = krb5_string_to_deltat(str, &lifetime);
71 0 : if (ret != 0) {
72 0 : DEBUG(SSSDBG_CRIT_FAILURE, "Invalid value [%s] for a lifetime.\n", str);
73 0 : ret = EINVAL;
74 0 : goto done;
75 : }
76 :
77 0 : ret = setenv(env_name, str, 1);
78 0 : if (ret != EOK) {
79 0 : ret = errno;
80 0 : DEBUG(SSSDBG_OP_FAILURE, "setenv [%s] failed.\n", env_name);
81 0 : goto done;
82 : }
83 :
84 0 : ret = EOK;
85 :
86 : done:
87 0 : if (free_str) {
88 0 : talloc_free(str);
89 : }
90 :
91 0 : return ret;
92 : }
93 :
94 : #ifdef HAVE_KRB5_CC_COLLECTION
95 : /* source default_ccache_name from krb5.conf */
96 0 : static errno_t sss_get_system_ccname_template(TALLOC_CTX *mem_ctx,
97 : char **ccname)
98 : {
99 : krb5_context ctx;
100 : profile_t p;
101 0 : char *value = NULL;
102 : long ret;
103 :
104 0 : *ccname = NULL;
105 :
106 0 : ret = krb5_init_context(&ctx);
107 0 : if (ret) return ret;
108 :
109 0 : ret = krb5_get_profile(ctx, &p);
110 0 : if (ret) goto done;
111 :
112 0 : ret = profile_get_string(p, "libdefaults", "default_ccache_name",
113 : NULL, NULL, &value);
114 0 : if (ret) goto done;
115 :
116 0 : if (!value) {
117 0 : ret = ERR_NOT_FOUND;
118 0 : goto done;
119 : }
120 :
121 0 : *ccname = talloc_strdup(mem_ctx, value);
122 0 : if (*ccname == NULL) {
123 0 : ret = ENOMEM;
124 0 : goto done;
125 : }
126 :
127 0 : ret = EOK;
128 :
129 : done:
130 0 : krb5_free_context(ctx);
131 0 : free(value);
132 0 : return ret;
133 : }
134 : #else
135 : static errno_t sss_get_system_ccname_template(TALLOC_CTX *mem_ctx,
136 : char **ccname)
137 : {
138 : DEBUG(SSSDBG_CONF_SETTINGS,
139 : "Your kerberos library does not support the default_ccache_name "
140 : "option or the profile library. Please use krb5_ccname_template "
141 : "in sssd.conf if you want to change the default\n");
142 : *ccname = NULL;
143 : return ERR_NOT_FOUND;
144 : }
145 : #endif
146 :
147 0 : static void sss_check_cc_template(const char *cc_template)
148 : {
149 : size_t template_len;
150 :
151 0 : template_len = strlen(cc_template);
152 0 : if (template_len >= 6 &&
153 0 : strcmp(cc_template + (template_len - 6), "XXXXXX") != 0) {
154 0 : DEBUG(SSSDBG_CONF_SETTINGS, "ccache file name template [%s] doesn't "
155 : "contain randomizing characters (XXXXXX), file might not "
156 : "be rewritable\n", cc_template);
157 : }
158 0 : }
159 :
160 0 : errno_t check_and_export_options(struct dp_option *opts,
161 : struct sss_domain_info *dom,
162 : struct krb5_ctx *krb5_ctx)
163 : {
164 0 : TALLOC_CTX *tmp_ctx = NULL;
165 : int ret;
166 : const char *realm;
167 : const char *dummy;
168 : char *use_fast_str;
169 : char *fast_principal;
170 : char *ccname;
171 :
172 0 : tmp_ctx = talloc_new(NULL);
173 0 : if (!tmp_ctx) {
174 0 : ret = ENOMEM;
175 0 : goto done;
176 : }
177 :
178 0 : realm = dp_opt_get_cstring(opts, KRB5_REALM);
179 0 : if (realm == NULL) {
180 0 : ret = dp_opt_set_string(opts, KRB5_REALM, dom->name);
181 0 : if (ret != EOK) {
182 0 : DEBUG(SSSDBG_CRIT_FAILURE, "dp_opt_set_string failed.\n");
183 0 : goto done;
184 : }
185 0 : realm = dom->name;
186 : }
187 :
188 0 : ret = setenv(SSSD_KRB5_REALM, realm, 1);
189 0 : if (ret != EOK) {
190 0 : DEBUG(SSSDBG_OP_FAILURE,
191 : "setenv %s failed, authentication might fail.\n",
192 : SSSD_KRB5_REALM);
193 : }
194 :
195 0 : ret = check_and_export_lifetime(opts, KRB5_RENEWABLE_LIFETIME,
196 : SSSD_KRB5_RENEWABLE_LIFETIME);
197 0 : if (ret != EOK) {
198 0 : DEBUG(SSSDBG_CRIT_FAILURE,
199 : "Failed to check value of krb5_renewable_lifetime. [%d][%s]\n",
200 : ret, strerror(ret));
201 0 : goto done;
202 : }
203 :
204 0 : ret = check_and_export_lifetime(opts, KRB5_LIFETIME,
205 : SSSD_KRB5_LIFETIME);
206 0 : if (ret != EOK) {
207 0 : DEBUG(SSSDBG_CRIT_FAILURE,
208 : "Failed to check value of krb5_lifetime. [%d][%s]\n",
209 : ret, strerror(ret));
210 0 : goto done;
211 : }
212 :
213 :
214 0 : use_fast_str = dp_opt_get_string(opts, KRB5_USE_FAST);
215 0 : if (use_fast_str != NULL) {
216 0 : ret = check_fast(use_fast_str, &krb5_ctx->use_fast);
217 0 : if (ret != EOK) {
218 0 : DEBUG(SSSDBG_CRIT_FAILURE, "check_fast failed.\n");
219 0 : goto done;
220 : }
221 :
222 0 : if (krb5_ctx->use_fast) {
223 0 : ret = setenv(SSSD_KRB5_USE_FAST, use_fast_str, 1);
224 0 : if (ret != EOK) {
225 0 : DEBUG(SSSDBG_OP_FAILURE,
226 : "setenv [%s] failed.\n", SSSD_KRB5_USE_FAST);
227 : } else {
228 0 : fast_principal = dp_opt_get_string(opts, KRB5_FAST_PRINCIPAL);
229 0 : if (fast_principal != NULL) {
230 0 : ret = setenv(SSSD_KRB5_FAST_PRINCIPAL, fast_principal, 1);
231 0 : if (ret != EOK) {
232 0 : DEBUG(SSSDBG_OP_FAILURE,
233 : "setenv [%s] failed.\n", SSSD_KRB5_FAST_PRINCIPAL);
234 : }
235 : }
236 : }
237 : }
238 : }
239 :
240 : /* In contrast to MIT KDCs AD does not automatically canonicalize the
241 : * enterprise principal in an AS request but requires the canonicalize
242 : * flags to be set. To be on the safe side we always enable
243 : * canonicalization if enterprise principals are used. */
244 0 : if (dp_opt_get_bool(opts, KRB5_CANONICALIZE)
245 0 : || dp_opt_get_bool(opts, KRB5_USE_ENTERPRISE_PRINCIPAL)) {
246 0 : ret = setenv(SSSD_KRB5_CANONICALIZE, "true", 1);
247 : } else {
248 0 : ret = setenv(SSSD_KRB5_CANONICALIZE, "false", 1);
249 : }
250 0 : if (ret != EOK) {
251 0 : DEBUG(SSSDBG_OP_FAILURE,
252 : "setenv [%s] failed.\n", SSSD_KRB5_CANONICALIZE);
253 : }
254 :
255 0 : dummy = dp_opt_get_cstring(opts, KRB5_KDC);
256 0 : if (dummy == NULL) {
257 0 : DEBUG(SSSDBG_CONF_SETTINGS, "No KDC explicitly configured, using defaults.\n");
258 : }
259 :
260 0 : dummy = dp_opt_get_cstring(opts, KRB5_KPASSWD);
261 0 : if (dummy == NULL) {
262 0 : DEBUG(SSSDBG_CONF_SETTINGS, "No kpasswd server explicitly configured, "
263 : "using the KDC or defaults.\n");
264 : }
265 :
266 0 : ccname = dp_opt_get_string(opts, KRB5_CCNAME_TMPL);
267 0 : if (ccname != NULL) {
268 0 : DEBUG(SSSDBG_CONF_SETTINGS,
269 : "The credential ccache name template has been explicitly set "
270 : "in sssd.conf, it is recommended to set default_ccache_name "
271 : "in krb5.conf instead so that a system default is used\n");
272 0 : ccname = talloc_strdup(tmp_ctx, ccname);
273 0 : if (!ccname) {
274 0 : ret = ENOMEM;
275 0 : goto done;
276 : }
277 : } else {
278 0 : ret = sss_get_system_ccname_template(tmp_ctx, &ccname);
279 0 : if (ret && ret != ERR_NOT_FOUND) {
280 0 : goto done;
281 : }
282 0 : if (ret == ERR_NOT_FOUND) {
283 : /* Use fallback default */
284 0 : ccname = talloc_strdup(tmp_ctx, DEFAULT_CCNAME_TEMPLATE);
285 0 : if (!ccname) {
286 0 : ret = ENOMEM;
287 0 : goto done;
288 : }
289 : }
290 :
291 : /* set back in opts */
292 0 : ret = dp_opt_set_string(opts, KRB5_CCNAME_TMPL, ccname);
293 0 : if (ret != EOK) {
294 0 : DEBUG(SSSDBG_CRIT_FAILURE, "dp_opt_set_string failed.\n");
295 0 : goto done;
296 : }
297 : }
298 :
299 0 : if ((ccname[0] == '/') || (strncmp(ccname, "FILE:", 5) == 0)) {
300 0 : DEBUG(SSSDBG_CONF_SETTINGS, "ccache is of type FILE\n");
301 : /* warn if the file type (which is usally created in a sticky bit
302 : * laden directory) does not have randomizing chracters */
303 0 : sss_check_cc_template(ccname);
304 :
305 0 : if (ccname[0] == '/') {
306 : /* /path/to/cc prepend FILE: */
307 0 : DEBUG(SSSDBG_CONF_SETTINGS, "The ccname template was "
308 : "missing an explicit type, but is an absolute "
309 : "path specifier. Assuming FILE:\n");
310 :
311 0 : ccname = talloc_asprintf(tmp_ctx, "FILE:%s", ccname);
312 0 : if (!ccname) {
313 0 : ret = ENOMEM;
314 0 : goto done;
315 : }
316 :
317 0 : ret = dp_opt_set_string(opts, KRB5_CCNAME_TMPL, ccname);
318 0 : if (ret != EOK) {
319 0 : DEBUG(SSSDBG_CRIT_FAILURE, "dp_opt_set_string failed.\n");
320 0 : goto done;
321 : }
322 : }
323 : }
324 :
325 0 : ret = EOK;
326 :
327 : done:
328 0 : talloc_free(tmp_ctx);
329 0 : return ret;
330 : }
331 :
332 0 : errno_t krb5_try_kdcip(struct confdb_ctx *cdb, const char *conf_path,
333 : struct dp_option *opts, int opt_id)
334 : {
335 0 : char *krb5_servers = NULL;
336 : errno_t ret;
337 :
338 0 : krb5_servers = dp_opt_get_string(opts, opt_id);
339 0 : if (krb5_servers == NULL) {
340 0 : DEBUG(SSSDBG_CONF_SETTINGS,
341 : "No KDC found in configuration, trying legacy option\n");
342 0 : ret = confdb_get_string(cdb, NULL, conf_path,
343 : "krb5_kdcip", NULL, &krb5_servers);
344 0 : if (ret != EOK) {
345 0 : DEBUG(SSSDBG_CRIT_FAILURE, "confdb_get_string failed.\n");
346 0 : return ret;
347 : }
348 :
349 0 : if (krb5_servers != NULL)
350 : {
351 0 : ret = dp_opt_set_string(opts, opt_id, krb5_servers);
352 0 : if (ret != EOK) {
353 0 : DEBUG(SSSDBG_CRIT_FAILURE, "dp_opt_set_string failed.\n");
354 0 : talloc_free(krb5_servers);
355 0 : return ret;
356 : }
357 :
358 0 : DEBUG(SSSDBG_CONF_SETTINGS,
359 : "Set krb5 server [%s] based on legacy krb5_kdcip option\n",
360 : krb5_servers);
361 0 : DEBUG(SSSDBG_FATAL_FAILURE,
362 : "Your configuration uses the deprecated option "
363 : "'krb5_kdcip' to specify the KDC. Please change the "
364 : "configuration to use the 'krb5_server' option "
365 : "instead.\n");
366 0 : talloc_free(krb5_servers);
367 : }
368 : }
369 :
370 0 : return EOK;
371 : }
372 :
373 0 : errno_t krb5_get_options(TALLOC_CTX *memctx, struct confdb_ctx *cdb,
374 : const char *conf_path, struct dp_option **_opts)
375 : {
376 : int ret;
377 : struct dp_option *opts;
378 :
379 0 : opts = talloc_zero(memctx, struct dp_option);
380 0 : if (opts == NULL) {
381 0 : DEBUG(SSSDBG_CRIT_FAILURE, "talloc_zero failed.\n");
382 0 : return ENOMEM;
383 : }
384 :
385 0 : ret = dp_get_options(opts, cdb, conf_path, default_krb5_opts,
386 : KRB5_OPTS, &opts);
387 0 : if (ret != EOK) {
388 0 : DEBUG(SSSDBG_CRIT_FAILURE, "dp_get_options failed.\n");
389 0 : goto done;
390 : }
391 :
392 : /* If there is no KDC, try the deprecated krb5_kdcip option, too */
393 : /* FIXME - this can be removed in a future version */
394 0 : ret = krb5_try_kdcip(cdb, conf_path, opts, KRB5_KDC);
395 0 : if (ret != EOK) {
396 0 : DEBUG(SSSDBG_CRIT_FAILURE, "sss_krb5_try_kdcip failed.\n");
397 0 : goto done;
398 : }
399 :
400 0 : *_opts = opts;
401 0 : ret = EOK;
402 :
403 : done:
404 0 : if (ret != EOK) {
405 0 : talloc_zfree(opts);
406 : }
407 :
408 0 : return ret;
409 : }
410 :
411 0 : errno_t write_krb5info_file(const char *realm, const char *server,
412 : const char *service)
413 : {
414 : int ret;
415 0 : int fd = -1;
416 0 : char *tmp_name = NULL;
417 0 : char *krb5info_name = NULL;
418 0 : TALLOC_CTX *tmp_ctx = NULL;
419 0 : const char *name_tmpl = NULL;
420 : size_t server_len;
421 : ssize_t written;
422 :
423 0 : if (realm == NULL || *realm == '\0' || server == NULL || *server == '\0' ||
424 0 : service == NULL || *service == '\0') {
425 0 : DEBUG(SSSDBG_CRIT_FAILURE,
426 : "Missing or empty realm, server or service.\n");
427 0 : return EINVAL;
428 : }
429 :
430 0 : if (sss_krb5_realm_has_proxy(realm)) {
431 0 : DEBUG(SSSDBG_CONF_SETTINGS,
432 : "KDC Proxy available for realm [%s], no kdcinfo file created.\n",
433 : realm);
434 0 : return EOK;
435 : }
436 :
437 0 : if (strcmp(service, SSS_KRB5KDC_FO_SRV) == 0) {
438 0 : name_tmpl = KDCINFO_TMPL;
439 0 : } else if (strcmp(service, SSS_KRB5KPASSWD_FO_SRV) == 0) {
440 0 : name_tmpl = KPASSWDINFO_TMPL;
441 : } else {
442 0 : DEBUG(SSSDBG_CRIT_FAILURE, "Unsupported service [%s].\n", service);
443 0 : return EINVAL;
444 : }
445 :
446 0 : server_len = strlen(server);
447 :
448 0 : tmp_ctx = talloc_new(NULL);
449 0 : if (tmp_ctx == NULL) {
450 0 : DEBUG(SSSDBG_CRIT_FAILURE, "talloc_new failed.\n");
451 0 : return ENOMEM;
452 : }
453 :
454 0 : tmp_name = talloc_asprintf(tmp_ctx, PUBCONF_PATH"/.krb5info_dummy_XXXXXX");
455 0 : if (tmp_name == NULL) {
456 0 : DEBUG(SSSDBG_CRIT_FAILURE, "talloc_asprintf failed.\n");
457 0 : ret = ENOMEM;
458 0 : goto done;
459 : }
460 :
461 0 : krb5info_name = talloc_asprintf(tmp_ctx, name_tmpl, realm);
462 0 : if (krb5info_name == NULL) {
463 0 : DEBUG(SSSDBG_CRIT_FAILURE, "talloc_asprintf failed.\n");
464 0 : ret = ENOMEM;
465 0 : goto done;
466 : }
467 :
468 0 : fd = sss_unique_file(tmp_ctx, tmp_name, &ret);
469 0 : if (fd == -1) {
470 0 : DEBUG(SSSDBG_CRIT_FAILURE,
471 : "sss_unique_file failed [%d][%s].\n", ret, strerror(ret));
472 0 : goto done;
473 : }
474 :
475 0 : errno = 0;
476 0 : written = sss_atomic_write_s(fd, discard_const(server), server_len);
477 0 : if (written == -1) {
478 0 : ret = errno;
479 0 : DEBUG(SSSDBG_CRIT_FAILURE,
480 : "write failed [%d][%s].\n", ret, strerror(ret));
481 0 : goto done;
482 : }
483 :
484 0 : if (written != server_len) {
485 0 : DEBUG(SSSDBG_CRIT_FAILURE,
486 : "Write error, wrote [%zd] bytes, expected [%zu]\n",
487 : written, server_len);
488 0 : ret = EIO;
489 0 : goto done;
490 : }
491 :
492 0 : ret = fchmod(fd, S_IRUSR|S_IWUSR|S_IRGRP|S_IROTH);
493 0 : if (ret == -1) {
494 0 : ret = errno;
495 0 : DEBUG(SSSDBG_CRIT_FAILURE,
496 : "fchmod failed [%d][%s].\n", ret, strerror(ret));
497 0 : goto done;
498 : }
499 :
500 0 : ret = close(fd);
501 0 : fd = -1;
502 0 : if (ret == -1) {
503 0 : ret = errno;
504 0 : DEBUG(SSSDBG_CRIT_FAILURE,
505 : "close failed [%d][%s].\n", ret, strerror(ret));
506 0 : goto done;
507 : }
508 :
509 0 : ret = rename(tmp_name, krb5info_name);
510 0 : if (ret == -1) {
511 0 : ret = errno;
512 0 : DEBUG(SSSDBG_CRIT_FAILURE,
513 : "rename failed [%d][%s].\n", ret, strerror(ret));
514 0 : goto done;
515 : }
516 :
517 0 : ret = EOK;
518 : done:
519 0 : if (fd != -1) {
520 0 : close(fd);
521 : }
522 :
523 0 : talloc_free(tmp_ctx);
524 0 : return ret;
525 : }
526 :
527 0 : static void krb5_resolve_callback(void *private_data, struct fo_server *server)
528 : {
529 : struct krb5_service *krb5_service;
530 : struct resolv_hostent *srvaddr;
531 : char *address;
532 : char *safe_address;
533 : int ret;
534 0 : TALLOC_CTX *tmp_ctx = NULL;
535 :
536 0 : tmp_ctx = talloc_new(NULL);
537 0 : if (tmp_ctx == NULL) {
538 0 : DEBUG(SSSDBG_CRIT_FAILURE, "talloc_new failed\n");
539 0 : return;
540 : }
541 :
542 0 : krb5_service = talloc_get_type(private_data, struct krb5_service);
543 0 : if (!krb5_service) {
544 0 : DEBUG(SSSDBG_CRIT_FAILURE, "FATAL: Bad private_data\n");
545 0 : talloc_free(tmp_ctx);
546 0 : return;
547 : }
548 :
549 0 : srvaddr = fo_get_server_hostent(server);
550 0 : if (!srvaddr) {
551 0 : DEBUG(SSSDBG_CRIT_FAILURE,
552 : "FATAL: No hostent available for server (%s)\n",
553 : fo_get_server_str_name(server));
554 0 : talloc_free(tmp_ctx);
555 0 : return;
556 : }
557 :
558 0 : address = resolv_get_string_address(tmp_ctx, srvaddr);
559 0 : if (address == NULL) {
560 0 : DEBUG(SSSDBG_CRIT_FAILURE, "resolv_get_string_address failed.\n");
561 0 : talloc_free(tmp_ctx);
562 0 : return;
563 : }
564 :
565 0 : safe_address = sss_escape_ip_address(tmp_ctx,
566 : srvaddr->family,
567 : address);
568 0 : if (safe_address == NULL) {
569 0 : DEBUG(SSSDBG_CRIT_FAILURE, "sss_escape_ip_address failed.\n");
570 0 : talloc_free(tmp_ctx);
571 0 : return;
572 : }
573 :
574 0 : if (krb5_service->write_kdcinfo) {
575 0 : safe_address = talloc_asprintf_append(safe_address, ":%d",
576 : fo_get_server_port(server));
577 0 : if (safe_address == NULL) {
578 0 : DEBUG(SSSDBG_CRIT_FAILURE, "talloc_asprintf_append failed.\n");
579 0 : talloc_free(tmp_ctx);
580 0 : return;
581 : }
582 :
583 0 : ret = write_krb5info_file(krb5_service->realm, safe_address,
584 0 : krb5_service->name);
585 0 : if (ret != EOK) {
586 0 : DEBUG(SSSDBG_OP_FAILURE,
587 : "write_krb5info_file failed, authentication might fail.\n");
588 : }
589 : }
590 :
591 0 : talloc_free(tmp_ctx);
592 0 : return;
593 : }
594 :
595 0 : static errno_t _krb5_servers_init(struct be_ctx *ctx,
596 : struct krb5_service *service,
597 : const char *service_name,
598 : const char *servers,
599 : bool primary)
600 : {
601 : TALLOC_CTX *tmp_ctx;
602 0 : char **list = NULL;
603 0 : errno_t ret = 0;
604 : int i;
605 : char *port_str;
606 : long port;
607 : char *server_spec;
608 : char *endptr;
609 : struct servent *servent;
610 :
611 0 : tmp_ctx = talloc_new(NULL);
612 0 : if (!tmp_ctx) {
613 0 : return ENOMEM;
614 : }
615 :
616 0 : ret = split_on_separator(tmp_ctx, servers, ',', true, true, &list, NULL);
617 0 : if (ret != EOK) {
618 0 : DEBUG(SSSDBG_CRIT_FAILURE, "Failed to parse server list!\n");
619 0 : goto done;
620 : }
621 :
622 0 : for (i = 0; list[i]; i++) {
623 0 : talloc_steal(service, list[i]);
624 0 : server_spec = talloc_strdup(service, list[i]);
625 0 : if (!server_spec) {
626 0 : ret = ENOMEM;
627 0 : goto done;
628 : }
629 :
630 0 : if (be_fo_is_srv_identifier(server_spec)) {
631 0 : if (!primary) {
632 0 : DEBUG(SSSDBG_MINOR_FAILURE,
633 : "Failed to add server [%s] to failover service: "
634 : "SRV resolution only allowed for primary servers!\n",
635 : list[i]);
636 0 : continue;
637 : }
638 :
639 0 : ret = be_fo_add_srv_server(ctx, service_name, service_name, NULL,
640 : BE_FO_PROTO_UDP, true, NULL);
641 0 : if (ret) {
642 0 : DEBUG(SSSDBG_FATAL_FAILURE, "Failed to add server\n");
643 0 : goto done;
644 : }
645 :
646 0 : DEBUG(SSSDBG_TRACE_FUNC, "Added service lookup\n");
647 0 : continue;
648 : }
649 :
650 : /* Do not try to get port number if last character is ']' */
651 0 : if (server_spec[strlen(server_spec) - 1] != ']') {
652 0 : port_str = strrchr(server_spec, ':');
653 : } else {
654 0 : port_str = NULL;
655 : }
656 :
657 0 : if (port_str == NULL) {
658 0 : port = 0;
659 : } else {
660 0 : *port_str = '\0';
661 0 : ++port_str;
662 0 : if (isdigit(*port_str)) {
663 0 : errno = 0;
664 0 : port = strtol(port_str, &endptr, 10);
665 0 : if (errno != 0) {
666 0 : ret = errno;
667 0 : DEBUG(SSSDBG_CRIT_FAILURE, "strtol failed on [%s]: [%d][%s].\n", port_str,
668 : ret, strerror(ret));
669 0 : goto done;
670 : }
671 0 : if (*endptr != '\0') {
672 0 : DEBUG(SSSDBG_CRIT_FAILURE, "Found additional characters [%s] in port number "
673 : "[%s].\n", endptr, port_str);
674 0 : ret = EINVAL;
675 0 : goto done;
676 : }
677 :
678 0 : if (port < 1 || port > 65535) {
679 0 : DEBUG(SSSDBG_CRIT_FAILURE, "Illegal port number [%ld].\n", port);
680 0 : ret = EINVAL;
681 0 : goto done;
682 : }
683 0 : } else if (isalpha(*port_str)) {
684 0 : servent = getservbyname(port_str, NULL);
685 0 : if (servent == NULL) {
686 0 : DEBUG(SSSDBG_CRIT_FAILURE, "getservbyname cannot find service [%s].\n",
687 : port_str);
688 0 : ret = EINVAL;
689 0 : goto done;
690 : }
691 :
692 0 : port = servent->s_port;
693 : } else {
694 0 : DEBUG(SSSDBG_CRIT_FAILURE, "Unsupported port specifier in [%s].\n", list[i]);
695 0 : ret = EINVAL;
696 0 : goto done;
697 : }
698 : }
699 :
700 : /* It could be ipv6 address in square brackets. Remove
701 : * the brackets if needed. */
702 0 : ret = remove_ipv6_brackets(server_spec);
703 0 : if (ret != EOK) {
704 0 : goto done;
705 : }
706 :
707 0 : ret = be_fo_add_server(ctx, service_name, server_spec, (int) port,
708 0 : list[i], primary);
709 0 : if (ret && ret != EEXIST) {
710 0 : DEBUG(SSSDBG_FATAL_FAILURE, "Failed to add server\n");
711 0 : goto done;
712 : }
713 :
714 0 : DEBUG(SSSDBG_TRACE_FUNC, "Added Server %s\n", list[i]);
715 : }
716 :
717 : done:
718 0 : talloc_free(tmp_ctx);
719 0 : return ret;
720 : }
721 :
722 : static inline errno_t
723 0 : krb5_primary_servers_init(struct be_ctx *ctx, struct krb5_service *service,
724 : const char *service_name, const char *servers)
725 : {
726 0 : return _krb5_servers_init(ctx, service, service_name, servers, true);
727 : }
728 :
729 : static inline errno_t
730 0 : krb5_backup_servers_init(struct be_ctx *ctx, struct krb5_service *service,
731 : const char *service_name, const char *servers)
732 : {
733 0 : return _krb5_servers_init(ctx, service, service_name, servers, false);
734 : }
735 :
736 0 : static int krb5_user_data_cmp(void *ud1, void *ud2)
737 : {
738 0 : return strcasecmp((char*) ud1, (char*) ud2);
739 : }
740 :
741 0 : int krb5_service_init(TALLOC_CTX *memctx, struct be_ctx *ctx,
742 : const char *service_name,
743 : const char *primary_servers,
744 : const char *backup_servers,
745 : const char *realm,
746 : bool use_kdcinfo,
747 : struct krb5_service **_service)
748 : {
749 : TALLOC_CTX *tmp_ctx;
750 : struct krb5_service *service;
751 : int ret;
752 :
753 0 : tmp_ctx = talloc_new(memctx);
754 0 : if (!tmp_ctx) {
755 0 : return ENOMEM;
756 : }
757 :
758 0 : service = talloc_zero(tmp_ctx, struct krb5_service);
759 0 : if (!service) {
760 0 : ret = ENOMEM;
761 0 : goto done;
762 : }
763 :
764 0 : ret = be_fo_add_service(ctx, service_name, krb5_user_data_cmp);
765 0 : if (ret != EOK) {
766 0 : DEBUG(SSSDBG_CRIT_FAILURE, "Failed to create failover service!\n");
767 0 : goto done;
768 : }
769 :
770 0 : service->name = talloc_strdup(service, service_name);
771 0 : if (!service->name) {
772 0 : ret = ENOMEM;
773 0 : goto done;
774 : }
775 :
776 0 : service->realm = talloc_strdup(service, realm);
777 0 : if (!service->realm) {
778 0 : ret = ENOMEM;
779 0 : goto done;
780 : }
781 :
782 0 : service->write_kdcinfo = use_kdcinfo;
783 :
784 0 : if (!primary_servers) {
785 0 : DEBUG(SSSDBG_CONF_SETTINGS,
786 : "No primary servers defined, using service discovery\n");
787 0 : primary_servers = BE_SRV_IDENTIFIER;
788 : }
789 :
790 0 : ret = krb5_primary_servers_init(ctx, service, service_name, primary_servers);
791 0 : if (ret != EOK) {
792 0 : goto done;
793 : }
794 :
795 0 : if (backup_servers) {
796 0 : ret = krb5_backup_servers_init(ctx, service, service_name,
797 : backup_servers);
798 0 : if (ret != EOK) {
799 0 : goto done;
800 : }
801 : }
802 :
803 0 : ret = be_fo_service_add_callback(memctx, ctx, service_name,
804 : krb5_resolve_callback, service);
805 0 : if (ret != EOK) {
806 0 : DEBUG(SSSDBG_CRIT_FAILURE, "Failed to add failover callback!\n");
807 0 : goto done;
808 : }
809 :
810 0 : ret = EOK;
811 :
812 : done:
813 0 : if (ret == EOK) {
814 0 : *_service = talloc_steal(memctx, service);
815 : }
816 0 : talloc_zfree(tmp_ctx);
817 0 : return ret;
818 : }
819 :
820 :
821 0 : errno_t remove_krb5_info_files(TALLOC_CTX *mem_ctx, const char *realm)
822 : {
823 : int ret;
824 : errno_t err;
825 : char *file;
826 :
827 0 : file = talloc_asprintf(mem_ctx, KDCINFO_TMPL, realm);
828 0 : if(file == NULL) {
829 0 : DEBUG(SSSDBG_CRIT_FAILURE, "talloc_asprintf failed.\n");
830 0 : return ENOMEM;
831 : }
832 :
833 0 : errno = 0;
834 0 : ret = unlink(file);
835 0 : if (ret == -1) {
836 0 : err = errno;
837 0 : DEBUG(SSSDBG_FUNC_DATA, "Could not remove [%s], [%d][%s]\n", file,
838 : err, strerror(err));
839 : }
840 :
841 0 : file = talloc_asprintf(mem_ctx, KPASSWDINFO_TMPL, realm);
842 0 : if(file == NULL) {
843 0 : DEBUG(SSSDBG_CRIT_FAILURE, "talloc_asprintf failed.\n");
844 0 : return ENOMEM;
845 : }
846 :
847 0 : errno = 0;
848 0 : ret = unlink(file);
849 0 : if (ret == -1) {
850 0 : err = errno;
851 0 : DEBUG(SSSDBG_FUNC_DATA, "Could not remove [%s], [%d][%s]\n", file,
852 : err, strerror(err));
853 : }
854 :
855 0 : return EOK;
856 : }
857 :
858 0 : void remove_krb5_info_files_callback(void *pvt)
859 : {
860 : int ret;
861 0 : TALLOC_CTX *tmp_ctx = NULL;
862 0 : struct remove_info_files_ctx *ctx = talloc_get_type(pvt,
863 : struct remove_info_files_ctx);
864 :
865 0 : ret = be_fo_run_callbacks_at_next_request(ctx->be_ctx,
866 : ctx->kdc_service_name);
867 0 : if (ret != EOK) {
868 0 : DEBUG(SSSDBG_CRIT_FAILURE,
869 : "be_fo_run_callbacks_at_next_request failed, "
870 : "krb5 info files will not be removed, because "
871 : "it is unclear if they will be recreated properly.\n");
872 0 : return;
873 : }
874 0 : if (ctx->kpasswd_service_name != NULL) {
875 0 : ret = be_fo_run_callbacks_at_next_request(ctx->be_ctx,
876 : ctx->kpasswd_service_name);
877 0 : if (ret != EOK) {
878 0 : DEBUG(SSSDBG_CRIT_FAILURE,
879 : "be_fo_run_callbacks_at_next_request failed, "
880 : "krb5 info files will not be removed, because "
881 : "it is unclear if they will be recreated properly.\n");
882 0 : return;
883 : }
884 : }
885 :
886 0 : tmp_ctx = talloc_new(NULL);
887 0 : if (tmp_ctx == NULL) {
888 0 : DEBUG(SSSDBG_CRIT_FAILURE,
889 : "talloc_new failed, cannot remove krb5 info files.\n");
890 0 : return;
891 : }
892 :
893 0 : ret = remove_krb5_info_files(tmp_ctx, ctx->realm);
894 0 : if (ret != EOK) {
895 0 : DEBUG(SSSDBG_CRIT_FAILURE, "remove_krb5_info_files failed.\n");
896 : }
897 :
898 0 : talloc_zfree(tmp_ctx);
899 : }
900 :
901 0 : void krb5_finalize(struct tevent_context *ev,
902 : struct tevent_signal *se,
903 : int signum,
904 : int count,
905 : void *siginfo,
906 : void *private_data)
907 : {
908 0 : char *realm = (char *)private_data;
909 : int ret;
910 :
911 0 : ret = remove_krb5_info_files(se, realm);
912 0 : if (ret != EOK) {
913 0 : DEBUG(SSSDBG_CRIT_FAILURE, "remove_krb5_info_files failed.\n");
914 : }
915 :
916 0 : orderly_shutdown(0);
917 0 : }
918 :
919 0 : errno_t krb5_install_offline_callback(struct be_ctx *be_ctx,
920 : struct krb5_ctx *krb5_ctx)
921 : {
922 : int ret;
923 : struct remove_info_files_ctx *ctx;
924 : const char *krb5_realm;
925 :
926 0 : if (krb5_ctx->service == NULL || krb5_ctx->service->name == NULL) {
927 0 : DEBUG(SSSDBG_CRIT_FAILURE, "Missing KDC service name!\n");
928 0 : return EINVAL;
929 : }
930 :
931 0 : ctx = talloc_zero(krb5_ctx, struct remove_info_files_ctx);
932 0 : if (ctx == NULL) {
933 0 : DEBUG(SSSDBG_CRIT_FAILURE, "talloc_zfree failed.\n");
934 0 : return ENOMEM;
935 : }
936 :
937 0 : krb5_realm = dp_opt_get_cstring(krb5_ctx->opts, KRB5_REALM);
938 0 : if (krb5_realm == NULL) {
939 0 : DEBUG(SSSDBG_CRIT_FAILURE, "Missing krb5_realm option!\n");
940 0 : ret = EINVAL;
941 0 : goto done;
942 : }
943 :
944 0 : ctx->realm = talloc_strdup(ctx, krb5_realm);
945 0 : if (ctx->realm == NULL) {
946 0 : DEBUG(SSSDBG_CRIT_FAILURE, "talloc_strdup failed!\n");
947 0 : ret = ENOMEM;
948 0 : goto done;
949 : }
950 :
951 0 : ctx->be_ctx = be_ctx;
952 0 : ctx->kdc_service_name = krb5_ctx->service->name;
953 0 : if (krb5_ctx->kpasswd_service == NULL) {
954 0 : ctx->kpasswd_service_name =NULL;
955 : } else {
956 0 : ctx->kpasswd_service_name = krb5_ctx->kpasswd_service->name;
957 : }
958 :
959 0 : ret = be_add_offline_cb(ctx, be_ctx, remove_krb5_info_files_callback, ctx,
960 : NULL);
961 0 : if (ret != EOK) {
962 0 : DEBUG(SSSDBG_CRIT_FAILURE, "be_add_offline_cb failed.\n");
963 0 : goto done;
964 : }
965 :
966 0 : ret = EOK;
967 :
968 : done:
969 0 : if (ret != EOK) {
970 0 : talloc_zfree(ctx);
971 : }
972 :
973 0 : return ret;
974 : }
975 :
976 0 : errno_t krb5_install_sigterm_handler(struct tevent_context *ev,
977 : struct krb5_ctx *krb5_ctx)
978 : {
979 : const char *krb5_realm;
980 : char *sig_realm;
981 : struct tevent_signal *sige;
982 :
983 0 : BlockSignals(false, SIGTERM);
984 :
985 0 : krb5_realm = dp_opt_get_cstring(krb5_ctx->opts, KRB5_REALM);
986 0 : if (krb5_realm == NULL) {
987 0 : DEBUG(SSSDBG_CRIT_FAILURE, "Missing krb5_realm option!\n");
988 0 : return EINVAL;
989 : }
990 :
991 0 : sig_realm = talloc_strdup(krb5_ctx, krb5_realm);
992 0 : if (sig_realm == NULL) {
993 0 : DEBUG(SSSDBG_CRIT_FAILURE, "talloc_strdup failed!\n");
994 0 : return ENOMEM;
995 : }
996 :
997 0 : sige = tevent_add_signal(ev, krb5_ctx, SIGTERM, SA_SIGINFO, krb5_finalize,
998 : sig_realm);
999 0 : if (sige == NULL) {
1000 0 : DEBUG(SSSDBG_CRIT_FAILURE, "tevent_add_signal failed.\n");
1001 0 : talloc_free(sig_realm);
1002 0 : return ENOMEM;
1003 : }
1004 0 : talloc_steal(sige, sig_realm);
1005 :
1006 0 : return EOK;
1007 : }
1008 :
1009 0 : errno_t krb5_get_simple_upn(TALLOC_CTX *mem_ctx, struct krb5_ctx *krb5_ctx,
1010 : struct sss_domain_info *dom, const char *username,
1011 : const char *user_dom, char **_upn)
1012 : {
1013 0 : const char *realm = NULL;
1014 0 : char *uc_dom = NULL;
1015 : char *upn;
1016 : char *name;
1017 0 : TALLOC_CTX *tmp_ctx = NULL;
1018 : errno_t ret;
1019 :
1020 0 : tmp_ctx = talloc_new(NULL);
1021 0 : if (tmp_ctx == NULL) {
1022 0 : DEBUG(SSSDBG_CRIT_FAILURE, "talloc_new failed.\n");
1023 0 : return ENOMEM;
1024 : }
1025 :
1026 0 : if (user_dom != NULL && dom->name != NULL &&
1027 0 : strcasecmp(dom->name, user_dom) != 0) {
1028 0 : uc_dom = get_uppercase_realm(tmp_ctx, user_dom);
1029 0 : if (uc_dom == NULL) {
1030 0 : DEBUG(SSSDBG_OP_FAILURE, "get_uppercase_realm failed.\n");
1031 0 : ret = ENOMEM;
1032 0 : goto done;
1033 : }
1034 : } else {
1035 0 : realm = dp_opt_get_cstring(krb5_ctx->opts, KRB5_REALM);
1036 0 : if (realm == NULL) {
1037 0 : DEBUG(SSSDBG_OP_FAILURE, "Missing Kerberos realm.\n");
1038 0 : ret = ENOMEM;
1039 0 : goto done;
1040 : }
1041 : }
1042 :
1043 : /* Subdomains already have a fully qualified name, which contains
1044 : * the domain name. We need to replace it with the realm name
1045 : */
1046 0 : ret = sss_parse_name(tmp_ctx, dom->names, username, NULL, &name);
1047 0 : if (ret != EOK) {
1048 0 : DEBUG(SSSDBG_OP_FAILURE,
1049 : "Could not parse [%s] into name and "
1050 : "domain components, login might fail\n", username);
1051 0 : name = discard_const(username);
1052 : }
1053 :
1054 : /* NOTE: this is a hack, works only in some environments */
1055 0 : upn = talloc_asprintf(tmp_ctx, "%s@%s", name,
1056 : realm != NULL ? realm : uc_dom);
1057 0 : if (upn == NULL) {
1058 0 : DEBUG(SSSDBG_CRIT_FAILURE, "talloc_asprintf failed.\n");
1059 0 : ret = ENOMEM;
1060 0 : goto done;
1061 : }
1062 :
1063 0 : DEBUG(SSSDBG_TRACE_ALL, "Using simple UPN [%s].\n", upn);
1064 :
1065 0 : *_upn = talloc_steal(mem_ctx, upn);
1066 0 : ret = EOK;
1067 : done:
1068 0 : talloc_free(tmp_ctx);
1069 0 : return ret;
1070 : }
1071 :
1072 10 : errno_t compare_principal_realm(const char *upn, const char *realm,
1073 : bool *different_realm)
1074 : {
1075 : char *at_sign;
1076 :
1077 17 : if (upn == NULL || realm == NULL || different_realm == NULL ||
1078 13 : *upn == '\0' || *realm == '\0') {
1079 5 : return EINVAL;
1080 : }
1081 :
1082 5 : at_sign = strchr(upn, '@');
1083 :
1084 5 : if (at_sign == NULL) {
1085 2 : return EINVAL;
1086 : }
1087 :
1088 3 : if (strcmp(realm, at_sign + 1) == 0) {
1089 1 : *different_realm = false;
1090 : } else {
1091 2 : *different_realm = true;
1092 : }
1093 :
1094 3 : return EOK;
1095 : }
|