main.c.patch   [plain text]


--- /tmp/jabberd-2.1.24.1/s2s/main.c	2008-04-27 02:57:31.000000000 -0700
+++ ./jabberd2/s2s/main.c	2009-06-08 17:53:19.000000000 -0700
@@ -65,6 +65,7 @@ static void _s2s_pidfile(s2s_t s2s) {
 static void _s2s_config_expand(s2s_t s2s) {
     char *str, secret[41];
     int i, r;
+    config_elem_t elem;
 
     s2s->id = config_get_one(s2s->config, "id", 0);
     if(s2s->id == NULL)
@@ -85,6 +86,10 @@ static void _s2s_config_expand(s2s_t s2s
 
     s2s->router_pemfile = config_get_one(s2s->config, "router.pemfile", 0);
 
+    s2s->router_cachain = config_get_one(s2s->config, "router.cachain", 0);
+
+    s2s->router_private_key_password = config_get_one(s2s->config, "router.private_key_password", 0);
+
     s2s->retry_init = j_atoi(config_get_one(s2s->config, "router.retry.init", 0), 3);
     s2s->retry_lost = j_atoi(config_get_one(s2s->config, "router.retry.lost", 0), 3);
     if((s2s->retry_sleep = j_atoi(config_get_one(s2s->config, "router.retry.sleep", 0), 2)) < 1)
@@ -141,10 +146,21 @@ static void _s2s_config_expand(s2s_t s2s
     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_private_key_password = config_get_one(s2s->config, "local.private_key_password", 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);
 
+    s2s->require_tls = j_atoi(config_get_one(s2s->config, "security.require_tls", 0), 0);
+    s2s->enable_whitelist = j_atoi(config_get_one(s2s->config, "security.enable_whitelist", 0), 0);
+    if((elem = config_get(s2s->config, "security.whitelist_domain")) != NULL) {
+        _s2s_populate_whitelist_domains(s2s, elem->values, elem->nvalues);
+    }
+
+
     s2s->check_interval = j_atoi(config_get_one(s2s->config, "check.interval", 0), 60);
     s2s->check_queue = j_atoi(config_get_one(s2s->config, "check.queue", 0), 60);
     s2s->check_keepalive = j_atoi(config_get_one(s2s->config, "check.keepalive", 0), 0);
@@ -375,6 +391,191 @@ static void _s2s_time_checks(s2s_t s2s) 
     return;
 }
 
+/* Populate the whitelist_domains array with the config file values */
+int _s2s_populate_whitelist_domains(s2s_t s2s, char **values, int nvalues) {
+	int i, j;
+	int elem_len;
+    s2s->whitelist_domains = (char **)malloc(sizeof(char*) * (nvalues));
+	memset(s2s->whitelist_domains, 0, (sizeof(char *) * (nvalues)));	
+    for (i = 0, j = 0; i < nvalues; i++) {
+		elem_len = strlen(values[i]);
+        if (elem_len > MAX_DOMAIN_LEN) {
+			log_debug(ZONE, "whitelist domain element is too large, skipping");
+			continue;
+        }
+        if (elem_len == 0) {
+			log_debug(ZONE, "whitelist domain element is blank, skipping");
+            continue;
+        }
+        s2s->whitelist_domains[j] = (char *) malloc(sizeof(char) * (elem_len+1));
+		memset(s2s->whitelist_domains[j], 0, (sizeof(char) * (elem_len+1)));
+        strncpy(s2s->whitelist_domains[j], values[i], elem_len);
+		s2s->whitelist_domains[j][elem_len+1] = '\0';
+        log_debug(ZONE, "s2s whitelist domain read from file: %s\n", s2s->whitelist_domains[j]);
+		j++;
+    }
+
+	s2s->n_whitelist_domains = j;
+	log_debug(ZONE, "n_whitelist_domains = %d", s2s->n_whitelist_domains);
+    return 0;
+}
+
+
+/* Compare a domain with whitelist values.
+	The whitelist values may be FQDN or domain only (with no prepended hostname).
+	returns 1 on match, 0 on failure to match
+*/
+int s2s_domain_in_whitelist(s2s_t s2s, char *in_domain) {
+	int segcount = 0;
+	int dotcount;
+	char **segments = NULL;
+	char **dst = NULL;
+	char *seg_tmp = NULL;	
+	int seg_tmp_len;
+	char matchstr[MAX_DOMAIN_LEN + 1];
+	int domain_index;
+	int x, i;
+	int wl_index;
+	int wl_len;
+	int matchstr_len;
+	char domain[strlen(in_domain)+1];
+	char *domain_ptr = &domain[0];
+	int domain_len;
+
+	strlcpy((char *)&domain, in_domain, sizeof(domain));
+	domain_len = strlen((const char *)&domain);
+
+	if (domain_len <= 0) {
+		log_write(s2s->log, LOG_NOTICE, "s2s_domain_in_whitelist: in_domain is empty");
+		return 0;
+	}
+
+	if (domain_len > MAX_DOMAIN_LEN) {
+		log_write(s2s->log, LOG_NOTICE, "s2s_domain_in_whitelist: in_domain is longer than %s chars", MAX_DOMAIN_LEN);
+		return 0;
+	}
+
+	// first try matching the FQDN with whitelist domains
+	if (s2s->n_whitelist_domains <= 0)
+		return 0;
+
+	for (wl_index =0; wl_index < s2s->n_whitelist_domains; wl_index++) {
+		wl_len = strlen(s2s->whitelist_domains[wl_index]);
+		if (!strncmp((const char *)&domain, s2s->whitelist_domains[wl_index], (domain_len > wl_len) ? domain_len : wl_len)) {
+			log_debug(ZONE, "domain \"%s\" matches whitelist entry", &domain);
+			return 1;
+		}
+		else {
+			//log_debug(ZONE, "domain: %s (len %d) does not match whitelist_domains[%d]: %s (len %d)", &domain, strlen((const char *)&domain), wl_index, s2s->whitelist_domains[wl_index], strlen(s2s->whitelist_domains[wl_index]));
+		}
+	}
+
+	// break domain into segments for domain-only comparision
+	for (dotcount = 0, x = 0; domain[x] != '\0'; x++) {
+		if (domain[x] == '.')
+			dotcount++;
+	}
+		
+	segments = (char **)malloc(sizeof(char*) * (dotcount + 1));
+	if (segments == NULL) {
+		log_write(s2s->log, LOG_ERR, "s2s_domain_in_whitelist: malloc() error");
+		return 0;
+	}
+	memset((char **)segments, 0, (sizeof(char*) * (dotcount + 1)));
+
+	do {
+		if (segcount > (dotcount+1)) {
+			log_write(s2s->log, LOG_ERR, "s2s_domain_in_whitelist: did not malloc enough room for domain segments; should never get here");
+			if (seg_tmp != NULL) {
+				free(seg_tmp);
+				seg_tmp = NULL;
+			}
+			for (x = 0; x < segcount; x++) {
+				free(segments[x]);
+				segments[x] = NULL;
+			}
+			free(segments);
+			segments = NULL;
+			return 0;
+		}
+		seg_tmp = strsep(&domain_ptr, ".");
+		if (seg_tmp == NULL) {
+			break;
+		}
+
+		seg_tmp_len = strlen(seg_tmp);
+		if (seg_tmp_len > MAX_DOMAIN_LEN) {
+			log_write(s2s->log, LOG_NOTICE, "s2s_domain_in_whitelist: domain contains a segment greater than %s chars", MAX_DOMAIN_LEN);
+			if (seg_tmp != NULL) {
+				free(seg_tmp);
+				seg_tmp = NULL;
+			}
+			for (x = 0; x < segcount; x++) {
+				free(segments[x]);
+				segments[x] = NULL;
+			}   
+			free(segments);
+			segments = NULL;
+			return 0;
+		}
+		dst = &segments[segcount];
+		*dst = (char *)malloc(seg_tmp_len + 1);
+		if (*dst != NULL) {
+			strlcpy(*dst, seg_tmp, seg_tmp_len + 1);
+		} else { 
+			if (seg_tmp != NULL) {
+				free(seg_tmp);
+				seg_tmp = NULL;
+			}
+			for (x = 0; x < segcount; x++) {
+				free(segments[x]);
+				segments[x] = NULL;
+			}   
+			free(segments);
+			segments = NULL;
+			log_write(s2s->log, LOG_ERR, "s2s_domain_in_whitelist: malloc() error");
+			return 0;
+		}
+		segcount++;
+    } while (seg_tmp != NULL);
+
+	if (segcount > 1) {
+		for (domain_index = segcount-2; domain_index > 0; domain_index--) {
+			matchstr[0] = '\0';
+			for (i = domain_index; i < segcount; i++) {
+				if (i > domain_index)
+					strlcat((char *)&matchstr, ".", sizeof(matchstr));
+				strlcat((char *)&matchstr, (char *)segments[i], sizeof(matchstr));
+			}
+			for (wl_index = 0; wl_index < s2s->n_whitelist_domains; wl_index++) {
+				wl_len = strlen(s2s->whitelist_domains[wl_index]);
+				matchstr_len = strlen((const char *)&matchstr);
+				if (!strncmp((const char *)&matchstr, s2s->whitelist_domains[wl_index], (wl_len > matchstr_len ? wl_len : matchstr_len))) {
+					log_debug(ZONE, "matchstr \"%s\" matches whitelist entry", &matchstr);
+					for (x = 0; x < segcount; x++) {
+						free(segments[x]);
+						segments[x] = NULL;
+					}   
+					free(segments);
+					segments = NULL;
+					return 1;
+				} 
+				else { 
+					//log_debug(ZONE, "matchstr: %s (len %d) does not match whitelist_domains[%d]: %s (len %d)", &matchstr, strlen((const char *)&matchstr), wl_index, s2s->whitelist_domains[wl_index], strlen(s2s->whitelist_domains[wl_index]));
+				}
+			}
+		}
+    }
+	for (x = 0; x < segcount; x++) {
+		free(segments[x]);
+		segments[x] = NULL;
+	}   
+	free(segments);
+	segments = NULL;
+
+	return 0;	
+}
+
 JABBER_MAIN("jabberd2s2s", "Jabber 2 S2S", "Jabber Open Source Server: Server to Server", "jabberd2router\0")
 {
     s2s_t s2s;
@@ -486,25 +687,23 @@ JABBER_MAIN("jabberd2s2s", "Jabber 2 S2S
 
 #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, s2s->local_pemfile, s2s->local_cachain, s2s->local_verify_mode, s2s->local_private_key_password);
 
         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;
         } else
             log_debug(ZONE, "loaded pemfile for SSL connections to peers");
-    }
 
     /* 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, s2s->router_pemfile, s2s->router_cachain, NULL, s2s->router_private_key_password);
         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;
         }
     }
-#endif
+#endif // HAVE_SSL
 
     /* get sasl online */
     s2s->sx_sasl = sx_env_plugin(s2s->sx_env, sx_sasl_init, "xmpp", NULL, NULL);