Branch data 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/dp_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 : 0 : errno_t check_and_export_lifetime(struct dp_option *opts, const int opt_id,
37 : : const char *env_name)
38 : : {
39 : : int ret;
40 : : char *str;
41 : : krb5_deltat lifetime;
42 : 0 : bool free_str = false;
43 : :
44 : 0 : str = dp_opt_get_string(opts, opt_id);
45 [ # # ][ # # ]: 0 : if (str == NULL || *str == '\0') {
46 [ # # ][ # # ]: 0 : DEBUG(5, ("No lifetime configured.\n"));
[ # # ][ # # ]
[ # # ]
47 : : return EOK;
48 : : }
49 : :
50 [ # # ]: 0 : if (isdigit(str[strlen(str)-1])) {
51 : 0 : str = talloc_asprintf(opts, "%ss", str);
52 [ # # ]: 0 : if (str == NULL) {
53 [ # # ][ # # ]: 0 : DEBUG(1, ("talloc_asprintf failed\n"));
[ # # ][ # # ]
[ # # ]
54 : : return ENOMEM;
55 : : }
56 : 0 : free_str = true;
57 : :
58 : 0 : ret = dp_opt_set_string(opts, opt_id, str);
59 [ # # ]: 0 : if (ret != EOK) {
60 [ # # ][ # # ]: 0 : DEBUG(1, ("dp_opt_set_string failed\n"));
[ # # ][ # # ]
[ # # ]
61 : : goto done;
62 : : }
63 : : }
64 : :
65 : 0 : ret = krb5_string_to_deltat(str, &lifetime);
66 [ # # ]: 0 : if (ret != 0) {
67 [ # # ][ # # ]: 0 : DEBUG(1, ("Invalid value [%s] for a lifetime.\n", str));
[ # # ][ # # ]
[ # # ]
68 : : ret = EINVAL;
69 : : goto done;
70 : : }
71 : :
72 : 0 : ret = setenv(env_name, str, 1);
73 [ # # ]: 0 : if (ret != EOK) {
74 : 0 : ret = errno;
75 [ # # ][ # # ]: 0 : DEBUG(2, ("setenv [%s] failed.\n", env_name));
[ # # ][ # # ]
[ # # ]
76 : : goto done;
77 : : }
78 : :
79 : : ret = EOK;
80 : :
81 : : done:
82 [ # # ]: 0 : if (free_str) {
83 : 0 : talloc_free(str);
84 : : }
85 : :
86 : : return ret;
87 : : }
88 : :
89 : :
90 : 0 : errno_t check_and_export_options(struct dp_option *opts,
91 : : struct sss_domain_info *dom,
92 : : struct krb5_ctx *krb5_ctx)
93 : : {
94 : : int ret;
95 : : const char *realm;
96 : : const char *dummy;
97 : : char *use_fast_str;
98 : : char *fast_principal;
99 : : enum sss_krb5_cc_type cc_be;
100 : :
101 : 0 : realm = dp_opt_get_cstring(opts, KRB5_REALM);
102 [ # # ]: 0 : if (realm == NULL) {
103 : 0 : ret = dp_opt_set_string(opts, KRB5_REALM, dom->name);
104 [ # # ]: 0 : if (ret != EOK) {
105 [ # # ][ # # ]: 0 : DEBUG(1, ("dp_opt_set_string failed.\n"));
[ # # ][ # # ]
[ # # ]
106 : 0 : return ret;
107 : : }
108 : 0 : realm = dom->name;
109 : : }
110 : :
111 : 0 : ret = setenv(SSSD_KRB5_REALM, realm, 1);
112 [ # # ]: 0 : if (ret != EOK) {
113 [ # # ][ # # ]: 0 : DEBUG(2, ("setenv %s failed, authentication might fail.\n",
[ # # ][ # # ]
[ # # ]
114 : : SSSD_KRB5_REALM));
115 : : }
116 : :
117 : 0 : ret = check_and_export_lifetime(opts, KRB5_RENEWABLE_LIFETIME,
118 : : SSSD_KRB5_RENEWABLE_LIFETIME);
119 [ # # ]: 0 : if (ret != EOK) {
120 [ # # ][ # # ]: 0 : DEBUG(1, ("Failed to check value of krb5_renewable_lifetime. [%d][%s]\n",
[ # # ][ # # ]
[ # # ]
121 : : ret, strerror(ret)));
122 : 0 : return ret;
123 : : }
124 : :
125 : 0 : ret = check_and_export_lifetime(opts, KRB5_LIFETIME,
126 : : SSSD_KRB5_LIFETIME);
127 [ # # ]: 0 : if (ret != EOK) {
128 [ # # ][ # # ]: 0 : DEBUG(1, ("Failed to check value of krb5_lifetime. [%d][%s]\n",
[ # # ][ # # ]
[ # # ]
129 : : ret, strerror(ret)));
130 : 0 : return ret;
131 : : }
132 : :
133 : :
134 : 0 : use_fast_str = dp_opt_get_string(opts, KRB5_USE_FAST);
135 [ # # ]: 0 : if (use_fast_str != NULL) {
136 : 0 : ret = check_fast(use_fast_str, &krb5_ctx->use_fast);
137 [ # # ]: 0 : if (ret != EOK) {
138 [ # # ][ # # ]: 0 : DEBUG(1, ("check_fast failed.\n"));
[ # # ][ # # ]
[ # # ]
139 : 0 : return ret;
140 : : }
141 : :
142 [ # # ]: 0 : if (krb5_ctx->use_fast) {
143 : 0 : ret = setenv(SSSD_KRB5_USE_FAST, use_fast_str, 1);
144 [ # # ]: 0 : if (ret != EOK) {
145 [ # # ][ # # ]: 0 : DEBUG(2, ("setenv [%s] failed.\n", SSSD_KRB5_USE_FAST));
[ # # ][ # # ]
[ # # ]
146 : : } else {
147 : 0 : fast_principal = dp_opt_get_string(opts, KRB5_FAST_PRINCIPAL);
148 [ # # ]: 0 : if (fast_principal != NULL) {
149 : 0 : ret = setenv(SSSD_KRB5_FAST_PRINCIPAL, fast_principal, 1);
150 [ # # ]: 0 : if (ret != EOK) {
151 [ # # ][ # # ]: 0 : DEBUG(2, ("setenv [%s] failed.\n", SSSD_KRB5_FAST_PRINCIPAL));
[ # # ][ # # ]
[ # # ]
152 : : }
153 : : }
154 : : }
155 : : }
156 : : }
157 : :
158 [ # # ]: 0 : if (dp_opt_get_bool(opts, KRB5_CANONICALIZE)) {
159 : 0 : ret = setenv(SSSD_KRB5_CANONICALIZE, "true", 1);
160 : : } else {
161 : 0 : ret = setenv(SSSD_KRB5_CANONICALIZE, "false", 1);
162 : : }
163 [ # # ]: 0 : if (ret != EOK) {
164 [ # # ][ # # ]: 0 : DEBUG(2, ("setenv [%s] failed.\n", SSSD_KRB5_CANONICALIZE));
[ # # ][ # # ]
[ # # ]
165 : : }
166 : :
167 : 0 : dummy = dp_opt_get_cstring(opts, KRB5_KDC);
168 [ # # ]: 0 : if (dummy == NULL) {
169 [ # # ][ # # ]: 0 : DEBUG(SSSDBG_CONF_SETTINGS, ("No KDC explicitly configured, using defaults.\n"));
[ # # ][ # # ]
[ # # ]
170 : : }
171 : :
172 : 0 : dummy = dp_opt_get_cstring(opts, KRB5_KPASSWD);
173 [ # # ]: 0 : if (dummy == NULL) {
174 [ # # ][ # # ]: 0 : DEBUG(SSSDBG_CONF_SETTINGS, ("No kpasswd server explicitly configured, "
[ # # ][ # # ]
[ # # ]
175 : : "using the KDC or defaults.\n"));
176 : : }
177 : :
178 : 0 : dummy = dp_opt_get_cstring(opts, KRB5_CCNAME_TMPL);
179 [ # # ]: 0 : if (dummy == NULL) {
180 [ # # ][ # # ]: 0 : DEBUG(1, ("Missing credential cache name template.\n"));
[ # # ][ # # ]
[ # # ]
181 : : return EINVAL;
182 : : }
183 : :
184 : 0 : cc_be = sss_krb5_get_type(dummy);
185 [ # # # ]: 0 : switch (cc_be) {
186 : : case SSS_KRB5_TYPE_FILE:
187 [ # # ][ # # ]: 0 : DEBUG(SSSDBG_CONF_SETTINGS, ("ccache is of type FILE\n"));
[ # # ][ # # ]
[ # # ]
188 : 0 : krb5_ctx->cc_be = &file_cc;
189 [ # # ]: 0 : if (dummy[0] != '/') {
190 : : /* FILE:/path/to/cc */
191 : : break;
192 : : }
193 : :
194 [ # # ][ # # ]: 0 : DEBUG(SSSDBG_CONF_SETTINGS, ("The ccname template was "
[ # # ][ # # ]
[ # # ]
195 : : "missing an explicit type, but is an absolute "
196 : : "path specifier. Assuming FILE:\n"));
197 : :
198 : 0 : dummy = talloc_asprintf(opts, "FILE:%s", dummy);
199 [ # # ]: 0 : if (!dummy) return ENOMEM;
200 : :
201 : 0 : ret = dp_opt_set_string(opts, KRB5_CCNAME_TMPL, dummy);
202 [ # # ]: 0 : if (ret != EOK) {
203 [ # # ][ # # ]: 0 : DEBUG(SSSDBG_CRIT_FAILURE, ("dp_opt_set_string failed.\n"));
[ # # ][ # # ]
[ # # ]
204 : 0 : return ret;
205 : : }
206 : : break;
207 : :
208 : : #ifdef HAVE_KRB5_DIRCACHE
209 : : case SSS_KRB5_TYPE_DIR:
210 [ # # ][ # # ]: 0 : DEBUG(SSSDBG_CONF_SETTINGS, ("ccache is of type DIR\n"));
[ # # ][ # # ]
[ # # ]
211 : 0 : krb5_ctx->cc_be = &dir_cc;
212 : 0 : break;
213 : : #endif
214 : :
215 : : default:
216 [ # # ][ # # ]: 0 : DEBUG(SSSDBG_OP_FAILURE, ("Unknown ccname database\n"));
[ # # ][ # # ]
[ # # ]
217 : : return EINVAL;
218 : : break;
219 : : }
220 : :
221 : : return EOK;
222 : : }
223 : :
224 : 0 : errno_t krb5_try_kdcip(struct confdb_ctx *cdb, const char *conf_path,
225 : : struct dp_option *opts, int opt_id)
226 : : {
227 : 0 : char *krb5_servers = NULL;
228 : : errno_t ret;
229 : :
230 : 0 : krb5_servers = dp_opt_get_string(opts, opt_id);
231 [ # # ]: 0 : if (krb5_servers == NULL) {
232 [ # # ][ # # ]: 0 : DEBUG(4, ("No KDC found in configuration, trying legacy option\n"));
[ # # ][ # # ]
[ # # ]
233 : 0 : ret = confdb_get_string(cdb, NULL, conf_path,
234 : : "krb5_kdcip", NULL, &krb5_servers);
235 [ # # ]: 0 : if (ret != EOK) {
236 [ # # ][ # # ]: 0 : DEBUG(1, ("confdb_get_string failed.\n"));
[ # # ][ # # ]
[ # # ]
237 : : return ret;
238 : : }
239 : :
240 [ # # ]: 0 : if (krb5_servers != NULL)
241 : : {
242 : 0 : ret = dp_opt_set_string(opts, opt_id, krb5_servers);
243 [ # # ]: 0 : if (ret != EOK) {
244 [ # # ][ # # ]: 0 : DEBUG(1, ("dp_opt_set_string failed.\n"));
[ # # ][ # # ]
[ # # ]
245 : 0 : talloc_free(krb5_servers);
246 : : return ret;
247 : : }
248 : :
249 [ # # ][ # # ]: 0 : DEBUG(SSSDBG_CONF_SETTINGS,
[ # # ][ # # ]
[ # # ]
250 : : ("Set krb5 server [%s] based on legacy krb5_kdcip option\n",
251 : : krb5_servers));
252 [ # # ][ # # ]: 0 : DEBUG(SSSDBG_FATAL_FAILURE,
[ # # ][ # # ]
[ # # ]
253 : : ("Your configuration uses the deprecated option "
254 : : "'krb5_kdcip' to specify the KDC. Please change the "
255 : : "configuration to use the 'krb5_server' option "
256 : : "instead.\n"));
257 : 0 : talloc_free(krb5_servers);
258 : : }
259 : : }
260 : :
261 : : return EOK;
262 : : }
263 : :
264 : 0 : errno_t krb5_get_options(TALLOC_CTX *memctx, struct confdb_ctx *cdb,
265 : : const char *conf_path, struct dp_option **_opts)
266 : : {
267 : : int ret;
268 : : struct dp_option *opts;
269 : :
270 : 0 : opts = talloc_zero(memctx, struct dp_option);
271 [ # # ]: 0 : if (opts == NULL) {
272 [ # # ][ # # ]: 0 : DEBUG(1, ("talloc_zero failed.\n"));
[ # # ][ # # ]
[ # # ]
273 : : return ENOMEM;
274 : : }
275 : :
276 : 0 : ret = dp_get_options(opts, cdb, conf_path, default_krb5_opts,
277 : : KRB5_OPTS, &opts);
278 [ # # ]: 0 : if (ret != EOK) {
279 [ # # ][ # # ]: 0 : DEBUG(1, ("dp_get_options failed.\n"));
[ # # ][ # # ]
[ # # ]
280 : : goto done;
281 : : }
282 : :
283 : : /* If there is no KDC, try the deprecated krb5_kdcip option, too */
284 : : /* FIXME - this can be removed in a future version */
285 : 0 : ret = krb5_try_kdcip(cdb, conf_path, opts, KRB5_KDC);
286 [ # # ]: 0 : if (ret != EOK) {
287 [ # # ][ # # ]: 0 : DEBUG(1, ("sss_krb5_try_kdcip failed.\n"));
[ # # ][ # # ]
[ # # ]
288 : : goto done;
289 : : }
290 : :
291 : 0 : *_opts = opts;
292 : 0 : ret = EOK;
293 : :
294 : : done:
295 [ # # ]: 0 : if (ret != EOK) {
296 : 0 : talloc_zfree(opts);
297 : : }
298 : :
299 : : return ret;
300 : : }
301 : :
302 : 0 : errno_t write_krb5info_file(const char *realm, const char *server,
303 : : const char *service)
304 : : {
305 : : int ret;
306 : 0 : int fd = -1;
307 : 0 : char *tmp_name = NULL;
308 : 0 : char *krb5info_name = NULL;
309 : 0 : TALLOC_CTX *tmp_ctx = NULL;
310 : 0 : const char *name_tmpl = NULL;
311 : : int server_len;
312 : : ssize_t written;
313 : : mode_t old_umask;
314 : :
315 [ # # ][ # # ]: 0 : if (realm == NULL || *realm == '\0' || server == NULL || *server == '\0' ||
[ # # ][ # # ]
316 [ # # ]: 0 : service == NULL || service == '\0') {
317 [ # # ][ # # ]: 0 : DEBUG(1, ("Missing or empty realm, server or service.\n"));
[ # # ][ # # ]
[ # # ]
318 : : return EINVAL;
319 : : }
320 : :
321 [ # # ]: 0 : if (strcmp(service, SSS_KRB5KDC_FO_SRV) == 0) {
322 : : name_tmpl = KDCINFO_TMPL;
323 [ # # ]: 0 : } else if (strcmp(service, SSS_KRB5KPASSWD_FO_SRV) == 0) {
324 : : name_tmpl = KPASSWDINFO_TMPL;
325 : : } else {
326 [ # # ][ # # ]: 0 : DEBUG(1, ("Unsupported service [%s]\n.", service));
[ # # ][ # # ]
[ # # ]
327 : : return EINVAL;
328 : : }
329 : :
330 : 0 : server_len = strlen(server);
331 : :
332 : 0 : tmp_ctx = talloc_new(NULL);
333 [ # # ]: 0 : if (tmp_ctx == NULL) {
334 [ # # ][ # # ]: 0 : DEBUG(1, ("talloc_new failed.\n"));
[ # # ][ # # ]
[ # # ]
335 : : return ENOMEM;
336 : : }
337 : :
338 : 0 : tmp_name = talloc_asprintf(tmp_ctx, PUBCONF_PATH"/.krb5info_dummy_XXXXXX");
339 [ # # ]: 0 : if (tmp_name == NULL) {
340 [ # # ][ # # ]: 0 : DEBUG(1, ("talloc_asprintf failed.\n"));
[ # # ][ # # ]
[ # # ]
341 : : ret = ENOMEM;
342 : : goto done;
343 : : }
344 : :
345 : 0 : krb5info_name = talloc_asprintf(tmp_ctx, name_tmpl, realm);
346 [ # # ]: 0 : if (krb5info_name == NULL) {
347 [ # # ][ # # ]: 0 : DEBUG(1, ("talloc_asprintf failed.\n"));
[ # # ][ # # ]
[ # # ]
348 : : ret = ENOMEM;
349 : : goto done;
350 : : }
351 : :
352 : 0 : old_umask = umask(077);
353 : 0 : fd = mkstemp(tmp_name);
354 : 0 : umask(old_umask);
355 [ # # ]: 0 : if (fd == -1) {
356 : 0 : ret = errno;
357 [ # # ][ # # ]: 0 : DEBUG(1, ("mkstemp failed [%d][%s].\n", ret, strerror(ret)));
[ # # ][ # # ]
[ # # ]
358 : : goto done;
359 : : }
360 : :
361 : 0 : errno = 0;
362 : 0 : written = sss_atomic_write_s(fd, discard_const(server), server_len);
363 [ # # ]: 0 : if (written == -1) {
364 : 0 : ret = errno;
365 [ # # ][ # # ]: 0 : DEBUG(SSSDBG_CRIT_FAILURE,
[ # # ][ # # ]
[ # # ]
366 : : ("write failed [%d][%s].\n", ret, strerror(ret)));
367 : : goto done;
368 : : }
369 : :
370 [ # # ]: 0 : if (written != server_len) {
371 [ # # ][ # # ]: 0 : DEBUG(SSSDBG_CRIT_FAILURE,
[ # # ][ # # ]
[ # # ]
372 : : ("Write error, wrote [%d] bytes, expected [%d]\n",
373 : : written, server_len));
374 : : ret = EIO;
375 : : goto done;
376 : : }
377 : :
378 : 0 : ret = fchmod(fd, S_IRUSR|S_IWUSR|S_IRGRP|S_IROTH);
379 [ # # ]: 0 : if (ret == -1) {
380 : 0 : ret = errno;
381 [ # # ][ # # ]: 0 : DEBUG(1, ("fchmod failed [%d][%s].\n", ret, strerror(ret)));
[ # # ][ # # ]
[ # # ]
382 : : goto done;
383 : : }
384 : :
385 : 0 : ret = close(fd);
386 [ # # ]: 0 : if (ret == -1) {
387 : 0 : ret = errno;
388 [ # # ][ # # ]: 0 : DEBUG(1, ("close failed [%d][%s].\n", ret, strerror(ret)));
[ # # ][ # # ]
[ # # ]
389 : : goto done;
390 : : }
391 : :
392 : 0 : ret = rename(tmp_name, krb5info_name);
393 [ # # ]: 0 : if (ret == -1) {
394 : 0 : ret = errno;
395 [ # # ][ # # ]: 0 : DEBUG(1, ("rename failed [%d][%s].\n", ret, strerror(ret)));
[ # # ][ # # ]
[ # # ]
396 : : goto done;
397 : : }
398 : :
399 : : done:
400 : 0 : talloc_free(tmp_ctx);
401 : 0 : return ret;
402 : : }
403 : :
404 : 0 : static void krb5_resolve_callback(void *private_data, struct fo_server *server)
405 : : {
406 : : struct krb5_service *krb5_service;
407 : : struct resolv_hostent *srvaddr;
408 : : char *address;
409 : : char *safe_address;
410 : : int ret;
411 : 0 : TALLOC_CTX *tmp_ctx = NULL;
412 : :
413 : 0 : tmp_ctx = talloc_new(NULL);
414 [ # # ]: 0 : if (tmp_ctx == NULL) {
415 [ # # ][ # # ]: 0 : DEBUG(1, ("talloc_new failed\n"));
[ # # ][ # # ]
[ # # ]
416 : : return;
417 : : }
418 : :
419 : 0 : krb5_service = talloc_get_type(private_data, struct krb5_service);
420 [ # # ]: 0 : if (!krb5_service) {
421 [ # # ][ # # ]: 0 : DEBUG(1, ("FATAL: Bad private_data\n"));
[ # # ][ # # ]
[ # # ]
422 : 0 : talloc_free(tmp_ctx);
423 : 0 : return;
424 : : }
425 : :
426 : 0 : srvaddr = fo_get_server_hostent(server);
427 [ # # ]: 0 : if (!srvaddr) {
428 [ # # ][ # # ]: 0 : DEBUG(1, ("FATAL: No hostent available for server (%s)\n",
[ # # ][ # # ]
[ # # ]
429 : : fo_get_server_str_name(server)));
430 : 0 : talloc_free(tmp_ctx);
431 : 0 : return;
432 : : }
433 : :
434 : 0 : address = resolv_get_string_address(tmp_ctx, srvaddr);
435 [ # # ]: 0 : if (address == NULL) {
436 [ # # ][ # # ]: 0 : DEBUG(1, ("resolv_get_string_address failed.\n"));
[ # # ][ # # ]
[ # # ]
437 : 0 : talloc_free(tmp_ctx);
438 : 0 : return;
439 : : }
440 : :
441 : 0 : safe_address = sss_escape_ip_address(tmp_ctx,
442 : : srvaddr->family,
443 : : address);
444 [ # # ]: 0 : if (safe_address == NULL) {
445 [ # # ][ # # ]: 0 : DEBUG(1, ("sss_escape_ip_address failed.\n"));
[ # # ][ # # ]
[ # # ]
446 : 0 : talloc_free(tmp_ctx);
447 : 0 : return;
448 : : }
449 : :
450 : 0 : safe_address = talloc_asprintf_append(safe_address, ":%d",
451 : : fo_get_server_port(server));
452 [ # # ]: 0 : if (safe_address == NULL) {
453 [ # # ][ # # ]: 0 : DEBUG(1, ("talloc_asprintf_append failed.\n"));
[ # # ][ # # ]
[ # # ]
454 : 0 : talloc_free(tmp_ctx);
455 : 0 : return;
456 : : }
457 : :
458 : 0 : ret = write_krb5info_file(krb5_service->realm, safe_address,
459 : 0 : krb5_service->name);
460 [ # # ]: 0 : if (ret != EOK) {
461 [ # # ][ # # ]: 0 : DEBUG(2, ("write_krb5info_file failed, authentication might fail.\n"));
[ # # ][ # # ]
[ # # ]
462 : : }
463 : :
464 : 0 : talloc_free(tmp_ctx);
465 : 0 : return;
466 : : }
467 : :
468 : 0 : errno_t krb5_servers_init(struct be_ctx *ctx,
469 : : struct krb5_service *service,
470 : : const char *service_name,
471 : : const char *servers,
472 : : bool primary)
473 : : {
474 : : TALLOC_CTX *tmp_ctx;
475 : 0 : char **list = NULL;
476 : 0 : errno_t ret = 0;
477 : : int i;
478 : : char *port_str;
479 : : long port;
480 : : char *server_spec;
481 : : char *endptr;
482 : : struct servent *servent;
483 : :
484 : 0 : tmp_ctx = talloc_new(NULL);
485 [ # # ]: 0 : if (!tmp_ctx) {
486 : : return ENOMEM;
487 : : }
488 : :
489 : 0 : ret = split_on_separator(tmp_ctx, servers, ',', true, &list, NULL);
490 [ # # ]: 0 : if (ret != EOK) {
491 [ # # ][ # # ]: 0 : DEBUG(SSSDBG_CRIT_FAILURE, ("Failed to parse server list!\n"));
[ # # ][ # # ]
[ # # ]
492 : : goto done;
493 : : }
494 : :
495 [ # # ]: 0 : for (i = 0; list[i]; i++) {
496 : 0 : talloc_steal(service, list[i]);
497 : 0 : server_spec = talloc_strdup(service, list[i]);
498 [ # # ]: 0 : if (!server_spec) {
499 : : ret = ENOMEM;
500 : : goto done;
501 : : }
502 : :
503 [ # # ]: 0 : if (be_fo_is_srv_identifier(server_spec)) {
504 [ # # ]: 0 : if (!primary) {
505 [ # # ][ # # ]: 0 : DEBUG(SSSDBG_MINOR_FAILURE,
[ # # ][ # # ]
[ # # ]
506 : : ("Failed to add server [%s] to failover service: "
507 : : "SRV resolution only allowed for primary servers!\n",
508 : : list[i]));
509 : 0 : continue;
510 : : }
511 : :
512 : 0 : ret = be_fo_add_srv_server(ctx, service_name, service_name, NULL,
513 : : BE_FO_PROTO_UDP, true, NULL);
514 [ # # ]: 0 : if (ret) {
515 [ # # ][ # # ]: 0 : DEBUG(SSSDBG_FATAL_FAILURE, ("Failed to add server\n"));
[ # # ][ # # ]
[ # # ]
516 : : goto done;
517 : : }
518 : :
519 [ # # ][ # # ]: 0 : DEBUG(SSSDBG_TRACE_FUNC, ("Added service lookup\n"));
[ # # ][ # # ]
[ # # ]
520 : 0 : continue;
521 : : }
522 : :
523 : : /* Do not try to get port number if last character is ']' */
524 [ # # ]: 0 : if (server_spec[strlen(server_spec) - 1] != ']') {
525 : 0 : port_str = strrchr(server_spec, ':');
526 : : } else {
527 : : port_str = NULL;
528 : : }
529 : :
530 [ # # ]: 0 : if (port_str == NULL) {
531 : : port = 0;
532 : : } else {
533 : 0 : *port_str = '\0';
534 : 0 : ++port_str;
535 [ # # ]: 0 : if (isdigit(*port_str)) {
536 : 0 : errno = 0;
537 : 0 : port = strtol(port_str, &endptr, 10);
538 [ # # ]: 0 : if (errno != 0) {
539 : 0 : ret = errno;
540 [ # # ][ # # ]: 0 : DEBUG(SSSDBG_CRIT_FAILURE, ("strtol failed on [%s]: [%d][%s].\n", port_str,
[ # # ][ # # ]
[ # # ]
541 : : ret, strerror(ret)));
542 : : goto done;
543 : : }
544 [ # # ]: 0 : if (*endptr != '\0') {
545 [ # # ][ # # ]: 0 : DEBUG(SSSDBG_CRIT_FAILURE, ("Found additional characters [%s] in port number "
[ # # ][ # # ]
[ # # ]
546 : : "[%s].\n", endptr, port_str));
547 : : ret = EINVAL;
548 : : goto done;
549 : : }
550 : :
551 [ # # ]: 0 : if (port < 1 || port > 65535) {
552 [ # # ][ # # ]: 0 : DEBUG(SSSDBG_CRIT_FAILURE, ("Illegal port number [%d].\n", port));
[ # # ][ # # ]
[ # # ]
553 : : ret = EINVAL;
554 : : goto done;
555 : : }
556 [ # # ]: 0 : } else if (isalpha(*port_str)) {
557 : 0 : servent = getservbyname(port_str, NULL);
558 [ # # ]: 0 : if (servent == NULL) {
559 [ # # ][ # # ]: 0 : DEBUG(SSSDBG_CRIT_FAILURE, ("getservbyname cannot find service [%s].\n",
[ # # ][ # # ]
[ # # ]
560 : : port_str));
561 : : ret = EINVAL;
562 : : goto done;
563 : : }
564 : :
565 : 0 : port = servent->s_port;
566 : : } else {
567 [ # # ][ # # ]: 0 : DEBUG(SSSDBG_CRIT_FAILURE, ("Unsupported port specifier in [%s].\n", list[i]));
[ # # ][ # # ]
[ # # ]
568 : : ret = EINVAL;
569 : : goto done;
570 : : }
571 : : }
572 : :
573 : : /* It could be ipv6 address in square brackets. Remove
574 : : * the brackets if needed. */
575 : 0 : ret = remove_ipv6_brackets(server_spec);
576 [ # # ]: 0 : if (ret != EOK) {
577 : : goto done;
578 : : }
579 : :
580 : 0 : ret = be_fo_add_server(ctx, service_name, server_spec, (int) port,
581 : 0 : list[i], primary);
582 [ # # ]: 0 : if (ret && ret != EEXIST) {
583 [ # # ][ # # ]: 0 : DEBUG(SSSDBG_FATAL_FAILURE, ("Failed to add server\n"));
[ # # ][ # # ]
[ # # ]
584 : : goto done;
585 : : }
586 : :
587 [ # # ][ # # ]: 0 : DEBUG(SSSDBG_TRACE_FUNC, ("Added Server %s\n", list[i]));
[ # # ][ # # ]
[ # # ]
588 : : }
589 : :
590 : : done:
591 : 0 : talloc_free(tmp_ctx);
592 : : return ret;
593 : : }
594 : :
595 : 0 : static int krb5_user_data_cmp(void *ud1, void *ud2)
596 : : {
597 : 0 : return strcasecmp((char*) ud1, (char*) ud2);
598 : : }
599 : :
600 : 0 : int krb5_service_init(TALLOC_CTX *memctx, struct be_ctx *ctx,
601 : : const char *service_name,
602 : : const char *primary_servers,
603 : : const char *backup_servers,
604 : : const char *realm, struct krb5_service **_service)
605 : : {
606 : : TALLOC_CTX *tmp_ctx;
607 : : struct krb5_service *service;
608 : : int ret;
609 : :
610 : 0 : tmp_ctx = talloc_new(memctx);
611 [ # # ]: 0 : if (!tmp_ctx) {
612 : : return ENOMEM;
613 : : }
614 : :
615 : 0 : service = talloc_zero(tmp_ctx, struct krb5_service);
616 [ # # ]: 0 : if (!service) {
617 : : ret = ENOMEM;
618 : : goto done;
619 : : }
620 : :
621 : 0 : ret = be_fo_add_service(ctx, service_name, krb5_user_data_cmp);
622 [ # # ]: 0 : if (ret != EOK) {
623 [ # # ][ # # ]: 0 : DEBUG(1, ("Failed to create failover service!\n"));
[ # # ][ # # ]
[ # # ]
624 : : goto done;
625 : : }
626 : :
627 : 0 : service->name = talloc_strdup(service, service_name);
628 [ # # ]: 0 : if (!service->name) {
629 : : ret = ENOMEM;
630 : : goto done;
631 : : }
632 : :
633 : 0 : service->realm = talloc_strdup(service, realm);
634 [ # # ]: 0 : if (!service->realm) {
635 : : ret = ENOMEM;
636 : : goto done;
637 : : }
638 : :
639 [ # # ]: 0 : if (!primary_servers) {
640 [ # # ][ # # ]: 0 : DEBUG(SSSDBG_CONF_SETTINGS,
[ # # ][ # # ]
[ # # ]
641 : : ("No primary servers defined, using service discovery\n"));
642 : : primary_servers = BE_SRV_IDENTIFIER;
643 : : }
644 : :
645 : 0 : ret = krb5_servers_init(ctx, service, service_name, primary_servers, true);
646 [ # # ]: 0 : if (ret != EOK) {
647 : : goto done;
648 : : }
649 : :
650 [ # # ]: 0 : if (backup_servers) {
651 : 0 : ret = krb5_servers_init(ctx, service, service_name, backup_servers, false);
652 [ # # ]: 0 : if (ret != EOK) {
653 : : goto done;
654 : : }
655 : : }
656 : :
657 : 0 : ret = be_fo_service_add_callback(memctx, ctx, service_name,
658 : : krb5_resolve_callback, service);
659 [ # # ]: 0 : if (ret != EOK) {
660 [ # # ][ # # ]: 0 : DEBUG(1, ("Failed to add failover callback!\n"));
[ # # ][ # # ]
[ # # ]
661 : : goto done;
662 : : }
663 : :
664 : : ret = EOK;
665 : :
666 : : done:
667 [ # # ]: 0 : if (ret == EOK) {
668 : 0 : *_service = talloc_steal(memctx, service);
669 : : }
670 : 0 : talloc_zfree(tmp_ctx);
671 : 0 : return ret;
672 : : }
673 : :
674 : :
675 : 0 : errno_t remove_krb5_info_files(TALLOC_CTX *mem_ctx, const char *realm)
676 : : {
677 : : int ret;
678 : : errno_t err;
679 : : char *file;
680 : :
681 : 0 : file = talloc_asprintf(mem_ctx, KDCINFO_TMPL, realm);
682 [ # # ]: 0 : if(file == NULL) {
683 [ # # ][ # # ]: 0 : DEBUG(1, ("talloc_asprintf failed.\n"));
[ # # ][ # # ]
[ # # ]
684 : : return ENOMEM;
685 : : }
686 : :
687 : 0 : errno = 0;
688 : 0 : ret = unlink(file);
689 [ # # ]: 0 : if (ret == -1) {
690 : 0 : err = errno;
691 [ # # ][ # # ]: 0 : DEBUG(5, ("Could not remove [%s], [%d][%s]\n", file,
[ # # ][ # # ]
[ # # ]
692 : : err, strerror(err)));
693 : : }
694 : :
695 : 0 : file = talloc_asprintf(mem_ctx, KPASSWDINFO_TMPL, realm);
696 [ # # ]: 0 : if(file == NULL) {
697 [ # # ][ # # ]: 0 : DEBUG(1, ("talloc_asprintf failed.\n"));
[ # # ][ # # ]
[ # # ]
698 : : return ENOMEM;
699 : : }
700 : :
701 : 0 : errno = 0;
702 : 0 : ret = unlink(file);
703 [ # # ]: 0 : if (ret == -1) {
704 : 0 : err = errno;
705 [ # # ][ # # ]: 0 : DEBUG(5, ("Could not remove [%s], [%d][%s]\n", file,
[ # # ][ # # ]
[ # # ]
706 : : err, strerror(err)));
707 : : }
708 : :
709 : : return EOK;
710 : : }
711 : :
712 : 0 : void remove_krb5_info_files_callback(void *pvt)
713 : : {
714 : : int ret;
715 : 0 : TALLOC_CTX *tmp_ctx = NULL;
716 : 0 : struct remove_info_files_ctx *ctx = talloc_get_type(pvt,
717 : : struct remove_info_files_ctx);
718 : :
719 : 0 : ret = be_fo_run_callbacks_at_next_request(ctx->be_ctx,
720 : : ctx->kdc_service_name);
721 [ # # ]: 0 : if (ret != EOK) {
722 [ # # ][ # # ]: 0 : DEBUG(1, ("be_fo_run_callbacks_at_next_request failed, "
[ # # ][ # # ]
[ # # ]
723 : : "krb5 info files will not be removed, because "
724 : : "it is unclear if they will be recreated properly.\n"));
725 : : return;
726 : : }
727 [ # # ]: 0 : if (ctx->kpasswd_service_name != NULL) {
728 : 0 : ret = be_fo_run_callbacks_at_next_request(ctx->be_ctx,
729 : : ctx->kpasswd_service_name);
730 [ # # ]: 0 : if (ret != EOK) {
731 [ # # ][ # # ]: 0 : DEBUG(1, ("be_fo_run_callbacks_at_next_request failed, "
[ # # ][ # # ]
[ # # ]
732 : : "krb5 info files will not be removed, because "
733 : : "it is unclear if they will be recreated properly.\n"));
734 : : return;
735 : : }
736 : : }
737 : :
738 : 0 : tmp_ctx = talloc_new(NULL);
739 [ # # ]: 0 : if (tmp_ctx == NULL) {
740 [ # # ][ # # ]: 0 : DEBUG(1, ("talloc_new failed, cannot remove krb5 info files.\n"));
[ # # ][ # # ]
[ # # ]
741 : : return;
742 : : }
743 : :
744 : 0 : ret = remove_krb5_info_files(tmp_ctx, ctx->realm);
745 [ # # ]: 0 : if (ret != EOK) {
746 [ # # ][ # # ]: 0 : DEBUG(1, ("remove_krb5_info_files failed.\n"));
[ # # ][ # # ]
[ # # ]
747 : : }
748 : :
749 : 0 : talloc_zfree(tmp_ctx);
750 : : }
751 : :
752 : 0 : void krb5_finalize(struct tevent_context *ev,
753 : : struct tevent_signal *se,
754 : : int signum,
755 : : int count,
756 : : void *siginfo,
757 : : void *private_data)
758 : : {
759 : 0 : char *realm = (char *)private_data;
760 : : int ret;
761 : :
762 : 0 : ret = remove_krb5_info_files(se, realm);
763 [ # # ]: 0 : if (ret != EOK) {
764 [ # # ][ # # ]: 0 : DEBUG(1, ("remove_krb5_info_files failed.\n"));
[ # # ][ # # ]
[ # # ]
765 : : }
766 : :
767 : 0 : sig_term(signum);
768 : 0 : }
769 : :
770 : 0 : errno_t krb5_install_offline_callback(struct be_ctx *be_ctx,
771 : : struct krb5_ctx *krb5_ctx)
772 : : {
773 : : int ret;
774 : : struct remove_info_files_ctx *ctx;
775 : : const char *krb5_realm;
776 : :
777 [ # # ][ # # ]: 0 : if (krb5_ctx->service == NULL || krb5_ctx->service->name == NULL) {
778 [ # # ][ # # ]: 0 : DEBUG(1, ("Missing KDC service name!\n"));
[ # # ][ # # ]
[ # # ]
779 : : return EINVAL;
780 : : }
781 : :
782 : 0 : ctx = talloc_zero(krb5_ctx, struct remove_info_files_ctx);
783 [ # # ]: 0 : if (ctx == NULL) {
784 [ # # ][ # # ]: 0 : DEBUG(1, ("talloc_zfree failed.\n"));
[ # # ][ # # ]
[ # # ]
785 : : return ENOMEM;
786 : : }
787 : :
788 : 0 : krb5_realm = dp_opt_get_cstring(krb5_ctx->opts, KRB5_REALM);
789 [ # # ]: 0 : if (krb5_realm == NULL) {
790 [ # # ][ # # ]: 0 : DEBUG(1, ("Missing krb5_realm option!\n"));
[ # # ][ # # ]
[ # # ]
791 : : ret = EINVAL;
792 : : goto done;
793 : : }
794 : :
795 : 0 : ctx->realm = talloc_strdup(ctx, krb5_realm);
796 [ # # ]: 0 : if (ctx->realm == NULL) {
797 [ # # ][ # # ]: 0 : DEBUG(1, ("talloc_strdup failed!\n"));
[ # # ][ # # ]
[ # # ]
798 : : ret = ENOMEM;
799 : : goto done;
800 : : }
801 : :
802 : 0 : ctx->be_ctx = be_ctx;
803 : 0 : ctx->kdc_service_name = krb5_ctx->service->name;
804 [ # # ]: 0 : if (krb5_ctx->kpasswd_service == NULL) {
805 : 0 : ctx->kpasswd_service_name =NULL;
806 : : } else {
807 : 0 : ctx->kpasswd_service_name = krb5_ctx->kpasswd_service->name;
808 : : }
809 : :
810 : 0 : ret = be_add_offline_cb(ctx, be_ctx, remove_krb5_info_files_callback, ctx,
811 : : NULL);
812 [ # # ]: 0 : if (ret != EOK) {
813 [ # # ][ # # ]: 0 : DEBUG(1, ("be_add_offline_cb failed.\n"));
[ # # ][ # # ]
[ # # ]
814 : : goto done;
815 : : }
816 : :
817 : : ret = EOK;
818 : :
819 : : done:
820 [ # # ]: 0 : if (ret != EOK) {
821 : 0 : talloc_zfree(ctx);
822 : : }
823 : :
824 : 0 : return ret;
825 : : }
826 : :
827 : 0 : errno_t krb5_install_sigterm_handler(struct tevent_context *ev,
828 : : struct krb5_ctx *krb5_ctx)
829 : : {
830 : : const char *krb5_realm;
831 : : char *sig_realm;
832 : : struct tevent_signal *sige;
833 : :
834 : 0 : BlockSignals(false, SIGTERM);
835 : :
836 : 0 : krb5_realm = dp_opt_get_cstring(krb5_ctx->opts, KRB5_REALM);
837 [ # # ]: 0 : if (krb5_realm == NULL) {
838 [ # # ][ # # ]: 0 : DEBUG(1, ("Missing krb5_realm option!\n"));
[ # # ][ # # ]
[ # # ]
839 : : return EINVAL;
840 : : }
841 : :
842 : 0 : sig_realm = talloc_strdup(krb5_ctx, krb5_realm);
843 [ # # ]: 0 : if (sig_realm == NULL) {
844 [ # # ][ # # ]: 0 : DEBUG(1, ("talloc_strdup failed!\n"));
[ # # ][ # # ]
[ # # ]
845 : : return ENOMEM;
846 : : }
847 : :
848 : 0 : sige = tevent_add_signal(ev, krb5_ctx, SIGTERM, SA_SIGINFO, krb5_finalize,
849 : : sig_realm);
850 [ # # ]: 0 : if (sige == NULL) {
851 [ # # ][ # # ]: 0 : DEBUG(1, ("tevent_add_signal failed.\n"));
[ # # ][ # # ]
[ # # ]
852 : 0 : talloc_free(sig_realm);
853 : 0 : return ENOMEM;
854 : : }
855 : 0 : talloc_steal(sige, sig_realm);
856 : :
857 : 0 : return EOK;
858 : : }
859 : :
860 : 0 : errno_t krb5_get_simple_upn(TALLOC_CTX *mem_ctx, struct krb5_ctx *krb5_ctx,
861 : : const char *domain_name, const char *username,
862 : : const char *user_dom, char **_upn)
863 : : {
864 : 0 : const char *realm = NULL;
865 : 0 : char *uc_dom = NULL;
866 : : char *upn;
867 : :
868 [ # # ][ # # ]: 0 : if (user_dom != NULL && domain_name != NULL &&
869 : 0 : strcasecmp(domain_name,user_dom) != 0) {
870 : 0 : uc_dom = get_uppercase_realm(mem_ctx, user_dom);
871 [ # # ]: 0 : if (uc_dom == NULL) {
872 [ # # ][ # # ]: 0 : DEBUG(SSSDBG_OP_FAILURE, ("get_uppercase_realm failed.\n"));
[ # # ][ # # ]
[ # # ]
873 : : return ENOMEM;
874 : : }
875 : : } else {
876 : 0 : realm = dp_opt_get_cstring(krb5_ctx->opts, KRB5_REALM);
877 [ # # ]: 0 : if (realm == NULL) {
878 [ # # ][ # # ]: 0 : DEBUG(SSSDBG_OP_FAILURE, ("Missing Kerberos realm.\n"));
[ # # ][ # # ]
[ # # ]
879 : : return ENOENT;
880 : : }
881 : : }
882 : :
883 : : /* NOTE: this is a hack, works only in some environments */
884 [ # # ]: 0 : upn = talloc_asprintf(mem_ctx, "%s@%s", username,
885 : : realm != NULL ? realm : uc_dom);
886 : 0 : talloc_free(uc_dom);
887 [ # # ]: 0 : if (upn == NULL) {
888 [ # # ][ # # ]: 0 : DEBUG(1, ("talloc_asprintf failed.\n"));
[ # # ][ # # ]
[ # # ]
889 : : return ENOMEM;
890 : : }
891 : :
892 [ # # ][ # # ]: 0 : DEBUG(9, ("Using simple UPN [%s].\n", upn));
[ # # ][ # # ]
[ # # ]
893 : :
894 : 0 : *_upn = upn;
895 : 0 : return EOK;
896 : : }
897 : :
898 : 10 : errno_t compare_principal_realm(const char *upn, const char *realm,
899 : : bool *different_realm)
900 : : {
901 : : char *at_sign;
902 : :
903 [ + + ][ + + ]: 10 : if (upn == NULL || realm == NULL || different_realm == NULL ||
[ + + ]
904 [ + + ]: 6 : *upn == '\0' || *realm == '\0') {
905 : : return EINVAL;
906 : : }
907 : :
908 : 5 : at_sign = strchr(upn, '@');
909 : :
910 [ + + ]: 5 : if (at_sign == NULL) {
911 : : return EINVAL;
912 : : }
913 : :
914 [ + + ]: 3 : if (strcmp(realm, at_sign + 1) == 0) {
915 : 1 : *different_realm = false;
916 : : } else {
917 : 10 : *different_realm = true;
918 : : }
919 : :
920 : : return EOK;
921 : : }
|