Author: tross
Date: Wed Nov 11 17:33:33 2009
New Revision: 834975

URL: http://svn.apache.org/viewvc?rev=834975&view=rev
Log:
Added full SASL authentication and security layer for the Python client.

Added:
    qpid/trunk/qpid/cpp/bindings/sasl/
    qpid/trunk/qpid/cpp/bindings/sasl/Makefile.am
    qpid/trunk/qpid/cpp/bindings/sasl/cyrus/
    qpid/trunk/qpid/cpp/bindings/sasl/cyrus/saslwrapper.cpp
    qpid/trunk/qpid/cpp/bindings/sasl/python/
    qpid/trunk/qpid/cpp/bindings/sasl/python/python.i
    qpid/trunk/qpid/cpp/bindings/sasl/ruby/
    qpid/trunk/qpid/cpp/bindings/sasl/saslwrapper.h
    qpid/trunk/qpid/cpp/bindings/sasl/saslwrapper.i
Modified:
    qpid/trunk/qpid/cpp/Makefile.am
    qpid/trunk/qpid/cpp/configure.ac
    qpid/trunk/qpid/cpp/src/tests/acl.py
    qpid/trunk/qpid/python/commands/qpid-route
    qpid/trunk/qpid/python/qmf/console.py
    qpid/trunk/qpid/python/qpid/connection.py
    qpid/trunk/qpid/python/qpid/delegates.py
    qpid/trunk/qpid/python/qpid/framer.py

Modified: qpid/trunk/qpid/cpp/Makefile.am
URL: 
http://svn.apache.org/viewvc/qpid/trunk/qpid/cpp/Makefile.am?rev=834975&r1=834974&r2=834975&view=diff
==============================================================================
--- qpid/trunk/qpid/cpp/Makefile.am (original)
+++ qpid/trunk/qpid/cpp/Makefile.am Wed Nov 11 17:33:33 2009
@@ -26,7 +26,7 @@
   LICENSE NOTICE README SSL RELEASE_NOTES DESIGN \
   xml/cluster.xml INSTALL-WINDOWS
 
-SUBDIRS = managementgen etc src docs/api docs/man examples bindings/qmf
+SUBDIRS = managementgen etc src docs/api docs/man examples bindings/qmf 
bindings/sasl
 
 # Update libtool, if needed.
 libtool: $(LIBTOOL_DEPS)

Added: qpid/trunk/qpid/cpp/bindings/sasl/Makefile.am
URL: 
http://svn.apache.org/viewvc/qpid/trunk/qpid/cpp/bindings/sasl/Makefile.am?rev=834975&view=auto
==============================================================================
--- qpid/trunk/qpid/cpp/bindings/sasl/Makefile.am (added)
+++ qpid/trunk/qpid/cpp/bindings/sasl/Makefile.am Wed Nov 11 17:33:33 2009
@@ -0,0 +1,65 @@
+#
+# Licensed to the Apache Software Foundation (ASF) under one
+# or more contributor license agreements.  See the NOTICE file
+# distributed with this work for additional information
+# regarding copyright ownership.  The ASF licenses this file
+# to you under the Apache License, Version 2.0 (the
+# "License"); you may not use this file except in compliance
+# with the License.  You may obtain a copy of the License at
+#
+#   http://www.apache.org/licenses/LICENSE-2.0
+#
+# Unless required by applicable law or agreed to in writing,
+# software distributed under the License is distributed on an
+# "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
+# KIND, either express or implied.  See the License for the
+# specific language governing permissions and limitations
+# under the License.
+#
+
+if HAVE_SWIG
+if HAVE_SASL
+
+lib_LTLIBRARIES = libsaslwrapper.la
+libsaslwrapper_la_SOURCES = saslwrapper.h cyrus/saslwrapper.cpp
+
+# Library Version Information:
+#
+#  CURRENT  => API/ABI version.  Bump this if the interface changes
+#  REVISION => Version of underlying implementation.
+#              Bump if implementation changes but API/ABI doesn't
+#  AGE      => Number of API/ABI versions this is backward compatible with
+#
+CURRENT  = 1
+REVISION = 0
+AGE      = 0
+
+libsaslwrapper_la_LDFLAGS = -version-info $(CURRENT):$(REVISION):$(AGE)
+EXTRA_DIST = saslwrapper.i
+CLEANFILES =
+
+if HAVE_PYTHON_DEVEL
+
+EXTRA_DIST += $(srcdir)/python/python.i
+generated_file_list = python/qpidsasl.cpp python/qpidsasl.py
+BUILT_SOURCES = $(generated_file_list)
+
+$(generated_file_list): $(srcdir)/python/python.i $(srcdir)/saslwrapper.i
+       $(SWIG) -c++ -python -Wall -I/usr/include -I. -o python/qpidsasl.cpp 
$(srcdir)/python/python.i
+
+pylibdir = $(PYTHON_LIB)
+lib_LTLIBRARIES += _qpidsasl.la
+
+_qpidsasl_la_LDFLAGS = -avoid-version -module -shared
+_qpidsasl_la_LIBADD = $(PYTHON_LIBS) libsaslwrapper.la -lsasl2
+_qpidsasl_la_CXXFLAGS = -I$(PYTHON_INC)
+nodist__qpidsasl_la_SOURCES = python/qpidsasl.cpp
+
+CLEANFILES += $(generated_file_list)
+endif
+
+if HAVE_RUBY_DEVEL
+endif
+
+endif
+endif

Added: qpid/trunk/qpid/cpp/bindings/sasl/cyrus/saslwrapper.cpp
URL: 
http://svn.apache.org/viewvc/qpid/trunk/qpid/cpp/bindings/sasl/cyrus/saslwrapper.cpp?rev=834975&view=auto
==============================================================================
--- qpid/trunk/qpid/cpp/bindings/sasl/cyrus/saslwrapper.cpp (added)
+++ qpid/trunk/qpid/cpp/bindings/sasl/cyrus/saslwrapper.cpp Wed Nov 11 17:33:33 
2009
@@ -0,0 +1,372 @@
+/*
+ * Licensed to the Apache Software Foundation (ASF) under one
+ * or more contributor license agreements.  See the NOTICE file
+ * distributed with this work for additional information
+ * regarding copyright ownership.  The ASF licenses this file
+ * to you under the Apache License, Version 2.0 (the
+ * "License"); you may not use this file except in compliance
+ * with the License.  You may obtain a copy of the License at
+ *
+ *   http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing,
+ * software distributed under the License is distributed on an
+ * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
+ * KIND, either express or implied.  See the License for the
+ * specific language governing permissions and limitations
+ * under the License.
+*/
+
+#include "saslwrapper.h"
+#include <sasl/sasl.h>
+#include <sstream>
+#include <malloc.h>
+#include <string.h>
+#include <unistd.h>
+#include <iostream>
+
+using namespace std;
+using namespace saslwrapper;
+
+namespace saslwrapper {
+
+    class ClientImpl {
+        friend class Client;
+        ClientImpl() : conn(0), cbIndex(0), maxBufSize(65535), minSsf(0), 
maxSsf(65535), externalSsf(0), secret(0) {}
+        ~ClientImpl() { if (conn) sasl_dispose(&conn); conn = 0; }
+        bool setAttr(const string& key, const string& value);
+        bool setAttr(const string& key, uint32_t value);
+        bool init();
+        bool start(const string& mechList, output_string& chosen, 
output_string& initialResponse);
+        bool step(const string& challenge, output_string& response);
+        bool encode(const string& clearText, output_string& cipherText);
+        bool decode(const string& cipherText, output_string& clearText);
+        bool getUserId(output_string& userId);
+        void getError(output_string& error);
+
+        void addCallback(unsigned long id, void* proc);
+        void lastCallback() { addCallback(SASL_CB_LIST_END, 0); }
+        void setError(const string& context, int code, const string& text = 
"", const string& text2 = "");
+        void interact(sasl_interact_t* prompt);
+
+        static int cbName(void *context, int id, const char **result, unsigned 
*len);
+        static int cbPassword(sasl_conn_t *conn, void *context, int id, 
sasl_secret_t **psecret);
+
+        static bool initialized;
+        sasl_conn_t* conn;
+        sasl_callback_t callbacks[8];
+        int cbIndex;
+        string error;
+        string serviceName;
+        string userName;
+        string authName;
+        string password;
+        string hostName;
+        string externalUserName;
+        uint32_t maxBufSize;
+        uint32_t minSsf;
+        uint32_t maxSsf;
+        uint32_t externalSsf;
+        sasl_secret_t* secret;
+    };
+}
+
+bool ClientImpl::initialized = false;
+
+bool ClientImpl::init()
+{
+    int result;
+
+    if (!initialized) {
+        initialized = true;
+        result = sasl_client_init(0);
+        if (result != SASL_OK) {
+            setError("sasl_client_init", result, sasl_errstring(result, 0, 0));
+            return false;
+        }
+    }
+
+    int cbIndex = 0;
+
+    addCallback(SASL_CB_GETREALM, 0);
+    if (!userName.empty()) {
+        addCallback(SASL_CB_USER, (void*) cbName);
+        addCallback(SASL_CB_AUTHNAME, (void*) cbName);
+
+        if (!password.empty())
+            addCallback(SASL_CB_PASS, (void*) cbPassword);
+        else
+            addCallback(SASL_CB_PASS, 0);
+    }
+    lastCallback();
+
+    unsigned flags;
+
+    flags = 0;
+    if (!authName.empty() && authName != userName)
+        flags |= SASL_NEED_PROXY;
+
+    result = sasl_client_new(serviceName.c_str(), hostName.c_str(), 0, 0, 
callbacks, flags, &conn);
+    if (result != SASL_OK) {
+        setError("sasl_client_new", result, sasl_errstring(result, 0, 0));
+        return false;
+    }
+
+    sasl_security_properties_t secprops;
+
+    secprops.min_ssf = minSsf;
+    secprops.max_ssf = maxSsf;
+    secprops.maxbufsize = maxBufSize;
+    secprops.property_names = 0;
+    secprops.property_values = 0;
+    secprops.security_flags = 0;
+
+    result = sasl_setprop(conn, SASL_SEC_PROPS, &secprops);
+    if (result != SASL_OK) {
+        setError("sasl_setprop(SASL_SEC_PROPS)", result);
+        sasl_dispose(&conn);
+        conn = 0;
+        return false;
+    }
+
+    if (!externalUserName.empty()) {
+        result = sasl_setprop(conn, SASL_AUTH_EXTERNAL, 
externalUserName.c_str());
+        if (result != SASL_OK) {
+            setError("sasl_setprop(SASL_AUTH_EXTERNAL)", result);
+            sasl_dispose(&conn);
+            conn = 0;
+            return false;
+        }
+
+        result = sasl_setprop(conn, SASL_SSF_EXTERNAL, &externalSsf);
+        if (result != SASL_OK) {
+            setError("sasl_setprop(SASL_SSF_EXTERNAL)", result);
+            sasl_dispose(&conn);
+            conn = 0;
+            return false;
+        }
+    }
+
+    return true;
+}
+
+bool ClientImpl::setAttr(const string& key, const string& value)
+{
+    if (key == "service")
+        serviceName = value;
+    else if (key == "username")
+        userName = value;
+    else if (key == "authname")
+        authName = value;
+    else if (key == "password") {
+        password = value;
+        free(secret);
+        secret = (sasl_secret_t*) malloc(sizeof(sasl_secret_t) + 
password.length());
+    }
+    else if (key == "host")
+        hostName = value;
+    else if (key == "externaluser")
+        externalUserName = value;
+    else {
+        setError("setAttr", -1, "Unknown string attribute name", key);
+        return false;
+    }
+
+    return true;
+}
+
+bool ClientImpl::setAttr(const string& key, uint32_t value)
+{
+    if (key == "minssf")
+        minSsf = value;
+    else if (key == "maxssf")
+        maxSsf = value;
+    else if (key == "externalssf")
+        externalSsf = value;
+    else if (key == "maxbufsize")
+        maxBufSize = value;
+    else {
+        setError("setAttr", -1, "Unknown integer attribute name", key);
+        return false;
+    }
+
+    return true;
+}
+
+bool ClientImpl::start(const string& mechList, output_string& chosen, 
output_string& initialResponse)
+{
+    int result;
+    sasl_interact_t* prompt = 0;
+    const char* resp;
+    const char* mech;
+    unsigned int len;
+
+    do {
+        result = sasl_client_start(conn, mechList.c_str(), &prompt, &resp, 
&len, &mech);
+        if (result == SASL_INTERACT)
+            interact(prompt);
+    } while (result == SASL_INTERACT);
+    if (result != SASL_OK && result != SASL_CONTINUE) {
+        setError("sasl_client_start", result);
+        return false;
+    }
+
+    chosen = string(mech);
+    initialResponse = string(resp, len);
+    return true;
+}
+
+bool ClientImpl::step(const string& challenge, output_string& response)
+{
+    int result;
+    sasl_interact_t* prompt = 0;
+    const char* resp;
+    unsigned int len;
+
+    do {
+        result = sasl_client_step(conn, challenge.c_str(), challenge.size(), 
&prompt, &resp, &len);
+        if (result == SASL_INTERACT)
+            interact(prompt);
+    } while (result == SASL_INTERACT);
+    if (result != SASL_OK && result != SASL_CONTINUE) {
+        setError("sasl_client_step", result);
+        return false;
+    }
+
+    response = string(resp, len);
+    return true;
+}
+
+bool ClientImpl::encode(const string& clearText, output_string& cipherText)
+{
+    const char* output;
+    unsigned int outlen;
+    int result = sasl_encode(conn, clearText.c_str(), clearText.size(), 
&output, &outlen);
+    if (result != SASL_OK) {
+        setError("sasl_encode", result);
+        return false;
+    }
+    cipherText = string(output, outlen);
+    return true;
+}
+
+bool ClientImpl::decode(const string& cipherText, output_string& clearText)
+{
+    const char* output;
+    unsigned int outlen;
+    int result = sasl_decode(conn, cipherText.c_str(), cipherText.size(), 
&output, &outlen);
+    if (result != SASL_OK) {
+        setError("sasl_decode", result);
+        return false;
+    }
+    clearText = string(output, outlen);
+    return true;
+}
+
+bool ClientImpl::getUserId(output_string& userId)
+{
+    int result;
+    const char* operName;
+
+    result = sasl_getprop(conn, SASL_USERNAME, (const void**) &operName);
+    if (result != SASL_OK) {
+        setError("sasl_getprop(SASL_USERNAME)", result);
+        return false;
+    }
+
+    userId = string(operName);
+    return true;
+}
+
+void ClientImpl::getError(output_string& _error)
+{
+    _error = error;
+    error.clear();
+}
+
+void ClientImpl::addCallback(unsigned long id, void* proc)
+{
+    callbacks[cbIndex].id = id;
+    callbacks[cbIndex].proc = (int (*)()) proc;
+    callbacks[cbIndex].context = this;
+    cbIndex++;
+}
+
+void ClientImpl::setError(const string& context, int code, const string& text, 
const string& text2)
+{
+    stringstream err;
+    string etext(text.empty() ? sasl_errdetail(conn) : text);
+    err << "Error in " << context << " (" << code << ") " << etext;
+    if (!text2.empty())
+        err << " - " << text2;
+    error = err.str();
+}
+
+void ClientImpl::interact(sasl_interact_t* prompt)
+{
+    string output;
+    char* input;
+
+    if (prompt->id == SASL_CB_PASS) {
+        string ppt(prompt->prompt);
+        ppt += ": ";
+        char* pass = getpass(ppt.c_str());
+        output = string(pass);
+    } else {
+        cout << prompt->prompt;
+        if (prompt->defresult)
+            cout << " [" << prompt->defresult << "]";
+        cout << ": ";
+        cin >> output;
+    }
+    prompt->result = output.c_str();
+    prompt->len = output.length();
+}
+
+int ClientImpl::cbName(void *context, int id, const char **result, unsigned 
*len)
+{
+    ClientImpl* impl = (ClientImpl*) context;
+
+    if (id == SASL_CB_USER || (id == SASL_CB_AUTHNAME && 
impl->authName.empty())) {
+        *result = impl->userName.c_str();
+        //*len    = impl->userName.length();
+    } else if (id == SASL_CB_AUTHNAME) {
+        *result = impl->authName.c_str();
+        //*len    = impl->authName.length();
+    }
+
+    return SASL_OK;
+}
+
+int ClientImpl::cbPassword(sasl_conn_t *conn, void *context, int id, 
sasl_secret_t **psecret)
+{
+    ClientImpl* impl = (ClientImpl*) context;
+    size_t length = impl->password.length();
+
+    if (id == SASL_CB_PASS) {
+        impl->secret->len = length;
+        ::memcpy(impl->secret->data, impl->password.c_str(), length);
+    } else
+        impl->secret->len = 0;
+
+    *psecret = impl->secret;
+    return SASL_OK;
+}
+
+
+//==========================================================
+// WRAPPERS
+//==========================================================
+
+Client::Client() : impl(new ClientImpl()) {}
+Client::~Client() { delete impl; }
+bool Client::setAttr(const string& key, const string& value) { return 
impl->setAttr(key, value); }
+bool Client::setAttr(const string& key, uint32_t value) { return 
impl->setAttr(key, value); }
+bool Client::init() { return impl->init(); }
+bool Client::start(const string& mechList, output_string& chosen, 
output_string& initialResponse) { return impl->start(mechList, chosen, 
initialResponse); }
+bool Client::step(const string& challenge, output_string& response) { return 
impl->step(challenge, response); }
+bool Client::encode(const string& clearText, output_string& cipherText) { 
return impl->encode(clearText, cipherText); }
+bool Client::decode(const string& cipherText, output_string& clearText) { 
return impl->decode(cipherText, clearText); }
+bool Client::getUserId(output_string& userId) { return 
impl->getUserId(userId); }
+void Client::getError(output_string& error) { impl->getError(error); }
+

Added: qpid/trunk/qpid/cpp/bindings/sasl/python/python.i
URL: 
http://svn.apache.org/viewvc/qpid/trunk/qpid/cpp/bindings/sasl/python/python.i?rev=834975&view=auto
==============================================================================
--- qpid/trunk/qpid/cpp/bindings/sasl/python/python.i (added)
+++ qpid/trunk/qpid/cpp/bindings/sasl/python/python.i Wed Nov 11 17:33:33 2009
@@ -0,0 +1,169 @@
+/*
+ * Licensed to the Apache Software Foundation (ASF) under one
+ * or more contributor license agreements.  See the NOTICE file
+ * distributed with this work for additional information
+ * regarding copyright ownership.  The ASF licenses this file
+ * to you under the Apache License, Version 2.0 (the
+ * "License"); you may not use this file except in compliance
+ * with the License.  You may obtain a copy of the License at
+ *
+ *   http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing,
+ * software distributed under the License is distributed on an
+ * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
+ * KIND, either express or implied.  See the License for the
+ * specific language governing permissions and limitations
+ * under the License.
+ */
+
+%module qpidsasl
+
+
+/* unsigned32 Convert from Python --> C */
+%typemap(in) uint32_t {
+    if (PyInt_Check($input)) {
+        $1 = (uint32_t) PyInt_AsUnsignedLongMask($input);
+    } else if (PyLong_Check($input)) {
+        $1 = (uint32_t) PyLong_AsUnsignedLong($input);
+    } else {
+        SWIG_exception_fail(SWIG_ValueError, "unknown integer type");
+    }
+}
+
+/* unsigned32 Convert from C --> Python */
+%typemap(out) uint32_t {
+    $result = PyInt_FromLong((long)$1);
+}
+
+
+/* unsigned16 Convert from Python --> C */
+%typemap(in) uint16_t {
+    if (PyInt_Check($input)) {
+        $1 = (uint16_t) PyInt_AsUnsignedLongMask($input);
+    } else if (PyLong_Check($input)) {
+        $1 = (uint16_t) PyLong_AsUnsignedLong($input);
+    } else {
+        SWIG_exception_fail(SWIG_ValueError, "unknown integer type");
+    }
+}
+
+/* unsigned16 Convert from C --> Python */
+%typemap(out) uint16_t {
+    $result = PyInt_FromLong((long)$1);
+}
+
+
+/* signed32 Convert from Python --> C */
+%typemap(in) int32_t {
+    if (PyInt_Check($input)) {
+        $1 = (int32_t) PyInt_AsLong($input);
+    } else if (PyLong_Check($input)) {
+        $1 = (int32_t) PyLong_AsLong($input);
+    } else {
+        SWIG_exception_fail(SWIG_ValueError, "unknown integer type");
+    }
+}
+
+/* signed32 Convert from C --> Python */
+%typemap(out) int32_t {
+    $result = PyInt_FromLong((long)$1);
+}
+
+
+/* unsigned64 Convert from Python --> C */
+%typemap(in) uint64_t {
+%#ifdef HAVE_LONG_LONG
+    if (PyLong_Check($input)) {
+        $1 = (uint64_t)PyLong_AsUnsignedLongLong($input);
+    } else if (PyInt_Check($input)) {
+        $1 = (uint64_t)PyInt_AsUnsignedLongLongMask($input);
+    } else
+%#endif
+    {
+        SWIG_exception_fail(SWIG_ValueError, "unsupported integer size - 
uint64_t input too large");
+    }
+}
+
+/* unsigned64 Convert from C --> Python */
+%typemap(out) uint64_t {
+%#ifdef HAVE_LONG_LONG
+    $result = PyLong_FromUnsignedLongLong((unsigned PY_LONG_LONG)$1);
+%#else
+    SWIG_exception_fail(SWIG_ValueError, "unsupported integer size - uint64_t 
output too large");
+%#endif
+}
+
+/* signed64 Convert from Python --> C */
+%typemap(in) int64_t {
+%#ifdef HAVE_LONG_LONG
+    if (PyLong_Check($input)) {
+        $1 = (int64_t)PyLong_AsLongLong($input);
+    } else if (PyInt_Check($input)) {
+        $1 = (int64_t)PyInt_AsLong($input);
+    } else
+%#endif
+    {
+        SWIG_exception_fail(SWIG_ValueError, "unsupported integer size - 
int64_t input too large");
+    }
+}
+
+/* signed64 Convert from C --> Python */
+%typemap(out) int64_t {
+%#ifdef HAVE_LONG_LONG
+    $result = PyLong_FromLongLong((PY_LONG_LONG)$1);
+%#else
+    SWIG_exception_fail(SWIG_ValueError, "unsupported integer size - int64_t 
output too large");
+%#endif
+}
+
+
+/* Convert from Python --> C */
+%typemap(in) void * {
+    $1 = (void *)$input;
+}
+
+/* Convert from C --> Python */
+%typemap(out) void * {
+    $result = (PyObject *) $1;
+    Py_INCREF($result);
+}
+
+%typemap (typecheck, precedence=SWIG_TYPECHECK_UINT64) uint64_t {
+    $1 = PyLong_Check($input) ? 1 : 0;
+}
+
+%typemap (typecheck, precedence=SWIG_TYPECHECK_UINT32) uint32_t {
+    $1 = PyInt_Check($input) ? 1 : 0;
+}
+
+/* Handle output arguments of type "output_string" */
+%typemap(in, numinputs=0) saslwrapper::output_string& (std::string temp) {
+   $1 = &temp;
+}
+
+%typemap(argout) saslwrapper::output_string& {
+    // Append output value $1 to $result
+    PyObject *o, *o2, *o3;
+    o = PyString_FromStringAndSize($1->c_str(), $1->length());
+    if ((!$result) || ($result == Py_None)) {
+        $result = o;
+    } else {
+        if (!PyTuple_Check($result)) {
+            PyObject *o2 = $result;
+            $result = PyTuple_New(1);
+            PyTuple_SetItem($result,0,o2);
+        }
+        o3 = PyTuple_New(1);
+        PyTuple_SetItem(o3,0,o);
+        o2 = $result;
+        $result = PySequence_Concat(o2,o3);
+        Py_DECREF(o2);
+        Py_DECREF(o3);
+    }
+}
+
+
+
+%include "../saslwrapper.i"
+

Added: qpid/trunk/qpid/cpp/bindings/sasl/saslwrapper.h
URL: 
http://svn.apache.org/viewvc/qpid/trunk/qpid/cpp/bindings/sasl/saslwrapper.h?rev=834975&view=auto
==============================================================================
--- qpid/trunk/qpid/cpp/bindings/sasl/saslwrapper.h (added)
+++ qpid/trunk/qpid/cpp/bindings/sasl/saslwrapper.h Wed Nov 11 17:33:33 2009
@@ -0,0 +1,151 @@
+#ifndef _BINDINGS_SASLWRAPPER_H_
+#define _BINDINGS_SASLWRAPPER_H_ 1
+
+/*
+ * Licensed to the Apache Software Foundation (ASF) under one
+ * or more contributor license agreements.  See the NOTICE file
+ * distributed with this work for additional information
+ * regarding copyright ownership.  The ASF licenses this file
+ * to you under the Apache License, Version 2.0 (the
+ * "License"); you may not use this file except in compliance
+ * with the License.  You may obtain a copy of the License at
+ *
+ *   http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing,
+ * software distributed under the License is distributed on an
+ * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
+ * KIND, either express or implied.  See the License for the
+ * specific language governing permissions and limitations
+ * under the License.
+*/
+
+#include <stdint.h>
+#include <string>
+
+namespace saslwrapper {
+
+    /**
+     * The following type is used for output arguments (that are strings).  
The fact that it has
+     * a unique name is used in a SWIG typemap to indicate output arguments.  
For scripting languages
+     * such as Python and Ruby (which do not support output arguments), the 
outputs are placed in and
+     * array that is returned by the function.  For example, a function that 
looks like:
+     *
+     *      bool function(const string& input, output_string& out1, 
output_string& out2);
+     *
+     * would be called (in Python) like this:
+     *
+     *      boolResult, out1, out2 = function(input)
+     */
+    typedef std::string output_string;
+    class ClientImpl;
+
+    class Client {
+    public:
+
+        Client();
+        ~Client();
+
+        /**
+         * Set attributes to be used in authenticating the session.  All 
attributes should be set
+         * before init() is called.
+         *
+         * @param key Name of attribute being set
+         * @param value Value of attribute being set
+         * @return true iff success.  If false is returned, call getError() 
for error details.
+         *
+         * Available attribute keys:
+         *
+         *    service      - Name of the service being accessed
+         *    username     - User identity for authentication
+         *    authname     - User identity for authorization (if different 
from username)
+         *    password     - Password associated with username
+         *    host         - Fully qualified domain name of the server host
+         *    maxbufsize   - Maximum receive buffer size for the security layer
+         *    minssf       - Minimum acceptable security strength factor 
(integer)
+         *    maxssf       - Maximum acceptable security strength factor 
(integer)
+         *    externalssf  - Security strength factor supplied by external 
mechanism (i.e. SSL/TLS)
+         *    externaluser - Authentication ID (of client) as established by 
external mechanism
+         */
+        bool setAttr(const std::string& key, const std::string& value);
+        bool setAttr(const std::string& key, uint32_t value);
+
+        /**
+         * Initialize the client object.  This should be called after all of 
the properties have been set.
+         *
+         * @return true iff success.  If false is returned, call getError() 
for error details.
+         */
+        bool init();
+
+        /**
+         * Start the SASL exchange with the server.
+         *
+         * @param mechList List of mechanisms provided by the server
+         * @param chosen The mechanism chosen by the client
+         * @param initialResponse Initial block of data to send to the server
+         *
+         * @return true iff success.  If false is returned, call getError() 
for error details.
+         */
+        bool start(const std::string& mechList, output_string& chosen, 
output_string& initialResponse);
+
+        /**
+         * Step the SASL handshake.
+         *
+         * @param challenge The challenge supplied by the server
+         * @param response (output) The response to be sent back to the server
+         *
+         * @return true iff success.  If false is returned, call getError() 
for error details.
+         */
+        bool step(const std::string& challenge, output_string& response);
+
+        /**
+         * Encode data for secure transmission to the server.
+         *
+         * @param clearText Clear text data to be encrypted
+         * @param cipherText (output) Encrypted data to be transmitted
+         *
+         * @return true iff success.  If false is returned, call getError() 
for error details.
+         */
+        bool encode(const std::string& clearText, output_string& cipherText);
+
+        /**
+         * Decode data received from the server.
+         *
+         * @param cipherText Encrypted data received from the server
+         * @param clearText (output) Decrypted clear text data 
+         *
+         * @return true iff success.  If false is returned, call getError() 
for error details.
+         */
+        bool decode(const std::string& cipherText, output_string& clearText);
+
+        /**
+         * Get the user identity (used for authentication) associated with 
this session.
+         * Note that this is particularly useful for single-sign-on mechanisms 
in which the 
+         * username is not supplied by the application.
+         *
+         * @param userId (output) Authenticated user ID for this session.
+         */
+        bool getUserId(output_string& userId);
+
+        /**
+         * Get error message for last error.
+         * This function will return the last error message then clear the 
error state.
+         * If there was no error or the error state has been cleared, this 
function will output
+         * an empty string.
+         *
+         * @param error Error message string
+         */
+        void getError(output_string& error);
+
+    private:
+        ClientImpl* impl;
+
+        // Declare private copy constructor and assignment operator.  Ensure 
that this
+        // class is non-copyable.
+        Client(const Client&);
+        const Client& operator=(const Client&);
+    };
+
+}
+
+#endif

Added: qpid/trunk/qpid/cpp/bindings/sasl/saslwrapper.i
URL: 
http://svn.apache.org/viewvc/qpid/trunk/qpid/cpp/bindings/sasl/saslwrapper.i?rev=834975&view=auto
==============================================================================
--- qpid/trunk/qpid/cpp/bindings/sasl/saslwrapper.i (added)
+++ qpid/trunk/qpid/cpp/bindings/sasl/saslwrapper.i Wed Nov 11 17:33:33 2009
@@ -0,0 +1,40 @@
+/*
+ * Licensed to the Apache Software Foundation (ASF) under one
+ * or more contributor license agreements.  See the NOTICE file
+ * distributed with this work for additional information
+ * regarding copyright ownership.  The ASF licenses this file
+ * to you under the Apache License, Version 2.0 (the
+ * "License"); you may not use this file except in compliance
+ * with the License.  You may obtain a copy of the License at
+ *
+ *   http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing,
+ * software distributed under the License is distributed on an
+ * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
+ * KIND, either express or implied.  See the License for the
+ * specific language governing permissions and limitations
+ * under the License.
+ */
+
+%{
+#include "saslwrapper.h"
+%}
+
+%include stl.i
+%include <saslwrapper.h>
+
+%inline {
+
+using namespace std;
+using namespace saslwrapper;
+
+namespace saslwrapper {
+
+}
+}
+
+%{
+
+%};
+

Modified: qpid/trunk/qpid/cpp/configure.ac
URL: 
http://svn.apache.org/viewvc/qpid/trunk/qpid/cpp/configure.ac?rev=834975&r1=834974&r2=834975&view=diff
==============================================================================
--- qpid/trunk/qpid/cpp/configure.ac (original)
+++ qpid/trunk/qpid/cpp/configure.ac Wed Nov 11 17:33:33 2009
@@ -517,6 +517,7 @@
   bindings/qmf/ruby/Makefile
   bindings/qmf/python/Makefile
   bindings/qmf/tests/Makefile
+  bindings/sasl/Makefile
   managementgen/Makefile
   etc/Makefile
   src/Makefile

Modified: qpid/trunk/qpid/cpp/src/tests/acl.py
URL: 
http://svn.apache.org/viewvc/qpid/trunk/qpid/cpp/src/tests/acl.py?rev=834975&r1=834974&r2=834975&view=diff
==============================================================================
--- qpid/trunk/qpid/cpp/src/tests/acl.py (original)
+++ qpid/trunk/qpid/cpp/src/tests/acl.py Wed Nov 11 17:33:33 2009
@@ -66,7 +66,7 @@
         Test the deny all mode
         """
         aclf = ACLFile()
-        aclf.write('acl allow gu...@qpid all all\n')
+        aclf.write('acl allow anonymous all all\n')
         aclf.write('acl allow b...@qpid create queue\n')
         aclf.write('acl deny all all')
         aclf.close()        
@@ -330,7 +330,7 @@
         aclf.write('acl allow b...@qpid create queue name=q4\n')               
 
         aclf.write('acl allow b...@qpid delete queue name=q4\n')   
         aclf.write('acl allow b...@qpid create queue name=q5 maxqueuesize=1000 
maxqueuecount=100\n')                   
-        aclf.write('acl allow gu...@qpid all all\n')
+        aclf.write('acl allow anonymous all all\n')
         aclf.write('acl deny all all')
         aclf.close()        
         
@@ -581,7 +581,7 @@
         aclf.write('acl allow b...@qpid unbind exchange name=amq.topic 
queuename=bar routingkey=foo.*\n')
         aclf.write('acl allow b...@qpid access exchange name=myEx queuename=q1 
routingkey=rk1.*\n')
         aclf.write('acl allow b...@qpid delete exchange name=myEx\n')
-        aclf.write('acl allow gu...@qpid all all\n') 
+        aclf.write('acl allow anonymous all all\n') 
         aclf.write('acl deny all all')
         aclf.close()        
         
@@ -740,7 +740,7 @@
         aclf.write('acl allow b...@qpid consume queue name=q1\n')
         aclf.write('acl allow b...@qpid consume queue name=q2\n')
         aclf.write('acl allow b...@qpid create queue\n')                       
         
-        aclf.write('acl allow gu...@qpid all\n')                               
 
+        aclf.write('acl allow anonymous all\n')                                
         aclf.write('acl deny all all')
         aclf.close()        
         
@@ -836,7 +836,7 @@
         aclf.write('acl allow b...@qpid publish exchange name=amq.topic\n')
         aclf.write('acl allow b...@qpid publish exchange name=myEx 
routingkey=rk2\n')
         aclf.write('acl allow b...@qpid create exchange\n')    
-        aclf.write('acl allow gu...@qpid all all \n')    
+        aclf.write('acl allow anonymous all all \n')    
         aclf.write('acl deny all all')
         aclf.close()        
         

Modified: qpid/trunk/qpid/python/commands/qpid-route
URL: 
http://svn.apache.org/viewvc/qpid/trunk/qpid/python/commands/qpid-route?rev=834975&r1=834974&r2=834975&view=diff
==============================================================================
--- qpid/trunk/qpid/python/commands/qpid-route (original)
+++ qpid/trunk/qpid/python/commands/qpid-route Wed Nov 11 17:33:33 2009
@@ -93,12 +93,12 @@
         broker = brokers[0]
         link = self.getLink()
         if link == None:
-            if self.remote.authName == "anonymous":
+            if not self.remote.authName or self.remote.authName == "anonymous":
                 mech = "ANONYMOUS"
             else:
                 mech = "PLAIN"
             res = broker.connect(self.remote.host, self.remote.port, _durable,
-                                 mech, self.remote.authName, 
self.remote.authPass,
+                                 mech, self.remote.authName or "", 
self.remote.authPass or "",
                                  _transport)
             if _verbose:
                 print "Connect method returned:", res.status, res.text

Modified: qpid/trunk/qpid/python/qmf/console.py
URL: 
http://svn.apache.org/viewvc/qpid/trunk/qpid/python/qmf/console.py?rev=834975&r1=834974&r2=834975&view=diff
==============================================================================
--- qpid/trunk/qpid/python/qmf/console.py (original)
+++ qpid/trunk/qpid/python/qmf/console.py Wed Nov 11 17:33:33 2009
@@ -38,6 +38,9 @@
 from time            import time, strftime, gmtime
 from cStringIO       import StringIO
 
+#import qpid.log
+#qpid.log.enable(name="qpid.io.cmd", level=qpid.log.DEBUG)
+
 class Console:
   """ To access the asynchronous operations, a class must be derived from
   Console with overrides of any combination of the available methods. """
@@ -100,9 +103,12 @@
         self.port = 5671
       else:
         self.port = 5672
-    self.authName = str(self.user or "guest")
-    self.authPass = str(self.password or "guest")
-    self.authMech = "PLAIN"
+    self.authName = None
+    self.authPass = None
+    if self.user:
+      self.authName = str(self.user)
+    if self.password:
+      self.authPass = str(self.password)
 
   def name(self):
     return self.host + ":" + str(self.port)
@@ -469,10 +475,10 @@
   def __repr__(self):
     return "QMF Console Session Manager (brokers: %d)" % len(self.brokers)
 
-  def addBroker(self, target="localhost", timeout=None):
+  def addBroker(self, target="localhost", timeout=None, mechanisms=None):
     """ Connect to a Qpid broker.  Returns an object of type Broker. """
     url = BrokerURL(target)
-    broker = Broker(self, url.host, url.port, url.authMech, url.authName, 
url.authPass,
+    broker = Broker(self, url.host, url.port, mechanisms, url.authName, 
url.authPass,
                     ssl = url.scheme == URL.AMQPS, connTimeout=timeout)
 
     self.brokers.append(broker)
@@ -1557,10 +1563,11 @@
   SYNC_TIME = 60
   nextSeq = 1
 
-  def __init__(self, session, host, port, authMech, authUser, authPass, 
ssl=False, connTimeout=None):
+  def __init__(self, session, host, port, authMechs, authUser, authPass, 
ssl=False, connTimeout=None):
     self.session  = session
     self.host = host
     self.port = port
+    self.mechanisms = authMechs
     self.ssl = ssl
     self.connTimeout = connTimeout
     self.authUser = authUser
@@ -1654,7 +1661,8 @@
         connSock = ssl(sock)
       else:
         connSock = sock
-      self.conn = Connection(connSock, username=self.authUser, 
password=self.authPass)
+      self.conn = Connection(connSock, username=self.authUser, 
password=self.authPass,
+                             mechanism = self.mechanisms, host=self.host, 
service="qpidd")
       def aborted():
         raise Timeout("Waiting for connection to be established with broker")
       oldAborted = self.conn.aborted

Modified: qpid/trunk/qpid/python/qpid/connection.py
URL: 
http://svn.apache.org/viewvc/qpid/trunk/qpid/python/qpid/connection.py?rev=834975&r1=834974&r2=834975&view=diff
==============================================================================
--- qpid/trunk/qpid/python/qpid/connection.py (original)
+++ qpid/trunk/qpid/python/qpid/connection.py Wed Nov 11 17:33:33 2009
@@ -65,6 +65,7 @@
     self.thread.setDaemon(True)
 
     self.channel_max = 65535
+    self.user_id = None
 
     self.op_enc = OpEncoder()
     self.seg_enc = SegmentEncoder()
@@ -156,6 +157,8 @@
     while not self.closed:
       try:
         data = self.sock.recv(64*1024)
+        if self.security_layer_rx and data:
+          status, data = self.security_layer_rx.decode(data)
         if not data:
           self.detach_all()
           break

Modified: qpid/trunk/qpid/python/qpid/delegates.py
URL: 
http://svn.apache.org/viewvc/qpid/trunk/qpid/python/qpid/delegates.py?rev=834975&r1=834974&r2=834975&view=diff
==============================================================================
--- qpid/trunk/qpid/python/qpid/delegates.py (original)
+++ qpid/trunk/qpid/python/qpid/delegates.py Wed Nov 11 17:33:33 2009
@@ -20,11 +20,20 @@
 import os, connection, session
 from util import notify
 from datatypes import RangedSet
-from exceptions import VersionError
+from exceptions import VersionError, Closed
 from logging import getLogger
 from ops import Control
 import sys
 
+_have_sasl = None
+try:
+  import qpidsasl
+  _have_sasl = True
+except:
+  pass
+finally:
+  pass
+
 log = getLogger("qpid.io.ctl")
 
 class Delegate:
@@ -152,13 +161,36 @@
                 "qpid.client_pid": os.getpid(),
                 "qpid.client_ppid": ppid}
 
-  def __init__(self, connection, username="guest", password="guest",
-               mechanism="PLAIN", heartbeat=None):
+  def __init__(self, connection, username=None, password=None,
+               mechanism=None, heartbeat=None, **kwargs):
     Delegate.__init__(self, connection)
-    self.username = username
-    self.password = password
-    self.mechanism = mechanism
+
+    ##
+    ## self.acceptableMechanisms is the list of SASL mechanisms that the 
client is willing to
+    ## use.  If it's None, then any mechanism is acceptable.
+    ##
+    self.acceptableMechanisms = None
+    if mechanism:
+      self.acceptableMechanisms = mechanism.split(" ")
     self.heartbeat = heartbeat
+    self.username  = username
+    self.password  = password
+
+    if _have_sasl:
+      self.sasl = qpidsasl.Client()
+      if username and len(username) > 0:
+        self.sasl.setAttr("username", str(username))
+      if password and len(password) > 0:
+        self.sasl.setAttr("password", str(password))
+      if "service" in kwargs:
+        self.sasl.setAttr("service", str(kwargs["service"]))
+      if "host" in kwargs:
+        self.sasl.setAttr("host", str(kwargs["host"]))
+      if "min_ssf" in kwargs:
+        self.sasl.setAttr("minssf", kwargs["min_ssf"])
+      if "max_ssf" in kwargs:
+        self.sasl.setAttr("maxssf", kwargs["max_ssf"])
+      self.sasl.init()
 
   def start(self):
     # XXX
@@ -171,14 +203,44 @@
                          (cli_major, cli_minor, major, minor))
 
   def connection_start(self, ch, start):
-    r = "\0%s\0%s" % (self.username, self.password)
-    ch.connection_start_ok(client_properties=Client.PROPERTIES, 
mechanism=self.mechanism, response=r)
+    mech_list = ""
+    for mech in start.mechanisms:
+      if (not self.acceptableMechanisms) or mech in self.acceptableMechanisms:
+        mech_list += str(mech) + " "
+    mech = None
+    initial = None
+    if _have_sasl:
+      status, mech, initial = self.sasl.start(mech_list)
+      if status == False:
+        raise Closed("SASL error: %s" % self.sasl.getError())
+    else:
+      if self.username and self.password and ("PLAIN" in mech_list):
+        mech = "PLAIN"
+        initial = "\0%s\0%s" % (self.username, self.password)
+      else:
+        mech = "ANONYMOUS"
+        if not mech in mech_list:
+          raise Closed("No acceptable SASL authentication mechanism available")
+    ch.connection_start_ok(client_properties=Client.PROPERTIES, 
mechanism=mech, response=initial)
+
+  def connection_secure(self, ch, secure):
+    resp = None
+    if _have_sasl:
+      status, resp = self.sasl.step(secure.challenge)
+      if status == False:
+        raise Closed("SASL error: %s" % self.sasl.getError())
+    ch.connection_secure_ok(response=resp)
 
   def connection_tune(self, ch, tune):
     ch.connection_tune_ok(heartbeat=self.heartbeat)
     ch.connection_open()
+    if _have_sasl:
+      self.connection.user_id = self.sasl.getUserId()
+      self.connection.security_layer_tx = self.sasl
 
   def connection_open_ok(self, ch, open_ok):
+    if _have_sasl:
+      self.connection.security_layer_rx = self.sasl
     self.connection.opened = True
     notify(self.connection.condition)
 

Modified: qpid/trunk/qpid/python/qpid/framer.py
URL: 
http://svn.apache.org/viewvc/qpid/trunk/qpid/python/qpid/framer.py?rev=834975&r1=834974&r2=834975&view=diff
==============================================================================
--- qpid/trunk/qpid/python/qpid/framer.py (original)
+++ qpid/trunk/qpid/python/qpid/framer.py Wed Nov 11 17:33:33 2009
@@ -35,19 +35,29 @@
   def __init__(self, sock):
     self.sock = sock
     self.sock_lock = RLock()
-    self._buf = ""
+    self.tx_buf = ""
+    self.rx_buf = ""
+    self.security_layer_tx = None
+    self.security_layer_rx = None
+    self.maxbufsize = 65535
 
   def aborted(self):
     return False
 
   def write(self, buf):
-    self._buf += buf
+    self.tx_buf += buf
 
   def flush(self):
     self.sock_lock.acquire()
     try:
-      self._write(self._buf)
-      self._buf = ""
+      if self.security_layer_tx:
+        status, cipher_buf = self.security_layer_tx.encode(self.tx_buf)
+        if status == False:
+          raise Closed(self.security_layer_tx.getError())
+        self._write(cipher_buf)
+      else:
+        self._write(self.tx_buf)
+      self.tx_buf = ""
       frm.debug("FLUSHED")
     finally:
       self.sock_lock.release()
@@ -64,25 +74,42 @@
       raw.debug("SENT %r", buf[:n])
       buf = buf[n:]
 
+  ##
+  ## Implementation Note:
+  ##
+  ##    This function was modified to use the SASL security layer for content
+  ##    decryption.  As such, the socket read should read in "self.maxbufsize"
+  ##    instead of "n" (the requested number of octets).  However, since this
+  ##    is one of two places in the code where the socket is read, the read
+  ##    size had to be left at "n".  This is because this function is
+  ##    apparently only used to read the first 8 octets from a TCP socket.  If
+  ##    we read beyond "n" octets, the remaing octets won't be processed and
+  ##    the connection handshake will fail.
+  ##
   def read(self, n):
-    data = ""
-    while len(data) < n:
+    while len(self.rx_buf) < n:
       try:
-        s = self.sock.recv(n - len(data))
+        s = self.sock.recv(n) # NOTE: instead of "n", arg should be 
"self.maxbufsize"
+        if self.security_layer_rx:
+          status, s = self.security_layer_rx.decode(s)
+          if status == False:
+            raise Closed(self.security_layer_tx.getError())
       except socket.timeout:
         if self.aborted():
           raise Closed()
         else:
           continue
       except socket.error, e:
-        if data != "":
+        if self.rx_buf != "":
           raise e
         else:
           raise Closed()
       if len(s) == 0:
         raise Closed()
-      data += s
+      self.rx_buf += s
       raw.debug("RECV %r", s)
+    data = self.rx_buf[0:n]
+    self.rx_buf = self.rx_buf[n:]
     return data
 
   def read_header(self):



---------------------------------------------------------------------
Apache Qpid - AMQP Messaging Implementation
Project:      http://qpid.apache.org
Use/Interact: mailto:commits-subscr...@qpid.apache.org

Reply via email to