]> git.ipfire.org Git - thirdparty/freeradius-server.git/commitdiff
look for CoA requests from a home server, and send replies
authorAlan T. DeKok <aland@freeradius.org>
Wed, 31 Mar 2021 18:41:17 +0000 (14:41 -0400)
committerAlan T. DeKok <aland@freeradius.org>
Fri, 2 Apr 2021 19:13:05 +0000 (15:13 -0400)
src/include/radiusd.h
src/main/listen.c
src/main/tls_listen.c

index 81490a3396296bd8d2df4482103606470ad012d7..b737b420b372b98dceb3907b209c6ef6dcab264f 100644 (file)
@@ -606,6 +606,7 @@ int dual_tls_send(rad_listen_t *listener, REQUEST *request);
 int proxy_tls_recv(rad_listen_t *listener);
 int proxy_tls_send(rad_listen_t *listener, REQUEST *request);
 #ifdef WITH_COA_TUNNEL
+int proxy_tls_send_reply(rad_listen_t *listener, REQUEST *request);
 int dual_tls_send_coa_request(rad_listen_t *listener, REQUEST *request);
 #endif
 #endif
index f665b4120b9f21cb598a58ec56df70fa0297fc48..110b4f53a36a73717dde3f79daad111e822139fc 100644 (file)
@@ -748,7 +748,7 @@ static int dual_tcp_accept(rad_listen_t *listener)
                this->proxy_encode = master_listen[RAD_LISTEN_PROXY].encode;
                this->proxy_decode = master_listen[RAD_LISTEN_PROXY].decode;
 
-               // @todo - save this listener as being outbound to the NAS IP
+//             listener_store_byaddr(this, &sock->other_ipaddr);
 
                /*
                 *      Automatically create a home server for this
@@ -2947,6 +2947,42 @@ rad_listen_t *proxy_new_listener(TALLOC_CTX *ctx, home_server_t *home, uint16_t
 
                this->recv = proxy_tls_recv;
                this->proxy_send = proxy_tls_send;
+
+#ifdef WITH_COA_TUNNEL
+               if (home->recv_coa) {
+                       RADCLIENT *client;
+
+                       /*
+                        *      Don't set this->send_coa, as we are
+                        *      not sending CoA-Request packets to
+                        *      this home server.  Instead, we are
+                        *      receiving CoA packets from this home
+                        *      server.
+                        */
+                       this->send = proxy_tls_send_reply;
+                       this->encode = master_listen[RAD_LISTEN_AUTH].encode;
+                       this->decode = master_listen[RAD_LISTEN_AUTH].decode;
+
+                       /*
+                        *      Automatically create a client for this
+                        *      home server.  There MAY be one already
+                        *      one for that IP in the configuration
+                        *      files, but there's no guarantee that
+                        *      it exists.
+                        *
+                        *      The only real reason to use an
+                        *      existing client is to track various
+                        *      statistics.
+                        */
+                       sock->client = client = talloc_zero(sock, RADCLIENT);
+                       client->ipaddr = sock->other_ipaddr;
+                       client->src_ipaddr = sock->my_ipaddr;
+                       client->longname = client->shortname = talloc_typed_strdup(client, home->name);
+                       client->secret = talloc_typed_strdup(client, home->secret);
+                       client->nas_type = "none";
+                       client->server = talloc_typed_strdup(client, home->coa_server);
+               }
+#endif
        }
 #endif
 #endif
index 30b64140a27034e5bc4d3c168059ff3f2108e918..2d7b9b9cf0725b5c262050ffe3898174aee3cd7d 100644 (file)
@@ -847,6 +847,10 @@ int proxy_tls_recv(rad_listen_t *listener)
        RADIUS_PACKET *packet;
        uint8_t *data;
        ssize_t data_len;
+#ifdef WITH_COA_TUNNEL
+       bool is_request = false;
+       RADCLIENT *client = sock->client;
+#endif
 
        if (listener->status != RAD_LISTEN_STATUS_KNOWN) return 0;
 
@@ -900,9 +904,26 @@ int proxy_tls_recv(rad_listen_t *listener)
        case PW_CODE_DISCONNECT_ACK:
        case PW_CODE_DISCONNECT_NAK:
                break;
+
+#ifdef WITH_COA_TUNNEL
+       case PW_CODE_COA_REQUEST:
+               if (!listener->send_coa) goto bad_packet;
+               FR_STATS_INC(coa, total_requests);
+               is_request = true;
+               break;
+
+       case PW_CODE_DISCONNECT_REQUEST:
+               if (!listener->send_coa) goto bad_packet;
+               FR_STATS_INC(dsc, total_requests);
+               is_request = true;
+               break;
+#endif
 #endif
 
        default:
+#ifdef WITH_COA_TUNNEL
+       bad_packet:
+#endif
                /*
                 *      FIXME: Update MIB for packet types?
                 */
@@ -915,6 +936,15 @@ int proxy_tls_recv(rad_listen_t *listener)
                return 0;
        }
 
+#ifdef WITH_COA_TUNNEL
+       if (is_request) {
+               if (!request_receive(NULL, listener, packet, client, rad_coa_recv)) {
+                       FR_STATS_INC(auth, total_packets_dropped);
+                       rad_free(&packet);
+                       return 0;
+               }
+       } else
+#endif
        if (!request_proxy_reply(packet)) {
                rad_free(&packet);
                return 0;
@@ -985,6 +1015,71 @@ int proxy_tls_send(rad_listen_t *listener, REQUEST *request)
 
        return 1;
 }
+
+#ifdef WITH_COA_TUNNEL
+int proxy_tls_send_reply(rad_listen_t *listener, REQUEST *request)
+{
+       int rcode;
+       listen_socket_t *sock = listener->data;
+
+       VERIFY_REQUEST(request);
+
+       rad_assert(sock->ssn->connected);
+
+       if ((listener->status != RAD_LISTEN_STATUS_INIT &&
+           (listener->status != RAD_LISTEN_STATUS_KNOWN))) return 0;
+
+       /*
+        *      Pack the VPs
+        */
+       if (rad_encode(request->reply, request->packet,
+                      request->client->secret) < 0) {
+               RERROR("Failed encoding packet: %s", fr_strerror());
+               return 0;
+       }
+
+       if (request->reply->data_len > (MAX_PACKET_LEN - 100)) {
+               RWARN("Packet is large, and possibly truncated - %zd vs max %d",
+                     request->reply->data_len, MAX_PACKET_LEN);
+       }
+
+       /*
+        *      Sign the packet.
+        */
+       if (rad_sign(request->reply, request->packet,
+                      request->client->secret) < 0) {
+               RERROR("Failed signing packet: %s", fr_strerror());
+               return 0;
+       }
+
+       DEBUG3("Proxy is writing %u bytes to SSL",
+              (unsigned int) request->reply->data_len);
+       PTHREAD_MUTEX_LOCK(&sock->mutex);
+       rcode = SSL_write(sock->ssn->ssl, request->reply->data,
+                         request->reply->data_len);
+       if (rcode < 0) {
+               int err;
+
+               err = ERR_get_error();
+               switch (err) {
+               case SSL_ERROR_NONE:
+               case SSL_ERROR_WANT_READ:
+               case SSL_ERROR_WANT_WRITE:
+                       break;  /* let someone else retry */
+
+               default:
+                       tls_error_log(NULL, "Failed in proxy send");
+                       DEBUG("Closing TLS socket to home server");
+                       tls_socket_close(listener);
+                       PTHREAD_MUTEX_UNLOCK(&sock->mutex);
+                       return 0;
+               }
+       }
+       PTHREAD_MUTEX_UNLOCK(&sock->mutex);
+
+       return 1;
+}
+#endif /* WITH_COA_TUNNEL */
 #endif /* WITH_PROXY */
 
 #endif /* WITH_TLS */