summaryrefslogtreecommitdiff
path: root/source3/libads
diff options
context:
space:
mode:
authorGünther Deschner <gd@samba.org>2007-03-13 16:04:17 +0000
committerGerald (Jerry) Carter <jerry@samba.org>2007-10-10 12:18:36 -0500
commit0e702698f9a7cf0e528f073bae65371ed58e5496 (patch)
tree3d1bf4dc3ad6c59d65f8eb407258c8bbf8dc382b /source3/libads
parente6ccc787d93289b76db5e5cbaa3b109047e7139c (diff)
downloadsamba-0e702698f9a7cf0e528f073bae65371ed58e5496.tar.gz
samba-0e702698f9a7cf0e528f073bae65371ed58e5496.tar.bz2
samba-0e702698f9a7cf0e528f073bae65371ed58e5496.zip
r21822: Adding experimental krb5 lib locator plugin.
This is a starting point and may get changed. Basically we need follow the exact same path to detect (K)DCs like other Samba tools/winbind do. In particular with regard to the server affinity cache and the site-awarness for DNS SRV lookups. To compile just call "make bin/smb_krb5_locator.so", copy to /usr/lib/plugin/krb5/ (Heimdal HEAD) or /usr/lib/krb5/plugins/libkrb5/ (MIT) and you should immediately be able to kinit to your AD domain without having your REALM with kdc or kpasswd directives defined in /etc/krb5.conf at all. Tested with todays Heimdal HEAD and MIT krb5 1.5. Guenther (This used to be commit 34ae610bd5b9fd1210f16beac07a1c5984144ca7)
Diffstat (limited to 'source3/libads')
-rw-r--r--source3/libads/smb_krb5_locator.c384
1 files changed, 384 insertions, 0 deletions
diff --git a/source3/libads/smb_krb5_locator.c b/source3/libads/smb_krb5_locator.c
new file mode 100644
index 0000000000..9861511714
--- /dev/null
+++ b/source3/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 <krb5/locate_plugin.h>
+
+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<count; i++) {
+
+ const char *host = NULL;
+ const char *port = NULL;
+
+ ZERO_STRUCT(aihints);
+
+ aihints.ai_family = family;
+ aihints.ai_socktype = socktype;
+
+ host = inet_ntoa(ip_list[i].ip);
+ port = get_service_from_locate_service_type(svc);
+
+ ret = smb_krb5_locator_call_cbfunc(host,
+ port,
+ &aihints,
+ cbfunc, cbdata);
+ if (ret) {
+ /* got error */
+ break;
+ }
+ }
+
+ return ret;
+}
+
+#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