]> git.ipfire.org Git - thirdparty/freeradius-server.git/commitdiff
it helps to commit files
authorAlan T. DeKok <aland@freeradius.org>
Tue, 8 Oct 2024 14:10:13 +0000 (10:10 -0400)
committerAlan T. DeKok <aland@freeradius.org>
Tue, 8 Oct 2024 14:10:13 +0000 (10:10 -0400)
src/protocols/radius/server.c [new file with mode: 0644]
src/protocols/radius/server.h [new file with mode: 0644]
src/protocols/radius/server_udp.c [new file with mode: 0644]
src/protocols/radius/server_udp.h [new file with mode: 0644]

diff --git a/src/protocols/radius/server.c b/src/protocols/radius/server.c
new file mode 100644 (file)
index 0000000..a81cd4f
--- /dev/null
@@ -0,0 +1,202 @@
+/*
+ *   This library is free software; you can redistribute it and/or
+ *   modify it under the terms of the GNU Lesser General Public
+ *   License as published by the Free Software Foundation; either
+ *   version 2.1 of the License, or (at your option) any later version.
+ *
+ *   This library is distributed in the hope that it will be useful,
+ *   but WITHOUT ANY WARRANTY; without even the implied warranty of
+ *   MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
+ *   Lesser General Public License for more details.
+ *
+ *   You should have received a copy of the GNU Lesser General Public
+ *   License along with this library; if not, write to the Free Software
+ *   Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301, USA
+ */
+
+/**
+ * $Id$
+ *
+ * @file protocols/radius/server.c
+ * @brief Functions to support RADIUS bio handlers for server sockets
+ *
+ * @copyright 2024 Network RADIUS SAS (legal@networkradius.com)
+ */
+RCSID("$Id$")
+
+#include <freeradius-devel/radius/server.h>
+#include <freeradius-devel/radius/server_udp.h>
+#include <freeradius-devel/radius/server_priv.h>
+
+fr_bio_packet_t *fr_radius_server_bio_alloc(TALLOC_CTX *ctx, fr_radius_server_config_t *cfg, fr_bio_fd_config_t const *fd_cfg)
+{
+       fr_assert(fd_cfg->type == FR_BIO_FD_UNCONNECTED); /* UDP sockets only for now */
+
+       if (fd_cfg->path || fd_cfg->filename) {
+               fr_strerror_const("Domain sockets and files are not supported");
+               return NULL;
+       }
+
+       if (fd_cfg->socket_type == SOCK_DGRAM) return fr_radius_server_udp_bio_alloc(ctx, cfg, fd_cfg);
+
+       fr_strerror_const("No TCP for you.");
+       return -1;
+
+//     return fr_radius_server_tcp_bio_alloc(ctx, cfg, fd_cfg);
+}
+
+static int _radius_server_fd_bio_free(fr_radius_server_fd_bio_t *my)
+{
+       if (fr_bio_shutdown(my->common.bio) < 0) return -1;
+
+       if (fr_bio_free(my->common.bio) < 0) return -1;
+
+       return 0;
+}
+
+
+fr_radius_server_fd_bio_t *fr_radius_server_fd_bio_alloc(TALLOC_CTX *ctx, size_t read_size, fr_radius_server_config_t *cfg, fr_bio_fd_config_t const *fd_cfg)
+{
+       fr_radius_server_fd_bio_t *my;
+
+       /*
+        *      For now we only support unconnected UDP server sockets.
+        *
+        *      Connected TCP server sockets require the ability to create new BIOs and add new sockets on the fly.
+        */
+       fr_assert(fd_cfg->type == FR_BIO_FD_UNCONNECTED);
+
+       my = talloc_zero(ctx, fr_radius_server_fd_bio_t);
+       if (!my) return NULL;
+
+       my->fd = fr_bio_fd_alloc(my, fd_cfg, 0);
+       if (!my->fd) {
+       fail:
+               talloc_free(my);
+               return NULL;
+       }
+
+       /*
+        *      So that read / write pause / resume callbacks can find us
+        */
+       my->fd->uctx = my;
+
+       my->info.fd_info = fr_bio_fd_info(my->fd);
+       fr_assert(my->info.fd_info != NULL);
+
+       my->mem = fr_bio_mem_alloc(my, read_size, 2 * 4096, my->fd);
+       if (!my->mem) goto fail;
+       my->mem->uctx = &my->cfg.verify;
+
+       my->cfg = *cfg;
+
+       my->common.bio = my->mem;
+
+       talloc_set_destructor(my, _radius_server_fd_bio_free);
+
+       return my;
+}
+
+int fr_radius_server_fd_bio_write(fr_bio_packet_t *bio, UNUSED void *pctx, fr_packet_t *reply, fr_pair_list_t *list)
+{
+       fr_radius_server_fd_bio_t *my = talloc_get_type_abort(bio, fr_radius_server_fd_bio_t);
+       fr_packet_t *request = reply->uctx;
+       ssize_t slen;
+       
+       fr_assert(!reply->data);
+
+       fr_assert(reply->code > 0);
+       fr_assert(reply->code < FR_RADIUS_CODE_MAX);
+
+       /*
+        *      Encode the packet.
+        */
+       if (fr_packet_encode(reply, list, request, (char const *) my->cfg.verify.secret) < 0) {
+       fail:
+               return fr_bio_error(GENERIC);
+       }
+
+       if (fr_packet_sign(reply, request, (char const *) my->cfg.verify.secret) < 0) goto fail;
+
+       slen = fr_bio_write(my->common.bio, &reply->socket, reply->data, reply->data_len);
+       if (slen < 0) {
+               fr_assert((slen != fr_bio_error(IO_WOULD_BLOCK)) || my->common.write_blocked);
+
+               return slen;
+       }
+
+       my->info.write_blocked = false;
+
+       return 0;
+}
+
+
+int fr_radius_server_fd_bio_read(fr_bio_packet_t *bio, UNUSED void **packet_ctx_p, fr_packet_t **packet_p,
+                                TALLOC_CTX *out_ctx, fr_pair_list_t *out)
+{
+       ssize_t slen;
+       fr_radius_server_fd_bio_t *my = talloc_get_type_abort(bio, fr_radius_server_fd_bio_t);
+       fr_packet_t *packet;
+
+       /*
+        *      We don't need to set up response.socket for connected bios.
+        */
+       fr_packet_t base = {};
+
+       /*
+        *      We read the response packet ctx into our local structure.  If we have a real response, we will
+        *      swap to using the request context, and not the response context.
+        */
+       slen = fr_bio_read(my->common.bio, &base, &my->buffer, sizeof(my->buffer));
+       if (!slen) return 0;
+
+       if (slen < 0) {
+               fr_assert(slen != fr_bio_error(IO_WOULD_BLOCK));
+               return slen;
+       }
+
+
+       /*
+        *      Allocate the packet data structure
+        */
+       packet = fr_packet_alloc(out_ctx, false);
+       if (!packet) return -1;
+
+       packet->data = talloc_memdup(packet, my->buffer, slen);
+       if (!packet->data) {
+               talloc_free(packet);
+               return -1;
+       }
+       packet->data_len = slen;
+
+       packet->code = packet->data[0];
+       packet->id = packet->data[1];
+       memcpy(packet->vector, packet->data + 4, sizeof(packet->vector));
+
+       /*
+        *      If this fails, we're out of memory.
+        */
+       if (fr_radius_decode_simple(packet, out, packet->data, packet->data_len,
+                                   NULL, (char const *) my->cfg.verify.secret) < 0) {
+               talloc_free(packet);
+               return -1;
+       }
+
+       *packet_p = packet;
+
+       return 1;
+}
+
+fr_bio_t *fr_radius_server_bio_get_fd(fr_bio_packet_t *bio)
+{
+       fr_radius_server_fd_bio_t *my = talloc_get_type_abort(bio, fr_radius_server_fd_bio_t);
+
+       return my->fd;
+}
+
+fr_radius_server_bio_info_t const *fr_radius_server_bio_info(fr_bio_packet_t *bio)
+{
+       fr_radius_server_fd_bio_t *my = talloc_get_type_abort(bio, fr_radius_server_fd_bio_t);
+
+       return &my->info;
+}
diff --git a/src/protocols/radius/server.h b/src/protocols/radius/server.h
new file mode 100644 (file)
index 0000000..97c8f24
--- /dev/null
@@ -0,0 +1,61 @@
+#pragma once
+/*
+ *  This program is free software; you can redistribute it and/or modify
+ *  it under the terms of the GNU General Public License as published by
+ *  the Free Software Foundation; either version 2 of the License, or
+ *  (at your option) any later version.
+ *
+ *  This program is distributed in the hope that it will be useful,
+ *  but WITHOUT ANY WARRANTY; without even the implied warranty of
+ *  MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
+ *  GNU General Public License for more details.
+ *
+ *  You should have received a copy of the GNU General Public License
+ *  along with this program; if not, write to the Free Software
+ *  Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301, USA
+ */
+
+/**
+ * $Id$
+ *
+ * @file protocols/radius/server.h
+ * @brief RADIUS bio handlers for outgoing RADIUS server sockets
+ *
+ * @copyright 2024 Network RADIUS SAS (legal@networkradius.com)
+ */
+RCSIDH(radius_server_h, "$Id$")
+
+#include <freeradius-devel/radius/radius.h>
+#include <freeradius-devel/radius/bio.h>
+#include <freeradius-devel/bio/packet.h>
+#include <freeradius-devel/bio/fd.h>
+#include <freeradius-devel/bio/dedup.h>
+
+typedef struct {
+       fr_log_t                *log;
+
+       fr_radius_bio_verify_t  verify;
+
+       fr_bio_dedup_config_t   dedup_cfg;
+
+       fr_bio_packet_cb_funcs_t packet_cb_cfg;
+} fr_radius_server_config_t;
+
+typedef struct {
+       bool                    connected;
+       bool                    write_blocked;
+       bool                    read_blocked;
+
+       fr_bio_fd_info_t const  *fd_info;
+} fr_radius_server_bio_info_t;
+
+typedef struct {
+       fr_bio_fd_packet_ctx_t  fd;
+       fr_bio_dedup_entry_t    *dedup;
+} fr_radius_server_bio_pctx_t;
+
+fr_bio_packet_t *fr_radius_server_bio_alloc(TALLOC_CTX *ctx, fr_radius_server_config_t *cfg, fr_bio_fd_config_t const *fd_cfg) CC_HINT(nonnull);
+
+fr_bio_t       *fr_radius_server_bio_get_fd(fr_bio_packet_t *bio) CC_HINT(nonnull);
+
+fr_radius_server_bio_info_t const *fr_radius_server_bio_info(fr_bio_packet_t *bio) CC_HINT(nonnull);
diff --git a/src/protocols/radius/server_udp.c b/src/protocols/radius/server_udp.c
new file mode 100644 (file)
index 0000000..b383049
--- /dev/null
@@ -0,0 +1,137 @@
+/*
+ *   This library is free software; you can redistribute it and/or
+ *   modify it under the terms of the GNU Lesser General Public
+ *   License as published by the Free Software Foundation; either
+ *   version 2.1 of the License, or (at your option) any later version.
+ *
+ *   This library is distributed in the hope that it will be useful,
+ *   but WITHOUT ANY WARRANTY; without even the implied warranty of
+ *   MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
+ *   Lesser General Public License for more details.
+ *
+ *   You should have received a copy of the GNU Lesser General Public
+ *   License along with this library; if not, write to the Free Software
+ *   Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301, USA
+ */
+
+/**
+ * $Id$
+ *
+ * @file protocols/radius/server_udp.c
+ * @brief Functions to support RADIUS bio handlers for server udp sockets
+ *
+ * @copyright 2024 Network RADIUS SAS (legal@networkradius.com)
+ */
+RCSID("$Id$")
+
+#include <freeradius-devel/bio/packet.h>
+#include <freeradius-devel/radius/server_udp.h>
+#include <freeradius-devel/radius/server_priv.h>
+
+static bool radius_server_dedup_receive(fr_bio_t *bio, fr_bio_dedup_entry_t *dedup_ctx, void *packet_ctx)
+{
+       fr_radius_server_fd_bio_t *my = talloc_get_type_abort(bio->uctx, fr_radius_server_fd_bio_t);
+       fr_bio_dedup_entry_t *prev;
+       fr_radius_server_bio_pctx_t *ctx = packet_ctx;
+
+       /*
+        *      Find any previous entry.
+        */
+       prev = fr_rb_find(&my->rb, dedup_ctx);
+       if (prev) {
+               // @todo - signal duplicate packet
+               return false;
+       }
+       
+       if (!fr_rb_insert(&my->rb, dedup_ctx)) {
+               // @todo - signal an error
+               return false;
+       }
+
+       /*
+        *      Glue it all together
+        */
+       dedup_ctx->uctx = ctx;
+       ctx->dedup = dedup_ctx;
+
+       return true;
+}
+
+static fr_bio_dedup_entry_t *radius_server_dedup_get_item(UNUSED fr_bio_t *bio, void *packet_ctx)
+{
+       fr_radius_server_bio_pctx_t *ctx = packet_ctx;
+
+       return ctx->dedup;
+}
+
+static void radius_server_dedup_release(fr_bio_t *bio, fr_bio_dedup_entry_t *dedup_ctx, UNUSED fr_bio_dedup_release_reason_t reason)
+{
+       fr_radius_server_fd_bio_t *my = talloc_get_type_abort(bio->uctx, fr_radius_server_fd_bio_t);
+       fr_radius_server_bio_pctx_t *ctx = dedup_ctx->uctx;
+
+       (void) fr_rb_delete(&my->rb, dedup_ctx);
+       ctx->dedup = NULL;
+}
+
+static int fr_radius_server_udp_bio_read(fr_bio_packet_t *bio, void **packet_ctx_p, fr_packet_t **packet_p,
+                                        TALLOC_CTX *out_ctx, fr_pair_list_t *out)
+{
+       int rcode;
+       fr_radius_server_bio_pctx_t *ctx;
+
+       /*
+        *      Read the packet.
+        */
+       rcode = fr_radius_server_fd_bio_read(bio, packet_ctx_p, packet_p, out_ctx, out);
+       if (rcode < 0) return rcode;
+
+       ctx = *packet_ctx_p;
+
+       /*
+        *      The dedup_ctx starts off with the raw data in a buffer somewhere.  That buffer will get
+        *      over-written with a later packet.  So be sure to update the dedup_ctx with the long-term
+        *      version of the packet contents.
+        */
+       fr_assert(ctx->dedup->packet_size == (*packet_p)->data_len);
+
+       ctx->dedup->packet = (*packet_p)->data;
+       ctx->dedup->packet_size = (*packet_p)->data_len;
+
+       return 0;
+}
+
+/** Allocate a RADIUS bio for receiving packets from clients.
+ *
+ *  It also verifies that the packets we receive are valid for RADIUS.
+ */
+fr_bio_packet_t *fr_radius_server_udp_bio_alloc(TALLOC_CTX *ctx, fr_radius_server_config_t *cfg, fr_bio_fd_config_t const *fd_cfg)
+{
+       fr_radius_server_fd_bio_t *my;
+
+       my = fr_radius_server_fd_bio_alloc(ctx, 2 * 4096, cfg, fd_cfg);
+       if (!my) return NULL;
+
+       if (fr_bio_mem_set_verify(my->mem, fr_radius_bio_verify_datagram, true) < 0) {
+       fail:
+               talloc_free(my);
+               return NULL;
+       }
+
+       /*
+        *      Once we've allocated a FD and memory BIO, UDP needs de-duping.
+        */
+       my->dedup = fr_bio_dedup_alloc(my, 256, radius_server_dedup_receive, radius_server_dedup_release,
+                                      radius_server_dedup_get_item, &cfg->dedup_cfg, my->mem);
+       if (!my->dedup) goto fail;
+       my->dedup->uctx = my;
+
+       my->common.bio = my->dedup;
+
+       my->common.read = fr_radius_server_udp_bio_read;
+       my->common.write = fr_radius_server_fd_bio_write;
+
+       // @todo - insert comparison function
+       // @todo - comparison function is different for connected and unconnected sockets
+
+       return (fr_bio_packet_t *) my;
+}
diff --git a/src/protocols/radius/server_udp.h b/src/protocols/radius/server_udp.h
new file mode 100644 (file)
index 0000000..08ea28b
--- /dev/null
@@ -0,0 +1,30 @@
+#pragma once
+/*
+ *  This program is free software; you can redistribute it and/or modify
+ *  it under the terms of the GNU General Public License as published by
+ *  the Free Software Foundation; either version 2 of the License, or
+ *  (at your option) any later version.
+ *
+ *  This program is distributed in the hope that it will be useful,
+ *  but WITHOUT ANY WARRANTY; without even the implied warranty of
+ *  MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
+ *  GNU General Public License for more details.
+ *
+ *  You should have received a copy of the GNU General Public License
+ *  along with this program; if not, write to the Free Software
+ *  Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301, USA
+ */
+
+/**
+ * $Id$
+ *
+ * @file protocols/radius/server_udp.h
+ * @brief RADIUS bio handlers for outgoing RADIUS server sockets over UDP
+ *
+ * @copyright 2024 Network RADIUS SAS (legal@networkradius.com)
+ */
+RCSIDH(radius_server_udp_h, "$Id$")
+
+#include <freeradius-devel/radius/server.h>
+
+fr_bio_packet_t *fr_radius_server_udp_bio_alloc(TALLOC_CTX *ctx, fr_radius_server_config_t *cfg, fr_bio_fd_config_t const *fd_cfg) CC_HINT(nonnull);