]> git.ipfire.org Git - thirdparty/freeswitch.git/commitdiff
mod_rayo: support secure client-to-server connections
authorChris Rienzo <chris.rienzo@grasshopper.com>
Mon, 15 Jul 2013 22:52:09 +0000 (18:52 -0400)
committerChris Rienzo <chris.rienzo@grasshopper.com>
Mon, 15 Jul 2013 22:52:09 +0000 (18:52 -0400)
conf/rayo/autoload_configs/rayo.conf.xml
src/mod/event_handlers/mod_rayo/conf/autoload_configs/rayo.conf.xml
src/mod/event_handlers/mod_rayo/iks_helpers.h
src/mod/event_handlers/mod_rayo/mod_rayo.c
src/mod/event_handlers/mod_rayo/xmpp_streams.c
src/mod/event_handlers/mod_rayo/xmpp_streams.h

index f9ac49b99dce36e11eb4386e07518f5504e5e122..0cb46d799262b21646c77b426e930ed0ab03592c 100644 (file)
                <param name="record-file-prefix" value="$${recordings_dir}/"/>
        </record>
 
-       <!-- domain to use in from attribute of client messages -->
+       <!-- XMPP server domain -->
        <domain name="$${rayo_domain_name}" shared-secret="ClueCon">
+       <!-- use this instead if you want secure XMPP client to server connections.  Put .crt and .key file in freeswitch/certs -->
+       <!--domain name="$${rayo_domain_name}" shared-secret="ClueCon" cert="$${base_dir}/certs/$${rayo_domain_name}.crt" key="$${base_dir}/certs/$${rayo_domain_name}.key"-->
 
                <!-- Listeners for new Rayo client connections -->
                <!--listen type="c2s" port="5222" address="$${local_ip_v4}" acl="rayo-clients"/-->
index f9ac49b99dce36e11eb4386e07518f5504e5e122..0cb46d799262b21646c77b426e930ed0ab03592c 100644 (file)
                <param name="record-file-prefix" value="$${recordings_dir}/"/>
        </record>
 
-       <!-- domain to use in from attribute of client messages -->
+       <!-- XMPP server domain -->
        <domain name="$${rayo_domain_name}" shared-secret="ClueCon">
+       <!-- use this instead if you want secure XMPP client to server connections.  Put .crt and .key file in freeswitch/certs -->
+       <!--domain name="$${rayo_domain_name}" shared-secret="ClueCon" cert="$${base_dir}/certs/$${rayo_domain_name}.crt" key="$${base_dir}/certs/$${rayo_domain_name}.key"-->
 
                <!-- Listeners for new Rayo client connections -->
                <!--listen type="c2s" port="5222" address="$${local_ip_v4}" acl="rayo-clients"/-->
index 4e21113452594b34b8a777bc4428d338759211c7..442722a267b5ebb9c1845f50aa3780e28df6fc52 100644 (file)
@@ -39,8 +39,7 @@
 #define IKS_NS_XMPP_STANZAS "urn:ietf:params:xml:ns:xmpp-stanzas"
 #define IKS_NS_XMPP_STREAMS "http://etherx.jabber.org/streams"
 #define IKS_NS_XMPP_DIALBACK "jabber:server:dialback"
-#define IKS_NS_BIDI_FEATURE "urn:xmpp:features:bidi"
-#define IKS_NS_BIDI "urn:xmpp:bidi"
+#define IKS_NS_XMPP_TLS "urn:ietf:params:xml:ns:xmpp-tls"
 
 struct xmpp_error {
        const char *name;
index 24b187d7393d1104452a85c85e53d79e5e6385e0..a110cdb703c65c2b57bdd60ad8eaa097a7bbade4 100644 (file)
@@ -3079,6 +3079,8 @@ static switch_status_t do_config(switch_memory_pool_t *pool, const char *config_
                        switch_xml_t l;
                        const char *shared_secret = switch_xml_attr_soft(domain, "shared-secret");
                        const char *name = switch_xml_attr_soft(domain, "name");
+                       const char *cert = switch_xml_attr_soft(domain, "cert");
+                       const char *key = switch_xml_attr_soft(domain, "key");
                        if (zstr(name)) {
                                switch_log_printf(SWITCH_CHANNEL_LOG, SWITCH_LOG_ERROR, "Missing <domain name=\"... failed to configure rayo server\n");
                                status = SWITCH_STATUS_FALSE;
@@ -3093,6 +3095,14 @@ static switch_status_t do_config(switch_memory_pool_t *pool, const char *config_
                        globals.xmpp_context = xmpp_stream_context_create(name, shared_secret, on_xmpp_stream_ready, on_xmpp_stream_recv, on_xmpp_stream_destroy);
                        globals.server = rayo_server_create(name);
 
+                       /* set up TLS */
+                       if (!zstr(cert)) {
+                               xmpp_stream_context_add_cert(globals.xmpp_context, cert);
+                       }
+                       if (!zstr(key)) {
+                               xmpp_stream_context_add_key(globals.xmpp_context, key);
+                       }
+
                        /* configure authorized users for this domain */
                        l = switch_xml_child(domain, "users");
                        if (l) {
@@ -3719,14 +3729,14 @@ SWITCH_MODULE_SHUTDOWN_FUNCTION(mod_rayo_shutdown)
        switch_event_unbind_callback(on_call_end_event);
        switch_event_unbind_callback(route_mixer_event);
 
-        switch_core_hash_destroy(&globals.command_handlers);
-        switch_core_hash_destroy(&globals.event_handlers);
-        switch_core_hash_destroy(&globals.clients_roster);
-        switch_core_hash_destroy(&globals.actors);
-        switch_core_hash_destroy(&globals.destroy_actors);
-        switch_core_hash_destroy(&globals.actors_by_id);
-        switch_core_hash_destroy(&globals.dial_gateways);
-        switch_core_hash_destroy(&globals.cmd_aliases);
+       switch_core_hash_destroy(&globals.command_handlers);
+       switch_core_hash_destroy(&globals.event_handlers);
+       switch_core_hash_destroy(&globals.clients_roster);
+       switch_core_hash_destroy(&globals.actors);
+       switch_core_hash_destroy(&globals.destroy_actors);
+       switch_core_hash_destroy(&globals.actors_by_id);
+       switch_core_hash_destroy(&globals.dial_gateways);
+       switch_core_hash_destroy(&globals.cmd_aliases);
 
        switch_log_printf(SWITCH_CHANNEL_LOG, SWITCH_LOG_DEBUG, "Module shutdown\n");
 
index f050ab65e08e6b328dca722ae2792613b483c92e..033fdb8fb491eb5ff1ccc3b42850b8072e7b44af 100644 (file)
@@ -29,6 +29,8 @@
 #include <switch.h>
 #include <iksemel.h>
 
+#include <openssl/ssl.h>
+
 #include "xmpp_streams.h"
 #include "iks_helpers.h"
 #include "sasl.h"
@@ -63,6 +65,10 @@ struct xmpp_stream_context {
        int shutdown;
        /** prevents context shutdown until all threads are finished */
        switch_thread_rwlock_t *shutdown_rwlock;
+       /** path to cert PEM file */
+       const char *cert_pem_file;
+       /** path to key PEM file */
+       const char *key_pem_file;
 };
 
 /**
@@ -71,8 +77,8 @@ struct xmpp_stream_context {
 enum xmpp_stream_state {
        /** new connection */
        XSS_CONNECT,
-       /** bidirectional comms established */
-       XSS_BIDI,
+       /** encrypted comms established */
+       XSS_SECURE,
        /** remote party authenticated */
        XSS_AUTHENTICATED,
        /** client resource bound */
@@ -159,7 +165,7 @@ static const char *xmpp_stream_state_to_string(enum xmpp_stream_state state)
 {
        switch(state) {
                case XSS_CONNECT: return "CONNECT";
-               case XSS_BIDI: return "BIDI";
+               case XSS_SECURE: return "SECURE";
                case XSS_AUTHENTICATED: return "AUTHENTICATED";
                case XSS_RESOURCE_BOUND: return "RESOURCE_BOUND";
                case XSS_READY: return "READY";
@@ -358,6 +364,31 @@ static void xmpp_send_client_header_auth(struct xmpp_stream *stream)
        free(header);
 }
 
+/**
+ * Send sasl + starttls reply to xmpp <stream>
+ * @param stream the xmpp stream
+ */
+static void xmpp_send_client_header_tls(struct xmpp_stream *stream)
+{
+       if (stream->context->key_pem_file && stream->context->cert_pem_file) {
+               struct xmpp_stream_context *context = stream->context;
+               char *header = switch_mprintf(
+                       "<stream:stream xmlns='"IKS_NS_CLIENT"' xmlns:db='"IKS_NS_XMPP_DIALBACK"'"
+                       " from='%s' id='%s' xml:lang='en' version='1.0'"
+                       " xmlns:stream='"IKS_NS_XMPP_STREAMS"'><stream:features>"
+                       "<starttls xmlns='"IKS_NS_XMPP_TLS"'><required/></starttls>"
+                       "<mechanisms xmlns='"IKS_NS_XMPP_SASL"'>"
+                       "<mechanism>PLAIN</mechanism>"
+                       "</mechanisms></stream:features>", context->domain, stream->id);
+               iks_send_raw(stream->parser, header);
+               free(header);
+       } else {
+               /* not set up for TLS, skip it */
+               stream->state = XSS_SECURE;
+               xmpp_send_client_header_auth(stream);
+       }
+}
+
 /**
  * Send sasl reply to xmpp <stream>
  * @param stream the xmpp stream
@@ -370,12 +401,6 @@ static void xmpp_send_server_header_auth(struct xmpp_stream *stream)
                " from='%s' id='%s' xml:lang='en' version='1.0'"
                " xmlns:stream='"IKS_NS_XMPP_STREAMS"'>"
                "<stream:features>"
-#if 0
-               "<bidi xmlns='"IKS_NS_BIDI_FEATURE"'/>"
-               "<mechanisms xmlns='"IKS_NS_XMPP_SASL"'>"
-               "<mechanism>PLAIN</mechanism>"
-               "</mechanisms>"
-#endif
                "</stream:features>",
                context->domain, stream->id);
        iks_send_raw(stream->parser, header);
@@ -417,6 +442,21 @@ static void xmpp_send_outbound_server_header(struct xmpp_stream *stream)
        free(header);
 }
 
+/**
+ * Handle <starttls> message.
+ * @param the xmpp stream
+ * @param node the <starttls> packet
+ */
+static void on_stream_starttls(struct xmpp_stream *stream, iks *node)
+{
+       /* wait for handshake to start */
+       if (iks_proceed_tls(stream->parser, stream->context->cert_pem_file, stream->context->key_pem_file, 1) == IKS_OK) {
+               stream->state = XSS_SECURE;
+       } else {
+               stream->state = XSS_ERROR;
+       }
+}
+
 /**
  * Handle <auth> message.  Only PLAIN supported.
  * @param stream the xmpp stream
@@ -431,7 +471,7 @@ static void on_stream_auth(struct xmpp_stream *stream, iks *node)
        switch_log_printf(SWITCH_CHANNEL_UUID_LOG(stream->id), SWITCH_LOG_DEBUG, "%s, auth, state = %s\n", stream->jid, xmpp_stream_state_to_string(stream->state));
 
        /* wrong state for authentication */
-       if (stream->state != XSS_BIDI) {
+       if (stream->state != XSS_SECURE) {
                switch_log_printf(SWITCH_CHANNEL_UUID_LOG(stream->id), SWITCH_LOG_WARNING, "%s, auth UNEXPECTED, state = %s\n", stream->jid, xmpp_stream_state_to_string(stream->state));
                /* on_auth unexpected error */
                stream->state = XSS_ERROR;
@@ -486,38 +526,6 @@ static void on_stream_auth(struct xmpp_stream *stream, iks *node)
        }
 }
 
-/**
- * Handle <bidi> message.
- * @param stream the xmpp stream
- * @param node the <bidi> packet
- */
-static void on_stream_bidi(struct xmpp_stream *stream, iks *node)
-{
-       /* only allow bidi on s2s connections before auth */
-       if (stream->s2s) {
-               switch(stream->state) {
-                       case XSS_CONNECT:
-                               stream->state = XSS_BIDI;
-                               break;
-                       case XSS_BIDI:
-                       case XSS_AUTHENTICATED:
-                       case XSS_RESOURCE_BOUND:
-                       case XSS_READY:
-                       case XSS_SHUTDOWN:
-                       case XSS_ERROR:
-                       case XSS_DESTROY:
-                               /* error */
-                               stream->state = XSS_ERROR;
-                               switch_log_printf(SWITCH_CHANNEL_UUID_LOG(stream->id), SWITCH_LOG_INFO, "%s, bad state: %s\n", stream->jid, xmpp_stream_state_to_string(stream->state));
-                               break;
-               }
-       } else {
-               /* error */
-               stream->state = XSS_ERROR;
-               switch_log_printf(SWITCH_CHANNEL_UUID_LOG(stream->id), SWITCH_LOG_INFO, "%s, bidi not allowed from client\n", stream->jid);
-       }
-}
-
 /**
  * Handle <iq><session> request
  * @param stream the xmpp stream
@@ -606,7 +614,7 @@ static void on_stream_iq(struct xmpp_stream *stream, iks *iq)
        struct xmpp_stream_context *context = stream->context;
        switch(stream->state) {
                case XSS_CONNECT:
-               case XSS_BIDI: {
+               case XSS_SECURE: {
                        iks *error = iks_new_error(iq, STANZA_ERROR_NOT_AUTHORIZED);
                        xmpp_stream_stanza_send(stream, error);
                        break;
@@ -689,7 +697,9 @@ static void on_client_stream_start(struct xmpp_stream *stream, iks *node)
 
        switch (stream->state) {
                case XSS_CONNECT:
-               case XSS_BIDI:
+                       xmpp_send_client_header_tls(stream);
+                       break;
+               case XSS_SECURE:
                        xmpp_send_client_header_auth(stream);
                        break;
                case XSS_AUTHENTICATED:
@@ -960,7 +970,7 @@ static void on_outbound_server_stream_start(struct xmpp_stream *stream, iks *nod
                        /* strange... I expect IKS_NODE_STOP, this is a workaround. */
                        stream->state = XSS_DESTROY;
                        break;
-               case XSS_BIDI:
+               case XSS_SECURE:
                case XSS_AUTHENTICATED:
                case XSS_RESOURCE_BOUND:
                case XSS_READY:
@@ -1001,7 +1011,7 @@ static void on_inbound_server_stream_start(struct xmpp_stream *stream, iks *node
                case XSS_CONNECT:
                        xmpp_send_server_header_auth(stream);
                        break;
-               case XSS_BIDI:
+               case XSS_SECURE:
                        break;
                case XSS_AUTHENTICATED: {
                        /* all set */
@@ -1075,8 +1085,8 @@ static int on_stream(void *user_data, int type, iks *node)
                                        on_stream_presence(stream, node);
                                } else if (!strcmp("auth", name)) {
                                        on_stream_auth(stream, node);
-                               } else if (!strcmp("bidi", name)) {
-                                       on_stream_bidi(stream, node);
+                               } else if (!strcmp("starttls", name)) {
+                                       on_stream_starttls(stream, node);
                                } else if (!strcmp("db:result", name)) {
                                        on_stream_dialback_result(stream, node);
                                } else if (!strcmp("db:verify", name)) {
@@ -1194,6 +1204,7 @@ static void *SWITCH_THREAD_FUNC xmpp_stream_thread(switch_thread_t *thread, void
                case IKS_OK:
                        err_count = 0;
                        break;
+               case IKS_NET_TLSFAIL:
                case IKS_NET_RWERR:
                case IKS_NET_NOCONN:
                case IKS_NET_NOSOCK:
@@ -1291,11 +1302,6 @@ static struct xmpp_stream *xmpp_stream_init(struct xmpp_stream_context *context,
        stream->incoming = incoming;
        switch_queue_create(&stream->msg_queue, MAX_QUEUE_LEN, pool);
 
-       if (!stream->s2s) {
-               /* client is already bi-directional */
-               stream->state = XSS_BIDI;
-       }
-
        /* set up XMPP stream parser */
        stream->parser = iks_stream_new(stream->s2s ? IKS_NS_SERVER : IKS_NS_CLIENT, stream, on_stream);
 
@@ -1829,6 +1835,23 @@ void *xmpp_stream_get_private(struct xmpp_stream *stream)
        return stream->user_private;
 }
 
+/**
+ * Add PEM cert file to stream for new SSL connections
+ */
+void xmpp_stream_context_add_cert(struct xmpp_stream_context *context, const char *cert_pem_file)
+{
+       context->cert_pem_file = switch_core_strdup(context->pool, cert_pem_file);
+}
+
+/**
+ * Add PEM key file to stream for new SSL connections
+ */
+void xmpp_stream_context_add_key(struct xmpp_stream_context *context, const char *key_pem_file)
+{
+       context->key_pem_file = switch_core_strdup(context->pool, key_pem_file);
+}
+
+
 /* For Emacs:
  * Local Variables:
  * mode:c
index d4ba66324624381d07c26160247db7f62d4918bc..a07c75d025c0c59c41f65daa168ba917cd8715da 100644 (file)
@@ -37,6 +37,8 @@ typedef void (* xmpp_stream_recv_callback)(struct xmpp_stream *stream, iks *stan
 typedef void (* xmpp_stream_destroy_callback)(struct xmpp_stream *stream);
 
 extern struct xmpp_stream_context *xmpp_stream_context_create(const char *domain, const char *domain_secret, xmpp_stream_ready_callback ready, xmpp_stream_recv_callback recv, xmpp_stream_destroy_callback destroy);
+extern void xmpp_stream_context_add_cert(struct xmpp_stream_context *context, const char *cert_pem_file);
+extern void xmpp_stream_context_add_key(struct xmpp_stream_context *context, const char *key_pem_file);
 extern void xmpp_stream_context_add_user(struct xmpp_stream_context *context, const char *user, const char *password);
 extern void xmpp_stream_context_dump(struct xmpp_stream_context *context, switch_stream_handle_t *stream);
 extern void xmpp_stream_context_destroy(struct xmpp_stream_context *context);