Sign Up
Log In
Log In
or
Sign Up
Places
All Projects
Status Monitor
Collapse sidebar
openSUSE:Leap:15.5:Update
sssd.27545
0055-Populate-kdcinfo-files-on-trust-clients.patch
Overview
Repositories
Revisions
Requests
Users
Attributes
Meta
File 0055-Populate-kdcinfo-files-on-trust-clients.patch of Package sssd.27545
From c49d154bf9692b2db51169ef35f2e5082091f78b Mon Sep 17 00:00:00 2001 From: Sumit Bose <sbose@redhat.com> Date: Fri, 24 Feb 2017 13:55:47 +0100 Subject: [PATCH 01/24] krb5 locator: add support for multiple addresses Read multiple addresses from the kdcinfo files add call the provided callback with each of them. Related to https://pagure.io/SSSD/sssd/issue/941 Reviewed-by: Jakub Hrozek <jhrozek@redhat.com> (cherry picked from commit efae9509cb05648357e9b4c10a93c0d38558bed4) --- src/krb5_plugin/sssd_krb5_locator_plugin.c | 342 +++++++++++++++------ 1 file changed, 245 insertions(+), 97 deletions(-) diff --git a/src/krb5_plugin/sssd_krb5_locator_plugin.c b/src/krb5_plugin/sssd_krb5_locator_plugin.c index 7c17fcb33..82fb5c7b2 100644 --- a/src/krb5_plugin/sssd_krb5_locator_plugin.c +++ b/src/krb5_plugin/sssd_krb5_locator_plugin.c @@ -42,7 +42,7 @@ #define DEFAULT_KADMIN_PORT 749 #define DEFAULT_KPASSWD_PORT 464 -#define BUFSIZE 512 +#define BUFSIZE 4096 #define PORT_STR_SIZE 7 #define SSSD_KRB5_LOCATOR_DEBUG "SSSD_KRB5_LOCATOR_DEBUG" #define SSSD_KRB5_LOCATOR_DISABLE "SSSD_KRB5_LOCATOR_DISABLE" @@ -53,12 +53,15 @@ } \ } while(0) +struct addr_port { + char *addr; + uint16_t port; +}; + struct sssd_ctx { char *sssd_realm; - char *kdc_addr; - uint16_t kdc_port; - char *kpasswd_addr; - uint16_t kpasswd_port; + struct addr_port *kdc_addr; + struct addr_port *kpasswd_addr; bool debug; bool disabled; }; @@ -82,6 +85,186 @@ void plugin_debug_fn(const char *format, ...) free(s); } + +static void free_addr_port_list(struct addr_port **list) +{ + size_t c; + + if (list == NULL || *list == NULL) { + return; + } + + for (c = 0; (*list)[c].addr != NULL; c++) { + free((*list)[c].addr); + } + free(*list); + *list = NULL; +} + +static int copy_addr_port_list(struct addr_port *src, bool clear_port, + struct addr_port **dst) +{ + size_t c; + struct addr_port *d = NULL; + int ret; + + /* only copy if dst is initialized to NULL */ + if (dst == NULL || *dst != NULL) { + return EINVAL; + } + + if (src == NULL) { + return 0; + } + + for (c = 0; src[c].addr != NULL; c++); + + d = calloc((c + 1), sizeof(struct addr_port)); + if (d == NULL) { + return ENOMEM; + } + + for (c = 0; src[c].addr != NULL; c++) { + d[c].addr = strdup(src[c].addr); + if (d[c].addr == NULL) { + ret = ENOMEM; + goto done; + } + if (clear_port) { + d[c].port = 0; + } else { + d[c].port = src[c].port; + } + } + + ret = EOK; + +done: + if (ret != EOK) { + free_addr_port_list(&d); + } else { + *dst = d; + } + + return ret; +} + +static int buf_to_addr_port_list(struct sssd_ctx *ctx, + uint8_t *buf, size_t buf_size, + struct addr_port **list) +{ + struct addr_port *l = NULL; + int ret; + uint8_t *p; + uint8_t *pn; + size_t c; + size_t len; + char *tmp = NULL; + char *port_str; + long port; + char *endptr; + + /* only create if list is initialized to NULL */ + if (buf == NULL || buf_size == 0 || list == NULL || *list != NULL) { + return EINVAL; + } + + c = 1; /* to account for a missing \n at the very end */ + p = buf; + while ((p - buf) < buf_size + && (p = memchr(p, '\n', buf_size - (p - buf))) != NULL) { + p++; + c++; + } + + l = calloc((c + 1), sizeof(struct addr_port)); + if (l == NULL) { + return ENOMEM; + } + + c = 0; + p = buf; + do { + pn = memchr(p, '\n', buf_size - (p - buf)); + if (pn != NULL) { + len = pn - p; + } else { + len = buf_size - (p - buf); + } + if (len == 0) { + /* empty line no more processing */ + break; + } + + free(tmp); + tmp = strndup((char *) p, len); + if (tmp == NULL) { + ret = ENOMEM; + goto done; + } + + port_str = strrchr(tmp, ':'); + if (port_str == NULL) { + port = 0; + } else { + *port_str = '\0'; + ++port_str; + + if (isdigit(*port_str)) { + errno = 0; + port = strtol(port_str, &endptr, 10); + if (errno != 0) { + ret = errno; + PLUGIN_DEBUG(("strtol failed on [%s]: [%d][%s], " + "assuming default.\n", port_str, ret, + strerror(ret))); + port = 0; + } + if (*endptr != '\0') { + PLUGIN_DEBUG(("Found additional characters [%s] in port " + "number [%s], assuming default.\n", endptr, + port_str)); + port = 0; + } + + if (port < 0 || port > 65535) { + PLUGIN_DEBUG(("Illegal port number [%ld], assuming " + "default.\n", port)); + port = 0; + } + } else { + PLUGIN_DEBUG(("Illegal port number [%s], assuming default.\n", + port_str)); + port = 0; + } + } + + PLUGIN_DEBUG(("Found [%s][%d].\n", tmp, port)); + + l[c].addr = strdup(tmp); + if (l[c].addr == NULL) { + ret = ENOMEM; + goto done; + } + l[c].port = port; + + c++; + p = pn == NULL ? NULL : (pn + 1); + } while (p != NULL); + + ret = EOK; + +done: + free(tmp); + if (ret != EOK) { + free_addr_port_list(&l); + } else { + *list = l; + } + + return ret; +} + static int get_krb5info(const char *realm, struct sssd_ctx *ctx, enum locate_service_type svc) { @@ -91,9 +274,6 @@ static int get_krb5info(const char *realm, struct sssd_ctx *ctx, uint8_t buf[BUFSIZE + 1]; int fd = -1; const char *name_tmpl = NULL; - char *port_str; - long port; - char *endptr; switch (svc) { case locate_service_kdc: @@ -148,62 +328,21 @@ static int get_krb5info(const char *realm, struct sssd_ctx *ctx, PLUGIN_DEBUG(("Content of krb5info file [%s] is [%d] or larger.\n", krb5info_name, BUFSIZE)); } - PLUGIN_DEBUG(("Found [%s] in [%s].\n", buf, krb5info_name)); - - port_str = strrchr((char *) buf, ':'); - if (port_str == NULL) { - port = 0; - } else { - *port_str = '\0'; - ++port_str; - - if (isdigit(*port_str)) { - errno = 0; - port = strtol(port_str, &endptr, 10); - if (errno != 0) { - ret = errno; - PLUGIN_DEBUG(("strtol failed on [%s]: [%d][%s], " - "assuming default.\n", port_str, ret, strerror(ret))); - port = 0; - } - if (*endptr != '\0') { - PLUGIN_DEBUG(("Found additional characters [%s] in port number " - "[%s], assuming default.\n", endptr, port_str)); - port = 0; - } - - if (port < 0 || port > 65535) { - PLUGIN_DEBUG(("Illegal port number [%ld], assuming default.\n", - port)); - port = 0; - } - } else { - PLUGIN_DEBUG(("Illegal port number [%s], assuming default.\n", - port_str)); - port = 0; - } - } switch (svc) { case locate_service_kdc: - free(ctx->kdc_addr); - ctx->kdc_addr = strdup((char *) buf); - if (ctx->kdc_addr == NULL) { - PLUGIN_DEBUG(("strdup failed.\n")); - ret = ENOMEM; + free_addr_port_list(&(ctx->kdc_addr)); + ret = buf_to_addr_port_list(ctx, buf, len, &(ctx->kdc_addr)); + if (ret != EOK) { goto done; } - ctx->kdc_port = (uint16_t) port; break; case locate_service_kpasswd: - free(ctx->kpasswd_addr); - ctx->kpasswd_addr = strdup((char *) buf); - if (ctx->kpasswd_addr == NULL) { - PLUGIN_DEBUG(("strdup failed.\n")); - ret = ENOMEM; + free_addr_port_list(&(ctx->kpasswd_addr)); + ret = buf_to_addr_port_list(ctx, buf, len, &(ctx->kpasswd_addr)); + if (ret != EOK) { goto done; } - ctx->kpasswd_port = (uint16_t) port; break; default: PLUGIN_DEBUG(("Unsupported service [%d].\n", svc)); @@ -256,8 +395,8 @@ void sssd_krb5_locator_close(void *private_data) ctx = (struct sssd_ctx *) private_data; PLUGIN_DEBUG(("sssd_krb5_locator_close called\n")); - free(ctx->kdc_addr); - free(ctx->kpasswd_addr); + free_addr_port_list(&(ctx->kdc_addr)); + free_addr_port_list(&(ctx->kpasswd_addr)); free(ctx->sssd_realm); free(ctx); @@ -277,8 +416,10 @@ krb5_error_code sssd_krb5_locator_lookup(void *private_data, struct sssd_ctx *ctx; struct addrinfo ai_hints; uint16_t port = 0; - const char *addr = NULL; + uint16_t default_port = 0; + struct addr_port *addr = NULL; char port_str[PORT_STR_SIZE]; + size_t c; if (private_data == NULL) return KRB5_PLUGIN_NO_HANDLE; ctx = (struct sssd_ctx *) private_data; @@ -308,9 +449,13 @@ krb5_error_code sssd_krb5_locator_lookup(void *private_data, if (ret != EOK) { PLUGIN_DEBUG(("reading kpasswd address failed, " "using kdc address.\n")); - free(ctx->kpasswd_addr); - ctx->kpasswd_addr = strdup(ctx->kdc_addr); - ctx->kpasswd_port = 0; + free_addr_port_list(&(ctx->kpasswd_addr)); + ret = copy_addr_port_list(ctx->kdc_addr, true, + &(ctx->kpasswd_addr)); + if (ret != EOK) { + PLUGIN_DEBUG(("copying address list failed.\n")); + return KRB5_PLUGIN_NO_HANDLE; + } } } } @@ -322,19 +467,19 @@ krb5_error_code sssd_krb5_locator_lookup(void *private_data, switch (svc) { case locate_service_kdc: addr = ctx->kdc_addr; - port = ctx->kdc_port ? ctx->kdc_port : DEFAULT_KERBEROS_PORT; + default_port = DEFAULT_KERBEROS_PORT; break; case locate_service_master_kdc: addr = ctx->kpasswd_addr; - port = DEFAULT_KERBEROS_PORT; + default_port = DEFAULT_KERBEROS_PORT; break; case locate_service_kadmin: addr = ctx->kpasswd_addr; - port = DEFAULT_KADMIN_PORT; + default_port = DEFAULT_KADMIN_PORT; break; case locate_service_kpasswd: addr = ctx->kpasswd_addr; - port = ctx->kpasswd_port ? ctx->kpasswd_port : DEFAULT_KPASSWD_PORT; + default_port = DEFAULT_KPASSWD_PORT; break; case locate_service_krb524: return KRB5_PLUGIN_NO_HANDLE; @@ -362,46 +507,49 @@ krb5_error_code sssd_krb5_locator_lookup(void *private_data, if (strcmp(realm, ctx->sssd_realm) != 0) return KRB5_PLUGIN_NO_HANDLE; - memset(port_str, 0, PORT_STR_SIZE); - ret = snprintf(port_str, PORT_STR_SIZE-1, "%u", port); - if (ret < 0 || ret >= (PORT_STR_SIZE-1)) { - PLUGIN_DEBUG(("snprintf failed.\n")); - return KRB5_PLUGIN_NO_HANDLE; - } + for (c = 0; addr[c].addr != NULL; c++) { + port = (addr[c].port == 0 ? default_port : addr[c].port); + memset(port_str, 0, PORT_STR_SIZE); + ret = snprintf(port_str, PORT_STR_SIZE-1, "%u", port); + if (ret < 0 || ret >= (PORT_STR_SIZE-1)) { + PLUGIN_DEBUG(("snprintf failed.\n")); + return KRB5_PLUGIN_NO_HANDLE; + } - memset(&ai_hints, 0, sizeof(struct addrinfo)); - ai_hints.ai_flags = AI_NUMERICHOST|AI_NUMERICSERV; - ai_hints.ai_socktype = socktype; - - ret = getaddrinfo(addr, port_str, &ai_hints, &ai); - if (ret != 0) { - PLUGIN_DEBUG(("getaddrinfo failed [%d][%s].\n", ret, - gai_strerror(ret))); - if (ret == EAI_SYSTEM) { - PLUGIN_DEBUG(("getaddrinfo failed [%d][%s].\n", errno, - strerror(errno))); + memset(&ai_hints, 0, sizeof(struct addrinfo)); + ai_hints.ai_flags = AI_NUMERICHOST|AI_NUMERICSERV; + ai_hints.ai_socktype = socktype; + + ret = getaddrinfo(addr[c].addr, port_str, &ai_hints, &ai); + if (ret != 0) { + PLUGIN_DEBUG(("getaddrinfo failed [%d][%s].\n", ret, + gai_strerror(ret))); + if (ret == EAI_SYSTEM) { + PLUGIN_DEBUG(("getaddrinfo failed [%d][%s].\n", + errno, strerror(errno))); + } + return KRB5_PLUGIN_NO_HANDLE; } - return KRB5_PLUGIN_NO_HANDLE; - } - PLUGIN_DEBUG(("addr[%s:%s] family[%d] socktype[%d]\n", addr, port_str, - ai->ai_family, ai->ai_socktype)); + PLUGIN_DEBUG(("addr[%s:%s] family[%d] socktype[%d]\n", addr[c].addr, + port_str, ai->ai_family, ai->ai_socktype)); - if ((family == AF_UNSPEC || ai->ai_family == family) && - ai->ai_socktype == socktype) { + if ((family == AF_UNSPEC || ai->ai_family == family) && + ai->ai_socktype == socktype) { - ret = cbfunc(cbdata, socktype, ai->ai_addr); - if (ret != 0) { - PLUGIN_DEBUG(("cbfunc failed\n")); - freeaddrinfo(ai); - return ret; + ret = cbfunc(cbdata, socktype, ai->ai_addr); + if (ret != 0) { + PLUGIN_DEBUG(("cbfunc failed\n")); + freeaddrinfo(ai); + return ret; + } else { + PLUGIN_DEBUG(("[%s] used\n", addr[c].addr)); + } } else { - PLUGIN_DEBUG(("[%s] used\n", addr)); + PLUGIN_DEBUG(("[%s] NOT used\n", addr[c].addr)); } - } else { - PLUGIN_DEBUG(("[%s] NOT used\n", addr)); + freeaddrinfo(ai); } - freeaddrinfo(ai); return 0; } -- 2.37.0 From 681391923313998ad2d68a9dfd52e492758bd0ef Mon Sep 17 00:00:00 2001 From: Sumit Bose <sbose@redhat.com> Date: Tue, 22 May 2018 17:59:52 +0200 Subject: [PATCH 02/24] krb5 locator: fix IPv6 support IPv6 addresses are added with surrounding '[' and ']' to the kdcinfo file to be able to specify a port number properly. The Kerberos location plugin didn't handle those entries properly. Related to https://pagure.io/SSSD/sssd/issue/941 Reviewed-by: Jakub Hrozek <jhrozek@redhat.com> (cherry picked from commit 9f683246228848173c57ad02bde241bd761481ea) --- src/krb5_plugin/sssd_krb5_locator_plugin.c | 19 +++++++++++++++++-- 1 file changed, 17 insertions(+), 2 deletions(-) diff --git a/src/krb5_plugin/sssd_krb5_locator_plugin.c b/src/krb5_plugin/sssd_krb5_locator_plugin.c index 82fb5c7b2..58cac7f4b 100644 --- a/src/krb5_plugin/sssd_krb5_locator_plugin.c +++ b/src/krb5_plugin/sssd_krb5_locator_plugin.c @@ -159,6 +159,8 @@ static int buf_to_addr_port_list(struct sssd_ctx *ctx, uint8_t *pn; size_t c; size_t len; + size_t addr_len; + char *addr_str = NULL; char *tmp = NULL; char *port_str; long port; @@ -206,6 +208,9 @@ static int buf_to_addr_port_list(struct sssd_ctx *ctx, port_str = strrchr(tmp, ':'); if (port_str == NULL) { port = 0; + } else if (tmp[0] == '[' && *(port_str - 1) != ']') { + /* IPv6 address without port number */ + port = 0; } else { *port_str = '\0'; ++port_str; @@ -239,9 +244,19 @@ static int buf_to_addr_port_list(struct sssd_ctx *ctx, } } - PLUGIN_DEBUG(("Found [%s][%d].\n", tmp, port)); + /* make sure tmp is not modified so that it can be freed later */ + addr_str = tmp; + /* strip leading '[' and trailing ']' from IPv6 addresses */ + if (addr_str[0] == '[' + && (addr_len = strlen(addr_str)) + && addr_str[addr_len - 1] == ']') { + addr_str[addr_len -1] = '\0'; + addr_str++; + } + + PLUGIN_DEBUG(("Found [%s][%d].\n", addr_str, port)); - l[c].addr = strdup(tmp); + l[c].addr = strdup(addr_str); if (l[c].addr == NULL) { ret = ENOMEM; goto done; -- 2.37.0 From 260379172db06c5beb6a3a125e09c0829ca00e52 Mon Sep 17 00:00:00 2001 From: Sumit Bose <sbose@redhat.com> Date: Tue, 22 May 2018 18:03:05 +0200 Subject: [PATCH 03/24] krb5 locator: make plugin more robust Although currently libkrb5 sets all parameters of the locator plugin calls to suitable values we should make sure that provided pointers are not NULL before trying to dereference them. Related to https://pagure.io/SSSD/sssd/issue/941 Reviewed-by: Jakub Hrozek <jhrozek@redhat.com> (cherry picked from commit c1fbc6b64ecaf51efc4379c4c8a4960de095abf0) --- src/krb5_plugin/sssd_krb5_locator_plugin.c | 4 ++++ 1 file changed, 4 insertions(+) diff --git a/src/krb5_plugin/sssd_krb5_locator_plugin.c b/src/krb5_plugin/sssd_krb5_locator_plugin.c index 58cac7f4b..9874fd2d1 100644 --- a/src/krb5_plugin/sssd_krb5_locator_plugin.c +++ b/src/krb5_plugin/sssd_krb5_locator_plugin.c @@ -439,6 +439,10 @@ krb5_error_code sssd_krb5_locator_lookup(void *private_data, if (private_data == NULL) return KRB5_PLUGIN_NO_HANDLE; ctx = (struct sssd_ctx *) private_data; + if (realm == NULL || cbfunc == NULL || cbdata == NULL) { + return KRB5_PLUGIN_NO_HANDLE; + } + if (ctx->disabled) { PLUGIN_DEBUG(("Plugin disabled, nothing to do.\n")); return KRB5_PLUGIN_NO_HANDLE; -- 2.37.0 From 9fb8622c48417d4fe6f99d8d236b38ef9e37d3f6 Mon Sep 17 00:00:00 2001 From: Sumit Bose <sbose@redhat.com> Date: Thu, 24 May 2018 17:14:42 +0200 Subject: [PATCH 04/24] krb5 locator: add unit tests Unit test for existing and new functionality of the Kerberos locator plugin. Related to https://pagure.io/SSSD/sssd/issue/941 Reviewed-by: Jakub Hrozek <jhrozek@redhat.com> (cherry picked from commit 2124275fe494a0241a552538c70f40c2291f3795) --- Makefile.am | 20 + src/krb5_plugin/sssd_krb5_locator_plugin.c | 16 + .../cmocka/test_sssd_krb5_locator_plugin.c | 631 ++++++++++++++++++ 3 files changed, 667 insertions(+) create mode 100644 src/tests/cmocka/test_sssd_krb5_locator_plugin.c diff --git a/Makefile.am b/Makefile.am index fab293a70..a041a4cfc 100644 --- a/Makefile.am +++ b/Makefile.am @@ -289,6 +289,7 @@ if HAVE_CMOCKA krb5_common_test \ test_iobuf \ sss_certmap_test \ + test_sssd_krb5_locator_plugin \ $(NULL) @@ -3494,6 +3495,25 @@ sss_certmap_test_LDADD = \ libsss_certmap.la \ $(NULL) +test_sssd_krb5_locator_plugin_SOURCES = \ + src/tests/cmocka/test_sssd_krb5_locator_plugin.c \ + src/krb5_plugin/sssd_krb5_locator_plugin.c \ + $(NULL) +test_sssd_krb5_locator_plugin_CFLAGS = \ + $(AM_CFLAGS) \ + $(POPT_CFLAGS) \ + $(TALLOC_CFLAGS) \ + $(KRB5_CFLAGS) \ + -DTEST_PUBCONF_PATH=\"$(abs_builddir)/src/tests/cmocka/pubconf\" \ + $(NULL) +test_sssd_krb5_locator_plugin_LDADD = \ + $(CMOCKA_LIBS) \ + $(POPT_LIBS) \ + $(TALLOC_LIBS) \ + $(KRB5_LIBS) \ + libsss_test_common.la \ + $(NULL) + if BUILD_KCM test_kcm_json_SOURCES = \ src/tests/cmocka/test_kcm_json_marshalling.c \ diff --git a/src/krb5_plugin/sssd_krb5_locator_plugin.c b/src/krb5_plugin/sssd_krb5_locator_plugin.c index 9874fd2d1..952d487c2 100644 --- a/src/krb5_plugin/sssd_krb5_locator_plugin.c +++ b/src/krb5_plugin/sssd_krb5_locator_plugin.c @@ -38,6 +38,22 @@ #include "providers/krb5/krb5_common.h" +/* The following override of KDCINFO_TMPL and KPASSWDINFO_TMPL is not very + * elegant but since they are defined in krb5_common.h with the help of + * PUBCONF_PATH from config.h and PUBCONF_PATH can by set by a configure + * options I didn't found another way to change the path for a unit test. */ +#ifdef TEST_PUBCONF_PATH +#ifdef KDCINFO_TMPL +#undef KDCINFO_TMPL +#endif +#define KDCINFO_TMPL TEST_PUBCONF_PATH"/kdcinfo.%s" + +#ifdef KPASSWDINFO_TMPL +#undef KPASSWDINFO_TMPL +#endif +#define KPASSWDINFO_TMPL TEST_PUBCONF_PATH"/kpasswdinfo.%s" +#endif /* TEST_PUBCONF_PATH */ + #define DEFAULT_KERBEROS_PORT 88 #define DEFAULT_KADMIN_PORT 749 #define DEFAULT_KPASSWD_PORT 464 diff --git a/src/tests/cmocka/test_sssd_krb5_locator_plugin.c b/src/tests/cmocka/test_sssd_krb5_locator_plugin.c new file mode 100644 index 000000000..3e7d00632 --- /dev/null +++ b/src/tests/cmocka/test_sssd_krb5_locator_plugin.c @@ -0,0 +1,631 @@ +/* + SSSD + + Unit test for SSSD's MIT Kerberos locator plugin + + Authors: + Sumit Bose <sbose@redhat.com> + + Copyright (C) 2018 Red Hat + + This program is free software; you can redistribute it and/or modify + it under the terms of the GNU General Public License as published by + the Free Software Foundation; either version 3 of the License, or + (at your option) any later version. + + This program is distributed in the hope that it will be useful, + but WITHOUT ANY WARRANTY; without even the implied warranty of + MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + GNU General Public License for more details. + + You should have received a copy of the GNU General Public License + along with this program. If not, see <http://www.gnu.org/licenses/>. +*/ +#include "config.h" + +#include <popt.h> +#include <sys/types.h> +#include <sys/socket.h> +#include <sys/stat.h> +#include <string.h> +#include <fcntl.h> +#include <netdb.h> +#include <krb5/krb5.h> +#include <krb5/locate_plugin.h> + +#include "tests/cmocka/common_mock.h" + +#define TEST_REALM "TEST.REALM" +#define TEST_IP_1 "123.231.132.213" +#define TEST_IPV6_1_PURE "7025:4d2d:2b06:e321:d971:16c0:6eeb:cc41" +#define TEST_IPV6_1 "["TEST_IPV6_1_PURE"]" +#define TEST_SERVICE_1 "22334" +#define TEST_SERVICE_2 "54321" +#define TEST_IP_1_WITH_SERVICE TEST_IP_1":"TEST_SERVICE_1 +#define TEST_IPV6_1_WITH_SERVICE TEST_IPV6_1":"TEST_SERVICE_2 + +struct test_state { + void *dummy; +}; + +static int setup(void **state) +{ + struct test_state *ts = NULL; + + assert_true(leak_check_setup()); + + ts = talloc(global_talloc_context, struct test_state); + assert_non_null(ts); + + check_leaks_push(ts); + *state = (void *)ts; + + unlink(TEST_PUBCONF_PATH"/kdcinfo."TEST_REALM); + rmdir(TEST_PUBCONF_PATH); + + return 0; +} + +static int teardown(void **state) +{ + struct test_state *ts = talloc_get_type_abort(*state, struct test_state); + + assert_non_null(ts); + + assert_true(check_leaks_pop(ts)); + talloc_free(ts); + assert_true(leak_check_teardown()); + return 0; +} + +/* Taken from MIT Kerberos src/lib/krb5/os/locate_kdc.c and + * lib/krb5/os/os-proto.h */ + +typedef enum { + TCP_OR_UDP = 0, + TCP, + UDP, + HTTPS, +} k5_transport; + +/* A single server hostname or address. */ +struct server_entry { + char *hostname; /* NULL -> use addrlen/addr instead */ + int port; /* Used only if hostname set */ + k5_transport transport; /* May be 0 for UDP/TCP if hostname set */ + char *uri_path; /* Used only if transport is HTTPS */ + int family; /* May be 0 (aka AF_UNSPEC) if hostname set */ + int master; /* True, false, or -1 for unknown. */ + size_t addrlen; + struct sockaddr_storage addr; +}; + +/* A list of server hostnames/addresses. */ +struct serverlist { + struct server_entry *servers; + size_t nservers; +}; +#define SERVERLIST_INIT { NULL, 0 } + +/* Free up everything pointed to by the serverlist structure, but don't + * * free the structure itself. */ +void +k5_free_serverlist (struct serverlist *list) +{ + size_t i; + + for (i = 0; i < list->nservers; i++) { + free(list->servers[i].hostname); + free(list->servers[i].uri_path); + } + free(list->servers); + list->servers = NULL; + list->nservers = 0; +} + +/* Make room for a new server entry in list and return a pointer to the new + * entry. (Do not increment list->nservers.) */ +static struct server_entry * +new_server_entry(struct serverlist *list) +{ + struct server_entry *newservers, *entry; + size_t newspace = (list->nservers + 1) * sizeof(struct server_entry); + + newservers = realloc(list->servers, newspace); + if (newservers == NULL) + return NULL; + list->servers = newservers; + entry = &newservers[list->nservers]; + memset(entry, 0, sizeof(*entry)); + entry->master = -1; + return entry; +} + +/* Add an address entry to list. */ +static int +add_addr_to_list(struct serverlist *list, k5_transport transport, int family, + size_t addrlen, struct sockaddr *addr) +{ + struct server_entry *entry; + + entry = new_server_entry(list); + if (entry == NULL) + return ENOMEM; + entry->transport = transport; + entry->family = family; + entry->hostname = NULL; + entry->uri_path = NULL; + entry->addrlen = addrlen; + memcpy(&entry->addr, addr, addrlen); + list->nservers++; + return 0; +} + +struct module_callback_data { + int out_of_mem; + struct serverlist *list; +}; + +static int +module_callback(void *cbdata, int socktype, struct sockaddr *sa) +{ + struct module_callback_data *d = cbdata; + size_t addrlen; + k5_transport transport; + + if (socktype != SOCK_STREAM && socktype != SOCK_DGRAM) + return 0; + if (sa->sa_family == AF_INET) + addrlen = sizeof(struct sockaddr_in); + else if (sa->sa_family == AF_INET6) + addrlen = sizeof(struct sockaddr_in6); + else + return 0; + transport = (socktype == SOCK_STREAM) ? TCP : UDP; + if (add_addr_to_list(d->list, transport, sa->sa_family, addrlen, + sa) != 0) { + /* Assumes only error is ENOMEM. */ + d->out_of_mem = 1; + return 1; + } + return 0; +} + +krb5_error_code sssd_krb5_locator_init(krb5_context context, + void **private_data); +void sssd_krb5_locator_close(void *private_data); + +krb5_error_code sssd_krb5_locator_lookup(void *private_data, + enum locate_service_type svc, + const char *realm, + int socktype, + int family, + int (*cbfunc)(void *, int, struct sockaddr *), + void *cbdata); + +void test_init(void **state) +{ + krb5_context ctx; + krb5_error_code kerr; + void *priv; + + kerr = krb5_init_context (&ctx); + assert_int_equal(kerr, 0); + + kerr = sssd_krb5_locator_init(ctx, &priv); + assert_int_equal(kerr, 0); + + sssd_krb5_locator_close(priv); + + krb5_free_context(ctx); +} + +void test_failed_lookup(void **state) +{ + krb5_context ctx; + krb5_error_code kerr; + void *priv; + struct module_callback_data cbdata = { 0 }; + + + kerr = krb5_init_context (&ctx); + assert_int_equal(kerr, 0); + + kerr = sssd_krb5_locator_init(ctx, &priv); + assert_int_equal(kerr, 0); + + kerr = sssd_krb5_locator_lookup(NULL, -1, NULL, -1, -1, NULL, NULL); + assert_int_equal(kerr, KRB5_PLUGIN_NO_HANDLE); + + kerr = sssd_krb5_locator_lookup(priv, -1, NULL, -1, -1, NULL, NULL); + assert_int_equal(kerr, KRB5_PLUGIN_NO_HANDLE); + + kerr = sssd_krb5_locator_lookup(priv, locate_service_kdc , NULL, -1, -1, + NULL, NULL); + assert_int_equal(kerr, KRB5_PLUGIN_NO_HANDLE); + + kerr = sssd_krb5_locator_lookup(priv, locate_service_kdc , TEST_REALM, -1, + -1, NULL, NULL); + assert_int_equal(kerr, KRB5_PLUGIN_NO_HANDLE); + + kerr = sssd_krb5_locator_lookup(priv, locate_service_kdc , TEST_REALM, + SOCK_DGRAM, -1, NULL, NULL); + assert_int_equal(kerr, KRB5_PLUGIN_NO_HANDLE); + + kerr = sssd_krb5_locator_lookup(priv, locate_service_kdc , TEST_REALM, + SOCK_DGRAM, AF_INET6, NULL, NULL); + assert_int_equal(kerr, KRB5_PLUGIN_NO_HANDLE); + + kerr = sssd_krb5_locator_lookup(priv, locate_service_kdc , TEST_REALM, + SOCK_DGRAM, AF_INET6, module_callback, + NULL); + assert_int_equal(kerr, KRB5_PLUGIN_NO_HANDLE); + + kerr = sssd_krb5_locator_lookup(priv, locate_service_kdc , TEST_REALM, + SOCK_DGRAM, AF_INET6, module_callback, + &cbdata); + assert_int_equal(kerr, KRB5_PLUGIN_NO_HANDLE); + + sssd_krb5_locator_close(priv); + + krb5_free_context(ctx); +} + +void test_empty(void **state) +{ + krb5_context ctx; + krb5_error_code kerr; + void *priv; + int fd; + struct module_callback_data cbdata = { 0 }; + + kerr = krb5_init_context (&ctx); + assert_int_equal(kerr, 0); + + kerr = sssd_krb5_locator_init(ctx, &priv); + assert_int_equal(kerr, 0); + + mkdir(TEST_PUBCONF_PATH, 0777); + fd = open(TEST_PUBCONF_PATH"/kdcinfo."TEST_REALM, O_CREAT, 0777); + assert_int_not_equal(fd, -1); + close(fd); + + kerr = sssd_krb5_locator_lookup(priv, locate_service_kdc , TEST_REALM, + SOCK_DGRAM, AF_INET6, module_callback, + &cbdata); + assert_int_equal(kerr, KRB5_PLUGIN_NO_HANDLE); + unlink(TEST_PUBCONF_PATH"/kdcinfo."TEST_REALM); + rmdir(TEST_PUBCONF_PATH); + + sssd_krb5_locator_close(priv); + + krb5_free_context(ctx); +} + +void test_single(void **state) +{ + krb5_context ctx; + krb5_error_code kerr; + void *priv; + int fd; + struct serverlist list = SERVERLIST_INIT; + struct module_callback_data cbdata = { 0 }; + ssize_t s; + int ret; + char host[NI_MAXHOST]; + char service[NI_MAXSERV]; + + cbdata.list = &list; + + kerr = krb5_init_context (&ctx); + assert_int_equal(kerr, 0); + + kerr = sssd_krb5_locator_init(ctx, &priv); + assert_int_equal(kerr, 0); + + mkdir(TEST_PUBCONF_PATH, 0777); + fd = open(TEST_PUBCONF_PATH"/kdcinfo."TEST_REALM, O_CREAT|O_RDWR, 0777); + assert_int_not_equal(fd, -1); + s = write(fd, TEST_IP_1, sizeof(TEST_IP_1)); + assert_int_equal(s, sizeof(TEST_IP_1)); + close(fd); + + kerr = sssd_krb5_locator_lookup(priv, locate_service_kdc , TEST_REALM, + SOCK_DGRAM, AF_INET6, module_callback, + &cbdata); + assert_int_equal(kerr, 0); + + /* We asked for AF_INET6, but TEST_IP_1 is IPv4 */ + assert_int_equal(list.nservers, 0); + assert_null(list.servers); + + kerr = sssd_krb5_locator_lookup(priv, locate_service_kdc , TEST_REALM, + SOCK_DGRAM, AF_INET, module_callback, + &cbdata); + assert_int_equal(kerr, 0); + assert_int_equal(list.nservers, 1); + assert_non_null(list.servers); + assert_int_equal(list.servers[0].addrlen, 16); + ret = getnameinfo((struct sockaddr *) &list.servers[0].addr, + list.servers[0].addrlen, + host, sizeof(host), service, sizeof(service), + NI_NUMERICHOST|NI_NUMERICSERV); + assert_int_equal(ret, 0); + assert_string_equal(TEST_IP_1, host); + assert_string_equal("88", service); + + k5_free_serverlist(&list); + + kerr = sssd_krb5_locator_lookup(priv, locate_service_kdc , TEST_REALM, + SOCK_DGRAM, AF_UNSPEC, module_callback, + &cbdata); + assert_int_equal(kerr, 0); + assert_int_equal(list.nservers, 1); + assert_non_null(list.servers); + assert_int_equal(list.servers[0].addrlen, 16); + ret = getnameinfo((struct sockaddr *) &list.servers[0].addr, + list.servers[0].addrlen, + host, sizeof(host), service, sizeof(service), + NI_NUMERICHOST|NI_NUMERICSERV); + assert_int_equal(ret, 0); + assert_string_equal(TEST_IP_1, host); + assert_string_equal("88", service); + + k5_free_serverlist(&list); + + unlink(TEST_PUBCONF_PATH"/kdcinfo."TEST_REALM); + rmdir(TEST_PUBCONF_PATH); + sssd_krb5_locator_close(priv); + + krb5_free_context(ctx); +} + +struct test_data { + const char *ip; + bool found; +}; + +void test_multi_check_results(struct test_data *test_data, + struct serverlist *list, + const char *exp_service) +{ + int ret; + char host[NI_MAXHOST]; + char service[NI_MAXSERV]; + size_t c; + size_t d; + + /* To make sure each result from list has a matching entry in test_data we + * use a flag to mark found entries, this way we can properly detect is + * the same address is used multiple times. */ + for (d = 0; test_data[d].ip != NULL; d++) { + test_data[d].found = false; + } + + for (c = 0; c < list->nservers; c++) { + ret = getnameinfo((struct sockaddr *) &list->servers[c].addr, + list->servers[c].addrlen, + host, sizeof(host), service, sizeof(service), + NI_NUMERICHOST|NI_NUMERICSERV); + assert_int_equal(ret, 0); + assert_string_equal(exp_service, service); + for (d = 0; test_data[d].ip != NULL; d++) { + /* Compare result with test_data, be aware that the test_data has + * '[]' around IPv& addresses */ + if (strncmp(host, + test_data[d].ip + (test_data[d].ip[0] == '[' ? 1 : 0), + strlen(host)) == 0 && !test_data[d].found) { + test_data[d].found = true; + break; + } + } + /* Make sure we found the result in the list */ + assert_non_null(test_data[d].ip); + } +} + +void test_multi(void **state) +{ + krb5_context ctx; + krb5_error_code kerr; + void *priv; + int fd; + struct serverlist list = SERVERLIST_INIT; + struct module_callback_data cbdata = { 0 }; + ssize_t s; + size_t c; + struct test_data test_data[] = { + {TEST_IP_1, false}, + {TEST_IPV6_1, false}, + {"[c89a:565b:4510:5b9f:41fe:ea81:87a0:f21b]", false}, + {"155.42.66.53", false}, + {"[f812:5941:ba69:2bae:e806:3b68:770d:d75e]", false}, + {"[3ad3:9dda:50e4:3c82:548f:eaa1:e120:6dd]", false}, + {"55.116.79.183", false}, + {"[ce8a:ee99:98cd:d8cd:218d:393e:d5a9:dc52]", false}, + /* the following address is added twice to check if + * an address can be added more than once. */ + {"37.230.88.162", false}, + {"37.230.88.162", false}, + {NULL, false} }; + + cbdata.list = &list; + + kerr = krb5_init_context (&ctx); + assert_int_equal(kerr, 0); + + kerr = sssd_krb5_locator_init(ctx, &priv); + assert_int_equal(kerr, 0); + + mkdir(TEST_PUBCONF_PATH, 0777); + fd = open(TEST_PUBCONF_PATH"/kdcinfo."TEST_REALM, O_CREAT|O_RDWR, 0777); + assert_int_not_equal(fd, -1); + for (c = 0; test_data[c].ip != NULL; c++) { + s = write(fd, test_data[c].ip, strlen(test_data[c].ip)); + assert_int_equal(s, strlen(test_data[c].ip)); + s = write(fd, "\n", 1); + assert_int_equal(s, 1); + } + close(fd); + + kerr = sssd_krb5_locator_lookup(priv, locate_service_kdc , TEST_REALM, + SOCK_DGRAM, AF_INET6, module_callback, + &cbdata); + assert_int_equal(kerr, 0); + + assert_int_equal(list.nservers, 5); + assert_non_null(list.servers); + test_multi_check_results(test_data, &list, "88"); + + k5_free_serverlist(&list); + + kerr = sssd_krb5_locator_lookup(priv, locate_service_kdc , TEST_REALM, + SOCK_DGRAM, AF_INET, module_callback, + &cbdata); + assert_int_equal(kerr, 0); + + assert_int_equal(list.nservers, 5); + assert_non_null(list.servers); + test_multi_check_results(test_data, &list, "88"); + + + k5_free_serverlist(&list); + + kerr = sssd_krb5_locator_lookup(priv, locate_service_kdc , TEST_REALM, + SOCK_DGRAM, AF_UNSPEC, module_callback, + &cbdata); + assert_int_equal(kerr, 0); + + assert_int_equal(list.nservers, 10); + assert_non_null(list.servers); + test_multi_check_results(test_data, &list, "88"); + + k5_free_serverlist(&list); + + unlink(TEST_PUBCONF_PATH"/kdcinfo."TEST_REALM); + rmdir(TEST_PUBCONF_PATH); + sssd_krb5_locator_close(priv); + + krb5_free_context(ctx); +} + +void test_service(void **state) +{ + krb5_context ctx; + krb5_error_code kerr; + void *priv; + int fd; + struct serverlist list = SERVERLIST_INIT; + struct module_callback_data cbdata = { 0 }; + ssize_t s; + int ret; + char host[NI_MAXHOST]; + char service[NI_MAXSERV]; + + cbdata.list = &list; + + kerr = krb5_init_context (&ctx); + assert_int_equal(kerr, 0); + + kerr = sssd_krb5_locator_init(ctx, &priv); + assert_int_equal(kerr, 0); + + mkdir(TEST_PUBCONF_PATH, 0777); + fd = open(TEST_PUBCONF_PATH"/kdcinfo."TEST_REALM, O_CREAT|O_RDWR, 0777); + assert_int_not_equal(fd, -1); + s = write(fd, TEST_IP_1_WITH_SERVICE, sizeof(TEST_IP_1_WITH_SERVICE)); + assert_int_equal(s, sizeof(TEST_IP_1_WITH_SERVICE)); + s = write(fd, "\n", 1); + assert_int_equal(s, 1); + s = write(fd, TEST_IPV6_1_WITH_SERVICE, sizeof(TEST_IPV6_1_WITH_SERVICE)); + assert_int_equal(s, sizeof(TEST_IPV6_1_WITH_SERVICE)); + close(fd); + + kerr = sssd_krb5_locator_lookup(priv, locate_service_kdc , TEST_REALM, + SOCK_DGRAM, AF_INET6, module_callback, + &cbdata); + assert_int_equal(kerr, 0); + + assert_int_equal(list.nservers, 1); + assert_non_null(list.servers); + ret = getnameinfo((struct sockaddr *) &list.servers[0].addr, + list.servers[0].addrlen, + host, sizeof(host), service, sizeof(service), + NI_NUMERICHOST|NI_NUMERICSERV); + assert_int_equal(ret, 0); + assert_string_equal(TEST_IPV6_1_PURE, host); + assert_string_equal(TEST_SERVICE_2, service); + + k5_free_serverlist(&list); + + kerr = sssd_krb5_locator_lookup(priv, locate_service_kdc , TEST_REALM, + SOCK_DGRAM, AF_INET, module_callback, + &cbdata); + assert_int_equal(kerr, 0); + assert_int_equal(list.nservers, 1); + assert_non_null(list.servers); + ret = getnameinfo((struct sockaddr *) &list.servers[0].addr, + list.servers[0].addrlen, + host, sizeof(host), service, sizeof(service), + NI_NUMERICHOST|NI_NUMERICSERV); + assert_int_equal(ret, 0); + assert_string_equal(TEST_IP_1, host); + assert_string_equal(TEST_SERVICE_1, service); + + k5_free_serverlist(&list); + + + unlink(TEST_PUBCONF_PATH"/kdcinfo."TEST_REALM); + rmdir(TEST_PUBCONF_PATH); + sssd_krb5_locator_close(priv); + + krb5_free_context(ctx); +} + +int main(int argc, const char *argv[]) +{ + poptContext pc; + int opt; + int ret; + struct poptOption long_options[] = { + POPT_AUTOHELP + SSSD_DEBUG_OPTS + POPT_TABLEEND + }; + + const struct CMUnitTest tests[] = { + cmocka_unit_test_setup_teardown(test_init, + setup, teardown), + cmocka_unit_test_setup_teardown(test_failed_lookup, + setup, teardown), + cmocka_unit_test_setup_teardown(test_empty, + setup, teardown), + cmocka_unit_test_setup_teardown(test_single, + setup, teardown), + cmocka_unit_test_setup_teardown(test_multi, + setup, teardown), + cmocka_unit_test_setup_teardown(test_service, + setup, teardown), + }; + + /* Set debug level to invalid value so we can decide if -d 0 was used. */ + debug_level = SSSDBG_INVALID; + + pc = poptGetContext(argv[0], argc, argv, long_options, 0); + while((opt = poptGetNextOpt(pc)) != -1) { + switch(opt) { + default: + fprintf(stderr, "\nInvalid option %s: %s\n\n", + poptBadOption(pc, 0), poptStrerror(opt)); + poptPrintUsage(pc, stderr, 0); + return 1; + } + } + poptFreeContext(pc); + + DEBUG_CLI_INIT(debug_level); + + ret = cmocka_run_group_tests(tests, NULL, NULL); + + return ret; +} -- 2.37.0 From 30e179d5623baa68b0f9e654677a8d3d851f9dc7 Mon Sep 17 00:00:00 2001 From: Sumit Bose <sbose@redhat.com> Date: Thu, 24 May 2018 17:15:38 +0200 Subject: [PATCH 05/24] AD/IPA: Create kdcinfo file for sub-domains With this patch kdcinfo files are created for sub-domains by the AD provider and by the IPA provider on the IPA servers (ipa_server_mode=True). Related to https://pagure.io/SSSD/sssd/issue/3652 Reviewed-by: Jakub Hrozek <jhrozek@redhat.com> (cherry picked from commit cc7922755dac53c69558ba060b309ac48ae82783) --- src/providers/ad/ad_common.c | 9 +++++++++ src/providers/ad/ad_common.h | 1 + src/providers/ad/ad_init.c | 1 + src/providers/ad/ad_subdomains.c | 18 +++++++++++++++--- src/providers/ipa/ipa_subdomains_server.c | 16 ++++++++++++++-- 5 files changed, 40 insertions(+), 5 deletions(-) diff --git a/src/providers/ad/ad_common.c b/src/providers/ad/ad_common.c index b8daec216..fd7ab0bf9 100644 --- a/src/providers/ad/ad_common.c +++ b/src/providers/ad/ad_common.c @@ -763,6 +763,7 @@ ad_failover_init(TALLOC_CTX *mem_ctx, struct be_ctx *bectx, const char *ad_gc_service, const char *ad_domain, bool ad_use_ldaps, + bool use_kdcinfo, struct ad_service **_service) { errno_t ret; @@ -808,6 +809,14 @@ ad_failover_init(TALLOC_CTX *mem_ctx, struct be_ctx *bectx, goto done; } + /* Set flag that controls whether we want to write the + * kdcinfo files at all + */ + service->krb5_service->write_kdcinfo = use_kdcinfo; + DEBUG(SSSDBG_CONF_SETTINGS, "write_kdcinfo for realm %s set to %s\n", + krb5_realm, + service->krb5_service->write_kdcinfo ? "true" : "false"); + ret = be_fo_add_service(bectx, ad_service, ad_user_data_cmp); if (ret != EOK) { DEBUG(SSSDBG_CRIT_FAILURE, "Failed to create failover service!\n"); diff --git a/src/providers/ad/ad_common.h b/src/providers/ad/ad_common.h index b59799a65..efe82d846 100644 --- a/src/providers/ad/ad_common.h +++ b/src/providers/ad/ad_common.h @@ -149,6 +149,7 @@ ad_failover_init(TALLOC_CTX *mem_ctx, struct be_ctx *ctx, const char *ad_gc_service, const char *ad_domain, bool ad_use_ldaps, + bool use_kdcinfo, struct ad_service **_service); errno_t diff --git a/src/providers/ad/ad_init.c b/src/providers/ad/ad_init.c index 7f171cb5e..6e7a3bab7 100644 --- a/src/providers/ad/ad_init.c +++ b/src/providers/ad/ad_init.c @@ -162,6 +162,7 @@ static errno_t ad_init_options(TALLOC_CTX *mem_ctx, ad_realm, AD_SERVICE_NAME, AD_GC_SERVICE_NAME, dp_opt_get_string(ad_options->basic, AD_DOMAIN), ad_use_ldaps, + false, /* will be set in ad_get_auth_options() */ &ad_options->service); if (ret != EOK) { DEBUG(SSSDBG_FATAL_FAILURE, "Failed to init AD failover service: " diff --git a/src/providers/ad/ad_subdomains.c b/src/providers/ad/ad_subdomains.c index 3ac34ed04..f12b4c3c7 100644 --- a/src/providers/ad/ad_subdomains.c +++ b/src/providers/ad/ad_subdomains.c @@ -250,6 +250,7 @@ ad_subdom_ad_ctx_new(struct be_ctx *be_ctx, const char *keytab; char *subdom_conf_path; bool ad_use_ldaps = false; + bool use_kdcinfo = false; realm = dp_opt_get_cstring(id_ctx->ad_options->basic, AD_KRB5_REALM); hostname = dp_opt_get_cstring(id_ctx->ad_options->basic, AD_HOSTNAME); @@ -329,9 +330,20 @@ ad_subdom_ad_ctx_new(struct be_ctx *be_ctx, backup_servers = dp_opt_get_string(ad_options->basic, AD_BACKUP_SERVER); ad_use_ldaps = dp_opt_get_bool(ad_options->basic, AD_USE_LDAPS); - ret = ad_failover_init(ad_options, be_ctx, servers, backup_servers, realm, - service_name, gc_service_name, - subdom->name, ad_use_ldaps, &ad_options->service); + if (id_ctx->ad_options->auth_ctx != NULL + && id_ctx->ad_options->auth_ctx->opts != NULL) { + use_kdcinfo = dp_opt_get_bool(id_ctx->ad_options->auth_ctx->opts, + KRB5_USE_KDCINFO); + } + + DEBUG(SSSDBG_TRACE_ALL, + "Init failover for [%s][%s] with use_kdcinfo [%s].\n", + subdom->name, subdom->realm, use_kdcinfo ? "true" : "false"); + + ret = ad_failover_init(ad_options, be_ctx, servers, backup_servers, + subdom->realm, service_name, gc_service_name, + subdom->name, ad_use_ldaps, use_kdcinfo, + &ad_options->service); if (ret != EOK) { DEBUG(SSSDBG_OP_FAILURE, "Cannot initialize AD failover\n"); talloc_free(ad_options); diff --git a/src/providers/ipa/ipa_subdomains_server.c b/src/providers/ipa/ipa_subdomains_server.c index 40ded9442..70235abba 100644 --- a/src/providers/ipa/ipa_subdomains_server.c +++ b/src/providers/ipa/ipa_subdomains_server.c @@ -243,6 +243,7 @@ ipa_ad_ctx_new(struct be_ctx *be_ctx, struct sdap_domain *sdom; errno_t ret; const char *extra_attrs; + bool use_kdcinfo = false; ad_domain = subdom->name; DEBUG(SSSDBG_TRACE_LIBS, "Setting up AD subdomain %s\n", subdom->name); @@ -299,12 +300,23 @@ ipa_ad_ctx_new(struct be_ctx *be_ctx, ad_servers = dp_opt_get_string(ad_options->basic, AD_SERVER); ad_backup_servers = dp_opt_get_string(ad_options->basic, AD_BACKUP_SERVER); + if (id_ctx->ipa_options != NULL && id_ctx->ipa_options->auth != NULL) { + use_kdcinfo = dp_opt_get_bool(id_ctx->ipa_options->auth, + KRB5_USE_KDCINFO); + } + + DEBUG(SSSDBG_TRACE_ALL, + "Init failover for [%s][%s] with use_kdcinfo [%s].\n", + subdom->name, subdom->realm, use_kdcinfo ? "true" : "false"); + /* Set KRB5 realm to same as the one of IPA when IPA * is able to attach PAC. For testing, use hardcoded. */ + /* Why? */ ret = ad_failover_init(ad_options, be_ctx, ad_servers, ad_backup_servers, - id_ctx->server_mode->realm, + subdom->realm, service_name, gc_service_name, - subdom->name, false, &ad_options->service); + subdom->name, false, use_kdcinfo, + &ad_options->service); if (ret != EOK) { DEBUG(SSSDBG_OP_FAILURE, "Cannot initialize AD failover\n"); talloc_free(ad_options); -- 2.37.0 From 0f70d2f4f55dd3effd054a040a33811172f0ffda Mon Sep 17 00:00:00 2001 From: Sumit Bose <sbose@redhat.com> Date: Tue, 5 Jun 2018 17:44:59 +0200 Subject: [PATCH 06/24] krb5: refactor removal of krb5info files Currently a persistent offline callback removes the krb5info files for the configured main domain and those files were removed by a SIGTERM signal handlers as well. This does not scale if krb5info files are created for sub-domains as well. To remove the files automatically the removal is moved into a talloc destructor of an offline callback which is added if the file is created and frees itself when the system goes offline. Due to the talloc memory hierarchy we get removal on shutdown for free. Related to https://pagure.io/SSSD/sssd/issue/3652 Reviewed-by: Jakub Hrozek <jhrozek@redhat.com> (cherry picked from commit d91661e295c8e878f1bbf34e6f65f61e8301bf0e) --- src/providers/ad/ad_common.c | 7 +- src/providers/ipa/ipa_common.c | 5 +- src/providers/krb5/krb5_common.c | 176 +++++++++++++------------- src/providers/krb5/krb5_common.h | 7 +- src/providers/krb5/krb5_init_shared.c | 6 - src/providers/ldap/ldap_common.c | 87 ------------- 6 files changed, 102 insertions(+), 186 deletions(-) diff --git a/src/providers/ad/ad_common.c b/src/providers/ad/ad_common.c index fd7ab0bf9..27a39070f 100644 --- a/src/providers/ad/ad_common.c +++ b/src/providers/ad/ad_common.c @@ -850,6 +850,8 @@ ad_failover_init(TALLOC_CTX *mem_ctx, struct be_ctx *bectx, goto done; } + service->krb5_service->be_ctx = bectx; + if (!primary_servers) { DEBUG(SSSDBG_CONF_SETTINGS, "No primary servers defined, using service discovery\n"); @@ -1035,8 +1037,9 @@ ad_resolve_callback(void *private_data, struct fo_server *server) goto done; } - ret = write_krb5info_file(service->krb5_service->realm, safe_address, - SSS_KRB5KDC_FO_SRV); + ret = write_krb5info_file(service->krb5_service, + safe_address, + SSS_KRB5KDC_FO_SRV); if (ret != EOK) { DEBUG(SSSDBG_MINOR_FAILURE, "write_krb5info_file failed, authentication might fail.\n"); diff --git a/src/providers/ipa/ipa_common.c b/src/providers/ipa/ipa_common.c index 2b81d7f3f..3342cd76d 100644 --- a/src/providers/ipa/ipa_common.c +++ b/src/providers/ipa/ipa_common.c @@ -836,7 +836,8 @@ static void ipa_resolve_callback(void *private_data, struct fo_server *server) return; } - ret = write_krb5info_file(service->krb5_service->realm, safe_address, + ret = write_krb5info_file(service->krb5_service, + safe_address, SSS_KRB5KDC_FO_SRV); if (ret != EOK) { DEBUG(SSSDBG_OP_FAILURE, @@ -1010,6 +1011,8 @@ int ipa_service_init(TALLOC_CTX *memctx, struct be_ctx *ctx, goto done; } + service->krb5_service->be_ctx = ctx; + if (!primary_servers) { DEBUG(SSSDBG_CONF_SETTINGS, "No primary servers defined, using service discovery\n"); diff --git a/src/providers/krb5/krb5_common.c b/src/providers/krb5/krb5_common.c index 520e7591c..c6896a6cd 100644 --- a/src/providers/krb5/krb5_common.c +++ b/src/providers/krb5/krb5_common.c @@ -389,7 +389,76 @@ done: return ret; } -errno_t write_krb5info_file(const char *realm, const char *server, +static int remove_info_files_destructor(void *p) +{ + int ret; + struct remove_info_files_ctx *ctx = talloc_get_type(p, + struct remove_info_files_ctx); + + ret = remove_krb5_info_files(ctx, ctx->realm); + if (ret != EOK) { + DEBUG(SSSDBG_CRIT_FAILURE, "remove_krb5_info_files failed.\n"); + } + + return 0; +} + +static errno_t +krb5_add_krb5info_offline_callback(struct krb5_service *krb5_service) +{ + int ret; + struct remove_info_files_ctx *ctx; + + if (krb5_service == NULL || krb5_service->name == NULL + || krb5_service->realm == NULL + || krb5_service->be_ctx == NULL) { + DEBUG(SSSDBG_CRIT_FAILURE, "Missing KDC service name or realm!\n"); + return EINVAL; + } + + ctx = talloc_zero(krb5_service->be_ctx, struct remove_info_files_ctx); + if (ctx == NULL) { + DEBUG(SSSDBG_CRIT_FAILURE, "talloc_zfree failed.\n"); + return ENOMEM; + } + + ctx->realm = talloc_strdup(ctx, krb5_service->realm); + if (ctx->realm == NULL) { + DEBUG(SSSDBG_CRIT_FAILURE, "talloc_strdup failed!\n"); + ret = ENOMEM; + goto done; + } + + ctx->be_ctx = krb5_service->be_ctx; + ctx->kdc_service_name = talloc_strdup(ctx, krb5_service->name); + if (ctx->kdc_service_name == NULL) { + DEBUG(SSSDBG_CRIT_FAILURE, "talloc_strdup failed!\n"); + ret = ENOMEM; + goto done; + } + + ret = be_add_offline_cb(ctx, krb5_service->be_ctx, + remove_krb5_info_files_callback, ctx, NULL); + if (ret != EOK) { + DEBUG(SSSDBG_CRIT_FAILURE, "be_add_offline_cb failed.\n"); + goto done; + } + + talloc_set_destructor((TALLOC_CTX *) ctx, remove_info_files_destructor); + + ret = EOK; + +done: + if (ret != EOK) { + talloc_zfree(ctx); + } + + return ret; +} + + +errno_t write_krb5info_file(struct krb5_service *krb5_service, + const char *server, const char *service) { int ret; @@ -401,17 +470,19 @@ errno_t write_krb5info_file(const char *realm, const char *server, size_t server_len; ssize_t written; - if (realm == NULL || *realm == '\0' || server == NULL || *server == '\0' || - service == NULL || *service == '\0') { + if (krb5_service == NULL || krb5_service->realm == NULL + || *krb5_service->realm == '\0' + || server == NULL || *server == '\0' + || service == NULL || *service == '\0') { DEBUG(SSSDBG_CRIT_FAILURE, "Missing or empty realm, server or service.\n"); return EINVAL; } - if (sss_krb5_realm_has_proxy(realm)) { + if (sss_krb5_realm_has_proxy(krb5_service->realm)) { DEBUG(SSSDBG_CONF_SETTINGS, "KDC Proxy available for realm [%s], no kdcinfo file created.\n", - realm); + krb5_service->realm); return EOK; } @@ -439,7 +510,7 @@ errno_t write_krb5info_file(const char *realm, const char *server, goto done; } - krb5info_name = talloc_asprintf(tmp_ctx, name_tmpl, realm); + krb5info_name = talloc_asprintf(tmp_ctx, name_tmpl, krb5_service->realm); if (krb5info_name == NULL) { DEBUG(SSSDBG_CRIT_FAILURE, "talloc_asprintf failed.\n"); ret = ENOMEM; @@ -495,6 +566,12 @@ errno_t write_krb5info_file(const char *realm, const char *server, goto done; } + ret = krb5_add_krb5info_offline_callback(krb5_service); + if (ret != EOK) { + DEBUG(SSSDBG_OP_FAILURE, "Failed to add offline callback, krb5info " + "file might not be removed properly.\n"); + } + ret = EOK; done: if (fd != -1) { @@ -561,7 +638,8 @@ static void krb5_resolve_callback(void *private_data, struct fo_server *server) return; } - ret = write_krb5info_file(krb5_service->realm, safe_address, + ret = write_krb5info_file(krb5_service, + safe_address, krb5_service->name); if (ret != EOK) { DEBUG(SSSDBG_OP_FAILURE, @@ -761,6 +839,7 @@ int krb5_service_init(TALLOC_CTX *memctx, struct be_ctx *ctx, } service->write_kdcinfo = use_kdcinfo; + service->be_ctx = ctx; if (!primary_servers) { DEBUG(SSSDBG_CONF_SETTINGS, @@ -839,7 +918,6 @@ errno_t remove_krb5_info_files(TALLOC_CTX *mem_ctx, const char *realm) void remove_krb5_info_files_callback(void *pvt) { int ret; - TALLOC_CTX *tmp_ctx = NULL; struct remove_info_files_ctx *ctx = talloc_get_type(pvt, struct remove_info_files_ctx); @@ -864,19 +942,10 @@ void remove_krb5_info_files_callback(void *pvt) } } - tmp_ctx = talloc_new(NULL); - if (tmp_ctx == NULL) { - DEBUG(SSSDBG_CRIT_FAILURE, - "talloc_new failed, cannot remove krb5 info files.\n"); - return; - } - - ret = remove_krb5_info_files(tmp_ctx, ctx->realm); - if (ret != EOK) { - DEBUG(SSSDBG_CRIT_FAILURE, "remove_krb5_info_files failed.\n"); - } - - talloc_zfree(tmp_ctx); + /* Freeing the remove_info_files_ctx will remove the related krb5info + * file. Additionally the callback from the list of callbacks is removed, + * it will be added again when a new krb5info file is created. */ + talloc_free(ctx); } void krb5_finalize(struct tevent_context *ev, @@ -886,74 +955,9 @@ void krb5_finalize(struct tevent_context *ev, void *siginfo, void *private_data) { - char *realm = (char *)private_data; - int ret; - - ret = remove_krb5_info_files(se, realm); - if (ret != EOK) { - DEBUG(SSSDBG_CRIT_FAILURE, "remove_krb5_info_files failed.\n"); - } - orderly_shutdown(0); } -errno_t krb5_install_offline_callback(struct be_ctx *be_ctx, - struct krb5_ctx *krb5_ctx) -{ - int ret; - struct remove_info_files_ctx *ctx; - const char *krb5_realm; - - if (krb5_ctx->service == NULL || krb5_ctx->service->name == NULL) { - DEBUG(SSSDBG_CRIT_FAILURE, "Missing KDC service name!\n"); - return EINVAL; - } - - ctx = talloc_zero(krb5_ctx, struct remove_info_files_ctx); - if (ctx == NULL) { - DEBUG(SSSDBG_CRIT_FAILURE, "talloc_zfree failed.\n"); - return ENOMEM; - } - - krb5_realm = dp_opt_get_cstring(krb5_ctx->opts, KRB5_REALM); - if (krb5_realm == NULL) { - DEBUG(SSSDBG_CRIT_FAILURE, "Missing krb5_realm option!\n"); - ret = EINVAL; - goto done; - } - - ctx->realm = talloc_strdup(ctx, krb5_realm); - if (ctx->realm == NULL) { - DEBUG(SSSDBG_CRIT_FAILURE, "talloc_strdup failed!\n"); - ret = ENOMEM; - goto done; - } - - ctx->be_ctx = be_ctx; - ctx->kdc_service_name = krb5_ctx->service->name; - if (krb5_ctx->kpasswd_service == NULL) { - ctx->kpasswd_service_name =NULL; - } else { - ctx->kpasswd_service_name = krb5_ctx->kpasswd_service->name; - } - - ret = be_add_offline_cb(ctx, be_ctx, remove_krb5_info_files_callback, ctx, - NULL); - if (ret != EOK) { - DEBUG(SSSDBG_CRIT_FAILURE, "be_add_offline_cb failed.\n"); - goto done; - } - - ret = EOK; - -done: - if (ret != EOK) { - talloc_zfree(ctx); - } - - return ret; -} - errno_t krb5_install_sigterm_handler(struct tevent_context *ev, struct krb5_ctx *krb5_ctx) { diff --git a/src/providers/krb5/krb5_common.h b/src/providers/krb5/krb5_common.h index 3e7b62422..a432adc47 100644 --- a/src/providers/krb5/krb5_common.h +++ b/src/providers/krb5/krb5_common.h @@ -68,6 +68,7 @@ enum krb5_opts { typedef enum { INIT_PW, INIT_KT, RENEW, VALIDATE } action_type; struct krb5_service { + struct be_ctx *be_ctx; char *name; char *realm; bool write_kdcinfo; @@ -158,7 +159,8 @@ errno_t krb5_try_kdcip(struct confdb_ctx *cdb, const char *conf_path, errno_t sss_krb5_get_options(TALLOC_CTX *memctx, struct confdb_ctx *cdb, const char *conf_path, struct dp_option **_opts); -errno_t write_krb5info_file(const char *realm, const char *kdc, +errno_t write_krb5info_file(struct krb5_service *krb5_service, + const char *server, const char *service); int krb5_service_init(TALLOC_CTX *memctx, struct be_ctx *ctx, @@ -178,9 +180,6 @@ void krb5_finalize(struct tevent_context *ev, void *siginfo, void *private_data); -errno_t krb5_install_offline_callback(struct be_ctx *be_ctx, - struct krb5_ctx *krb_ctx); - errno_t krb5_install_sigterm_handler(struct tevent_context *ev, struct krb5_ctx *krb5_ctx); diff --git a/src/providers/krb5/krb5_init_shared.c b/src/providers/krb5/krb5_init_shared.c index 92f722deb..1be05a155 100644 --- a/src/providers/krb5/krb5_init_shared.c +++ b/src/providers/krb5/krb5_init_shared.c @@ -71,12 +71,6 @@ errno_t krb5_child_init(struct krb5_ctx *krb5_auth_ctx, goto done; } - ret = krb5_install_offline_callback(bectx, krb5_auth_ctx); - if (ret != EOK) { - DEBUG(SSSDBG_CRIT_FAILURE, "krb5_install_offline_callback failed.\n"); - goto done; - } - ret = krb5_install_sigterm_handler(bectx->ev, krb5_auth_ctx); if (ret != EOK) { DEBUG(SSSDBG_CRIT_FAILURE, "krb5_install_sigterm_handler failed.\n"); diff --git a/src/providers/ldap/ldap_common.c b/src/providers/ldap/ldap_common.c index 70c5429e8..414ea78f0 100644 --- a/src/providers/ldap/ldap_common.c +++ b/src/providers/ldap/ldap_common.c @@ -161,14 +161,6 @@ static void sdap_finalize(struct tevent_context *ev, void *siginfo, void *private_data) { - char *realm = (char *) private_data; - int ret; - - ret = remove_krb5_info_files(se, realm); - if (ret != EOK) { - DEBUG(SSSDBG_CRIT_FAILURE, "remove_krb5_info_files failed.\n"); - } - orderly_shutdown(0); } @@ -199,78 +191,6 @@ errno_t sdap_install_sigterm_handler(TALLOC_CTX *mem_ctx, return EOK; } -void sdap_remove_kdcinfo_files_callback(void *pvt) -{ - int ret; - TALLOC_CTX *tmp_ctx = NULL; - struct remove_info_files_ctx *ctx = talloc_get_type(pvt, - struct remove_info_files_ctx); - - ret = be_fo_run_callbacks_at_next_request(ctx->be_ctx, - ctx->kdc_service_name); - if (ret != EOK) { - DEBUG(SSSDBG_CRIT_FAILURE, - "be_fo_run_callbacks_at_next_request failed, " - "krb5 info files will not be removed, because " - "it is unclear if they will be recreated properly.\n"); - return; - } - - tmp_ctx = talloc_new(NULL); - if (tmp_ctx == NULL) { - DEBUG(SSSDBG_CRIT_FAILURE, - "talloc_new failed, cannot remove krb5 info files.\n"); - return; - } - - ret = remove_krb5_info_files(tmp_ctx, ctx->realm); - if (ret != EOK) { - DEBUG(SSSDBG_CRIT_FAILURE, "remove_krb5_info_files failed.\n"); - } - - talloc_zfree(tmp_ctx); -} - - -errno_t sdap_install_offline_callback(TALLOC_CTX *mem_ctx, - struct be_ctx *be_ctx, - const char *realm, - const char *service_name) -{ - int ret; - struct remove_info_files_ctx *ctx; - - ctx = talloc_zero(mem_ctx, struct remove_info_files_ctx); - if (ctx == NULL) { - DEBUG(SSSDBG_CRIT_FAILURE, "talloc_zfree failed.\n"); - return ENOMEM; - } - - ctx->be_ctx = be_ctx; - ctx->realm = talloc_strdup(ctx, realm); - ctx->kdc_service_name = talloc_strdup(ctx, service_name); - if (ctx->realm == NULL || ctx->kdc_service_name == NULL) { - DEBUG(SSSDBG_CRIT_FAILURE, "talloc_strdup failed!\n"); - ret = ENOMEM; - goto done; - } - - ret = be_add_offline_cb(ctx, be_ctx, - sdap_remove_kdcinfo_files_callback, - ctx, NULL); - if (ret != EOK) { - DEBUG(SSSDBG_CRIT_FAILURE, "be_add_offline_cb failed.\n"); - goto done; - } - - ret = EOK; -done: - if (ret != EOK) { - talloc_zfree(ctx); - } - return ret; -} - errno_t sdap_set_sasl_options(struct sdap_options *id_opts, char *default_primary, @@ -461,13 +381,6 @@ int sdap_gssapi_init(TALLOC_CTX *mem_ctx, goto done; } - ret = sdap_install_offline_callback(mem_ctx, bectx, - krb5_realm, SSS_KRB5KDC_FO_SRV); - if (ret != EOK) { - DEBUG(SSSDBG_FATAL_FAILURE, "Failed to install sigterm handler\n"); - goto done; - } - sdap_service->kinit_service_name = talloc_strdup(sdap_service, service->name); if (sdap_service->kinit_service_name == NULL) { -- 2.37.0 From 69c8078eddc7329d97dcdf09edb00cdb6634fa8c Mon Sep 17 00:00:00 2001 From: Sumit Bose <sbose@redhat.com> Date: Fri, 8 Jun 2018 08:29:04 +0200 Subject: [PATCH 07/24] krb5_common: add callback only once Reviewed-by: Jakub Hrozek <jhrozek@redhat.com> (cherry picked from commit 4759a482781bcecdb0ad1119e74dcefa1fe94337) --- src/providers/krb5/krb5_common.c | 12 +++++++++++- src/providers/krb5/krb5_common.h | 2 ++ 2 files changed, 13 insertions(+), 1 deletion(-) diff --git a/src/providers/krb5/krb5_common.c b/src/providers/krb5/krb5_common.c index c6896a6cd..d064a09ac 100644 --- a/src/providers/krb5/krb5_common.c +++ b/src/providers/krb5/krb5_common.c @@ -399,6 +399,7 @@ static int remove_info_files_destructor(void *p) if (ret != EOK) { DEBUG(SSSDBG_CRIT_FAILURE, "remove_krb5_info_files failed.\n"); } + ctx->krb5_service->removal_callback_available = false; return 0; } @@ -407,7 +408,7 @@ static errno_t krb5_add_krb5info_offline_callback(struct krb5_service *krb5_service) { int ret; - struct remove_info_files_ctx *ctx; + struct remove_info_files_ctx *ctx = NULL; if (krb5_service == NULL || krb5_service->name == NULL || krb5_service->realm == NULL @@ -416,6 +417,13 @@ krb5_add_krb5info_offline_callback(struct krb5_service *krb5_service) return EINVAL; } + if (krb5_service->removal_callback_available) { + DEBUG(SSSDBG_TRACE_ALL, + "Removal callback already available for service [%s].\n", + krb5_service->name); + return EOK; + } + ctx = talloc_zero(krb5_service->be_ctx, struct remove_info_files_ctx); if (ctx == NULL) { DEBUG(SSSDBG_CRIT_FAILURE, "talloc_zfree failed.\n"); @@ -430,6 +438,7 @@ krb5_add_krb5info_offline_callback(struct krb5_service *krb5_service) } ctx->be_ctx = krb5_service->be_ctx; + ctx->krb5_service = krb5_service; ctx->kdc_service_name = talloc_strdup(ctx, krb5_service->name); if (ctx->kdc_service_name == NULL) { DEBUG(SSSDBG_CRIT_FAILURE, "talloc_strdup failed!\n"); @@ -445,6 +454,7 @@ krb5_add_krb5info_offline_callback(struct krb5_service *krb5_service) } talloc_set_destructor((TALLOC_CTX *) ctx, remove_info_files_destructor); + krb5_service->removal_callback_available = true; ret = EOK; diff --git a/src/providers/krb5/krb5_common.h b/src/providers/krb5/krb5_common.h index a432adc47..7d2d1bb10 100644 --- a/src/providers/krb5/krb5_common.h +++ b/src/providers/krb5/krb5_common.h @@ -72,6 +72,7 @@ struct krb5_service { char *name; char *realm; bool write_kdcinfo; + bool removal_callback_available; }; struct fo_service; @@ -147,6 +148,7 @@ struct remove_info_files_ctx { struct be_ctx *be_ctx; const char *kdc_service_name; const char *kpasswd_service_name; + struct krb5_service *krb5_service; }; errno_t sss_krb5_check_options(struct dp_option *opts, -- 2.37.0 From d42ab824f0c45daca2bbddc3293596de5801ca67 Mon Sep 17 00:00:00 2001 From: Sumit Bose <sbose@redhat.com> Date: Fri, 8 Jun 2018 18:42:28 +0200 Subject: [PATCH 08/24] data provider: run offline callbacks only once Reviewed-by: Jakub Hrozek <jhrozek@redhat.com> (cherry picked from commit f28d995719db632130e9e063cb1ab7cb4e0fc8d8) --- src/providers/backend.h | 1 + src/providers/data_provider_be.c | 1 + src/providers/data_provider_callbacks.c | 36 +++++++++++++++++++------ 3 files changed, 30 insertions(+), 8 deletions(-) diff --git a/src/providers/backend.h b/src/providers/backend.h index 191427403..6a34b91a9 100644 --- a/src/providers/backend.h +++ b/src/providers/backend.h @@ -95,6 +95,7 @@ struct be_ctx { struct be_cb *online_cb_list; bool run_online_cb; struct be_cb *offline_cb_list; + bool run_offline_cb; struct be_cb *reconnect_cb_list; /* In contrast to online_cb_list which are only run if the backend is * offline the unconditional_online_cb_list should be run whenever the diff --git a/src/providers/data_provider_be.c b/src/providers/data_provider_be.c index 586a4be21..70158efb7 100644 --- a/src/providers/data_provider_be.c +++ b/src/providers/data_provider_be.c @@ -219,6 +219,7 @@ static void be_reset_offline(struct be_ctx *ctx) { ctx->offstat.went_offline = 0; ctx->offstat.offline = false; + ctx->run_offline_cb = true; reactivate_subdoms(ctx->domain); diff --git a/src/providers/data_provider_callbacks.c b/src/providers/data_provider_callbacks.c index 436357e22..24e125ea5 100644 --- a/src/providers/data_provider_callbacks.c +++ b/src/providers/data_provider_callbacks.c @@ -265,22 +265,42 @@ void be_run_unconditional_online_cb(struct be_ctx *be) int be_add_offline_cb(TALLOC_CTX *mem_ctx, struct be_ctx *ctx, be_callback_t cb, void *pvt, struct be_cb **offline_cb) { - return be_add_cb(mem_ctx, ctx, cb, pvt, &ctx->offline_cb_list, offline_cb); + int ret; + + ret = be_add_cb(mem_ctx, ctx, cb, pvt, &ctx->offline_cb_list, offline_cb); + if (ret != EOK) { + DEBUG(SSSDBG_CRIT_FAILURE, "be_add_cb failed.\n"); + return ret; + } + + /* Make sure we run the callback when SSSD goes offline */ + ctx->run_offline_cb = true; + + return EOK; } void be_run_offline_cb(struct be_ctx *be) { int ret; - if (be->offline_cb_list) { - DEBUG(SSSDBG_MINOR_FAILURE, "Going offline. Running callbacks.\n"); + if (be->run_offline_cb) { + /* Reset the flag, we only want to run these callbacks once when going + * offline */ + be->run_offline_cb = false; - ret = be_run_cb(be, be->offline_cb_list); - if (ret != EOK) { - DEBUG(SSSDBG_CRIT_FAILURE, "be_run_cb failed.\n"); - } + if (be->offline_cb_list) { + DEBUG(SSSDBG_MINOR_FAILURE, "Going offline. Running callbacks.\n"); + ret = be_run_cb(be, be->offline_cb_list); + if (ret != EOK) { + DEBUG(SSSDBG_CRIT_FAILURE, "be_run_cb failed.\n"); + } + + } else { + DEBUG(SSSDBG_TRACE_ALL, + "Offline call back list is empty, nothing to do.\n"); + } } else { DEBUG(SSSDBG_TRACE_ALL, - "Offline call back list is empty, nothing to do.\n"); + "Flag indicates that offline callback were already called.\n"); } } -- 2.37.0 From 6f443148f48d5fe96880e7c271a4334d7ad95878 Mon Sep 17 00:00:00 2001 From: Jakub Hrozek <jhrozek@redhat.com> Date: Mon, 25 Jun 2018 13:03:38 +0200 Subject: [PATCH 09/24] AD: expose the helper function to format the site DNS query This function will be used later in the patchset. Instead of exposing the format constant, expose the function that builds the DNS query for site discovery. Related: https://pagure.io/SSSD/sssd/issue/3291 Reviewed-by: Sumit Bose <sbose@redhat.com> (cherry picked from commit 29bbc8e017f2d9b98667890a9b7056128a93e572) --- src/providers/ad/ad_srv.c | 21 +++++++++++++++------ src/providers/ad/ad_srv.h | 4 ++++ 2 files changed, 19 insertions(+), 6 deletions(-) diff --git a/src/providers/ad/ad_srv.c b/src/providers/ad/ad_srv.c index 4056d3e70..ca15d3715 100644 --- a/src/providers/ad/ad_srv.c +++ b/src/providers/ad/ad_srv.c @@ -38,6 +38,13 @@ #define AD_SITE_DOMAIN_FMT "%s._sites.%s" +char *ad_site_dns_discovery_domain(TALLOC_CTX *mem_ctx, + const char *site, + const char *domain) +{ + return talloc_asprintf(mem_ctx, AD_SITE_DOMAIN_FMT, site, domain); +} + static errno_t ad_sort_servers_by_dns(TALLOC_CTX *mem_ctx, const char *domain, struct fo_server_info **_srv, @@ -154,8 +161,8 @@ static struct tevent_req *ad_get_dc_servers_send(TALLOC_CTX *mem_ctx, DEBUG(SSSDBG_TRACE_FUNC, "Looking up domain controllers in domain " "%s and site %s\n", discovery_domain, site); - domains[0] = talloc_asprintf(state, AD_SITE_DOMAIN_FMT, - site, discovery_domain); + domains[0] = ad_site_dns_discovery_domain(domains, + site, discovery_domain); if (domains[0] == NULL) { ret = ENOMEM; goto immediately; @@ -785,9 +792,10 @@ static void ad_srv_plugin_site_done(struct tevent_req *subreq) if (strcmp(state->service, "gc") == 0) { if (state->forest != NULL) { if (state->site != NULL) { - primary_domain = talloc_asprintf(state, AD_SITE_DOMAIN_FMT, - state->site, - state->forest); + primary_domain = ad_site_dns_discovery_domain( + state, + state->site, + state->forest); if (primary_domain == NULL) { ret = ENOMEM; goto done; @@ -801,7 +809,8 @@ static void ad_srv_plugin_site_done(struct tevent_req *subreq) } } else { if (state->site != NULL) { - primary_domain = talloc_asprintf(state, AD_SITE_DOMAIN_FMT, + primary_domain = ad_site_dns_discovery_domain( + state, state->site, state->discovery_domain); if (primary_domain == NULL) { diff --git a/src/providers/ad/ad_srv.h b/src/providers/ad/ad_srv.h index 59377e096..8e410ec26 100644 --- a/src/providers/ad/ad_srv.h +++ b/src/providers/ad/ad_srv.h @@ -50,4 +50,8 @@ errno_t ad_srv_plugin_recv(TALLOC_CTX *mem_ctx, struct fo_server_info **_backup_servers, size_t *_num_backup_servers); +char *ad_site_dns_discovery_domain(TALLOC_CTX *mem_ctx, + const char *site, + const char *domain); + #endif /* __AD_SRV_H__ */ -- 2.37.0 From 830c4c499051301725ef6948e62610d10094e4b8 Mon Sep 17 00:00:00 2001 From: Jakub Hrozek <jhrozek@redhat.com> Date: Mon, 25 Jun 2018 12:46:51 +0200 Subject: [PATCH 10/24] RESOLV: Add a resolv_hostport_list request Adds a request that resolves a list of (host,port) tuples and returns a list of structures that contain the resolv_hostent structure as other resolver requests do, but also a pointer to the original request tuple. This is done because the request skips any unresolvable inputs, so it might be handy to know which input an output maps to. It is expected that the request will be used in the future also for cases where we want to e.g. try the connectivity to a serve using a mechanism such as an LDAP ping. Related: https://pagure.io/SSSD/sssd/issue/3291 Reviewed-by: Sumit Bose <sbose@redhat.com> (cherry picked from commit 6f80bccc6f8203381c387080bd0563ba10994487) --- src/resolv/async_resolv.c | 187 ++++++++++++++++++++++++++++++++++++++ src/resolv/async_resolv.h | 30 ++++++ 2 files changed, 217 insertions(+) diff --git a/src/resolv/async_resolv.c b/src/resolv/async_resolv.c index 40553037d..17174b3e7 100644 --- a/src/resolv/async_resolv.c +++ b/src/resolv/async_resolv.c @@ -2326,3 +2326,190 @@ resolv_sort_srv_reply(struct ares_srv_reply **reply) return EOK; } + +struct resolv_hostport_list_state { + struct tevent_context *ev; + struct resolv_ctx *ctx; + struct resolv_hostport *hostport_list; + size_t list_size; + size_t limit; + enum restrict_family family_order; + enum host_database *db; + + size_t hpindex; + + struct resolv_hostport_addr **rhp_addrs; + size_t addrindex; +}; + +static errno_t resolv_hostport_list_step(struct tevent_req *req); +static void resolv_hostport_list_resolv_hostname_done(struct tevent_req *subreq); + +struct tevent_req *resolv_hostport_list_send(TALLOC_CTX *mem_ctx, + struct tevent_context *ev, + struct resolv_ctx *ctx, + struct resolv_hostport *hostport_list, + size_t list_size, + size_t limit, + enum restrict_family family_order, + enum host_database *db) +{ + struct resolv_hostport_list_state *state; + struct tevent_req *req; + errno_t ret; + + req = tevent_req_create(mem_ctx, &state, + struct resolv_hostport_list_state); + if (req == NULL) { + return NULL; + } + state->ev = ev; + state->ctx = ctx; + state->hostport_list = hostport_list; + state->family_order = family_order; + state->db = db; + state->list_size = list_size; + state->limit = limit; + + state->rhp_addrs = talloc_array(state, + struct resolv_hostport_addr *, + state->list_size); + if (state->rhp_addrs == NULL) { + ret = ENOMEM; + goto immediately; + } + + ret = resolv_hostport_list_step(req); + if (ret != EAGAIN) { + goto immediately; + } + + return req; + +immediately: + if (ret != EOK) { + tevent_req_error(req, ret); + } else { + tevent_req_done(req); + } + tevent_req_post(req, ev); + return req; +} + +static errno_t resolv_hostport_list_step(struct tevent_req *req) +{ + struct tevent_req *subreq = NULL; + struct resolv_hostport_list_state *state = tevent_req_data(req, + struct resolv_hostport_list_state); + + if (state->hpindex >= state->list_size) { + DEBUG(SSSDBG_TRACE_INTERNAL, "Done\n"); + return EOK; + } + + subreq = resolv_gethostbyname_send(state, + state->ev, + state->ctx, + state->hostport_list[state->hpindex].host, + state->family_order, + state->db); + if (subreq == NULL) { + return ENOMEM; + } + tevent_req_set_callback(subreq, + resolv_hostport_list_resolv_hostname_done, req); + return EAGAIN; +} + +static struct resolv_hostport_addr* +resolv_hostport_addr_new(TALLOC_CTX *mem_ctx, + const char *host, + int port, + struct resolv_hostent *reply) +{ + struct resolv_hostport_addr *rhp_addr; + + rhp_addr = talloc_zero(mem_ctx, struct resolv_hostport_addr); + if (rhp_addr == NULL) { + return NULL; + } + + rhp_addr->origin.host = talloc_strdup(rhp_addr, host); + if (rhp_addr->origin.host == NULL) { + return NULL; + } + + rhp_addr->origin.port = port; + rhp_addr->reply = talloc_steal(rhp_addr, reply); + return rhp_addr; +} + +static void resolv_hostport_list_resolv_hostname_done(struct tevent_req *subreq) +{ + errno_t ret; + struct tevent_req *req = + tevent_req_callback_data(subreq, struct tevent_req); + struct resolv_hostport_list_state *state = tevent_req_data(req, + struct resolv_hostport_list_state); + struct resolv_hostent *rhostent; + int resolv_status; + + ret = resolv_gethostbyname_recv(subreq, state, &resolv_status, NULL, + &rhostent); + talloc_zfree(subreq); + + if (ret != EOK) { + /* Don't abort the request, just go to the next one */ + DEBUG(SSSDBG_OP_FAILURE, + "Could not resolve address for this machine, error [%d]: %s, " + "resolver returned: [%d]: %s\n", ret, sss_strerror(ret), + resolv_status, resolv_strerror(resolv_status)); + } else { + state->rhp_addrs[state->addrindex] = \ + resolv_hostport_addr_new(state->rhp_addrs, + state->hostport_list[state->hpindex].host, + state->hostport_list[state->hpindex].port, + rhostent); + state->addrindex++; + + if (state->limit > 0 && state->addrindex >= state->limit) { + DEBUG(SSSDBG_TRACE_INTERNAL, + "Reached the limit or addresses to resolve\n"); + tevent_req_done(req); + return; + } + } + + state->hpindex++; + + ret = resolv_hostport_list_step(req); + if (ret == EOK) { + tevent_req_done(req); + return; + } else if (ret != EAGAIN) { + tevent_req_error(req, ret); + return; + } + /* Next iteration .. */ +} + +int resolv_hostport_list_recv(struct tevent_req *req, + TALLOC_CTX *mem_ctx, + size_t *_rhp_len, + struct resolv_hostport_addr ***_rhp_addrs) +{ + struct resolv_hostport_list_state *state = tevent_req_data(req, + struct resolv_hostport_list_state); + + TEVENT_REQ_RETURN_ON_ERROR(req); + + if (_rhp_len) { + *_rhp_len = state->addrindex; + } + + if (_rhp_addrs) { + *_rhp_addrs = talloc_steal(mem_ctx, state->rhp_addrs); + } + + return EOK; +} diff --git a/src/resolv/async_resolv.h b/src/resolv/async_resolv.h index b602a425c..90ed03707 100644 --- a/src/resolv/async_resolv.h +++ b/src/resolv/async_resolv.h @@ -112,6 +112,36 @@ int resolv_gethostbyname_recv(struct tevent_req *req, TALLOC_CTX *mem_ctx, int *status, int *timeouts, struct resolv_hostent **rhostent); +struct resolv_hostport { + const char *host; + int port; +}; + +struct resolv_hostport_addr { + struct resolv_hostport origin; + struct resolv_hostent *reply; +}; + +/* Resolves a list of resolv_hostport tuples into a list of + * resolv_hostport_addr. Any unresolvable addresses are skipped. + * + * Optionally takes a limit argument and stops after the request + * had resolved addresses up to the limit. + */ +struct tevent_req *resolv_hostport_list_send(TALLOC_CTX *mem_ctx, + struct tevent_context *ev, + struct resolv_ctx *ctx, + struct resolv_hostport *hostport_list, + size_t list_size, + size_t limit, + enum restrict_family family_order, + enum host_database *db); + +int resolv_hostport_list_recv(struct tevent_req *req, + TALLOC_CTX *mem_ctx, + size_t *_rhp_len, + struct resolv_hostport_addr ***_rhp_addrs); + char * resolv_get_string_address_index(TALLOC_CTX *mem_ctx, struct resolv_hostent *hostent, -- 2.37.0 From cb49c36018e1d978c2ce01e99d7d0f1b03e61b21 Mon Sep 17 00:00:00 2001 From: Jakub Hrozek <jhrozek@redhat.com> Date: Mon, 25 Jun 2018 13:08:25 +0200 Subject: [PATCH 11/24] KRB5/IPA/AD: Add a utility function to create a krb5_service instance Each Kerberized provider used hand-crafted copy-paste code to set up its copy of the krb5_service structure. Instead of adding yet another copy in this patchset in the IPA subdomains code, create a utility function instead. Due to IPA provider first creating the krb5_service in the common setup function, but only later reading the auth options in the auth provider constructor, the code first uses the default true value for the use_kdcinfo flag and then overrides it with the configured value in the auth constructor -- it would be preferable to create the structure with the right value at creation time, but this would require bigger refactoring. Also, the code before this change was even less correct as the flag was initially set the "false" due to the structure being allocated with talloc_zero(). At least now it uses the default value. Related: https://pagure.io/SSSD/sssd/issue/3291 Reviewed-by: Sumit Bose <sbose@redhat.com> (cherry picked from commit a9a9f39342ebd26425cb1b3baedfea2429d88b04) --- src/providers/ad/ad_common.c | 26 ++-------------- src/providers/ipa/ipa_common.c | 35 +++++++++------------- src/providers/krb5/krb5_common.c | 51 ++++++++++++++++++++++---------- src/providers/krb5/krb5_common.h | 6 ++++ 4 files changed, 58 insertions(+), 60 deletions(-) diff --git a/src/providers/ad/ad_common.c b/src/providers/ad/ad_common.c index 27a39070f..a6f4ce6bf 100644 --- a/src/providers/ad/ad_common.c +++ b/src/providers/ad/ad_common.c @@ -803,20 +803,14 @@ ad_failover_init(TALLOC_CTX *mem_ctx, struct be_ctx *bectx, goto done; } - service->krb5_service = talloc_zero(service, struct krb5_service); + service->krb5_service = krb5_service_new(service, bectx, + ad_service, krb5_realm, + use_kdcinfo); if (!service->krb5_service) { ret = ENOMEM; goto done; } - /* Set flag that controls whether we want to write the - * kdcinfo files at all - */ - service->krb5_service->write_kdcinfo = use_kdcinfo; - DEBUG(SSSDBG_CONF_SETTINGS, "write_kdcinfo for realm %s set to %s\n", - krb5_realm, - service->krb5_service->write_kdcinfo ? "true" : "false"); - ret = be_fo_add_service(bectx, ad_service, ad_user_data_cmp); if (ret != EOK) { DEBUG(SSSDBG_CRIT_FAILURE, "Failed to create failover service!\n"); @@ -829,12 +823,6 @@ ad_failover_init(TALLOC_CTX *mem_ctx, struct be_ctx *bectx, goto done; } - service->krb5_service->name = talloc_strdup(service->krb5_service, - ad_service); - if (!service->krb5_service->name) { - ret = ENOMEM; - goto done; - } service->sdap->kinit_service_name = service->krb5_service->name; service->gc->kinit_service_name = service->krb5_service->name; @@ -843,14 +831,6 @@ ad_failover_init(TALLOC_CTX *mem_ctx, struct be_ctx *bectx, ret = EINVAL; goto done; } - service->krb5_service->realm = - talloc_strdup(service->krb5_service, krb5_realm); - if (!service->krb5_service->realm) { - ret = ENOMEM; - goto done; - } - - service->krb5_service->be_ctx = bectx; if (!primary_servers) { DEBUG(SSSDBG_CONF_SETTINGS, diff --git a/src/providers/ipa/ipa_common.c b/src/providers/ipa/ipa_common.c index 3342cd76d..fdc44292a 100644 --- a/src/providers/ipa/ipa_common.c +++ b/src/providers/ipa/ipa_common.c @@ -963,6 +963,13 @@ int ipa_service_init(TALLOC_CTX *memctx, struct be_ctx *ctx, return ENOMEM; } + realm = dp_opt_get_string(options->basic, IPA_KRB5_REALM); + if (!realm) { + DEBUG(SSSDBG_CRIT_FAILURE, "No Kerberos realm set\n"); + ret = EINVAL; + goto done; + } + service = talloc_zero(tmp_ctx, struct ipa_service); if (!service) { ret = ENOMEM; @@ -973,7 +980,13 @@ int ipa_service_init(TALLOC_CTX *memctx, struct be_ctx *ctx, ret = ENOMEM; goto done; } - service->krb5_service = talloc_zero(service, struct krb5_service); + + service->krb5_service = krb5_service_new(service, ctx, + "IPA", realm, + true); /* The configured value + * will be set later when + * the auth provider is set up + */ if (!service->krb5_service) { ret = ENOMEM; goto done; @@ -991,28 +1004,8 @@ int ipa_service_init(TALLOC_CTX *memctx, struct be_ctx *ctx, goto done; } - service->krb5_service->name = talloc_strdup(service, "IPA"); - if (!service->krb5_service->name) { - ret = ENOMEM; - goto done; - } service->sdap->kinit_service_name = service->krb5_service->name; - realm = dp_opt_get_string(options->basic, IPA_KRB5_REALM); - if (!realm) { - DEBUG(SSSDBG_CRIT_FAILURE, "No Kerberos realm set\n"); - ret = EINVAL; - goto done; - } - service->krb5_service->realm = - talloc_strdup(service->krb5_service, realm); - if (!service->krb5_service->realm) { - ret = ENOMEM; - goto done; - } - - service->krb5_service->be_ctx = ctx; - if (!primary_servers) { DEBUG(SSSDBG_CONF_SETTINGS, "No primary servers defined, using service discovery\n"); diff --git a/src/providers/krb5/krb5_common.c b/src/providers/krb5/krb5_common.c index d064a09ac..2a50dfec5 100644 --- a/src/providers/krb5/krb5_common.c +++ b/src/providers/krb5/krb5_common.c @@ -807,6 +807,40 @@ static int krb5_user_data_cmp(void *ud1, void *ud2) return strcasecmp((char*) ud1, (char*) ud2); } +struct krb5_service *krb5_service_new(TALLOC_CTX *mem_ctx, + struct be_ctx *be_ctx, + const char *service_name, + const char *realm, + bool use_kdcinfo) +{ + struct krb5_service *service; + + service = talloc_zero(mem_ctx, struct krb5_service); + if (service == NULL) { + return NULL; + } + + service->name = talloc_strdup(service, service_name); + if (service->name == NULL) { + talloc_free(service); + return NULL; + } + + service->realm = talloc_strdup(service, realm); + if (service->realm == NULL) { + talloc_free(service); + return NULL; + } + + DEBUG(SSSDBG_CONF_SETTINGS, + "write_kdcinfo for realm %s set to %s\n", + realm, + use_kdcinfo ? "true" : "false"); + service->write_kdcinfo = use_kdcinfo; + service->be_ctx = be_ctx; + return service; +} + int krb5_service_init(TALLOC_CTX *memctx, struct be_ctx *ctx, const char *service_name, const char *primary_servers, @@ -824,7 +858,7 @@ int krb5_service_init(TALLOC_CTX *memctx, struct be_ctx *ctx, return ENOMEM; } - service = talloc_zero(tmp_ctx, struct krb5_service); + service = krb5_service_new(tmp_ctx, ctx, service_name, realm, use_kdcinfo); if (!service) { ret = ENOMEM; goto done; @@ -836,21 +870,6 @@ int krb5_service_init(TALLOC_CTX *memctx, struct be_ctx *ctx, goto done; } - service->name = talloc_strdup(service, service_name); - if (!service->name) { - ret = ENOMEM; - goto done; - } - - service->realm = talloc_strdup(service, realm); - if (!service->realm) { - ret = ENOMEM; - goto done; - } - - service->write_kdcinfo = use_kdcinfo; - service->be_ctx = ctx; - if (!primary_servers) { DEBUG(SSSDBG_CONF_SETTINGS, "No primary servers defined, using service discovery\n"); diff --git a/src/providers/krb5/krb5_common.h b/src/providers/krb5/krb5_common.h index 7d2d1bb10..a9bea3e2a 100644 --- a/src/providers/krb5/krb5_common.h +++ b/src/providers/krb5/krb5_common.h @@ -165,6 +165,12 @@ errno_t write_krb5info_file(struct krb5_service *krb5_service, const char *server, const char *service); +struct krb5_service *krb5_service_new(TALLOC_CTX *mem_ctx, + struct be_ctx *be_ctx, + const char *service_name, + const char *realm, + bool use_kdcinfo); + int krb5_service_init(TALLOC_CTX *memctx, struct be_ctx *ctx, const char *service_name, const char *primary_servers, -- 2.37.0 From 45cef51fb456f415c536d62f927a23b611c81181 Mon Sep 17 00:00:00 2001 From: Jakub Hrozek <jhrozek@redhat.com> Date: Tue, 26 Jun 2018 10:35:15 +0200 Subject: [PATCH 12/24] KRB5: Allow writing multiple addresses to the kdcinfo plugin Turns the previous write_krb5info_file() function into a static function that writes whatever input it recevies. Adds a wrapper around it that accepts a list of strings, turns that into a newline-separated string which is then passed to the original function. Related: https://pagure.io/SSSD/sssd/issue/3291 Reviewed-by: Sumit Bose <sbose@redhat.com> (cherry picked from commit 8971399c872c21769d5c62cf753c5f9df4caf8cb) --- src/providers/ad/ad_common.c | 12 ++--- src/providers/ipa/ipa_common.c | 8 ++-- src/providers/krb5/krb5_common.c | 75 +++++++++++++++++++++++++------- src/providers/krb5/krb5_common.h | 2 +- 4 files changed, 70 insertions(+), 27 deletions(-) diff --git a/src/providers/ad/ad_common.c b/src/providers/ad/ad_common.c index a6f4ce6bf..a495e0b81 100644 --- a/src/providers/ad/ad_common.c +++ b/src/providers/ad/ad_common.c @@ -894,7 +894,7 @@ ad_resolve_callback(void *private_data, struct fo_server *server) struct resolv_hostent *srvaddr; struct sockaddr_storage *sockaddr; char *address; - const char *safe_address; + char *safe_addr_list[2] = { NULL, NULL }; char *new_uri; int new_port; const char *srv_name; @@ -1008,17 +1008,17 @@ ad_resolve_callback(void *private_data, struct fo_server *server) if ((sdata == NULL || sdata->gc == false) && service->krb5_service->write_kdcinfo) { /* Write krb5 info files */ - safe_address = sss_escape_ip_address(tmp_ctx, - srvaddr->family, - address); - if (safe_address == NULL) { + safe_addr_list[0] = sss_escape_ip_address(tmp_ctx, + srvaddr->family, + address); + if (safe_addr_list[0] == NULL) { DEBUG(SSSDBG_CRIT_FAILURE, "sss_escape_ip_address failed.\n"); ret = ENOMEM; goto done; } ret = write_krb5info_file(service->krb5_service, - safe_address, + safe_addr_list, SSS_KRB5KDC_FO_SRV); if (ret != EOK) { DEBUG(SSSDBG_MINOR_FAILURE, diff --git a/src/providers/ipa/ipa_common.c b/src/providers/ipa/ipa_common.c index fdc44292a..3d414e110 100644 --- a/src/providers/ipa/ipa_common.c +++ b/src/providers/ipa/ipa_common.c @@ -764,7 +764,7 @@ static void ipa_resolve_callback(void *private_data, struct fo_server *server) struct resolv_hostent *srvaddr; struct sockaddr_storage *sockaddr; char *address; - const char *safe_address; + char *safe_addr_list[2] = { NULL, NULL }; char *new_uri; const char *srv_name; int ret; @@ -827,17 +827,17 @@ static void ipa_resolve_callback(void *private_data, struct fo_server *server) service->sdap->sockaddr = talloc_steal(service, sockaddr); if (service->krb5_service->write_kdcinfo) { - safe_address = sss_escape_ip_address(tmp_ctx, + safe_addr_list[0] = sss_escape_ip_address(tmp_ctx, srvaddr->family, address); - if (safe_address == NULL) { + if (safe_addr_list[0] == NULL) { DEBUG(SSSDBG_CRIT_FAILURE, "sss_escape_ip_address failed.\n"); talloc_free(tmp_ctx); return; } ret = write_krb5info_file(service->krb5_service, - safe_address, + safe_addr_list, SSS_KRB5KDC_FO_SRV); if (ret != EOK) { DEBUG(SSSDBG_OP_FAILURE, diff --git a/src/providers/krb5/krb5_common.c b/src/providers/krb5/krb5_common.c index 2a50dfec5..2b003e164 100644 --- a/src/providers/krb5/krb5_common.c +++ b/src/providers/krb5/krb5_common.c @@ -466,10 +466,9 @@ done: return ret; } - -errno_t write_krb5info_file(struct krb5_service *krb5_service, - const char *server, - const char *service) +static errno_t write_krb5info_file_contents(struct krb5_service *krb5_service, + const char *contents, + const char *service) { int ret; int fd = -1; @@ -482,7 +481,7 @@ errno_t write_krb5info_file(struct krb5_service *krb5_service, if (krb5_service == NULL || krb5_service->realm == NULL || *krb5_service->realm == '\0' - || server == NULL || *server == '\0' + || contents == NULL || *contents == '\0' || service == NULL || *service == '\0') { DEBUG(SSSDBG_CRIT_FAILURE, "Missing or empty realm, server or service.\n"); @@ -505,7 +504,7 @@ errno_t write_krb5info_file(struct krb5_service *krb5_service, return EINVAL; } - server_len = strlen(server); + server_len = strlen(contents); tmp_ctx = talloc_new(NULL); if (tmp_ctx == NULL) { @@ -535,7 +534,7 @@ errno_t write_krb5info_file(struct krb5_service *krb5_service, } errno = 0; - written = sss_atomic_write_s(fd, discard_const(server), server_len); + written = sss_atomic_write_s(fd, discard_const(contents), server_len); if (written == -1) { ret = errno; DEBUG(SSSDBG_CRIT_FAILURE, @@ -592,12 +591,56 @@ done: return ret; } +errno_t write_krb5info_file(struct krb5_service *krb5_service, + char **server_list, + const char *service) +{ + int i; + errno_t ret; + TALLOC_CTX *tmp_ctx = NULL; + char *contents = NULL; + + if (krb5_service == NULL || server_list == NULL || service == NULL) { + return EINVAL; + } + + if (server_list[0] == NULL) { + return EOK; + } + + tmp_ctx = talloc_new(NULL); + if (tmp_ctx == NULL) { + return ENOMEM; + } + + contents = talloc_strdup(tmp_ctx, ""); + if (contents == NULL) { + ret = ENOMEM; + goto done; + } + + i = 0; + do { + contents = talloc_asprintf_append(contents, "%s\n", server_list[i]); + if (contents == NULL) { + ret = ENOMEM; + goto done; + } + i++; + } while (server_list[i] != NULL); + + ret = write_krb5info_file_contents(krb5_service, contents, service); +done: + talloc_free(tmp_ctx); + return ret; +} + static void krb5_resolve_callback(void *private_data, struct fo_server *server) { struct krb5_service *krb5_service; struct resolv_hostent *srvaddr; char *address; - char *safe_address; + char *safe_addr_list[2] = { NULL, NULL }; int ret; TALLOC_CTX *tmp_ctx = NULL; @@ -630,26 +673,26 @@ static void krb5_resolve_callback(void *private_data, struct fo_server *server) return; } - safe_address = sss_escape_ip_address(tmp_ctx, - srvaddr->family, - address); - if (safe_address == NULL) { + safe_addr_list[0] = sss_escape_ip_address(tmp_ctx, + srvaddr->family, + address); + if (safe_addr_list[0] == NULL) { DEBUG(SSSDBG_CRIT_FAILURE, "sss_escape_ip_address failed.\n"); talloc_free(tmp_ctx); return; } if (krb5_service->write_kdcinfo) { - safe_address = talloc_asprintf_append(safe_address, ":%d", - fo_get_server_port(server)); - if (safe_address == NULL) { + safe_addr_list[0] = talloc_asprintf_append(safe_addr_list[0], ":%d", + fo_get_server_port(server)); + if (safe_addr_list[0] == NULL) { DEBUG(SSSDBG_CRIT_FAILURE, "talloc_asprintf_append failed.\n"); talloc_free(tmp_ctx); return; } ret = write_krb5info_file(krb5_service, - safe_address, + safe_addr_list, krb5_service->name); if (ret != EOK) { DEBUG(SSSDBG_OP_FAILURE, diff --git a/src/providers/krb5/krb5_common.h b/src/providers/krb5/krb5_common.h index a9bea3e2a..55489dece 100644 --- a/src/providers/krb5/krb5_common.h +++ b/src/providers/krb5/krb5_common.h @@ -162,7 +162,7 @@ errno_t sss_krb5_get_options(TALLOC_CTX *memctx, struct confdb_ctx *cdb, const char *conf_path, struct dp_option **_opts); errno_t write_krb5info_file(struct krb5_service *krb5_service, - const char *server, + char **server_list, const char *service); struct krb5_service *krb5_service_new(TALLOC_CTX *mem_ctx, -- 2.37.0 From c2976f80f9743bae8ca81703347e96850014584f Mon Sep 17 00:00:00 2001 From: Jakub Hrozek <jhrozek@redhat.com> Date: Mon, 25 Jun 2018 13:10:34 +0200 Subject: [PATCH 13/24] IPA: Add the options that the IPA subdomains code will read for trusted domains on the client With this patchset, IPA clients will read and evaluate the ad_server and ad_site options. This patch just adds the required structures for later usage. Related: https://pagure.io/SSSD/sssd/issue/3291 Reviewed-by: Sumit Bose <sbose@redhat.com> (cherry picked from commit 1cce549e0f88f4873c320577d6213dcaeb08766f) --- src/providers/ipa/ipa_common.h | 7 +++++++ src/providers/ipa/ipa_opts.c | 6 ++++++ src/providers/ipa/ipa_opts.h | 2 ++ 3 files changed, 15 insertions(+) diff --git a/src/providers/ipa/ipa_common.h b/src/providers/ipa/ipa_common.h index 3a1259ccd..f704ab1f5 100644 --- a/src/providers/ipa/ipa_common.h +++ b/src/providers/ipa/ipa_common.h @@ -175,6 +175,13 @@ enum ipa_sudocmd_attrs { IPA_OPTS_SUDOCMD }; +enum ipa_cli_ad_subdom_attrs { + IPA_CLI_AD_SERVER, + IPA_CLI_AD_SITE, + + IPA_OPTS_CLI_AD_SUBDOM +}; + struct ipa_auth_ctx { struct krb5_ctx *krb5_auth_ctx; struct sdap_id_ctx *sdap_id_ctx; diff --git a/src/providers/ipa/ipa_opts.c b/src/providers/ipa/ipa_opts.c index 49da52e05..85e4bd5ab 100644 --- a/src/providers/ipa/ipa_opts.c +++ b/src/providers/ipa/ipa_opts.c @@ -391,3 +391,9 @@ struct sdap_attr_map ipa_sudocmd_map[] = { { "ipa_sudocmd_memberof", "memberOf", SYSDB_MEMBEROF, NULL }, SDAP_ATTR_MAP_TERMINATOR }; + +struct dp_option ipa_cli_ad_subdom_opts [] = { + { "ad_server", DP_OPT_STRING, NULL_STRING, NULL_STRING }, + { "ad_site", DP_OPT_STRING, NULL_STRING, NULL_STRING }, + DP_OPTION_TERMINATOR +}; diff --git a/src/providers/ipa/ipa_opts.h b/src/providers/ipa/ipa_opts.h index 68326b864..378a9922c 100644 --- a/src/providers/ipa/ipa_opts.h +++ b/src/providers/ipa/ipa_opts.h @@ -64,4 +64,6 @@ extern struct sdap_attr_map ipa_sudocmdgroup_map[]; extern struct sdap_attr_map ipa_sudocmd_map[]; +extern struct dp_option ipa_cli_ad_subdom_opts[]; + #endif /* IPA_OPTS_H_ */ -- 2.37.0 From 83f6ea82ed184c8d6fc9a7de8b7050b55feca62e Mon Sep 17 00:00:00 2001 From: Jakub Hrozek <jhrozek@redhat.com> Date: Mon, 25 Jun 2018 13:10:39 +0200 Subject: [PATCH 14/24] IPA: Populate kdcinfo files on trust clients with configured AD servers Resolves: https://pagure.io/SSSD/sssd/issue/3291 Adds a new request into the IPA subdomains provider. This request runs on IPA clients only. The request looks into the configuration for either the ad_site or ad_server options for each subdomain. If none are found, the subdomain is skipped. If either is found, the request resolves the server names, or first the site and then the server names from the site and writes their addresses to the kdcinfo files for each subdomain. This allows programs such as kinit but also SSSD's krb5_child to use the configured servers. Reviewed-by: Sumit Bose <sbose@redhat.com> (cherry picked from commit 18b7f0a30b4745b7d61b3e599e5fb8cd399c23f3) --- src/providers/ipa/ipa_subdomains.c | 718 ++++++++++++++++++++++++++++- 1 file changed, 716 insertions(+), 2 deletions(-) diff --git a/src/providers/ipa/ipa_subdomains.c b/src/providers/ipa/ipa_subdomains.c index a8a18ad8a..1b443559e 100644 --- a/src/providers/ipa/ipa_subdomains.c +++ b/src/providers/ipa/ipa_subdomains.c @@ -76,6 +76,18 @@ "("IPA_ENABLED_FLAG"="IPA_TRUE_VALUE"))" \ "("OBJECTCLASS"="IPA_OC_CERTMAP_CONFIG_OBJECT"))" +/* It doesn't make sense to resolve more servers than this from the SRV + * lookup because kinit would time out before we are able to cycle + * through the whole list + */ +#define MAX_SERVERS_FROM_SRV 5 + +struct ipa_sd_k5_svc_list { + struct krb5_service *k5svc; + + struct ipa_sd_k5_svc_list *next; + struct ipa_sd_k5_svc_list *prev; +}; struct ipa_subdomains_ctx { struct be_ctx *be_ctx; @@ -88,6 +100,11 @@ struct ipa_subdomains_ctx { time_t last_refreshed; bool view_read_at_init; + /* List of krb5_service structures for each subdomain + * in order to write the kdcinfo files. For use on + * the client only + */ + struct ipa_sd_k5_svc_list *k5svc_list; }; static errno_t @@ -635,6 +652,69 @@ done: return ret; } +static struct krb5_service * +ipa_subdom_get_k5_svc(struct ipa_subdomains_ctx *ctx, + struct sss_domain_info *dom, + bool use_kdcinfo) +{ + struct ipa_sd_k5_svc_list *k5svc_ent; + + /* get the service by realm */ + DLIST_FOR_EACH(k5svc_ent, ctx->k5svc_list) { + if (strcasecmp(dom->realm, k5svc_ent->k5svc->realm) == 0) { + break; + } + } + + if (k5svc_ent != NULL) { + /* Already exists */ + return k5svc_ent->k5svc; + } + + /* Create a new service */ + k5svc_ent = talloc_zero(ctx, struct ipa_sd_k5_svc_list); + if (k5svc_ent == NULL) { + return NULL; + } + + k5svc_ent->k5svc = krb5_service_new(k5svc_ent, + ctx->be_ctx, + "IPA", + dom->realm, + use_kdcinfo); + if (k5svc_ent->k5svc == NULL) { + talloc_free(k5svc_ent); + return NULL; + } + DLIST_ADD(ctx->k5svc_list, k5svc_ent); + + return k5svc_ent->k5svc; +} + +static void ipa_subdom_remove_k5_svc(struct ipa_subdomains_ctx *ctx) +{ + /* Domain going away is such a rare operation that it makes + * more sense to just throw away the whole k5svc_list and let + * the write_kdcinfo request recreate them all again instead + * of coding up complex logic.. + */ + talloc_zfree(ctx->k5svc_list); +} + +static void ipa_subdom_remove_step(struct ipa_subdomains_ctx *ctx, + struct sss_domain_info *dom) +{ + if (dp_opt_get_bool(ctx->ipa_id_ctx->ipa_options->basic, + IPA_SERVER_MODE) == false) { + /* IPA clients keep track of krb5_service wrappers */ + return ipa_subdom_remove_k5_svc(ctx); + } else { + /* IPA servers keeps track of AD contexts */ + return ipa_ad_subdom_remove(ctx->be_ctx, ctx->ipa_id_ctx, dom); + } + +} + static void ipa_subdom_store_step(struct sss_domain_info *parent, struct ipa_id_ctx *id_ctx, struct sdap_idmap_ctx *sdap_idmap_ctx, @@ -697,8 +777,7 @@ static errno_t ipa_subdomains_refresh(struct ipa_subdomains_ctx *ctx, goto done; } - /* Remove the AD ID ctx from the list of LDAP domains */ - ipa_ad_subdom_remove(ctx->be_ctx, ctx->ipa_id_ctx, dom); + ipa_subdom_remove_step(ctx, dom); } else { /* ok let's try to update it */ ipa_subdom_store_step(parent, ctx->ipa_id_ctx, @@ -1917,6 +1996,611 @@ static errno_t ipa_domain_resolution_order_recv(struct tevent_req *req) return EOK; } +struct kdcinfo_from_server_list_state { + struct resolv_hostport *hostport_list; + enum host_database db[2]; + + struct resolv_hostport_addr **rhp_addrs; + size_t rhp_len; +}; + +static void kdcinfo_from_server_list_done(struct tevent_req *subreq); + +static struct tevent_req * +kdcinfo_from_server_list_send(TALLOC_CTX *mem_ctx, + struct tevent_context *ev, + struct be_resolv_ctx *be_res, + const char *servers) +{ + struct kdcinfo_from_server_list_state *state; + struct tevent_req *req; + struct tevent_req *subreq; + errno_t ret; + int server_list_len; + char **server_list; + + req = tevent_req_create(mem_ctx, &state, + struct kdcinfo_from_server_list_state); + if (req == NULL) { + return NULL; + } + state->db[0] = DB_DNS; + state->db[1] = DB_SENTINEL; + + if (servers == NULL) { + ret = EOK; + goto immediately; + } + + ret = split_on_separator(state, servers, ',', true, true, + &server_list, + &server_list_len); + if (ret != EOK) { + DEBUG(SSSDBG_OP_FAILURE, "Failed to parse server list!\n"); + goto immediately; + } + + state->hostport_list = talloc_array(state, + struct resolv_hostport, + server_list_len); + if (state->hostport_list == NULL) { + ret = ENOMEM; + goto immediately; + } + + for (int i = 0; i < server_list_len; i++) { + state->hostport_list[i].host = server_list[i]; + state->hostport_list[i].port = 0; + } + + subreq = resolv_hostport_list_send(state, + ev, + be_res->resolv, + state->hostport_list, + server_list_len, + 0, + be_res->family_order, + state->db); + if (subreq == NULL) { + ret = ENOMEM; + goto immediately; + } + tevent_req_set_callback(subreq, kdcinfo_from_server_list_done, req); + return req; + +immediately: + if (ret != EOK) { + tevent_req_error(req, ret); + } else { + tevent_req_done(req); + } + tevent_req_post(req, ev); + return req; +} + +static void kdcinfo_from_server_list_done(struct tevent_req *subreq) +{ + errno_t ret; + struct tevent_req *req = + tevent_req_callback_data(subreq, struct tevent_req); + struct kdcinfo_from_server_list_state *state = tevent_req_data(req, + struct kdcinfo_from_server_list_state); + + ret = resolv_hostport_list_recv(subreq, + state, + &state->rhp_len, + &state->rhp_addrs); + talloc_zfree(subreq); + if (ret != EOK) { + DEBUG(SSSDBG_OP_FAILURE, + "Failed to resolve address list [%d]: %s\n", ret, sss_strerror(ret)); + tevent_req_error(req, ret); + return; + } + + tevent_req_done(req); +} + +static errno_t kdcinfo_from_server_list_recv(TALLOC_CTX *mem_ctx, + struct tevent_req *req, + struct resolv_hostport_addr ***_rhp_addrs, + size_t *_rhp_len) +{ + struct kdcinfo_from_server_list_state *state = tevent_req_data(req, + struct kdcinfo_from_server_list_state); + + TEVENT_REQ_RETURN_ON_ERROR(req); + + if (_rhp_addrs != NULL) { + *_rhp_addrs = talloc_steal(mem_ctx, state->rhp_addrs); + } + + if (_rhp_len != NULL) { + *_rhp_len = state->rhp_len; + } + + return EOK; +} + +struct kdcinfo_from_site_state { + struct tevent_context *ev; + struct be_resolv_ctx *be_res; + + const char *discovery_domains[2]; + struct resolv_hostport *hostport_list; + enum host_database db[2]; + + struct resolv_hostport_addr **rhp_addrs; + size_t rhp_len; +}; + +static void kdcinfo_from_site_srv_done(struct tevent_req *subreq); +static void kdcinfo_from_site_server_list_done(struct tevent_req *subreq); + +static struct tevent_req * +kdcinfo_from_site_send(TALLOC_CTX *mem_ctx, + struct tevent_context *ev, + struct be_resolv_ctx *be_res, + const char *site, + const char *domain) +{ + struct kdcinfo_from_site_state *state; + struct tevent_req *req; + struct tevent_req *subreq; + errno_t ret; + + req = tevent_req_create(mem_ctx, &state, + struct kdcinfo_from_site_state); + if (req == NULL) { + return NULL; + } + state->ev = ev; + state->be_res = be_res; + state->db[0] = DB_DNS; + state->db[1] = DB_SENTINEL; + + state->discovery_domains[0] = ad_site_dns_discovery_domain(state, + site, + domain); + if (state->discovery_domains[0] == NULL) { + ret = ENOMEM; + goto immediately; + } + state->discovery_domains[1] = NULL; + + subreq = fo_discover_srv_send(state, + state->ev, + state->be_res->resolv, + "kerberos", "tcp", + state->discovery_domains); + if (subreq == NULL) { + ret = ENOMEM; + goto immediately; + } + tevent_req_set_callback(subreq, kdcinfo_from_site_srv_done, req); + return req; + +immediately: + if (ret != EOK) { + tevent_req_error(req, ret); + } else { + tevent_req_done(req); + } + tevent_req_post(req, ev); + return req; +} + +static void kdcinfo_from_site_srv_done(struct tevent_req *subreq) +{ + errno_t ret; + struct tevent_req *req = + tevent_req_callback_data(subreq, struct tevent_req); + struct kdcinfo_from_site_state *state = tevent_req_data(req, + struct kdcinfo_from_site_state); + struct fo_server_info *servers; + size_t num_servers; + + ret = fo_discover_srv_recv(state, subreq, + NULL, NULL, /* not interested in TTL etc */ + &servers, &num_servers); + talloc_zfree(subreq); + if (ret != EOK) { + DEBUG(SSSDBG_OP_FAILURE, + "Could not resolve the site [%d]: %s\n", ret, sss_strerror(ret)); + tevent_req_error(req, ret); + return; + } + + state->hostport_list = talloc_array(state, + struct resolv_hostport, + num_servers); + if (state->hostport_list == NULL) { + tevent_req_error(req, ENOMEM); + return; + } + + for (size_t i = 0; i < num_servers; i++) { + state->hostport_list[i].host = servers[i].host; + state->hostport_list[i].port = servers[i].port; + } + + subreq = resolv_hostport_list_send(state, + state->ev, + state->be_res->resolv, + state->hostport_list, + num_servers, + MAX_SERVERS_FROM_SRV, + state->be_res->family_order, + state->db); + if (subreq == NULL) { + tevent_req_error(req, ENOMEM); + return; + } + tevent_req_set_callback(subreq, kdcinfo_from_site_server_list_done, req); +} + +static void kdcinfo_from_site_server_list_done(struct tevent_req *subreq) +{ + errno_t ret; + struct tevent_req *req = + tevent_req_callback_data(subreq, struct tevent_req); + struct kdcinfo_from_site_state *state = tevent_req_data(req, + struct kdcinfo_from_site_state); + + ret = resolv_hostport_list_recv(subreq, + state, + &state->rhp_len, + &state->rhp_addrs); + talloc_zfree(subreq); + if (ret != EOK) { + DEBUG(SSSDBG_OP_FAILURE, + "Failed to resolve address list [%d]: %s\n", + ret, sss_strerror(ret)); + tevent_req_error(req, ret); + return; + } + + tevent_req_done(req); +} + + +static errno_t kdcinfo_from_site_recv(TALLOC_CTX *mem_ctx, + struct tevent_req *req, + struct resolv_hostport_addr ***_rhp_addrs, + size_t *_rhp_len) +{ + struct kdcinfo_from_site_state *state = tevent_req_data(req, + struct kdcinfo_from_site_state); + + TEVENT_REQ_RETURN_ON_ERROR(req); + + if (_rhp_addrs != NULL) { + *_rhp_addrs = talloc_steal(mem_ctx, state->rhp_addrs); + } + + if (_rhp_len != NULL) { + *_rhp_len = state->rhp_len; + } + + return EOK; +} + +/* Anything per-domain in this request goes here so that we + * can just free the whole struct without mixing data from + * different domains or the overhead of another request + */ +struct ipa_sd_per_dom_kdcinfo_ctx { + struct sss_domain_info *dom; + + const char *servers; + const char *site; + + const char *discovery_domains[2]; + struct krb5_service *krb5_service; +}; + +struct ipa_subdomains_write_kdcinfo_state { + struct tevent_context *ev; + struct ipa_subdomains_ctx *ipa_sd_ctx; + struct be_ctx *be_ctx; + + bool use_kdcinfo; + struct ipa_sd_per_dom_kdcinfo_ctx *pdctx; +}; + +static errno_t ipa_subdomains_write_kdcinfo_domain_step(struct sss_domain_info *start_dom, + struct tevent_req *req); +static void ipa_subdomains_write_kdcinfo_domain_done(struct tevent_req *subreq); +static errno_t ipa_subdomains_write_kdcinfo_write_step(struct sss_domain_info *dom, + struct krb5_service *krb5_service, + struct resolv_hostport_addr **rhp_addrs, + size_t rhp_len); + +static struct tevent_req * +ipa_subdomains_write_kdcinfo_send(TALLOC_CTX *mem_ctx, + struct tevent_context *ev, + struct ipa_subdomains_ctx *ipa_sd_ctx, + struct be_ctx *be_ctx) +{ + struct ipa_subdomains_write_kdcinfo_state *state; + struct tevent_req *req; + errno_t ret; + + req = tevent_req_create(mem_ctx, &state, + struct ipa_subdomains_write_kdcinfo_state); + if (req == NULL) { + return NULL; + } + state->ev = ev; + state->ipa_sd_ctx = ipa_sd_ctx; + state->be_ctx = be_ctx; + + if (ipa_sd_ctx->ipa_id_ctx->server_mode != NULL) { + /* This request is valid for clients only */ + ret = EOK; + goto immediately; + } + + state->use_kdcinfo = dp_opt_get_bool(ipa_sd_ctx->ipa_id_ctx->ipa_options->auth, + KRB5_USE_KDCINFO); + if (state->use_kdcinfo == false) { + DEBUG(SSSDBG_CONF_SETTINGS, "kdcinfo creation disabled\n"); + ret = EOK; + goto immediately; + } + + if (be_ctx->domain->subdomains == NULL) { + DEBUG(SSSDBG_CONF_SETTINGS, "No subdomains, done\n"); + ret = EOK; + goto immediately; + } + + ret = ipa_subdomains_write_kdcinfo_domain_step(be_ctx->domain->subdomains, + req); + if (ret != EAGAIN) { + goto immediately; + } + return req; + +immediately: + if (ret == EOK) { + tevent_req_done(req); + } else { + tevent_req_error(req, ret); + } + tevent_req_post(req, ev); + return req; +} + +static errno_t ipa_subdomains_write_kdcinfo_domain_step(struct sss_domain_info *start_dom, + struct tevent_req *req) +{ + struct ipa_subdomains_write_kdcinfo_state *state = \ + tevent_req_data(req, + struct ipa_subdomains_write_kdcinfo_state); + struct dp_option *ipa_ad_subdom_opts; + struct tevent_req *subreq = NULL; + char *subdom_conf_path; + errno_t ret; + const char *servers; + const char *site; + + for (struct sss_domain_info *dom = start_dom; + dom != NULL; + dom = get_next_domain(dom, 0)) { + + talloc_zfree(state->pdctx); + + subdom_conf_path = subdomain_create_conf_path(state, dom); + if (subdom_conf_path == NULL) { + DEBUG(SSSDBG_MINOR_FAILURE, + "subdom_conf_path failed for %s\n", dom->name); + /* Not fatal */ + continue; + } + + ret = dp_get_options(state, state->be_ctx->cdb, + subdom_conf_path, + ipa_cli_ad_subdom_opts, + IPA_OPTS_CLI_AD_SUBDOM, + &ipa_ad_subdom_opts); + if (ret != EOK) { + DEBUG(SSSDBG_MINOR_FAILURE, + "Cannot get options for %s: [%d]: %s\n", + dom->name, ret, sss_strerror(ret)); + /* Not fatal */ + continue; + } + + servers = dp_opt_get_string(ipa_ad_subdom_opts, IPA_CLI_AD_SERVER); + site = dp_opt_get_string(ipa_ad_subdom_opts, IPA_CLI_AD_SITE); + + if (servers == NULL && site == NULL) { + /* If neither is set, just go to the next domain */ + DEBUG(SSSDBG_TRACE_INTERNAL, + "No site or server defined for %s, skipping\n", + dom->name); + continue; + } + + /* We will resolve this domain, create a per-domain context */ + state->pdctx = talloc_zero(state, struct ipa_sd_per_dom_kdcinfo_ctx); + if (state->pdctx == NULL) { + return ENOMEM; + } + state->pdctx->dom = dom; + state->pdctx->servers = servers; + state->pdctx->site = site; + state->pdctx->krb5_service = ipa_subdom_get_k5_svc(state->ipa_sd_ctx, + dom, + state->use_kdcinfo); + if (state->pdctx->krb5_service == NULL) { + continue; + } + + if (state->pdctx->servers != NULL) { + DEBUG(SSSDBG_CONF_SETTINGS, + "Resolving servers [%s] for domain %s\n", + state->pdctx->servers, dom->name); + + subreq = kdcinfo_from_server_list_send(state, + state->ev, + state->be_ctx->be_res, + state->pdctx->servers); + } else if (state->pdctx->site != NULL) { + DEBUG(SSSDBG_CONF_SETTINGS, + "Resolving site %s for domain %s\n", + state->pdctx->site, dom->name); + + subreq = kdcinfo_from_site_send(state, + state->ev, + state->be_ctx->be_res, + state->pdctx->site, + state->pdctx->dom->name); + } else { + /* We should never get here */ + return EINVAL; + } + + if (subreq == NULL) { + return ENOMEM; + } + tevent_req_set_callback(subreq, ipa_subdomains_write_kdcinfo_domain_done, req); + return EAGAIN; + } + + return EOK; +} + +static void ipa_subdomains_write_kdcinfo_domain_done(struct tevent_req *subreq) +{ + errno_t ret; + struct tevent_req *req = + tevent_req_callback_data(subreq, struct tevent_req); + struct ipa_subdomains_write_kdcinfo_state *state = \ + tevent_req_data(req, + struct ipa_subdomains_write_kdcinfo_state); + struct sss_domain_info *next_domain; + struct resolv_hostport_addr **rhp_addrs; + size_t rhp_len; + + if (state->pdctx->servers != NULL) { + ret = kdcinfo_from_server_list_recv(state->pdctx, subreq, + &rhp_addrs, &rhp_len); + } else if (state->pdctx->site != NULL) { + ret = kdcinfo_from_site_recv(state->pdctx, subreq, + &rhp_addrs, &rhp_len); + } else { + DEBUG(SSSDBG_CRIT_FAILURE, "Neither site nor servers set\n"); + ret = EINVAL; + } + + if (ret == EOK) { + ret = ipa_subdomains_write_kdcinfo_write_step(state->pdctx->dom, + state->pdctx->krb5_service, + rhp_addrs, rhp_len); + if (ret != EOK) { + DEBUG(SSSDBG_MINOR_FAILURE, + "Could not write kdcinfo file for %s\n", state->pdctx->dom->name); + /* Not fatal, loop to the next domain below */ + } + } else { + DEBUG(SSSDBG_MINOR_FAILURE, + "Could not get address list for %s\n", state->pdctx->dom->name); + /* Not fatal, loop to the next domain below */ + } + + next_domain = get_next_domain(state->pdctx->dom, 0); + ret = ipa_subdomains_write_kdcinfo_domain_step(next_domain, req); + if (ret == EOK) { + tevent_req_done(req); + return; + } else if (ret != EAGAIN) { + /* the loop in ipa_subdomains_write_kdcinfo_domain_step already + * tries to be quite permissive, so any error is fatal + */ + tevent_req_error(req, ret); + return; + } + + /* Continue to the next domain */ +} + +static errno_t ipa_subdomains_write_kdcinfo_write_step(struct sss_domain_info *dom, + struct krb5_service *krb5_service, + struct resolv_hostport_addr **rhp_addrs, + size_t rhp_len) +{ + errno_t ret; + char *address = NULL; + char *safe_address = NULL; + char **safe_addr_list; + int addr_index = 0; + TALLOC_CTX *tmp_ctx = NULL; + + tmp_ctx = talloc_new(NULL); + if (tmp_ctx == NULL) { + return ENOMEM; + } + + safe_addr_list = talloc_zero_array(tmp_ctx, char *, rhp_len+1); + if (safe_addr_list == NULL) { + ret = ENOMEM; + goto done; + } + + for (size_t i = 0; i < rhp_len; i++) { + address = resolv_get_string_address(tmp_ctx, rhp_addrs[i]->reply); + if (address == NULL) { + DEBUG(SSSDBG_CRIT_FAILURE, "resolv_get_string_address failed.\n"); + continue; + } + + if (rhp_addrs[i]->origin.port != 0) { + address = talloc_asprintf_append(address, + ":%d", + rhp_addrs[i]->origin.port); + } + + safe_address = sss_escape_ip_address(tmp_ctx, + rhp_addrs[i]->reply->family, + address); + talloc_zfree(address); + if (safe_address == NULL) { + DEBUG(SSSDBG_CRIT_FAILURE, "sss_escape_ip_address failed.\n"); + continue; + } + + DEBUG(SSSDBG_CONF_SETTINGS, + "Will write [%s] for %s\n", + safe_address, dom->name); + + safe_addr_list[addr_index] = talloc_steal(safe_addr_list, + safe_address); + addr_index++; + } + + ret = write_krb5info_file(krb5_service, + safe_addr_list, + SSS_KRB5KDC_FO_SRV); + if (ret != EOK) { + DEBUG(SSSDBG_OP_FAILURE, + "write_krb5info_file failed, authentication might fail.\n"); + goto done; + } + + ret = EOK; +done: + talloc_free(tmp_ctx); + return ret; +} + +static errno_t ipa_subdomains_write_kdcinfo_recv(struct tevent_req *req) +{ + TEVENT_REQ_RETURN_ON_ERROR(req); + return EOK; +} + struct ipa_subdomains_refresh_state { struct tevent_context *ev; struct ipa_subdomains_ctx *sd_ctx; @@ -1933,6 +2617,7 @@ static void ipa_subdomains_refresh_view_name_done(struct tevent_req *subreq); static void ipa_subdomains_refresh_view_domain_resolution_order_done( struct tevent_req *subreq); static void ipa_domain_refresh_resolution_order_done(struct tevent_req *subreq); +static void ipa_domain_refresh_kdcinfo_done(struct tevent_req *subreq); static struct tevent_req * ipa_subdomains_refresh_send(TALLOC_CTX *mem_ctx, @@ -2253,6 +2938,35 @@ ipa_domain_refresh_resolution_order_done(struct tevent_req *subreq) return; } + subreq = ipa_subdomains_write_kdcinfo_send(state, + state->ev, + state->sd_ctx, + state->sd_ctx->be_ctx); + if (subreq == NULL) { + tevent_req_error(req, ENOMEM); + return; + } + tevent_req_set_callback(subreq, ipa_domain_refresh_kdcinfo_done, req); +} + +static void +ipa_domain_refresh_kdcinfo_done(struct tevent_req *subreq) +{ + struct tevent_req *req; + errno_t ret; + + req = tevent_req_callback_data(subreq, struct tevent_req); + + ret = ipa_subdomains_write_kdcinfo_recv(subreq); + talloc_zfree(subreq); + if (ret != EOK) { + DEBUG(SSSDBG_MINOR_FAILURE, + "Unable to write the kdc info files, authentication might " + "fail or time out [%d]: %s\n", + ret, sss_strerror(ret)); + /* Not fatal, let's hope DNS is set correctly */ + } + tevent_req_done(req); } -- 2.37.0 From 8ee17c0ed2a98f45e4e20ad7363f7883ff2caa18 Mon Sep 17 00:00:00 2001 From: Jakub Hrozek <jhrozek@redhat.com> Date: Wed, 27 Jun 2018 09:59:42 +0200 Subject: [PATCH 15/24] MAN: Document the options available for AD trusted domains Related: https://pagure.io/SSSD/sssd/issue/3291 Reviewed-by: Sumit Bose <sbose@redhat.com> (cherry picked from commit 014e7d8ab6aa4cf3051764052326258230c0bc86) --- src/man/sssd-ipa.5.xml | 92 ++++++++++++++++++++++++++++++++++++++++++ 1 file changed, 92 insertions(+) diff --git a/src/man/sssd-ipa.5.xml b/src/man/sssd-ipa.5.xml index e4e58afaf..e46957d5f 100644 --- a/src/man/sssd-ipa.5.xml +++ b/src/man/sssd-ipa.5.xml @@ -728,6 +728,98 @@ </para> </refsect1> + <refsect1 id='trusted_domains'> + <title>TRUSTED DOMAINS CONFIGURATION</title> + <para> + Some configuration options can be also set for a trusted domain. + A trusted domain configuration can either be done using + a subsection, for example: +<programlisting> +[domain/ipa.domain.com/ad.domain.com] +ad_server = dc.ad.domain.com +</programlisting> + </para> + <para> + In addition, some options can be set in the parent domain + and inherited by the trusted domain using the + <quote>subdomain_inherit</quote> option. For more details, + see the + <citerefentry> + <refentrytitle>sssd.conf</refentrytitle> + <manvolnum>5</manvolnum> + </citerefentry> manual page. + </para> + <para> + Different configuration options are tunable for a trusted + domain depending on whether you are configuring SSSD on an + IPA server or an IPA client. + </para> + <refsect2 id='server_configuration'> + <title>OPTIONS TUNABLE ON IPA MASTERS</title> + <para> + The following options can be set in a subdomain + section on an IPA master: + <itemizedlist> + <listitem> + <para>ad_server</para> + </listitem> + <listitem> + <para>ad_backup_server</para> + </listitem> + <listitem> + <para>ad_site</para> + </listitem> + <listitem> + <para>ldap_search_base</para> + </listitem> + <listitem> + <para>ldap_user_search_base</para> + </listitem> + <listitem> + <para>ldap_group_search_base</para> + </listitem> + <listitem> + <para>use_fully_qualified_names</para> + </listitem> + </itemizedlist> + </para> + </refsect2> + <refsect2 id='client_configuration'> + <title>OPTIONS TUNABLE ON IPA CLIENTS</title> + <para> + The following options can be set in a subdomain + section on an IPA client: + <itemizedlist> + <listitem> + <para>ad_server</para> + </listitem> + <listitem> + <para>ad_site</para> + </listitem> + </itemizedlist> + </para> + <para> + Note that if both options are set, only + <quote>ad_server</quote> is evaluated. + </para> + <para> + Since any request for a user or a group identity from a + trusted domain triggered from an IPA client is resolved + by the IPA server, the <quote>ad_server</quote> and + <quote>ad_site</quote> options only affect which AD DC will + the authentication be performed against. In particular, + the addresses resolved from these lists will be written to + <quote>kdcinfo</quote> files read by the Kerberos locator + plugin. Please refer to the + <citerefentry> + <refentrytitle>sssd_krb5_locator_plugin</refentrytitle> + <manvolnum>8</manvolnum> + </citerefentry> manual page for more details on the Kerberos + locator plugin. + </para> + </refsect2> + </refsect1> + <xi:include xmlns:xi="http://www.w3.org/2001/XInclude" href="include/failover.xml" /> <xi:include xmlns:xi="http://www.w3.org/2001/XInclude" href="include/service_discovery.xml" /> -- 2.37.0 From 27ee698caf8fa0d1a38f7c00858b36d370495ce9 Mon Sep 17 00:00:00 2001 From: Lukas Slebodnik <lslebodn@redhat.com> Date: Thu, 19 Jul 2018 09:38:22 +0200 Subject: [PATCH 16/24] krb5_locator: Make debug function internal Merges: https://pagure.io/SSSD/sssd/pull-request/3786 Reviewed-by: Jakub Hrozek <jhrozek@redhat.com> (cherry picked from commit 86de91f93f51d41d71c504b871c65fea31dd5485) --- src/krb5_plugin/sssd_krb5_locator_plugin.c | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/krb5_plugin/sssd_krb5_locator_plugin.c b/src/krb5_plugin/sssd_krb5_locator_plugin.c index 952d487c2..7800ab0c7 100644 --- a/src/krb5_plugin/sssd_krb5_locator_plugin.c +++ b/src/krb5_plugin/sssd_krb5_locator_plugin.c @@ -82,7 +82,7 @@ struct sssd_ctx { bool disabled; }; -void plugin_debug_fn(const char *format, ...) +static void plugin_debug_fn(const char *format, ...) { va_list ap; char *s = NULL; -- 2.37.0 From d5517f9e618f2423403a615fdcaedcea090bc4b3 Mon Sep 17 00:00:00 2001 From: Lukas Slebodnik <lslebodn@redhat.com> Date: Thu, 19 Jul 2018 09:44:33 +0200 Subject: [PATCH 17/24] krb5_locator: Simplify usage of macro PLUGIN_DEBUG It should look like real function call Merges: https://pagure.io/SSSD/sssd/pull-request/3786 Reviewed-by: Jakub Hrozek <jhrozek@redhat.com> (cherry picked from commit 276f2e345548947b66f7bd3b984628eaf6f4cbd4) --- src/krb5_plugin/sssd_krb5_locator_plugin.c | 88 +++++++++++----------- 1 file changed, 44 insertions(+), 44 deletions(-) diff --git a/src/krb5_plugin/sssd_krb5_locator_plugin.c b/src/krb5_plugin/sssd_krb5_locator_plugin.c index 7800ab0c7..61fee6b94 100644 --- a/src/krb5_plugin/sssd_krb5_locator_plugin.c +++ b/src/krb5_plugin/sssd_krb5_locator_plugin.c @@ -63,9 +63,9 @@ #define SSSD_KRB5_LOCATOR_DEBUG "SSSD_KRB5_LOCATOR_DEBUG" #define SSSD_KRB5_LOCATOR_DISABLE "SSSD_KRB5_LOCATOR_DISABLE" #define DEBUG_KEY "[sssd_krb5_locator] " -#define PLUGIN_DEBUG(body) do { \ +#define PLUGIN_DEBUG(format, ...) do { \ if (ctx->debug) { \ - plugin_debug_fn body; \ + plugin_debug_fn(format, ##__VA_ARGS__); \ } \ } while(0) @@ -236,26 +236,26 @@ static int buf_to_addr_port_list(struct sssd_ctx *ctx, port = strtol(port_str, &endptr, 10); if (errno != 0) { ret = errno; - PLUGIN_DEBUG(("strtol failed on [%s]: [%d][%s], " - "assuming default.\n", port_str, ret, - strerror(ret))); + PLUGIN_DEBUG("strtol failed on [%s]: [%d][%s], " + "assuming default.\n", + port_str, ret, strerror(ret)); port = 0; } if (*endptr != '\0') { - PLUGIN_DEBUG(("Found additional characters [%s] in port " - "number [%s], assuming default.\n", endptr, - port_str)); + PLUGIN_DEBUG("Found additional characters [%s] in port " + "number [%s], assuming default.\n", + endptr, port_str); port = 0; } if (port < 0 || port > 65535) { - PLUGIN_DEBUG(("Illegal port number [%ld], assuming " - "default.\n", port)); + PLUGIN_DEBUG("Illegal port number [%ld], assuming " + "default.\n", port); port = 0; } } else { - PLUGIN_DEBUG(("Illegal port number [%s], assuming default.\n", - port_str)); + PLUGIN_DEBUG("Illegal port number [%s], assuming default.\n", + port_str); port = 0; } } @@ -270,7 +270,7 @@ static int buf_to_addr_port_list(struct sssd_ctx *ctx, addr_str++; } - PLUGIN_DEBUG(("Found [%s][%d].\n", addr_str, port)); + PLUGIN_DEBUG("Found [%s][%d].\n", addr_str, port); l[c].addr = strdup(addr_str); if (l[c].addr == NULL) { @@ -314,7 +314,7 @@ static int get_krb5info(const char *realm, struct sssd_ctx *ctx, name_tmpl = KPASSWDINFO_TMPL; break; default: - PLUGIN_DEBUG(("Unsupported service [%d].\n", svc)); + PLUGIN_DEBUG("Unsupported service [%d].\n", svc); return EINVAL; } @@ -323,13 +323,13 @@ static int get_krb5info(const char *realm, struct sssd_ctx *ctx, krb5info_name = calloc(1, len + 1); if (krb5info_name == NULL) { - PLUGIN_DEBUG(("malloc failed.\n")); + PLUGIN_DEBUG("malloc failed.\n"); return ENOMEM; } ret = snprintf(krb5info_name, len, name_tmpl, realm); if (ret < 0) { - PLUGIN_DEBUG(("snprintf failed.\n")); + PLUGIN_DEBUG("snprintf failed.\n"); ret = EINVAL; goto done; } @@ -337,8 +337,8 @@ static int get_krb5info(const char *realm, struct sssd_ctx *ctx, fd = open(krb5info_name, O_RDONLY); if (fd == -1) { - PLUGIN_DEBUG(("open failed [%s][%d][%s].\n", - krb5info_name, errno, strerror(errno))); + PLUGIN_DEBUG("open failed [%s][%d][%s].\n", + krb5info_name, errno, strerror(errno)); ret = errno; goto done; } @@ -349,15 +349,15 @@ static int get_krb5info(const char *realm, struct sssd_ctx *ctx, len = sss_atomic_read_s(fd, buf, BUFSIZE); if (len == -1) { ret = errno; - PLUGIN_DEBUG(("read failed [%d][%s].\n", ret, strerror(ret))); + PLUGIN_DEBUG("read failed [%d][%s].\n", ret, strerror(ret)); close(fd); goto done; } close(fd); if (len == BUFSIZE) { - PLUGIN_DEBUG(("Content of krb5info file [%s] is [%d] or larger.\n", - krb5info_name, BUFSIZE)); + PLUGIN_DEBUG("Content of krb5info file [%s] is [%d] or larger.\n", + krb5info_name, BUFSIZE); } switch (svc) { @@ -376,7 +376,7 @@ static int get_krb5info(const char *realm, struct sssd_ctx *ctx, } break; default: - PLUGIN_DEBUG(("Unsupported service [%d].\n", svc)); + PLUGIN_DEBUG("Unsupported service [%d].\n", svc); ret = EINVAL; goto done; } @@ -401,7 +401,7 @@ krb5_error_code sssd_krb5_locator_init(krb5_context context, ctx->debug = false; } else { ctx->debug = true; - PLUGIN_DEBUG(("sssd_krb5_locator_init called\n")); + PLUGIN_DEBUG("sssd_krb5_locator_init called\n"); } dummy = getenv(SSSD_KRB5_LOCATOR_DISABLE); @@ -409,7 +409,7 @@ krb5_error_code sssd_krb5_locator_init(krb5_context context, ctx->disabled = false; } else { ctx->disabled = true; - PLUGIN_DEBUG(("SSSD KRB5 locator plugin is disabled.\n")); + PLUGIN_DEBUG("SSSD KRB5 locator plugin is disabled.\n"); } *private_data = ctx; @@ -424,7 +424,7 @@ void sssd_krb5_locator_close(void *private_data) if (private_data == NULL) return; ctx = (struct sssd_ctx *) private_data; - PLUGIN_DEBUG(("sssd_krb5_locator_close called\n")); + PLUGIN_DEBUG("sssd_krb5_locator_close called\n"); free_addr_port_list(&(ctx->kdc_addr)); free_addr_port_list(&(ctx->kpasswd_addr)); @@ -460,7 +460,7 @@ krb5_error_code sssd_krb5_locator_lookup(void *private_data, } if (ctx->disabled) { - PLUGIN_DEBUG(("Plugin disabled, nothing to do.\n")); + PLUGIN_DEBUG("Plugin disabled, nothing to do.\n"); return KRB5_PLUGIN_NO_HANDLE; } @@ -468,13 +468,13 @@ krb5_error_code sssd_krb5_locator_lookup(void *private_data, free(ctx->sssd_realm); ctx->sssd_realm = strdup(realm); if (ctx->sssd_realm == NULL) { - PLUGIN_DEBUG(("strdup failed.\n")); + PLUGIN_DEBUG("strdup failed.\n"); return KRB5_PLUGIN_NO_HANDLE; } ret = get_krb5info(realm, ctx, locate_service_kdc); if (ret != EOK) { - PLUGIN_DEBUG(("get_krb5info failed.\n")); + PLUGIN_DEBUG("get_krb5info failed.\n"); return KRB5_PLUGIN_NO_HANDLE; } @@ -482,22 +482,22 @@ krb5_error_code sssd_krb5_locator_lookup(void *private_data, svc == locate_service_master_kdc) { ret = get_krb5info(realm, ctx, locate_service_kpasswd); if (ret != EOK) { - PLUGIN_DEBUG(("reading kpasswd address failed, " - "using kdc address.\n")); + PLUGIN_DEBUG("reading kpasswd address failed, " + "using kdc address.\n"); free_addr_port_list(&(ctx->kpasswd_addr)); ret = copy_addr_port_list(ctx->kdc_addr, true, &(ctx->kpasswd_addr)); if (ret != EOK) { - PLUGIN_DEBUG(("copying address list failed.\n")); + PLUGIN_DEBUG("copying address list failed.\n"); return KRB5_PLUGIN_NO_HANDLE; } } } } - PLUGIN_DEBUG(("sssd_realm[%s] requested realm[%s] family[%d] socktype[%d] " - "locate_service[%d]\n", ctx->sssd_realm, realm, family, - socktype, svc)); + PLUGIN_DEBUG("sssd_realm[%s] requested realm[%s] family[%d] socktype[%d] " + "locate_service[%d]\n", + ctx->sssd_realm, realm, family, socktype, svc); switch (svc) { case locate_service_kdc: @@ -547,7 +547,7 @@ krb5_error_code sssd_krb5_locator_lookup(void *private_data, memset(port_str, 0, PORT_STR_SIZE); ret = snprintf(port_str, PORT_STR_SIZE-1, "%u", port); if (ret < 0 || ret >= (PORT_STR_SIZE-1)) { - PLUGIN_DEBUG(("snprintf failed.\n")); + PLUGIN_DEBUG("snprintf failed.\n"); return KRB5_PLUGIN_NO_HANDLE; } @@ -557,31 +557,31 @@ krb5_error_code sssd_krb5_locator_lookup(void *private_data, ret = getaddrinfo(addr[c].addr, port_str, &ai_hints, &ai); if (ret != 0) { - PLUGIN_DEBUG(("getaddrinfo failed [%d][%s].\n", ret, - gai_strerror(ret))); + PLUGIN_DEBUG("getaddrinfo failed [%d][%s].\n", + ret, gai_strerror(ret)); if (ret == EAI_SYSTEM) { - PLUGIN_DEBUG(("getaddrinfo failed [%d][%s].\n", - errno, strerror(errno))); + PLUGIN_DEBUG("getaddrinfo failed [%d][%s].\n", + errno, strerror(errno)); } return KRB5_PLUGIN_NO_HANDLE; } - PLUGIN_DEBUG(("addr[%s:%s] family[%d] socktype[%d]\n", addr[c].addr, - port_str, ai->ai_family, ai->ai_socktype)); + PLUGIN_DEBUG("addr[%s:%s] family[%d] socktype[%d]\n", + addr[c].addr, port_str, ai->ai_family, ai->ai_socktype); if ((family == AF_UNSPEC || ai->ai_family == family) && ai->ai_socktype == socktype) { ret = cbfunc(cbdata, socktype, ai->ai_addr); if (ret != 0) { - PLUGIN_DEBUG(("cbfunc failed\n")); + PLUGIN_DEBUG("cbfunc failed\n"); freeaddrinfo(ai); return ret; } else { - PLUGIN_DEBUG(("[%s] used\n", addr[c].addr)); + PLUGIN_DEBUG("[%s] used\n", addr[c].addr); } } else { - PLUGIN_DEBUG(("[%s] NOT used\n", addr[c].addr)); + PLUGIN_DEBUG("[%s] NOT used\n", addr[c].addr); } freeaddrinfo(ai); } -- 2.37.0 From 441d75a612d37c33889aae42246977db232f108a Mon Sep 17 00:00:00 2001 From: Lukas Slebodnik <lslebodn@redhat.com> Date: Thu, 19 Jul 2018 09:50:12 +0200 Subject: [PATCH 18/24] krb5_locator: Fix typo in debug message Merges: https://pagure.io/SSSD/sssd/pull-request/3786 Reviewed-by: Jakub Hrozek <jhrozek@redhat.com> (cherry picked from commit 09dc1d9dc10780d126d477c394ae2ef4c0d0cff3) --- src/krb5_plugin/sssd_krb5_locator_plugin.c | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/krb5_plugin/sssd_krb5_locator_plugin.c b/src/krb5_plugin/sssd_krb5_locator_plugin.c index 61fee6b94..acb20f25d 100644 --- a/src/krb5_plugin/sssd_krb5_locator_plugin.c +++ b/src/krb5_plugin/sssd_krb5_locator_plugin.c @@ -323,7 +323,7 @@ static int get_krb5info(const char *realm, struct sssd_ctx *ctx, krb5info_name = calloc(1, len + 1); if (krb5info_name == NULL) { - PLUGIN_DEBUG("malloc failed.\n"); + PLUGIN_DEBUG("calloc failed.\n"); return ENOMEM; } -- 2.37.0 From 5a25f4fa2056d31e8693f719ca7f79f77d0cd8fc Mon Sep 17 00:00:00 2001 From: Lukas Slebodnik <lslebodn@redhat.com> Date: Sat, 21 Jul 2018 23:50:11 +0200 Subject: [PATCH 19/24] krb5_locator: Fix formatting of the variable port Merges: https://pagure.io/SSSD/sssd/pull-request/3786 Reviewed-by: Jakub Hrozek <jhrozek@redhat.com> (cherry picked from commit aefdf70351d01d1dcfe3ebb2769fbd3bb1bd0441) --- src/krb5_plugin/sssd_krb5_locator_plugin.c | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/krb5_plugin/sssd_krb5_locator_plugin.c b/src/krb5_plugin/sssd_krb5_locator_plugin.c index acb20f25d..4b0b6a146 100644 --- a/src/krb5_plugin/sssd_krb5_locator_plugin.c +++ b/src/krb5_plugin/sssd_krb5_locator_plugin.c @@ -270,7 +270,7 @@ static int buf_to_addr_port_list(struct sssd_ctx *ctx, addr_str++; } - PLUGIN_DEBUG("Found [%s][%d].\n", addr_str, port); + PLUGIN_DEBUG("Found [%s][%ld].\n", addr_str, port); l[c].addr = strdup(addr_str); if (l[c].addr == NULL) { -- 2.37.0 From 2b45008aece1b4ec44d253bce9fe27b20ac23f63 Mon Sep 17 00:00:00 2001 From: Lukas Slebodnik <lslebodn@redhat.com> Date: Thu, 19 Jul 2018 09:53:13 +0200 Subject: [PATCH 20/24] krb5_locator: Use format string checking for debug function Merges: https://pagure.io/SSSD/sssd/pull-request/3786 Reviewed-by: Jakub Hrozek <jhrozek@redhat.com> (cherry picked from commit 9680ac9ce20511b3f34dc1c8635d0c4435006ce3) --- src/krb5_plugin/sssd_krb5_locator_plugin.c | 3 +++ 1 file changed, 3 insertions(+) diff --git a/src/krb5_plugin/sssd_krb5_locator_plugin.c b/src/krb5_plugin/sssd_krb5_locator_plugin.c index 4b0b6a146..720878e95 100644 --- a/src/krb5_plugin/sssd_krb5_locator_plugin.c +++ b/src/krb5_plugin/sssd_krb5_locator_plugin.c @@ -82,6 +82,9 @@ struct sssd_ctx { bool disabled; }; +#ifdef HAVE_FUNCTION_ATTRIBUTE_FORMAT +__attribute__((format(printf, 1, 2))) +#endif static void plugin_debug_fn(const char *format, ...) { va_list ap; -- 2.37.0 From 723e90056b02d23cb19f0056e709608dc7dcb44c Mon Sep 17 00:00:00 2001 From: Sumit Bose <sbose@redhat.com> Date: Fri, 15 Feb 2019 16:54:19 +0100 Subject: [PATCH 21/24] krb5_locator: always use port 88 for master KDC If the kpasswdinfo file exists and the found IP address includes a port number as well the master KDC lookup will use this port number which is most probably wrong. Better use the default port 88 always for master KDC lookups. This patch also updates the man page for the locator plugin which was quite outdated. Related to https://pagure.io/SSSD/sssd/issue/3958 Reviewed-by: Jakub Hrozek <jhrozek@redhat.com> (cherry picked from commit 05350abdf2ab98770ca296b9485578218644a2a7) (cherry picked from commit 1791eed5d453cdd9cb5cef3bb4d056a848f8b461) --- src/krb5_plugin/sssd_krb5_locator_plugin.c | 43 +++-- src/man/sssd_krb5_locator_plugin.8.xml | 76 +++++---- .../cmocka/test_sssd_krb5_locator_plugin.c | 156 ++++++++++++++++++ 3 files changed, 233 insertions(+), 42 deletions(-) diff --git a/src/krb5_plugin/sssd_krb5_locator_plugin.c b/src/krb5_plugin/sssd_krb5_locator_plugin.c index 720878e95..bdac8933c 100644 --- a/src/krb5_plugin/sssd_krb5_locator_plugin.c +++ b/src/krb5_plugin/sssd_krb5_locator_plugin.c @@ -80,6 +80,7 @@ struct sssd_ctx { struct addr_port *kpasswd_addr; bool debug; bool disabled; + bool kpasswdinfo_used; }; #ifdef HAVE_FUNCTION_ATTRIBUTE_FORMAT @@ -414,6 +415,7 @@ krb5_error_code sssd_krb5_locator_init(krb5_context context, ctx->disabled = true; PLUGIN_DEBUG("SSSD KRB5 locator plugin is disabled.\n"); } + ctx->kpasswdinfo_used = false; *private_data = ctx; @@ -454,6 +456,7 @@ krb5_error_code sssd_krb5_locator_lookup(void *private_data, struct addr_port *addr = NULL; char port_str[PORT_STR_SIZE]; size_t c; + bool force_port = false; if (private_data == NULL) return KRB5_PLUGIN_NO_HANDLE; ctx = (struct sssd_ctx *) private_data; @@ -481,20 +484,24 @@ krb5_error_code sssd_krb5_locator_lookup(void *private_data, return KRB5_PLUGIN_NO_HANDLE; } - if (svc == locate_service_kadmin || svc == locate_service_kpasswd || - svc == locate_service_master_kdc) { - ret = get_krb5info(realm, ctx, locate_service_kpasswd); + } + + if (ctx->kpasswd_addr == NULL + && (svc == locate_service_kadmin || svc == locate_service_kpasswd || + svc == locate_service_master_kdc)) { + ret = get_krb5info(realm, ctx, locate_service_kpasswd); + if (ret != EOK) { + PLUGIN_DEBUG("reading kpasswd address failed, " + "using kdc address.\n"); + free_addr_port_list(&(ctx->kpasswd_addr)); + ret = copy_addr_port_list(ctx->kdc_addr, true, + &(ctx->kpasswd_addr)); if (ret != EOK) { - PLUGIN_DEBUG("reading kpasswd address failed, " - "using kdc address.\n"); - free_addr_port_list(&(ctx->kpasswd_addr)); - ret = copy_addr_port_list(ctx->kdc_addr, true, - &(ctx->kpasswd_addr)); - if (ret != EOK) { - PLUGIN_DEBUG("copying address list failed.\n"); - return KRB5_PLUGIN_NO_HANDLE; - } + PLUGIN_DEBUG("copying address list failed.\n"); + return KRB5_PLUGIN_NO_HANDLE; } + } else { + ctx->kpasswdinfo_used = true; } } @@ -510,6 +517,12 @@ krb5_error_code sssd_krb5_locator_lookup(void *private_data, case locate_service_master_kdc: addr = ctx->kpasswd_addr; default_port = DEFAULT_KERBEROS_PORT; + if (ctx->kpasswdinfo_used) { + /* Use default port if the addresses from the kpasswdinfo + * files are used because the port numbers from the file will + * most probably not be suitable. */ + force_port = true; + } break; case locate_service_kadmin: addr = ctx->kpasswd_addr; @@ -542,11 +555,13 @@ krb5_error_code sssd_krb5_locator_lookup(void *private_data, return KRB5_PLUGIN_NO_HANDLE; } - if (strcmp(realm, ctx->sssd_realm) != 0) + if (strcmp(realm, ctx->sssd_realm) != 0 || addr == NULL) { return KRB5_PLUGIN_NO_HANDLE; + } for (c = 0; addr[c].addr != NULL; c++) { - port = (addr[c].port == 0 ? default_port : addr[c].port); + port = ((addr[c].port == 0 || force_port) ? default_port + : addr[c].port); memset(port_str, 0, PORT_STR_SIZE); ret = snprintf(port_str, PORT_STR_SIZE-1, "%u", port); if (ret < 0 || ret >= (PORT_STR_SIZE-1)) { diff --git a/src/man/sssd_krb5_locator_plugin.8.xml b/src/man/sssd_krb5_locator_plugin.8.xml index d28546012..d77f59d6a 100644 --- a/src/man/sssd_krb5_locator_plugin.8.xml +++ b/src/man/sssd_krb5_locator_plugin.8.xml @@ -20,40 +20,60 @@ <title>DESCRIPTION</title> <para> The Kerberos locator plugin - <command>sssd_krb5_locator_plugin</command> is used by the Kerberos - provider of - <citerefentry> - <refentrytitle>sssd</refentrytitle> - <manvolnum>8</manvolnum> - </citerefentry> - to tell the Kerberos libraries what Realm and which KDC to use. - Typically this is done in + <command>sssd_krb5_locator_plugin</command> is used by libkrb5 to + find KDCs for a given Kerberos realm. SSSD provides such a plugin to + guide all Kerberos clients on a system to a single KDC. In general + it should not matter to which KDC a client process is talking to. + But there are cases, e.g. after a password change, where not all + KDCs are in the same state because the new data has to be replicated + first. To avoid unexpected authentication failures and maybe even + account lockings it would be good to talk to a single KDC as long as + possible. + </para> + <para> + libkrb5 will search the locator plugin in the libkrb5 sub-directory + of the Kerberos plugin directory, see plugin_base_dir in <citerefentry> <refentrytitle>krb5.conf</refentrytitle> <manvolnum>5</manvolnum> </citerefentry> - which is always read by the Kerberos libraries. To simplify the - configuration the Realm and the KDC can be defined in - <citerefentry> - <refentrytitle>sssd.conf</refentrytitle> - <manvolnum>5</manvolnum> - </citerefentry> - as described in - <citerefentry> - <refentrytitle>sssd-krb5</refentrytitle> - <manvolnum>5</manvolnum> - </citerefentry> + for details. The plugin can only be disabled by removing the plugin + file. There is no option in the Kerberos configuration to disable + it. But the SSSD_KRB5_LOCATOR_DISABLE environment variable can be + used to disable the plugin for individual commands. Alternatively + the SSSD option krb5_use_kdcinfo=False can be used to not generate + the data needed by the plugin. With this the plugin is still + called but will provide no data to the caller so that libkrb5 can + fall back to other methods defined in krb5.conf. </para> <para> - <citerefentry> - <refentrytitle>sssd</refentrytitle> - <manvolnum>8</manvolnum> - </citerefentry> - puts the Realm and the name or IP address of the KDC into the - environment variables SSSD_KRB5_REALM and SSSD_KRB5_KDC respectively. - When <command>sssd_krb5_locator_plugin</command> is called by the - kerberos libraries it reads and evaluates these variables and returns - them to the libraries. + The plugin reads the information about the KDCs of a given realm + from a file called <filename>kdcinfo.REALM</filename>. The file + should contain one or more IP addresses either in dotted-decimal + IPv4 notation or the hexadecimal IPv6 notation. An optional port + number can be added to the end separated with a colon, the IPv6 + address has to be enclosed in squared brackets in this case as + usual. Valid entries are: + <itemizedlist> + <listitem><para>1.2.3.4</para></listitem> + <listitem><para>5.6.7.8:99</para></listitem> + <listitem><para>2001:db8:85a3::8a2e:370:7334</para></listitem> + <listitem><para>[2001:db8:85a3::8a2e:370:7334]:321</para></listitem> + </itemizedlist> + SSSD's krb5 auth-provider which is used by the IPA and AD providers + as well adds the address of the current KDC or domain controller + SSSD is using to this file. + </para> + <para> + In environments with read-only and read-write KDCs where clients are + expected to use the read-only instances for the general operations + and only the read-write KDC for config changes like password changes + a <filename>kpasswdinfo.REALM</filename> is used as well to identify + read-write KDCs. If this file exists for the given realm the content + will be used by the plugin to reply to requests for a kpasswd or + kadmin server or for the MIT Kerberos specific master KDC. If the + address contains a port number the default KDC port 88 will be used + for the latter. </para> </refsect1> diff --git a/src/tests/cmocka/test_sssd_krb5_locator_plugin.c b/src/tests/cmocka/test_sssd_krb5_locator_plugin.c index 3e7d00632..1b6838345 100644 --- a/src/tests/cmocka/test_sssd_krb5_locator_plugin.c +++ b/src/tests/cmocka/test_sssd_krb5_locator_plugin.c @@ -44,6 +44,9 @@ #define TEST_IP_1_WITH_SERVICE TEST_IP_1":"TEST_SERVICE_1 #define TEST_IPV6_1_WITH_SERVICE TEST_IPV6_1":"TEST_SERVICE_2 +#define TEST_IP_1_WITH_SERVICE_2 TEST_IP_1":"TEST_SERVICE_2 +#define TEST_IPV6_1_WITH_SERVICE_1 TEST_IPV6_1":"TEST_SERVICE_1 + struct test_state { void *dummy; }; @@ -61,6 +64,7 @@ static int setup(void **state) *state = (void *)ts; unlink(TEST_PUBCONF_PATH"/kdcinfo."TEST_REALM); + unlink(TEST_PUBCONF_PATH"/kpasswdinfo."TEST_REALM); rmdir(TEST_PUBCONF_PATH); return 0; @@ -574,7 +578,157 @@ void test_service(void **state) k5_free_serverlist(&list); + /* locate_service_master_kdc should get the default port 88 if kpasswdinfo + * does not exists. */ + kerr = sssd_krb5_locator_lookup(priv, locate_service_master_kdc, TEST_REALM, + SOCK_DGRAM, AF_INET, module_callback, + &cbdata); + assert_int_equal(kerr, 0); + assert_int_equal(list.nservers, 1); + assert_non_null(list.servers); + ret = getnameinfo((struct sockaddr *) &list.servers[0].addr, + list.servers[0].addrlen, + host, sizeof(host), service, sizeof(service), + NI_NUMERICHOST|NI_NUMERICSERV); + assert_int_equal(ret, 0); + assert_string_equal(TEST_IP_1, host); + assert_string_equal("88", service); + + k5_free_serverlist(&list); + + kerr = sssd_krb5_locator_lookup(priv, locate_service_master_kdc, TEST_REALM, + SOCK_DGRAM, AF_INET6, module_callback, + &cbdata); + assert_int_equal(kerr, 0); + assert_int_equal(list.nservers, 1); + assert_non_null(list.servers); + ret = getnameinfo((struct sockaddr *) &list.servers[0].addr, + list.servers[0].addrlen, + host, sizeof(host), service, sizeof(service), + NI_NUMERICHOST|NI_NUMERICSERV); + assert_int_equal(ret, 0); + assert_string_equal(TEST_IPV6_1_PURE, host); + assert_string_equal("88", service); + + k5_free_serverlist(&list); + + unlink(TEST_PUBCONF_PATH"/kdcinfo."TEST_REALM); + rmdir(TEST_PUBCONF_PATH); + sssd_krb5_locator_close(priv); + + krb5_free_context(ctx); +} + +void test_kpasswd_and_master_kdc(void **state) +{ + krb5_context ctx; + krb5_error_code kerr; + void *priv; + int fd; + struct serverlist list = SERVERLIST_INIT; + struct module_callback_data cbdata = { 0 }; + ssize_t s; + int ret; + char host[NI_MAXHOST]; + char service[NI_MAXSERV]; + + cbdata.list = &list; + + kerr = krb5_init_context (&ctx); + assert_int_equal(kerr, 0); + + kerr = sssd_krb5_locator_init(ctx, &priv); + assert_int_equal(kerr, 0); + + mkdir(TEST_PUBCONF_PATH, 0777); + fd = open(TEST_PUBCONF_PATH"/kdcinfo."TEST_REALM, O_CREAT|O_RDWR, 0777); + assert_int_not_equal(fd, -1); + s = write(fd, TEST_IP_1_WITH_SERVICE, sizeof(TEST_IP_1_WITH_SERVICE)); + assert_int_equal(s, sizeof(TEST_IP_1_WITH_SERVICE)); + s = write(fd, "\n", 1); + assert_int_equal(s, 1); + s = write(fd, TEST_IPV6_1_WITH_SERVICE, sizeof(TEST_IPV6_1_WITH_SERVICE)); + assert_int_equal(s, sizeof(TEST_IPV6_1_WITH_SERVICE)); + close(fd); + fd = open(TEST_PUBCONF_PATH"/kpasswdinfo."TEST_REALM, O_CREAT|O_RDWR, 0777); + assert_int_not_equal(fd, -1); + s = write(fd, TEST_IP_1_WITH_SERVICE_2, sizeof(TEST_IP_1_WITH_SERVICE_2)); + assert_int_equal(s, sizeof(TEST_IP_1_WITH_SERVICE_2)); + s = write(fd, "\n", 1); + assert_int_equal(s, 1); + s = write(fd, TEST_IPV6_1_WITH_SERVICE_1, + sizeof(TEST_IPV6_1_WITH_SERVICE_1)); + assert_int_equal(s, sizeof(TEST_IPV6_1_WITH_SERVICE_1)); + close(fd); + + kerr = sssd_krb5_locator_lookup(priv, locate_service_kpasswd, TEST_REALM, + SOCK_DGRAM, AF_INET, module_callback, + &cbdata); + assert_int_equal(kerr, 0); + assert_int_equal(list.nservers, 1); + assert_non_null(list.servers); + ret = getnameinfo((struct sockaddr *) &list.servers[0].addr, + list.servers[0].addrlen, + host, sizeof(host), service, sizeof(service), + NI_NUMERICHOST|NI_NUMERICSERV); + assert_int_equal(ret, 0); + assert_string_equal(TEST_IP_1, host); + assert_string_equal(TEST_SERVICE_2, service); + + k5_free_serverlist(&list); + + kerr = sssd_krb5_locator_lookup(priv, locate_service_kpasswd , TEST_REALM, + SOCK_DGRAM, AF_INET6, module_callback, + &cbdata); + assert_int_equal(kerr, 0); + + assert_int_equal(list.nservers, 1); + assert_non_null(list.servers); + ret = getnameinfo((struct sockaddr *) &list.servers[0].addr, + list.servers[0].addrlen, + host, sizeof(host), service, sizeof(service), + NI_NUMERICHOST|NI_NUMERICSERV); + assert_int_equal(ret, 0); + assert_string_equal(TEST_IPV6_1_PURE, host); + assert_string_equal(TEST_SERVICE_1, service); + + k5_free_serverlist(&list); + + /* locate_service_master_kdc should use the default KDC port 88 and not + * the one set in the kpasswdinfo file. */ + kerr = sssd_krb5_locator_lookup(priv, locate_service_master_kdc, TEST_REALM, + SOCK_DGRAM, AF_INET, module_callback, + &cbdata); + assert_int_equal(kerr, 0); + assert_int_equal(list.nservers, 1); + assert_non_null(list.servers); + ret = getnameinfo((struct sockaddr *) &list.servers[0].addr, + list.servers[0].addrlen, + host, sizeof(host), service, sizeof(service), + NI_NUMERICHOST|NI_NUMERICSERV); + assert_int_equal(ret, 0); + assert_string_equal(TEST_IP_1, host); + assert_string_equal("88", service); + + k5_free_serverlist(&list); + kerr = sssd_krb5_locator_lookup(priv, locate_service_master_kdc, TEST_REALM, + SOCK_DGRAM, AF_INET6, module_callback, + &cbdata); + assert_int_equal(kerr, 0); + assert_int_equal(list.nservers, 1); + assert_non_null(list.servers); + ret = getnameinfo((struct sockaddr *) &list.servers[0].addr, + list.servers[0].addrlen, + host, sizeof(host), service, sizeof(service), + NI_NUMERICHOST|NI_NUMERICSERV); + assert_int_equal(ret, 0); + assert_string_equal(TEST_IPV6_1_PURE, host); + assert_string_equal("88", service); + + k5_free_serverlist(&list); + + unlink(TEST_PUBCONF_PATH"/kpasswdinfo."TEST_REALM); unlink(TEST_PUBCONF_PATH"/kdcinfo."TEST_REALM); rmdir(TEST_PUBCONF_PATH); sssd_krb5_locator_close(priv); @@ -606,6 +760,8 @@ int main(int argc, const char *argv[]) setup, teardown), cmocka_unit_test_setup_teardown(test_service, setup, teardown), + cmocka_unit_test_setup_teardown(test_kpasswd_and_master_kdc, + setup, teardown), }; /* Set debug level to invalid value so we can decide if -d 0 was used. */ -- 2.37.0 From 762657084fcb5a58b579776350d7211d586d1342 Mon Sep 17 00:00:00 2001 From: Tomas Halman <thalman@redhat.com> Date: Mon, 4 Mar 2019 10:02:20 +0100 Subject: [PATCH 22/24] krb5_locator: Allow hostname in kdcinfo files Currently we support only IP addresses in kdcinfo files. We need to resolv eventual dns name and then we have to iterate trough list of addresses because hostname can be resolved that way, including IPv4 and IPv6 addresses. Resolves: https://pagure.io/SSSD/sssd/issue/3973 Reviewed-by: Sumit Bose <sbose@redhat.com> (cherry picked from commit 63ccbfe00f7519081a8d25bda4cd8f5bd9f46f35) (cherry picked from commit 96e4d713a65ff743dfb6427f7561ead3a1cac4bf) --- src/krb5_plugin/sssd_krb5_locator_plugin.c | 59 +++++++++++++++++----- src/man/sssd_krb5_locator_plugin.8.xml | 18 +++++-- 2 files changed, 58 insertions(+), 19 deletions(-) diff --git a/src/krb5_plugin/sssd_krb5_locator_plugin.c b/src/krb5_plugin/sssd_krb5_locator_plugin.c index bdac8933c..06520ef2e 100644 --- a/src/krb5_plugin/sssd_krb5_locator_plugin.c +++ b/src/krb5_plugin/sssd_krb5_locator_plugin.c @@ -62,6 +62,7 @@ #define PORT_STR_SIZE 7 #define SSSD_KRB5_LOCATOR_DEBUG "SSSD_KRB5_LOCATOR_DEBUG" #define SSSD_KRB5_LOCATOR_DISABLE "SSSD_KRB5_LOCATOR_DISABLE" +#define SSSD_KRB5_LOCATOR_IGNORE_DNS_FAILURES "SSSD_KRB5_LOCATOR_IGNORE_DNS_FAILURES" #define DEBUG_KEY "[sssd_krb5_locator] " #define PLUGIN_DEBUG(format, ...) do { \ if (ctx->debug) { \ @@ -81,6 +82,7 @@ struct sssd_ctx { bool debug; bool disabled; bool kpasswdinfo_used; + bool ignore_dns_failure; }; #ifdef HAVE_FUNCTION_ATTRIBUTE_FORMAT @@ -415,8 +417,17 @@ krb5_error_code sssd_krb5_locator_init(krb5_context context, ctx->disabled = true; PLUGIN_DEBUG("SSSD KRB5 locator plugin is disabled.\n"); } + ctx->kpasswdinfo_used = false; + dummy = getenv(SSSD_KRB5_LOCATOR_IGNORE_DNS_FAILURES); + if (dummy == NULL) { + ctx->ignore_dns_failure = false; + } else { + ctx->ignore_dns_failure = true; + PLUGIN_DEBUG("SSSD KRB5 locator plugin ignores DNS resolving errors.\n"); + } + *private_data = ctx; return 0; @@ -448,7 +459,7 @@ krb5_error_code sssd_krb5_locator_lookup(void *private_data, void *cbdata) { int ret; - struct addrinfo *ai; + struct addrinfo *ai, *ai_item; struct sssd_ctx *ctx; struct addrinfo ai_hints; uint16_t port = 0; @@ -457,6 +468,7 @@ krb5_error_code sssd_krb5_locator_lookup(void *private_data, char port_str[PORT_STR_SIZE]; size_t c; bool force_port = false; + char address[NI_MAXHOST]; if (private_data == NULL) return KRB5_PLUGIN_NO_HANDLE; ctx = (struct sssd_ctx *) private_data; @@ -570,7 +582,7 @@ krb5_error_code sssd_krb5_locator_lookup(void *private_data, } memset(&ai_hints, 0, sizeof(struct addrinfo)); - ai_hints.ai_flags = AI_NUMERICHOST|AI_NUMERICSERV; + ai_hints.ai_flags = AI_NUMERICSERV; ai_hints.ai_socktype = socktype; ret = getaddrinfo(addr[c].addr, port_str, &ai_hints, &ai); @@ -581,25 +593,44 @@ krb5_error_code sssd_krb5_locator_lookup(void *private_data, PLUGIN_DEBUG("getaddrinfo failed [%d][%s].\n", errno, strerror(errno)); } + + if (ctx->ignore_dns_failure) { + continue; + } + return KRB5_PLUGIN_NO_HANDLE; } - PLUGIN_DEBUG("addr[%s:%s] family[%d] socktype[%d]\n", - addr[c].addr, port_str, ai->ai_family, ai->ai_socktype); + for (ai_item = ai; ai_item != NULL; ai_item = ai_item->ai_next) { + if (ctx->debug) { + ret = getnameinfo(ai_item->ai_addr, ai_item->ai_addrlen, + address, NI_MAXHOST, + NULL, 0, + NI_NUMERICHOST); + if (ret != 0) { + address[0] = 0; + } + + PLUGIN_DEBUG("addr[%s (%s)] port[%s] family[%d] socktype[%d]\n", + addr[c].addr, address, + port_str, ai_item->ai_family, + ai_item->ai_socktype); + } - if ((family == AF_UNSPEC || ai->ai_family == family) && - ai->ai_socktype == socktype) { + if ((family == AF_UNSPEC || ai_item->ai_family == family) && + ai_item->ai_socktype == socktype) { - ret = cbfunc(cbdata, socktype, ai->ai_addr); - if (ret != 0) { - PLUGIN_DEBUG("cbfunc failed\n"); - freeaddrinfo(ai); - return ret; + ret = cbfunc(cbdata, socktype, ai_item->ai_addr); + if (ret != 0) { + PLUGIN_DEBUG("cbfunc failed\n"); + freeaddrinfo(ai); + return ret; + } else { + PLUGIN_DEBUG("[%s (%s)] used\n", addr[c].addr, address); + } } else { - PLUGIN_DEBUG("[%s] used\n", addr[c].addr); + PLUGIN_DEBUG("[%s (%s)] NOT used\n", addr[c].addr, address); } - } else { - PLUGIN_DEBUG("[%s] NOT used\n", addr[c].addr); } freeaddrinfo(ai); } diff --git a/src/man/sssd_krb5_locator_plugin.8.xml b/src/man/sssd_krb5_locator_plugin.8.xml index d77f59d6a..c438cdae6 100644 --- a/src/man/sssd_krb5_locator_plugin.8.xml +++ b/src/man/sssd_krb5_locator_plugin.8.xml @@ -49,12 +49,14 @@ <para> The plugin reads the information about the KDCs of a given realm from a file called <filename>kdcinfo.REALM</filename>. The file - should contain one or more IP addresses either in dotted-decimal - IPv4 notation or the hexadecimal IPv6 notation. An optional port - number can be added to the end separated with a colon, the IPv6 - address has to be enclosed in squared brackets in this case as - usual. Valid entries are: + should contain one or more DNS names or IP addresses either in + dotted-decimal IPv4 notation or the hexadecimal IPv6 notation. + An optional port number can be added to the end separated with + a colon, the IPv6 address has to be enclosed in squared brackets + in this case as usual. Valid entries are: <itemizedlist> + <listitem><para>kdc.example.com</para></listitem> + <listitem><para>kdc.example.com:321</para></listitem> <listitem><para>1.2.3.4</para></listitem> <listitem><para>5.6.7.8:99</para></listitem> <listitem><para>2001:db8:85a3::8a2e:370:7334</para></listitem> @@ -94,6 +96,12 @@ value the plugin is disabled and will just return KRB5_PLUGIN_NO_HANDLE to the caller. </para> + <para> + If the environment variable SSSD_KRB5_LOCATOR_IGNORE_DNS_FAILURES + is set to any value plugin will try to resolve all DNS names + in kdcinfo file. By default plugin returns KRB5_PLUGIN_NO_HANDLE + to the caller immediately on first DNS resolving failure. + </para> </refsect1> <xi:include xmlns:xi="http://www.w3.org/2001/XInclude" href="include/seealso.xml" /> -- 2.37.0 From e119395bd588060727f808e37ef3a1f57279de68 Mon Sep 17 00:00:00 2001 From: Tomas Halman <thalman@redhat.com> Date: Wed, 13 Mar 2019 08:37:36 +0100 Subject: [PATCH 23/24] krb5: Write multiple dnsnames into kdc info file Multiple servers should be written to kdc info file. In this PR we iterate trough server list and we write list of primary servers followed by backup servers. Resolves: https://pagure.io/SSSD/sssd/issue/3974 Reviewed-by: Sumit Bose <sbose@redhat.com> (cherry picked from commit 208a79a83c76b6693bdf927c3d7d6267e3218b0b) (cherry picked from commit c225ed7ffe5dae9876d5846e16a4daec51da6453) --- src/providers/ad/ad_common.c | 39 +++++---- src/providers/fail_over.c | 27 ++++++ src/providers/fail_over.h | 9 ++ src/providers/ipa/ipa_common.c | 25 +----- src/providers/ipa/ipa_subdomains.c | 4 +- src/providers/krb5/krb5_common.c | 131 ++++++++++++++++++++--------- src/providers/krb5/krb5_common.h | 7 +- 7 files changed, 158 insertions(+), 84 deletions(-) diff --git a/src/providers/ad/ad_common.c b/src/providers/ad/ad_common.c index a495e0b81..6874a5156 100644 --- a/src/providers/ad/ad_common.c +++ b/src/providers/ad/ad_common.c @@ -24,6 +24,7 @@ #include "providers/ad/ad_common.h" #include "providers/ad/ad_opts.h" #include "providers/be_dyndns.h" +#include "providers/fail_over.h" struct ad_server_data { bool gc; @@ -885,6 +886,20 @@ done: return ret; } +static bool +ad_krb5info_file_filter(struct fo_server *server) +{ + struct ad_server_data *sdata = NULL; + if (server == NULL) return true; + + sdata = fo_get_server_user_data(server); + if (sdata && sdata->gc) { + /* Only write kdcinfo files for local servers */ + return true; + } + return false; +} + static void ad_resolve_callback(void *private_data, struct fo_server *server) { @@ -894,7 +909,6 @@ ad_resolve_callback(void *private_data, struct fo_server *server) struct resolv_hostent *srvaddr; struct sockaddr_storage *sockaddr; char *address; - char *safe_addr_list[2] = { NULL, NULL }; char *new_uri; int new_port; const char *srv_name; @@ -1004,25 +1018,14 @@ ad_resolve_callback(void *private_data, struct fo_server *server) goto done; } - /* Only write kdcinfo files for local servers */ - if ((sdata == NULL || sdata->gc == false) && - service->krb5_service->write_kdcinfo) { - /* Write krb5 info files */ - safe_addr_list[0] = sss_escape_ip_address(tmp_ctx, - srvaddr->family, - address); - if (safe_addr_list[0] == NULL) { - DEBUG(SSSDBG_CRIT_FAILURE, "sss_escape_ip_address failed.\n"); - ret = ENOMEM; - goto done; - } - - ret = write_krb5info_file(service->krb5_service, - safe_addr_list, - SSS_KRB5KDC_FO_SRV); + if (service->krb5_service->write_kdcinfo) { + ret = write_krb5info_file_from_fo_server(service->krb5_service, + server, + SSS_KRB5KDC_FO_SRV, + ad_krb5info_file_filter); if (ret != EOK) { DEBUG(SSSDBG_MINOR_FAILURE, - "write_krb5info_file failed, authentication might fail.\n"); + "write_krb5info_file failed, authentication might fail.\n"); } } diff --git a/src/providers/fail_over.c b/src/providers/fail_over.c index 168e59d6f..dc5c7c7d8 100644 --- a/src/providers/fail_over.c +++ b/src/providers/fail_over.c @@ -1637,6 +1637,33 @@ fo_get_server_hostname_last_change(struct fo_server *server) return server->common->last_status_change.tv_sec; } +struct fo_server *fo_server_first(struct fo_server *server) +{ + if (!server) return NULL; + + while (server->prev) { server = server->prev; } + return server; +} + +struct fo_server *fo_server_next(struct fo_server *server) +{ + if (!server) return NULL; + + return server->next; +} + +size_t fo_server_count(struct fo_server *server) +{ + struct fo_server *item = fo_server_first(server); + size_t size = 0; + + while (item) { + ++size; + item = item->next; + } + return size; +} + time_t fo_get_service_retry_timeout(struct fo_service *svc) { if (svc == NULL || svc->ctx == NULL || svc->ctx->opts == NULL) { diff --git a/src/providers/fail_over.h b/src/providers/fail_over.h index d70212fb7..bc8142710 100644 --- a/src/providers/fail_over.h +++ b/src/providers/fail_over.h @@ -216,6 +216,15 @@ const char **fo_svc_server_list(TALLOC_CTX *mem_ctx, struct fo_service *service, size_t *_count); +/* + * Folowing functions allow to iterate trough list of servers. + */ +struct fo_server *fo_server_first(struct fo_server *server); + +struct fo_server *fo_server_next(struct fo_server *server); + +size_t fo_server_count(struct fo_server *server); + /* * pvt will be talloc_stealed to ctx */ diff --git a/src/providers/ipa/ipa_common.c b/src/providers/ipa/ipa_common.c index 3d414e110..3f574d128 100644 --- a/src/providers/ipa/ipa_common.c +++ b/src/providers/ipa/ipa_common.c @@ -763,8 +763,6 @@ static void ipa_resolve_callback(void *private_data, struct fo_server *server) struct ipa_service *service; struct resolv_hostent *srvaddr; struct sockaddr_storage *sockaddr; - char *address; - char *safe_addr_list[2] = { NULL, NULL }; char *new_uri; const char *srv_name; int ret; @@ -798,13 +796,6 @@ static void ipa_resolve_callback(void *private_data, struct fo_server *server) return; } - address = resolv_get_string_address(tmp_ctx, srvaddr); - if (address == NULL) { - DEBUG(SSSDBG_CRIT_FAILURE, "resolv_get_string_address failed.\n"); - talloc_free(tmp_ctx); - return; - } - srv_name = fo_get_server_name(server); if (srv_name == NULL) { DEBUG(SSSDBG_CRIT_FAILURE, "Could not get server host name\n"); @@ -827,18 +818,10 @@ static void ipa_resolve_callback(void *private_data, struct fo_server *server) service->sdap->sockaddr = talloc_steal(service, sockaddr); if (service->krb5_service->write_kdcinfo) { - safe_addr_list[0] = sss_escape_ip_address(tmp_ctx, - srvaddr->family, - address); - if (safe_addr_list[0] == NULL) { - DEBUG(SSSDBG_CRIT_FAILURE, "sss_escape_ip_address failed.\n"); - talloc_free(tmp_ctx); - return; - } - - ret = write_krb5info_file(service->krb5_service, - safe_addr_list, - SSS_KRB5KDC_FO_SRV); + ret = write_krb5info_file_from_fo_server(service->krb5_service, + server, + SSS_KRB5KDC_FO_SRV, + NULL); if (ret != EOK) { DEBUG(SSSDBG_OP_FAILURE, "write_krb5info_file failed, authentication might fail.\n"); diff --git a/src/providers/ipa/ipa_subdomains.c b/src/providers/ipa/ipa_subdomains.c index 1b443559e..bf8b18a92 100644 --- a/src/providers/ipa/ipa_subdomains.c +++ b/src/providers/ipa/ipa_subdomains.c @@ -2534,7 +2534,7 @@ static errno_t ipa_subdomains_write_kdcinfo_write_step(struct sss_domain_info *d errno_t ret; char *address = NULL; char *safe_address = NULL; - char **safe_addr_list; + const char **safe_addr_list; int addr_index = 0; TALLOC_CTX *tmp_ctx = NULL; @@ -2543,7 +2543,7 @@ static errno_t ipa_subdomains_write_kdcinfo_write_step(struct sss_domain_info *d return ENOMEM; } - safe_addr_list = talloc_zero_array(tmp_ctx, char *, rhp_len+1); + safe_addr_list = talloc_zero_array(tmp_ctx, const char *, rhp_len+1); if (safe_addr_list == NULL) { ret = ENOMEM; goto done; diff --git a/src/providers/krb5/krb5_common.c b/src/providers/krb5/krb5_common.c index 2b003e164..1e33fc0f5 100644 --- a/src/providers/krb5/krb5_common.c +++ b/src/providers/krb5/krb5_common.c @@ -33,6 +33,7 @@ #include "providers/krb5/krb5_common.h" #include "providers/krb5/krb5_opts.h" #include "providers/krb5/krb5_utils.h" +#include "providers/fail_over.h" #ifdef HAVE_KRB5_CC_COLLECTION /* krb5 profile functions */ @@ -592,7 +593,7 @@ done: } errno_t write_krb5info_file(struct krb5_service *krb5_service, - char **server_list, + const char **server_list, const char *service) { int i; @@ -635,73 +636,119 @@ done: return ret; } -static void krb5_resolve_callback(void *private_data, struct fo_server *server) +static const char* fo_server_address_or_name(TALLOC_CTX *tmp_ctx, struct fo_server *server) { - struct krb5_service *krb5_service; struct resolv_hostent *srvaddr; char *address; - char *safe_addr_list[2] = { NULL, NULL }; - int ret; + + if (!server) return NULL; + + srvaddr = fo_get_server_hostent(server); + if (srvaddr) { + address = resolv_get_string_address(tmp_ctx, srvaddr); + if (address) { + return sss_escape_ip_address(tmp_ctx, + srvaddr->family, + address); + } + } + + return fo_get_server_name(server); +} + +errno_t write_krb5info_file_from_fo_server(struct krb5_service *krb5_service, + struct fo_server *server, + const char *service, + bool (*filter)(struct fo_server *)) +{ TALLOC_CTX *tmp_ctx = NULL; + const char **server_list; + size_t server_idx; + struct fo_server *item; + int primary; + const char *address; + errno_t ret; tmp_ctx = talloc_new(NULL); if (tmp_ctx == NULL) { DEBUG(SSSDBG_CRIT_FAILURE, "talloc_new failed\n"); - return; + return ENOMEM; } - krb5_service = talloc_get_type(private_data, struct krb5_service); - if (!krb5_service) { - DEBUG(SSSDBG_CRIT_FAILURE, "FATAL: Bad private_data\n"); + server_idx = 0; + server_list = talloc_zero_array(tmp_ctx, + const char *, + fo_server_count(server) + 1); + if (server_list == NULL) { + DEBUG(SSSDBG_CRIT_FAILURE, "talloc_zero_array failed\n"); talloc_free(tmp_ctx); - return; + return ENOMEM; } - srvaddr = fo_get_server_hostent(server); - if (!srvaddr) { - DEBUG(SSSDBG_CRIT_FAILURE, - "FATAL: No hostent available for server (%s)\n", - fo_get_server_str_name(server)); - talloc_free(tmp_ctx); - return; + if (filter == NULL || filter(server) == false) { + address = fo_server_address_or_name(tmp_ctx, server); + if (address) { + server_list[server_idx++] = address; + } else { + DEBUG(SSSDBG_CRIT_FAILURE, + "Server without name and address found in list.\n"); + } } - address = resolv_get_string_address(tmp_ctx, srvaddr); - if (address == NULL) { - DEBUG(SSSDBG_CRIT_FAILURE, "resolv_get_string_address failed.\n"); - talloc_free(tmp_ctx); - return; + for (primary = 1; primary >= 0; --primary) { + for (item = fo_server_next(server) ? fo_server_next(server) : fo_server_first(server); + item != server; + item = fo_server_next(item) ? fo_server_next(item) : fo_server_first(item)) { + + if (primary && !fo_is_server_primary(item)) continue; + if (!primary && fo_is_server_primary(item)) continue; + if (filter != NULL && filter(item)) continue; + + address = fo_server_address_or_name(tmp_ctx, item); + if (address == NULL) { + DEBUG(SSSDBG_CRIT_FAILURE, + "Server without name and address found in list.\n"); + continue; + } + + server_list[server_idx++] = address; + } } + if (server_list[0] == NULL) { + DEBUG(SSSDBG_CRIT_FAILURE, + "There is no server that can be written into kdc info file.\n"); + ret = EINVAL; + } else { + ret = write_krb5info_file(krb5_service, + server_list, + service); + } + talloc_free(tmp_ctx); + return ret; +} - safe_addr_list[0] = sss_escape_ip_address(tmp_ctx, - srvaddr->family, - address); - if (safe_addr_list[0] == NULL) { - DEBUG(SSSDBG_CRIT_FAILURE, "sss_escape_ip_address failed.\n"); - talloc_free(tmp_ctx); + +static void krb5_resolve_callback(void *private_data, struct fo_server *server) +{ + struct krb5_service *krb5_service; + int ret; + + krb5_service = talloc_get_type(private_data, struct krb5_service); + if (!krb5_service) { + DEBUG(SSSDBG_CRIT_FAILURE, "FATAL: Bad private_data\n"); return; } if (krb5_service->write_kdcinfo) { - safe_addr_list[0] = talloc_asprintf_append(safe_addr_list[0], ":%d", - fo_get_server_port(server)); - if (safe_addr_list[0] == NULL) { - DEBUG(SSSDBG_CRIT_FAILURE, "talloc_asprintf_append failed.\n"); - talloc_free(tmp_ctx); - return; - } - - ret = write_krb5info_file(krb5_service, - safe_addr_list, - krb5_service->name); + ret = write_krb5info_file_from_fo_server(krb5_service, + server, + krb5_service->name, + NULL); if (ret != EOK) { DEBUG(SSSDBG_OP_FAILURE, "write_krb5info_file failed, authentication might fail.\n"); } } - - talloc_free(tmp_ctx); - return; } static errno_t _krb5_servers_init(struct be_ctx *ctx, diff --git a/src/providers/krb5/krb5_common.h b/src/providers/krb5/krb5_common.h index 55489dece..5f68ab38f 100644 --- a/src/providers/krb5/krb5_common.h +++ b/src/providers/krb5/krb5_common.h @@ -162,9 +162,14 @@ errno_t sss_krb5_get_options(TALLOC_CTX *memctx, struct confdb_ctx *cdb, const char *conf_path, struct dp_option **_opts); errno_t write_krb5info_file(struct krb5_service *krb5_service, - char **server_list, + const char **server_list, const char *service); +errno_t write_krb5info_file_from_fo_server(struct krb5_service *krb5_service, + struct fo_server *server, + const char *service, + bool (*filter)(struct fo_server *)); + struct krb5_service *krb5_service_new(TALLOC_CTX *mem_ctx, struct be_ctx *be_ctx, const char *service_name, -- 2.37.0 From 17f904d8cad6f68e803562d89d2e0f069c054e3b Mon Sep 17 00:00:00 2001 From: Tomas Halman <thalman@redhat.com> Date: Fri, 15 Mar 2019 10:27:50 +0100 Subject: [PATCH 24/24] krb5: Lookahead resolving of host names The caller that initializes the fail over service (maybe with be_fo_add_service) should provide a hint with the value of the lookahead option. Then, if a request for server resolution is triggered, the fail over code would resolve a server and afterwards check if enough fo_server entries with a valid hostname in the struct server_common structure. If not, the request would check if any of the fo_server structures represents a SRV query and try to resolve the query to receive more host names. Resolves: https://pagure.io/SSSD/sssd/issue/3975 Reviewed-by: Jakub Hrozek <jhrozek@redhat.com> (cherry picked from commit e8d806d9bbb1ba288ed6a83158113f4d8f8a8929) (cherry picked from commit ec0c31a71ef8ec61e11b1c6d21edcf9b923ce4ca) --- Makefile.am | 1 + src/man/sssd-krb5.5.xml | 34 +++++++++++ src/providers/ad/ad_common.c | 10 +++- src/providers/ad/ad_common.h | 2 + src/providers/ad/ad_init.c | 2 + src/providers/ad/ad_opts.c | 2 + src/providers/ad/ad_subdomains.c | 9 +++ src/providers/ipa/ipa_common.c | 14 +++-- src/providers/ipa/ipa_opts.c | 2 + src/providers/ipa/ipa_subdomains.c | 4 +- src/providers/ipa/ipa_subdomains_server.c | 7 +++ src/providers/krb5/krb5_common.c | 71 ++++++++++++++++++++++- src/providers/krb5/krb5_common.h | 13 ++++- src/providers/krb5/krb5_init.c | 19 +++++- src/providers/krb5/krb5_opts.c | 1 + src/providers/ldap/ldap_common.c | 9 +++ src/providers/ldap/ldap_opts.c | 1 + src/providers/ldap/sdap.h | 1 + 18 files changed, 191 insertions(+), 11 deletions(-) diff --git a/Makefile.am b/Makefile.am index a041a4cfc..03729da38 100644 --- a/Makefile.am +++ b/Makefile.am @@ -3201,6 +3201,7 @@ test_ad_subdom_LDADD = \ libsss_idmap.la \ libsss_test_common.la \ libdlopen_test_providers.la \ + libsss_krb5_common.la \ $(NULL) test_ipa_subdom_util_SOURCES = \ diff --git a/src/man/sssd-krb5.5.xml b/src/man/sssd-krb5.5.xml index 60b7dfb50..5a0bb5e9c 100644 --- a/src/man/sssd-krb5.5.xml +++ b/src/man/sssd-krb5.5.xml @@ -501,6 +501,40 @@ </listitem> </varlistentry> + <varlistentry> + <term>krb5_kdcinfo_lookahead (string)</term> + <listitem> + <para> + When krb5_use_kdcinfo is set to true, you can limit the amount + of servers handed to + <citerefentry> + <refentrytitle>sssd_krb5_locator_plugin</refentrytitle> + <manvolnum>8</manvolnum> + </citerefentry>. + This might be helpful when there are too many servers + discovered using SRV record. + </para> + <para> + The krb5_kdcinfo_lookahead option contains two + numbers seperated by a colon. The first number represents + number of primary servers used and the second number + specifies the number of backup servers. + </para> + <para> + For example <emphasis>10:0</emphasis> means that up to + 10 primary servers will be handed to + <citerefentry> + <refentrytitle>sssd_krb5_locator_plugin</refentrytitle> + <manvolnum>8</manvolnum> + </citerefentry>. + but no backup servers. + </para> + <para> + Default: 3:1 + </para> + </listitem> + </varlistentry> + <varlistentry> <term>krb5_use_enterprise_principal (boolean)</term> <listitem> diff --git a/src/providers/ad/ad_common.c b/src/providers/ad/ad_common.c index 6874a5156..df36b731c 100644 --- a/src/providers/ad/ad_common.c +++ b/src/providers/ad/ad_common.c @@ -765,6 +765,8 @@ ad_failover_init(TALLOC_CTX *mem_ctx, struct be_ctx *bectx, const char *ad_domain, bool ad_use_ldaps, bool use_kdcinfo, + size_t n_lookahead_primary, + size_t n_lookahead_backup, struct ad_service **_service) { errno_t ret; @@ -806,7 +808,9 @@ ad_failover_init(TALLOC_CTX *mem_ctx, struct be_ctx *bectx, service->krb5_service = krb5_service_new(service, bectx, ad_service, krb5_realm, - use_kdcinfo); + use_kdcinfo, + n_lookahead_primary, + n_lookahead_backup); if (!service->krb5_service) { ret = ENOMEM; goto done; @@ -1362,6 +1366,10 @@ ad_get_auth_options(TALLOC_CTX *mem_ctx, DEBUG(SSSDBG_CONF_SETTINGS, "Option %s set to %s\n", krb5_options[KRB5_USE_KDCINFO].opt_name, ad_opts->service->krb5_service->write_kdcinfo ? "true" : "false"); + sss_krb5_parse_lookahead( + dp_opt_get_string(krb5_options, KRB5_KDCINFO_LOOKAHEAD), + &ad_opts->service->krb5_service->lookahead_primary, + &ad_opts->service->krb5_service->lookahead_backup); *_opts = talloc_steal(mem_ctx, krb5_options); diff --git a/src/providers/ad/ad_common.h b/src/providers/ad/ad_common.h index efe82d846..3ab7636b8 100644 --- a/src/providers/ad/ad_common.h +++ b/src/providers/ad/ad_common.h @@ -150,6 +150,8 @@ ad_failover_init(TALLOC_CTX *mem_ctx, struct be_ctx *ctx, const char *ad_domain, bool ad_use_ldaps, bool use_kdcinfo, + size_t n_lookahead_primary, + size_t n_lookahead_backup, struct ad_service **_service); errno_t diff --git a/src/providers/ad/ad_init.c b/src/providers/ad/ad_init.c index 6e7a3bab7..01336b8c2 100644 --- a/src/providers/ad/ad_init.c +++ b/src/providers/ad/ad_init.c @@ -163,6 +163,8 @@ static errno_t ad_init_options(TALLOC_CTX *mem_ctx, dp_opt_get_string(ad_options->basic, AD_DOMAIN), ad_use_ldaps, false, /* will be set in ad_get_auth_options() */ + (size_t) -1, + (size_t) -1, &ad_options->service); if (ret != EOK) { DEBUG(SSSDBG_FATAL_FAILURE, "Failed to init AD failover service: " diff --git a/src/providers/ad/ad_opts.c b/src/providers/ad/ad_opts.c index c2e517152..6991da332 100644 --- a/src/providers/ad/ad_opts.c +++ b/src/providers/ad/ad_opts.c @@ -112,6 +112,7 @@ struct dp_option ad_def_ldap_opts[] = { { "krb5_realm", DP_OPT_STRING, NULL_STRING, NULL_STRING }, { "krb5_canonicalize", DP_OPT_BOOL, BOOL_FALSE, BOOL_FALSE }, { "krb5_use_kdcinfo", DP_OPT_BOOL, BOOL_TRUE, BOOL_TRUE }, + { "krb5_kdcinfo_lookahead", DP_OPT_STRING, NULL_STRING, NULL_STRING }, { "ldap_pwd_policy", DP_OPT_STRING, { "none" }, NULL_STRING }, { "ldap_referrals", DP_OPT_BOOL, BOOL_FALSE, BOOL_FALSE }, { "account_cache_expiration", DP_OPT_NUMBER, { .number = 0 }, NULL_NUMBER }, @@ -177,6 +178,7 @@ struct dp_option ad_def_krb5_opts[] = { { "krb5_canonicalize", DP_OPT_BOOL, BOOL_FALSE, BOOL_FALSE }, { "krb5_use_enterprise_principal", DP_OPT_BOOL, BOOL_TRUE, BOOL_TRUE }, { "krb5_use_kdcinfo", DP_OPT_BOOL, BOOL_TRUE, BOOL_TRUE }, + { "krb5_kdcinfo_lookahead", DP_OPT_STRING, NULL_STRING, NULL_STRING }, { "krb5_map_user", DP_OPT_STRING, NULL_STRING, NULL_STRING }, DP_OPTION_TERMINATOR }; diff --git a/src/providers/ad/ad_subdomains.c b/src/providers/ad/ad_subdomains.c index f12b4c3c7..0080d726f 100644 --- a/src/providers/ad/ad_subdomains.c +++ b/src/providers/ad/ad_subdomains.c @@ -251,6 +251,8 @@ ad_subdom_ad_ctx_new(struct be_ctx *be_ctx, char *subdom_conf_path; bool ad_use_ldaps = false; bool use_kdcinfo = false; + size_t n_lookahead_primary = SSS_KRB5_LOOKAHEAD_PRIMARY_DEFAULT; + size_t n_lookahead_backup = SSS_KRB5_LOOKAHEAD_BACKUP_DEFAULT; realm = dp_opt_get_cstring(id_ctx->ad_options->basic, AD_KRB5_REALM); hostname = dp_opt_get_cstring(id_ctx->ad_options->basic, AD_HOSTNAME); @@ -334,6 +336,11 @@ ad_subdom_ad_ctx_new(struct be_ctx *be_ctx, && id_ctx->ad_options->auth_ctx->opts != NULL) { use_kdcinfo = dp_opt_get_bool(id_ctx->ad_options->auth_ctx->opts, KRB5_USE_KDCINFO); + sss_krb5_parse_lookahead( + dp_opt_get_string(id_ctx->ad_options->auth_ctx->opts, + KRB5_KDCINFO_LOOKAHEAD), + &n_lookahead_primary, + &n_lookahead_backup); } DEBUG(SSSDBG_TRACE_ALL, @@ -343,6 +350,8 @@ ad_subdom_ad_ctx_new(struct be_ctx *be_ctx, ret = ad_failover_init(ad_options, be_ctx, servers, backup_servers, subdom->realm, service_name, gc_service_name, subdom->name, ad_use_ldaps, use_kdcinfo, + n_lookahead_primary, + n_lookahead_backup, &ad_options->service); if (ret != EOK) { DEBUG(SSSDBG_OP_FAILURE, "Cannot initialize AD failover\n"); diff --git a/src/providers/ipa/ipa_common.c b/src/providers/ipa/ipa_common.c index 3f574d128..847e771ea 100644 --- a/src/providers/ipa/ipa_common.c +++ b/src/providers/ipa/ipa_common.c @@ -745,6 +745,12 @@ int ipa_get_auth_options(struct ipa_options *ipa_opts, DEBUG(SSSDBG_CONF_SETTINGS, "Option %s set to %s\n", ipa_opts->auth[KRB5_USE_KDCINFO].opt_name, ipa_opts->service->krb5_service->write_kdcinfo ? "true" : "false"); + if (ipa_opts->service->krb5_service->write_kdcinfo) { + sss_krb5_parse_lookahead( + dp_opt_get_string(ipa_opts->auth, KRB5_KDCINFO_LOOKAHEAD), + &ipa_opts->service->krb5_service->lookahead_primary, + &ipa_opts->service->krb5_service->lookahead_backup); + } *_opts = ipa_opts->auth; ret = EOK; @@ -966,10 +972,10 @@ int ipa_service_init(TALLOC_CTX *memctx, struct be_ctx *ctx, service->krb5_service = krb5_service_new(service, ctx, "IPA", realm, - true); /* The configured value - * will be set later when - * the auth provider is set up - */ + true, /* The configured value */ + 0, /* will be set later when */ + 0); /* the auth provider is set up */ + if (!service->krb5_service) { ret = ENOMEM; goto done; diff --git a/src/providers/ipa/ipa_opts.c b/src/providers/ipa/ipa_opts.c index 85e4bd5ab..672b96c37 100644 --- a/src/providers/ipa/ipa_opts.c +++ b/src/providers/ipa/ipa_opts.c @@ -121,6 +121,7 @@ struct dp_option ipa_def_ldap_opts[] = { { "krb5_realm", DP_OPT_STRING, NULL_STRING, NULL_STRING }, { "krb5_canonicalize", DP_OPT_BOOL, BOOL_TRUE, BOOL_TRUE }, { "krb5_use_kdcinfo", DP_OPT_BOOL, BOOL_TRUE, BOOL_TRUE }, + { "krb5_kdcinfo_lookahead", DP_OPT_STRING, NULL_STRING, NULL_STRING }, { "ldap_pwd_policy", DP_OPT_STRING, { "none" } , NULL_STRING }, { "ldap_referrals", DP_OPT_BOOL, BOOL_TRUE, BOOL_TRUE }, { "account_cache_expiration", DP_OPT_NUMBER, { .number = 0 }, NULL_NUMBER }, @@ -321,6 +322,7 @@ struct dp_option ipa_def_krb5_opts[] = { { "krb5_canonicalize", DP_OPT_BOOL, BOOL_TRUE, BOOL_TRUE }, { "krb5_use_enterprise_principal", DP_OPT_BOOL, BOOL_FALSE, BOOL_FALSE }, { "krb5_use_kdcinfo", DP_OPT_BOOL, BOOL_TRUE, BOOL_TRUE }, + { "krb5_kdcinfo_lookahead", DP_OPT_STRING, NULL_STRING, NULL_STRING }, { "krb5_map_user", DP_OPT_STRING, NULL_STRING, NULL_STRING }, DP_OPTION_TERMINATOR }; diff --git a/src/providers/ipa/ipa_subdomains.c b/src/providers/ipa/ipa_subdomains.c index bf8b18a92..e5d093461 100644 --- a/src/providers/ipa/ipa_subdomains.c +++ b/src/providers/ipa/ipa_subdomains.c @@ -681,7 +681,9 @@ ipa_subdom_get_k5_svc(struct ipa_subdomains_ctx *ctx, ctx->be_ctx, "IPA", dom->realm, - use_kdcinfo); + use_kdcinfo, + (size_t) -1, + (size_t) -1); if (k5svc_ent->k5svc == NULL) { talloc_free(k5svc_ent); return NULL; diff --git a/src/providers/ipa/ipa_subdomains_server.c b/src/providers/ipa/ipa_subdomains_server.c index 70235abba..54ee87aeb 100644 --- a/src/providers/ipa/ipa_subdomains_server.c +++ b/src/providers/ipa/ipa_subdomains_server.c @@ -244,6 +244,8 @@ ipa_ad_ctx_new(struct be_ctx *be_ctx, errno_t ret; const char *extra_attrs; bool use_kdcinfo = false; + size_t n_lookahead_primary = (size_t)-1; + size_t n_lookahead_backup = (size_t)-1; ad_domain = subdom->name; DEBUG(SSSDBG_TRACE_LIBS, "Setting up AD subdomain %s\n", subdom->name); @@ -303,6 +305,10 @@ ipa_ad_ctx_new(struct be_ctx *be_ctx, if (id_ctx->ipa_options != NULL && id_ctx->ipa_options->auth != NULL) { use_kdcinfo = dp_opt_get_bool(id_ctx->ipa_options->auth, KRB5_USE_KDCINFO); + sss_krb5_parse_lookahead( + dp_opt_get_string(id_ctx->ipa_options->auth, KRB5_KDCINFO_LOOKAHEAD), + &n_lookahead_primary, + &n_lookahead_backup); } DEBUG(SSSDBG_TRACE_ALL, @@ -316,6 +322,7 @@ ipa_ad_ctx_new(struct be_ctx *be_ctx, subdom->realm, service_name, gc_service_name, subdom->name, false, use_kdcinfo, + n_lookahead_primary, n_lookahead_backup, &ad_options->service); if (ret != EOK) { DEBUG(SSSDBG_OP_FAILURE, "Cannot initialize AD failover\n"); diff --git a/src/providers/krb5/krb5_common.c b/src/providers/krb5/krb5_common.c index 1e33fc0f5..e56820b8d 100644 --- a/src/providers/krb5/krb5_common.c +++ b/src/providers/krb5/krb5_common.c @@ -390,6 +390,39 @@ done: return ret; } +void sss_krb5_parse_lookahead(const char *param, size_t *primary, size_t *backup) +{ + int ret; + + if (primary == NULL || backup == NULL) { + return; + } + + *primary = SSS_KRB5_LOOKAHEAD_PRIMARY_DEFAULT; + *backup = SSS_KRB5_LOOKAHEAD_BACKUP_DEFAULT; + + if (param == NULL) { + return; + } + + if (strchr(param, ':')) { + ret = sscanf(param, "%zu:%zu", primary, backup); + if (ret != 2) { + DEBUG(SSSDBG_MINOR_FAILURE, "Could not parse krb5_kdcinfo_lookahead!\n"); + } + } else { + ret = sscanf(param, "%zu", primary); + if (ret != 1) { + DEBUG(SSSDBG_MINOR_FAILURE, "Could not parse krb5_kdcinfo_lookahead!\n"); + } + } + + DEBUG(SSSDBG_CONF_SETTINGS, + "Option krb5_kdcinfo_lookahead set to %zu:%zu", + *primary, *backup); +} + + static int remove_info_files_destructor(void *p) { int ret; @@ -668,6 +701,13 @@ errno_t write_krb5info_file_from_fo_server(struct krb5_service *krb5_service, int primary; const char *address; errno_t ret; + size_t n_lookahead_primary; + size_t n_lookahead_backup; + + if (krb5_service == NULL) { + DEBUG(SSSDBG_CRIT_FAILURE, "The krb5_service must not be NULL!\n"); + return EINVAL; + } tmp_ctx = talloc_new(NULL); if (tmp_ctx == NULL) { @@ -675,6 +715,9 @@ errno_t write_krb5info_file_from_fo_server(struct krb5_service *krb5_service, return ENOMEM; } + n_lookahead_primary = krb5_service->lookahead_primary; + n_lookahead_backup = krb5_service->lookahead_backup; + server_idx = 0; server_list = talloc_zero_array(tmp_ctx, const char *, @@ -689,6 +732,15 @@ errno_t write_krb5info_file_from_fo_server(struct krb5_service *krb5_service, address = fo_server_address_or_name(tmp_ctx, server); if (address) { server_list[server_idx++] = address; + if (fo_is_server_primary(server)) { + if (n_lookahead_primary > 0) { + n_lookahead_primary--; + } + } else { + if (n_lookahead_backup > 0) { + n_lookahead_backup--; + } + } } else { DEBUG(SSSDBG_CRIT_FAILURE, "Server without name and address found in list.\n"); @@ -700,6 +752,8 @@ errno_t write_krb5info_file_from_fo_server(struct krb5_service *krb5_service, item != server; item = fo_server_next(item) ? fo_server_next(item) : fo_server_first(item)) { + if (primary && n_lookahead_primary == 0) break; + if (!primary && n_lookahead_backup == 0) break; if (primary && !fo_is_server_primary(item)) continue; if (!primary && fo_is_server_primary(item)) continue; if (filter != NULL && filter(item)) continue; @@ -712,6 +766,11 @@ errno_t write_krb5info_file_from_fo_server(struct krb5_service *krb5_service, } server_list[server_idx++] = address; + if (primary) { + n_lookahead_primary--; + } else { + n_lookahead_backup--; + } } } if (server_list[0] == NULL) { @@ -901,7 +960,9 @@ struct krb5_service *krb5_service_new(TALLOC_CTX *mem_ctx, struct be_ctx *be_ctx, const char *service_name, const char *realm, - bool use_kdcinfo) + bool use_kdcinfo, + size_t n_lookahead_primary, + size_t n_lookahead_backup) { struct krb5_service *service; @@ -927,6 +988,9 @@ struct krb5_service *krb5_service_new(TALLOC_CTX *mem_ctx, realm, use_kdcinfo ? "true" : "false"); service->write_kdcinfo = use_kdcinfo; + service->lookahead_primary = n_lookahead_primary; + service->lookahead_backup = n_lookahead_backup; + service->be_ctx = be_ctx; return service; } @@ -937,6 +1001,8 @@ int krb5_service_init(TALLOC_CTX *memctx, struct be_ctx *ctx, const char *backup_servers, const char *realm, bool use_kdcinfo, + size_t n_lookahead_primary, + size_t n_lookahead_backup, struct krb5_service **_service) { TALLOC_CTX *tmp_ctx; @@ -948,7 +1014,8 @@ int krb5_service_init(TALLOC_CTX *memctx, struct be_ctx *ctx, return ENOMEM; } - service = krb5_service_new(tmp_ctx, ctx, service_name, realm, use_kdcinfo); + service = krb5_service_new(tmp_ctx, ctx, service_name, realm, use_kdcinfo, + n_lookahead_primary, n_lookahead_backup); if (!service) { ret = ENOMEM; goto done; diff --git a/src/providers/krb5/krb5_common.h b/src/providers/krb5/krb5_common.h index 5f68ab38f..df4ad822d 100644 --- a/src/providers/krb5/krb5_common.h +++ b/src/providers/krb5/krb5_common.h @@ -39,6 +39,8 @@ #define SSS_KRB5KDC_FO_SRV "KERBEROS" #define SSS_KRB5KPASSWD_FO_SRV "KPASSWD" +#define SSS_KRB5_LOOKAHEAD_PRIMARY_DEFAULT 3 +#define SSS_KRB5_LOOKAHEAD_BACKUP_DEFAULT 1 enum krb5_opts { KRB5_KDC = 0, @@ -60,6 +62,7 @@ enum krb5_opts { KRB5_CANONICALIZE, KRB5_USE_ENTERPRISE_PRINCIPAL, KRB5_USE_KDCINFO, + KRB5_KDCINFO_LOOKAHEAD, KRB5_MAP_USER, KRB5_OPTS @@ -72,6 +75,8 @@ struct krb5_service { char *name; char *realm; bool write_kdcinfo; + size_t lookahead_primary; + size_t lookahead_backup; bool removal_callback_available; }; @@ -161,6 +166,8 @@ errno_t krb5_try_kdcip(struct confdb_ctx *cdb, const char *conf_path, errno_t sss_krb5_get_options(TALLOC_CTX *memctx, struct confdb_ctx *cdb, const char *conf_path, struct dp_option **_opts); +void sss_krb5_parse_lookahead(const char *param, size_t *primary, size_t *backup); + errno_t write_krb5info_file(struct krb5_service *krb5_service, const char **server_list, const char *service); @@ -174,7 +181,9 @@ struct krb5_service *krb5_service_new(TALLOC_CTX *mem_ctx, struct be_ctx *be_ctx, const char *service_name, const char *realm, - bool use_kdcinfo); + bool use_kdcinfo, + size_t n_lookahead_primary, + size_t n_lookahead_backup); int krb5_service_init(TALLOC_CTX *memctx, struct be_ctx *ctx, const char *service_name, @@ -182,6 +191,8 @@ int krb5_service_init(TALLOC_CTX *memctx, struct be_ctx *ctx, const char *backup_servers, const char *realm, bool use_kdcinfo, + size_t n_lookahead_primary, + size_t n_lookahead_backup, struct krb5_service **_service); void remove_krb5_info_files_callback(void *pvt); diff --git a/src/providers/krb5/krb5_init.c b/src/providers/krb5/krb5_init.c index 66ae68fb4..3f4c1b361 100644 --- a/src/providers/krb5/krb5_init.c +++ b/src/providers/krb5/krb5_init.c @@ -40,6 +40,8 @@ static errno_t krb5_init_kpasswd(struct krb5_ctx *ctx, const char *backup_servers; const char *kdc_servers; bool use_kdcinfo; + size_t n_lookahead_primary; + size_t n_lookahead_backup; errno_t ret; realm = dp_opt_get_string(ctx->opts, KRB5_REALM); @@ -52,6 +54,9 @@ static errno_t krb5_init_kpasswd(struct krb5_ctx *ctx, primary_servers = dp_opt_get_string(ctx->opts, KRB5_KPASSWD); backup_servers = dp_opt_get_string(ctx->opts, KRB5_BACKUP_KPASSWD); use_kdcinfo = dp_opt_get_bool(ctx->opts, KRB5_USE_KDCINFO); + sss_krb5_parse_lookahead(dp_opt_get_string(ctx->opts, KRB5_KDCINFO_LOOKAHEAD), + &n_lookahead_primary, &n_lookahead_backup); + if (primary_servers == NULL && backup_servers != NULL) { DEBUG(SSSDBG_CONF_SETTINGS, "kpasswd server wasn't specified but " @@ -67,7 +72,10 @@ static errno_t krb5_init_kpasswd(struct krb5_ctx *ctx, } else { ret = krb5_service_init(ctx, be_ctx, SSS_KRB5KPASSWD_FO_SRV, primary_servers, backup_servers, realm, - use_kdcinfo, &ctx->kpasswd_service); + use_kdcinfo, + n_lookahead_primary, + n_lookahead_backup, + &ctx->kpasswd_service); if (ret != EOK) { DEBUG(SSSDBG_FATAL_FAILURE, "Failed to init KRB5KPASSWD failover service!\n"); @@ -84,6 +92,8 @@ static errno_t krb5_init_kdc(struct krb5_ctx *ctx, struct be_ctx *be_ctx) const char *backup_servers; const char *realm; bool use_kdcinfo; + size_t n_lookahead_primary; + size_t n_lookahead_backup; errno_t ret; realm = dp_opt_get_string(ctx->opts, KRB5_REALM); @@ -96,10 +106,15 @@ static errno_t krb5_init_kdc(struct krb5_ctx *ctx, struct be_ctx *be_ctx) backup_servers = dp_opt_get_string(ctx->opts, KRB5_BACKUP_KDC); use_kdcinfo = dp_opt_get_bool(ctx->opts, KRB5_USE_KDCINFO); + sss_krb5_parse_lookahead(dp_opt_get_string(ctx->opts, KRB5_KDCINFO_LOOKAHEAD), + &n_lookahead_primary, &n_lookahead_backup); ret = krb5_service_init(ctx, be_ctx, SSS_KRB5KDC_FO_SRV, primary_servers, backup_servers, realm, - use_kdcinfo, &ctx->service); + use_kdcinfo, + n_lookahead_primary, + n_lookahead_backup, + &ctx->service); if (ret != EOK) { DEBUG(SSSDBG_FATAL_FAILURE, "Failed to init KRB5 failover service!\n"); return ret; diff --git a/src/providers/krb5/krb5_opts.c b/src/providers/krb5/krb5_opts.c index 6bec52767..05395e0f4 100644 --- a/src/providers/krb5/krb5_opts.c +++ b/src/providers/krb5/krb5_opts.c @@ -42,6 +42,7 @@ struct dp_option default_krb5_opts[] = { { "krb5_canonicalize", DP_OPT_BOOL, BOOL_FALSE, BOOL_FALSE }, { "krb5_use_enterprise_principal", DP_OPT_BOOL, BOOL_FALSE, BOOL_FALSE }, { "krb5_use_kdcinfo", DP_OPT_BOOL, BOOL_TRUE, BOOL_TRUE }, + { "krb5_kdcinfo_lookahead", DP_OPT_STRING, NULL_STRING, NULL_STRING }, { "krb5_map_user", DP_OPT_STRING, NULL_STRING, NULL_STRING }, DP_OPTION_TERMINATOR }; diff --git a/src/providers/ldap/ldap_common.c b/src/providers/ldap/ldap_common.c index 414ea78f0..0f94f4d67 100644 --- a/src/providers/ldap/ldap_common.c +++ b/src/providers/ldap/ldap_common.c @@ -338,6 +338,8 @@ int sdap_gssapi_init(TALLOC_CTX *mem_ctx, const char *krb5_opt_realm; struct krb5_service *service = NULL; TALLOC_CTX *tmp_ctx; + size_t n_lookahead_primary; + size_t n_lookahead_backup; tmp_ctx = talloc_new(NULL); if (tmp_ctx == NULL) return ENOMEM; @@ -364,11 +366,18 @@ int sdap_gssapi_init(TALLOC_CTX *mem_ctx, } } + sss_krb5_parse_lookahead( + dp_opt_get_string(opts, SDAP_KRB5_KDCINFO_LOOKAHEAD), + &n_lookahead_primary, + &n_lookahead_backup); + ret = krb5_service_init(mem_ctx, bectx, SSS_KRB5KDC_FO_SRV, krb5_servers, krb5_backup_servers, krb5_realm, dp_opt_get_bool(opts, SDAP_KRB5_USE_KDCINFO), + n_lookahead_primary, + n_lookahead_backup, &service); if (ret != EOK) { DEBUG(SSSDBG_FATAL_FAILURE, "Failed to init KRB5 failover service!\n"); diff --git a/src/providers/ldap/ldap_opts.c b/src/providers/ldap/ldap_opts.c index 6b711f665..dfa0928bd 100644 --- a/src/providers/ldap/ldap_opts.c +++ b/src/providers/ldap/ldap_opts.c @@ -82,6 +82,7 @@ struct dp_option default_basic_opts[] = { { "krb5_realm", DP_OPT_STRING, NULL_STRING, NULL_STRING }, { "krb5_canonicalize", DP_OPT_BOOL, BOOL_TRUE, BOOL_TRUE }, { "krb5_use_kdcinfo", DP_OPT_BOOL, BOOL_TRUE, BOOL_TRUE }, + { "krb5_kdcinfo_lookahead", DP_OPT_STRING, NULL_STRING, NULL_STRING }, { "ldap_pwd_policy", DP_OPT_STRING, { "none" }, NULL_STRING }, { "ldap_referrals", DP_OPT_BOOL, BOOL_TRUE, BOOL_TRUE }, { "account_cache_expiration", DP_OPT_NUMBER, { .number = 0 }, NULL_NUMBER }, diff --git a/src/providers/ldap/sdap.h b/src/providers/ldap/sdap.h index be559b977..33f6b7931 100644 --- a/src/providers/ldap/sdap.h +++ b/src/providers/ldap/sdap.h @@ -198,6 +198,7 @@ enum sdap_basic_opt { SDAP_KRB5_REALM, SDAP_KRB5_CANONICALIZE, SDAP_KRB5_USE_KDCINFO, + SDAP_KRB5_KDCINFO_LOOKAHEAD, SDAP_PWD_POLICY, SDAP_REFERRALS, SDAP_ACCOUNT_CACHE_EXPIRATION, -- 2.37.0
Locations
Projects
Search
Status Monitor
Help
OpenBuildService.org
Documentation
API Documentation
Code of Conduct
Contact
Support
@OBShq
Terms
openSUSE Build Service is sponsored by
The Open Build Service is an
openSUSE project
.
Sign Up
Log In
Places
Places
All Projects
Status Monitor