summaryrefslogtreecommitdiff
path: root/nsswitch/winbind_krb5_locator.c
diff options
context:
space:
mode:
authorKai Blin <kai@samba.org>2008-12-16 09:30:16 +0100
committerKai Blin <kai@samba.org>2008-12-16 13:02:45 +0100
commit6821d898d7ecf10c6a9359560c5cf1c50a812b62 (patch)
treeb4305a8ac51ff27bb37063a62fc34ceb4142bb90 /nsswitch/winbind_krb5_locator.c
parent46f530e42f42b860407696f4e4733ad88e69f1be (diff)
downloadsamba-6821d898d7ecf10c6a9359560c5cf1c50a812b62.tar.gz
samba-6821d898d7ecf10c6a9359560c5cf1c50a812b62.tar.bz2
samba-6821d898d7ecf10c6a9359560c5cf1c50a812b62.zip
nsswitch: Move source3 files to top level dir.
Don't move source4 files yet to not confuse git's rename tracking too much.
Diffstat (limited to 'nsswitch/winbind_krb5_locator.c')
-rw-r--r--nsswitch/winbind_krb5_locator.c411
1 files changed, 411 insertions, 0 deletions
diff --git a/nsswitch/winbind_krb5_locator.c b/nsswitch/winbind_krb5_locator.c
new file mode 100644
index 0000000000..b9e35bdec5
--- /dev/null
+++ b/nsswitch/winbind_krb5_locator.c
@@ -0,0 +1,411 @@
+/*
+ Unix SMB/CIFS implementation.
+ kerberos locator plugin
+ Copyright (C) Guenther Deschner 2007-2008
+
+ 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 "nsswitch/winbind_client.h"
+#include "libwbclient/wbclient.h"
+
+#ifndef DEBUG_KRB5
+#undef DEBUG_KRB5
+#endif
+
+#if defined(HAVE_KRB5) && defined(HAVE_KRB5_LOCATE_PLUGIN_H)
+
+#include <krb5/locate_plugin.h>
+
+#ifndef KRB5_PLUGIN_NO_HANDLE
+#define KRB5_PLUGIN_NO_HANDLE KRB5_KDC_UNREACH /* Heimdal */
+#endif
+
+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_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:
+ return KRB5_PLUGIN_NO_HANDLE;
+ default:
+ return EINVAL;
+ }
+
+ switch (family) {
+ case AF_UNSPEC:
+ case AF_INET:
+ break;
+#if defined(HAVE_IPV6)
+ case AF_INET6:
+ break;
+#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 = NULL;
+ int ret;
+ int count = 3;
+
+ while (count) {
+
+ ret = getaddrinfo(name, service, in, &out);
+ if (ret == 0) {
+ break;
+ }
+
+ if (ret == EAI_AGAIN) {
+ count--;
+ continue;
+ }
+
+#ifdef DEBUG_KRB5
+ fprintf(stderr, "[%5u]: smb_krb5_locator_lookup: "
+ "getaddrinfo failed: %s (%d)\n",
+ (unsigned int)getpid(), gai_strerror(ret), ret);
+#endif
+
+ return KRB5_PLUGIN_NO_HANDLE;
+ }
+
+ ret = cbfunc(cbdata, out->ai_socktype, out->ai_addr);
+#ifdef DEBUG_KRB5
+ if (ret) {
+ fprintf(stderr, "[%5u]: smb_krb5_locator_lookup: "
+ "failed to call callback: %s (%d)\n",
+ (unsigned int)getpid(), error_message(ret), ret);
+ }
+#endif
+
+ freeaddrinfo(out);
+ 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_locator_init(krb5_context context,
+ void **private_data)
+{
+ return 0;
+}
+
+/**
+ * PUBLIC INTERFACE: close locate
+ *
+ * @param private_data pointer to private data
+ *
+ * @return void.
+ */
+
+static void smb_krb5_locator_close(void *private_data)
+{
+ return;
+}
+
+
+static bool ask_winbind(const char *realm, char **dcname)
+{
+ wbcErr wbc_status;
+ const char *dc = NULL;
+ struct wbcDomainControllerInfoEx *dc_info = NULL;
+ uint32_t flags;
+
+ flags = WBC_LOOKUP_DC_KDC_REQUIRED |
+ WBC_LOOKUP_DC_IS_DNS_NAME |
+ WBC_LOOKUP_DC_RETURN_DNS_NAME |
+ WBC_LOOKUP_DC_IP_REQUIRED;
+
+ wbc_status = wbcLookupDomainControllerEx(realm, NULL, NULL, flags, &dc_info);
+
+ if (!WBC_ERROR_IS_OK(wbc_status)) {
+#ifdef DEBUG_KRB5
+ fprintf(stderr,"[%5u]: smb_krb5_locator_lookup: failed with: %s\n",
+ (unsigned int)getpid(), wbcErrorString(wbc_status));
+#endif
+ return false;
+ }
+
+ if (dc_info->dc_address) {
+ dc = dc_info->dc_address;
+ if (dc[0] == '\\') dc++;
+ if (dc[0] == '\\') dc++;
+ }
+
+ if (!dc && dc_info->dc_unc) {
+ dc = dc_info->dc_unc;
+ if (dc[0] == '\\') dc++;
+ if (dc[0] == '\\') dc++;
+ }
+
+ if (!dc) {
+ wbcFreeMemory(dc_info);
+ return false;
+ }
+
+ *dcname = strdup(dc);
+ if (!*dcname) {
+ wbcFreeMemory(dc_info);
+ return false;
+ }
+
+ wbcFreeMemory(dc_info);
+ return true;
+}
+
+/**
+ * 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_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;
+ struct addrinfo aihints;
+ char *kdc_name = NULL;
+ const char *service = get_service_from_locate_service_type(svc);
+
+ ZERO_STRUCT(aihints);
+
+#ifdef DEBUG_KRB5
+ fprintf(stderr,"[%5u]: smb_krb5_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_locator_lookup_sanity_check(svc, realm, socktype,
+ family);
+ if (ret) {
+#ifdef DEBUG_KRB5
+ fprintf(stderr, "[%5u]: smb_krb5_locator_lookup: "
+ "returning ret: %s (%d)\n",
+ (unsigned int)getpid(), error_message(ret), ret);
+#endif
+ return ret;
+ }
+
+ if (!winbind_env_set()) {
+ if (!ask_winbind(realm, &kdc_name)) {
+#ifdef DEBUG_KRB5
+ fprintf(stderr, "[%5u]: smb_krb5_locator_lookup: "
+ "failed to query winbindd\n",
+ (unsigned int)getpid());
+#endif
+ goto failed;
+ }
+ } else {
+ const char *env = NULL;
+ char *var = NULL;
+ if (asprintf(&var, "%s_%s",
+ WINBINDD_LOCATOR_KDC_ADDRESS, realm) == -1) {
+ goto failed;
+ }
+ env = getenv(var);
+ if (!env) {
+#ifdef DEBUG_KRB5
+ fprintf(stderr, "[%5u]: smb_krb5_locator_lookup: "
+ "failed to get kdc from env %s\n",
+ (unsigned int)getpid(), var);
+#endif
+ free(var);
+ goto failed;
+ }
+ free(var);
+
+ kdc_name = strdup(env);
+ if (!kdc_name) {
+ goto failed;
+ }
+ }
+#ifdef DEBUG_KRB5
+ fprintf(stderr, "[%5u]: smb_krb5_locator_lookup: "
+ "got '%s' for '%s' from winbindd\n", (unsigned int)getpid(),
+ kdc_name, realm);
+#endif
+
+ aihints.ai_family = family;
+ aihints.ai_socktype = socktype;
+
+ ret = smb_krb5_locator_call_cbfunc(kdc_name,
+ service,
+ &aihints,
+ cbfunc, cbdata);
+ SAFE_FREE(kdc_name);
+
+ return ret;
+
+ failed:
+ return KRB5_PLUGIN_NO_HANDLE;
+}
+
+#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 = {
+ 0, /* version */
+ smb_krb5_locator_init,
+ smb_krb5_locator_close,
+ smb_krb5_locator_lookup,
+};
+
+#endif