]> git.ipfire.org Git - thirdparty/freeradius-server.git/commitdiff
last bit of magic to make it work
authorAlan T. DeKok <aland@freeradius.org>
Wed, 12 Apr 2023 14:05:44 +0000 (10:05 -0400)
committerAlan T. DeKok <aland@freeradius.org>
Wed, 12 Apr 2023 14:05:44 +0000 (10:05 -0400)
the client calls SSL_get0_alpn_selected()

src/include/listen.h
src/main/listen.c
src/main/tls_listen.c

index 723cea46e40959815607d40ee57a2f41d26da990..a82b91d2962eaec079faf7ed7a5d31086e381400 100644 (file)
@@ -195,6 +195,7 @@ typedef struct listen_socket_t {
        } state;
 
 #ifdef WITH_RADIUSV11
+       bool            alpn_checked;
        bool            radiusv11;              //!< defaults to "no"!
 #endif
 #endif
index 2c7d337cdc9fe097310825e545bf81e1a89a17c0..2adfef31db06ce188bb81774612324180c40f41a 100644 (file)
@@ -769,7 +769,7 @@ static int radiusv11_server_alpn_cb(SSL *ssl,
        }
 
        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]);
        }
 
        /*
@@ -785,7 +785,7 @@ static int radiusv11_server_alpn_cb(SSL *ssl,
                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;
        }
 
@@ -796,95 +796,88 @@ static int radiusv11_server_alpn_cb(SSL *ssl,
        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
@@ -1089,7 +1082,6 @@ static int dual_tcp_accept(rad_listen_t *listener)
                         *      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 {
@@ -1506,10 +1498,6 @@ int common_socket_parse(CONF_SECTION *cs, rad_listen_t *this)
                                }
 
                                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
                }
@@ -3403,6 +3391,15 @@ rad_listen_t *proxy_new_listener(TALLOC_CTX *ctx, home_server_t *home, uint16_t
                        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;
index 255b80735486a45d4988c49594d0f0bd06d9297c..5bab84a690513167e103e4816683cd6641d047c3 100644 (file)
@@ -1064,11 +1064,15 @@ static int try_connect(listen_socket_t *sock)
        }
 
        sock->ssn->connected = true;
-       return 1;
+               return 1;
 }
 
 
 #ifdef WITH_PROXY
+#ifdef WITH_RADIUSV11
+extern int fr_radiusv11_client_get_alpn(rad_listen_t *listener);
+#endif
+
 /*
  *     Read from the SSL socket.  Safe with either blocking or
  *     non-blocking IO.  This level of complexity is probably not
@@ -1095,6 +1099,14 @@ static ssize_t proxy_tls_read(rad_listen_t *listener)
                        radius_update_listener(listener);
                        return rcode;
                }
+
+#ifdef WITH_RADIUSV11
+               if (!sock->alpn_checked && (fr_radiusv11_client_get_alpn(listener) < 0)) {
+                       listener->status = RAD_LISTEN_STATUS_EOL;
+                       radius_update_listener(listener);
+                       return -1;
+               }
+#endif
        }
 
        /*
@@ -1362,16 +1374,13 @@ int proxy_tls_send(rad_listen_t *listener, REQUEST *request)
                        radius_update_listener(listener);
                        return rcode;
                }
-#ifdef WITH_RADIUSV11
-       } else if ((listener->radiusv11 == FR_RADIUSV11_REQUIRE) &&
-                  !sock->radiusv11) {
-
-                       DEBUG("(TLS) We have 'radiusv11 = require', but the home server has not negotiated it - closing socket");
 
-                       PTHREAD_MUTEX_LOCK(&TLS_MUTEX);
-                       tls_socket_close(listener);
-                       PTHREAD_MUTEX_UNLOCK(&TLS_MUTEX);
-                       return 0;
+#ifdef WITH_RADIUSV11
+               if (!sock->alpn_checked && (fr_radiusv11_client_get_alpn(listener) < 0)) {
+                       listener->status = RAD_LISTEN_STATUS_EOL;
+                       radius_update_listener(listener);
+                       return -1;
+               }
 #endif
        }