#include "jabberd.h" #ifdef HAVE_SSL HASHTABLE ssl__ctxs; #ifndef NO_RSA /* This function will generate a temporary key for us */ RSA *_ssl_tmp_rsa_cb(SSL *ssl, int export, int keylength) { RSA *rsa_tmp = NULL; rsa_tmp = RSA_generate_key(keylength, RSA_F4, NULL, NULL); if(!rsa_tmp) { log_debug(ZONE, "Error generating temp RSA key"); return NULL; } return rsa_tmp; } #endif /* NO_RSA */ /*************************************************************************** * This can return whatever we need, it is just designed to read a xmlnode * and hash the SSL contexts it creates from the keys in the node * * Sample node: * * /path/to/the/key/file.pem * /path/to/the/key/file.pem * **************************************************************************/ void mio_ssl_init(xmlnode x) { /* PSEUDO CODE for $key in children(xmlnode x) { - SSL init - Load key into SSL ctx - Hash ctx based on hostname } register a cleanup function to free our contexts */ SSL_CTX *ctx = NULL; xmlnode cur; char *host; char *keypath; log_debug(ZONE, "MIO SSL init"); /* Make sure we have a valid xmlnode to play with */ if(x == NULL && xmlnode_has_children(x)) { log_debug(ZONE, "SSL Init called with invalid xmlnode"); return; } log_debug(ZONE, "Handling configuration using: %s", xmlnode2str(x)); /* Generic SSL Inits */ OpenSSL_add_all_algorithms(); SSL_load_error_strings(); /* Setup our hashtable */ ssl__ctxs = ghash_create(19,(KEYHASHFUNC)str_hash_code, (KEYCOMPAREFUNC)j_strcmp); /* Walk our node and add the created contexts */ for(cur = xmlnode_get_tag(x, "key"); cur != NULL; cur = xmlnode_get_nextsibling(cur)) { host = xmlnode_get_attrib(cur, "ip"); keypath = xmlnode_get_data(cur); if(!host || !keypath) continue; log_debug(ZONE, "Handling: %s", xmlnode2str(cur)); ctx=SSL_CTX_new(SSLv23_server_method()); if(ctx == NULL) { unsigned long e; static char *buf; e = ERR_get_error(); buf = ERR_error_string(e, NULL); log_debug(ZONE, "Could not create SSL Context: %s", buf); return; } #ifndef NO_RSA log_debug(ZONE, "Setting temporary RSA callback"); SSL_CTX_set_tmp_rsa_callback(ctx, _ssl_tmp_rsa_cb); #endif /* NO_RSA */ SSL_CTX_set_session_cache_mode(ctx, SSL_SESS_CACHE_BOTH); /* XXX I would like to make this a configurable option */ /* SSL_CTX_set_timeout(ctx, session_timeout); */ /* Setup the keys and certs */ log_debug(ZONE, "Loading SSL certificate %s for %s", keypath, host); if(!SSL_CTX_use_certificate_file(ctx, keypath,SSL_FILETYPE_PEM)) { log_debug(ZONE, "SSL Error using certificate file"); SSL_CTX_free(ctx); continue; } if(!SSL_CTX_use_PrivateKey_file(ctx, keypath,SSL_FILETYPE_PEM)) { log_debug(ZONE, "SSL Error using Private Key file"); SSL_CTX_free(ctx); continue; } ghash_put(ssl__ctxs, host, ctx); log_debug(ZONE, "Added context %x for %s", ctx, host); } } void _mio_ssl_cleanup(void *arg) { SSL *ssl = (SSL *)arg; log_debug(ZONE, "SSL Cleanup for %x", ssl); SSL_free(ssl); } ssize_t _mio_ssl_read(mio m, void *buf, size_t count) { return SSL_read((SSL *)m->ssl, (char *)buf, count); } ssize_t _mio_ssl_write(mio m, const void *buf, size_t count) { return SSL_write((SSL *)m->ssl, buf, count); } int _mio_ssl_accept(mio m, struct sockaddr *serv_addr, socklen_t *addrlen) { SSL *ssl=NULL; SSL_CTX *ctx = NULL; int fd; if(m->ip == NULL) { log_warn(ZONE, "SSL accept without an IP"); return -1; } fd = accept(m->fd, serv_addr, addrlen); ctx = ghash_get(ssl__ctxs, m->ip); if(ctx == NULL) { log_debug(ZONE, "NULL ctx found in SSL hash"); return -1; } ssl = SSL_new(ctx); log_debug(ZONE, "SSL accepting socket with new session %x", ssl); SSL_set_fd(ssl, fd); SSL_set_accept_state(ssl); if(SSL_accept(ssl) <= 0){ unsigned long e; static char *buf; e = ERR_get_error(); buf = ERR_error_string(e, NULL); log_debug(ZONE, "Error from SSL: %s", buf); log_debug(ZONE, "SSL Error in SSL_accept call"); SSL_free(ssl); close(fd); return -1; } m->ssl = ssl; log_debug(ZONE, "Accepted new SSL socket %d for %s", fd, m->ip); return fd; } int _mio_ssl_connect(mio m, struct sockaddr *serv_addr, socklen_t addrlen) { /* PSEUDO I need to actually look this one up, but I assume it's similar to the SSL accept stuff. */ SSL *ssl=NULL; SSL_CTX *ctx = NULL; int fd; log_debug(ZONE, "Connecting new SSL socket for %s", m->ip); ctx = ghash_get(ssl__ctxs, m->ip); fd = connect(m->fd, serv_addr, addrlen); SSL_set_fd(ssl, fd); if(SSL_connect(ssl) <= 0){ log_debug(ZONE, "SSL Error in SSL_connect call"); SSL_free(ssl); close(fd); return -1; } pool_cleanup(m->p, _mio_ssl_cleanup, (void *)ssl); m->ssl = ssl; return fd; } #endif /* HAVE_SSL */