diff -r -U4 jabberd-2.1.23/c2s/c2s.h jabberd-2.1.23+sa/c2s/c2s.h --- jabberd-2.1.23/c2s/c2s.h 2008-02-03 14:23:06.000000000 +0000 +++ jabberd-2.1.23+sa/c2s/c2s.h 2008-05-06 20:43:09.000000000 +0100 @@ -111,8 +111,11 @@ /** starttls pemfile */ char *host_pemfile; + /** certificate chain */ + char *host_cachain; + /** verify-mode */ int host_verify_mode; /** require starttls */ diff -r -U4 jabberd-2.1.23/c2s/main.c jabberd-2.1.23+sa/c2s/main.c --- jabberd-2.1.23/c2s/main.c 2008-02-03 14:23:06.000000000 +0000 +++ jabberd-2.1.23+sa/c2s/main.c 2008-05-06 18:58:48.000000000 +0100 @@ -240,16 +240,25 @@ host->realm = (realm != NULL) ? realm : pstrdup(xhash_pool(c2s->hosts), id); host->host_pemfile = j_attr((const char **) elem->attrs[i], "pemfile"); + host->host_cachain = j_attr((const char **) elem->attrs[i], "cachain"); + host->host_verify_mode = j_atoi(j_attr((const char **) elem->attrs[i], "verify-mode"), 0); #ifdef HAVE_SSL - if(c2s->sx_ssl == NULL && host->host_pemfile != NULL) { - c2s->sx_ssl = sx_env_plugin(c2s->sx_env, sx_ssl_init, host->host_pemfile, NULL, host->host_verify_mode); + if(host->host_pemfile != NULL) { if(c2s->sx_ssl == NULL) { - log_write(c2s->log, LOG_ERR, "failed to load %s SSL pemfile", host->realm); - host->host_pemfile = NULL; + c2s->sx_ssl = sx_env_plugin(c2s->sx_env, sx_ssl_init, host->realm, host->host_pemfile, host->host_cachain, host->host_verify_mode); + if(c2s->sx_ssl == NULL) { + log_write(c2s->log, LOG_ERR, "failed to load %s SSL pemfile", host->realm); + host->host_pemfile = NULL; + } + } else { + if(sx_ssl_server_addcert(c2s->sx_ssl, host->realm, host->host_pemfile, host->host_cachain, host->host_verify_mode) != 0) { + log_write(c2s->log, LOG_ERR, "failed to load %s SSL pemfile", host->realm); + host->host_pemfile = NULL; + } } } #endif @@ -636,18 +645,18 @@ #ifdef HAVE_SSL /* get the ssl context up and running */ if(c2s->local_pemfile != NULL) { - c2s->sx_ssl = sx_env_plugin(c2s->sx_env, sx_ssl_init, c2s->local_pemfile, NULL, c2s->local_verify_mode); + c2s->sx_ssl = sx_env_plugin(c2s->sx_env, sx_ssl_init, NULL, c2s->local_pemfile, NULL, c2s->local_verify_mode); if(c2s->sx_ssl == NULL) { log_write(c2s->log, LOG_ERR, "failed to load local SSL pemfile, SSL will not be available to clients"); c2s->local_pemfile = NULL; } } /* try and get something online, so at least we can encrypt to the router */ if(c2s->sx_ssl == NULL && c2s->router_pemfile != NULL) { - c2s->sx_ssl = sx_env_plugin(c2s->sx_env, sx_ssl_init, c2s->router_pemfile, NULL, NULL); + c2s->sx_ssl = sx_env_plugin(c2s->sx_env, sx_ssl_init, NULL, c2s->router_pemfile, NULL, NULL); if(c2s->sx_ssl == NULL) { log_write(c2s->log, LOG_ERR, "failed to load router SSL pemfile, channel to router will not be SSL encrypted"); c2s->router_pemfile = NULL; } diff -r -U4 jabberd-2.1.23/resolver/resolver.c jabberd-2.1.23+sa/resolver/resolver.c --- jabberd-2.1.23/resolver/resolver.c 2008-02-03 14:23:08.000000000 +0000 +++ jabberd-2.1.23+sa/resolver/resolver.c 2008-05-06 00:01:48.000000000 +0100 @@ -597,9 +597,9 @@ r->sx_env = sx_env_new(); #ifdef HAVE_SSL if(r->router_pemfile != NULL) { - r->sx_ssl = sx_env_plugin(r->sx_env, sx_ssl_init, r->router_pemfile, NULL, NULL); + r->sx_ssl = sx_env_plugin(r->sx_env, sx_ssl_init, NULL, r->router_pemfile, NULL, NULL); if(r->sx_ssl == NULL) { log_write(r->log, LOG_ERR, "failed to load SSL pemfile, SSL disabled"); r->router_pemfile = NULL; } diff -r -U4 jabberd-2.1.23/router/main.c jabberd-2.1.23+sa/router/main.c --- jabberd-2.1.23/router/main.c 2008-02-03 14:23:07.000000000 +0000 +++ jabberd-2.1.23+sa/router/main.c 2008-05-06 00:02:00.000000000 +0100 @@ -393,9 +393,9 @@ r->sx_env = sx_env_new(); #ifdef HAVE_SSL if(r->local_pemfile != NULL) { - r->sx_ssl = sx_env_plugin(r->sx_env, sx_ssl_init, r->local_pemfile, NULL, NULL); + r->sx_ssl = sx_env_plugin(r->sx_env, sx_ssl_init, NULL, r->local_pemfile, NULL, NULL); if(r->sx_ssl == NULL) log_write(r->log, LOG_ERR, "failed to load SSL pemfile, SSL disabled"); } #endif diff -r -U4 jabberd-2.1.23/s2s/main.c jabberd-2.1.23+sa/s2s/main.c --- jabberd-2.1.23/s2s/main.c 2008-02-03 14:23:07.000000000 +0000 +++ jabberd-2.1.23+sa/s2s/main.c 2008-05-06 20:59:38.000000000 +0100 @@ -19,8 +19,10 @@ */ #include "s2s.h" +#include + static sig_atomic_t s2s_shutdown = 0; sig_atomic_t s2s_lost_router = 0; static sig_atomic_t s2s_logrotate = 0; @@ -137,10 +139,10 @@ if(s2s->local_secret == NULL) s2s->local_secret = "secret"; s2s->local_pemfile = config_get_one(s2s->config, "local.pemfile", 0); - if (s2s->local_pemfile != NULL) - log_debug(ZONE,"loaded local pemfile for peer s2s connections"); + s2s->local_cachain = config_get_one(s2s->config, "local.cachain", 0); + s2s->local_verify_mode = j_atoi(config_get_one(s2s->config, "local.verify-mode", 0), 0); s2s->io_max_fds = j_atoi(config_get_one(s2s->config, "io.max_fds", 0), 1024); s2s->stanza_size_limit = j_atoi(config_get_one(s2s->config, "io.limits.stanzasize", 0), 0); @@ -151,8 +153,65 @@ s2s->check_idle = j_atoi(config_get_one(s2s->config, "check.idle", 0), 86400); } +static void _s2s_hosts_expand(s2s_t s2s) +{ + char *realm; + config_elem_t elem; + char id[1024]; + int i; + + elem = config_get(s2s->config, "local.id"); + for(i = 0; i < elem->nvalues; i++) { + host_t host = (host_t) pmalloco(xhash_pool(s2s->hosts), sizeof(struct host_st)); + if(!host) { + log_write(s2s->log, LOG_ERR, "cannot allocate memory for new host, aborting"); + exit(1); + } + + realm = j_attr((const char **) elem->attrs[i], "realm"); + + /* stringprep ids (domain names) so that they are in canonical form */ + strncpy(id, elem->values[i], 1024); + id[1023] = '\0'; + if (stringprep_nameprep(id, 1024) != 0) { + log_write(s2s->log, LOG_ERR, "cannot stringprep id %s, aborting", id); + exit(1); + } + + host->realm = (realm != NULL) ? realm : pstrdup(xhash_pool(s2s->hosts), id); + + host->host_pemfile = j_attr((const char **) elem->attrs[i], "pemfile"); + + host->host_cachain = j_attr((const char **) elem->attrs[i], "cachain"); + + host->host_verify_mode = j_atoi(j_attr((const char **) elem->attrs[i], "verify-mode"), 0); + +#ifdef HAVE_SSL + if(host->host_pemfile != NULL) { + if(s2s->sx_ssl == NULL) { + s2s->sx_ssl = sx_env_plugin(s2s->sx_env, sx_ssl_init, host->realm, host->host_pemfile, host->host_cachain, host->host_verify_mode); + if(s2s->sx_ssl == NULL) { + log_write(s2s->log, LOG_ERR, "failed to load %s SSL pemfile", host->realm); + host->host_pemfile = NULL; + } + } else { + if(sx_ssl_server_addcert(s2s->sx_ssl, host->realm, host->host_pemfile, host->host_cachain, host->host_verify_mode) != 0) { + log_write(s2s->log, LOG_ERR, "failed to load %s SSL pemfile", host->realm); + host->host_pemfile = NULL; + } + } + } +#endif + + /* insert into vHosts xhash */ + xhash_put(s2s->hosts, pstrdup(xhash_pool(s2s->hosts), id), host); + + log_write(s2s->log, LOG_NOTICE, "[%s] configured; realm=%s", id, host->realm); + } +} + static int _s2s_router_connect(s2s_t s2s) { log_write(s2s->log, LOG_NOTICE, "attempting connection to router at %s, port=%d", s2s->router_ip, s2s->router_port); s2s->fd = mio_connect(s2s->mio, s2s->router_port, s2s->router_ip, s2s_router_mio_callback, (void *) s2s); @@ -486,9 +545,9 @@ #ifdef HAVE_SSL /* get the ssl context up and running */ if(s2s->local_pemfile != NULL) { - s2s->sx_ssl = sx_env_plugin(s2s->sx_env, sx_ssl_init, s2s->local_pemfile, s2s->local_cachain, s2s->local_verify_mode); + s2s->sx_ssl = sx_env_plugin(s2s->sx_env, sx_ssl_init, NULL, s2s->local_pemfile, s2s->local_cachain, s2s->local_verify_mode); if(s2s->sx_ssl == NULL) { log_write(s2s->log, LOG_ERR, "failed to load local SSL pemfile, SSL will not be available to peers"); s2s->local_pemfile = NULL; @@ -497,9 +556,9 @@ } /* try and get something online, so at least we can encrypt to the router */ if(s2s->sx_ssl == NULL && s2s->router_pemfile != NULL) { - s2s->sx_ssl = sx_env_plugin(s2s->sx_env, sx_ssl_init, s2s->router_pemfile, NULL, NULL); + s2s->sx_ssl = sx_env_plugin(s2s->sx_env, sx_ssl_init, NULL, s2s->router_pemfile, NULL, NULL); if(s2s->sx_ssl == NULL) { log_write(s2s->log, LOG_ERR, "failed to load router SSL pemfile, channel to router will not be SSL encrypted"); s2s->router_pemfile = NULL; } @@ -511,9 +570,13 @@ if(s2s->sx_sasl == NULL) { log_write(s2s->log, LOG_ERR, "failed to initialise SASL context, aborting"); exit(1); } - + + /* hosts mapping */ + s2s->hosts = xhash_new(1021); + _s2s_hosts_expand(s2s); + s2s->sx_db = sx_env_plugin(s2s->sx_env, s2s_db_init); s2s->mio = mio_new(s2s->io_max_fds); @@ -664,8 +727,9 @@ xhash_free(s2s->out); xhash_free(s2s->in); xhash_free(s2s->in_accept); xhash_free(s2s->dnscache); + xhash_free(s2s->hosts); prep_cache_free(s2s->pc); jqueue_free(s2s->dead); diff -r -U4 jabberd-2.1.23/s2s/out.c jabberd-2.1.23+sa/s2s/out.c --- jabberd-2.1.23/s2s/out.c 2008-02-03 14:23:07.000000000 +0000 +++ jabberd-2.1.23+sa/s2s/out.c 2008-05-06 20:35:40.000000000 +0100 @@ -283,9 +283,9 @@ #ifdef HAVE_SSL /* Send a stream version of 1.0 if we can do STARTTLS */ if(out->s2s->sx_ssl != NULL && out->s2s->local_pemfile != NULL) { - sx_client_init(out->s, S2S_DB_HEADER, uri_SERVER, pkt->to->domain, NULL, "1.0"); + sx_client_init(out->s, S2S_DB_HEADER, uri_SERVER, pkt->to->domain, pkt->from->domain, "1.0"); } else { sx_client_init(out->s, S2S_DB_HEADER, uri_SERVER, NULL, NULL, NULL); } #else diff -r -U4 jabberd-2.1.23/s2s/s2s.h jabberd-2.1.23+sa/s2s/s2s.h --- jabberd-2.1.23/s2s/s2s.h 2008-02-03 14:23:07.000000000 +0000 +++ jabberd-2.1.23+sa/s2s/s2s.h 2008-05-06 18:57:47.000000000 +0100 @@ -32,13 +32,28 @@ # include #endif /* forward decl */ +typedef struct host_st *host_t; typedef struct s2s_st *s2s_t; typedef struct pkt_st *pkt_t; typedef struct conn_st *conn_t; typedef struct dnscache_st *dnscache_t; +struct host_st { + /** our realm */ + char *realm; + + /** starttls pemfile */ + char *host_pemfile; + + /** certificate chain */ + char *host_cachain; + + /** verify-mode */ + int host_verify_mode; +}; + struct s2s_st { /** our id (hostname) with the router */ char *id; @@ -105,8 +120,11 @@ /** verify-mode */ int local_verify_mode; + /** hosts mapping */ + xht hosts; + /** max file descriptors */ int io_max_fds; /** maximum stanza size */ diff -r -U4 jabberd-2.1.23/sm/main.c jabberd-2.1.23+sa/sm/main.c --- jabberd-2.1.23/sm/main.c 2008-02-03 14:23:06.000000000 +0000 +++ jabberd-2.1.23+sa/sm/main.c 2008-05-06 00:01:39.000000000 +0100 @@ -309,9 +309,9 @@ sm->sx_env = sx_env_new(); #ifdef HAVE_SSL if(sm->router_pemfile != NULL) { - sm->sx_ssl = sx_env_plugin(sm->sx_env, sx_ssl_init, sm->router_pemfile, NULL, NULL); + sm->sx_ssl = sx_env_plugin(sm->sx_env, sx_ssl_init, NULL, sm->router_pemfile, NULL, NULL); if(sm->sx_ssl == NULL) { log_write(sm->log, LOG_ERR, "failed to load SSL pemfile, SSL disabled"); sm->router_pemfile = NULL; } diff -r -U4 jabberd-2.1.23/sx/plugins.h jabberd-2.1.23+sa/sx/plugins.h --- jabberd-2.1.23/sx/plugins.h 2008-02-03 14:23:07.000000000 +0000 +++ jabberd-2.1.23+sa/sx/plugins.h 2008-05-05 22:46:42.000000000 +0100 @@ -59,8 +59,11 @@ /** init function */ JABBERD2_API int sx_ssl_init(sx_env_t env, sx_plugin_t p, va_list args); +/** add cert function */ +JABBERD2_API int sx_ssl_server_addcert(sx_plugin_t p, char *name, char *pemfile, char *cachain, int mode); + /** trigger for client starttls */ JABBERD2_API int sx_ssl_client_starttls(sx_plugin_t p, sx_t s, char *pemfile); /* previous states */ diff -r -U4 jabberd-2.1.23/sx/ssl.c jabberd-2.1.23+sa/sx/ssl.c --- jabberd-2.1.23/sx/ssl.c 2008-02-03 14:23:07.000000000 +0000 +++ jabberd-2.1.23+sa/sx/ssl.c 2008-05-06 20:44:09.000000000 +0100 @@ -66,12 +66,22 @@ return preverify_ok; } static void _sx_ssl_starttls_notify_proceed(sx_t s, void *arg) { + char *to = NULL; _sx_debug(ZONE, "preparing for starttls"); + /* store the destination so we can select an ssl context */ + if(s->req_to != NULL) to = strdup(s->req_to); + _sx_reset(s); + /* restore destination */ + if(s->req_to == NULL) + s->req_to = to; + else /* ? */ + free(to); + /* start listening */ sx_server_init(s, s->flags | SX_SSL_WRAPPER); } @@ -482,25 +492,36 @@ } static void _sx_ssl_client(sx_t s, sx_plugin_t p) { _sx_ssl_conn_t sc; + SSL_CTX *ctx; char *pemfile = NULL; int ret; /* only bothering if they asked for wrappermode */ if(!(s->flags & SX_SSL_WRAPPER) || s->ssf > 0) return; - _sx_debug(ZONE, "preparing for ssl connect for %d", s->tag); + _sx_debug(ZONE, "preparing for ssl connect for %d from %s", s->tag, s->req_from); + + /* find the ssl context for this source */ + ctx = xhash_get((xht) p->private, s->req_from); + if(ctx == NULL) { + _sx_debug(ZONE, "using default ssl context for %d", s->tag); + ctx = xhash_get((xht) p->private, "*"); + } else { + _sx_debug(ZONE, "using configured ssl context for %d", s->tag); + } + assert((int) (ctx != NULL)); sc = (_sx_ssl_conn_t) calloc(1, sizeof(struct _sx_ssl_conn_st)); /* create the buffers */ sc->rbio = BIO_new(BIO_s_mem()); sc->wbio = BIO_new(BIO_s_mem()); /* new ssl conn */ - sc->ssl = SSL_new((SSL_CTX *) p->private); + sc->ssl = SSL_new(ctx); SSL_set_bio(sc->ssl, sc->rbio, sc->wbio); SSL_set_connect_state(sc->ssl); SSL_set_ssl_method(sc->ssl, TLSv1_client_method()); @@ -563,23 +584,34 @@ } static void _sx_ssl_server(sx_t s, sx_plugin_t p) { _sx_ssl_conn_t sc; + SSL_CTX *ctx; /* only bothering if they asked for wrappermode */ if(!(s->flags & SX_SSL_WRAPPER) || s->ssf > 0) return; - _sx_debug(ZONE, "preparing for ssl accept for %d", s->tag); + _sx_debug(ZONE, "preparing for ssl accept for %d to %s", s->tag, s->req_to); + + /* find the ssl context for this destination */ + ctx = xhash_get((xht) p->private, s->req_to); + if(ctx == NULL) { + _sx_debug(ZONE, "using default ssl context for %d", s->tag); + ctx = xhash_get((xht) p->private, "*"); + } else { + _sx_debug(ZONE, "using configured ssl context for %d", s->tag); + } + assert((int) (ctx != NULL)); sc = (_sx_ssl_conn_t) calloc(1, sizeof(struct _sx_ssl_conn_st)); /* create the buffers */ sc->rbio = BIO_new(BIO_s_mem()); sc->wbio = BIO_new(BIO_s_mem()); /* new ssl conn */ - sc->ssl = SSL_new((SSL_CTX *) p->private); + sc->ssl = SSL_new(ctx); SSL_set_bio(sc->ssl, sc->rbio, sc->wbio); SSL_set_accept_state(sc->ssl); /* empty external_id */ @@ -626,22 +658,31 @@ s->plugin_data[p->index] = NULL; } static void _sx_ssl_unload(sx_plugin_t p) { - SSL_CTX_free((SSL_CTX *) p->private); + xht contexts = (xht) p->private; + void *ctx; + + if(xhash_iter_first(contexts)) + do { + xhash_iter_get(contexts, NULL, &ctx); + SSL_CTX_free((SSL_CTX *) ctx); + } while(xhash_iter_next(contexts)); + + xhash_free(contexts); } int sx_openssl_initialized = 0; -/** args: pemfile, cachain, mode */ +/** args: name, pemfile, cachain, mode */ int sx_ssl_init(sx_env_t env, sx_plugin_t p, va_list args) { - char *pemfile, *cachain; - SSL_CTX *ctx; + char *name, *pemfile, *cachain; int ret; int mode; _sx_debug(ZONE, "initialising ssl plugin"); + name = va_arg(args, char *); pemfile = va_arg(args, char *); if(pemfile == NULL) return 1; @@ -653,13 +694,53 @@ /* !!! output openssl error messages to the debug log */ /* openssl startup */ - SSL_library_init(); - SSL_load_error_strings(); - + if(!sx_openssl_initialized) { + SSL_library_init(); + SSL_load_error_strings(); + } sx_openssl_initialized = 1; + ret = sx_ssl_server_addcert(p, name, pemfile, cachain, mode); + if(ret) + return 1; + + p->magic = SX_SSL_MAGIC; + + p->unload = _sx_ssl_unload; + + p->client = _sx_ssl_client; + p->server = _sx_ssl_server; + p->rio = _sx_ssl_rio; + p->wio = _sx_ssl_wio; + p->features = _sx_ssl_features; + p->process = _sx_ssl_process; + p->free = _sx_ssl_free; + + return 0; +} + +/** args: name, pemfile, cachain, mode */ +int sx_ssl_server_addcert(sx_plugin_t p, char *name, char *pemfile, char *cachain, int mode) { + xht contexts = (xht) p->private; + SSL_CTX *ctx; + SSL_CTX *tmp; + int ret; + + if(!sx_openssl_initialized) { + _sx_debug(ZONE, "ssl plugin not initialised"); + return 1; + } + + if(name == NULL) + name = "*"; + + if(pemfile == NULL) + return 1; + + /* !!! output openssl error messages to the debug log */ + /* create the context */ ctx = SSL_CTX_new(SSLv23_method()); if(ctx == NULL) { _sx_debug(ZONE, "ssl context creation failed"); @@ -697,27 +778,37 @@ SSL_CTX_free(ctx); return 1; } - _sx_debug(ZONE, "Setting verify mode to %02x", mode); + _sx_debug(ZONE, "setting ssl context '%s' verify mode to %02x", name, mode); SSL_CTX_set_verify(ctx, mode, _sx_ssl_verify_callback); - /* its good */ - _sx_debug(ZONE, "ssl context initialised; certificate and key loaded from %s", pemfile); - - p->magic = SX_SSL_MAGIC; + /* create hash and create default context */ + if(contexts == NULL) { + contexts = xhash_new(1021); + p->private = (void *) contexts; + + /* this is the first context, if it's not the default then make a copy of it as the default */ + if(!(name[0] == '*' && name[1] == 0)) { + int ret = sx_ssl_server_addcert(p, "*", pemfile, cachain, mode); + + if(ret) { + /* uh-oh */ + xhash_free(contexts); + p->private = NULL; + return 1; + } + } + } - p->private = (void *) ctx; + _sx_debug(ZONE, "ssl context '%s' initialised; certificate and key loaded from %s", name, pemfile); - p->unload = _sx_ssl_unload; + /* remove an existing context with the same name before replacing it */ + tmp = xhash_get(contexts, name); + if(tmp != NULL) + SSL_CTX_free((SSL_CTX *) tmp); - p->client = _sx_ssl_client; - p->server = _sx_ssl_server; - p->rio = _sx_ssl_rio; - p->wio = _sx_ssl_wio; - p->features = _sx_ssl_features; - p->process = _sx_ssl_process; - p->free = _sx_ssl_free; + xhash_put(contexts, name, ctx); return 0; }