See raddb/certs/realms/README.md
* Add Server Name Indication (SNI) for outbound RadSec connections.
See raddb/sites-available/tls, and the home server tls configuration.
+ * Support SNI for inbound RadSec connections. Certificates will
+ be loaded from "realm_dir" in the "tls" section. SNI will be
+ cached in the TLS-Server-Name-Indication attribute.
Bug fixes
* Fix crash in trustrouter module (#4115). Patch from Alejandro Perez
EAP.
This means that the server can accept RadSec connections, and then
-present different server certificates to different clients. However,
-there is currently no standard way for RadSec clients to send a Server
-Name Indicator (SNI) as with HTTPS. As a result, certificate
-selection has to be done on something else, such as source IP address.
+present different server certificates to different clients.
+
+For this functionality to work, the certificates for EAP and RadSec
+*should* be in separate directories.
+
+### Clients
+
+RadSec clients can set the SNI to send in the `tls` subsection of the
+`home_server` definition. See `sites-available/tls` for examples.
+
+### Servers
+
+See the `realm_dir` configuration item in the `tls` subsection for the
+location of the server certificates.
+
+If the server receives an SNI for a realm it does not recognize, it
+will just use the default TLS configuration.
+
+If the realm is recognized (i.e. there is a file in
+`${realm_dir}/%{TLS-Server-Name-Indication}.pem`, then that certificate will be chosen, and
+present to the RadSec client. If there is no such file, then the
+default TLS configuration is used.
+
+The current behavior is to _require_ that the server certificate is in
+a file which taken from
+`${realm_dir}/%{TLS-Server-Name-Indication}.pem`. Only the
+`realm_dir` portion of the filename is configurable. The SNI portion
+is taken from the TLS messages, and the `.pem` suffix is hard-coded in
+the source code.
return 1;
}
+typedef struct {
+ char const *name;
+ SSL_CTX *ctx;
+} fr_realm_ctx_t; /* hack from tls. */
+
+static int tls_sni_callback(SSL *ssl, UNUSED int *al, void *arg)
+{
+ fr_tls_server_conf_t *conf = arg;
+ char const *name, *p;
+ int type;
+ fr_realm_ctx_t my_r, *r;
+ REQUEST *request;
+ char buffer[PATH_MAX];
+
+ /*
+ * No SNI, that's fine.
+ */
+ type = SSL_get_servername_type(ssl);
+ if (type < 0) return SSL_TLSEXT_ERR_OK;
+
+ /*
+ * No realms configured, just use the default context.
+ */
+ if (!conf->realms) return SSL_TLSEXT_ERR_OK;
+
+ name = SSL_get_servername(ssl, TLSEXT_NAMETYPE_host_name);
+ if (!name) return SSL_TLSEXT_ERR_OK;
+
+ /*
+ * RFC Section 6066 Section 3 says that the names are
+ * ASCII, without a trailing dot. i.e. punycode.
+ */
+ for (p = name; *p != '\0'; p++) {
+ if (*p == '-') continue;
+ if (*p == '.') continue;
+ if ((*p >= 'A') && (*p <= 'Z')) continue;
+ if ((*p >= 'a') && (*p <= 'z')) continue;
+ if ((*p >= '0') && (*p <= '9')) continue;
+
+ /*
+ * Anything else, ignore it.
+ */
+ return SSL_TLSEXT_ERR_OK;
+ }
+
+ snprintf(buffer, sizeof(buffer), "%s/%s.pem", conf->realm_dir, name);
+
+ my_r.name = buffer;
+ r = fr_hash_table_finddata(conf->realms, &my_r);
+ if (!r) return SSL_TLSEXT_ERR_OK;
+
+ /*
+ * Set an attribute saying which server has been selected.
+ */
+ request = (REQUEST *)SSL_get_ex_data(ssl, FR_TLS_EX_INDEX_REQUEST);
+ if (request) {
+ (void) pair_make_config("TLS-Server-Name-Indication", name, T_OP_SET);
+ }
+
+ (void) SSL_set_SSL_CTX(ssl, r->ctx);
+ return SSL_TLSEXT_ERR_OK;
+}
static int dual_tcp_accept(rad_listen_t *listener)
{
#ifdef WITH_TLS
if (this->tls) {
+ /*
+ * Set up SNI callback. We don't do it
+ * in the main TLS code, because EAP
+ * doesn't need or use SNI.
+ */
+ SSL_CTX_set_tlsext_servername_callback(this->tls->ctx, tls_sni_callback);
+ SSL_CTX_set_tlsext_servername_arg(this->tls->ctx, this->tls);
+
this->recv = dual_tls_recv;
this->send = dual_tls_send;
}