}
for (i = 0; i < inlen; i += in[0] + 1) {
- RDEBUG("(TLS) ALPN sent by client is %.*s", in[i], &in[i + 1]);
+ RDEBUG("(TLS) ALPN sent by client is \"%.*s\"", in[i], &in[i + 1]);
}
/*
fr_assert(*outlen == 10);
sock->radiusv11 = (server[9] == '1');
- RDEBUG("(TLS) ALPN negotiated application protocol %.*s", (int) *outlen, server);
+ RDEBUG("(TLS) ALPN server negotiated application protocol \"%.*s\"", (int) *outlen, server);
return SSL_TLSEXT_ERR_OK;
}
return SSL_TLSEXT_ERR_ALERT_FATAL;
}
-static int radiusv11_client_alpn_cb(UNUSED SSL *ssl,
- unsigned char **out,
- unsigned char *outlen,
- const unsigned char *in,
- unsigned int inlen,
- void *arg)
-{
- const unsigned char **my_out;
+int fr_radiusv11_client_init(fr_tls_server_conf_t *tls);
+int fr_radiusv11_client_get_alpn(rad_listen_t *listener);
- /*
- * The OpenSSL functions randonly take "const" (or not).
- */
- memcpy(&my_out, &out, sizeof(out)); /* const issues */
+int fr_radiusv11_client_init(fr_tls_server_conf_t *tls)
+{
+ switch (tls->radiusv11) {
+ case FR_RADIUSV11_ALLOW:
+ if (SSL_CTX_set_alpn_protos(tls->ctx, radiusv11_allow_protos, sizeof(radiusv11_allow_protos)) != 0) {
+ fail_protos:
+ ERROR("Failed setting RADIUSv11 negotiation flags");
+ return -1;
+ }
+ break;
- // @todo - get this listener from SSL context, and then pass it as "arg".
+ case FR_RADIUSV11_REQUIRE:
+ if (SSL_CTX_set_alpn_protos(tls->ctx, radiusv11_require_protos, sizeof(radiusv11_require_protos)) != 0) goto fail_protos;
+ break;
- fr_assert(0);
+ default:
+ break;
+ }
- return radiusv11_server_alpn_cb(ssl, my_out, outlen, in, inlen, arg);
+ return 0;
}
-
-static int radiusv11_server_advertised_cb(UNUSED SSL *ssl,
- const unsigned char **out,
- unsigned int *outlen,
- void *arg)
+int fr_radiusv11_client_get_alpn(rad_listen_t *listener)
{
- rad_listen_t *this = arg;
+ const unsigned char *data;
+ unsigned int len;
+ listen_socket_t *sock = listener->data;
- fr_assert(0);
+ SSL_get0_alpn_selected(sock->ssn->ssl, &data, &len);
+ if (!data) {
+ DEBUG("(TLS) ALPN server did not send any application protocol");
+ if (listener->radiusv11 == FR_RADIUSV11_REQUIRE) {
+ DEBUG("(TLS) We have 'radiusv11 = require', but the home server has not negotiated it - closing socket");
+ return -1;
+ }
- /*
- * The RADIUSv11 configuration for this socket is a combination of what we require, and what we
- * require of the client.
- */
- switch (this->radiusv11) {
- /*
- * If we forbid RADIUSv11, then we never advertised it via ALPN, and this callback should
- * never have been registered.
- */
- case FR_RADIUSV11_FORBID:
- fr_assert(0);
- return SSL_TLSEXT_ERR_NOACK; /* no ALPN was negotiated for this connection */
+ return 0; /* allow radius/1.0 */
+ }
- case FR_RADIUSV11_ALLOW:
- *out = radiusv11_allow_protos;
- *outlen = sizeof(radiusv11_allow_protos);
- break;
+ DEBUG("(TLS) ALPN server sent application protocol \"%.*s\"", (int) len, data);
- case FR_RADIUSV11_REQUIRE:
- *out = radiusv11_require_protos;
- *outlen = sizeof(radiusv11_require_protos);
- break;
+ if (len != 10) {
+ radiusv11_unknown:
+ DEBUG("(TLS) ALPN unknown application protocol - closing connection to home server");
+ return -1;
}
- return SSL_TLSEXT_ERR_OK;
-}
+ /*
+ * Should always be "radius/1.0" or "radius/1.1"
+ */
+ if (memcmp(data, "radius/1.", 9) != 0) goto radiusv11_unknown;
-/*
- * Tell the TLS code that we have a list of ALPN protocols to send over, and set the
- * callbacks to decide what we negotiated.
- *
- * However, the OpenSSL API allows setting the callback only for the SSL_CTX, and not for
- * this particular SSL session. So we have to associate this listener with the SSL*, and then
- * the radiusv11_client_alpn_cb() will retrieve the listener from SSL*, and then do the actual work.
- */
-int fr_radiusv11_client_init(fr_tls_server_conf_t *tls);
+ if ((data[9] != '0') && (data[9] != '1')) goto radiusv11_unknown;
-int fr_radiusv11_client_init(fr_tls_server_conf_t *tls)
-{
- switch (tls->radiusv11) {
- case FR_RADIUSV11_ALLOW:
- if (SSL_CTX_set_alpn_protos(tls->ctx, radiusv11_allow_protos, sizeof(radiusv11_allow_protos)) != 0) {
- fail_protos:
- ERROR("Failed setting RADIUSv11 negotiation flags");
+
+ switch (listener->radiusv11) {
+ case FR_RADIUSV11_FORBID:
+ if (data[9] != '0') {
+ DEBUG("(TLS) ALPN is not \"radius/v1.0\" - closing connection to home server");
return -1;
}
- SSL_CTX_set_next_proto_select_cb(tls->ctx, radiusv11_client_alpn_cb, NULL);
break;
- case FR_RADIUSV11_REQUIRE:
- if (SSL_CTX_set_alpn_protos(tls->ctx, radiusv11_require_protos, sizeof(radiusv11_require_protos)) != 0) goto fail_protos;
- SSL_CTX_set_next_proto_select_cb(tls->ctx, radiusv11_client_alpn_cb, NULL);
+ case FR_RADIUSV11_ALLOW:
+ sock->radiusv11 = (data[9] == '1');
break;
+
+ case FR_RADIUSV11_REQUIRE:
+ if (data[9] != '1') {
+ DEBUG("(TLS) ALPN is not \"radius/v1.1\" - closing connection to home server");
+ return -1;
+ }
- default:
+ sock->radiusv11 = true;
break;
+
}
+ sock->alpn_checked = true;
return 0;
}
#endif
* the ServerHello does not contain an ALPN section.
*/
if (client->radiusv11 != FR_RADIUSV11_FORBID) {
- SSL_CTX_set_next_protos_advertised_cb(this->tls->ctx, radiusv11_server_advertised_cb, this);
SSL_CTX_set_alpn_select_cb(this->tls->ctx, radiusv11_server_alpn_cb, this);
DEBUG("(TLS) ALPN radiusv11 = allow / require");
} else {
}
this->radiusv11 = this->tls->radiusv11 = rcode;
-
- if (this->type != RAD_LISTEN_PROXY) {
- SSL_CTX_set_next_protos_advertised_cb(this->tls->ctx, radiusv11_server_advertised_cb, this);
- }
}
#endif
}
goto error;
}
+#ifdef WITH_RADIUSV11
+ /*
+ * Must not have alpn_checked yet. This code only runs for blocking sockets.
+ */
+ if (sock->ssn->connected && (fr_radiusv11_client_get_alpn(this) < 0)) {
+ goto error;
+ }
+#endif
+
sock->connect_timeout = home->connect_timeout;
this->recv = proxy_tls_recv;