summaryrefslogtreecommitdiffstats
path: root/src/plugins/locate/python
diff options
context:
space:
mode:
authorKen Raeburn <raeburn@mit.edu>2006-03-07 20:45:24 +0000
committerKen Raeburn <raeburn@mit.edu>2006-03-07 20:45:24 +0000
commit8f09bfe9fa0e51c2bd1e2f533eb25655e88ca43b (patch)
tree68c4097fc6650d9d2952fdc0b242263b60ae7f95 /src/plugins/locate/python
parentca39d95f3cb9681664d3761f4c0c2ec23d36dfd3 (diff)
downloadkrb5-8f09bfe9fa0e51c2bd1e2f533eb25655e88ca43b.tar.gz
krb5-8f09bfe9fa0e51c2bd1e2f533eb25655e88ca43b.tar.xz
krb5-8f09bfe9fa0e51c2bd1e2f533eb25655e88ca43b.zip
Merge from plugin branch
Add plugin support: - plugin routines in support library (may break windows build!) - plugin support in KDC location code - sample Python-based plugin for KDC location, not built without tweaking sources - changed service location interface to use an enum instead of passing profile string and DNS strings and port numbers - changed pathnames for plugin locations, including kdb back end - remove locate_service from accessor API Also, do build shared libraries for Darwin just like any other UNIX box. Not present yet: - use new plugin interface for kdb back end - Windows support - Mac bundle support (but dlopen support works) - search path for libkrb5 plugins (only one hard-coded directory for now) - sorting of plugin collections for predictable ordering See the various ChangeLogs for specifics. git-svn-id: svn://anonsvn.mit.edu/krb5/trunk@17706 dc483132-0cff-0310-8789-dd5450dbe970
Diffstat (limited to 'src/plugins/locate/python')
-rw-r--r--src/plugins/locate/python/ChangeLog5
-rw-r--r--src/plugins/locate/python/Makefile.in42
-rw-r--r--src/plugins/locate/python/configure.in10
-rw-r--r--src/plugins/locate/python/locate-service.py77
-rw-r--r--src/plugins/locate/python/py-locate.c286
-rw-r--r--src/plugins/locate/python/python.exports1
6 files changed, 421 insertions, 0 deletions
diff --git a/src/plugins/locate/python/ChangeLog b/src/plugins/locate/python/ChangeLog
new file mode 100644
index 000000000..6824a2945
--- /dev/null
+++ b/src/plugins/locate/python/ChangeLog
@@ -0,0 +1,5 @@
+2006-03-06 Ken Raeburn <raeburn@mit.edu>
+
+ * Makefile.in, configure.in, py-locate.c, python.exports,
+ locate-service.py: New files.
+
diff --git a/src/plugins/locate/python/Makefile.in b/src/plugins/locate/python/Makefile.in
new file mode 100644
index 000000000..46a83fede
--- /dev/null
+++ b/src/plugins/locate/python/Makefile.in
@@ -0,0 +1,42 @@
+thisconfigdir=.
+myfulldir=plugins/locate/python
+mydir=.
+BUILDTOP=$(REL)..$(S)..$(S)..
+
+LIBBASE=python
+LIBMAJOR=0
+LIBMINOR=0
+SO_EXT=.so
+RELDIR=../plugins/locate/python
+MODULE_INSTALL_DIR = $(KRB5_LIBKRB5_MODULE_DIR)
+
+SHLIB_EXPDEPS=
+SHLIB_EXPLIBS= -lpython2.3
+
+SHLIB_DIRS=-L$(TOPLIBD)
+SHLIB_RDIRS=$(KRB5_LIBDIR)
+
+SRCS= \
+ $(srcdir)/py-locate.c
+STOBJLISTS=OBJS.ST
+STLIBOBJS= py-locate.o
+
+all-unix:: $(LIBBASE)$(SO_EXT)
+install-unix:: install-libs
+clean-unix:: clean-libs clean-libobjs
+
+# @libnover_frag@
+# @libobj_frag@
+
+# +++ Dependency line eater +++
+#
+# Makefile dependencies follow. This must be the last section in
+# the Makefile.in file
+#
+py-locate.so py-locate.po $(OUTPRE)py-locate.$(OBJEXT): \
+ py-locate.c $(BUILDTOP)/include/krb5/autoconf.h $(SRCTOP)/include/k5-int.h \
+ $(BUILDTOP)/include/krb5/osconf.h $(SRCTOP)/include/k5-platform.h \
+ $(SRCTOP)/include/k5-thread.h $(BUILDTOP)/include/krb5.h \
+ $(COM_ERR_DEPS) $(BUILDTOP)/include/profile.h $(SRCTOP)/include/port-sockets.h \
+ $(SRCTOP)/include/socket-utils.h $(SRCTOP)/include/krb5/kdb.h \
+ $(SRCTOP)/include/k5-plugin.h
diff --git a/src/plugins/locate/python/configure.in b/src/plugins/locate/python/configure.in
new file mode 100644
index 000000000..34f8306b5
--- /dev/null
+++ b/src/plugins/locate/python/configure.in
@@ -0,0 +1,10 @@
+K5_AC_INIT(configure.in)
+enable_shared=yes
+build_dynobj=yes
+CONFIG_RULES
+AC_CHECK_HEADERS(Python.h python2.3/Python.h)
+dnl AC_CHECK_LIB(python2.3)
+
+KRB5_BUILD_LIBOBJS
+KRB5_BUILD_LIBRARY_WITH_DEPS
+V5_AC_OUTPUT_MAKEFILE
diff --git a/src/plugins/locate/python/locate-service.py b/src/plugins/locate/python/locate-service.py
new file mode 100644
index 000000000..53153be77
--- /dev/null
+++ b/src/plugins/locate/python/locate-service.py
@@ -0,0 +1,77 @@
+# Copyright 2006 Massachusetts Institute of Technology.
+# All Rights Reserved.
+#
+# Export of this software from the United States of America may
+# require a specific license from the United States Government.
+# It is the responsibility of any person or organization contemplating
+# export to obtain such a license before exporting.
+#
+# WITHIN THAT CONSTRAINT, permission to use, copy, modify, and
+# distribute this software and its documentation for any purpose and
+# without fee is hereby granted, provided that the above copyright
+# notice appear in all copies and that both that copyright notice and
+# this permission notice appear in supporting documentation, and that
+# the name of M.I.T. not be used in advertising or publicity pertaining
+# to distribution of the software without specific, written prior
+# permission. Furthermore if you modify this software you must label
+# your software as modified software and not distribute it in such a
+# fashion that it might be confused with the original M.I.T. software.
+# M.I.T. makes no representations about the suitability of
+# this software for any purpose. It is provided "as is" without express
+# or implied warranty.
+
+# possible return values:
+# False: request not handled by this script, try another means
+# empty list: no server available, e.g., TCP KDC in realm with only UDP
+# ordered list of (ip-addr-string, port-number-or-string, socket-type)
+#
+# Field ip-addr-string is a numeric representation of the IPv4 or IPv6
+# address. Field port-number-or-string is, for example, "88" or 88. The
+# socket type is also expressed numerically, SOCK_DGRAM or SOCK_STREAM.
+# It must agree with the supplied socktype value if that is non-zero, but
+# zero must not be used in the returned list.
+#
+# service enum values: kdc=1, master_kdc, kadmin, krb524, kpasswd
+
+from socket import getaddrinfo, SOCK_STREAM, SOCK_DGRAM, AF_INET, AF_INET6
+def locate1 (service, realm, socktype, family):
+ if (service == 1 or service == 2) and realm == "ATHENA.MIT.EDU":
+ if socktype == SOCK_STREAM: return []
+ socktype = SOCK_DGRAM
+ result = []
+ hlist = (("kerberos.mit.edu", 88), ("kerberos-1.mit.edu", 88),
+ ("some-random-name-that-does-not-exist.mit.edu", 12345),
+ ("kerberos.mit.edu", 750))
+ if service == 2: hlist = (hlist[0],)
+ for (hname,hport) in hlist:
+ try:
+ alist = getaddrinfo(hname, hport, family, socktype)
+ for a in alist:
+ (fam, stype, proto, canonname, sa) = a
+ if fam == AF_INET or fam == AF_INET6:
+ addr = sa[0]
+ port = sa[1]
+ result = result + [(addr, port, stype)]
+ except Exception, inst:
+# print "getaddrinfo error for " + hname + ":", inst
+ pass # Enh, this is just a demo.
+ return result
+ if realm == "BOBO.MIT.EDU": return []
+ return False
+
+verbose = 0
+servicenames = { 1: "kdc", 2: "master_kdc", 3: "kadmin", 4: "krb524", 5: "kpasswd" }
+socktypenames = { SOCK_STREAM: "STREAM", SOCK_DGRAM: "DGRAM" }
+familynames = { 0: "UNSPEC", AF_INET: "INET", AF_INET6: "INET6" }
+
+def locate (service, realm, socktype, family):
+ socktypename = socktype
+ if socktype in socktypenames: socktypename = "%s(%d)" % (socktypenames[socktype], socktype)
+ familyname = family
+ if family in familynames: familyname = "%s(%d)" % (familynames[family], family)
+ servicename = service
+ if service in servicenames: servicename = "%s(%d)" % (servicenames[service], service)
+ if verbose: print "locate called with service", servicename, "realm", realm, "socktype", socktypename, "family", familyname
+ result = locate1 (service, realm, socktype, family)
+ if verbose: print "locate result is", result
+ return result
diff --git a/src/plugins/locate/python/py-locate.c b/src/plugins/locate/python/py-locate.c
new file mode 100644
index 000000000..a315e3555
--- /dev/null
+++ b/src/plugins/locate/python/py-locate.c
@@ -0,0 +1,286 @@
+/*
+ * plugins/locate/python/py-locate.c
+ *
+ * Copyright 2006 Massachusetts Institute of Technology.
+ * All Rights Reserved.
+ *
+ * Export of this software from the United States of America may
+ * require a specific license from the United States Government.
+ * It is the responsibility of any person or organization contemplating
+ * export to obtain such a license before exporting.
+ *
+ * WITHIN THAT CONSTRAINT, permission to use, copy, modify, and
+ * distribute this software and its documentation for any purpose and
+ * without fee is hereby granted, provided that the above copyright
+ * notice appear in all copies and that both that copyright notice and
+ * this permission notice appear in supporting documentation, and that
+ * the name of M.I.T. not be used in advertising or publicity pertaining
+ * to distribution of the software without specific, written prior
+ * permission. Furthermore if you modify this software you must label
+ * your software as modified software and not distribute it in such a
+ * fashion that it might be confused with the original M.I.T. software.
+ * M.I.T. makes no representations about the suitability of
+ * this software for any purpose. It is provided "as is" without express
+ * or implied warranty.
+ */
+
+/* This is a demo module. The error checking is incomplete, there's
+ no exception handling, and it wouldn't surprise me in the least if
+ there are more bugs in the refcount maintenance.
+
+ But it will demonstrate (1) the plugin interface for locating a KDC
+ or other Kerberos-related service, and (2) that it's possible for
+ these plugins to call out to scripts in various languages for
+ prototyping or whatever.
+
+ Some notes:
+
+ If delayed initialization is not done, and the script is executed
+ when this module is loaded, loading other Python modules may not
+ work, if they include object code referencing the Python symbols.
+ Under glibc at least, it appears that the symbols of this module
+ aren't available to random dlopen/dlsym calls until loading
+ finishes, including the initialization routine. It's completely
+ logical -- in fact, I'd be concerned if it were otherwise. But not
+ obvious if you're not thinking about it.
+
+ This module seems rather sensitive to bugs in the Python code. If
+ it's not correct, you may get core dumps, Python GC errors, etc.
+ Probably more signs of bugs in this code.
+
+ All of the -1 returns should be cleaned up and made to return
+ real error codes, with appropriate output if debugging is enabled.
+
+ Blah. */
+
+/* Include Python.h before autoconf.h, because our autoconf.h seems
+ to confuse Python's headers. */
+#if HAVE_PYTHON_H
+#include <Python.h>
+#elif HAVE_PYTHON2_3_PYTHON_H
+#include <python2.3/Python.h>
+#else
+#error "Where's the Python header file?"
+#endif
+#include <autoconf.h>
+#include <errno.h>
+#include "k5-int.h"
+
+#include "k5-plugin.h"
+
+#define LIBDIR "/tmp" /* should be imported from configure */
+#define SCRIPT_PATH LIBDIR "/krb5/locate-service.py"
+#define LOOKUP_FUNC_NAME "locate"
+
+static PyObject *locatefn;
+
+MAKE_INIT_FUNCTION(my_init);
+MAKE_FINI_FUNCTION(my_fini);
+
+#define F (strchr(__FILE__, '/') ? 1 + strrchr(__FILE__, '/') : __FILE__)
+
+int
+my_init (void)
+{
+ PyObject *mainmodule;
+ FILE *f;
+
+ Py_Initialize ();
+// fprintf(stderr, "trying to load %s\n", SCRIPT_PATH);
+ f = fopen(SCRIPT_PATH, "r");
+ if (f == NULL)
+ return -1;
+ PyRun_SimpleFile (f, SCRIPT_PATH);
+ fclose(f);
+ mainmodule = PyModule_GetDict(PyImport_AddModule("__main__"));
+ if (PyErr_Occurred()) { fprintf(stderr,"%s:%d: python error\n", F, __LINE__); PyErr_Print(); return -1; }
+ locatefn = PyDict_GetItemString (mainmodule, LOOKUP_FUNC_NAME);
+ if (PyErr_Occurred()) { fprintf(stderr,"%s:%d: python error\n", F, __LINE__); PyErr_Print(); return -1; }
+ /* Don't DECREF mainmodule, it's sometimes causing crashes. */
+ if (locatefn == 0)
+ return -1;
+ if (!PyCallable_Check (locatefn)) {
+ Py_DECREF (locatefn);
+ locatefn = 0;
+ return -1;
+ }
+ if (PyErr_Occurred()) { fprintf(stderr,"%s:%d: python error\n", F, __LINE__); PyErr_Print(); return -1; }
+ return 0;
+}
+
+void
+my_fini (void)
+{
+// fprintf(stderr, "%s:%d: Python module finalization\n", F, __LINE__);
+ if (! INITIALIZER_RAN (my_init))
+ return;
+ Py_DECREF (locatefn);
+ locatefn = 0;
+ Py_Finalize ();
+}
+
+static krb5_error_code
+ctxinit (krb5_context ctx, void **blobptr)
+{
+ /* If we wanted to create a separate Python interpreter instance,
+ look up the pathname of the script in the config file used for
+ the current krb5_context, and load the script in that
+ interpreter, this would be a good place for it; the blob could
+ be allocated to hold the reference to the interpreter
+ instance. */
+ *blobptr = 0;
+ return 0;
+}
+
+static void
+ctxfini (void *blob)
+{
+}
+
+/* Special return codes:
+
+ 0: We set a (possibly empty) set of server locations in the result
+ field. If the server location set is empty, that means there
+ aren't any servers, *not* that we should try the krb5.conf file or
+ DNS or something.
+
+ KRB5_PLUGIN_NO_HANDLE: This realm or service isn't handled here,
+ try some other means.
+
+ Other: Some error happened here. It may be reported, if the
+ service can't be located by other means. (In this implementation,
+ the catch-all error code returned in a bunch of places is -1, which
+ isn't going to be very useful to the caller.) */
+
+static krb5_error_code
+lookup (void *blob, enum locate_service_type svc, const char *realm,
+ int socktype, int family,
+ int (*cbfunc)(void *, int, struct sockaddr *), void *cbdata)
+{
+ PyObject *py_result, *svcarg, *realmarg, *arglist;
+ int listsize, i, x;
+ struct addrinfo aihints, *airesult;
+ int thissocktype;
+
+// fprintf(stderr, "%s:%d: lookup(%d,%s,%d,%d)\n", F, __LINE__,
+// svc, realm, socktype, family);
+ i = CALL_INIT_FUNCTION (my_init);
+ if (i) {
+ fprintf(stderr, "%s:%d: module initialization failed %d\n",
+ F, __LINE__, i);
+ return i;
+ }
+ if (locatefn == 0)
+ return KRB5_PLUGIN_NO_HANDLE;
+ svcarg = PyInt_FromLong (svc);
+ /* error? */
+ realmarg = PyString_FromString ((char *) realm);
+ /* error? */
+ arglist = PyTuple_New (4);
+ /* error? */
+
+ PyTuple_SetItem (arglist, 0, svcarg);
+ PyTuple_SetItem (arglist, 1, realmarg);
+ PyTuple_SetItem (arglist, 2, PyInt_FromLong (socktype));
+ PyTuple_SetItem (arglist, 3, PyInt_FromLong (family));
+ /* references handed off, no decref */
+
+ py_result = PyObject_CallObject (locatefn, arglist);
+ Py_DECREF (arglist);
+ if (PyErr_Occurred()) { fprintf(stderr,"%s:%d: python error\n", F, __LINE__); PyErr_Print(); return -1; }
+ if (py_result == 0) {
+ fprintf(stderr, "%s:%d: returned null object\n", F, __LINE__);
+ return -1;
+ }
+ if (py_result == Py_False)
+ return KRB5_PLUGIN_NO_HANDLE;
+ if (! PyList_Check (py_result)) {
+ Py_DECREF (py_result);
+ fprintf(stderr, "%s:%d: returned non-list, non-False\n", F, __LINE__);
+ return -1;
+ }
+ listsize = PyList_Size (py_result);
+ /* allocate */
+ memset(&aihints, 0, sizeof(aihints));
+ aihints.ai_flags = AI_NUMERICHOST;
+ aihints.ai_family = family;
+ for (i = 0; i < listsize; i++) {
+ PyObject *answer, *field;
+ char *hoststr, *portstr, portbuf[3*sizeof(long) + 4];
+ int cbret;
+
+ answer = PyList_GetItem (py_result, i);
+ if (! PyTuple_Check (answer)) {
+ fprintf(stderr, "%s:%d: item %d non-tuple\n", F, __LINE__, i);
+ /* leak? */
+ return -1;
+ }
+ if (PyTuple_Size (answer) != 3) {
+ fprintf(stderr, "%s:%d: item %d tuple size %d should be 3\n", F, __LINE__, i,
+ PyTuple_Size (answer));
+ /* leak? */
+ return -1;
+ }
+ field = PyTuple_GetItem (answer, 0);
+ if (! PyString_Check (field)) {
+ /* leak? */
+ fprintf(stderr, "%s:%d: item %d first component not a string\n", F, __LINE__, i);
+ return -1;
+ }
+ hoststr = PyString_AsString (field);
+ field = PyTuple_GetItem (answer, 1);
+ if (PyString_Check (field)) {
+ portstr = PyString_AsString (field);
+ } else if (PyInt_Check (field)) {
+ sprintf(portbuf, "%ld", PyInt_AsLong (field));
+ portstr = portbuf;
+ } else {
+ fprintf(stderr, "%s:%d: item %d second component neither string nor int\n",
+ F, __LINE__, i);
+ /* leak? */
+ return -1;
+ }
+ field = PyTuple_GetItem (answer, 2);
+ if (! PyInt_Check (field)) {
+ fprintf(stderr, "%s:%d: item %d third component not int\n", F, __LINE__, i);
+ /* leak? */
+ return -1;
+ }
+ thissocktype = PyInt_AsLong (field);
+ switch (thissocktype) {
+ case SOCK_STREAM:
+ case SOCK_DGRAM:
+ /* okay */
+ if (socktype != 0 && socktype != thissocktype) {
+ fprintf(stderr, "%s:%d: item %d socket type %d should be %d\n",
+ F, __LINE__, i, thissocktype, socktype);
+ /* leak? */
+ return -1;
+ }
+ break;
+ default:
+ /* 0 is not acceptable */
+ fprintf(stderr, "%s:%d: item %d socket type %d invalid\n", F, __LINE__, i,
+ thissocktype);
+ /* leak? */
+ return -1;
+ }
+ aihints.ai_socktype = thissocktype;
+ x = getaddrinfo (hoststr, portstr, &aihints, &airesult);
+ if (x != 0)
+ continue;
+ cbret = cbfunc(cbdata, airesult->ai_socktype, airesult->ai_addr);
+ freeaddrinfo(airesult);
+ if (cbret != 0)
+ break;
+ }
+ Py_DECREF (py_result);
+ return 0;
+}
+
+const struct krb5plugin_service_locate_ftable service_locator = {
+ /* version */
+ 1, 0,
+ /* functions */
+ ctxinit, ctxfini, lookup,
+};
diff --git a/src/plugins/locate/python/python.exports b/src/plugins/locate/python/python.exports
new file mode 100644
index 000000000..60ff46e8d
--- /dev/null
+++ b/src/plugins/locate/python/python.exports
@@ -0,0 +1 @@
+service_locator