From eraviart at entrouvert.com Sun Dec 19 09:54:23 2004 From: eraviart at entrouvert.com (Emmanuel Raviart) Date: Sun, 19 Dec 2004 09:54:23 +0100 Subject: [pyOpenSSL] Missing API for client authentication renegotiation Message-ID: <41C541BF.1080602@entrouvert.com> Hello, I'm using PyOpenSSL to develop a free software implementation of Liberty Alliance single sign-on protocol. http://lasso.entrouvert.org/souk It works very well, but to improve client authentication, I need to renegotiate client verify mode after the SSL connection is established. So, I would like to transpose the following C code into Python: SSL_set_verify(ssl,SSL_VERIFY_PEER | SSL_VERIFY_FAIL_IF_NO_PEER_CERT,0); /* Stop the client from just resuming the un-authenticated session */ SSL_set_session_id_context(ssl, (void *)&s_server_auth_session_id_context, sizeof(s_server_auth_session_id_context)); SSL_renegotiate(ssl); SSL_do_handshake(ssl); ssl->state=SSL_ST_ACCEPT; SSL_do_handshake(ssl); [This code fragment is taken from wserver2.c sample code by Eric Rescoria ] But I didn't find in PyOpenSSL a way to: - call SSL_set_verify - change ssl->state without using SSL_set_accept_state Is this a missing feature of PyOpenSSL? Is there another way to implement client authentication renegotiation? Regards, Emmanuel Raviart -- Lasso (Liberty Alliance Single Sign-On) -- http://lasso.entrouvert.org Entr'ouvert -- www.entrouvert.com From fpeters at debian.org Sun Dec 19 13:21:22 2004 From: fpeters at debian.org (Frederic Peters) Date: Sun, 19 Dec 2004 13:21:22 +0100 Subject: [pyOpenSSL] Re: [PATCH] Missing API for client authentication renegotiation In-Reply-To: <41C541BF.1080602@entrouvert.com> References: <41C541BF.1080602@entrouvert.com> Message-ID: <20041219122121.GA8792@entrouvert.com> Emmanuel Raviart wrote: > So, I would like to transpose the following C code into Python: > SSL_set_verify(ssl,SSL_VERIFY_PEER | > SSL_VERIFY_FAIL_IF_NO_PEER_CERT,0); > > /* Stop the client from just resuming the > un-authenticated session */ > SSL_set_session_id_context(ssl, > (void *)&s_server_auth_session_id_context, > sizeof(s_server_auth_session_id_context)); > > SSL_renegotiate(ssl); > SSL_do_handshake(ssl); > ssl->state=SSL_ST_ACCEPT; > SSL_do_handshake(ssl); I wrote a patch for this; it adds bindings for SSL_set_verify, SSL_set_verify_depth, SSL_get_verify_mode, SSL_get_verify_depth and SSL_set_session_id_context. It also adds to new methods, set_state and get_state to SSL objects and the SSL state constants (SSL_ST_*). It has *not* been extensively tested and I'm not sure at all wrt the global_verify_callback function I copied/pasted from context.c What are the odds for this to be included in pyOpenSSL ? Regards, Frederic -------------- next part -------------- --- pyopenssl-0.6.orig/src/ssl/ssl.c +++ pyopenssl-0.6/src/ssl/ssl.c @@ -161,6 +161,15 @@ PyModule_AddIntConstant(module, "OP_NO_SSLv3", SSL_OP_NO_SSLv3); PyModule_AddIntConstant(module, "OP_NO_TLSv1", SSL_OP_NO_TLSv1); + /* SSL state constants */ + PyModule_AddIntConstant(module, "ST_CONNECT", SSL_ST_CONNECT); + PyModule_AddIntConstant(module, "ST_ACCEPT", SSL_ST_ACCEPT); + PyModule_AddIntConstant(module, "ST_MASK", SSL_ST_MASK); + PyModule_AddIntConstant(module, "ST_INIT", SSL_ST_INIT); + PyModule_AddIntConstant(module, "ST_BEFORE", SSL_ST_BEFORE); + PyModule_AddIntConstant(module, "ST_OK", SSL_ST_OK); + PyModule_AddIntConstant(module, "ST_RENEGOTIATE", SSL_ST_RENEGOTIATE); + /* More SSL option constants */ PyModule_AddIntConstant(module, "OP_MICROSOFT_SESS_ID_BUG", SSL_OP_MICROSOFT_SESS_ID_BUG); PyModule_AddIntConstant(module, "OP_NETSCAPE_CHALLENGE_BUG", SSL_OP_NETSCAPE_CHALLENGE_BUG); --- pyopenssl-0.6.orig/src/ssl/connection.c +++ pyopenssl-0.6/src/ssl/connection.c @@ -199,6 +199,64 @@ } /* + * Globally defined verify callback + * + * Arguments: ok - True everything is OK "so far", false otherwise + * x509_ctx - Contains the certificate being checked, the current + * error number and depth, and the Connection we're + * dealing with + * Returns: True if everything is okay, false otherwise + */ +static int +global_verify_callback(int ok, X509_STORE_CTX *x509_ctx) +{ + PyObject *argv, *ret; + SSL *ssl; + ssl_ConnectionObj *conn; + crypto_X509Obj *cert; + int errnum, errdepth, c_ret; + + cert = crypto_X509_New(X509_STORE_CTX_get_current_cert(x509_ctx), 0); + errnum = X509_STORE_CTX_get_error(x509_ctx); + errdepth = X509_STORE_CTX_get_error_depth(x509_ctx); + ssl = (SSL *)X509_STORE_CTX_get_app_data(x509_ctx); + conn = (ssl_ConnectionObj *)SSL_get_app_data(ssl); + + argv = Py_BuildValue("(OOiii)", (PyObject *)conn, (PyObject *)cert, + errnum, errdepth, ok); + Py_DECREF(cert); + if (conn->tstate != NULL) + { + /* We need to get back our thread state before calling the callback */ + MY_END_ALLOW_THREADS(conn->tstate); + ret = PyEval_CallObject(conn->context->verify_callback, argv); + MY_BEGIN_ALLOW_THREADS(conn->tstate); + } + else + { + ret = PyEval_CallObject(conn->context->verify_callback, argv); + } + Py_DECREF(argv); + + if (ret == NULL) + return 0; + + if (PyObject_IsTrue(ret)) + { + X509_STORE_CTX_set_error(x509_ctx, X509_V_OK); + c_ret = 1; + } + else + c_ret = 0; + + Py_DECREF(ret); + + return c_ret; +} + + + +/* * Here be member methods of the Connection "class" */ @@ -853,6 +911,169 @@ return PyInt_FromLong((long)SSL_want_write(self->ssl)); } +static char ssl_Connection_set_session_id_doc[] = "\n\ +Set the session identifier, this is needed if you want to do session\n\ +resumption (which, ironically, isn't implemented yet)\n\ +\n\ +Arguments: self - The Connection object\n\ + args - The Python argument tuple, should be:\n\ + buf - A Python object that can be safely converted to a string\n\ +Returns: None\n\ +"; +static PyObject * +ssl_Connection_set_session_id(ssl_ConnectionObj *self, PyObject *args) +{ + char *buf; + int len; + + if (!PyArg_ParseTuple(args, "s#:set_session_id", &buf, &len)) + return NULL; + + if (!SSL_set_session_id_context(self->ssl, buf, len)) + { + exception_from_error_queue(); + return NULL; + } + else + { + Py_INCREF(Py_None); + return Py_None; + } +} + +static char ssl_Connection_set_verify_doc[] = "\n\ +Set the verify mode and verify callback\n\ +\n\ +Arguments: self - The Connection object\n\ + args - The Python argument tuple, should be:\n\ + mode - The verify mode, this is either SSL_VERIFY_NONE or\n\ + SSL_VERIFY_PEER combined with possible other flags\n\ + callback - The Python callback to use\n\ +Returns: None\n\ +"; +static PyObject * +ssl_Connection_set_verify(ssl_ConnectionObj *self, PyObject *args) +{ + int mode; + PyObject *callback = NULL; + + if (!PyArg_ParseTuple(args, "iO:set_verify", &mode, &callback)) + return NULL; + + if (!PyCallable_Check(callback)) + { + PyErr_SetString(PyExc_TypeError, "expected PyCallable"); + return NULL; + } + + Py_DECREF(self->verify_callback); + Py_INCREF(callback); + self->verify_callback = callback; + SSL_set_verify(self->ssl, mode, global_verify_callback); + + Py_INCREF(Py_None); + return Py_None; +} + +static char ssl_Connection_set_verify_depth_doc[] = "\n\ +Set the verify depth\n\ +\n\ +Arguments: self - The Connection object\n\ + args - The Python argument tuple, should be:\n\ + depth - An integer specifying the verify depth\n\ +Returns: None\n\ +"; +static PyObject * +ssl_Connection_set_verify_depth(ssl_ConnectionObj *self, PyObject *args) +{ + int depth; + + if (!PyArg_ParseTuple(args, "i:set_verify_depth", &depth)) + return NULL; + + SSL_set_verify_depth(self->ssl, depth); + Py_INCREF(Py_None); + return Py_None; +} + +static char ssl_Connection_get_verify_mode_doc[] = "\n\ +Get the verify mode\n\ +\n\ +Arguments: self - The Connection object\n\ + args - The Python argument tuple, should be empty\n\ +Returns: The verify mode\n\ +"; +static PyObject * +ssl_Connection_get_verify_mode(ssl_ConnectionObj *self, PyObject *args) +{ + int mode; + + if (!PyArg_ParseTuple(args, ":get_verify_mode")) + return NULL; + + mode = SSL_get_verify_mode(self->ssl); + return PyInt_FromLong((long)mode); +} + +static char ssl_Connection_get_verify_depth_doc[] = "\n\ +Get the verify depth\n\ +\n\ +Arguments: self - The Connection object\n\ + args - The Python argument tuple, should be empty\n\ +Returns: The verify depth\n\ +"; +static PyObject * +ssl_Connection_get_verify_depth(ssl_ConnectionObj *self, PyObject *args) +{ + int depth; + + if (!PyArg_ParseTuple(args, ":get_verify_depth")) + return NULL; + + depth = SSL_get_verify_depth(self->ssl); + return PyInt_FromLong((long)depth); +} + + +static char ssl_Connection_get_state_doc[] = "\n\ +Get application data\n\ +\n\ +Arguments: self - The Connection object\n\ + args - The Python argument tuple, should be empty\n\ +Returns: The SSL state\n\ +"; +static PyObject * +ssl_Connection_get_state(ssl_ConnectionObj *self, PyObject *args) +{ + if (!PyArg_ParseTuple(args, ":get_state")) + return NULL; + + return PyInt_FromLong(self->ssl->state); +} + +static char ssl_Connection_set_state_doc[] = "\n\ +Set application data\n\ +\n\ +Arguments: self - The Connection object\n\ + args - The Python argument tuple, should be\n\ + state - The SSL state\n\ +Returns: None\n\ +"; +static PyObject * +ssl_Connection_set_state(ssl_ConnectionObj *self, PyObject *args) +{ + int state; + + if (!PyArg_ParseTuple(args, "i:set_state", &state)) + return NULL; + + self->ssl->state = state; + + Py_INCREF(Py_None); + return Py_None; +} + + /* * Member methods in the Connection object * ADD_METHOD(name) expands to a correct PyMethodDef declaration @@ -895,12 +1116,18 @@ ADD_METHOD(want_write), ADD_METHOD(set_accept_state), ADD_METHOD(set_connect_state), + ADD_METHOD(set_session_id), + ADD_METHOD(set_verify), + ADD_METHOD(set_verify_depth), + ADD_METHOD(get_verify_mode), + ADD_METHOD(get_verify_depth), + ADD_METHOD(get_state), + ADD_METHOD(set_state), { NULL, NULL } }; #undef ADD_ALIAS #undef ADD_METHOD - /* * Constructor for Connection objects * @@ -928,6 +1155,8 @@ Py_INCREF(Py_None); self->app_data = Py_None; + Py_INCREF(Py_None); + self->verify_callback = Py_None; self->tstate = NULL; --- pyopenssl-0.6.orig/src/ssl/connection.h +++ pyopenssl-0.6/src/ssl/connection.h @@ -43,6 +43,7 @@ ssl_ContextObj *context; PyObject *socket; PyThreadState *tstate; + PyObject *verify_callback; PyObject *app_data; } ssl_ConnectionObj; From niklas at registrar.no Mon Dec 20 15:05:43 2004 From: niklas at registrar.no (Niklas Saers) Date: Mon, 20 Dec 2004 15:05:43 +0100 Subject: [pyOpenSSL] setting the key password Message-ID: <41C6DC37.7090706@registrar.no> Hi everyone, I'm using SecureXMLRPCServer to do my XMLRPC communication via SSL. For this I have a server.pkey and a server.cert, and when running the application I get: "Enter PEM pass phrase:" I want this to be a daemon that runs on a server and starts thus I would like to set the password without keyboard interaction. I've been unsuccessful at piping the password in, and I was wondering if I could set a variable or if there are other options available to me? Sincerely yours Niklas Saers From jos at xos.nl Mon Dec 20 16:23:11 2004 From: jos at xos.nl (Jos Vos) Date: Mon, 20 Dec 2004 16:23:11 +0100 Subject: [pyOpenSSL] setting the key password In-Reply-To: <41C6DC37.7090706@registrar.no>; from niklas@registrar.no on Mon, Dec 20, 2004 at 03:05:43PM +0100 References: <41C6DC37.7090706@registrar.no> Message-ID: <20041220162311.A22228@xos037.xos.nl> On Mon, Dec 20, 2004 at 03:05:43PM +0100, Niklas Saers wrote: > I'm using SecureXMLRPCServer to do my XMLRPC communication via SSL. For > this I have a server.pkey and a server.cert, and when running the > application I get: "Enter PEM pass phrase:" I want this to be a daemon > that runs on a server and starts thus I would like to set the password > without keyboard interaction. I've been unsuccessful at piping the > password in, and I was wondering if I could set a variable or if there > are other options available to me? You have to change the private key part for that, as follows: openssl rsa -in server.pkey -out server2.pkey And then use server2.pkey i.s.o. server.pkey from now on. -- -- Jos Vos -- X/OS Experts in Open Systems BV | Phone: +31 20 6938364 -- Amsterdam, The Netherlands | Fax: +31 20 6948204