diff options
| author | Ken Raeburn <raeburn@mit.edu> | 2006-03-07 20:45:24 +0000 |
|---|---|---|
| committer | Ken Raeburn <raeburn@mit.edu> | 2006-03-07 20:45:24 +0000 |
| commit | 8f09bfe9fa0e51c2bd1e2f533eb25655e88ca43b (patch) | |
| tree | 68c4097fc6650d9d2952fdc0b242263b60ae7f95 /src/plugins/locate/python | |
| parent | ca39d95f3cb9681664d3761f4c0c2ec23d36dfd3 (diff) | |
| download | krb5-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/ChangeLog | 5 | ||||
| -rw-r--r-- | src/plugins/locate/python/Makefile.in | 42 | ||||
| -rw-r--r-- | src/plugins/locate/python/configure.in | 10 | ||||
| -rw-r--r-- | src/plugins/locate/python/locate-service.py | 77 | ||||
| -rw-r--r-- | src/plugins/locate/python/py-locate.c | 286 | ||||
| -rw-r--r-- | src/plugins/locate/python/python.exports | 1 |
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 |
