From mboxrd@z Thu Jan 1 00:00:00 1970 From: kupcevic@sourceware.org Date: 8 Dec 2006 18:27:36 -0000 Subject: [Cluster-devel] conga ./conga.spec.in.in luci/Makefile luci/si ... Message-ID: <20061208182736.29493.qmail@sourceware.org> List-Id: To: cluster-devel.redhat.com MIME-Version: 1.0 Content-Type: text/plain; charset="us-ascii" Content-Transfer-Encoding: 7bit CVSROOT: /cvs/cluster Module name: conga Branch: RHEL5 Changes by: kupcevic at sourceware.org 2006-12-08 18:27:32 Modified files: . : conga.spec.in.in luci : Makefile luci/site : Makefile luci/site/luci/Extensions: HelperFunctions.py StorageReport.py ricci_communicator.py storage_adapters.py luci/storage : form-macros ricci/ricci : SSLInstance.cpp Added files: luci/conga_ssl : Makefile SSLClient.cpp SSLClient.h conga_ssl_lib.cpp setup.py luci/site/luci/Extensions: conga_ssl.py Log message: Improved bz201394: luci doesn't verify ricci's SSL cert against trusted list (part 1 - backend) Patches: http://sourceware.org/cgi-bin/cvsweb.cgi/conga/conga.spec.in.in.diff?cvsroot=cluster&only_with_tag=RHEL5&r1=1.45.2.8&r2=1.45.2.9 http://sourceware.org/cgi-bin/cvsweb.cgi/conga/luci/Makefile.diff?cvsroot=cluster&only_with_tag=RHEL5&r1=1.20.2.1&r2=1.20.2.2 http://sourceware.org/cgi-bin/cvsweb.cgi/conga/luci/conga_ssl/Makefile.diff?cvsroot=cluster&only_with_tag=RHEL5&r1=NONE&r2=1.1.2.1 http://sourceware.org/cgi-bin/cvsweb.cgi/conga/luci/conga_ssl/SSLClient.cpp.diff?cvsroot=cluster&only_with_tag=RHEL5&r1=NONE&r2=1.1.2.1 http://sourceware.org/cgi-bin/cvsweb.cgi/conga/luci/conga_ssl/SSLClient.h.diff?cvsroot=cluster&only_with_tag=RHEL5&r1=NONE&r2=1.1.2.1 http://sourceware.org/cgi-bin/cvsweb.cgi/conga/luci/conga_ssl/conga_ssl_lib.cpp.diff?cvsroot=cluster&only_with_tag=RHEL5&r1=NONE&r2=1.1.2.1 http://sourceware.org/cgi-bin/cvsweb.cgi/conga/luci/conga_ssl/setup.py.diff?cvsroot=cluster&only_with_tag=RHEL5&r1=NONE&r2=1.1.2.1 http://sourceware.org/cgi-bin/cvsweb.cgi/conga/luci/site/Makefile.diff?cvsroot=cluster&only_with_tag=RHEL5&r1=1.11&r2=1.11.2.1 http://sourceware.org/cgi-bin/cvsweb.cgi/conga/luci/site/luci/Extensions/conga_ssl.py.diff?cvsroot=cluster&only_with_tag=RHEL5&r1=NONE&r2=1.1.2.1 http://sourceware.org/cgi-bin/cvsweb.cgi/conga/luci/site/luci/Extensions/HelperFunctions.py.diff?cvsroot=cluster&only_with_tag=RHEL5&r1=1.4.2.1&r2=1.4.2.2 http://sourceware.org/cgi-bin/cvsweb.cgi/conga/luci/site/luci/Extensions/StorageReport.py.diff?cvsroot=cluster&only_with_tag=RHEL5&r1=1.20.2.1&r2=1.20.2.2 http://sourceware.org/cgi-bin/cvsweb.cgi/conga/luci/site/luci/Extensions/ricci_communicator.py.diff?cvsroot=cluster&only_with_tag=RHEL5&r1=1.9.2.6&r2=1.9.2.7 http://sourceware.org/cgi-bin/cvsweb.cgi/conga/luci/site/luci/Extensions/storage_adapters.py.diff?cvsroot=cluster&only_with_tag=RHEL5&r1=1.7.2.1&r2=1.7.2.2 http://sourceware.org/cgi-bin/cvsweb.cgi/conga/luci/storage/form-macros.diff?cvsroot=cluster&only_with_tag=RHEL5&r1=1.17.2.3&r2=1.17.2.4 http://sourceware.org/cgi-bin/cvsweb.cgi/conga/ricci/ricci/SSLInstance.cpp.diff?cvsroot=cluster&only_with_tag=RHEL5&r1=1.5.2.1&r2=1.5.2.2 --- conga/conga.spec.in.in 2006/11/29 22:33:50 1.45.2.8 +++ conga/conga.spec.in.in 2006/12/08 18:27:31 1.45.2.9 @@ -126,10 +126,11 @@ %{_sbindir}/luci_admin %{_docdir}/luci-%{version}/ %defattr(-,luci,luci) + %{_localstatedir}/lib/luci + %{_libdir}/luci/ssl %if "%{include_zope_and_plone}" == "yes" - %{_libdir}/luci/ + %{_libdir}/luci/zope %endif - %{_localstatedir}/lib/luci %pre -n luci if ! /bin/grep luci\:x /etc/group 2>&1 >/dev/null; then @@ -283,14 +284,14 @@ %changelog -* day month date 2006 Stanko Kupcevic 0.8-26 +* Fri Dec 08 2006 Stanko Kupcevic 0.8-26 XXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXX XXXXXXXXXXXXXXXXXXX UPDATE NOT RELEASED YET XXXXXXXXXXXXXXXXXXX XXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXX - luci storage: fix bytes->TB conversion - +- Improved bz201394: luci doesn't verify ricci's SSL cert against trusted list (part 1 - backend) * Thu Nov 16 2006 Stanko Kupcevic 0.8-25 --- conga/luci/Makefile 2006/11/16 19:34:52 1.20.2.1 +++ conga/luci/Makefile 2006/12/08 18:27:32 1.20.2.2 @@ -18,6 +18,7 @@ luci: make -C site + make -C conga_ssl make -C utils make -C init.d make -C sysconfig @@ -46,6 +47,7 @@ install -d -m 700 ${DESTDIR}/var/lib/luci make -C site install + make -C conga_ssl install make -C utils install make -C init.d install make -C sysconfig install /cvs/cluster/conga/luci/conga_ssl/Makefile,v --> standard output revision 1.1.2.1 --- conga/luci/conga_ssl/Makefile +++ - 2006-12-08 18:27:33.432667000 +0000 @@ -0,0 +1,21 @@ + +include ../../make/version.in +include ../make/defines.mk + + +.PHONY: build + +build: + python setup.py build + +clean: + rm -rf build + rm -rf ricci + +install: + install -d ${libdir}/luci + install -d ${libdir}/luci/ssl + install -m 644 build/lib*/conga_ssl_lib.so ${libdir}/luci/ssl + +rebuild: clean build + /cvs/cluster/conga/luci/conga_ssl/SSLClient.cpp,v --> standard output revision 1.1.2.1 --- conga/luci/conga_ssl/SSLClient.cpp +++ - 2006-12-08 18:27:33.797469000 +0000 @@ -0,0 +1,552 @@ +/* + Copyright Red Hat, Inc. 2005 + + This program is free software; you can redistribute it and/or modify it + under the terms of the GNU General Public License as published by the + Free Software Foundation; either version 2, or (at your option) any + later version. + + This program is distributed in the hope that it will be useful, but + WITHOUT ANY WARRANTY; without even the implied warranty of + MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU + General Public License for more details. + + You should have received a copy of the GNU General Public License + along with this program; see the file COPYING. If not, write to the + Free Software Foundation, Inc., 675 Mass Ave, Cambridge, + MA 02139, USA. +*/ +/* + * Author: Stanko Kupcevic + */ + + +#include "SSLClient.h" +#include "Mutex.h" +#include "Time.h" +#include "Random.h" +#include "utils.h" +#include "File.h" + +#include +#include +#include +#include +#include + +#include +#include +#include + + + +#include + + + +using namespace std; + + + +static Mutex global_lock; +static bool ssl_inited = false; +static SSL_CTX* ctx = 0; +static vector > ssl_locks; + +class file_cert +{ +public: + file_cert(const String& file, const String& cert) : + file(file), + cert(cert) {} + + String file; + String cert; +}; +static list trusted_certs; + + + +static int +verify_cert_callback(int preverify_ok, X509_STORE_CTX *ctx) +{ + return 1; +} +static void +load_peer_certs() +{ + MutexLocker l(global_lock); + + // load trusted CAs + if (!SSL_CTX_load_verify_locations(ctx, + _trust_CAs, + NULL)) + cout << "failed to load trusted CAs" << endl; + + STACK_OF(X509_NAME) *cert_names = + SSL_load_client_CA_file(_trust_CAs); + if (cert_names) + SSL_CTX_set_client_CA_list(ctx, cert_names); + else + cout << "failed to load trusted CAs" << endl; + + // load saved certs + + set files; + String dir_path(_certs_store_dir); + DIR* d = opendir(dir_path.c_str()); + if (d == NULL) + throw String("unable to open directory ") + dir_path; + try { + while (true) { + struct dirent* ent = readdir(d); + if (ent == NULL) { + closedir(d); + break; + } + String kid_path = ent->d_name; + if (kid_path == "." || kid_path == "..") + continue; + kid_path = dir_path + "/" + kid_path; + struct stat st; + if (stat(kid_path.c_str(), &st)) + continue; + if (S_ISREG(st.st_mode)) + files.insert(kid_path); + } + } catch ( ... ) { + closedir(d); + throw; + } + + trusted_certs.clear(); + + for (set::const_iterator iter = files.begin(); + iter != files.end(); + iter++) { + try { + String cert(File::open(*iter).read()); + if (cert.size() && cert.size() < 10 * 1024) + trusted_certs.push_back(file_cert(*iter, cert)); + } catch ( ... ) {} + } +} +static void +ssl_mutex_callback(int mode, + int n, + const char *file, + int line) +{ + if (mode & CRYPTO_LOCK) + ssl_locks[n]->lock(); + else + ssl_locks[n]->unlock(); +} +static pthread_t +ssl_id_callback(void) +{ + return pthread_self(); +} + + + + +// ##### class SSLClient ##### + + +SSLClient::SSLClient(ClientSocket sock) : + _sock(sock), + _connected(false) +{ + { + MutexLocker l(global_lock); + if (!ssl_inited) { + // init library + + SSL_library_init(); + // TODO: random number generator, + // not on systems with /dev/urandom (eg. Linux) + + // thread support + ssl_locks.clear(); + for (int i=0; i(new Mutex())); + CRYPTO_set_locking_callback(ssl_mutex_callback); + CRYPTO_set_id_callback(ssl_id_callback); + + // create context + if (!ctx) + ctx = SSL_CTX_new(SSLv23_client_method()); + if (!ctx) + throw String("SSL context creation failed"); + // set verify_callback() function + SSL_CTX_set_verify(ctx, + SSL_VERIFY_PEER, + verify_cert_callback); + // set mode + SSL_CTX_set_mode(ctx, SSL_MODE_ENABLE_PARTIAL_WRITE); + SSL_CTX_set_mode(ctx, SSL_MODE_ACCEPT_MOVING_WRITE_BUFFER); + + // load key + if (!SSL_CTX_use_PrivateKey_file(ctx, + _privkey, + SSL_FILETYPE_PEM)) + throw String("error importing cert key file"); + // load cert + if (!SSL_CTX_use_certificate_file(ctx, + _pubkey, + SSL_FILETYPE_PEM)) + throw String("error importing cert file"); + // load peers' certs + load_peer_certs(); + + ssl_inited = true; + } + + // create SSL object, giving it context + _ssl = SSL_new(ctx); + if (!_ssl) + throw String("creation of ssl object failed"); + } + + // make socket non-blocking + try { + _sock.nonblocking(true); + } catch ( ... ) { + SSL_free(_ssl); + throw; + } + + // assign fd to _ssl + if (!SSL_set_fd(_ssl, _sock.get_sock())) { + SSL_free(_ssl); + throw String("fd assignment to ssl_obj failed"); + } +} + +SSLClient::~SSLClient() +{ + SSL_shutdown(_ssl); + SSL_free(_ssl); +} + + +bool +SSLClient::connect(unsigned int timeout) +{ + if (_connected) + return _connected; + + unsigned int beg = time_mil(); + while (time_mil() < beg + timeout) { + int ret = SSL_connect(_ssl); + if (ret == 1) { + _connected = true; + break; + } else { + bool want_read, want_write; + check_error(ret, want_read, want_write); + socket().ready(want_read, want_write, 250); + } + } + + return _connected; +} + +String +SSLClient::send(const String& msg, + unsigned int timeout) +{ + if (!_connected) + throw String("cannot send, yet: SSL connection not connected"); + + if (msg.empty()) + return msg; + + unsigned int beg = time_mil(); + while (time_mil() < beg + timeout) { + int ret = SSL_write(_ssl, msg.c_str(), msg.size()); + if (ret > 0) { + return msg.substr(ret); + } else { + bool want_read, want_write; + check_error(ret, want_read, want_write); + socket().ready(want_read, want_write, 250); + } + } + + return msg; +} + +String +SSLClient::recv(unsigned int timeout) +{ + if (!_connected) + throw String("cannot receive, yet: SSL connection not connected"); + + char buff[1024]; + + unsigned int beg = time_mil(); + while (time_mil() < beg + timeout) { + int ret = SSL_read(_ssl, buff, sizeof(buff)); + if (ret > 0) { + String data(buff, ret); + shred(buff, sizeof(buff)); + return data; + } else { + bool want_read, want_write; + check_error(ret, want_read, want_write); + socket().ready(want_read, want_write, 250); + } + } + + return ""; +} + +bool +SSLClient::peer_has_cert() +{ + if (!_connected) + throw String("cannot determine if peer has certificate: SSL connection not connected"); + + if (_cert_pem.size()) + return true; + + X509* cert = SSL_get_peer_certificate(_ssl); + if (!cert) + return false; + + // load cert into _cert_pem + FILE* f = NULL; + try { + if (!(f = tmpfile())) + throw String("unable to open temp file"); + if (!PEM_write_X509(f, cert)) + throw String("unable to write cert to tmp file"); + X509_free(cert); cert = NULL; + + // read cert + rewind(f); + while (true) { + char buff[1024]; + size_t i = fread(buff, sizeof(char), sizeof(buff), f); + _cert_pem.append(buff, i); + if (i == 0) { + if (feof(f)) + break; + else + throw String("error while reading certificate from temp file"); + } + } + fclose(f); f = NULL; + } catch ( ... ) { + if (cert) + X509_free(cert); + if (f) + fclose(f); + _cert_pem.clear(); + throw; + } + + return true; +} + +String +SSLClient::peer_cert_fingerprint(String& digest) +{ + if (!peer_has_cert()) + throw String("peer did not present cert"); + + String f_name("/tmp/luci_tmp_XXXXXX"); + int fd = -1; + char* buff = new char[f_name.size() + 1]; + try { + // pick a filename + strcpy(buff, f_name.c_str()); + if ((fd = mkstemp(buff)) == -1) + throw String("unable to generate random file"); + f_name = buff; + delete[] buff; buff = 0; + while (close(fd) && errno == EINTR) ; fd = -1; + + File f = File::open(f_name, true); + f.replace(_cert_pem); + + String out, err; + int status; + vector args; + args.push_back("x509"); + args.push_back("-sha1"); + args.push_back("-in"); + args.push_back(f_name); + args.push_back("-noout"); + args.push_back("-fingerprint"); + if (utils::execute("/usr/bin/openssl", + args, + out, + err, + status, + false)) + throw command_not_found_error_msg("/usr/bin/openssl"); + if (status) + throw String("openssl command failed"); + unlink(f_name.c_str()); + + vector words(utils::split(utils::strip(out))); + if (words.size() != 2) + throw String("error parsing fingerprint"); + + String finger(words[1]); + String::size_type idx = finger.find('='); + if (idx == finger.npos || + idx+1 == finger.size()) + throw String("error parsing fingerprint"); + + digest = words[0]; + return finger.substr(idx+1); + } catch ( ... ) { + delete[] buff; + if (fd != -1) + while (close(fd) && errno == EINTR) + ; + unlink(f_name.c_str()); + throw; + } +} + +bool +SSLClient::peer_cert_trusted() +{ + // signed by trusted CAs? + X509* cert = SSL_get_peer_certificate(_ssl); + if (!cert) + return false; + X509_free(cert); + if (SSL_get_verify_result(_ssl) == X509_V_OK) + return true; + + // cert present among saved certs? + peer_has_cert(); // make sure cert is saved in _cert_pem + MutexLocker l(global_lock); + for (list::const_iterator iter = trusted_certs.begin(); + iter != trusted_certs.end(); + iter++) + if (iter->cert == _cert_pem) + return true; + return false; +} + +bool +SSLClient::trust_peer_cert() +{ + MutexLocker l(global_lock); + + if (peer_cert_trusted()) + return true; + + if (!peer_has_cert()) + throw String("peer did not present cert"); + + String f_name(_certs_store_dir); + f_name += "/peer_cert_XXXXXX"; + int fd = -1; + char* buff = new char[f_name.size() + 1]; + try { + // pick a filename + strcpy(buff, f_name.c_str()); + if ((fd = mkstemp(buff)) == -1) + throw String("unable to generate random file"); + f_name = buff; + delete[] buff; buff = 0; + + String data(_cert_pem); + while (data.size()) { + ssize_t i = write(fd, data.c_str(), data.size()); + if (i == -1) { + if (errno != EINTR) + throw String("error writing certificate"); + } else + data = data.substr(i); + } + while (close(fd) && errno == EINTR) + ; + } catch ( ... ) { + delete[] buff; + if (fd != -1) + while (close(fd) && errno == EINTR) + ; + unlink(f_name.c_str()); + return false; + } + + load_peer_certs(); + + return true; +} + +bool +SSLClient::untrust_peer_cert() +{ + MutexLocker l(global_lock); + + if (!peer_has_cert()) + throw String("peer did not present cert"); + + for (list::const_iterator iter = trusted_certs.begin(); + iter != trusted_certs.end(); + iter++) + if (iter->cert == _cert_pem) + unlink(iter->file.c_str()); + + load_peer_certs(); + return true; +} + +ClientSocket& +SSLClient::socket() +{ + return _sock; +} + +void +SSLClient::check_error(int value, bool& want_read, bool& want_write) +{ + want_read = want_write = false; + + String e; + switch (SSL_get_error(_ssl, value)) { + case SSL_ERROR_NONE: + e = "SSL_ERROR_NONE"; + break; + case SSL_ERROR_ZERO_RETURN: + e = "SSL_ERROR_ZERO_RETURN"; + break; + case SSL_ERROR_WANT_READ: + want_read = true; + return; + case SSL_ERROR_WANT_WRITE: + want_write = true; + return; + case SSL_ERROR_WANT_CONNECT: + e = "SSL_ERROR_WANT_CONNECT"; + break; + case SSL_ERROR_WANT_ACCEPT: + e = "SSL_ERROR_WANT_ACCEPT"; + break; + case SSL_ERROR_WANT_X509_LOOKUP: + e = "SSL_ERROR_WANT_X509_LOOKUP"; + break; + case SSL_ERROR_SYSCALL: + e = "SSL_ERROR_SYSCALL"; + break; + case SSL_ERROR_SSL: + e = "SSL_ERROR_SSL"; + break; + } + + //FILE* f = fopen("/tmp/ssl_error_que", "a"); + //ERR_print_errors_fp(f); + //fclose(f); + + throw String("SSL error: ") + e; +} /cvs/cluster/conga/luci/conga_ssl/SSLClient.h,v --> standard output revision 1.1.2.1 --- conga/luci/conga_ssl/SSLClient.h +++ - 2006-12-08 18:27:34.372060000 +0000 @@ -0,0 +1,80 @@ +/* + Copyright Red Hat, Inc. 2005 + + This program is free software; you can redistribute it and/or modify it + under the terms of the GNU General Public License as published by the + Free Software Foundation; either version 2, or (at your option) any + later version. + + This program is distributed in the hope that it will be useful, but + WITHOUT ANY WARRANTY; without even the implied warranty of + MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU + General Public License for more details. + + You should have received a copy of the GNU General Public License + along with this program; see the file COPYING. If not, write to the + Free Software Foundation, Inc., 675 Mass Ave, Cambridge, + MA 02139, USA. +*/ +/* + * Author: Stanko Kupcevic + */ + + +#ifndef SSLClient_h +#define SSLClient_h + +#include "Socket.h" + +#include "String.h" +#include + + +#define _privkey "/var/lib/luci/var/certs/privkey.pem" +#define _pubkey "/var/lib/luci/var/certs/cacert.pem" +#define _trust_CAs "/var/lib/luci/var/certs/trust_CAs" +#define _certs_store_dir "/var/lib/luci/var/certs/peers" + + +// NOT THREAD SAFE + + +class SSLClient +{ + public: + SSLClient(ClientSocket sock); + virtual ~SSLClient(); + + bool connect(unsigned int timeout); + + String send(const String& msg, unsigned int timeout); + String recv(unsigned int timeout); + + + bool peer_has_cert(); + bool peer_cert_trusted(); // return true if peer's cert is trusted (either thru CA chain, or saved in cert_store) + + String peer_cert_fingerprint(String& digest); + + bool trust_peer_cert(); + bool untrust_peer_cert(); // remove peer's cert from cert_store + + ClientSocket& socket(); + + private: + SSLClient(const SSLClient&); + SSLClient operator=(const SSLClient&); + + ClientSocket _sock; + SSL* _ssl; + String _cert_pem; + + bool _connected; + + void check_error(int value, bool& want_read, bool& want_write); + + +}; // class SSLClient + + +#endif // SSLClient_h /cvs/cluster/conga/luci/conga_ssl/conga_ssl_lib.cpp,v --> standard output revision 1.1.2.1 --- conga/luci/conga_ssl/conga_ssl_lib.cpp +++ - 2006-12-08 18:27:34.717960000 +0000 @@ -0,0 +1,374 @@ +/* + Copyright Red Hat, Inc. 2006 + + This program is free software; you can redistribute it and/or modify it + under the terms of the GNU General Public License as published by the + Free Software Foundation; either version 2, or (at your option) any + later version. + + This program is distributed in the hope that it will be useful, but + WITHOUT ANY WARRANTY; without even the implied warranty of + MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU + General Public License for more details. + + You should have received a copy of the GNU General Public License + along with this program; see the file COPYING. If not, write to the + Free Software Foundation, Inc., 675 Mass Ave, Cambridge, + MA 02139, USA. +*/ +/* + * Author: Stanko Kupcevic + */ + + +#include + +#include +#include +#include +#include +#include + +#include "SSLClient.h" + +#include + +using namespace std; + + +class PythonThreadsAllower +{ +public: + PythonThreadsAllower() + { _save = PyEval_SaveThread(); } + + ~PythonThreadsAllower() + { PyEval_RestoreThread(_save); } + +private: + PyThreadState *_save; +}; + + + +static Mutex mutex; +static map > ssls; + + + +static PyObject * +conga_ssl_lib_connect(PyObject *self, PyObject *args); +static PyObject * +conga_ssl_lib_disconnect(PyObject *self, PyObject *args); + +static PyObject * +conga_ssl_lib_send(PyObject *self, PyObject *args); +static PyObject * +conga_ssl_lib_recv(PyObject *self, PyObject *args); + +static PyObject * +conga_ssl_lib_trust(PyObject *self, PyObject *args); +static PyObject * +conga_ssl_lib_trusted(PyObject *self, PyObject *args); +static PyObject * +conga_ssl_lib_untrust(PyObject *self, PyObject *args); + +static PyObject * +conga_ssl_lib_peer_fingerprint(PyObject *self, PyObject *args); + + + +static PyMethodDef SSLMethods[] = { + {"connect", conga_ssl_lib_connect, METH_VARARGS, + "doc"}, + {"disconnect", conga_ssl_lib_disconnect, METH_VARARGS, + "doc"}, + {"send", conga_ssl_lib_send, METH_VARARGS, + "doc"}, + {"recv", conga_ssl_lib_recv, METH_VARARGS, + "doc"}, + {"trust", conga_ssl_lib_trust, METH_VARARGS, + "doc"}, + {"trusted", conga_ssl_lib_trusted, METH_VARARGS, + "doc"}, + {"untrust", conga_ssl_lib_untrust, METH_VARARGS, + "doc"}, + {"peer_fingerprint", conga_ssl_lib_peer_fingerprint, METH_VARARGS, + "doc"}, + {NULL, NULL, 0, NULL} /* Sentinel */ +}; + + + +PyMODINIT_FUNC +initconga_ssl_lib(void) +{ + (void) Py_InitModule("conga_ssl_lib", SSLMethods); +} + + + +PyObject * +conga_ssl_lib_connect(PyObject *self, PyObject *args) +{ + const char* hostname; + int port; + int timeout; + if (!PyArg_ParseTuple(args, + "sii", + &hostname, + &port, + &timeout)) + return NULL; + if (port < 1 || port > 65535) { + PyErr_SetString(PyExc_ValueError, "invalid port number"); + return NULL; + } + if (timeout < 0) { + PyErr_SetString(PyExc_ValueError, "negative timeout"); + return NULL; + } + + try { + counting_auto_ptr ss; + { + PythonThreadsAllower all; + ClientSocket sock(hostname, port); + ss = counting_auto_ptr(new SSLClient(sock)); + ss->connect(timeout * 1000); + } + int id = (int) ss->socket().get_sock(); + ssls[id] = ss; + return Py_BuildValue("i", id); + } catch (String e) { + PyErr_SetString(PyExc_Exception, e.c_str()); + } catch ( ... ) { + PyErr_SetString(PyExc_Exception, "unknown"); + } + return NULL; +} + +PyObject * +conga_ssl_lib_disconnect(PyObject *self, PyObject *args) +{ + int id; + if (!PyArg_ParseTuple(args, "i", &id)) + return NULL; + + try { + map >::iterator iter = + ssls.find(id); + if (iter != ssls.end()) + ssls.erase(iter); + Py_INCREF(Py_None); + return Py_None; + } catch (String e) { + PyErr_SetString(PyExc_Exception, e.c_str()); + } catch ( ... ) { + PyErr_SetString(PyExc_Exception, "unknown"); + } + return NULL; +} + +PyObject * +conga_ssl_lib_send(PyObject *self, PyObject *args) +{ + int id; + const char* msg; + int timeout; + if (!PyArg_ParseTuple(args, "isi", &id, &msg, &timeout)) + return NULL; + if (timeout < 0) { + PyErr_SetString(PyExc_ValueError, "negative timeout"); + return NULL; + } + + try { + map >::const_iterator iter = + ssls.find(id); + if (iter == ssls.end()) + throw String("SSL connection closed"); + + { + PythonThreadsAllower all; + int beg = int(time_sec()); + String out(msg); + while (true) { + if (int(time_sec()) > beg + timeout) + throw String("timeout"); + else + if ((out = iter->second->send(out, 400)).empty()) + break; + } + } + + Py_INCREF(Py_None); + return Py_None; + } catch (String e) { + PyErr_SetString(PyExc_Exception, e.c_str()); + } catch ( ... ) { + PyErr_SetString(PyExc_Exception, "unknown"); + } + return NULL; +} + +PyObject * +conga_ssl_lib_recv(PyObject *self, PyObject *args) +{ + int id, timeout; + if (!PyArg_ParseTuple(args, "ii", &id, &timeout)) + return NULL; + if (timeout < 0) { + PyErr_SetString(PyExc_ValueError, "negative timeout"); + return NULL; + } + + try { + map >::const_iterator iter = + ssls.find(id); + if (iter == ssls.end()) + throw String("SSL connection closed"); + + String resp; + { + PythonThreadsAllower all; + int beg = int(time_sec()); + String xml_in; + while (true) { + if (int(time_sec()) > beg + timeout) + throw String("timeout"); + else + xml_in += iter->second->recv(400); + try { + parseXML(xml_in); + resp = xml_in; + break; + } catch ( ... ) {} + } + } + + PyObject* resp_p = Py_BuildValue("s", resp.c_str()); + return resp_p; + } catch (String e) { + PyErr_SetString(PyExc_Exception, e.c_str()); + } catch ( ... ) { + PyErr_SetString(PyExc_Exception, "unknown"); + } + return NULL; +} + +PyObject * +conga_ssl_lib_trust(PyObject *self, PyObject *args) +{ + int id; + if (!PyArg_ParseTuple(args, "i", &id)) + return NULL; + + try { + map >::const_iterator iter = + ssls.find(id); + if (iter == ssls.end()) + throw String("SSL connection closed"); + + bool resp; + { + PythonThreadsAllower all; + resp = iter->second->trust_peer_cert(); + } + + PyObject* resp_p = Py_BuildValue("i", (resp)?1:0); + return resp_p; + } catch (String e) { + PyErr_SetString(PyExc_Exception, e.c_str()); + } catch ( ... ) { + PyErr_SetString(PyExc_Exception, "unknown"); + } + return NULL; +} + +PyObject * +conga_ssl_lib_trusted(PyObject *self, PyObject *args) +{ + int id; + if (!PyArg_ParseTuple(args, "i", &id)) + return NULL; + + try { + map >::const_iterator iter = + ssls.find(id); + if (iter == ssls.end()) + throw String("SSL connection closed"); + + bool resp; + { + PythonThreadsAllower all; + resp = iter->second->peer_cert_trusted(); + } + + PyObject* resp_p = Py_BuildValue("i", (resp)?1:0); + return resp_p; + } catch (String e) { + PyErr_SetString(PyExc_Exception, e.c_str()); + } catch ( ... ) { + PyErr_SetString(PyExc_Exception, "unknown"); + } + return NULL; +} + +PyObject * +conga_ssl_lib_untrust(PyObject *self, PyObject *args) +{ + int id; + if (!PyArg_ParseTuple(args, "i", &id)) + return NULL; + + try { + map >::const_iterator iter = + ssls.find(id); + if (iter == ssls.end()) + throw String("SSL connection closed"); + + bool resp; + { + PythonThreadsAllower all; + resp = iter->second->untrust_peer_cert(); + } + + PyObject* resp_p = Py_BuildValue("i", (resp)?1:0); + return resp_p; + } catch (String e) { + PyErr_SetString(PyExc_Exception, e.c_str()); + } catch ( ... ) { + PyErr_SetString(PyExc_Exception, "unknown"); + } + return NULL; +} + +PyObject * +conga_ssl_lib_peer_fingerprint(PyObject *self, PyObject *args) +{ + int id; + if (!PyArg_ParseTuple(args, "i", &id)) + return NULL; + + try { + map >::const_iterator iter = + ssls.find(id); + if (iter == ssls.end()) + throw String("SSL connection closed"); + + String finger, digest; + { + PythonThreadsAllower all; + finger = iter->second->peer_cert_fingerprint(digest); + } + + PyObject* resp_p = Py_BuildValue("(ss)", digest.c_str(), finger.c_str()); + return resp_p; + } catch (String e) { + PyErr_SetString(PyExc_Exception, e.c_str()); + } catch ( ... ) { + PyErr_SetString(PyExc_Exception, "unknown"); + } + return NULL; +} /cvs/cluster/conga/luci/conga_ssl/setup.py,v --> standard output revision 1.1.2.1 --- conga/luci/conga_ssl/setup.py +++ - 2006-12-08 18:27:35.171396000 +0000 @@ -0,0 +1,37 @@ + +from distutils.core import setup, Extension + + +module1 = Extension('conga_ssl_lib', + define_macros = [('MAJOR_VERSION', '0'), + ('MINOR_VERSION', '8')], + include_dirs = ['../../ricci/include', + '/usr/include/libxml2'], + libraries = ['ssl', 'xml2'], + #library_dirs = ['/usr/local/lib'], + sources = ['conga_ssl_lib.cpp', + 'SSLClient.cpp', + '../../ricci/common/ClientSocket.cpp', + '../../ricci/common/Socket.cpp', + '../../ricci/common/Logger.cpp', + '../../ricci/common/Time.cpp', + '../../ricci/common/File.cpp', + '../../ricci/common/XML.cpp', + '../../ricci/common/utils.cpp', + '../../ricci/common/executils.cpp']) + + + +setup (name = 'conga_ssl_lib', + version = '0.8', + description = 'SSL Python bindings for Conga', + author = 'Stanko Kupcevic', + author_email = 'kupcevic at redhat.com', + copyright = 'Red Hat, Inc.', + license = 'GPL', + url = 'http://www.sourceware.org/cluster/conga', + long_description = ''' + conga_ssl_lib + ''', + ext_modules = [module1]) + --- conga/luci/site/Makefile 2006/08/24 14:38:28 1.11 +++ conga/luci/site/Makefile 2006/12/08 18:27:32 1.11.2.1 @@ -82,6 +82,7 @@ install -d ${DESTDIR}/var/lib/luci/var/pts # install -m 644 `find luci/var/pts -maxdepth 1 -type f | grep .mo | grep -v \~ | grep -v \#` ${DESTDIR}/var/lib/luci/var/pts install -d ${DESTDIR}/var/lib/luci/var/certs + install -d ${DESTDIR}/var/lib/luci/var/certs/peers install -m 644 luci/var/certs/cacert.config ${DESTDIR}/var/lib/luci/var/certs install -d ${DESTDIR}/var/lib/luci/var/stunnel /cvs/cluster/conga/luci/site/luci/Extensions/conga_ssl.py,v --> standard output revision 1.1.2.1 --- conga/luci/site/luci/Extensions/conga_ssl.py +++ - 2006-12-08 18:27:35.466686000 +0000 @@ -0,0 +1,48 @@ + + +import sys +sys.path.append('/usr/lib/luci/ssl') +sys.path.append('/usr/lib64/luci/ssl') +import conga_ssl_lib +sys.path.remove('/usr/lib/luci/ssl') +sys.path.remove('/usr/lib64/luci/ssl') + + + +# timeouts are in seconds (int) + + +class SSLSocket: + + def __init__(self, + hostname, + port, + timeout): + self.__id = -1 + self.__id = conga_ssl_lib.connect(hostname, port, timeout) + pass + def __del__(self): + self.disconnect() + pass + def disconnect(self): + if self.__id != -1: + conga_ssl_lib.disconnect(self.__id) + self.__id = -1 + + def peer_fingerprint(self): + return conga_ssl_lib.peer_fingerprint(self.__id) + + def trusted(self): + return conga_ssl_lib.trusted(self.__id) == 1 + def trust(self): + if self.trusted(): + return True + return conga_ssl_lib.trust(self.__id) == 1 + def untrust(self): + return conga_ssl_lib.untrust(self.__id) == 1 + + + def send(self, msg, timeout): + conga_ssl_lib.send(self.__id, msg, timeout) + def recv(self, timeout): + return conga_ssl_lib.recv(self.__id, timeout) --- conga/luci/site/luci/Extensions/HelperFunctions.py 2006/11/29 22:33:50 1.4.2.1 +++ conga/luci/site/luci/Extensions/HelperFunctions.py 2006/12/08 18:27:32 1.4.2.2 @@ -1,6 +1,8 @@ import AccessControl +import threading +from ricci_communicator import RicciCommunicator def add_commas(self, str1, str2): @@ -29,17 +31,75 @@ + +class Worker(threading.Thread): + def __init__(self, + mutex, + hosts, + riccis): + threading.Thread.__init__(self) + self.mutex = mutex + self.hosts = hosts + self.riccis = riccis + return + def run(self): + while True: + self.mutex.acquire() + if len(self.hosts) == 0: + self.mutex.release() + return + host = self.hosts.pop() + self.mutex.release() + r = None + try: + r = RicciCommunicator(host) + #print host, 'done' + except Exception, e: + #print host, 'failed', str(e) + pass + except: + #print host, 'failed' + pass + self.mutex.acquire() + self.riccis[host] = r + self.mutex.release() + + + # removes systems that user is not authorized access to -def get_systems_statuses(self, systems): - ss = {} +def get_systems_statuses(self, systems, from_cache=False): + CACHED_INDEX = '_get_systems_statuses()_cached_result_' + session = self.REQUEST.SESSION + if session.has_key(CACHED_INDEX): + res = session[CACHED_INDEX] + if res != None: + session.set(CACHED_INDEX, None) + if from_cache: + return res + pass + ass = self.allowed_systems(self, None, systems) + + mutex = threading.RLock() + hive = [] # workers + ss = {} # storage systems (will store riccis, and then use them to retrieve real info) + hosts = [] # hostnames for system in ass: - hostname = system[0] + hosts.append(system[0]) + if len(hosts) < 10: + hive.append(Worker(mutex, hosts, ss)) + + for bee in hive: + bee.start() + for bee in hive: + bee.join() + + for hostname in ss.keys(): OS = '' cluname = '' cluali = '' authed = False - ricci = self.get_ricci_communicator(hostname, ass) + ricci = ss[hostname] if ricci != None: OS = ricci.os() cluname = ricci.cluster_info()[0] @@ -53,6 +113,7 @@ 'clualias' : cluali, 'available': ricci != None, 'authed' : authed} + # replace ricci with system's info ss[hostname] = s pass ss_list = [] @@ -60,6 +121,9 @@ sorted_keys.sort() for name in sorted_keys: ss_list.append(ss[name]) + + session.set(CACHED_INDEX, ss_list) + return ss_list @@ -88,7 +152,6 @@ response.setCookie(cookie_prefix + var_name, value, expires='Tue, 30 Jun 2060 12:00:00 GMT') - return value --- conga/luci/site/luci/Extensions/StorageReport.py 2006/11/29 18:26:53 1.20.2.1 +++ conga/luci/site/luci/Extensions/StorageReport.py 2006/12/08 18:27:32 1.20.2.2 @@ -1878,17 +1878,8 @@ -def group_systems_by_cluster(self, allowed_systems, cache_it=False): - CACHED_INDEX = '_group_systems_by_cluster_cached_result_' - session = self.REQUEST.SESSION - if session.has_key(CACHED_INDEX): - res = session[CACHED_INDEX] - if res != None: - session.set(CACHED_INDEX, None) - return res - pass - - ss = get_systems_statuses(self, allowed_systems) +def group_systems_by_cluster(self, allowed_systems, from_cache=False): + ss = get_systems_statuses(self, allowed_systems, from_cache) clusters = {} bad_list = [] for s in ss: @@ -1912,10 +1903,6 @@ ret = [nonclu_list, clu_list, bad_list] - if cache_it: - session.set(CACHED_INDEX, ret) - pass - return ret --- conga/luci/site/luci/Extensions/ricci_communicator.py 2006/11/20 23:32:43 1.9.2.6 +++ conga/luci/site/luci/Extensions/ricci_communicator.py 2006/12/08 18:27:32 1.9.2.7 @@ -1,8 +1,8 @@ -from socket import socket, ssl, AF_INET, SOCK_STREAM import xml import xml.dom from xml.dom import minidom from LuciSyslog import LuciSyslog +from conga_ssl import SSLSocket CERTS_DIR_PATH = '/var/lib/luci/var/certs/' @@ -15,32 +15,25 @@ pass class RicciCommunicator: - def __init__(self, hostname, port=11111): + def __init__(self, hostname, enforce_trust=False, port=11111): self.__hostname = hostname self.__port = port + self.__timeout_init = 4 + self.__timeout_auth = 4 + self.__timeout_short = 6 + self.__timeout_long = 600 + self.__privkey_file = CERTS_DIR_PATH + 'privkey.pem' self.__cert_file = CERTS_DIR_PATH + 'cacert.pem' - # socket try: - sock = socket(AF_INET, SOCK_STREAM) - sock.settimeout(2.0) - sock.connect((self.__hostname, self.__port)) - except Exception, e: - raise RicciError, 'Error connecting to %s:%d: %s' \ - % (self.__hostname, self.__port, str(e)) - except: - raise RicciError, 'Error connecting to %s:%d: unknown error' \ - % (self.__hostname, self.__port) - - luci_log.debug_verbose('RC:init0: Connected to %s:%d' \ - % (self.__hostname, self.__port)) - try: - self.ss = ssl(sock, self.__privkey_file, self.__cert_file) - # TODO: data transfer timeout should be much less, - # leave until all calls are async ricci calls - sock.settimeout(600.0) # 10 minutes + self.ss = SSLSocket(self.__hostname, + self.__port, + self.__timeout_init) + if enforce_trust: + if not self.ss.trusted(): + raise RicciError, 'ricci\'s certificate is not trusted' except Exception, e: raise RicciError, 'Error setting up SSL for connection to %s: %s' \ % (self.__hostname, str(e)) @@ -49,20 +42,20 @@ % self.__hostname # receive ricci header - hello = self.__receive() + hello = self.__receive(self.__timeout_init) try: - luci_log.debug_verbose('RC:init1: Received header from %s: \"%s\"' \ + luci_log.debug_verbose('RC:init0: Received header from %s: \"%s\"' \ % (self.__hostname, hello.toxml())) except: pass - + self.__authed = hello.firstChild.getAttribute('authenticated') == 'true' self.__cluname = hello.firstChild.getAttribute('clustername') self.__clualias = hello.firstChild.getAttribute('clusteralias') self.__reported_hostname = hello.firstChild.getAttribute('hostname') self.__os = hello.firstChild.getAttribute('os') self.__dom0 = hello.firstChild.getAttribute('xen_host') == 'true' - + pass @@ -104,10 +97,10 @@ ricci.setAttribute("function", "authenticate") ricci.setAttribute("password", password) doc.appendChild(ricci) - self.__send(doc) + self.__send(doc, self.__timeout_auth) # receive response - resp = self.__receive() + resp = self.__receive(self.__timeout_auth) self.__authed = resp.firstChild.getAttribute('authenticated') == 'true' luci_log.debug_verbose('RC:auth1: auth call returning %d' \ @@ -121,8 +114,8 @@ ricci.setAttribute('version', '1.0') ricci.setAttribute('function', 'unauthenticate') doc.appendChild(ricci) - self.__send(doc) - resp = self.__receive() + self.__send(doc, self.__timeout_auth) + resp = self.__receive(self.__timeout_auth) luci_log.debug_verbose('RC:unauth0: trying to unauthenticate to %s' \ % self.__hostname) @@ -167,7 +160,7 @@ # send request try: - self.__send(doc) + self.__send(doc, self.__timeout_short) except Exception, e: luci_log.debug_verbose('RC:PB1: Error sending XML \"%s\" to host %s' \ % (doc.toxml(), self.__hostname)) @@ -177,7 +170,7 @@ raise RicciError, 'Error sending XML to host %s' % self.__hostname # receive response - doc = self.__receive() + doc = self.__receive(self.__timeout_long) try: luci_log.debug_verbose('RC:PB2: received from %s XML \"%s\"' \ % (self.__hostname, doc.toxml())) @@ -243,11 +236,11 @@ doc.appendChild(ricci) # send request - self.__send(doc) + self.__send(doc, self.__timeout_short) # receive response - doc = self.__receive() + doc = self.__receive(self.__timeout_short) if doc.firstChild.getAttribute('success') == '12': return None if doc.firstChild.getAttribute('success') != '0': @@ -265,20 +258,18 @@ - def __send(self, xml_doc): + def __send(self, xml_doc, timeout): buff = xml_doc.toxml() + '\n' - while len(buff) != 0: - try: - pos = self.ss.write(buff) - except Exception, e: - luci_log.debug_verbose('RC:send0: Error sending XML \"%s\" to %s: %s' \ - % (buff, self.__hostname, str(e))) - raise RicciError, 'write error while sending XML to host %s' \ - % self.__hostname - except: - raise RicciError, 'write error while sending XML to host %s' \ - % self.__hostname - buff = buff[pos:] + try: + self.ss.send(buff, timeout) + except Exception, e: + luci_log.debug_verbose('RC:send0: Error sending XML \"%s\" to %s: %s' \ + % (buff, self.__hostname, str(e))) + raise RicciError, 'write error while sending XML to host %s' \ + % self.__hostname + except: + raise RicciError, 'write error while sending XML to host %s' \ + % self.__hostname try: luci_log.debug_verbose('RC:send1: Sent XML \"%s\" to host %s' \ % (xml_doc.toxml(), self.__hostname)) @@ -286,21 +277,11 @@ pass return - def __receive(self): + def __receive(self, timeout): doc = None xml_in = '' try: - while True: - buff = self.ss.read(10485760) - if buff == '': - break - xml_in += buff - try: - doc = minidom.parseString(xml_in) - break - except: - # we haven't received all of the XML data yet. - continue + xml_in = self.ss.recv(timeout) except Exception, e: luci_log.debug_verbose('RC:recv0: Error reading data from %s: %s' \ % (self.__hostname, str(e))) --- conga/luci/site/luci/Extensions/storage_adapters.py 2006/10/19 14:57:17 1.7.2.1 +++ conga/luci/site/luci/Extensions/storage_adapters.py 2006/12/08 18:27:32 1.7.2.2 @@ -61,7 +61,7 @@ #display_clusters = True display_clusters = False if display_clusters: - sorted_data = self.group_systems_by_cluster(systems, cache_it=True) + sorted_data = self.group_systems_by_cluster(systems, from_cache=False) for sdl in sorted_data[:2]: for data in sdl: createStorageChooser_inner(url, @@ -70,7 +70,7 @@ data, syslist) else: - sorted_data = get_systems_statuses(self, systems) + sorted_data = get_systems_statuses(self, systems, from_cache=False) for data in sorted_data: createStorageChooser_inner(url, pagetype, --- conga/luci/storage/form-macros 2006/10/31 17:48:33 1.17.2.3 +++ conga/luci/storage/form-macros 2006/12/08 18:27:32 1.17.2.4 @@ -293,7 +293,7 @@ -
--- conga/ricci/ricci/SSLInstance.cpp 2006/10/23 21:13:22 1.5.2.1 +++ conga/ricci/ricci/SSLInstance.cpp 2006/12/08 18:27:32 1.5.2.2 @@ -137,6 +137,11 @@ else ssl_locks[n]->unlock(); } +static pthread_t +ssl_id_callback(void) +{ + return pthread_self(); +} @@ -157,12 +162,12 @@ // TODO: random number generator, // not on systems with /dev/urandom (eg. Linux) - // set up lockings + // thread support ssl_locks.clear(); - for (int i=0; i(new Mutex())); CRYPTO_set_locking_callback(ssl_mutex_callback); - //CRYPTO_set_id_callback(ssl_id_function); not needed on Linux + CRYPTO_set_id_callback(ssl_id_callback); // create context if (!ctx) @@ -354,6 +359,7 @@ // cert present among saved certs? client_has_cert(); // make sure cert is saved in _cert_pem + MutexLocker l(global_lock); for (list::const_iterator iter = authorized_certs.begin(); iter != authorized_certs.end(); iter++)