diff --git a/source/Makefile.in b/source/Makefile.in index 091d2f4812f..83150a44de0 100644 --- a/source/Makefile.in +++ b/source/Makefile.in @@ -844,6 +844,10 @@ LDBADD_OBJ = $(LDB_CMDLINE_OBJ) lib/ldb/tools/ldbadd.o LDBDEL_OBJ = $(LDB_CMDLINE_OBJ) lib/ldb/tools/ldbdel.o LDBMODIFY_OBJ = $(LDB_CMDLINE_OBJ) lib/ldb/tools/ldbmodify.o +SMB_KRB5_LOCATOR_OBJ1 = libads/smb_krb5_locator.o +SMB_KRB5_LOCATOR_OBJ = $(SMB_KRB5_LOCATOR_OBJ1) $(PARAM_OBJ) $(LIB_NONSMBD_OBJ) \ + $(LIBNMB_OBJ) $(RPC_PARSE_OBJ1) $(SECRETS_OBJ) $(LIBSAMBA_OBJ) $(DOSERR_OBJ) + POPT_OBJ=popt/findme.o popt/popt.o popt/poptconfig.o \ popt/popthelp.o popt/poptparse.o @@ -1370,6 +1374,12 @@ bin/winbindd@EXEEXT@: proto_exists $(WINBINDD_OBJ) @BUILD_POPT@ bin/.dummy $(LDAP_LIBS) $(KRB5LIBS) $(LIBS) \ @SONAMEFLAG@`basename $@`@NSSSONAMEVERSIONSUFFIX@ +@SMB_KRB5_LOCATOR@: $(SMB_KRB5_LOCATOR_OBJ) + @echo "Linking $@" + @$(SHLD) $(LDSHFLAGS) -o $@ $(SMB_KRB5_LOCATOR_OBJ) \ + $(LDAP_LIBS) $(LIBS) -lcom_err \ + @SONAMEFLAG@`basename $@` + bin/pam_winbind.@SHLIBEXT@: $(PAM_WINBIND_OBJ) bin/.dummy @echo "Linking shared library $@" @$(SHLD) $(LDSHFLAGS) -o $@ $(PAM_WINBIND_OBJ) -lpam @INIPARSERLIBS@ $(GPLIBS) \ diff --git a/source/configure.in b/source/configure.in index 6a380a1cdeb..5cd07924f65 100644 --- a/source/configure.in +++ b/source/configure.in @@ -3446,6 +3446,7 @@ if test x"$with_ads_support" != x"no"; then CPPFLAGS=$ac_save_CPPFLAGS LDFLAGS=$ac_save_LDFLAGS fi + AC_CHECK_HEADERS(krb5/locate_plugin.h) fi # Now we have determined whether we really want ADS support @@ -5624,6 +5625,8 @@ WINBIND_WINS_NSS="nsswitch/libnss_wins.$SHLIBEXT" WINBIND_NSS_LDSHFLAGS=$LDSHFLAGS NSSSONAMEVERSIONSUFFIX="" +SMB_KRB5_LOCATOR="bin/smb_krb5_locator.$SHLIBEXT" + case "$host_os" in *linux*) NSSSONAMEVERSIONSUFFIX=".2" @@ -5693,6 +5696,8 @@ AC_SUBST(WINBIND_NSS_EXTRA_OBJS) AC_SUBST(WINBIND_NSS_EXTRA_LIBS) AC_SUBST(NSSSONAMEVERSIONSUFFIX) +AC_SUBST(SMB_KRB5_LOCATOR) + # Check the setting of --with-winbind AC_ARG_WITH(winbind, diff --git a/source/libads/smb_krb5_locator.c b/source/libads/smb_krb5_locator.c new file mode 100644 index 00000000000..9861511714b --- /dev/null +++ b/source/libads/smb_krb5_locator.c @@ -0,0 +1,384 @@ +/* + Unix SMB/CIFS implementation. + kerberos locator plugin + Copyright (C) Guenther Deschner 2007 + + 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 2 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, write to the Free Software + Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA. +*/ + +#include "includes.h" + +#if defined(HAVE_KRB5) && defined(HAVE_KRB5_LOCATE_PLUGIN_H) + +#include + +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; + +} + +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"; + case AF_INET6: + return "AF_INET6"; + default: + break; + } + return "unknown"; +} + +/** + * 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_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: + case locate_service_kpasswd: + break; + case locate_service_kadmin: + case locate_service_krb524: +#ifdef KRB5_PLUGIN_NO_HANDLE + return KRB5_PLUGIN_NO_HANDLE; +#else + return KRB5_KDC_UNREACH; /* Heimdal */ +#endif + default: + return EINVAL; + } + + switch (family) { + case AF_UNSPEC: + case AF_INET: + break; + case AF_INET6: /* not yet */ +#ifdef KRB5_PLUGIN_NO_HANDLE + return KRB5_PLUGIN_NO_HANDLE; +#else + return KRB5_KDC_UNREACH; /* Heimdal */ +#endif + default: + return EINVAL; + } + + switch (socktype) { + case SOCK_STREAM: + case SOCK_DGRAM: + case 0: /* Heimdal uses that */ + break; + default: + return EINVAL; + } + + return 0; +} + +/** + * Try to get addrinfo for a given host and call the krb5 callback + * + * @param name string + * @param service string + * @param in struct addrinfo hint + * @param cbfunc krb5 callback function + * @param cbdata void pointer cbdata + * + * @return krb5_error_code. + */ + +static krb5_error_code smb_krb5_locator_call_cbfunc(const char *name, + const char *service, + struct addrinfo *in, + int (*cbfunc)(void *, int, struct sockaddr *), + void *cbdata) +{ + struct addrinfo *out; + int ret; + int count = 3; + + while (count) { + + ret = getaddrinfo(name, service, in, &out); + if (ret == 0) { + break; + } + + if (ret == EAI_AGAIN) { + count--; + continue; + } + + DEBUG(10,("smb_krb5_locator_lookup: got ret: %s (%d)\n", + gai_strerror(ret), ret)); +#ifdef KRB5_PLUGIN_NO_HANDLE + return KRB5_PLUGIN_NO_HANDLE; +#else + return KRB5_KDC_UNREACH; /* Heimdal */ +#endif + } + + ret = cbfunc(cbdata, out->ai_socktype, out->ai_addr); + if (ret) { + DEBUG(10,("smb_krb5_locator_lookup: failed to call callback: %s (%d)\n", + error_message(ret), ret)); + } + + freeaddrinfo(out); + + return ret; +} + +/** + * PUBLIC INTERFACE: locate init + * + * @param context krb5_context + * @param privata_data pointer to private data pointer + * + * @return krb5_error_code. + */ + +krb5_error_code smb_krb5_locator_init(krb5_context context, + void **private_data) +{ + setup_logging("smb_krb5_locator", True); + load_case_tables(); + lp_load(dyn_CONFIGFILE,True,False,False,True); + + DEBUG(10,("smb_krb5_locator_init: called\n")); + + return 0; +} + +/** + * PUBLIC INTERFACE: close locate + * + * @param private_data pointer to private data + * + * @return void. + */ + +void smb_krb5_locator_close(void *private_data) +{ + DEBUG(10,("smb_krb5_locator_close: called\n")); + + gfree_all(); +} + +/** + * 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. + */ + +krb5_error_code smb_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) +{ + NTSTATUS status; + krb5_error_code ret; + char *sitename = NULL; + struct ip_service *ip_list; + int count = 0; + struct addrinfo aihints; + char *saf_name = NULL; + int i; + + DEBUG(10,("smb_krb5_locator_lookup: called for\n")); + DEBUGADD(10,("\tsvc: %s (%d), realm: %s\n", + locate_service_type_name(svc), svc, realm)); + DEBUGADD(10,("\tsocktype: %s (%d), family: %s (%d)\n", + socktype_name(socktype), socktype, + family_name(family), family)); + + ret = smb_krb5_locator_lookup_sanity_check(svc, realm, socktype, family); + if (ret) { + DEBUG(10,("smb_krb5_locator_lookup: returning ret: %s (%d)\n", + error_message(ret), ret)); + return ret; + } + + /* first try to fetch from SAF cache */ + + saf_name = saf_fetch(realm); + if (!saf_name || strlen(saf_name) == 0) { + DEBUG(10,("smb_krb5_locator_lookup: no SAF name stored for %s\n", + realm)); + goto find_kdc; + } + + DEBUG(10,("smb_krb5_locator_lookup: got %s for %s from SAF cache\n", + saf_name, realm)); + + ZERO_STRUCT(aihints); + + aihints.ai_family = family; + aihints.ai_socktype = socktype; + + ret = smb_krb5_locator_call_cbfunc(saf_name, + get_service_from_locate_service_type(svc), + &aihints, + cbfunc, cbdata); + if (ret) { + return ret; + } + + return 0; + + find_kdc: + + /* now try to find via site-aware DNS SRV query */ + + sitename = sitename_fetch(realm); + status = get_kdc_list(realm, sitename, &ip_list, &count); + + /* if we didn't found any KDCs on our site go to the main list */ + + if (NT_STATUS_IS_OK(status) && sitename && (count == 0)) { + ip_list = NULL; + SAFE_FREE(sitename); + status = get_kdc_list(realm, NULL, &ip_list, &count); + } + + SAFE_FREE(sitename); + + if (!NT_STATUS_IS_OK(status)) { + DEBUG(10,("smb_krb5_locator_lookup: got %s (%s)\n", + nt_errstr(status), + error_message(nt_status_to_krb5(status)))); +#ifdef KRB5_PLUGIN_NO_HANDLE + return KRB5_PLUGIN_NO_HANDLE; +#else + return KRB5_KDC_UNREACH; /* Heimdal */ +#endif + } + + for (i=0; i