summaryrefslogtreecommitdiffstats
path: root/src/plugins/locate/python/py-locate.c
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/py-locate.c
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/py-locate.c')
-rw-r--r--src/plugins/locate/python/py-locate.c286
1 files changed, 286 insertions, 0 deletions
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,
+};