--- /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);