| From 5a38f9f94514669cfcd2d814bbc31fde3aed683c Mon Sep 17 00:00:00 2001 |
| From: Jeremy Allison <jra@samba.org> |
| Date: Fri, 28 Aug 2020 15:14:31 -0700 |
| Subject: [PATCH] Add MIT krb5 locator plugin that uses Samba async SRV DNS |
| lookups. |
| |
| Signed-off-by: Jeremy Allison <jra@samba.org> |
| --- |
| nsswitch/krb5_plugin/async_dns_krb5_locator.c | 458 ++++++++++++++++++ |
| nsswitch/wscript_build | 17 + |
| 2 files changed, 475 insertions(+) |
| create mode 100644 nsswitch/krb5_plugin/async_dns_krb5_locator.c |
| |
| diff --git a/nsswitch/krb5_plugin/async_dns_krb5_locator.c b/nsswitch/krb5_plugin/async_dns_krb5_locator.c |
| new file mode 100644 |
| index 00000000000..66aac7a2933 |
| --- /dev/null |
| +++ b/nsswitch/krb5_plugin/async_dns_krb5_locator.c |
| @@ -0,0 +1,458 @@ |
| +/* |
| + Unix SMB/CIFS implementation. |
| + Async DNS kerberos locator plugin |
| + Copyright (C) Guenther Deschner 2007-2008 |
| + Copyright (C) Jeremy Allison 2020. |
| + |
| + 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 "../../source3/include/includes.h" |
| +#include "../../source3/libsmb/namequery.h" |
| + |
| +#ifndef DEBUG_KRB5 |
| +#undef DEBUG_KRB5 |
| +#endif |
| + |
| +/* Uncomment to debug. */ |
| +/* #define DEBUG_KRB5 1 */ |
| + |
| +#if defined(HAVE_KRB5) && defined(HAVE_KRB5_LOCATE_PLUGIN_H) |
| + |
| +#ifdef HAVE_COM_ERR_H |
| +#include <com_err.h> |
| +#endif |
| + |
| +#include <krb5.h> |
| +#include <krb5/locate_plugin.h> |
| + |
| +#ifndef KRB5_PLUGIN_NO_HANDLE |
| +#define KRB5_PLUGIN_NO_HANDLE KRB5_KDC_UNREACH /* Heimdal */ |
| +#endif |
| + |
| +struct singleton_realm_kdc_list_cache { |
| + char *realm; |
| + struct ip_service *kdc_list; |
| + int num_kdcs; |
| +}; |
| + |
| +static struct singleton_realm_kdc_list_cache *scache; |
| + |
| +static const char *get_service_from_locate_service_type(enum locate_service_type svc) |
| +{ |
| + switch (svc) { |
| + case locate_service_kdc: |
| + case locate_service_master_kdc: |
| + return "88"; |
| + case locate_service_kadmin: |
| + case locate_service_krb524: |
| + /* not supported */ |
| + return NULL; |
| + case locate_service_kpasswd: |
| + return "464"; |
| + default: |
| + break; |
| + } |
| + return NULL; |
| + |
| +} |
| + |
| +#ifdef DEBUG_KRB5 |
| +static const char *locate_service_type_name(enum locate_service_type svc) |
| +{ |
| + switch (svc) { |
| + case locate_service_kdc: |
| + return "locate_service_kdc"; |
| + case locate_service_master_kdc: |
| + return "locate_service_master_kdc"; |
| + case locate_service_kadmin: |
| + return "locate_service_kadmin"; |
| + case locate_service_krb524: |
| + return "locate_service_krb524"; |
| + case locate_service_kpasswd: |
| + return "locate_service_kpasswd"; |
| + default: |
| + break; |
| + } |
| + return NULL; |
| +} |
| + |
| +static const char *socktype_name(int socktype) |
| +{ |
| + switch (socktype) { |
| + case SOCK_STREAM: |
| + return "SOCK_STREAM"; |
| + case SOCK_DGRAM: |
| + return "SOCK_DGRAM"; |
| + default: |
| + break; |
| + } |
| + return "unknown"; |
| +} |
| + |
| +static const char *family_name(int family) |
| +{ |
| + switch (family) { |
| + case AF_UNSPEC: |
| + return "AF_UNSPEC"; |
| + case AF_INET: |
| + return "AF_INET"; |
| +#if defined(HAVE_IPV6) |
| + case AF_INET6: |
| + return "AF_INET6"; |
| +#endif |
| + default: |
| + break; |
| + } |
| + return "unknown"; |
| +} |
| +#endif |
| + |
| +/** |
| + * Check input parameters, return KRB5_PLUGIN_NO_HANDLE for unsupported ones |
| + * |
| + * @param svc |
| + * @param realm string |
| + * @param socktype integer |
| + * @param family integer |
| + * |
| + * @return integer. |
| + */ |
| + |
| +static int smb_krb5_adns_locator_lookup_sanity_check( |
| + enum locate_service_type svc, |
| + const char *realm, |
| + int socktype, |
| + int family) |
| +{ |
| + if (!realm || strlen(realm) == 0) { |
| + return EINVAL; |
| + } |
| + |
| + switch (svc) { |
| + case locate_service_kdc: |
| + case locate_service_master_kdc: |
| + break; |
| + case locate_service_kadmin: |
| + case locate_service_krb524: |
| + case locate_service_kpasswd: |
| + return KRB5_PLUGIN_NO_HANDLE; |
| + default: |
| + return EINVAL; |
| + } |
| + |
| + switch (family) { |
| + case AF_UNSPEC: |
| + case AF_INET: |
| +#if defined(HAVE_IPV6) |
| + case AF_INET6: |
| +#endif |
| + break; |
| + default: |
| + return EINVAL; |
| + } |
| + |
| + switch (socktype) { |
| + case SOCK_STREAM: |
| + case SOCK_DGRAM: |
| + case 0: /* Heimdal uses that */ |
| + break; |
| + default: |
| + return EINVAL; |
| + } |
| + |
| + return 0; |
| +} |
| + |
| +/** |
| + * Call back into the MIT libraries with each address |
| + * we found. Assume AD-DC's always support both UDP and |
| + * TCP port 88 for KDC service. |
| + */ |
| + |
| +static krb5_error_code smb_krb5_adns_locator_call_cbfunc( |
| + struct ip_service *kdcs, |
| + int num_kdcs, |
| + const char *service, |
| + int socktype, |
| + int (*cbfunc)(void *, int, struct sockaddr *), |
| + void *cbdata) |
| +{ |
| + int ret = 0; |
| + int i; |
| + struct sockaddr *sa = NULL; |
| + |
| + for (i = 0; i < num_kdcs; i++) { |
| + if (kdcs[i].ss.ss_family == AF_INET) { |
| + struct sockaddr_in *sin = (struct sockaddr_in *)&kdcs[i].ss; |
| + sin->sin_family = AF_INET; |
| + sin->sin_port = htons(88); |
| + sa = (struct sockaddr *)sin; |
| + } |
| +#if defined(HAVE_IPV6) |
| + if (kdcs[i].ss.ss_family == AF_INET6) { |
| + struct sockaddr_in6 *sin6 = (struct sockaddr_in6 *)&kdcs[i].ss; |
| + sin6->sin6_family = AF_INET6; |
| + sin6->sin6_port = htons(88); |
| + sa = (struct sockaddr *)sin6; |
| + } |
| +#else |
| + else { |
| + return KRB5_PLUGIN_NO_HANDLE; |
| + } |
| +#endif |
| + |
| +#ifdef DEBUG_KRB5 |
| + { |
| + char addr[INET6_ADDRSTRLEN]; |
| + fprintf(stderr, "[%5u]: " |
| + "smb_krb5_adns_locator_call_cbfunc: " |
| + "IP[%d] %s\n", |
| + (unsigned int)getpid(), |
| + i, |
| + print_sockaddr(addr, |
| + sizeof(addr), |
| + &kdcs[i].ss)); |
| + } |
| +#endif |
| + |
| + /* Assume all AD-DC's do both UDP and TCP on port 88. */ |
| + ret = cbfunc(cbdata, socktype, sa); |
| + if (ret) { |
| +#ifdef DEBUG_KRB5 |
| + fprintf(stderr, "[%5u]: " |
| + "smb_krb5_adns_locator_call_cbfunc: " |
| + "failed to call callback: %s (%d)\n", |
| + (unsigned int)getpid(), |
| + error_message(ret), |
| + ret); |
| +#endif |
| + break; |
| + } |
| + } |
| + return ret; |
| +} |
| + |
| +/** |
| + * PUBLIC INTERFACE: locate init |
| + * |
| + * @param context krb5_context |
| + * @param privata_data pointer to private data pointer |
| + * |
| + * @return krb5_error_code. |
| + */ |
| + |
| +static krb5_error_code smb_krb5_adns_locator_init(krb5_context context, |
| + void **private_data) |
| +{ |
| + static bool loaded_config; |
| + if (!loaded_config) { |
| + lp_load_global(get_dyn_CONFIGFILE()); |
| + loaded_config = true; |
| + } |
| +#ifdef DEBUG_KRB5 |
| + fprintf(stderr,"[%5u]: smb_krb5_adns_locator_init\n", |
| + (unsigned int)getpid()); |
| +#endif |
| + return 0; |
| +} |
| + |
| +/** |
| + * PUBLIC INTERFACE: close locate |
| + * |
| + * @param private_data pointer to private data |
| + * |
| + * @return void. |
| + */ |
| + |
| +static void smb_krb5_adns_locator_close(void *private_data) |
| +{ |
| +#ifdef DEBUG_KRB5 |
| + fprintf(stderr,"[%5u]: smb_krb5_adns_locator_close\n", |
| + (unsigned int)getpid()); |
| +#endif |
| + return; |
| +} |
| + |
| +/** |
| + * PUBLIC INTERFACE: locate lookup |
| + * |
| + * @param private_data pointer to private data |
| + * @param svc enum locate_service_type. |
| + * @param realm string |
| + * @param socktype integer |
| + * @param family integer |
| + * @param cbfunc callback function to send back entries |
| + * @param cbdata void pointer to cbdata |
| + * |
| + * @return krb5_error_code. |
| + */ |
| + |
| +static krb5_error_code smb_krb5_adns_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) |
| +{ |
| + krb5_error_code ret; |
| + const char *service = get_service_from_locate_service_type(svc); |
| + |
| +#ifdef DEBUG_KRB5 |
| + fprintf(stderr,"[%5u]: smb_krb5_adns_locator_lookup: called for '%s' " |
| + "svc: '%s' (%d) " |
| + "socktype: '%s' (%d), family: '%s' (%d)\n", |
| + (unsigned int)getpid(), |
| + realm, |
| + locate_service_type_name(svc), |
| + svc, |
| + socktype_name(socktype), |
| + socktype, |
| + family_name(family), |
| + family); |
| +#endif |
| + ret = smb_krb5_adns_locator_lookup_sanity_check(svc, |
| + realm, |
| + socktype, |
| + family); |
| + if (ret) { |
| +#ifdef DEBUG_KRB5 |
| + fprintf(stderr, "[%5u]: smb_krb5_adns_locator_lookup: " |
| + "returning ret: %s (%d)\n", |
| + (unsigned int)getpid(), |
| + error_message(ret), |
| + ret); |
| +#endif |
| + return ret; |
| + } |
| + |
| + /* |
| + * If is a subsequent lookup for the same realm |
| + * and we have a cache for this already, don't re-do |
| + * the DNS SRV -> A/AAAA lookups. |
| + * |
| + * kinit does this a lot, it looks for UDP then TCP. |
| + */ |
| + |
| + if ((scache == NULL) || strcmp(realm, scache->realm) != 0) { |
| + /* Cache is NULL or a different realm lookup. */ |
| + NTSTATUS status; |
| + struct ip_service *kdcs = NULL; |
| + int num_kdcs = 0; |
| + |
| + /* |
| + * We have a new lookup to do. As it's a singleton |
| + * cache make sure we have no old cache. |
| + */ |
| + TALLOC_FREE(scache); |
| + |
| + status = get_kdc_list(realm, NULL, &kdcs, &num_kdcs); |
| + if (!NT_STATUS_IS_OK(status)) { |
| +#ifdef DEBUG_KRB5 |
| + fprintf(stderr, "[%5u]: " |
| + "smb_krb5_adns_locator_lookup: " |
| + "get_kdc_list() for realm %s failed " |
| + "with %s\n", |
| + (unsigned int)getpid(), |
| + realm, |
| + nt_errstr(status)); |
| +#endif |
| + SAFE_FREE(kdcs); |
| + return KRB5_PLUGIN_NO_HANDLE; |
| + } |
| + if (num_kdcs <= 0) { |
| + SAFE_FREE(kdcs); |
| + return KRB5_PLUGIN_NO_HANDLE; |
| + } |
| + /* |
| + * We looked up the realm successfully. |
| + * free up any previous scache and store it. |
| + */ |
| + |
| + scache = talloc_zero(NULL, |
| + struct singleton_realm_kdc_list_cache); |
| + if (scache == NULL) { |
| + SAFE_FREE(kdcs); |
| + return KRB5_PLUGIN_NO_HANDLE; |
| + } |
| + scache->realm = talloc_strdup(scache, realm); |
| + if (scache->realm == NULL) { |
| + TALLOC_FREE(scache); |
| + SAFE_FREE(kdcs); |
| + return KRB5_PLUGIN_NO_HANDLE; |
| + } |
| + scache->kdc_list = talloc_memdup(scache, |
| + kdcs, |
| + sizeof(struct ip_service) * num_kdcs); |
| + if (scache->kdc_list == NULL) { |
| + TALLOC_FREE(scache); |
| + SAFE_FREE(kdcs); |
| + return KRB5_PLUGIN_NO_HANDLE; |
| + } |
| + SAFE_FREE(kdcs); |
| + scache->num_kdcs = num_kdcs; |
| + } |
| +#ifdef DEBUG_KRB5 |
| + else { |
| + fprintf(stderr, "[%5u]: " |
| + "smb_krb5_adns_locator_lookup: " |
| + "returning cached data for realm %s\n", |
| + (unsigned int)getpid(), |
| + realm); |
| + } |
| +#endif |
| + /* |
| + * If we get here we know scache contains the right |
| + * realm and non-null address list. |
| + */ |
| + |
| +#ifdef DEBUG_KRB5 |
| + fprintf(stderr, "[%5u]: smb_krb5_adns_locator_lookup: " |
| + "got %d IP addresses for realm %s\n", |
| + (unsigned int)getpid(), |
| + scache->num_kdcs, |
| + scache->realm); |
| +#endif |
| + |
| + /* |
| + * Don't free kdc list on success, we're |
| + * always returning from the cache. |
| + */ |
| + return smb_krb5_adns_locator_call_cbfunc(scache->kdc_list, |
| + scache->num_kdcs, |
| + service, |
| + socktype, |
| + cbfunc, |
| + cbdata); |
| +} |
| + |
| +#ifdef HEIMDAL_KRB5_LOCATE_PLUGIN_H |
| +#define SMB_KRB5_LOCATOR_SYMBOL_NAME resolve /* Heimdal */ |
| +#else |
| +#define SMB_KRB5_LOCATOR_SYMBOL_NAME service_locator /* MIT */ |
| +#endif |
| + |
| +const krb5plugin_service_locate_ftable SMB_KRB5_LOCATOR_SYMBOL_NAME = { |
| + .minor_version = 0, |
| + .init = smb_krb5_adns_locator_init, |
| + .fini = smb_krb5_adns_locator_close, |
| +#ifdef KRB5_PLUGIN_LOCATE_VERSION_2 |
| + .old_lookup = smb_krb5_adns_locator_lookup, |
| +#else |
| + .lookup = smb_krb5_adns_locator_lookup, |
| +#endif |
| +}; |
| + |
| +#endif |
| diff --git a/nsswitch/wscript_build b/nsswitch/wscript_build |
| index 861ed2f23bf..b7710e5200d 100644 |
| --- a/nsswitch/wscript_build |
| +++ b/nsswitch/wscript_build |
| @@ -112,6 +112,23 @@ if bld.CONFIG_SET('HAVE_KRB5_LOCATE_PLUGIN_H'): |
| realname='winbind_krb5_locator.so', |
| install_path='${MODULESDIR}/krb5') |
| |
| +if bld.CONFIG_SET('HAVE_KRB5_LOCATE_PLUGIN_H'): |
| + bld.SAMBA_LIBRARY('async_dns_krb5_locator', |
| + source='krb5_plugin/async_dns_krb5_locator.c', |
| + deps=''' |
| + talloc |
| + addns |
| + samba_intl |
| + libsmb |
| + smbconf |
| + KRBCLIENT |
| + smbd_base |
| + krb5 |
| + com_err |
| + ''', |
| + realname='async_dns_krb5_locator.so', |
| + install_path='${MODULESDIR}/krb5') |
| + |
| if bld.CONFIG_SET('HAVE_KRB5_LOCALAUTH_PLUGIN_H'): |
| bld.SAMBA_LIBRARY('winbind_krb5_localauth', |
| source='krb5_plugin/winbind_krb5_localauth.c', |
| -- |
| 2.20.1 |
| |