]> git.ipfire.org Git - thirdparty/freeradius-server.git/commitdiff
rearrange negotiation of radiusv11 so that it works
authorAlan T. DeKok <aland@freeradius.org>
Tue, 11 Apr 2023 13:49:09 +0000 (09:49 -0400)
committerAlan T. DeKok <aland@freeradius.org>
Tue, 11 Apr 2023 13:49:09 +0000 (09:49 -0400)
src/main/listen.c

index ab85e587f261680ae2b3ecf9059d6460f2d9aedd..ead48fbdfe79c0f30a1279d62f34cd6b81b40b6f 100644 (file)
@@ -708,6 +708,116 @@ static int tls_sni_callback(SSL *ssl, UNUSED int *al, void *arg)
 }
 #endif
 
+#ifdef WITH_RADIUSV11
+static const unsigned char radiusv11_allow_protos[] = {
+       10, 'r', 'a', 'd', 'i', 'u', 's', '/', '1', '.', '0',
+       10, 'r', 'a', 'd', 'i', 'u', 's', '/', '1', '.', '1',
+};
+
+static const unsigned char radiusv11_require_protos[] = {
+       10, 'r', 'a', 'd', 'i', 'u', 's', '/', '1', '.', '1',
+};
+
+/*
+ *     On the server, get the ALPN list requested by the client.
+ */
+static int radiusv11_server_alpn_cb(UNUSED SSL *ssl,
+                                   const unsigned char **out,
+                                   unsigned char *outlen,
+                                   const unsigned char *in,
+                                   unsigned int inlen,
+                                   void *arg)
+{
+       rad_listen_t *this = arg;
+       listen_socket_t *sock = this->data;
+       const unsigned char *server, *client;
+       unsigned int server_len, i, j;
+
+       fr_assert(inlen > 0);
+
+       /*
+        *      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 */
+
+       case FR_RADIUSV11_ALLOW:
+               server = radiusv11_allow_protos;
+               server_len = sizeof(radiusv11_allow_protos);
+               break;
+
+       case FR_RADIUSV11_REQUIRE:
+               server = radiusv11_require_protos;
+               server_len = sizeof(radiusv11_require_protos);
+               break;
+       }
+
+       /*
+        *      SSL_select_next_proto() doesn't quite give us what we need, which is an easy way to know which
+        *      protocol was selected.
+        */
+       client = in;
+       for (i = 0; i < inlen; i += client[0] + 1) {
+               const unsigned char *s;
+
+               client = in + i;
+
+               if ((client + client[0] + 1) > (in + inlen)) break;
+
+               for (j = 0; j < server_len; j += s[0] + 1) {
+                       s = server + j;
+
+                       if (client[0] != s[0]) continue;
+
+                       if (memcmp(client + 1, s + 1, s[0]) != 0) continue;
+
+                      *out = s;
+                      *outlen = s[0] + 1;
+
+                      /*
+                       *       Tell our socket which protocol we negotiated.
+                       */
+                      fr_assert(s[0] == 10);
+                      sock->radiusv11 = (s[10] == '1');
+
+                      DEBUG("(TLS) Negotiated ALPN as %.*s", (int) s[0], s + 1);
+                      return SSL_TLSEXT_ERR_OK;
+               }
+       }
+
+       /*
+        *      No common ALPN.
+        */
+       DEBUG("(TLS) no common radiusv11 settings");
+       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;
+
+       /*
+        *      The OpenSSL functions randonly take "const" (or not).
+        */
+       memcpy(&my_out, &out, sizeof(out)); /* const issues */
+
+       return radiusv11_server_alpn_cb(ssl, my_out, outlen, in, inlen, arg);
+}
+#endif
+
+
 static int dual_tcp_accept(rad_listen_t *listener)
 {
        int newfd;
@@ -904,11 +1014,15 @@ static int dual_tcp_accept(rad_listen_t *listener)
 
 #ifdef WITH_RADIUSV11
                        /*
-                        *      We've checked above for conflicts between require/forbid and forbid/require.
-                        *      So either the settings agree (forbid/forbid, require/require), OR this listener
-                        *      is marker "allow".  In which case we just use whatever the client has set.
+                        *      Default is "forbid" (0).  In which case we don't set any ALPN callbacks, and
+                        *      the ServerHello does not contain an ALPN section.
                         */
-                       this->radiusv11 = client->radiusv11;
+                       if (client->radiusv11 != FR_RADIUSV11_FORBID) {
+                               SSL_CTX_set_alpn_select_cb(this->tls->ctx, radiusv11_server_alpn_cb, this);
+                               DEBUG("(TLS) LISTEN Maybe negotiating radiusv11");
+                       } else {
+                               DEBUG("(TLS) LISTEN Not doing radiusv11");
+                       }
 #endif
                }
 #endif
@@ -1184,111 +1298,6 @@ static int listener_unlink(UNUSED void *ctx, UNUSED void *data)
 }
 #endif
 
-#ifdef WITH_RADIUSV11
-static const unsigned char radiusv11_allow_protos[] = {
-       10, 'r', 'a', 'd', 'i', 'u', 's', '/', '1', '.', '0',
-       10, 'r', 'a', 'd', 'i', 'u', 's', '/', '1', '.', '1',
-};
-
-static const unsigned char radiusv11_require_protos[] = {
-       10, 'r', 'a', 'd', 'i', 'u', 's', '/', '1', '.', '1',
-};
-
-/*
- *     On the server, get the ALPN list requested by the client.
- */
-static int radiusv11_server_alpn_cb(UNUSED SSL *ssl,
-                                   const unsigned char **out,
-                                   unsigned char *outlen,
-                                   const unsigned char *in,
-                                   unsigned int inlen,
-                                   void *arg)
-{
-       rad_listen_t *this = arg;
-       listen_socket_t *sock = this->data;
-       const unsigned char *server, *client;
-       unsigned int server_len, i, j;
-
-       fr_assert(inlen > 0);
-
-       if (inlen > 2) inlen = 2; /* catch crazy clients. */
-
-       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 */
-
-       case FR_RADIUSV11_ALLOW:
-               server = radiusv11_allow_protos;
-               server_len = sizeof(radiusv11_allow_protos);
-               break;
-
-       case FR_RADIUSV11_REQUIRE:
-               server = radiusv11_require_protos;
-               server_len = sizeof(radiusv11_require_protos);
-               break;
-       }
-
-       /*
-        *      SSL_select_next_proto() doesn't quite give us what we need, which is an easy way to know which
-        *      protocol was selected.
-        */
-       client = in;
-       for (i = 0; i < inlen; i += client[0] + 1) {
-               const unsigned char *s;
-
-               client = in + i;
-
-               if ((client + client[0] + 1) > (in + inlen)) break; /* paranoia */
-
-               for (j = 0; j < server_len; j += s[0] + 1) {
-                       s = server + j;
-
-                       if (client[0] != s[0]) continue;
-
-                       if (memcmp(client + 1, s + 1, s[0]) != 0) continue;
-
-                      *out = s;
-                      *outlen = s[0] + 1;
-
-                      /*
-                       *       Tell our socket which protocol we negotiated.
-                       */
-                      fr_assert(s[0] == 10);
-                      sock->radiusv11 = (s[10] == '1');
-
-                      return SSL_TLSEXT_ERR_OK;
-               }
-       }
-
-       /*
-        *      No common ALPN.
-        */
-       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;
-
-       /*
-        *      The OpenSSL functions randonly take "const" (or not).
-        */
-       memcpy(&my_out, &out, sizeof(out)); /* const issues */
-
-       return radiusv11_server_alpn_cb(ssl, my_out, outlen, in, inlen, arg);
-}
-#endif
-
 
 /*
  *     Parse an authentication or accounting socket.
@@ -3286,6 +3295,38 @@ rad_listen_t *proxy_new_listener(TALLOC_CTX *ctx, home_server_t *home, uint16_t
                        (void) SSL_set_tlsext_host_name(sock->ssn->ssl, (void *) (uintptr_t) home->tls->client_hostname);
                }
 
+#ifdef WITH_RADIUSV11
+               /*
+                *      Tell the TLS code that we have a list of ALPN protocols to send over, and set the
+                *      callbacks to decide what we negotiated.
+                *
+                *      @todo - this code should arguably be in src/main/realms.c, as manging SSL_CTX isn't thread-safe.
+                */
+               this->radiusv11 = home->tls->radiusv11;
+               switch (home->tls->radiusv11) {
+               case FR_RADIUSV11_ALLOW:
+                       if (SSL_CTX_set_alpn_protos(home->tls->ctx, radiusv11_allow_protos, sizeof(radiusv11_allow_protos)) != 0) {
+                       fail_protos:
+                               ERROR("Failed setting RADIUSv11 negotiation flags");
+                               listen_free(&this);
+                               return 0;
+                       }
+                       SSL_CTX_set_next_proto_select_cb(home->tls->ctx, radiusv11_client_alpn_cb, this);
+                       DEBUG("(TLS) LISTEN-PROXY Maybe negotiating allow radiusv11");
+                       break;
+
+               case FR_RADIUSV11_REQUIRE:
+                       if (SSL_CTX_set_alpn_protos(home->tls->ctx, radiusv11_require_protos, sizeof(radiusv11_require_protos)) != 0) goto fail_protos;
+                       SSL_CTX_set_next_proto_select_cb(home->tls->ctx, radiusv11_client_alpn_cb, this);
+                       DEBUG("(TLS) LISTEN-PROXY Maybe negotiating require radiusv11");
+                       break;
+
+               default:
+                       DEBUG("(TLS) LISTEN-PROXY Forbidding radiusv11");
+                       break;
+               }
+#endif
+
                this->nonblock |= home->nonblock;
 
                /*
@@ -3327,30 +3368,6 @@ rad_listen_t *proxy_new_listener(TALLOC_CTX *ctx, home_server_t *home, uint16_t
                }
 #endif
 
-#ifdef WITH_RADIUSV11
-               /*
-                *      Tell the TLS code that we have a list of ALPN protocols to send over.
-                */
-               switch (this->radiusv11) {
-               case FR_RADIUSV11_ALLOW:
-                       if (SSL_set_alpn_protos(sock->ssn->ssl, radiusv11_allow_protos, sizeof(radiusv11_allow_protos)) != 0) {
-                       fail_protos:
-                               ERROR("Failed setting RADIUSv11 negotiation flags");
-                               listen_free(&this);
-                               return 0;
-                       }
-                       break;
-
-               case FR_RADIUSV11_REQUIRE:
-                       if (SSL_set_alpn_protos(sock->ssn->ssl, radiusv11_require_protos, sizeof(radiusv11_require_protos)) != 0) goto fail_protos;
-                       break;
-
-
-               default:
-                       break;
-               }
-#endif
-
                /*
                 *      Make sure that this listener is associated with the home server.
                 *