diff --git a/raddb/mods-available/radius b/raddb/mods-available/radius index 14875a8f9e33..1eb1fc2f5000 100644 --- a/raddb/mods-available/radius +++ b/raddb/mods-available/radius @@ -7,23 +7,53 @@ # # = Radius Module # -# The `radius` module in v4, can be used to implement proxying and request -# fan-out, as well as synchronous and asynchronous `CoA` and `DM`. +# The `radius` module in v4 implements RADIUS proxying and +# replication. In v3, RADIUS proxying is a special kind of +# configuration, with it's own load-balancing, fail-over etc. That +# configuration is now simpler in v4. The outbound RADIUS proxying +# is done by just another module: the `radius` module. +# Load-balancing and redundant fail-over are handled by the +# `load-balance` and `redundant` keywords. # -# ## Configuration Settings +# In v4, the `radius` module most clearly maps to a `home_server` in +# v4. The `radius` module typically makes a connection from one +# source IP address to a server at one destination IP/port. It may +# open multiple source ports, depending on how many packets are being +# proxied. +# +# ## Behavior +# +# The module adds a Proxy-State attribute to all proxied packets. +# This `Proxy-State` contains a 32-bit random number, which is unique +# to this module. This unique number helps to detect proxy loops. +# +# The reply from home server is appended to the reply list for the +# current packet. +# +# WARNING: For security reasons, the module ensures that all proxied +# `Access-Request` packets contain a `Message-Authenticator` +# attribute. This behavior is *NOT* configurable, and *CANNOT* be +# changed. This behavior is part of the BlastRADIUS mitigations. # # Any proxied packet (including `Accounting-Request`) can # receive a `Protocol-Error` response packet. This packet # is an explicit `NAK` that something went wrong processing # the request. # -# WARNING: For security reasons, the module ensures that all proxied -# `Access-Request` packets contain a `Message-Authenticator` attribute. -# This behavior is *NOT* configurable, and *CANNOT* be changed. +# Unlike v3, the server does not support any "pre-proxy" or +# "post-proxy" processing sections. Similarly, this module does not +# support any "proxy" or "proxy-reply" list. Instead, the current +# request is proxied as-is, and the proxied reply is added to the +# current reply list. If you want to modify the proxied request +# and/or proxied reply, then you should use a `subrequest` block to +# create a child request. That child request can then be modified +# independently of the parent. Any reply attributes will have to be +# copied back manually to the parent request. # -# The module adds a Proxy-State attribute to all proxied packets. -# This `Proxy-State` contains a 32-bit random number, which is unique -# to this module. This unique number helps to detect proxy loops. +# ## Configuration Settings +# +# The configuration settings of this module are similar to the +# `home_server` settings in v3. # # The module has the following return codes: # @@ -44,7 +74,61 @@ # radius { # - # transport:: Only UDP transport is allowed. + # mode:: What kind of client behavior is being used. + # + # proxy - forward packets which are received from a NAS + # - each packet has a Proxy-State attribute added. + # - it looks for, and stops proxy loops + # - retransmissions are sent only when the NAS retransmits + # - the module fails if it does not receive a reply + # + # client - originate packet, and do retransmissions ourselves + # - no Proxy-State is added. + # - the module retransmits as needed + # - the module fails if it does not receive a reply + # + # replicate - send packets without waiting for a reply. + # - no Proxy-State is added + # - the module does not expect any reply + # - all replies are discarded + # - the module continues with "ok" immediately after + # sending the packet. + # + # unconnected-replicate - replicate packets to dynamic destinations + # - For unconnected UDP sockets only. + # - It MUST have `transport = udp` + # - It MUST have `src_ipaddr = *` and no `src_port` + # - You CANNOT use the module "in place" as with normal proxying. + # - It is only supported via the function %replicate.sendto.ipaddr(ipaddr, port, secret) + # + # unconnected-proxy - proxy packets to dynamic destinations + # - For unconnected UDP sockets only. + # - It MUST have `transport = udp` + # - It MUST have `src_ipaddr = *` and no `src_port` + # - You CANNOT use the module "in place" as with normal proxying. + # - It is only supported via the function %proxy.sendto.ipaddr(ipaddr, port, secret) + # + # The server can still be used to create (i.e. originate) + # packets via this module when `mode = proxy` is set. The + # module can automatically detect the difference between + # proxied packets and client packets it originates. + # Originated packets are taken from the detail file, or + # result when changing packet type (e.g. Accounting-Request + # to Disconnect-Request), or when the current request is a + # subrequest, and the parent request is from a different + # protocol. + # + # Note that there is no `mode = unconnected`, where the + # module then both proxies packets, and replicates them. The + # need to track replies when proxying means that it's + # difficult to both proxy and replicate at the same time. As + # a result, there are two "unconnected" modes, one for each + # of "proxy" and "replicate". + # + mode = proxy + + # + # transport:: Transport protocol. Can be `udp` or `tcp`. # transport = udp @@ -58,77 +142,136 @@ radius { # # type:: List of allowed packet types. # - # There is currently no way to change the packet type in the - # request. See `unlang` fork for that functionality. + # The module will only send packets types which are listed + # here. Other types of packets will be ignored. The main + # purpose of the `type` configuration is to ensure that the + # correct packets are being sent to the home server. This + # entry serves as a double-check against misconfigurations. + # + # In v3, the `home_server` configuration of `auth`, `acct`, + # or `auth+acct` is used to _find_ a home server. That is, + # when FreeRADIUS has an `Access-Request` packet in v3, it + # proxies it by looking up a matching `home_server`. + # + # In v4, proxying is done by listing the `radius` module in a + # processing section, such as `authenticate radius { ... }`, + # or `recv Accounting-Request { ... }`. So unlike v3, the + # module doesn't have to _find_ a proxy destination for a + # particular kind of packet. Instead, the administrator + # _configures_ the module to send packets to a destination. + # + # As a result, the module doesn't really care about what kind + # of packets it sends. It has a packet, a destination where + # that packet should be sent, and it sends the packet. # - # NOTE: `Status-Server` is reserved for `inter-server` signaling, + # In order to change packet types, see the `subrequest` keyword. + # + # NOTE: `Status-Server` is reserved for connection signaling, # and cannot be proxied. # + # Unlike v3, all packet types are allocated from the same + # 8-bit ID space. This change does not affect the majority + # of RADIUS proxying, which only sends one type of packet. + # This change does not affect the home server which receives + # these packets, as the home server does not track IDs except + # to correlate requests to replies. + # + # The only visible difference, then, between v3 and v4 is + # that in some cases, the new `radius` module will use more + # source ports when proxying. + # + # This change simplifies the implementation of the RADIUS + # client. + # type = Access-Request type = Accounting-Request # - # replicate:: Whether or not we are replicating packets. - # - # Replication is "send proxied request, and continue, - # without waiting for a response". Any response received - # is discarded, and does not affect packet processing. + # require_message_authenticator::Require Message-Authenticator + # in responses. # -# replicate = no - + # A server should include Message-Authenticator attribute as + # the first attribute in responses to Access-Request packets. + # This behavior mitigates against the BlastRADIUS attack. # - # synchronous:: + # However, not all servers follow this security practice. As + # a result, this module can be configured to either not + # require, or require, Message-Authenticator. # - # In many cases, the module should do retransmissions of - # proxied request on its own. See the various - # configurations for `initial_rtx_time`, - # etc. below. This means setting `synchronous = no`. + # If value is `auto`, then the module will automatically + # detect the existence of Message-Authenticator in response + # packets. Once the module sees a Message-Authenticator, it + # will automatically change the configuration internally to + # `yes`. This change prevents security "down-bidding" + # attacks. # - # However, in some cases, it is useful to retransmit only - # when the server receives a retransmission from the NAS. - # This is done by setting `synchronous = yes` + # Allowed values: yes, no, auto # - # In general, if the server is receiving packets directly - # from a NAS, you should set `synchronous = no`. This is - # because the NAS retransmission behavior is horrible, - # inconsistent, and hard to configure. + # The default is `auto`. # - # If the server is receiving packets from another proxy - # server, you should set `synchronous = yes`. This allows - # the other proxy server to do retransmissions correctly. + require_message_authenticator = auto + # - # NOTE: The behavior in v3 is the same as `synchronous = yes` + # response_window: If we do not receive any replies within + # this time period, then start `zombie_period` # -# synchronous = no + response_window = 15 # - # originate:: Whether or not we are creating the packet. + # zombie_period:: If the home server does not reply to + # packets within `response_window`, then `zombie_period` + # starts. # - # Sometimes we are creating a request that is not for the purpose of - # proxying another request, in which case we do not want to add a - # Proxy-State attribute. + # When `zombie_period` starts, a connection is marked + # `zombie`, and then is not used to send new packets. If + # there are no responses on this connection within + # `zombie_period`, the module either closes the connection + # (no `status_check` subsection), or starts pinging the home + # server (`status_check.type = Status-Server`). # - # In some cases, such as originating a CoA or Disconnect request, - # including Proxy-State may confuse the receiving NAS. -# originate = no + zombie_period = 10 # - # require_message_authenticator::Require Message-Authenticator - # in responses. + # revive_interval:: If there are no status checks, mark the + # home server alive after `revive_interval` timeout. # - # Including a Message-Authenticator attribute first in response - # packet, mitigates against the blastradius prefix attack. + # Some home servers do not support status checks via the + # `Status-Server` packet. Others may not have a "test" user + # configured that can be used to query the server, to see if + # it is alive. For those servers, we have NO WAY of knowing + # when it becomes alive again. Therefore, after the server + # has been marked dead, we wait a period of time, and mark + # it alive again, in the hope that it has come back to + # life. # - # If value is auto, then if any packet received from the client - # contains a valid Message-Authenticator attribute, then the server - # will require it from all future packets from that client. + # If it has NOT come back to life, then the module will wait + # for `zombie_period` before marking it dead again. During + # the `zombie_period`, ALL AUTHENTICATIONS WILL FAIL, because + # the home server is still dead. There is NOTHING that can + # be done about this, other than to enable the status checks, + # as documented above. # - # Allowed values: yes, no, auto + # e.g. if `zombie_period` is 40 seconds, and `revive_interval` + # is 300 seconds, the for 40 seconds out of every 340, or about + # 10% of the time, all authentications will fail. # - # The default is "no". + # If the `zombie_period` and `revive_interval` configurations + # are set smaller, than it is possible for up to 50% of + # authentications to fail. # - require_message_authenticator = auto + # As a result, we recommend enabling status checks, and + # we do NOT recommend using `revive_interval`. + # + # The `revive_interval` configuration is used ONLY if the + # `status_check` subsection is not used. Otherwise, + # `revive_interval` is not necessary, and should be deleted. + # + # Useful range of values: 10 to 3600 + # + revive_interval = 3600 + # + # ### Check the status of a connection # # status_check { ... }:: For "are you alive?" queries. # @@ -141,12 +284,13 @@ radius { # e.g. 'Access-Request', 'Accounting-Request' or # 'Status-Server'. # - # Status-Server is recommended as other packet types may - # be interpreted incorrectly, or proxied to a remote - # server defeting the purpose of the status check + # Status-Server is recommended as other packet types + # may be interpreted incorrectly, or may ve proxied + # to a remote server, which defeats the purpose of + # the status checks. # # If you specify another type of packet, it MUST be listed - # as an allowed `type`, above. + # as an allowed `type` above. # type = Status-Server @@ -155,8 +299,9 @@ radius { # be edited. # # For other packet types, you can set the contents - # here. The section MUST be set over "&request. = value", and - # anything else will cause a parse error. + # here. The section MUST be set over + # "&request. = value", and anything else + # will cause a parse error. # # We RECOMMEND that you use packet contents which # lets the other end easily tell that they are not @@ -184,60 +329,71 @@ radius { } # - # response_window: If we do not receive a reply within this time period, then - # start `zombie_period` + # ## Transport Protocols # - response_window = 15 - - # - # zombie_period:: If the home server does not reply to a packet, the - # `zombie_period` starts. - # - # The connection is marked `zombie`, and isn't used to send new packets. - # If there are no responses within `zombie_period`, the server either - # closes the connection (no `status_check` subsection), or starts pinging the - # home server (`status_check.type = Status-Server`). + # The module supports multiple transport protocols. # - zombie_period = 10 # - # revive_interval:: If there are no status checks, mark the - # home server alive after `revive_interval` timeout. + # ### File Output # - # Some home servers do not support status checks via the - # `Status-Server` packet. Others may not have a "test" user - # configured that can be used to query the server, to see if - # it is alive. For those servers, we have NO WAY of knowing - # when it becomes alive again. Therefore, after the server - # has been marked dead, we wait a period of time, and mark - # it alive again, in the hope that it has come back to - # life. + # Write raw RADIUS packets (no IP or UDP header) to a file. + # This transport can only be used for `mode = replicate` # - # If it has NOT come back to life, then the module will wait - # for `zombie_period` before marking it dead again. During - # the `zombie_period`, ALL AUTHENTICATIONS WILL FAIL, because - # the home server is still dead. There is NOTHING that can - # be done about this, other than to enable the status checks, - # as documented above. + file { + filename = ${logdir}/packets.bin + } + # - # e.g. if `zombie_period` is 40 seconds, and `revive_interval` - # is 300 seconds, the for 40 seconds out of every 340, or about - # 10% of the time, all authentications will fail. + # ### UDP Transport # - # If the `zombie_period` and `revive_interval` configurations - # are set smaller, than it is possible for up to 50% of - # authentications to fail. + # Much like the v3 `home_server` configuration. # - # As a result, we recommend enabling status checks, and - # we do NOT recommend using `revive_interval`. + udp { + # + # src_ipaddr:: IP we open our socket on. + # +# src_ipaddr = * + + # + # Destination IP address, port, and secret. + # + # Use `ipv4addr = ...` to force IPv4 addresses. + # Use `ipv6addr = ...` to force IPv6 addresses. + # + ipaddr = 127.0.0.1 + port = 1812 + secret = testing123 + + # + # interface:: Interface to bind to. + # +# interface = eth0 + + # + # max_packet_size:: Our max packet size. may be different from the parent. + # +# max_packet_size = 4096 + + # + # recv_buff:: How big the kernel's receive buffer should be. + # +# recv_buff = 1048576 + + # + # send_buff:: How big the kernel's send buffer should be. + # +# send_buff = 1048576 + } + # - # The `revive_interval` configuration is used ONLY if the - # `status_check` subsection is not used. Otherwise, - # `revive_interval` is not necessary, and should be deleted. + # ### TCP # - # Useful range of values: 10 to 3600 + # The TCP configuration is identical to the `udp` configuration. # - revive_interval = 3600 +# tcp { +# ... +# } # # ## Connection trunking @@ -389,55 +545,12 @@ radius { } # - # ## Protocols - # - # For now, only UDP is supported. - # - # udp { ... }:: UDP is configured here. - # - udp { - ipaddr = 127.0.0.1 - port = 1812 - secret = testing123 - - # - # NOTE: Don't change anything if you are not sure. - # - - # - # interface:: Interface to bind to. - # -# interface = eth0 - - # - # max_packet_size:: Our max packet size. may be different from the parent. - # -# max_packet_size = 4096 - - # - # recv_buff:: How big the kernel's receive buffer should be. - # -# recv_buff = 1048576 - - # - # send_buff:: How big the kernel's send buffer should be. - # -# send_buff = 1048576 - - # - # src_ipaddr:: IP we open our socket on. - # -# src_ipaddr = "" - } - - # - # ## Packets + # ## Retransmission timers. # # Each packet can have its own retransmission timers. # # The sections are named for each packet type. The contents - # are the same for all packet types. Only the relevant ones - # are parsed (see `type` above). + # are the same for all packet types. # # @@ -535,3 +648,87 @@ radius { max_rtx_duration = 30 } } + +# +# ## Replication of Packets +# +# The module supports replication of packets to new destinations at +# run time. In this context, replication means "send the packet, and +# do not wait for the response". This functionality is most useful +# when copying large amounts of accounting data to multiple +# destinations. +# +# The module can then only be used as a dynamic expansion. That is, +# you cannot specify the `replicate` module directly in a processing +# section. +# +# ### Usage +# +# This module can only be used as a dynamic expansion. Since the +# module does not wait for any response, the expansion does not +# return any value. +# +# The module can be called from any processing section as follows: +# +# %replicate.sendto.ipaddr(127.0.0.1, 1813, 'testing123') +# +# The function takes three arguments: +# +# * IP address where the packet is sent. It MUST be the same +# address family as `src_ipaddr` below. +# * port where the packet is sent. +# * secret for this packet. +# +# This function allows the module to send packets to _any_ +# destination, where the destination is chosen dynamically at run +# time. The arguments to the function can be take from other +# attributes, database queries, etc. +# +radius replicate { + # + # Generally you only want to replicate accounting packets. + # + type = Accounting-Request + + # + # We are not opening a socket from our server to their + # server. We are replicating packets. + # + mode = unconnected-replicate + + # + # For replicated packets, only UDP is supported. + # + transport = udp + + # + # ### UDP Transport + # + # For unconnected modes, only UDP is supported. + # + udp { + # + # src_ipaddr:: The source IP address used by the module. + # + src_ipaddr = * + + # + # `src_port` cannot be used. If it is used here, the + # module will refuse to start. Instead, the module + # will open a unique source port per thread. + # + # `secret` cannot be used. If it is used, the value + # will be ignored. + # + } + + # + # ### Other Configuration + # + # No other configuration items are supported when using + # `mode = unconnected-replicate`. + # + # The `pool` configuration is ignored, as is `status-check`, + # along with all per-packet timeouts. + # +} diff --git a/src/modules/rlm_radius/TODO.md b/src/modules/rlm_radius/TODO.md deleted file mode 100644 index e3b8440bef86..000000000000 --- a/src/modules/rlm_radius/TODO.md +++ /dev/null @@ -1,110 +0,0 @@ -# rlm_radius - -## 2017-10-11 - -After refactoring... - -* on read(), don't put connection into active state, as it may not be writable? - * or, just do it, and hope for the best... with the event loop handling it - - -## RADIUS fixes - -* idle out old connections - -* limit # of bad / reconnected connections - -* move status_u allocation and init to connection alloc - -## connection state - -* maybe move Status-Server to fixed-time pings, as recommended in RFC 3539? - * low priority, and probably not useful - -## Limits - -We limit the number of connections, but not the number of proxied -packets. This is because (for now), each connection can only proxy 256 packets... - -## Status Checks - -* connection negotiation in Status-Server in proto_radius - * some is there (Response-Length) - * add more? Extended ID, etc. - -## Core Issues - -things to do in the server core. Tracked here because it's related to -the work in rlm_radius. - -## Cleanup_delay - -* need to double-check cleanup_delay - * it works, but it's likely set too small? - * especially if the client retransmits are 10s? - * or maybe it was the dup detection bug (timestamp) where it didn't detect dups... - -## sequence / ACK in network / worker - -* double-check ENABLE_SKIPS in src/lib/io/channel.c. It's disabled - for now, as it caused problems. So it ALWAYS signals the other side. :( - -We should move to a "must_signal" approach, as with the network side -The worker should suppress signals if it sees that the ACKs from the -other end haven't caught up to it's sent packets. Otherwise, it must -signal. - -this whole thing is wrong... we end up signaling on every damned packet in real life... - -OK... fix the damned channel to use queue depth instead of ACKs -which makes them less general, but better. The worker can NAK a packet, send a reply, or mark it ask discarded - - - DATA N -> W: (packet + queue 1, active) - - DATA N <- W (packet + queue is now 0, inactive) - - DISCARD N <- W (no packet, queue is now 0, inactive) - - SLEEPING N <- W (no packet, queue is 1, inactive) - - -We also need an "must_signal" flag, for if the other end is -sleeping... the network always sets it, I guess.. - -### Fork - -* fix fork - - fork server.packet-type { - &foo += &parent:bar - } - -* fork is an 'update' section that also runs a new virtual server. A - little weird, but it should work. - -* needs helper functions in virtual_server.c to do it.. and to create - child request_t async stuff with listen, protocol handler, etc. - -* fork also needs to do this sanity check on compile, so that it knows - it can dereference sections which exist... - -### clean up request_t structure - -many fields are essentially unused. request->proxy is no longer used, -but is referenced all over the place. - -grunt work, but very useful. - -### Network and worker fixups - -* switch worker selection from recursing / heap to O(N) lookups and "power of 2" - * see comments in src/lib/io/network.c - -* associate packets with a particular worker across multiple packets - * once this is done, we can move to per-thread SSL contexts, and drop contention massively - * with the caveat that *all SSL work* has to be done in one thread - * hopefully this doesn't affect things like SQL drivers? need to check... - -* do NUMA for high-end systems - * associate N network threads with W worker threads diff --git a/src/modules/rlm_radius/all.mk b/src/modules/rlm_radius/all.mk index 2413dc949ffd..52bd795c0eea 100644 --- a/src/modules/rlm_radius/all.mk +++ b/src/modules/rlm_radius/all.mk @@ -1,3 +1,7 @@ -ifeq "${WITH_RADIUS2}" "" -SUBMAKEFILES := rlm_radius.mk rlm_radius_udp.mk -endif +TARGETNAME := rlm_radius +TARGET := $(TARGETNAME)$(L) + +SOURCES := rlm_radius.c track.c + +TGT_PREREQS := libfreeradius-radius$(L) libfreeradius-bio-config$(L) libfreeradius-bio$(L) +LOG_ID_LIB = 39 diff --git a/src/modules/rlm_radius2/bio.c b/src/modules/rlm_radius/bio.c similarity index 100% rename from src/modules/rlm_radius2/bio.c rename to src/modules/rlm_radius/bio.c diff --git a/src/modules/rlm_radius/rlm_radius.c b/src/modules/rlm_radius/rlm_radius.c index 8cee28ddfed8..2036ae5ec8b3 100644 --- a/src/modules/rlm_radius/rlm_radius.c +++ b/src/modules/rlm_radius/rlm_radius.c @@ -26,11 +26,13 @@ RCSID("$Id$") #include #include +#include #include #include #include "rlm_radius.h" +static int mode_parse(TALLOC_CTX *ctx, void *out, UNUSED void *parent, CONF_ITEM *ci, conf_parser_t const *rule); static int type_parse(TALLOC_CTX *ctx, void *out, UNUSED void *parent, CONF_ITEM *ci, conf_parser_t const *rule); static int status_check_type_parse(TALLOC_CTX *ctx, void *out, UNUSED void *parent, CONF_ITEM *ci, conf_parser_t const *rule); static int status_check_update_parse(TALLOC_CTX *ctx, void *out, UNUSED void *parent, CONF_ITEM *ci, conf_parser_t const *rule); @@ -94,24 +96,46 @@ static conf_parser_t disconnect_config[] = { CONF_PARSER_TERMINATOR }; +static conf_parser_t const transport_config[] = { + { FR_CONF_OFFSET_FLAGS("secret", CONF_FLAG_REQUIRED, rlm_radius_t, secret) }, + + CONF_PARSER_TERMINATOR +}; + +/* + * We only parse the pool options if we're connected. + */ +static conf_parser_t const connected_config[] = { + { FR_CONF_POINTER("status_check", 0, CONF_FLAG_SUBSECTION, NULL), .subcs = (void const *) status_check_config }, + + { FR_CONF_OFFSET_SUBSECTION("pool", 0, rlm_radius_t, trunk_conf, trunk_config ) }, + + { FR_CONF_POINTER("udp", 0, CONF_FLAG_SUBSECTION | CONF_FLAG_OPTIONAL, NULL), .subcs = (void const *) transport_config }, + + { FR_CONF_POINTER("tcp", 0, CONF_FLAG_SUBSECTION | CONF_FLAG_OPTIONAL, NULL), .subcs = (void const *) transport_config }, + + CONF_PARSER_TERMINATOR +}; /* * A mapping of configuration file names to internal variables. */ static conf_parser_t const module_config[] = { - { FR_CONF_OFFSET_TYPE_FLAGS("transport", FR_TYPE_VOID, 0, rlm_radius_t, io_submodule), - .func = module_rlm_submodule_parse }, + { FR_CONF_OFFSET_FLAGS("mode", CONF_FLAG_REQUIRED, rlm_radius_t, mode), .func = mode_parse, .dflt = "proxy" }, + + { FR_CONF_OFFSET_REF(rlm_radius_t, fd_config, fr_bio_fd_client_config) }, { FR_CONF_OFFSET_FLAGS("type", CONF_FLAG_NOT_EMPTY | CONF_FLAG_MULTI | CONF_FLAG_REQUIRED, rlm_radius_t, types), .func = type_parse }, - { FR_CONF_OFFSET("replicate", rlm_radius_t, replicate) }, + { FR_CONF_OFFSET_FLAGS("replicate", CONF_FLAG_DEPRECATED, rlm_radius_t, replicate) }, - { FR_CONF_OFFSET("synchronous", rlm_radius_t, synchronous) }, + { FR_CONF_OFFSET_FLAGS("synchronous", CONF_FLAG_DEPRECATED, rlm_radius_t, synchronous) }, - { FR_CONF_OFFSET("originate", rlm_radius_t, originate) }, + { FR_CONF_OFFSET_FLAGS("originate", CONF_FLAG_DEPRECATED, rlm_radius_t, originate) }, - { FR_CONF_POINTER("status_check", 0, CONF_FLAG_SUBSECTION, NULL), .subcs = (void const *) status_check_config }, + { FR_CONF_OFFSET("max_packet_size", rlm_radius_t, max_packet_size), .dflt = "4096" }, + { FR_CONF_OFFSET("max_send_coalesce", rlm_radius_t, max_send_coalesce), .dflt = "1024" }, { FR_CONF_OFFSET("max_attributes", rlm_radius_t, max_attributes), .dflt = STRINGIFY(RADIUS_MAX_ATTRIBUTES) }, @@ -126,8 +150,6 @@ static conf_parser_t const module_config[] = { { FR_CONF_OFFSET("revive_interval", rlm_radius_t, revive_interval) }, - { FR_CONF_OFFSET_SUBSECTION("pool", 0, rlm_radius_t, trunk_conf, trunk_config ) }, - CONF_PARSER_TERMINATOR }; @@ -153,15 +175,97 @@ static fr_dict_attr_t const *attr_chap_password; static fr_dict_attr_t const *attr_packet_type; static fr_dict_attr_t const *attr_proxy_state; +static fr_dict_attr_t const *attr_error_cause; +static fr_dict_attr_t const *attr_event_timestamp; +static fr_dict_attr_t const *attr_extended_attribute_1; +static fr_dict_attr_t const *attr_message_authenticator; +static fr_dict_attr_t const *attr_eap_message; +static fr_dict_attr_t const *attr_nas_identifier; +static fr_dict_attr_t const *attr_original_packet_code; +static fr_dict_attr_t const *attr_response_length; +static fr_dict_attr_t const *attr_user_password; + extern fr_dict_attr_autoload_t rlm_radius_dict_attr[]; fr_dict_attr_autoload_t rlm_radius_dict_attr[] = { { .out = &attr_chap_challenge, .name = "CHAP-Challenge", .type = FR_TYPE_OCTETS, .dict = &dict_radius}, { .out = &attr_chap_password, .name = "CHAP-Password", .type = FR_TYPE_OCTETS, .dict = &dict_radius}, { .out = &attr_packet_type, .name = "Packet-Type", .type = FR_TYPE_UINT32, .dict = &dict_radius }, { .out = &attr_proxy_state, .name = "Proxy-State", .type = FR_TYPE_OCTETS, .dict = &dict_radius}, + + { .out = &attr_error_cause, .name = "Error-Cause", .type = FR_TYPE_UINT32, .dict = &dict_radius }, + { .out = &attr_event_timestamp, .name = "Event-Timestamp", .type = FR_TYPE_DATE, .dict = &dict_radius}, + { .out = &attr_extended_attribute_1, .name = "Extended-Attribute-1", .type = FR_TYPE_TLV, .dict = &dict_radius}, + { .out = &attr_message_authenticator, .name = "Message-Authenticator", .type = FR_TYPE_OCTETS, .dict = &dict_radius}, + { .out = &attr_eap_message, .name = "EAP-Message", .type = FR_TYPE_OCTETS, .dict = &dict_radius}, + { .out = &attr_nas_identifier, .name = "NAS-Identifier", .type = FR_TYPE_STRING, .dict = &dict_radius}, + { .out = &attr_original_packet_code, .name = "Extended-Attribute-1.Original-Packet-Code", .type = FR_TYPE_UINT32, .dict = &dict_radius}, + { .out = &attr_response_length, .name = "Extended-Attribute-1.Response-Length", .type = FR_TYPE_UINT32, .dict = &dict_radius }, + { .out = &attr_user_password, .name = "User-Password", .type = FR_TYPE_STRING, .dict = &dict_radius}, + { NULL } }; +#include "bio.c" + +static fr_table_num_sorted_t mode_names[] = { + { L("client"), RLM_RADIUS_MODE_CLIENT }, + { L("proxy"), RLM_RADIUS_MODE_PROXY }, + { L("replicate"), RLM_RADIUS_MODE_REPLICATE }, + { L("unconnected-replicate"), RLM_RADIUS_MODE_UNCONNECTED_REPLICATE }, +// { L("unconnected-proxy"), RLM_RADIUS_MODE_UNCONNECTED_PROXY }, +}; +static size_t mode_names_len = NUM_ELEMENTS(mode_names); + + +/** Set the mode of operation + * + * @param[in] ctx to allocate data in (instance of rlm_radius). + * @param[out] out Where to write the parsed data. + * @param[in] parent Base structure address. + * @param[in] ci #CONF_PAIR specifying the name of the type module. + * @param[in] rule unused. + * @return + * - 0 on success. + * - -1 on failure. + */ +static int mode_parse(UNUSED TALLOC_CTX *ctx, void *out, void *parent, + CONF_ITEM *ci, UNUSED conf_parser_t const *rule) +{ + char const *name = cf_pair_value(cf_item_to_pair(ci)); + rlm_radius_mode_t mode; + rlm_radius_t *inst = talloc_get_type_abort(parent, rlm_radius_t); + + mode = fr_table_value_by_str(mode_names, name, RLM_RADIUS_MODE_INVALID); + + /* + * Commented out until we upgrade the old configurations. + */ + if (mode == RLM_RADIUS_MODE_INVALID) { + cf_log_err(ci, "Invalid mode name \"%s\"", name); + return -1; + } + + *(rlm_radius_mode_t *) out = mode; + + /* + * Normally we want connected sockets, in which case we push additional configuration for connected sockets. + */ + if ((mode != RLM_RADIUS_MODE_UNCONNECTED_REPLICATE) && + (mode != RLM_RADIUS_MODE_UNCONNECTED_PROXY)) { + CONF_SECTION *cs = cf_item_to_section(cf_parent(ci)); + + inst->fd_config.type = FR_BIO_FD_CONNECTED; + + if (cf_section_rules_push(cs, connected_config) < 0) return -1; + + } else { + inst->fd_config.type = FR_BIO_FD_UNCONNECTED; + } + + return 0; +} + + /** Set which types of packets we can parse * * @param[in] ctx to allocate data in (instance of rlm_radius). @@ -217,7 +321,7 @@ static int type_parse(UNUSED TALLOC_CTX *ctx, void *out, UNUSED void *parent, */ cf_section_rule_push(cs, &type_interval_config[code]); - memcpy(out, &code, sizeof(code)); + *(uint32_t *) out = code; return 0; } @@ -345,7 +449,7 @@ static void radius_fixups(rlm_radius_t const *inst, request_t *request) /* * Check for proxy loops. */ - if (!inst->originate && RDEBUG_ENABLED) { + if ((inst->mode == RLM_RADIUS_MODE_PROXY) && RDEBUG_ENABLED) { fr_dcursor_t cursor; for (vp = fr_pair_dcursor_by_da_init(&cursor, &request->request_pairs, attr_proxy_state); @@ -353,13 +457,19 @@ static void radius_fixups(rlm_radius_t const *inst, request_t *request) vp = fr_dcursor_next(&cursor)) { if (vp->vp_length != 4) continue; - if (memcmp(&inst->proxy_state, vp->vp_octets, 4) == 0) { + if (memcmp(&inst->common_ctx.proxy_state, vp->vp_octets, 4) == 0) { RWARN("Possible proxy loop - please check server configuration."); break; } } } + /* + * @todo - check for proxy loops for client && replicate, too. + * + * This only catches "self loops", but it may be worth doing. + */ + if (request->packet->code != FR_RADIUS_CODE_ACCESS_REQUEST) return; if (fr_pair_find_by_da(&request->request_pairs, NULL, attr_chap_password) && @@ -395,7 +505,17 @@ static unlang_action_t CC_HINT(nonnull) mod_process(rlm_rcode_t *p_result, modul if ((request->packet->code >= FR_RADIUS_CODE_MAX) || !fr_time_delta_ispos(inst->retry[request->packet->code].irt)) { /* can't be zero */ - REDEBUG("Invalid packet code %d", request->packet->code); + REDEBUG("Invalid packet code %u", request->packet->code); + RETURN_MODULE_FAIL; + } + + /* + * Unconnected sockets use %radius.replicate(ip, port, secret), + * or %radius.sendto(ip, port, secret) + */ + if ((inst->mode == RLM_RADIUS_MODE_UNCONNECTED_REPLICATE) || + (inst->mode == RLM_RADIUS_MODE_UNCONNECTED_PROXY)) { + REDEBUG("When using 'mode = unconnected-*', this module cannot be used in-place. Instead, it must be called via a function call"); RETURN_MODULE_FAIL; } @@ -425,26 +545,104 @@ static unlang_action_t CC_HINT(nonnull) mod_process(rlm_rcode_t *p_result, modul * return another code which indicates what happened to * the request... */ - return inst->io->enqueue(&rcode, inst->io_submodule->data, - module_thread(inst->io_submodule)->data, request); + return mod_enqueue(&rcode, inst, + module_thread(mctx->mi)->data, request); } + static int mod_instantiate(module_inst_ctx_t const *mctx) { size_t i, num_types; rlm_radius_t *inst = talloc_get_type_abort(mctx->mi->data, rlm_radius_t); CONF_SECTION *conf = mctx->mi->conf; - inst->io = (rlm_radius_io_t const *)inst->io_submodule->exported; /* Public symbol exported by the module */ inst->name = mctx->mi->name; inst->received_message_authenticator = talloc_zero(NULL, bool); /* Allocated outside of inst to default protection */ /* - * These limits are specific to RADIUS, and cannot be over-ridden + * Allow explicit setting of mode. */ - FR_INTEGER_BOUND_CHECK("trunk.per_connection_max", inst->trunk_conf.max_req_per_conn, >=, 2); - FR_INTEGER_BOUND_CHECK("trunk.per_connection_max", inst->trunk_conf.max_req_per_conn, <=, 255); - FR_INTEGER_BOUND_CHECK("trunk.per_connection_target", inst->trunk_conf.target_req_per_conn, <=, inst->trunk_conf.max_req_per_conn / 2); + if (inst->mode != RLM_RADIUS_MODE_INVALID) goto check_others; + + /* + * If not set, try to insinuate it from context. + */ + if (inst->replicate) { + if (inst->originate) { + cf_log_err(conf, "Cannot set 'replicate=true' and 'originate=true' at the same time."); + return -1; + } + + if (inst->synchronous) { + cf_log_warn(conf, "Ignoring 'synchronous=true' due to 'replicate=true'"); + } + + inst->mode = RLM_RADIUS_MODE_REPLICATE; + goto check_others; + } + + /* + * Argubly we should be allowed to do synchronous proxying _and_ originating client packets. + * + * However, the previous code didn't really do that consistently. + */ + if (inst->synchronous && inst->originate) { + cf_log_err(conf, "Cannot set 'synchronous=true' and 'originate=true'"); + return -1; + } + + if (inst->synchronous) { + inst->mode = RLM_RADIUS_MODE_PROXY; + } else { + inst->mode = RLM_RADIUS_MODE_CLIENT; + } + +check_others: + /* + * Replication is write-only, and append by default. + */ + if (inst->mode == RLM_RADIUS_MODE_REPLICATE) { + if (inst->fd_config.filename && (inst->fd_config.flags != O_WRONLY)) { + cf_log_info(conf, "Setting 'flags = write-only' for writing to a file"); + } + inst->fd_config.flags = O_WRONLY | O_APPEND; + + } else if (inst->fd_config.filename) { + cf_log_err(conf, "When using an output 'filename', you MUST set 'mode = replicate'"); + return -1; + + } else { + /* + * All other IO is read+write. + */ + inst->fd_config.flags = O_RDWR; + } + + if (fr_bio_fd_check_config(&inst->fd_config) < 0) { + cf_log_perr(conf, "Invalid configuration"); + return -1; + } + + /* + * Clamp max_packet_size first before checking recv_buff and send_buff + */ + FR_INTEGER_BOUND_CHECK("max_packet_size", inst->max_packet_size, >=, 64); + FR_INTEGER_BOUND_CHECK("max_packet_size", inst->max_packet_size, <=, 65535); + + if ((inst->mode != RLM_RADIUS_MODE_UNCONNECTED_REPLICATE) && + (inst->mode != RLM_RADIUS_MODE_UNCONNECTED_PROXY)) { + /* + * These limits are specific to RADIUS, and cannot be over-ridden + */ + FR_INTEGER_BOUND_CHECK("trunk.per_connection_max", inst->trunk_conf.max_req_per_conn, >=, 2); + FR_INTEGER_BOUND_CHECK("trunk.per_connection_max", inst->trunk_conf.max_req_per_conn, <=, 255); + FR_INTEGER_BOUND_CHECK("trunk.per_connection_target", inst->trunk_conf.target_req_per_conn, <=, inst->trunk_conf.max_req_per_conn / 2); + } else { + if (inst->fd_config.src_port != 0) { + cf_log_err(conf, "Cannot set 'src_port' when using 'mode = unconnected'"); + return -1; + } + } FR_TIME_DELTA_BOUND_CHECK("response_window", inst->zombie_period, >=, fr_time_delta_from_sec(1)); FR_TIME_DELTA_BOUND_CHECK("response_window", inst->zombie_period, <=, fr_time_delta_from_sec(120)); @@ -460,6 +658,17 @@ static int mod_instantiate(module_inst_ctx_t const *mctx) num_types = talloc_array_length(inst->types); fr_assert(num_types > 0); + inst->timeout_retry = (fr_retry_config_t) { + .mrc = 1, + .mrd = inst->response_window, + }; + + inst->common_ctx = (fr_radius_ctx_t) { + .secret = inst->secret, + .secret_length = inst->secret ? talloc_array_length(inst->secret) - 1 : 0, + .proxy_state = fr_rand(), + }; + /* * Allow for O(1) lookup later... */ @@ -479,13 +688,20 @@ static int mod_instantiate(module_inst_ctx_t const *mctx) * If we're replicating, we don't care if the other end * is alive. */ - if (inst->replicate && inst->status_check) { - cf_log_warn(conf, "Ignoring 'status_check = %s' due to 'replicate = true'", - fr_radius_packet_name[inst->status_check]); - inst->status_check = 0; + if (inst->status_check) { + if (inst->mode == RLM_RADIUS_MODE_REPLICATE) { + cf_log_warn(conf, "Ignoring 'status_check = %s' due to 'mode = replicate'", + fr_radius_packet_name[inst->status_check]); + inst->status_check = false; + + } else if ((inst->mode == RLM_RADIUS_MODE_UNCONNECTED_REPLICATE) || + (inst->mode == RLM_RADIUS_MODE_UNCONNECTED_PROXY)) { + cf_log_warn(conf, "Ignoring 'status_check = %s' due to 'mode = unconnected-*'", + fr_radius_packet_name[inst->status_check]); + inst->status_check = false; + } } - /* * If we have status checks, then do some sanity checks. * Status-Server is always allowed. Otherwise, the @@ -507,13 +723,29 @@ static int mod_instantiate(module_inst_ctx_t const *mctx) * section, to be sure that (e.g.) Access-Request * contains User-Name, etc. */ + + if (inst->fd_config.filename) { + cf_log_info(conf, "Disabling status checks for output file %s", inst->fd_config.filename); + inst->status_check = 0; + } + } + + /* + * Files and unix sockets can just have us call write(). + */ + if (inst->fd_config.filename || inst->fd_config.path) { + inst->max_send_coalesce = 1; } + inst->trunk_conf.req_pool_headers = 4; /* One for the request, one for the buffer, one for the tracking binding, one for Proxy-State VP */ + inst->trunk_conf.req_pool_size = 1024 + sizeof(fr_pair_t) + 20; + /* - * Don't sanity check the async timers if we're doing - * synchronous proxying. + * Only check the async timers when we're acting as a client. */ - if (inst->synchronous) goto setup_io_submodule; + if (inst->mode != RLM_RADIUS_MODE_CLIENT) { + return 0; + } /* * Set limits on retransmission timers @@ -596,15 +828,32 @@ static int mod_instantiate(module_inst_ctx_t const *mctx) FR_TIME_DELTA_BOUND_CHECK("Disconnect-Request.max_rtx_duration", inst->retry[FR_RADIUS_CODE_DISCONNECT_REQUEST].mrd, <=, fr_time_delta_from_sec(30)); } -setup_io_submodule: - /* - * Get random Proxy-State identifier for this module. - */ - inst->proxy_state = fr_rand(); + return 0; +} + +static int mod_bootstrap(module_inst_ctx_t const *mctx) +{ + xlat_t *xlat; + rlm_radius_t const *inst = talloc_get_type_abort(mctx->mi->data, rlm_radius_t); + + switch (inst->mode) { + case RLM_RADIUS_MODE_UNCONNECTED_REPLICATE: + xlat = module_rlm_xlat_register(mctx->mi->boot, mctx, "sendto.ipaddr", xlat_radius_replicate, FR_TYPE_VOID); + xlat_func_args_set(xlat, xlat_radius_send_args); + break; + + case RLM_RADIUS_MODE_UNCONNECTED_PROXY: + fr_assert(0); /* not implemented */ + break; + + default: + break; + } return 0; } + static int mod_detach(module_detach_ctx_t const *mctx) { rlm_radius_t *inst = talloc_get_type_abort(mctx->mi->data, rlm_radius_t); @@ -647,8 +896,13 @@ module_rlm_t rlm_radius = { .onload = mod_load, .unload = mod_unload, + .bootstrap = mod_bootstrap, .instantiate = mod_instantiate, - .detach = mod_detach + .detach = mod_detach, + + .thread_inst_size = sizeof(bio_thread_t), + .thread_inst_type = "bio_thread_t", + .thread_instantiate = mod_thread_instantiate, }, .method_group = { .bindings = (module_method_binding_t[]){ diff --git a/src/modules/rlm_radius/rlm_radius.h b/src/modules/rlm_radius/rlm_radius.h index bc14e6e0e793..13697630e9bf 100644 --- a/src/modules/rlm_radius/rlm_radius.h +++ b/src/modules/rlm_radius/rlm_radius.h @@ -23,6 +23,9 @@ #include #include #include +#include + +#include /* * $Id$ @@ -34,32 +37,47 @@ */ typedef struct rlm_radius_s rlm_radius_t; -typedef struct rlm_radius_io_s rlm_radius_io_t; + +typedef enum { + RLM_RADIUS_MODE_INVALID = 0, + RLM_RADIUS_MODE_PROXY, + RLM_RADIUS_MODE_CLIENT, + RLM_RADIUS_MODE_REPLICATE, + RLM_RADIUS_MODE_UNCONNECTED_REPLICATE, + RLM_RADIUS_MODE_UNCONNECTED_PROXY, +} rlm_radius_mode_t; /* * Define a structure for our module configuration. */ struct rlm_radius_s { + fr_bio_fd_config_t fd_config; //!< for now MUST be at the start! + char const *name; - module_instance_t *io_submodule; - rlm_radius_io_t const *io; //!< Public symbol exported by the submodule. fr_time_delta_t response_window; fr_time_delta_t zombie_period; fr_time_delta_t revive_interval; + char const *secret; //!< Shared secret. + + uint32_t max_packet_size; //!< Maximum packet size. + uint16_t max_send_coalesce; //!< Maximum number of packets to coalesce into one mmsg call. + + fr_radius_ctx_t common_ctx; + bool replicate; //!< Ignore responses. bool synchronous; //!< Retransmit when receiving a duplicate request. - bool originate; //!< Originating packets, instead of proxying existing ones. + bool originate; //!< Originating packets, instead of proxying existing ones. ///< Controls whether Proxy-State is added to the outbound - ///< request. + ///< request + rlm_radius_mode_t mode; //!< proxy, client, etc. uint32_t max_attributes; //!< Maximum number of attributes to decode in response. fr_radius_require_ma_t require_message_authenticator; //!< Require Message-Authenticator in responses. bool *received_message_authenticator; //!< Received Message-Authenticator in responses. - uint32_t proxy_state; //!< Unique ID (mostly) of this module. uint32_t *types; //!< array of allowed packet types uint32_t status_check; //!< code of status-check type map_list_t status_check_map; //!< attributes for the status-server checks @@ -67,21 +85,9 @@ struct rlm_radius_s { ///< mark the connection as alive. bool allowed[FR_RADIUS_CODE_MAX]; + + fr_retry_config_t timeout_retry; fr_retry_config_t retry[FR_RADIUS_CODE_MAX]; trunk_conf_t trunk_conf; //!< trunk configuration }; - -/** Enqueue a request_t to an IO submodule - * - */ -typedef unlang_action_t (*rlm_radius_io_enqueue_t)(rlm_rcode_t *p_result, void *instance, void *thread, request_t *request); - -/** Public structure describing an I/O path for an outgoing socket. - * - * This structure is exported by client I/O modules e.g. rlm_radius_udp. - */ -struct rlm_radius_io_s { - module_t common; //!< Common fields to all loadable modules. - rlm_radius_io_enqueue_t enqueue; //!< Enqueue a request_t with an IO submodule. -}; diff --git a/src/modules/rlm_radius/track.c b/src/modules/rlm_radius/track.c index 80a69d6923be..b4d71316d4d7 100644 --- a/src/modules/rlm_radius/track.c +++ b/src/modules/rlm_radius/track.c @@ -30,7 +30,6 @@ RCSID("$Id$") #include #include "track.h" -#include "rlm_radius.h" /** Create an radius_track_t * @@ -57,25 +56,10 @@ radius_track_t *radius_track_alloc(TALLOC_CTX *ctx) fr_dlist_insert_tail(&tt->free_list, &tt->id[i]); } - tt->next_id = fr_rand() & 0xff; - return tt; } -/** Compare two radius_track_entry_t - * - */ -static int8_t te_cmp(void const *one, void const *two) -{ - radius_track_entry_t const *a = one; - radius_track_entry_t const *b = two; - int ret; - - ret = memcmp(a->vector, b->vector, sizeof(a->vector)); - return CMP(ret, 0); -} - /** Ensures the entry is released when the ctx passed to radius_track_entry_reserve is freed * * @param[in] te_p Entry to release. @@ -132,7 +116,7 @@ int radius_track_entry_reserve( * don't use it". Ensure that we only return IDs * which are in the static array. */ - if (!tt->use_authenticator && (te != &tt->id[te->id])) { + if (te != &tt->id[te->id]) { talloc_free(te); goto retry; } @@ -144,31 +128,8 @@ int radius_track_entry_reserve( * There are no free entries, and we can't use the * Request Authenticator. Oh well... */ - if (!tt->use_authenticator) { - fr_strerror_const("No free entries"); - return -1; - } - - /* - * Get a new ID. It's value doesn't matter at this - * point. - */ - tt->next_id++; - tt->next_id &= 0xff; - - /* - * If needed, allocate a subtree. - */ - if (!tt->subtree[tt->next_id]) { - MEM(tt->subtree[tt->next_id] = fr_rb_inline_talloc_alloc(tt, radius_track_entry_t, node, - te_cmp, NULL)); - } - - /* - * Allocate a new one, and insert it into the appropriate subtree. - */ - te = talloc_zero(tt, radius_track_entry_t); - te->id = tt->next_id; + fr_strerror_const("No free entries"); + return -1; done: te->tt = tt; @@ -238,43 +199,8 @@ int radius_track_entry_release( /* * We're freeing a static ID, just go do that... */ - if (te == &tt->id[te->id]) { - /* - * This entry MAY be in a subtree. If so, delete - * it. - */ - if (tt->subtree[te->id]) (void) fr_rb_delete(tt->subtree[te->id], te); + fr_assert(te == &tt->id[te->id]); - goto done; - } - - /* - * At this point, it MUST be talloc'd. - */ - (void) talloc_get_type_abort(te, radius_track_entry_t); - - /* - * Delete it from the tracking subtree. - */ - fr_assert(tt->subtree[te->id] != NULL); - (void) fr_rb_delete(tt->subtree[te->id], te); - - /* - * Try to free memory if the system gets idle. If the - * system is busy, we will try to keep entries in the - * free list. If the system becomes completely idle, we - * will clear the free list. - */ - if (fr_dlist_num_elements(&tt->free_list) > tt->num_requests) { - talloc_free(te); - *te_to_free = NULL; - return 0; - } - - /* - * Otherwise put it back on the free list. - */ -done: fr_dlist_insert_tail(&tt->free_list, te); *te_to_free = NULL; @@ -292,15 +218,12 @@ int radius_track_entry_release( */ int radius_track_entry_update(radius_track_entry_t *te, uint8_t const *vector) { +#ifndef NDEBUG radius_track_t *tt = te->tt; +#endif fr_assert(tt); - /* - * The authentication vector may have changed. - */ - if (tt->subtree[te->id]) (void) fr_rb_delete(tt->subtree[te->id], te); - memcpy(te->vector, vector, sizeof(te->vector)); /* @@ -309,20 +232,7 @@ int radius_track_entry_update(radius_track_entry_t *te, uint8_t const *vector) * * @todo - gracefully handle fallback if the server screws up. */ - if (!tt->use_authenticator) { - fr_assert(te == &tt->id[te->id]); - return 0; - } - - /* - * Insert it into the tree of authenticators - * - * We do this even if it was allocated from the static - * array. That way if the server responds with - * Original-Request-Authenticator, we can easily find it. - */ - if (!fr_rb_insert(tt->subtree[te->id], te)) return -1; - + fr_assert(te == &tt->id[te->id]); return 0; } @@ -337,81 +247,40 @@ int radius_track_entry_update(radius_track_entry_t *te, uint8_t const *vector) */ radius_track_entry_t *radius_track_entry_find(radius_track_t *tt, uint8_t packet_id, uint8_t const *vector) { - radius_track_entry_t my_te, *te; + radius_track_entry_t *te; (void) talloc_get_type_abort(tt, radius_track_t); /* * Just use the static array. */ - if (!tt->use_authenticator || !vector) { - te = &tt->id[packet_id]; - - /* - * Not in use, die. - */ - if (!te->request) return NULL; - - /* - * Ignore the Request Authenticator, as the - * caller doesn't have it. - */ - return te; - } + te = &tt->id[packet_id]; /* - * The entry MAY be in the subtree! + * Not in use, die. */ - memcpy(&my_te.vector, vector, sizeof(my_te.vector)); + if (!te->request) return NULL; - te = fr_rb_find(tt->subtree[packet_id], &my_te); + if (!vector) return te; /* - * Not found, the packet MAY have been allocated in the - * old-style method prior to negotiation of - * Original-Request-Identifier. + * Protocol-Error and Original-Packet-Vector + * + * This should arguably have been Original-Packet-Code, but we are stupid. + * + * @todo - Allow for multiple ID arrays, one for each packet code. Or, just switch to using + * src/protocols/radius/id.[ch]. */ - if (!te) { - te = &tt->id[packet_id]; - - /* - * Not in use, die. - */ - if (!te->request) return NULL; - - // @todo - add a "generation" count for packets, so we can skip this after all outstanding packets - // are using the new method. Hmm... probably just a timer "last sent packet with old-style" - // and then compare it to te->start - - /* - * We have the vector, so we need to check it. - */ - if (memcmp(te->vector, vector, sizeof(te->vector)) != 0) { - return NULL; - } - - return te; - } - - (void) talloc_get_type_abort(te, radius_track_entry_t); - fr_assert(te->request != NULL); + if (memcmp(te->vector, vector, sizeof(te->vector)) != 0) return NULL; + /* + * Ignore the Request Authenticator, as the + * caller doesn't have it. + */ return te; } -/** Use Request Authenticator (or not) as an Identifier - * - * @param tt The radius_track_t tracking table - * @param flag Whether or not to use it. - */ -void radius_track_use_authenticator(radius_track_t *tt, bool flag) -{ - (void) talloc_get_type_abort(tt, radius_track_t); - - tt->use_authenticator = flag; -} - #ifndef NDEBUG /** Print out the state of every tracking entry * diff --git a/src/modules/rlm_radius/track.h b/src/modules/rlm_radius/track.h index 0180ad7ce5a5..327361129818 100644 --- a/src/modules/rlm_radius/track.h +++ b/src/modules/rlm_radius/track.h @@ -42,7 +42,7 @@ struct radius_track_entry_s { ///< when its parent is freed. We also zero ///< out the tracking entry field in the parent. - request_t *request; //!< as always... + request_t *request; //!< as always... void *uctx; //!< Result/resumption context. @@ -66,13 +66,8 @@ struct radius_track_s { fr_dlist_head_t free_list; //!< so we allocate by least recently used - bool use_authenticator; //!< whether to use the request authenticator as an ID - int next_id; //!< next ID to allocate - radius_track_entry_t id[UINT8_MAX + 1]; //!< which ID was used - fr_rb_tree_t *subtree[UINT8_MAX + 1]; //!< for Original-Request-Authenticator - #ifndef NDEBUG uint64_t operation; //!< Incremented each alloc and de-alloc #endif @@ -119,5 +114,3 @@ int radius_track_entry_update(radius_track_entry_t *te, radius_track_entry_t *radius_track_entry_find(radius_track_t *tt, uint8_t packet_id, uint8_t const *vector) CC_HINT(nonnull(1)); - -void radius_track_use_authenticator(radius_track_t *te, bool flag) CC_HINT(nonnull); diff --git a/src/modules/rlm_radius2/README.md b/src/modules/rlm_radius2/README.md deleted file mode 100644 index 944923bbcd3a..000000000000 --- a/src/modules/rlm_radius2/README.md +++ /dev/null @@ -1,10 +0,0 @@ -# rlm_radius -## Metadata -
-
category
io
-
- -## Summary -Allows Access-Requests, Accounting-Requests, CoA-Requests and Disconnect-Messages to be sent during request processing. - -This module can be used to implement proxying and request fan-out, as well as synchronous and asynchronous CoA and DM. diff --git a/src/modules/rlm_radius2/all.mk b/src/modules/rlm_radius2/all.mk deleted file mode 100644 index c8a7b8ca9ec7..000000000000 --- a/src/modules/rlm_radius2/all.mk +++ /dev/null @@ -1,14 +0,0 @@ -# -# For now this is tracked in Git, but isn't part of the -# normal build. -# -ifneq "${WITH_RADIUS2}" "" -TARGETNAME := rlm_radius -TARGET := $(TARGETNAME)$(L) - -SOURCES := rlm_radius.c track.c - -TGT_PREREQS := libfreeradius-radius$(L) libfreeradius-bio-config$(L) libfreeradius-bio$(L) -LOG_ID_LIB = 39 - -endif diff --git a/src/modules/rlm_radius2/rlm_radius.c b/src/modules/rlm_radius2/rlm_radius.c deleted file mode 100644 index 40d5f4d14ea6..000000000000 --- a/src/modules/rlm_radius2/rlm_radius.c +++ /dev/null @@ -1,915 +0,0 @@ -/* - * This program is 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 rlm_radius.c - * @brief A RADIUS client library. - * - * @copyright 2016 The FreeRADIUS server project - * @copyright 2016 Network RADIUS SAS - */ -RCSID("$Id$") - -#include -#include -#include -#include -#include - -#include "rlm_radius.h" - -static int mode_parse(TALLOC_CTX *ctx, void *out, UNUSED void *parent, CONF_ITEM *ci, conf_parser_t const *rule); -static int type_parse(TALLOC_CTX *ctx, void *out, UNUSED void *parent, CONF_ITEM *ci, conf_parser_t const *rule); -static int status_check_type_parse(TALLOC_CTX *ctx, void *out, UNUSED void *parent, CONF_ITEM *ci, conf_parser_t const *rule); -static int status_check_update_parse(TALLOC_CTX *ctx, void *out, UNUSED void *parent, CONF_ITEM *ci, conf_parser_t const *rule); - -static conf_parser_t const status_check_config[] = { - { FR_CONF_OFFSET_TYPE_FLAGS("type", FR_TYPE_VOID, 0, rlm_radius_t, status_check), - .func = status_check_type_parse }, - - CONF_PARSER_TERMINATOR -}; - -static conf_parser_t const status_check_update_config[] = { - { FR_CONF_OFFSET_TYPE_FLAGS("update", FR_TYPE_VOID, CONF_FLAG_SUBSECTION | CONF_FLAG_REQUIRED, rlm_radius_t, status_check_map), - .name2 = CF_IDENT_ANY, - .func = status_check_update_parse }, - { FR_CONF_OFFSET("num_answers_to_alive", rlm_radius_t, num_answers_to_alive), .dflt = STRINGIFY(3) }, - - CONF_PARSER_TERMINATOR -}; - -/* - * Retransmission intervals for the packets we support. - */ -static conf_parser_t auth_config[] = { - { FR_CONF_OFFSET("initial_rtx_time", rlm_radius_t, retry[FR_RADIUS_CODE_ACCESS_REQUEST].irt), .dflt = STRINGIFY(2) }, - { FR_CONF_OFFSET("max_rtx_time", rlm_radius_t, retry[FR_RADIUS_CODE_ACCESS_REQUEST].mrt), .dflt = STRINGIFY(16) }, - { FR_CONF_OFFSET("max_rtx_count", rlm_radius_t, retry[FR_RADIUS_CODE_ACCESS_REQUEST].mrc), .dflt = STRINGIFY(5) }, - { FR_CONF_OFFSET("max_rtx_duration", rlm_radius_t, retry[FR_RADIUS_CODE_ACCESS_REQUEST].mrd), .dflt = STRINGIFY(30) }, - CONF_PARSER_TERMINATOR -}; - -static conf_parser_t acct_config[] = { - { FR_CONF_OFFSET("initial_rtx_time", rlm_radius_t, retry[FR_RADIUS_CODE_ACCOUNTING_REQUEST].irt), .dflt = STRINGIFY(2) }, - { FR_CONF_OFFSET("max_rtx_time", rlm_radius_t, retry[FR_RADIUS_CODE_ACCOUNTING_REQUEST].mrt), .dflt = STRINGIFY(5) }, - { FR_CONF_OFFSET("max_rtx_count", rlm_radius_t, retry[FR_RADIUS_CODE_ACCOUNTING_REQUEST].mrc), .dflt = STRINGIFY(1) }, - { FR_CONF_OFFSET("max_rtx_duration", rlm_radius_t, retry[FR_RADIUS_CODE_ACCOUNTING_REQUEST].mrd), .dflt = STRINGIFY(30) }, - CONF_PARSER_TERMINATOR -}; - -static conf_parser_t status_config[] = { - { FR_CONF_OFFSET("initial_rtx_time", rlm_radius_t, retry[FR_RADIUS_CODE_STATUS_SERVER].irt), .dflt = STRINGIFY(2) }, - { FR_CONF_OFFSET("max_rtx_time", rlm_radius_t, retry[FR_RADIUS_CODE_STATUS_SERVER].mrt), .dflt = STRINGIFY(5) }, - { FR_CONF_OFFSET("max_rtx_count", rlm_radius_t, retry[FR_RADIUS_CODE_STATUS_SERVER].mrc), .dflt = STRINGIFY(5) }, - { FR_CONF_OFFSET("max_rtx_duration", rlm_radius_t, retry[FR_RADIUS_CODE_STATUS_SERVER].mrd), .dflt = STRINGIFY(30) }, - CONF_PARSER_TERMINATOR -}; - -static conf_parser_t coa_config[] = { - { FR_CONF_OFFSET("initial_rtx_time", rlm_radius_t, retry[FR_RADIUS_CODE_COA_REQUEST].irt), .dflt = STRINGIFY(2) }, - { FR_CONF_OFFSET("max_rtx_time", rlm_radius_t, retry[FR_RADIUS_CODE_COA_REQUEST].mrt), .dflt = STRINGIFY(16) }, - { FR_CONF_OFFSET("max_rtx_count", rlm_radius_t, retry[FR_RADIUS_CODE_COA_REQUEST].mrc), .dflt = STRINGIFY(5) }, - { FR_CONF_OFFSET("max_rtx_duration", rlm_radius_t, retry[FR_RADIUS_CODE_COA_REQUEST].mrd), .dflt = STRINGIFY(30) }, - CONF_PARSER_TERMINATOR -}; - -static conf_parser_t disconnect_config[] = { - { FR_CONF_OFFSET("initial_rtx_time", rlm_radius_t, retry[FR_RADIUS_CODE_DISCONNECT_REQUEST].irt), .dflt = STRINGIFY(2) }, - { FR_CONF_OFFSET("max_rtx_time", rlm_radius_t, retry[FR_RADIUS_CODE_DISCONNECT_REQUEST].mrt), .dflt = STRINGIFY(16) }, - { FR_CONF_OFFSET("max_rtx_count", rlm_radius_t, retry[FR_RADIUS_CODE_DISCONNECT_REQUEST].mrc), .dflt = STRINGIFY(5) }, - { FR_CONF_OFFSET("max_rtx_duration", rlm_radius_t, retry[FR_RADIUS_CODE_DISCONNECT_REQUEST].mrd), .dflt = STRINGIFY(30) }, - CONF_PARSER_TERMINATOR -}; - -static conf_parser_t const transport_config[] = { - { FR_CONF_OFFSET_FLAGS("secret", CONF_FLAG_REQUIRED, rlm_radius_t, secret) }, - - CONF_PARSER_TERMINATOR -}; - -/* - * We only parse the pool options if we're connected. - */ -static conf_parser_t const connected_config[] = { - { FR_CONF_POINTER("status_check", 0, CONF_FLAG_SUBSECTION, NULL), .subcs = (void const *) status_check_config }, - - { FR_CONF_OFFSET_SUBSECTION("pool", 0, rlm_radius_t, trunk_conf, trunk_config ) }, - - { FR_CONF_POINTER("udp", 0, CONF_FLAG_SUBSECTION | CONF_FLAG_OPTIONAL, NULL), .subcs = (void const *) transport_config }, - - { FR_CONF_POINTER("tcp", 0, CONF_FLAG_SUBSECTION | CONF_FLAG_OPTIONAL, NULL), .subcs = (void const *) transport_config }, - - CONF_PARSER_TERMINATOR -}; - -/* - * A mapping of configuration file names to internal variables. - */ -static conf_parser_t const module_config[] = { - { FR_CONF_OFFSET_FLAGS("mode", CONF_FLAG_REQUIRED, rlm_radius_t, mode), .func = mode_parse, .dflt = "proxy" }, - - { FR_CONF_OFFSET_REF(rlm_radius_t, fd_config, fr_bio_fd_client_config) }, - - { FR_CONF_OFFSET_FLAGS("type", CONF_FLAG_NOT_EMPTY | CONF_FLAG_MULTI | CONF_FLAG_REQUIRED, rlm_radius_t, types), - .func = type_parse }, - - { FR_CONF_OFFSET_FLAGS("replicate", CONF_FLAG_DEPRECATED, rlm_radius_t, replicate) }, - - { FR_CONF_OFFSET_FLAGS("synchronous", CONF_FLAG_DEPRECATED, rlm_radius_t, synchronous) }, - - { FR_CONF_OFFSET_FLAGS("originate", CONF_FLAG_DEPRECATED, rlm_radius_t, originate) }, - - { FR_CONF_OFFSET("max_packet_size", rlm_radius_t, max_packet_size), .dflt = "4096" }, - { FR_CONF_OFFSET("max_send_coalesce", rlm_radius_t, max_send_coalesce), .dflt = "1024" }, - - { FR_CONF_OFFSET("max_attributes", rlm_radius_t, max_attributes), .dflt = STRINGIFY(RADIUS_MAX_ATTRIBUTES) }, - - { FR_CONF_OFFSET("require_message_authenticator", rlm_radius_t, require_message_authenticator), - .func = cf_table_parse_int, - .uctx = &(cf_table_parse_ctx_t){ .table = fr_radius_require_ma_table, .len = &fr_radius_require_ma_table_len }, - .dflt = "no" }, - - { FR_CONF_OFFSET("response_window", rlm_radius_t, response_window), .dflt = STRINGIFY(20) }, - - { FR_CONF_OFFSET("zombie_period", rlm_radius_t, zombie_period), .dflt = STRINGIFY(40) }, - - { FR_CONF_OFFSET("revive_interval", rlm_radius_t, revive_interval) }, - - CONF_PARSER_TERMINATOR -}; - -static conf_parser_t const type_interval_config[FR_RADIUS_CODE_MAX] = { - [FR_RADIUS_CODE_ACCESS_REQUEST] = { FR_CONF_POINTER("Access-Request", 0, CONF_FLAG_SUBSECTION, NULL), .subcs = (void const *) auth_config }, - - [FR_RADIUS_CODE_ACCOUNTING_REQUEST] = { FR_CONF_POINTER("Accounting-Request", 0, CONF_FLAG_SUBSECTION, NULL), .subcs = (void const *) acct_config }, - [FR_RADIUS_CODE_STATUS_SERVER] = { FR_CONF_POINTER("Status-Server", 0, CONF_FLAG_SUBSECTION, NULL), .subcs = (void const *) status_config }, - [FR_RADIUS_CODE_COA_REQUEST] = { FR_CONF_POINTER("CoA-Request", 0, CONF_FLAG_SUBSECTION, NULL), .subcs = (void const *) coa_config }, - [FR_RADIUS_CODE_DISCONNECT_REQUEST] = { FR_CONF_POINTER("Disconnect-Request", 0, CONF_FLAG_SUBSECTION, NULL), .subcs = (void const *) disconnect_config }, -}; - -static fr_dict_t const *dict_radius; - -extern fr_dict_autoload_t rlm_radius_dict[]; -fr_dict_autoload_t rlm_radius_dict[] = { - { .out = &dict_radius, .proto = "radius" }, - { NULL } -}; - -static fr_dict_attr_t const *attr_chap_challenge; -static fr_dict_attr_t const *attr_chap_password; -static fr_dict_attr_t const *attr_packet_type; -static fr_dict_attr_t const *attr_proxy_state; - -static fr_dict_attr_t const *attr_error_cause; -static fr_dict_attr_t const *attr_event_timestamp; -static fr_dict_attr_t const *attr_extended_attribute_1; -static fr_dict_attr_t const *attr_message_authenticator; -static fr_dict_attr_t const *attr_eap_message; -static fr_dict_attr_t const *attr_nas_identifier; -static fr_dict_attr_t const *attr_original_packet_code; -static fr_dict_attr_t const *attr_response_length; -static fr_dict_attr_t const *attr_user_password; - -extern fr_dict_attr_autoload_t rlm_radius_dict_attr[]; -fr_dict_attr_autoload_t rlm_radius_dict_attr[] = { - { .out = &attr_chap_challenge, .name = "CHAP-Challenge", .type = FR_TYPE_OCTETS, .dict = &dict_radius}, - { .out = &attr_chap_password, .name = "CHAP-Password", .type = FR_TYPE_OCTETS, .dict = &dict_radius}, - { .out = &attr_packet_type, .name = "Packet-Type", .type = FR_TYPE_UINT32, .dict = &dict_radius }, - { .out = &attr_proxy_state, .name = "Proxy-State", .type = FR_TYPE_OCTETS, .dict = &dict_radius}, - - { .out = &attr_error_cause, .name = "Error-Cause", .type = FR_TYPE_UINT32, .dict = &dict_radius }, - { .out = &attr_event_timestamp, .name = "Event-Timestamp", .type = FR_TYPE_DATE, .dict = &dict_radius}, - { .out = &attr_extended_attribute_1, .name = "Extended-Attribute-1", .type = FR_TYPE_TLV, .dict = &dict_radius}, - { .out = &attr_message_authenticator, .name = "Message-Authenticator", .type = FR_TYPE_OCTETS, .dict = &dict_radius}, - { .out = &attr_eap_message, .name = "EAP-Message", .type = FR_TYPE_OCTETS, .dict = &dict_radius}, - { .out = &attr_nas_identifier, .name = "NAS-Identifier", .type = FR_TYPE_STRING, .dict = &dict_radius}, - { .out = &attr_original_packet_code, .name = "Extended-Attribute-1.Original-Packet-Code", .type = FR_TYPE_UINT32, .dict = &dict_radius}, - { .out = &attr_response_length, .name = "Extended-Attribute-1.Response-Length", .type = FR_TYPE_UINT32, .dict = &dict_radius }, - { .out = &attr_user_password, .name = "User-Password", .type = FR_TYPE_STRING, .dict = &dict_radius}, - - { NULL } -}; - -#include "bio.c" - -static fr_table_num_sorted_t mode_names[] = { - { L("client"), RLM_RADIUS_MODE_CLIENT }, - { L("proxy"), RLM_RADIUS_MODE_PROXY }, - { L("replicate"), RLM_RADIUS_MODE_REPLICATE }, - { L("unconnected-replicate"), RLM_RADIUS_MODE_UNCONNECTED_REPLICATE }, -// { L("unconnected-proxy"), RLM_RADIUS_MODE_UNCONNECTED_PROXY }, -}; -static size_t mode_names_len = NUM_ELEMENTS(mode_names); - - -/** Set the mode of operation - * - * @param[in] ctx to allocate data in (instance of rlm_radius). - * @param[out] out Where to write the parsed data. - * @param[in] parent Base structure address. - * @param[in] ci #CONF_PAIR specifying the name of the type module. - * @param[in] rule unused. - * @return - * - 0 on success. - * - -1 on failure. - */ -static int mode_parse(UNUSED TALLOC_CTX *ctx, void *out, void *parent, - CONF_ITEM *ci, UNUSED conf_parser_t const *rule) -{ - char const *name = cf_pair_value(cf_item_to_pair(ci)); - rlm_radius_mode_t mode; - rlm_radius_t *inst = talloc_get_type_abort(parent, rlm_radius_t); - - mode = fr_table_value_by_str(mode_names, name, RLM_RADIUS_MODE_INVALID); - -#if 0 - /* - * Commented out until we upgrade the old configurations. - */ - if (mode == RLM_RADIUS_MODE_INVALID) { - cf_log_err(ci, "Invalid mode name \"%s\"", name); - return -1; - } -#endif - - *(rlm_radius_mode_t *) out = mode; - - /* - * Normally we want connected sockets, in which case we push additional configuration for connected sockets. - */ - if ((mode != RLM_RADIUS_MODE_UNCONNECTED_REPLICATE) && - (mode != RLM_RADIUS_MODE_UNCONNECTED_PROXY)) { - CONF_SECTION *cs = cf_item_to_section(cf_parent(ci)); - - inst->fd_config.type = FR_BIO_FD_CONNECTED; - - if (cf_section_rules_push(cs, connected_config) < 0) return -1; - - } else { - inst->fd_config.type = FR_BIO_FD_UNCONNECTED; - } - - return 0; -} - - -/** Set which types of packets we can parse - * - * @param[in] ctx to allocate data in (instance of rlm_radius). - * @param[out] out Where to write the parsed data. - * @param[in] parent Base structure address. - * @param[in] ci #CONF_PAIR specifying the name of the type module. - * @param[in] rule unused. - * @return - * - 0 on success. - * - -1 on failure. - */ -static int type_parse(UNUSED TALLOC_CTX *ctx, void *out, UNUSED void *parent, - CONF_ITEM *ci, UNUSED conf_parser_t const *rule) -{ - char const *type_str = cf_pair_value(cf_item_to_pair(ci)); - CONF_SECTION *cs = cf_item_to_section(cf_parent(ci)); - fr_dict_enum_value_t const *type_enum; - uint32_t code; - - /* - * Must be the RADIUS module - */ - fr_assert(cs && (strcmp(cf_section_name1(cs), "radius") == 0)); - - /* - * Allow the process module to be specified by - * packet type. - */ - type_enum = fr_dict_enum_by_name(attr_packet_type, type_str, -1); - if (!type_enum) { - invalid_code: - cf_log_err(ci, "Unknown or invalid RADIUS packet type '%s'", type_str); - return -1; - } - - code = type_enum->value->vb_uint32; - - /* - * Status-Server packets cannot be proxied. - */ - if (code == FR_RADIUS_CODE_STATUS_SERVER) { - cf_log_err(ci, "Invalid setting of 'type = Status-Server'. Status-Server packets cannot be proxied."); - return -1; - } - - if (!code || - (code >= FR_RADIUS_CODE_MAX) || - (!type_interval_config[code].name1)) goto invalid_code; - - /* - * If we're doing async proxying, push the timers for the - * various packet types. - */ - cf_section_rule_push(cs, &type_interval_config[code]); - - *(uint32_t *) out = code; - - return 0; -} - -/** Allow for Status-Server ping checks - * - * @param[in] ctx to allocate data in (instance of proto_radius). - * @param[out] out Where to write our parsed data. - * @param[in] parent Base structure address. - * @param[in] ci #CONF_PAIR specifying the name of the type module. - * @param[in] rule unused. - * @return - * - 0 on success. - * - -1 on failure. - */ -static int status_check_type_parse(UNUSED TALLOC_CTX *ctx, void *out, UNUSED void *parent, - CONF_ITEM *ci, UNUSED conf_parser_t const *rule) -{ - char const *type_str = cf_pair_value(cf_item_to_pair(ci)); - CONF_SECTION *cs = cf_item_to_section(cf_parent(ci)); - fr_dict_enum_value_t const *type_enum; - uint32_t code; - - /* - * Allow the process module to be specified by - * packet type. - */ - type_enum = fr_dict_enum_by_name(attr_packet_type, type_str, -1); - if (!type_enum) { - invalid_code: - cf_log_err(ci, "Unknown or invalid RADIUS packet type '%s'", type_str); - return -1; - } - - code = type_enum->value->vb_uint32; - - /* - * Cheat, and reuse the "type" array for allowed packet - * types. - */ - if (!code || - (code >= FR_RADIUS_CODE_MAX) || - (!type_interval_config[code].name1)) goto invalid_code; - - /* - * Add irt / mrt / mrd / mrc parsing, in the parent - * configuration section. - */ - cf_section_rule_push(cf_item_to_section(cf_parent(cs)), &type_interval_config[code]); - - memcpy(out, &code, sizeof(code)); - - /* - * Nothing more to do here, so we stop. - */ - if (code == FR_RADIUS_CODE_STATUS_SERVER) return 0; - - cf_section_rule_push(cs, status_check_update_config); - - return 0; -} - -/** Allow the admin to set packet contents for Status-Server ping checks - * - * @param[in] ctx to allocate data in (instance of proto_radius). - * @param[out] out Where to write our parsed data - * @param[in] parent Base structure address. - * @param[in] ci #CONF_SECTION specifying the things to update - * @param[in] rule unused. - * @return - * - 0 on success. - * - -1 on failure. - */ -static int status_check_update_parse(TALLOC_CTX *ctx, void *out, UNUSED void *parent, - CONF_ITEM *ci, UNUSED conf_parser_t const *rule) -{ - int rcode; - CONF_SECTION *cs; - char const *name2; - map_list_t *head = (map_list_t *)out; - - fr_assert(cf_item_is_section(ci)); - map_list_init(head); - - cs = cf_item_to_section(ci); - name2 = cf_section_name2(cs); - if (!name2 || (strcmp(name2, "request") != 0)) { - cf_log_err(cs, "You must specify 'request' as the destination list"); - return -1; - } - - /* - * Compile the "update" section. - */ - { - tmpl_rules_t parse_rules = { - .attr = { - .dict_def = dict_radius, - } - }; - - rcode = map_afrom_cs(ctx, head, cs, &parse_rules, &parse_rules, unlang_fixup_update, NULL, 128); - if (rcode < 0) return -1; /* message already printed */ - if (map_list_empty(head)) { - cf_log_err(cs, "'update' sections cannot be empty"); - return -1; - } - } - - /* - * Rely on "bootstrap" to do sanity checks between 'type - * = Access-Request', and 'update' containing passwords. - */ - return 0; -} - - -/** Do any RADIUS-layer fixups for proxying. - * - */ -static void radius_fixups(rlm_radius_t const *inst, request_t *request) -{ - fr_pair_t *vp; - - /* - * Check for proxy loops. - */ - if ((inst->mode == RLM_RADIUS_MODE_PROXY) && RDEBUG_ENABLED) { - fr_dcursor_t cursor; - - for (vp = fr_pair_dcursor_by_da_init(&cursor, &request->request_pairs, attr_proxy_state); - vp; - vp = fr_dcursor_next(&cursor)) { - if (vp->vp_length != 4) continue; - - if (memcmp(&inst->common_ctx.proxy_state, vp->vp_octets, 4) == 0) { - RWARN("Possible proxy loop - please check server configuration."); - break; - } - } - } - - /* - * @todo - check for proxy loops for client && replicate, too. - * - * This only catches "self loops", but it may be worth doing. - */ - - if (request->packet->code != FR_RADIUS_CODE_ACCESS_REQUEST) return; - - if (fr_pair_find_by_da(&request->request_pairs, NULL, attr_chap_password) && - !fr_pair_find_by_da(&request->request_pairs, NULL, attr_chap_challenge)) { - MEM(pair_append_request(&vp, attr_chap_challenge) >= 0); - fr_pair_value_memdup(vp, request->packet->vector, sizeof(request->packet->vector), true); - } -} - - -/** Send packets outbound. - * - */ -static unlang_action_t CC_HINT(nonnull) mod_process(rlm_rcode_t *p_result, module_ctx_t const *mctx, request_t *request) -{ - rlm_radius_t const *inst = talloc_get_type_abort_const(mctx->mi->data, rlm_radius_t); - rlm_rcode_t rcode; - fr_client_t *client; - - if (!request->packet->code) { - REDEBUG("You MUST specify a packet code"); - RETURN_MODULE_FAIL; - } - - /* - * Reserve Status-Server for ourselves, for link-specific - * signaling. - */ - if (request->packet->code == FR_RADIUS_CODE_STATUS_SERVER) { - REDEBUG("Cannot proxy Status-Server packets"); - RETURN_MODULE_FAIL; - } - - if ((request->packet->code >= FR_RADIUS_CODE_MAX) || - !fr_time_delta_ispos(inst->retry[request->packet->code].irt)) { /* can't be zero */ - REDEBUG("Invalid packet code %u", request->packet->code); - RETURN_MODULE_FAIL; - } - - /* - * Unconnected sockets use %radius.replicate(ip, port, secret), - * or %radius.sendto(ip, port, secret) - */ - if ((inst->mode == RLM_RADIUS_MODE_UNCONNECTED_REPLICATE) || - (inst->mode == RLM_RADIUS_MODE_UNCONNECTED_PROXY)) { - REDEBUG("When using 'mode = unconnected-*', this module cannot be used in-place. Instead, it must be called via a function call"); - RETURN_MODULE_FAIL; - } - - if (!inst->allowed[request->packet->code]) { - REDEBUG("Packet code %s is disallowed by the configuration", - fr_radius_packet_name[request->packet->code]); - RETURN_MODULE_FAIL; - } - - client = client_from_request(request); - if (client && client->dynamic && !client->active) { - REDEBUG("Cannot proxy packets which define dynamic clients"); - RETURN_MODULE_FAIL; - } - - /* - * Do any necessary RADIUS level fixups - * - check Proxy-State - * - do CHAP-Challenge fixups - */ - radius_fixups(inst, request); - - /* - * Push the request and it's data to the IO submodule. - * - * This may return YIELD, for "please yield", or it may - * return another code which indicates what happened to - * the request... - */ - return mod_enqueue(&rcode, inst, - module_thread(mctx->mi)->data, request); -} - - -static int mod_instantiate(module_inst_ctx_t const *mctx) -{ - size_t i, num_types; - rlm_radius_t *inst = talloc_get_type_abort(mctx->mi->data, rlm_radius_t); - CONF_SECTION *conf = mctx->mi->conf; - - inst->name = mctx->mi->name; - inst->received_message_authenticator = talloc_zero(NULL, bool); /* Allocated outside of inst to default protection */ - - /* - * Allow explicit setting of mode. - */ - if (inst->mode != RLM_RADIUS_MODE_INVALID) goto check_others; - - /* - * If not set, try to insinuate it from context. - */ - if (inst->replicate) { - if (inst->originate) { - cf_log_err(conf, "Cannot set 'replicate=true' and 'originate=true' at the same time."); - return -1; - } - - if (inst->synchronous) { - cf_log_warn(conf, "Ignoring 'synchronous=true' due to 'replicate=true'"); - } - - inst->mode = RLM_RADIUS_MODE_REPLICATE; - goto check_others; - } - - /* - * Argubly we should be allowed to do synchronous proxying _and_ originating client packets. - * - * However, the previous code didn't really do that consistently. - */ - if (inst->synchronous && inst->originate) { - cf_log_err(conf, "Cannot set 'synchronous=true' and 'originate=true'"); - return -1; - } - - if (inst->synchronous) { - inst->mode = RLM_RADIUS_MODE_PROXY; - } else { - inst->mode = RLM_RADIUS_MODE_CLIENT; - } - -check_others: - /* - * Replication is write-only, and append by default. - */ - if (inst->mode == RLM_RADIUS_MODE_REPLICATE) { - if (inst->fd_config.filename && (inst->fd_config.flags != O_WRONLY)) { - cf_log_info(conf, "Setting 'flags = write-only' for writing to a file"); - } - inst->fd_config.flags = O_WRONLY | O_APPEND; - - } else if (inst->fd_config.filename) { - cf_log_err(conf, "When using an output 'filename', you MUST set 'mode = replicate'"); - return -1; - - } else { - /* - * All other IO is read+write. - */ - inst->fd_config.flags = O_RDWR; - } - - if (fr_bio_fd_check_config(&inst->fd_config) < 0) { - cf_log_perr(conf, "Invalid configuration"); - return -1; - } - - /* - * Clamp max_packet_size first before checking recv_buff and send_buff - */ - FR_INTEGER_BOUND_CHECK("max_packet_size", inst->max_packet_size, >=, 64); - FR_INTEGER_BOUND_CHECK("max_packet_size", inst->max_packet_size, <=, 65535); - - if ((inst->mode != RLM_RADIUS_MODE_UNCONNECTED_REPLICATE) && - (inst->mode != RLM_RADIUS_MODE_UNCONNECTED_PROXY)) { - /* - * These limits are specific to RADIUS, and cannot be over-ridden - */ - FR_INTEGER_BOUND_CHECK("trunk.per_connection_max", inst->trunk_conf.max_req_per_conn, >=, 2); - FR_INTEGER_BOUND_CHECK("trunk.per_connection_max", inst->trunk_conf.max_req_per_conn, <=, 255); - FR_INTEGER_BOUND_CHECK("trunk.per_connection_target", inst->trunk_conf.target_req_per_conn, <=, inst->trunk_conf.max_req_per_conn / 2); - } else { - if (inst->fd_config.src_port != 0) { - cf_log_err(conf, "Cannot set 'src_port' when using 'mode = unconnected'"); - return -1; - } - } - - FR_TIME_DELTA_BOUND_CHECK("response_window", inst->zombie_period, >=, fr_time_delta_from_sec(1)); - FR_TIME_DELTA_BOUND_CHECK("response_window", inst->zombie_period, <=, fr_time_delta_from_sec(120)); - - FR_TIME_DELTA_BOUND_CHECK("zombie_period", inst->zombie_period, >=, fr_time_delta_from_sec(1)); - FR_TIME_DELTA_BOUND_CHECK("zombie_period", inst->zombie_period, <=, fr_time_delta_from_sec(120)); - - if (!inst->status_check) { - FR_TIME_DELTA_BOUND_CHECK("revive_interval", inst->revive_interval, >=, fr_time_delta_from_sec(10)); - FR_TIME_DELTA_BOUND_CHECK("revive_interval", inst->revive_interval, <=, fr_time_delta_from_sec(3600)); - } - - num_types = talloc_array_length(inst->types); - fr_assert(num_types > 0); - - inst->timeout_retry = (fr_retry_config_t) { - .mrc = 1, - .mrd = inst->response_window, - }; - - inst->common_ctx = (fr_radius_ctx_t) { - .secret = inst->secret, - .secret_length = inst->secret ? talloc_array_length(inst->secret) - 1 : 0, - .proxy_state = fr_rand(), - }; - - /* - * Allow for O(1) lookup later... - */ - for (i = 0; i < num_types; i++) { - uint32_t code; - - code = inst->types[i]; - fr_assert(code > 0); - fr_assert(code < FR_RADIUS_CODE_MAX); - - inst->allowed[code] = true; - } - - fr_assert(inst->status_check < FR_RADIUS_CODE_MAX); - - /* - * If we're replicating, we don't care if the other end - * is alive. - */ - if (inst->status_check) { - if (inst->mode == RLM_RADIUS_MODE_REPLICATE) { - cf_log_warn(conf, "Ignoring 'status_check = %s' due to 'mode = replicate'", - fr_radius_packet_name[inst->status_check]); - inst->status_check = false; - - } else if ((inst->mode == RLM_RADIUS_MODE_UNCONNECTED_REPLICATE) || - (inst->mode == RLM_RADIUS_MODE_UNCONNECTED_PROXY)) { - cf_log_warn(conf, "Ignoring 'status_check = %s' due to 'mode = unconnected-*'", - fr_radius_packet_name[inst->status_check]); - inst->status_check = false; - } - } - - /* - * If we have status checks, then do some sanity checks. - * Status-Server is always allowed. Otherwise, the - * status checks have to match one of the allowed - * packets. - */ - if (inst->status_check) { - if (inst->status_check == FR_RADIUS_CODE_STATUS_SERVER) { - inst->allowed[inst->status_check] = true; - - } else if (!inst->allowed[inst->status_check]) { - cf_log_err(conf, "Using 'status_check = %s' requires also 'type = %s'", - fr_radius_packet_name[inst->status_check], fr_radius_packet_name[inst->status_check]); - return -1; - } - - /* - * @todo - check the contents of the "update" - * section, to be sure that (e.g.) Access-Request - * contains User-Name, etc. - */ - - if (inst->fd_config.filename) { - cf_log_info(conf, "Disabling status checks for output file %s", inst->fd_config.filename); - inst->status_check = 0; - } - } - - /* - * Files and unix sockets can just have us call write(). - */ - if (inst->fd_config.filename || inst->fd_config.path) { - inst->max_send_coalesce = 1; - } - - inst->trunk_conf.req_pool_headers = 4; /* One for the request, one for the buffer, one for the tracking binding, one for Proxy-State VP */ - inst->trunk_conf.req_pool_size = 1024 + sizeof(fr_pair_t) + 20; - - /* - * Only check the async timers when we're acting as a client. - */ - if (inst->mode != RLM_RADIUS_MODE_CLIENT) { - return 0; - } - - /* - * Set limits on retransmission timers - */ - if (inst->allowed[FR_RADIUS_CODE_ACCESS_REQUEST]) { - FR_TIME_DELTA_BOUND_CHECK("Access-Request.initial_rtx_time", inst->retry[FR_RADIUS_CODE_ACCESS_REQUEST].irt, >=, fr_time_delta_from_sec(1)); - FR_TIME_DELTA_BOUND_CHECK("Access-Request.max_rtx_time", inst->retry[FR_RADIUS_CODE_ACCESS_REQUEST].mrt, >=, fr_time_delta_from_sec(5)); - FR_INTEGER_BOUND_CHECK("Access-Request.max_rtx_count", inst->retry[FR_RADIUS_CODE_ACCESS_REQUEST].mrc, >=, 1); - FR_TIME_DELTA_BOUND_CHECK("Access-Request.max_rtx_duration", inst->retry[FR_RADIUS_CODE_ACCESS_REQUEST].mrd, >=, fr_time_delta_from_sec(5)); - - FR_TIME_DELTA_BOUND_CHECK("Access-Request.initial_rtx_time", inst->retry[FR_RADIUS_CODE_ACCESS_REQUEST].irt, <=, fr_time_delta_from_sec(3)); - FR_TIME_DELTA_BOUND_CHECK("Access-Request.max_rtx_time", inst->retry[FR_RADIUS_CODE_ACCESS_REQUEST].mrt, <=, fr_time_delta_from_sec(30)); - FR_INTEGER_BOUND_CHECK("Access-Request.max_rtx_count", inst->retry[FR_RADIUS_CODE_ACCESS_REQUEST].mrc, <=, 10); - FR_TIME_DELTA_BOUND_CHECK("Access-Request.max_rtx_duration", inst->retry[FR_RADIUS_CODE_ACCESS_REQUEST].mrd, <=, fr_time_delta_from_sec(30)); - } - - /* - * Note that RFC 5080 allows for Accounting-Request to - * have mrt=mrc=mrd = 0, which means "retransmit - * forever". We allow that, with the restriction that - * the server core will automatically free the request at - * max_request_time. - */ - if (inst->allowed[FR_RADIUS_CODE_ACCOUNTING_REQUEST]) { - FR_TIME_DELTA_BOUND_CHECK("Accounting-Request.initial_rtx_time", inst->retry[FR_RADIUS_CODE_ACCOUNTING_REQUEST].irt, >=, fr_time_delta_from_sec(1)); -#if 0 - FR_TIME_DELTA_BOUND_CHECK("Accounting-Request.max_rtx_time", inst->retry[FR_RADIUS_CODE_ACCOUNTING_REQUEST].mrt, >=, fr_time_delta_from_sec(5)); - FR_INTEGER_BOUND_CHECK("Accounting-Request.max_rtx_count", inst->retry[FR_RADIUS_CODE_ACCOUNTING_REQUEST].mrc, >=, 0); - FR_TIME_DELTA_BOUND_CHECK("Accounting-Request.max_rtx_duration", inst->retry[FR_RADIUS_CODE_ACCOUNTING_REQUEST].mrd, >=, fr_time_delta_from_sec(0)); -#endif - - FR_TIME_DELTA_BOUND_CHECK("Accounting-Request.initial_rtx_time", inst->retry[FR_RADIUS_CODE_ACCOUNTING_REQUEST].irt, <=, fr_time_delta_from_sec(3)); - FR_TIME_DELTA_BOUND_CHECK("Accounting-Request.max_rtx_time", inst->retry[FR_RADIUS_CODE_ACCOUNTING_REQUEST].mrt, <=, fr_time_delta_from_sec(30)); - FR_INTEGER_BOUND_CHECK("Accounting-Request.max_rtx_count", inst->retry[FR_RADIUS_CODE_ACCOUNTING_REQUEST].mrc, <=, 10); - FR_TIME_DELTA_BOUND_CHECK("Accounting-Request.max_rtx_duration", inst->retry[FR_RADIUS_CODE_ACCOUNTING_REQUEST].mrd, <=, fr_time_delta_from_sec(30)); - } - - /* - * Status-Server - */ - if (inst->allowed[FR_RADIUS_CODE_STATUS_SERVER]) { - FR_TIME_DELTA_BOUND_CHECK("Status-Server.initial_rtx_time", inst->retry[FR_RADIUS_CODE_STATUS_SERVER].irt, >=, fr_time_delta_from_sec(1)); - FR_TIME_DELTA_BOUND_CHECK("Status-Server.max_rtx_time", inst->retry[FR_RADIUS_CODE_STATUS_SERVER].mrt, >=, fr_time_delta_from_sec(5)); - FR_INTEGER_BOUND_CHECK("Status-Server.max_rtx_count", inst->retry[FR_RADIUS_CODE_STATUS_SERVER].mrc, >=, 1); - FR_TIME_DELTA_BOUND_CHECK("Status-Server.max_rtx_duration", inst->retry[FR_RADIUS_CODE_STATUS_SERVER].mrd, >=, fr_time_delta_from_sec(5)); - - FR_TIME_DELTA_BOUND_CHECK("Status-Server.initial_rtx_time", inst->retry[FR_RADIUS_CODE_STATUS_SERVER].irt, <=, fr_time_delta_from_sec(3)); - FR_TIME_DELTA_BOUND_CHECK("Status-Server.max_rtx_time", inst->retry[FR_RADIUS_CODE_STATUS_SERVER].mrt, <=, fr_time_delta_from_sec(30)); - FR_INTEGER_BOUND_CHECK("Status-Server.max_rtx_count", inst->retry[FR_RADIUS_CODE_STATUS_SERVER].mrc, <=, 10); - FR_TIME_DELTA_BOUND_CHECK("Status-Server.max_rtx_duration", inst->retry[FR_RADIUS_CODE_STATUS_SERVER].mrd, <=, fr_time_delta_from_sec(30)); - } - - /* - * CoA - */ - if (inst->allowed[FR_RADIUS_CODE_COA_REQUEST]) { - FR_TIME_DELTA_BOUND_CHECK("CoA-Request.initial_rtx_time", inst->retry[FR_RADIUS_CODE_COA_REQUEST].irt, >=, fr_time_delta_from_sec(1)); - FR_TIME_DELTA_BOUND_CHECK("CoA-Request.max_rtx_time", inst->retry[FR_RADIUS_CODE_COA_REQUEST].mrt, >=, fr_time_delta_from_sec(5)); - FR_INTEGER_BOUND_CHECK("CoA-Request.max_rtx_count", inst->retry[FR_RADIUS_CODE_COA_REQUEST].mrc, >=, 1); - FR_TIME_DELTA_BOUND_CHECK("CoA-Request.max_rtx_duration", inst->retry[FR_RADIUS_CODE_COA_REQUEST].mrd, >=, fr_time_delta_from_sec(5)); - - FR_TIME_DELTA_BOUND_CHECK("CoA-Request.initial_rtx_time", inst->retry[FR_RADIUS_CODE_COA_REQUEST].irt, <=, fr_time_delta_from_sec(3)); - FR_TIME_DELTA_BOUND_CHECK("CoA-Request.max_rtx_time", inst->retry[FR_RADIUS_CODE_COA_REQUEST].mrt, <=, fr_time_delta_from_sec(60)); - FR_INTEGER_BOUND_CHECK("CoA-Request.max_rtx_count", inst->retry[FR_RADIUS_CODE_COA_REQUEST].mrc, <=, 10); - FR_TIME_DELTA_BOUND_CHECK("CoA-Request.max_rtx_duration", inst->retry[FR_RADIUS_CODE_COA_REQUEST].mrd, <=, fr_time_delta_from_sec(30)); - } - - /* - * Disconnect - */ - if (inst->allowed[FR_RADIUS_CODE_DISCONNECT_REQUEST]) { - FR_TIME_DELTA_BOUND_CHECK("Disconnect-Request.initial_rtx_time", inst->retry[FR_RADIUS_CODE_DISCONNECT_REQUEST].irt, >=, fr_time_delta_from_sec(1)); - FR_TIME_DELTA_BOUND_CHECK("Disconnect-Request.max_rtx_time", inst->retry[FR_RADIUS_CODE_DISCONNECT_REQUEST].mrt, >=, fr_time_delta_from_sec(5)); - FR_INTEGER_BOUND_CHECK("Disconnect-Request.max_rtx_count", inst->retry[FR_RADIUS_CODE_DISCONNECT_REQUEST].mrc, >=, 1); - FR_TIME_DELTA_BOUND_CHECK("Disconnect-Request.max_rtx_duration", inst->retry[FR_RADIUS_CODE_DISCONNECT_REQUEST].mrd, >=, fr_time_delta_from_sec(5)); - - FR_TIME_DELTA_BOUND_CHECK("Disconnect-Request.initial_rtx_time", inst->retry[FR_RADIUS_CODE_DISCONNECT_REQUEST].irt, <=, fr_time_delta_from_sec(3)); - FR_TIME_DELTA_BOUND_CHECK("Disconnect-Request.max_rtx_time", inst->retry[FR_RADIUS_CODE_DISCONNECT_REQUEST].mrt, <=, fr_time_delta_from_sec(30)); - FR_INTEGER_BOUND_CHECK("Disconnect-Request.max_rtx_count", inst->retry[FR_RADIUS_CODE_DISCONNECT_REQUEST].mrc, <=, 10); - FR_TIME_DELTA_BOUND_CHECK("Disconnect-Request.max_rtx_duration", inst->retry[FR_RADIUS_CODE_DISCONNECT_REQUEST].mrd, <=, fr_time_delta_from_sec(30)); - } - - return 0; -} - -static int mod_bootstrap(module_inst_ctx_t const *mctx) -{ - xlat_t *xlat; - rlm_radius_t const *inst = talloc_get_type_abort(mctx->mi->data, rlm_radius_t); - - switch (inst->mode) { - case RLM_RADIUS_MODE_UNCONNECTED_REPLICATE: - xlat = module_rlm_xlat_register(mctx->mi->boot, mctx, "sendto.ipaddr", xlat_radius_replicate, FR_TYPE_VOID); - xlat_func_args_set(xlat, xlat_radius_send_args); - break; - - case RLM_RADIUS_MODE_UNCONNECTED_PROXY: - fr_assert(0); /* not implemented */ - break; - - default: - break; - } - - return 0; -} - - -static int mod_detach(module_detach_ctx_t const *mctx) -{ - rlm_radius_t *inst = talloc_get_type_abort(mctx->mi->data, rlm_radius_t); - - talloc_free(inst->received_message_authenticator); - return 0; -} - -static int mod_load(void) -{ - if (fr_radius_global_init() < 0) { - PERROR("Failed initialising protocol library"); - return -1; - } - return 0; -} - -static void mod_unload(void) -{ - fr_radius_global_free(); -} - -/* - * The module name should be the only globally exported symbol. - * That is, everything else should be 'static'. - * - * If the module needs to temporarily modify it's instantiation - * data, the type should be changed to MODULE_TYPE_THREAD_UNSAFE. - * The server will then take care of ensuring that the module - * is single-threaded. - */ -extern module_rlm_t rlm_radius; -module_rlm_t rlm_radius = { - .common = { - .magic = MODULE_MAGIC_INIT, - .name = "radius", - .inst_size = sizeof(rlm_radius_t), - .config = module_config, - - .onload = mod_load, - .unload = mod_unload, - - .bootstrap = mod_bootstrap, - .instantiate = mod_instantiate, - .detach = mod_detach, - - .thread_inst_size = sizeof(bio_thread_t), - .thread_inst_type = "bio_thread_t", - .thread_instantiate = mod_thread_instantiate, - }, - .method_group = { - .bindings = (module_method_binding_t[]){ - { .section = SECTION_NAME(CF_IDENT_ANY, CF_IDENT_ANY), .method = mod_process }, - MODULE_BINDING_TERMINATOR - }, - } -}; diff --git a/src/modules/rlm_radius2/rlm_radius.h b/src/modules/rlm_radius2/rlm_radius.h deleted file mode 100644 index 13697630e9bf..000000000000 --- a/src/modules/rlm_radius2/rlm_radius.h +++ /dev/null @@ -1,93 +0,0 @@ -#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 - */ -#include -#include -#include -#include -#include -#include -#include -#include -#include -#include - -#include - -/* - * $Id$ - * - * @file rlm_radius.h - * @brief Structures for the RADIUS client packets - * - * @copyright 2017 Alan DeKok (aland@freeradius.org) - */ - -typedef struct rlm_radius_s rlm_radius_t; - -typedef enum { - RLM_RADIUS_MODE_INVALID = 0, - RLM_RADIUS_MODE_PROXY, - RLM_RADIUS_MODE_CLIENT, - RLM_RADIUS_MODE_REPLICATE, - RLM_RADIUS_MODE_UNCONNECTED_REPLICATE, - RLM_RADIUS_MODE_UNCONNECTED_PROXY, -} rlm_radius_mode_t; - -/* - * Define a structure for our module configuration. - */ -struct rlm_radius_s { - fr_bio_fd_config_t fd_config; //!< for now MUST be at the start! - - char const *name; - - fr_time_delta_t response_window; - fr_time_delta_t zombie_period; - fr_time_delta_t revive_interval; - - char const *secret; //!< Shared secret. - - uint32_t max_packet_size; //!< Maximum packet size. - uint16_t max_send_coalesce; //!< Maximum number of packets to coalesce into one mmsg call. - - fr_radius_ctx_t common_ctx; - - bool replicate; //!< Ignore responses. - bool synchronous; //!< Retransmit when receiving a duplicate request. - bool originate; //!< Originating packets, instead of proxying existing ones. - ///< Controls whether Proxy-State is added to the outbound - ///< request - rlm_radius_mode_t mode; //!< proxy, client, etc. - - uint32_t max_attributes; //!< Maximum number of attributes to decode in response. - - fr_radius_require_ma_t require_message_authenticator; //!< Require Message-Authenticator in responses. - bool *received_message_authenticator; //!< Received Message-Authenticator in responses. - - uint32_t *types; //!< array of allowed packet types - uint32_t status_check; //!< code of status-check type - map_list_t status_check_map; //!< attributes for the status-server checks - uint32_t num_answers_to_alive; //!< How many status check responses we need to - ///< mark the connection as alive. - - bool allowed[FR_RADIUS_CODE_MAX]; - - fr_retry_config_t timeout_retry; - fr_retry_config_t retry[FR_RADIUS_CODE_MAX]; - - trunk_conf_t trunk_conf; //!< trunk configuration -}; diff --git a/src/modules/rlm_radius2/track.c b/src/modules/rlm_radius2/track.c deleted file mode 100644 index b79b3a7e7026..000000000000 --- a/src/modules/rlm_radius2/track.c +++ /dev/null @@ -1,316 +0,0 @@ -/* - * This program is 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 rlm_radius/track.c - * @brief Tracking RADUS client packets - * - * @copyright 2017 Network RADIUS SAS - */ -RCSID("$Id$") - -#include -#include -#include -#include -#include - -#include "track.h" - -/** Create an radius_track_t - * - * @param ctx the talloc ctx - * @return - * - NULL on error - * - radius_track_t on success - */ -radius_track_t *radius_track_alloc(TALLOC_CTX *ctx) -{ - int i; - radius_track_t *tt; - - MEM(tt = talloc_zero(ctx, radius_track_t)); - - fr_dlist_init(&tt->free_list, radius_track_entry_t, entry); - - for (i = 0; i < 256; i++) { - tt->id[i].id = i; -#ifndef NDEBUG - tt->id[i].file = __FILE__; - tt->id[i].line = __LINE__; -#endif - fr_dlist_insert_tail(&tt->free_list, &tt->id[i]); - } - - return tt; -} - - -/** Ensures the entry is released when the ctx passed to radius_track_entry_reserve is freed - * - * @param[in] te_p Entry to release. - * @return 0 - */ -static int _radius_track_entry_release_on_free(radius_track_entry_t ***te_p) -{ - radius_track_entry_release(*te_p); - - return 0; -} - -/** Allocate a tracking entry. - * - * @param[in] file The allocation was made in. - * @param[in] line The allocation was made on. - * @param[out] te_out Where the tracking entry should be written. - * If ctx is not-null, then this pointer must - * remain valid for the lifetime of the ctx. - * @param[in] ctx If not-null, the tracking entry release will - * be bound to the lifetime of the talloc chunk. - * @param[in] tt The radius_track_t tracking table. - * @param[in] request The request which will send the proxied packet. - * @param[in] code Of the outbound request. - * @param[in] uctx The context to associate with the request - * @return - * - 0 on success. - * - -1 on failure. - */ -#ifndef NDEBUG -int _radius_track_entry_reserve(char const *file, int line, -#else -int radius_track_entry_reserve( -#endif - radius_track_entry_t **te_out, - TALLOC_CTX *ctx, radius_track_t *tt, request_t *request, uint8_t code, void *uctx) -{ - radius_track_entry_t *te; - - if (!fr_cond_assert_msg(!*te_out, "Expected tracking entry to be NULL")) return -1; - -retry: - te = fr_dlist_head(&tt->free_list); - if (te) { - fr_assert(te->request == NULL); - - /* - * Mark it as used, and remove it from the free list. - */ - fr_dlist_remove(&tt->free_list, te); - - /* - * We've transitioned from "use it", to "oops, - * don't use it". Ensure that we only return IDs - * which are in the static array. - */ - if (te != &tt->id[te->id]) { - talloc_free(te); - goto retry; - } - - goto done; - } - - /* - * There are no free entries, and we can't use the - * Request Authenticator. Oh well... - */ - fr_strerror_const("No free entries"); - return -1; - -done: - te->tt = tt; - te->request = request; - te->uctx = uctx; - te->code = code; -#ifndef NDEBUG - te->operation = te->tt->operation++; - te->file = file; - te->line = line; -#endif - if (ctx) { - te->binding = talloc_zero(ctx, radius_track_entry_t **); - talloc_set_destructor(te->binding, _radius_track_entry_release_on_free); - *(te->binding) = te_out; - } - - /* - * te->id is already allocated - */ - tt->num_requests++; - - *te_out = te; - - return 0; -} - -/** Release a tracking entry - * - * @param[in] file Allocation was released in. - * @param[in] line Allocation was released on. - * @param[in,out] te_to_free The #radius_track_entry_t allocated via #radius_track_entry_reserve. - * @return - * - <0 on error - * - 0 on success - */ -#ifndef NDEBUG -int _radius_track_entry_release(char const *file, int line, -#else -int radius_track_entry_release( -#endif - radius_track_entry_t **te_to_free) -{ - radius_track_entry_t *te = *te_to_free; - radius_track_t *tt; - - if (!te) return 0; - - tt = talloc_get_type_abort(te->tt, radius_track_t); /* Make sure table is still valid */ - - if (te->binding) { - talloc_set_destructor(te->binding, NULL); /* Disarm the destructor */ - talloc_free(te->binding); - } - -#ifndef NDEBUG - te->operation = te->tt->operation++; - te->file = file; - te->line = line; -#endif - - te->request = NULL; - - fr_assert(tt->num_requests > 0); - tt->num_requests--; - - /* - * We're freeing a static ID, just go do that... - */ - fr_assert(te == &tt->id[te->id]); - - fr_dlist_insert_tail(&tt->free_list, te); - - *te_to_free = NULL; - - return 0; -} - -/** Update a tracking entry with the authentication vector - * - * @param te The radius_track_entry_t, via radius_track_entry_reserve() - * @param vector The authentication vector for the packet we're sending - * @return - * - <0 on error - * - 0 on success - */ -int radius_track_entry_update(radius_track_entry_t *te, uint8_t const *vector) -{ - radius_track_t *tt = te->tt; - - fr_assert(tt); - - memcpy(te->vector, vector, sizeof(te->vector)); - - /* - * If we're not using the Request Authenticator, the - * tracking entry must be in the static array. - * - * @todo - gracefully handle fallback if the server screws up. - */ - fr_assert(te == &tt->id[te->id]); - return 0; -} - -/** Find a tracking entry from a request authenticator - * - * @param tt The radius_track_t tracking table - * @param packet_id The ID from the RADIUS header - * @param vector The Request Authenticator (may be NULL) - * @return - * - NULL on "not found" - * - radius_track_entry_t on success - */ -radius_track_entry_t *radius_track_entry_find(radius_track_t *tt, uint8_t packet_id, uint8_t const *vector) -{ - radius_track_entry_t *te; - - (void) talloc_get_type_abort(tt, radius_track_t); - - /* - * Just use the static array. - */ - te = &tt->id[packet_id]; - - /* - * Not in use, die. - */ - if (!te->request) return NULL; - - if (!vector) return te; - - /* - * Protocol-Error and Original-Packet-Vector - * - * This should arguably have been Original-Packet-Code, but we are stupid. - * - * @todo - Allow for multiple ID arrays, one for each packet code. Or, just switch to using - * src/protocols/radius/id.[ch]. - */ - if (memcmp(te->vector, vector, sizeof(te->vector)) != 0) return NULL; - - /* - * Ignore the Request Authenticator, as the - * caller doesn't have it. - */ - return te; -} - - -#ifndef NDEBUG -/** Print out the state of every tracking entry - * - * @param[in] log destination. - * @param[in] log_type Type of log message. - * @param[in] file this function was called in. - * @param[in] line this function was called on. - * @param[in] tt Table to print. - * @param[in] extra Callback function for printing extra detail. - */ -void radius_track_state_log(fr_log_t const *log, fr_log_type_t log_type, char const *file, int line, - radius_track_t *tt, radius_track_log_extra_t extra) -{ - size_t i; - - for (i = 0; i < NUM_ELEMENTS(tt->id); i++) { - radius_track_entry_t *entry; - - entry = &tt->id[i]; - - if (entry->request) { - fr_log(log, log_type, file, line, - "[%zu] %"PRIu64 " - Allocated at %s:%u to request %p (%s), uctx %p", - i, entry->operation, - entry->file, entry->line, entry->request, entry->request->name, entry->uctx); - } else { - fr_log(log, log_type, file, line, - "[%zu] %"PRIu64 " - Freed at %s:%u", - i, entry->operation, entry->file, entry->line); - } - - if (extra) extra(log, log_type, file, line, entry); - } -} -#endif diff --git a/src/modules/rlm_radius2/track.h b/src/modules/rlm_radius2/track.h deleted file mode 100644 index 327361129818..000000000000 --- a/src/modules/rlm_radius2/track.h +++ /dev/null @@ -1,116 +0,0 @@ -#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 track.h - * @brief RADIUS client packet tracking - * - * @copyright 2017 Alan DeKok (aland@freeradius.org) - */ - -#include "rlm_radius.h" -#include - -typedef struct radius_track_entry_s radius_track_entry_t; -typedef struct radius_track_s radius_track_t; - -/** Track one request to a response - * - */ -struct radius_track_entry_s { - fr_rb_node_t node; //!< Entry in the tracking tree. - - radius_track_t *tt; - - radius_track_entry_t ***binding; //!< Binding chunk we use to release the entry - ///< when its parent is freed. We also zero - ///< out the tracking entry field in the parent. - - request_t *request; //!< as always... - - void *uctx; //!< Result/resumption context. - - uint8_t code; //!< packet code (sigh) - uint8_t id; //!< our ID - - union { - fr_dlist_t entry; //!< For free list. - uint8_t vector[RADIUS_AUTH_VECTOR_LENGTH]; //!< copy of the request authenticator. - }; - -#ifndef NDEBUG - uint64_t operation; //!< Used to give an idea of the alloc/free timeline. - char const *file; //!< Where the entry was allocated. - int line; //!< Where the entry was freed. -#endif -}; - -struct radius_track_s { - unsigned int num_requests; //!< number of requests in the allocation - - fr_dlist_head_t free_list; //!< so we allocate by least recently used - - radius_track_entry_t id[UINT8_MAX + 1]; //!< which ID was used - -#ifndef NDEBUG - uint64_t operation; //!< Incremented each alloc and de-alloc -#endif -}; - -radius_track_t *radius_track_alloc(TALLOC_CTX *ctx); - -/* - * Debug functions which track allocations and frees - */ -#ifndef NDEBUG -# define radius_track_entry_reserve(_te_out, _ctx, _tt, _request, _code, _uctx) \ - _radius_track_entry_reserve( __FILE__, __LINE__, _te_out, _ctx, _tt, _request, _code, _uctx) -int _radius_track_entry_reserve(char const *file, int line, - radius_track_entry_t **te_out, - TALLOC_CTX *ctx, radius_track_t *tt, request_t *request, - uint8_t code, void *uctx) - CC_HINT(nonnull(3,5,6)); - -# define radius_track_entry_release(_te) \ - _radius_track_entry_release( __FILE__, __LINE__, _te) -int _radius_track_entry_release(char const *file, int line, radius_track_entry_t **te) - CC_HINT(nonnull); - -typedef void (*radius_track_log_extra_t)(fr_log_t const *log, fr_log_type_t log_type, char const *file, int line, - radius_track_entry_t *te); - -void radius_track_state_log(fr_log_t const *log, fr_log_type_t log_type, char const *file, int line, - radius_track_t *tt, radius_track_log_extra_t extra); -/* - * Non-debug functions - */ -#else -int radius_track_entry_reserve(radius_track_entry_t **te_out, - TALLOC_CTX *ctx, radius_track_t *tt, request_t *request, - uint8_t code, void *uctx) - CC_HINT(nonnull(1,3,4)); - -int radius_track_entry_release(radius_track_entry_t **te) CC_HINT(nonnull); -#endif - -int radius_track_entry_update(radius_track_entry_t *te, - uint8_t const *vector) CC_HINT(nonnull); - -radius_track_entry_t *radius_track_entry_find(radius_track_t *tt, uint8_t packet_id, - uint8_t const *vector) CC_HINT(nonnull(1)); diff --git a/src/tests/radclient/config/radiusd.conf b/src/tests/radclient/config/radiusd.conf index fee3de260882..8b41435bbdf9 100644 --- a/src/tests/radclient/config/radiusd.conf +++ b/src/tests/radclient/config/radiusd.conf @@ -68,6 +68,8 @@ modules { } radius { + mode = proxy + type = Access-Request type = Accounting-Request