# to which it sends proxy requests.
#
proxy server {
-#
-# The time (in seconds) to wait for a response from the proxy, before
-# re-sending the proxied request.
-#
-# If this time is set too high, then the NAS may re-send the request,
-# or it may give up entirely, and reject the user.
-#
-# If it is set too low, then the RADIUS server which receives the proxy
-# request will get kicked unnecessarily.
-#
- retry_delay = 5
+ #
+ # Note that as of 2.0, the "synchronous", "retry_delay",
+ # "retry_count", and "dead_time" have all been deprecated.
+ # For backwards compatibility, they are are still accepted
+ # by the server, but they ONLY apply to the old-style realm
+ # configuration. i.e. realms with "authhost" and/or "accthost"
+ # entries.
+ #
+ # i.e. "retry_delay" and "retry_count" have been replaced
+ # with per-home-server "pings". See the "home_server" example
+ # below for details.
+ #
+ # i.e. "dead_time" has been replaced with a per-home-server
+ # "revive_interval". We strongly recommend that this not
+ # be used, however. The new "ping" method is much better.
+
+ # Note that while we call these messages "pings" they are NOT
+ # the same as the ICMP packets sent by the "ping" command.
+ # These messages are normal RADIUS packets, sent to a home
+ # server to determine if it is alive.
+
+ #
+ # In 2.0, the server is always "synchronous", and setting
+ # "synchronous = no" is impossible. This simplifies the
+ # server and increases the stability of the network.
+ #
+ # If you need to set "synchronous = no", please send a
+ # message to the list <freeradius-users@lists.freeradius.org>
+ # explaining why this feature is vital for your network.
+
+ #
+ # If a realm exists, but there are no live home servers for
+ # it, we can fall back to using the "DEFAULT" realm. This is
+ # most useful for accounting, where the server can proxy
+ # accounting requests to home servers, but if they're down,
+ # use a DEFAULT realm that is LOCAL (i.e. accthost = LOCAL),
+ # and then store the packets in the "detail" file. That data
+ # can be later proxied to the home servers by radrelay, when
+ # those home servers come back up again.
+
+ # Setting this to "yes" may have issues for authentication.
+ # i.e. If you are proxying for two different ISP's, and then
+ # act as a general dial-up for Gric. If one of the first two
+ # ISP's has their RADIUS server go down, you do NOT want to
+ # proxy those requests to GRIC. Instead, you probably want
+ # to just drop the requests on the floor. In that case, set
+ # this value to 'no'.
+ #
+ # allowed values: {yes, no}
+ #
+ default_fallback = no
+
+}
+#######################################################################
#
-# The number of retries to send before giving up, and sending a reject
-# message to the NAS.
+# Configuration for the proxy realms.
#
- retry_count = 3
-
+# As of 2.0. the old-style "realms" file is deprecated, and is not
+# used by FreeRADIUS.
#
-# If the home server does not respond to any of the multiple retries,
-# then FreeRADIUS will stop sending it proxy requests, and mark it 'dead'.
+# As of 2.0, the "realm" configuration has changed. Instead of
+# specifying "authhost" and "accthost" in a realm section, the home
+# servers are specified seperately in a "home_server" section. For
+# backwards compatibility, you can still use the "authhost" and
+# "accthost" directives. If you only have one home server for a
+# realm, it is easier to use the old-style configuration.
#
-# If there are multiple entries configured for this realm, then the
-# server will fail-over to the next one listed. If no more are listed,
-# then no requests will be proxied to that realm.
+# However, if you have multiple servers for a realm, we STRONGLY
+# suggest moving to the new-style configuration.
#
#
-# After a configurable 'dead_time', in seconds, FreeRADIUS will
-# speculatively mark the home server active, and start sending requests
-# to it again.
+# Load-balancing and failover between home servers is handled via
+# a "server_pool" section.
#
-# If this dead time is set too low, then you will lose requests,
-# as FreeRADIUS will quickly switch back to the home server, even if
-# it isn't up again.
+# Finally, The "realm" section defines the realm, some options, and
+# indicates which server pool should be used for the realm.
#
-# If this dead time is set too high, then FreeRADIUS may take too long
-# to switch back to the primary home server.
+# This change means that simple configurations now require multiple
+# ssections to define a realm. However, complex configurations
+# are much simpler than before, as multiple realms can share the same
+# server pool.
#
-# Realistic values for this number are in the range of minutes to hours.
-# (60 to 3600)
+# That is, realms point to server pools, and server pools point to
+# home servers. Multiple realms can point to one server pool. One
+# server pool can point to multiple home servers. Each home server
+# can appear in one or more pools.
#
- dead_time = 120
-# An ldflag attribute for all realms to be included in a round-robin
-# setup must be specified, and that ldflag must be the same for all
-# realms of the same name.
-# Currently (0 or fail_over) and (1 or round_robin) are the
-# supported values for ldflag. Fail over is the default setup.
-#
-# DO NOT INCLUDE LOCAL AUTH/ACCT HOST REALMS IN A ROUND-ROBIN QUEUE.
+######################################################################
+#
+# This section defines a "Home Server" which is another RADIUS
+# server that gets sent proxied requests. In earlier versions
+# of FreeRADIUS, home servers were defined in "realm" sections,
+# which was awkward. In 2.0, they have been made independent
+# from realms, which is better for a number of reasons.
+#
+home_server localhost {
+ #
+ # Home servers can be sent Access-Request packets
+ # or Accounting-Request packets.
+ #
+ # Allowed values are:
+ # auth - send Access-Request packets
+ # acct - send Accounting-Request packets
+ type = auth
+ #
+ # Configure ONE OF the following three entries:
+ #
+ # IPv4 address
+ #
+ ipaddr = 127.0.0.1
-#
-# If all exact matching realms did not respond, we can try the
-# DEFAULT realm, too. This is what the server normally does.
-#
-# This behaviour may be undesired for some cases. e.g. You are proxying
-# for two different ISP's, and then act as a general dial-up for Gric.
-# If one of the first two ISP's has their RADIUS server go down, you do
-# NOT want to proxy those requests to GRIC. Instead, you probably want
-# to just drop the requests on the floor. In that case, set this value
-# to 'no'.
-#
-# allowed values: {yes, no}
-#
- default_fallback = yes
+ # OR IPv6 address
+ # ipv6addr = ::1
+ # OR hostname, which will do address detection automatically
+ #
+ # Note that we do NOT recommend using hostnames, because
+ # it means that the server has to do a DNS lookup to
+ # determine the IP address of the home server. If the
+ # DNS server is slow or unresponsible, it means that
+ # FreeRADIUS will NOT be able to determine the IP
+ # address, and will therefore NOT start.
+ #
+ # hostname = localhost
+
+ #
+ # The port to which packets are sent.
+ #
+ # Usually 1812 for type "auth", and 1813 for type "acct".
+ # Older servers may use 1645 and 1646.
+ #
+ port = 1812
+
+ #
+ # The shared secret use to "encrypt" and "sign" packets between
+ # FreeRADIUS and the home server.
+ #
+ # The secret can be any string, up to 8k characters in length.
+ #
+ # Control codes can be entered vi octal encoding,
+ # e.g. "\101\102" == "AB"
+ # Quotation marks can be entered by escaping them,
+ # e.g. "foo\"bar"
+ # Spaces or other "special" characters can be entered
+ # by putting quotes around the string.
+ # e.g. "foo bar"
+ # "foo;bar"
+ #
+ secret = testing123
+
+ ############################################################
+ #
+ # The rest of the configuration items listed here are optional,
+ # and do not have to appear in every home server definition.
+ #
+ ############################################################
+
+ #
+ # If the home server doesn't respond to the request within
+ # this time, this server will consider the request dead, and
+ # respond to the NAS with an Access-Reject.
+ #
+ # Useful range of values: 5 to 60
+ response_window = 20
+
+ #
+ # If the home server does not respond to ANY packets for
+ # a certain time, consider it dead. This time period is
+ # called the "zombie" period, because the server is neither
+ # alive nor dead.
+ #
+ # Useful range of values: 20 to 120
+ zombie_period = 40
+
+ ############################################################
+ #
+ # As of 2.0, FreeRADIUS supports RADIUS layer "pings". These
+ # are used by a proxy server to see if a home server is alive.
+ #
+ # Pings are sent ONLY if the proxying server believes that
+ # the home server is dead. Pings are NOT sent if the proxying
+ # server believes that the home server is alive. Pings are
+ # NOT sent if the proxying server is not proxying packets.
+ #
+ # If the home server responds to the "pings", then it is
+ # marked "alive" again, and is returned to use.
+ #
+ ############################################################
+
+ #
+ # Some home servers do not support RADIUS layer "pings" 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 it has NOT come back to life, then FreeRADIUS 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 "pings", as
+ # documented below.
+ #
+ # 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.
+ #
+ # If the "zombie_period" and "revive_interval" configurations
+ # are set smaller, than it is possible for up to 50% of
+ # authentications to fail.
+ #
+ # As a result, we recommend enabling Status-Server "pings", and
+ # we do NOT recommend using "revive_interval".
+ #
+ # If the "ping_check" entry below is not "none", then the
+ # "revive_interval" entry can be deleted, as it will not be
+ # used.
+ #
+ # Useful range of values: 60 to 3600
+ revive_interval = 120
+
+ #
+ # The proxying server (i.e. this one) can do periodic "ping"
+ # checks to see if a dead home server has come back alive.
+ #
+ # If set to "none", then the ping configuration items listed
+ # below are not used, and the "revive_interval" time is used
+ # instead.
+ #
+ # If set to "status-server", the Status-Server packets are
+ # sent. Many RADIUS servers support Status-Server. If a
+ # server does not support it, please contact the server
+ # vendor and request that they add it.
+ #
+ # If set to "request", then Access-Request, or Accounting-Request
+ # packets are sent, depending on the "type" entry above (auth/acct).
+ #
+ # Allowed values: none, status-server, request
+ ping_check = status-server
+
+ #
+ # If the home server does not support Status-Server "pings",
+ # then the server can still send Access-Request or
+ # Accounting-Request packets, with a pre-defined user name.
+ #
+ # This practice is NOT recommended, as it may potentially let
+ # users gain network access by using these "test" accounts!
+ #
+ # If it is used, we recommend that the home server ALWAYS
+ # respond to Access-Request "pings" with Access-Reject. The
+ # ping check just needs an answer, it does not need an
+ # Access-Accept.
+ #
+ # For Accounting-Request "pings", only the username needs to
+ # be set.
+ #
+ # username = "test_user_please_reject_me"
+ # password = "this is really secret"
+
+ #
+ # Configure the interval between sending ping packets.
+ #
+ # Setting it too low increases the probability of spurious
+ # fail-over and fallback attempts.
+ #
+ # Useful range of values: 6 to 120
+ ping_interval = 30
+
+ #
+ # Configure the number of pings in a row that the home
+ # server needs to respond to before it is marked alive.
+ #
+ # If you want to mark a home server as alive after a short
+ # time period of being responsive, it is best to use a small
+ # "ping_interval", and a large value for "num_pings_to_alive".
+ # Using a long "ping_interval" and a small number for
+ # "num_pings_to_alive" increases the probability of spurious
+ # fail-over and fallback attempts.
+ #
+ # Useful range of values: 3 to 10
+ num_pings_to_alive = 3
}
-#######################################################################
-#
-# Configuration for the proxy realms.
-#
-# The information given here is used in conjunction with the 'realms'
-# file. This format is preferred, as it is more flexible. The realms
-# listed here take priority over those listed in the 'realms' file.
-# A standard realm entry. A request from "user@company.com" will be
-# sent to radius.company.com as "user", unless the 'nostrip'
-# configuration item is specified. If the 'nostrip' configuration
-# item is specified, then the request will be proxied as
-# "user@company.com"
-#
-#realm company.com {
-# type = radius
-# authhost = radius.company.com:1600
-# accthost = radius.company.com:1601
-# secret = testing123
-#}
+######################################################################
+#
+# This section defines a pool of home servers that is used
+# for fail-over and load-balancing. In earlier versions of
+# FreeRADIUS, fail-over and load-balancing were defined per-realm.
+# As a result, if a server had 5 home servers, each of which served
+# the same 10 realms, you would need 50 "realm" entries.
+#
+# In version 2.0, you would need 5 "home_server" sections,
+# 10 'realm" sections, and one "server_pool" section to tie the
+# two together.
+#
+server_pool my_auth_failover {
+ #
+ # The type of this pool is either "fail-over" or "load-balance".
+ #
+ # With "fail-over", the request is sent to the first live
+ # home server in the list.
+ #
+ # With "load-balance", the request is load-balanced (randomly)
+ # between the live home servers. This is equivalent to the
+ # old per-realm configuration "round_robin".
+ #
+ type = fail-over
-# A realm entry with an optional fail-over realm. A request from
-# "user@isp2.com" will be sent to radius.isp2.com as "user@isp2.com",
-# because the 'nostrip' directive is specified for this realm.
-#
-#realm isp2.com {
-# type = radius
-# authhost = radius.isp2.com:1645
-# accthost = radius.isp2.com:1646
-# secret = TheirKey
-# nostrip
-#}
-#
-# The fail-over realm for isp2.com
-#
-#realm isp2.com {
-# type = radius
-# authhost = radius2.isp2.com:1645
-# accthost = radius2.isp2.com:1646
-# secret = TheirKey2
-# nostrip
-#}
+ #
+ # Next, a list of one or more home servers. The names
+ # of the home servers are NOT the hostnames, but the names
+ # of the sections. (e.g. home_server foo {...} has name "foo".
+ #
+ home_server = localhost
+
+ # Additional home servers can be listed.
+ # There is NO LIMIT to the number of home servers that can
+ # be listed, though using more than 10 or so will become
+ # difficult to manage.
+ #
+ # home_server = foo.example.com
+ # home_server = bar.example.com
+ # home_server = baz.example.com
+ # home_server = ...
+}
+######################################################################
#
-# 1st node serv.com...set up for round-robin.
#
-# The load balancing 'ldflag' attribute can be used to perform
-# load balancing. Allowed values are 'fail_over' and 'round_robin'.
+# This section defines a new-style "realm". Note the in version 2.0,
+# there are many fewer configuration items than in 1.x for a realm.
#
-# If there is no ldflag attribute, or it is set to 'fail_over', then
-# the realms are treated as "fail-over". That is, the first matching
-# realm is used, unless it is down, in which case the realm "fails
-# over" to the second matching realm. The process continues until an
-# active matching realm is found, OR the DEFAULT realm is returned.
+# Automatic proxying is done via the "realms" module (see "man
+# rlm_realm"). To manually proxy the request put this entry in the
+# "users" file:
+
#
-# If the ldflag attribute is set to 'round_robin', then all active
-# realms of the same name are put into a pool internally in the
-# server, and the proxied requests are evenly divided among the
-# realms in the pool. For this to work, all realms of the same name
-# MUST have the same value of their 'ldflag' attributes. Mixing up
-# different types of load balancing schemes for the same realm will
-# cause problems.
#
-# The round_robin load balancing method is a probabilistic method
-# which evenly scatters the requests among the home servers.
+#DEFAULT Proxy-To-Realm := "realm_name"
#
-# Note that you CANNOT include local auth/acct host realms in a
-# round-robin queue. Having a server load balance requests to itself
-# doesn't make any sense, as it only doubles the amount of work
-# which is needed to be done.
#
-#realm serv.com {
-# type = radius
-# authhost = radius.serv.com:1645
-# accthost = radius.serv.com:1646
-# secret = TheirKey
-# ldflag = round_robin
-# nostrip
-#}
+realm example.com {
+ auth_pool = my_auth_failover
+# acct_pool = acct
-#
-# Another node for serv.com
-#
-#realm serv.com {
-# type = radius
-# authhost = radius2.serv.com:1645
-# accthost = radius2.serv.com:1646
-# secret = TheirKey2
-# ldflag = round_robin
-# nostrip
-#}
+ #
+ # Normally, when an incoming User-Name is matched against the
+ # realm, the realm name is "stripped" off, and the "stripped"
+ # user name is used to perform matches.
+ #
+ # e.g. User-Name = "bob@example.com" will result in two new
+ # attributes being created by the "realms" module:
+ #
+ # Stripped-User-Name = "bob"
+ # Realm = "example.com"
+ #
+ # The Stripped-User-Name is then used as a key in the "users"
+ # file, for example.
+ #
+ # If you do not want this to happen, uncomment "nostrip" below.
+ #
+ # nostrip
-#
-# A third round-robin node realm for serv.com
-#
-#realm serv.com {
-# type = radius
-# authhost = radius3.serv.com:1645
-# accthost = radius3.serv.com:1646
-# secret = TheirKey2
-# ldflag = round_robin
-# nostrip
-#}
-#
-#
+ # There are no more configuration entries for a realm.
+}
-#
-# This is a local realm. The requests are NOT proxied,
-# but instead are authenticated by the RADIUS server itself.
-#
-# You don't need a secret if BOTH 'authhost' and 'accthost' are
-# set to LOCAL.
-#
-#realm bla.com {
-# type = radius
-# authhost = LOCAL
-# accthost = LOCAL
-#}
#
# This is a sample entry for iPass.
+# Note that you have to define "ipass_auth_pool" and
+# "ipass_acct_pool", along with home_servers for them, too.
#
#realm IPASS {
-# type = radius
-# authhost = ipass.server.hostname:11812
-# accthost = ipass.server.hostname:11813
-#
- # The shared secret here must be the same
- # value as the secret of the NetServer found in the
- # /usr/ipass/raddb/clients file of your NetServer software.
-# secret = mysecret
# nostrip
+#
+# auth_pool = ipass_auth_pool
+# acct_pool = ipass_acct_pool
#}
#
# DEFAULT EAP-Type == PEAP, Proxy-To-Realm := LOCAL
#
realm LOCAL {
- type = radius
- authhost = LOCAL
- accthost = LOCAL
+ # If we do not specify a server pool, the realm is LOCAL, and
+ # requests are not proxied to it.
}
#
# accthost = radius.company.com:1601
# secret = testing123
#}
+
--- /dev/null
+#ifndef LRAD_EVENT_H
+#define LRAD_EVENT_H
+
+/*
+ * event.h Simple event queue
+ *
+ * Version: $Id$
+ *
+ * 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
+ *
+ * Copyright 2007 The FreeRADIUS server project
+ * Copyright 2007 Alan DeKok <aland@deployingradius.com>
+ */
+
+#include <freeradius-devel/ident.h>
+RCSIDH(event_h, "$Id$")
+
+typedef struct lrad_event_list_t lrad_event_list_t;
+typedef void (*lrad_event_callback_t)(void *);
+
+lrad_event_list_t *lrad_event_list_create(void);
+void lrad_event_list_free(lrad_event_list_t *el);
+
+int lrad_event_list_num_elements(lrad_event_list_t *el);
+
+int lrad_event_insert(lrad_event_list_t *el, lrad_event_callback_t callback,
+ void *ctx, struct timeval *when);
+int lrad_event_delete(lrad_event_list_t *el, void *ctx);
+
+int lrad_event_callback(lrad_event_list_t *el, void *ctx,
+ lrad_event_callback_t *pcallback);
+int lrad_event_when(lrad_event_list_t *el, void *ctx, struct timeval *when);
+
+int lrad_event_run(lrad_event_list_t *el, struct timeval *when);
+
+int lrad_event_now(lrad_event_list_t *el, struct timeval *when);
+
+#endif /* LRAD_HASH_H */
#include <freeradius-devel/radpaths.h>
#include <freeradius-devel/conf.h>
#include <freeradius-devel/conffile.h>
+#include <freeradius-devel/realms.h>
#ifdef HAVE_UNISTD_H
#define child_kill kill
#endif
-#ifdef HAVE_NETINET_IN_H
-#endif
-
#define NO_SUCH_CHILD_PID (child_pid_t) (0)
#ifndef NDEBUG
*/
typedef struct rad_listen_t rad_listen_t;
-/*
- * For request lists.
- */
-typedef struct request_list_t request_list_t;
-
#define REQUEST_DATA_REGEX (0xadbeef00)
#define REQUEST_MAX_REGEX (8)
rad_listen_t *listener;
rad_listen_t *proxy_listener;
- /*
- * We could almost keep a const char here instead of a
- * _copy_ of the secret... but what if the RADCLIENT
- * structure is freed because it was taken out of the
- * config file and SIGHUPed?
- */
- char proxysecret[32];
- int proxy_try_count;
- int proxy_outstanding;
- time_t proxy_start_time;
-
int simul_max;
int simul_count;
int simul_mpp; /* WEIRD: 1 is false, 2 is true */
- int finished;
int options; /* miscellanous options */
const char *module; /* for debugging unresponsive children */
const char *component; /* ditto */
- void *container;
+
+ struct timeval received;
+ struct timeval when; /* to wake up */
+ int delay;
+
+ int master_state;
+ int child_state;
+
+ struct timeval next_when;
+ void *next_callback;
+
+ int in_request_hash;
+ int in_proxy_hash;
+
+ home_server *home_server;
+
+ struct timeval proxy_when;
+
+ int num_proxied_requests;
+ int num_proxied_responses;
+
} REQUEST;
#define RAD_REQUEST_OPTION_NONE (0)
-#define RAD_REQUEST_OPTION_LOGGED_CHILD (1 << 0)
-#define RAD_REQUEST_OPTION_DELAYED_REJECT (1 << 1)
-#define RAD_REQUEST_OPTION_DONT_CACHE (1 << 2)
-#define RAD_REQUEST_OPTION_FAKE_REQUEST (1 << 3)
-#define RAD_REQUEST_OPTION_REJECTED (1 << 4)
-#define RAD_REQUEST_OPTION_PROXIED (1 << 5)
-#define RAD_REQUEST_OPTION_STOP_NOW (1 << 6)
-#define RAD_REQUEST_OPTION_REPROCESS (1 << 7)
+
+#define REQUEST_ACTIVE (1)
+#define REQUEST_STOP_PROCESSING (2)
+
+#define REQUEST_QUEUED (1)
+#define REQUEST_RUNNING (2)
+#define REQUEST_PROXIED (3)
+#define REQUEST_REJECT_DELAY (4)
+#define REQUEST_CLEANUP_DELAY (5)
+#define REQUEST_DONE (6)
/*
* Function handler for requests.
typedef struct radclient_list RADCLIENT_LIST;
-typedef struct _realm {
- char realm[64];
- char server[64];
- char acct_server[64];
- lrad_ipaddr_t ipaddr; /* authentication */
- lrad_ipaddr_t acct_ipaddr;
- char secret[32];
- time_t last_reply; /* last time we saw a packet */
- int auth_port;
- int acct_port;
- int striprealm;
- int trusted; /* old */
- int notrealm;
- int active; /* is it dead? */
- time_t wakeup; /* when we should try it again */
- int acct_active;
- time_t acct_wakeup;
- int ldflag;
- struct _realm *next;
-} REALM;
-
typedef struct pair_list {
char *name;
VALUE_PAIR *check;
typedef int (*rad_listen_recv_t)(rad_listen_t *, RAD_REQUEST_FUNP *, REQUEST **);
typedef int (*rad_listen_send_t)(rad_listen_t *, REQUEST *);
-typedef int (*rad_listen_update_t)(rad_listen_t *, time_t);
typedef int (*rad_listen_print_t)(rad_listen_t *, char *, size_t);
+typedef int (*rad_listen_encode_t)(rad_listen_t *, REQUEST *);
+typedef int (*rad_listen_decode_t)(rad_listen_t *, REQUEST *);
struct rad_listen_t {
struct rad_listen_t *next; /* should be rbtree stuff */
RAD_LISTEN_TYPE type;
int fd;
const char *identity;
- request_list_t *rl;
rad_listen_recv_t recv;
rad_listen_send_t send;
- rad_listen_update_t update;
+ rad_listen_encode_t encode;
+ rad_listen_decode_t decode;
rad_listen_print_t print;
void *data;
radlog_dest_t radlog_dest;
CONF_SECTION *config;
RADCLIENT_LIST *clients;
- REALM *realms;
const char *radiusd_conf;
} MAIN_CONFIG_T;
const char *client_name_old(const lrad_ipaddr_t *ipaddr);
/* files.c */
-REALM *realm_find(const char *, int);
-REALM *realm_findbyaddr(uint32_t ipno, int port);
-void realm_free(REALM *cl);
-void realm_disable(REQUEST *);
int pairlist_read(const char *file, PAIR_LIST **list, int complain);
void pairlist_free(PAIR_LIST **);
int read_config_files(void);
-int read_realms_file(const char *file);
/* version.c */
void version(void);
int paircompare(REQUEST *req, VALUE_PAIR *request, VALUE_PAIR *check,
VALUE_PAIR **reply);
int simplepaircmp(REQUEST *, VALUE_PAIR *, VALUE_PAIR *);
-void pair_builtincompare_init(void);
void pairxlatmove(REQUEST *, VALUE_PAIR **to, VALUE_PAIR **from);
/* xlat.c */
int listen_init(const char *filename, rad_listen_t **head);
rad_listen_t *proxy_new_listener(void);
+/* event.c */
+int radius_event_init(int spawn_flag);
+void radius_event_free(void);
+int radius_event_process(struct timeval **pptv);
+void radius_handle_request(REQUEST *request, RAD_REQUEST_FUNP fun);
+int received_request(rad_listen_t *listener,
+ RADIUS_PACKET *packet, REQUEST **prequest,
+ const RADCLIENT *client);
+REQUEST *received_proxy_response(RADIUS_PACKET *packet);
+
#endif /*RADIUSD_H*/
--- /dev/null
+#ifndef REALMS_H
+#define REALMS_H
+
+/*
+ * realms.h Structures, prototypes and global variables
+ * for realms
+ *
+ * Version: $Id$
+ *
+ */
+
+#include <freeradius-devel/ident.h>
+RCSIDH(realms_h, "$Id$")
+
+#define HOME_TYPE_AUTH (1)
+#define HOME_TYPE_ACCT (2)
+
+#define HOME_PING_CHECK_NONE (0)
+#define HOME_PING_CHECK_STATUS_SERVER (1)
+#define HOME_PING_CHECK_REQUEST (2)
+
+#define HOME_STATE_ALIVE (0)
+#define HOME_STATE_ZOMBIE (1)
+#define HOME_STATE_IS_DEAD (2)
+
+typedef struct home_server {
+ const char *name;
+
+ const char *hostname;
+
+ lrad_ipaddr_t ipaddr;
+
+
+ int port;
+ int type; /* auth/acct */
+
+ /*
+ * Maybe also have list of source IP/ports, && socket?
+ */
+
+ const char *secret;
+
+ struct timeval when;
+
+ int response_window;
+ int max_outstanding; /* don't overload it */
+ int currently_outstanding;
+
+ struct timeval zombie_period_start;
+ int zombie_period; /* unresponsive for T, mark it dead */
+
+ int state;
+
+ int ping_check;
+ const char *ping_user_name;
+ const char *ping_user_password;
+
+ int ping_interval;
+ int num_pings_to_alive;
+ int num_received_pings;
+
+ int revive_interval; /* if it doesn't support pings */
+} home_server;
+
+
+typedef enum home_pool_type_t {
+ HOME_POOL_INVALID = 0,
+ HOME_POOL_LOAD_BALANCE,
+ HOME_POOL_FAIL_OVER
+} home_pool_type_t;
+
+
+typedef struct home_pool_t {
+ const char *name;
+ home_pool_type_t type;
+
+ int server_type;
+
+ int num_home_servers;
+ home_server *servers[1];
+} home_pool_t;
+
+
+typedef struct _realm {
+ const char *name;
+
+ int striprealm;
+
+ home_pool_t *auth_pool;
+ home_pool_t *acct_pool;
+} REALM;
+
+int realms_init(const char *filename);
+void realms_free(void);
+int realm_add(const char *filename, CONF_SECTION *cs);
+REALM *realm_find(const char *name);
+
+home_server *home_server_ldb(REALM *realm, int code);
+home_server *home_server_find(lrad_ipaddr_t *ipaddr, int port);
+
+#endif /* REALMS_H */
+++ /dev/null
-#ifndef _REQUEST_LIST_H
-#define _REQUEST_LIST_H
-/*
- * request_list.h Hide the handling of the REQUEST list from
- * the main server.
- *
- * Version: $Id$
- *
- */
-
-#include <freeradius-devel/ident.h>
-RCSIDH(request_list_h, "$Id$")
-
-extern request_list_t *rl_init(void);
-extern void rl_deinit(request_list_t *);
-extern void rl_yank(request_list_t *, REQUEST *);
-extern void rl_delete(request_list_t *, REQUEST *);
-extern int rl_add(request_list_t *, REQUEST *);
-extern REQUEST *rl_find(request_list_t *, RADIUS_PACKET *);
-
-extern int rl_init_proxy(void);
-extern int rl_add_proxy(REQUEST *request);
-extern REQUEST *rl_find_proxy(RADIUS_PACKET *packet);
-extern REQUEST *rl_next(request_list_t *, REQUEST *);
-extern int rl_num_requests(request_list_t *);
-extern int rl_clean_list(request_list_t *, time_t now);
-
-#endif /* _REQUEST_LIST_H */
SRCS = dict.c filters.c hash.c hmac.c hmacsha1.c isaac.c log.c \
misc.c missing.c md4.c md5.c print.c radius.c rbtree.c \
sha1.c snprintf.c strlcat.c strlcpy.c token.c udpfromto.c \
- valuepair.c fifo.c packet.c
+ valuepair.c fifo.c packet.c event.c
LT_OBJS = $(SRCS:.c=.lo)
--- /dev/null
+/*
+ * event.c Non-thread-safe event handling, specific to a RADIUS
+ * server.
+ *
+ * Version: $Id$
+ *
+ * This library is free software; you can redistribute it and/or
+ * modify it under the terms of the GNU Lesser General Public
+ * License as published by the Free Software Foundation; either
+ * version 2.1 of the License, or (at your option) any later version.
+ *
+ * This library is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
+ * Lesser General Public License for more details.
+ *
+ * You should have received a copy of the GNU Lesser General Public
+ * License along with this library; if not, write to the Free Software
+ * Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301, USA
+ *
+ * Copyright 2007 The FreeRADIUS server project
+ * Copyright 2007 Alan DeKok <aland@ox.org>
+ */
+
+#include <freeradius-devel/ident.h>
+RCSID("$Id$")
+
+#include <freeradius-devel/autoconf.h>
+
+#include <stdlib.h>
+#include <string.h>
+
+#include <freeradius-devel/missing.h>
+#include <freeradius-devel/libradius.h>
+#include <freeradius-devel/event.h>
+
+typedef struct lrad_event_fd_t {
+ int fd;
+ int priority;
+ lrad_event_callback_t callback;
+ void *ctx;
+ struct lrad_event_fd_t *next;
+} lrad_event_fd_t;
+
+struct lrad_event_list_t {
+ rbtree_t *times;
+ rbtree_t *contexts;
+
+ lrad_event_fd_t *fds;
+ int maxfd;
+ fd_set readfds;
+
+ int exit;
+
+ struct timeval now;
+ int dispatch;
+};
+
+/*
+ * Internal structure for managing events.
+ */
+typedef struct lrad_event_t {
+ lrad_event_callback_t callback;
+ void *ctx;
+ struct timeval when;
+} lrad_event_t;
+
+
+static int lrad_event_list_time_cmp(const void *one, const void *two)
+{
+ const lrad_event_t *a = one;
+ const lrad_event_t *b = two;
+
+ if (a->when.tv_sec < b->when.tv_sec) return -1;
+ if (a->when.tv_sec > b->when.tv_sec) return +1;
+
+ if (a->when.tv_usec < b->when.tv_usec) return -1;
+ if (a->when.tv_usec > b->when.tv_usec) return +1;
+
+ return 0;
+}
+
+static int lrad_event_list_ctx_cmp(const void *one, const void *two)
+{
+ const lrad_event_t *a = one;
+ const lrad_event_t *b = two;
+
+ if (a->ctx < b->ctx) return -1;
+ if (a->ctx > b->ctx) return +1;
+
+ return 0;
+}
+
+
+void lrad_event_list_free(lrad_event_list_t *el)
+{
+ if (!el) return;
+
+ rbtree_free(el->times);
+ rbtree_free(el->contexts);
+ free(el);
+}
+
+
+lrad_event_list_t *lrad_event_list_create(void)
+{
+ lrad_event_list_t *el;
+
+ el = malloc(sizeof(*el));
+ if (!el) return NULL;
+ memset(el, 0, sizeof(*el));
+
+ el->times = rbtree_create(lrad_event_list_time_cmp,
+ free, 0);
+ if (!el->times) {
+ lrad_event_list_free(el);
+ return NULL;
+ }
+
+ el->contexts = rbtree_create(lrad_event_list_ctx_cmp,
+ NULL, 0);
+ if (!el->contexts) {
+ lrad_event_list_free(el);
+ return NULL;
+ }
+
+ return el;
+}
+
+int lrad_event_list_num_elements(lrad_event_list_t *el)
+{
+ if (!el) return 0;
+
+ return rbtree_num_elements(el->times);
+}
+
+
+int lrad_event_delete(lrad_event_list_t *el, void *ctx)
+{
+ lrad_event_t my_ev, *ev;
+
+ if (!el || !ctx) return 0;
+
+ my_ev.ctx = ctx;
+ ev = rbtree_finddata(el->contexts, &my_ev);
+ if (!ev) return 0;
+
+ rbtree_deletebydata(el->contexts, ev);
+ rbtree_deletebydata(el->times, ev);
+
+ return 1;
+}
+
+
+int lrad_event_insert(lrad_event_list_t *el, lrad_event_callback_t callback,
+ void *ctx, struct timeval *when)
+{
+ lrad_event_t *ev;
+
+ if (!el || !callback | !when) return 0;
+
+ lrad_event_delete(el, ctx); /* can only be 1 event per ctx */
+
+ ev = malloc(sizeof(*ev));
+ if (!ev) return 0;
+ memset(ev, 0, sizeof(*ev));
+
+ ev->callback = callback;
+ ev->ctx = ctx;
+ ev->when = *when;
+
+ if (!rbtree_insert(el->contexts, ev)) {
+ free(ev);
+ return 0;
+ }
+
+ /*
+ * There's a tiny chance that two events will be
+ * scheduled at the same time. If this happens, we
+ * increase the usec counter by 1, in order to avoid the
+ * duplicate. If we can't insert it after 10 tries, die.
+ */
+ if (!rbtree_insert(el->times, ev)) {
+ if (rbtree_finddata(el->times, ev)) {
+ int i;
+
+ for (i = 0; i < 10; i++) {
+ ev->when.tv_usec++;
+ if (ev->when.tv_usec >= 1000000) {
+ ev->when.tv_usec = 0;
+ ev->when.tv_sec++;
+ }
+
+ if (rbtree_finddata(el->times, ev)) {
+ continue;
+ }
+
+ if (!rbtree_insert(el->times, ev)) {
+ break;
+ }
+
+ return 1;
+ }
+
+ }
+ rbtree_deletebydata(el->contexts, ev);
+ free(ev);
+ return 0;
+ }
+
+ return 1;
+}
+
+int lrad_event_callback(lrad_event_list_t *el, void *ctx,
+ lrad_event_callback_t *pcallback)
+{
+ lrad_event_t my_ev, *ev;
+
+ if (!el || !ctx || !pcallback) return 0;
+
+ my_ev.ctx = ctx;
+ ev = rbtree_finddata(el->contexts, &my_ev);
+ if (!ev) return 0;
+
+ *pcallback = ev->callback;
+ return 1;
+}
+
+
+int lrad_event_when(lrad_event_list_t *el, void *ctx, struct timeval *when)
+{
+ lrad_event_t my_ev, *ev;
+
+ if (!el || !ctx) return 0;
+
+ my_ev.ctx = ctx;
+ ev = rbtree_finddata(el->contexts, &my_ev);
+ if (!ev) return 0;
+
+ *when = ev->when;
+ return 1;
+}
+
+
+typedef struct lrad_event_walk_t {
+ lrad_event_t *ev;
+ struct timeval when;
+} lrad_event_walk_t;
+
+
+static int lrad_event_find_earliest(void *ctx, void *data)
+{
+ lrad_event_t *ev = data;
+ lrad_event_walk_t *w = ctx;
+
+ if (ev->when.tv_sec > w->when.tv_sec) {
+ w->when = ev->when;
+ return 1;
+ }
+
+ if ((ev->when.tv_sec == w->when.tv_sec) &&
+ (ev->when.tv_usec > w->when.tv_usec)) {
+ w->when = ev->when;
+ return 1;
+ }
+
+ w->ev = ev;
+ return 1;
+}
+
+
+int lrad_event_run(lrad_event_list_t *el, struct timeval *when)
+{
+ lrad_event_callback_t callback;
+ void *ctx;
+ lrad_event_walk_t w;
+
+ if (!el) return 0;
+
+ w.ev = NULL;
+ w.when = *when;
+
+ if (rbtree_num_elements(el->times) == 0) {
+ when->tv_sec = 0;
+ when->tv_usec = 0;
+ return 0;
+ }
+
+ rbtree_walk(el->times, InOrder, lrad_event_find_earliest, &w);
+ if (!w.ev) {
+ *when = w.when;
+ return 0;
+ }
+
+ callback = w.ev->callback;
+ ctx = w.ev->ctx;
+
+ /*
+ * Delete the event before calling it.
+ */
+ rbtree_deletebydata(el->contexts, w.ev);
+ rbtree_deletebydata(el->times, w.ev);
+
+ callback(ctx);
+ return 1;
+}
+
+
+static int lrad_event_insert_fd(lrad_event_list_t *el, int fd, int priority,
+ lrad_event_callback_t callback, void *ctx)
+{
+ lrad_event_fd_t **last, *ef;
+
+ if (!fd || (fd < 0) || (priority < 0) || !callback || !ctx) return 0;
+
+ ef = malloc(sizeof(*ef));
+ if (!ef) return 0;
+ memset(ef, 0, sizeof(*ef));
+
+ ef->fd = fd;
+ ef->priority = priority;
+ ef->callback = callback;
+ ef->ctx = ctx;
+
+ if (!el->fds) {
+ el->fds = ef;
+ el->maxfd = fd + 1;
+ FD_ZERO(&el->readfds);
+ FD_SET(fd, &el->readfds);
+ return 1;
+ }
+
+ for (last = &(el->fds);
+ *last != NULL;
+ last = &((*last)->next)) {
+ if ((*last)->priority < priority) {
+ ef->next = *last;
+ *last = ef;
+
+ if (fd >= el->maxfd) {
+ el->maxfd = fd + 1;
+ }
+ FD_SET(fd, &el->readfds);
+
+ return 1;
+ }
+ }
+
+ return 0;
+}
+
+
+static int lrad_event_delete_fd(lrad_event_list_t *el, int fd)
+{
+ lrad_event_fd_t **last;
+
+ if (!el || (fd < 0)) return 0;
+
+ for (last = &el->fds;
+ *last != NULL;
+ last = &((*last)->next)) {
+ if ((*last)->fd == fd) {
+ lrad_event_fd_t *ef = *last;
+ *last = (*last)->next;
+ free(ef);
+ return 1;
+ }
+ }
+
+ return 0;
+}
+
+int lrad_event_now(lrad_event_list_t *el, struct timeval *when)
+{
+ if (!el || !when || !el->dispatch) return 0;
+
+ *when = el->now;
+ return 1;
+}
+
+static void lrad_event_exit_loop_cb(void *data)
+{
+ lrad_event_list_t *el = data;
+
+ el->exit = 1;
+}
+
+static int lrad_event_exit_loop(lrad_event_list_t *el, struct timeval *when)
+{
+ if (!el || !when) return 0;
+
+ return lrad_event_insert(el, lrad_event_exit_loop_cb, el, when);
+}
+
+
+static int lrad_event_dispatch(lrad_event_list_t *el)
+{
+ if (!el || !el->fds) return 0;
+
+ el->dispatch = 1;
+
+ while (!el->exit) {
+ int rcode;
+ fd_set readfds;
+ struct timeval when, *timeout;
+ lrad_event_fd_t *ef;
+
+ gettimeofday(&el->now, NULL);
+ while (1) {
+ when = el->now;
+
+ if (!lrad_event_run(el, &when)) {
+ break;
+ }
+ }
+ gettimeofday(&el->now, NULL);
+
+ if (rbtree_num_elements(el->times) == 0) {
+ timeout = NULL;
+
+ } else if ((el->now.tv_sec >= when.tv_sec) ||
+ ((el->now.tv_sec == when.tv_sec) &&
+ (el->now.tv_usec >= when.tv_usec))) {
+ timeout = &when;
+ when.tv_sec = 0;
+ when.tv_usec = 0;
+
+ } else {
+ timeout = &when;
+
+ when.tv_sec -= el->now.tv_sec;
+ if (when.tv_sec == 0) {
+ when.tv_usec -= el->now.tv_usec;
+ } else if (when.tv_usec > el->now.tv_usec) {
+ when.tv_usec -= el->now.tv_usec;
+ } else {
+ when.tv_sec--;
+ when.tv_usec += 1000000;
+ when.tv_usec -= el->now.tv_usec;
+ }
+ }
+
+ readfds = el->readfds;
+
+ rcode = select(el->maxfd, &readfds, NULL, NULL, timeout);
+ if (rcode == 0) {
+ continue;
+ }
+
+ if (rcode < 0) {
+ el->dispatch = 0;
+ el->exit = 0;
+ return -1;
+ }
+
+ for (ef = el->fds; ef != NULL; ef = ef->next) {
+ if (FD_ISSET(ef->fd, &readfds)) {
+ ef->callback(ef->ctx);
+ continue; /* starve lower priority FD's! */
+ }
+ }
+ }
+
+ el->exit = 0;
+ el->dispatch = 0;
+
+ return 1;
+}
+
+
+#ifdef TESTING
+
+/*
+ * cc -g -I .. -c rbtree.c -o rbtree.o && cc -g -I .. -c isaac.c -o isaac.o && cc -DTESTING -I .. -c event.c -o event_mine.o && cc event_mine.o rbtree.o isaac.o -o event
+ *
+ * ./event
+ *
+ * And hit CTRL-S to stop the output, CTRL-Q to continue.
+ * It normally alternates printing the time and sleeping,
+ * but when you hit CTRL-S/CTRL-Q, you should see a number
+ * of events run right after each other.
+ *
+ * OR
+ *
+ * valgrind --tool=memcheck --leak-check=full --show-reachable=yes ./event
+ */
+
+static void print_time(void *ctx)
+{
+ struct timeval *when = ctx;
+
+ printf("%d.%06d\n", when->tv_sec, when->tv_usec);
+ fflush(stdout);
+}
+
+static lrad_randctx rand_pool;
+
+static uint32_t event_rand(void)
+{
+ uint32_t num;
+
+ num = rand_pool.randrsl[rand_pool.randcnt++];
+ if (rand_pool.randcnt == 256) {
+ lrad_isaac(&rand_pool);
+ rand_pool.randcnt = 0;
+ }
+
+ return num;
+}
+
+
+#define MAX 100
+int main(int argc, char **argv)
+{
+ int i, rcode;
+ struct timeval array[MAX];
+ struct timeval now, when;
+ lrad_event_list_t *el;
+
+ el = lrad_event_list_create();
+ if (!el) exit(1);
+
+ memset(&rand_pool, 0, sizeof(rand_pool));
+ rand_pool.randrsl[1] = time(NULL);
+
+ lrad_randinit(&rand_pool, 1);
+ rand_pool.randcnt = 0;
+
+ gettimeofday(&array[0], NULL);
+ for (i = 1; i < MAX; i++) {
+ array[i] = array[i - 1];
+
+ array[i].tv_usec += event_rand() & 0xffff;
+ if (array[i].tv_usec > 1000000) {
+ array[i].tv_usec -= 1000000;
+ array[i].tv_sec++;
+ }
+ lrad_event_insert(el, print_time, &array[i], &array[i]);
+ }
+
+ while (lrad_event_list_num_elements(el)) {
+ gettimeofday(&now, NULL);
+ when = now;
+ if (!lrad_event_run(el, &when)) {
+ int delay = (when.tv_sec - now.tv_sec) * 1000000;
+ delay += when.tv_usec;
+ delay -= now.tv_usec;
+
+ printf("\tsleep %d\n", delay);
+ fflush(stdout);
+ usleep(delay);
+ }
+ }
+
+ lrad_event_list_free(el);
+
+ return 0;
+}
+#endif
include ../../Make.inc
SERVER_SRCS = acct.c auth.c client.c conffile.c crypt.c exec.c files.c \
- listen.c log.c mainconfig.c modules.c modcall.c proxy.c \
- radiusd.c radius_snmp.c request_list.c request_process.c \
+ listen.c log.c mainconfig.c modules.c modcall.c \
+ radiusd.c radius_snmp.c \
session.c smux.c threads.c util.c valuepair.c version.c \
- xlat.c
+ xlat.c event.c realms.c
SERVER_OBJS += $(SERVER_SRCS:.c=.lo)
$(MODULE_LIBS) $(LIBS) $(SNMP_LIBS) $(LCRYPT) \
$(PTHREADLIB) $(LIBLTDL) $(OPENSSL_LIBS)
-radiusd.lo: radiusd.c ../include/request_list.h ../include/modules.h ../include/modcall.h ../include/modpriv.h
+radiusd.lo: radiusd.c ../include/modules.h ../include/modcall.h ../include/modpriv.h
$(LIBTOOL) --mode=compile $(CC) $(CFLAGS) -c radiusd.c
acct.lo: acct.c ../include/modules.h
modules.lo: modules.c
$(LIBTOOL) --mode=compile $(CC) $(CFLAGS) $(VFLAGS) $(INCLTDL) -c modules.c
-proxy.lo: proxy.c
- $(LIBTOOL) --mode=compile $(CC) $(CFLAGS) -c proxy.c
-
radius_snmp.lo: radius_snmp.c
$(LIBTOOL) --mode=compile $(CC) $(CFLAGS) -c radius_snmp.c
-request_list.lo: request_list.c
- $(LIBTOOL) --mode=compile $(CC) $(CFLAGS) -c request_list.c
+event.lo: event.c
+ $(LIBTOOL) --mode=compile $(CC) $(CFLAGS) -c event.c
-request_process.lo: request_process.c
- $(LIBTOOL) --mode=compile $(CC) $(CFLAGS) -c request_process.c
+realms.lo: realms.c
+ $(LIBTOOL) --mode=compile $(CC) $(CFLAGS) -c realms.c
session.lo: session.c ../include/modules.h
$(LIBTOOL) --mode=compile $(CC) $(CFLAGS) -c session.c
* Check whether Proxy-To-Realm is
* a LOCAL realm.
*/
- realm = realm_find(vp->vp_strvalue, TRUE);
- if (realm != NULL &&
- realm->acct_ipaddr.af == AF_INET &&
- realm->acct_ipaddr.ipaddr.ip4addr.s_addr == htonl(INADDR_NONE)) {
- DEBUG("rad_accounting: Cancelling proxy to realm %s, as it is a LOCAL realm.", realm->realm);
+ realm = realm_find(vp->vp_strvalue);
+ if (realm && !realm->acct_pool) {
+ DEBUG("rad_accounting: Cancelling proxy to realm %s, as it is a LOCAL realm.", realm->name);
pairdelete(&request->config_items, PW_PROXY_TO_REALM);
} else {
/*
* Catch users who set Proxy-To-Realm to a LOCAL
* realm (sigh).
*/
- realm = realm_find(tmp->vp_strvalue, 0);
- rad_assert((realm == NULL) || (realm->ipaddr.af == AF_INET));
- if (realm && (realm->ipaddr.ipaddr.ip4addr.s_addr == htonl(INADDR_NONE))) {
- DEBUG2(" WARNING: You set Proxy-To-Realm = %s, but it is a LOCAL realm! Cancelling invalid proxy request.", realm->realm);
+ realm = realm_find(tmp->vp_strvalue);
+ if (realm && !realm->auth_pool) {
+ DEBUG2(" WARNING: You set Proxy-To-Realm = %s, but it is a LOCAL realm! Cancelling invalid proxy request.", realm->name);
} else {
/*
* Don't authenticate, as the request is
--- /dev/null
+/*
+ * event.c Server event handling
+ *
+ * Version: $Id$
+ *
+ * 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
+ *
+ * Copyright 2007 The FreeRADIUS server project
+ * Copyright 2007 Alan DeKok <aland@deployingradius.com>
+ */
+
+#include <freeradius-devel/ident.h>
+RCSID("$Id$")
+
+#include <freeradius-devel/autoconf.h>
+
+#include <stdlib.h>
+#include <string.h>
+#include <signal.h>
+
+#include <freeradius-devel/radiusd.h>
+#include <freeradius-devel/rad_assert.h>
+#include <freeradius-devel/event.h>
+#include <freeradius-devel/modules.h>
+
+#define USEC (1000000)
+
+/*
+ * Ridiculous amounts of local state.
+ */
+static lrad_event_list_t *el = NULL;
+static lrad_packet_list_t *pl = NULL;
+static int request_num_counter = 0;
+static struct timeval now;
+static time_t start_time;
+static int have_children;
+
+#ifdef HAVE_PTHREAD_H
+static pthread_mutex_t proxy_mutex;
+
+#define PTHREAD_MUTEX_LOCK if (have_children) pthread_mutex_lock
+#define PTHREAD_MUTEX_UNLOCK if (have_children) pthread_mutex_unlock
+#else
+/*
+ * This is easier than ifdef's throughout the code.
+ */
+#define PTHREAD_MUTEX_LOCK(_x)
+#define PTHREAD_MUTEX_UNLOCK(_x)
+#endif
+
+static lrad_packet_list_t *proxy_list = NULL;
+
+/*
+ * We keep the proxy FD's here. The RADIUS Id's are marked
+ * "allocated" per Id, via a bit per proxy FD.
+ */
+static int proxy_fds[32];
+static rad_listen_t *proxy_listeners[32];
+
+
+static void _rad_panic(const char *file, unsigned int line, const char *msg)
+{
+ radlog(L_ERR, "]%s:%d] %s", file, line, msg);
+ _exit(1);
+}
+
+#define rad_panic(x) _rad_panic(__FILE__, __LINE__, x)
+
+
+/*
+ * FIXME FIXME: Do SNMP, but smarter... have an array[code].foo,
+ * so we increment counters by just using code as an offset. The
+ * array should be the union of the server && client SNMP
+ * variables, which should simplify it a lot.
+ */
+
+static void tv_add(struct timeval *tv, int usec_delay)
+{
+ if (usec_delay > USEC) {
+ tv->tv_sec += usec_delay / USEC;
+ usec_delay %= USEC;
+ }
+ tv->tv_usec += usec_delay;
+
+ if (tv->tv_usec > USEC) {
+ tv->tv_usec -= USEC;
+ tv->tv_sec++;
+ }
+}
+
+
+static void remove_from_request_hash(REQUEST *request)
+{
+#ifdef WITH_SNMP
+ /*
+ * Update the SNMP statistics.
+ *
+ * Note that we do NOT do this in a child thread.
+ * Instead, we update the stats when a request is
+ * deleted, because only the main server thread calls
+ * this function...
+ */
+ if (mainconfig.do_snmp) {
+ switch (request->reply->code) {
+ case PW_AUTHENTICATION_ACK:
+ rad_snmp.auth.total_responses++;
+ rad_snmp.auth.total_access_accepts++;
+ break;
+
+ case PW_AUTHENTICATION_REJECT:
+ rad_snmp.auth.total_responses++;
+ rad_snmp.auth.total_access_rejects++;
+ break;
+
+ case PW_ACCESS_CHALLENGE:
+ rad_snmp.auth.total_responses++;
+ rad_snmp.auth.total_access_challenges++;
+ break;
+
+ case PW_ACCOUNTING_RESPONSE:
+ rad_snmp.acct.total_responses++;
+ break;
+
+ default:
+ break;
+ }
+ }
+#endif
+
+ if (!request->in_request_hash) return;
+
+ lrad_packet_list_yank(pl, request->packet);
+ request->in_request_hash = FALSE;
+}
+
+
+static REQUEST *lookup_in_proxy_hash(RADIUS_PACKET *reply)
+{
+ RADIUS_PACKET **proxy_p;
+ REQUEST *request;
+
+ PTHREAD_MUTEX_LOCK(&proxy_mutex);
+ proxy_p = lrad_packet_list_find_byreply(proxy_list, reply);
+
+ if (!proxy_p) {
+ PTHREAD_MUTEX_UNLOCK(&proxy_mutex);
+ return NULL;
+ }
+
+ request = lrad_packet2myptr(REQUEST, proxy, proxy_p);
+
+ if (!request) {
+ PTHREAD_MUTEX_UNLOCK(&proxy_mutex);
+ return NULL;
+ }
+
+ request->num_proxied_responses++;
+
+ /*
+ * Catch the most common case of everything working
+ * correctly.
+ */
+ if (request->num_proxied_requests == request->num_proxied_responses) {
+ /*
+ * FIXME: remove from the event list, too?
+ */
+ lrad_packet_list_yank(proxy_list, request->proxy);
+ lrad_packet_list_id_free(proxy_list, request->proxy);
+ request->in_proxy_hash = FALSE;
+ }
+
+ PTHREAD_MUTEX_UNLOCK(&proxy_mutex);
+
+ return request;
+}
+
+
+static void remove_from_proxy_hash(REQUEST *request)
+{
+ if (!request->in_proxy_hash) return;
+
+ PTHREAD_MUTEX_LOCK(&proxy_mutex);
+ request->home_server->currently_outstanding--;
+ lrad_packet_list_yank(proxy_list, request->proxy);
+ lrad_packet_list_id_free(proxy_list, request->proxy);
+ PTHREAD_MUTEX_UNLOCK(&proxy_mutex);
+
+ request->in_proxy_hash = FALSE;
+}
+
+
+static int insert_into_proxy_hash(REQUEST *request)
+{
+ int i, proxy;
+ char buf[128];
+
+ rad_assert(request->proxy != NULL);
+ rad_assert(proxy_list != NULL);
+
+ request->proxy->sockfd = -1;
+
+ PTHREAD_MUTEX_LOCK(&proxy_mutex);
+
+ request->home_server->currently_outstanding++;
+
+ if (!lrad_packet_list_id_alloc(proxy_list, request->proxy)) {
+ int found;
+ rad_listen_t *proxy_listener;
+
+ /*
+ * Allocate a new proxy fd. This function adds it
+ * into the list of listeners.
+ */
+ proxy_listener = proxy_new_listener();
+ if (!proxy_listener) {
+ PTHREAD_MUTEX_UNLOCK(&proxy_mutex);
+ DEBUG2("ERROR: Failed to create a new socket for proxying requests.");
+ return 0;
+ }
+
+ /*
+ * Cache it locally.
+ */
+ found = -1;
+ proxy = proxy_listener->fd;
+ for (i = 0; i < 32; i++) {
+ DEBUG2("PROXY %d %d", i, proxy_fds[(proxy + i) & 0x1f]);
+
+ /*
+ * Found a free entry. Save the socket,
+ * and remember where we saved it.
+ */
+ if (proxy_fds[(proxy + i) & 0x1f] == -1) {
+ found = (proxy + i) & 0x1f;
+ proxy_fds[found] = proxy;
+ proxy_listeners[found] = proxy_listener;
+ break;
+ }
+ }
+ rad_assert(found >= 0);
+
+ if (!lrad_packet_list_socket_add(proxy_list, proxy_listener->fd)) {
+ PTHREAD_MUTEX_UNLOCK(&proxy_mutex);
+ DEBUG2("ERROR: Failed to create a new socket for proxying requests.");
+ return 0; /* leak proxy_listener */
+
+ }
+
+ if (!lrad_packet_list_id_alloc(proxy_list, request->proxy)) {
+ PTHREAD_MUTEX_UNLOCK(&proxy_mutex);
+ DEBUG2("ERROR: Failed to create a new socket for proxying requests.");
+ return 0;
+ }
+ }
+ rad_assert(request->proxy->sockfd >= 0);
+
+ /*
+ * FIXME: Hack until we get rid of rad_listen_t, and put
+ * the information into the packet_list.
+ */
+ proxy = -1;
+ for (i = 0; i < 32; i++) {
+ if (proxy_fds[i] == request->proxy->sockfd) {
+ proxy = i;
+ break;
+ }
+ }
+
+ if (proxy < 0) {
+ PTHREAD_MUTEX_UNLOCK(&proxy_mutex);
+ DEBUG2("ERROR: All sockets are full.");
+ return 0;
+ }
+
+ rad_assert(proxy_fds[proxy] != -1);
+ rad_assert(proxy_listeners[proxy] != NULL);
+ request->proxy_listener = proxy_listeners[proxy];
+
+ if (!lrad_packet_list_insert(proxy_list, &request->proxy)) {
+ /* FIXME: free id? */
+ PTHREAD_MUTEX_UNLOCK(&proxy_mutex);
+ DEBUG2("ERROR: Failed to insert entry into proxy list");
+ return 0;
+ }
+
+ PTHREAD_MUTEX_UNLOCK(&proxy_mutex);
+
+ DEBUG3(" proxy: allocating destination %s port %d - Id %d",
+ inet_ntop(request->proxy->dst_ipaddr.af,
+ &request->proxy->dst_ipaddr.ipaddr, buf, sizeof(buf)),
+ request->proxy->dst_port,
+ request->proxy->id);
+
+ request->in_proxy_hash = TRUE;
+
+ return 1;
+}
+
+
+/*
+ * Called as BOTH an event, and in-line from other functions.
+ */
+static void wait_for_proxy_id_to_expire(void *ctx)
+{
+ REQUEST *request = ctx;
+ home_server *home = request->home_server;
+
+ rad_assert(request->magic == REQUEST_MAGIC);
+ rad_assert(request->proxy != NULL);
+
+ request->when = request->proxy_when;
+ request->when.tv_sec += home->response_window;
+
+ if ((request->num_proxied_requests == request->num_proxied_responses) ||
+ timercmp(&now, &request->when, >)) {
+ if (request->packet) {
+ DEBUG2("Cleaning up request %d ID %d with timestamp +%d",
+ request->number, request->packet->id,
+ (unsigned int) (request->timestamp - start_time));
+ } else {
+ DEBUG2("Cleaning up request %d with timestamp +%d",
+ request->number,
+ (unsigned int) (request->timestamp - start_time));
+ }
+ lrad_event_delete(el, request);
+ remove_from_proxy_hash(request);
+ remove_from_request_hash(request);
+ request_free(&request);
+ return;
+ }
+
+ if (!lrad_event_insert(el, wait_for_proxy_id_to_expire,
+ request, &request->when)) {
+ rad_panic("Failed to insert event");
+ }
+}
+
+
+static void wait_for_child_to_die(void *ctx)
+{
+ REQUEST *request = ctx;
+
+ rad_assert(request->magic == REQUEST_MAGIC);
+
+ if ((request->child_state == REQUEST_QUEUED) |
+ (request->child_state == REQUEST_RUNNING)) {
+ request->delay += (request->delay >> 1);
+ tv_add(&request->when, request->delay);
+
+ DEBUG2("Child is still stuck for request %d", request->number);
+ if (!lrad_event_insert(el, wait_for_child_to_die,
+ request, &request->when)) {
+ rad_panic("Failed to insert event");
+ }
+ return;
+ }
+
+ DEBUG2("Child is finally responsive for request %d", request->number);
+ remove_from_request_hash(request);
+
+ if (request->proxy) {
+ return wait_for_proxy_id_to_expire(request);
+ }
+
+ request_free(&request);
+}
+
+
+static void cleanup_delay(void *ctx)
+{
+ REQUEST *request = ctx;
+
+ rad_assert(request->magic == REQUEST_MAGIC);
+ rad_assert(request->child_state == REQUEST_CLEANUP_DELAY);
+
+ remove_from_request_hash(request);
+
+ if (request->proxy) {
+ return wait_for_proxy_id_to_expire(request);
+ }
+
+ DEBUG2("Cleaning up request %d ID %d with timestamp +%d",
+ request->number, request->packet->id,
+ (unsigned int) (request->timestamp - start_time));
+
+ request_free(&request);
+}
+
+
+static void reject_delay(void *ctx)
+{
+ REQUEST *request = ctx;
+
+ rad_assert(request->magic == REQUEST_MAGIC);
+ rad_assert(request->child_state == REQUEST_REJECT_DELAY);
+
+ DEBUG2("Sending delayed reject for request %d", request->number);
+
+ request->listener->send(request->listener, request);
+
+ request->when.tv_sec += mainconfig.cleanup_delay;
+ request->child_state = REQUEST_CLEANUP_DELAY;
+
+ if (!lrad_event_insert(el, cleanup_delay,
+ request, &request->when)) {
+ rad_panic("Failed to insert event");
+ }
+}
+
+
+static void revive_home_server(void *ctx)
+{
+ home_server *home = ctx;
+
+ home->state = HOME_STATE_ALIVE;
+ DEBUG2("Marking home server alive again... we have no idea if it really is alive or not.");
+}
+
+
+static void no_response_to_ping(void *ctx)
+{
+ REQUEST *request = ctx;
+ home_server *home = request->home_server;
+ char buffer[128];
+
+ home->num_received_pings = 0;
+
+ DEBUG2("No response to ping %d from home server %s port %d",
+ request->number,
+ inet_ntop(request->proxy->dst_ipaddr.af,
+ &request->proxy->dst_ipaddr.ipaddr,
+ buffer, sizeof(buffer)),
+ request->proxy->dst_port);
+
+ wait_for_proxy_id_to_expire(request);
+}
+
+
+static void received_response_to_ping(REQUEST *request)
+{
+ home_server *home = request->home_server;
+ char buffer[128];
+
+ home->num_received_pings++;
+
+ DEBUG2("Received response to ping %d (%d in current sequence)",
+ request->number, home->num_received_pings);
+
+ if (home->num_received_pings < home->num_pings_to_alive) {
+ wait_for_proxy_id_to_expire(request);
+ return;
+ }
+
+ DEBUG2("Marking home server %s port %d alive",
+ inet_ntop(request->proxy->dst_ipaddr.af,
+ &request->proxy->dst_ipaddr.ipaddr,
+ buffer, sizeof(buffer)),
+ request->proxy->dst_port);
+
+ if (!lrad_event_delete(el, home)) {
+ DEBUG2("Hmm... no event for home server, WTF?");
+ }
+
+ if (!lrad_event_delete(el, request)) {
+ DEBUG2("Hmm... no event for request, WTF?");
+ }
+
+ wait_for_proxy_id_to_expire(request);
+
+ home->state = HOME_STATE_ALIVE;
+}
+
+
+static void ping_home_server(void *ctx)
+{
+ uint32_t jitter;
+ home_server *home = ctx;
+ REQUEST *request;
+ VALUE_PAIR *vp;
+
+ if (home->state == HOME_STATE_ALIVE) {
+ radlog(L_INFO, "Suspicious proxy state... continuing");
+ return;
+ }
+
+ request = request_alloc();
+ request->number = request_num_counter++;
+
+ request->proxy = rad_alloc(1);
+ rad_assert(request->proxy != NULL);
+
+ gettimeofday(&request->when, NULL);
+ home->when = request->when;
+
+ if (home->ping_check == HOME_PING_CHECK_STATUS_SERVER) {
+ request->proxy->code = PW_STATUS_SERVER;
+
+ vp = pairmake("Message-Authenticator", "0x00", T_OP_SET);
+ if (!vp) rad_panic("Out of memory");
+ pairadd(&request->proxy->vps, vp);
+
+ } else if (home->type == HOME_TYPE_AUTH) {
+ request->proxy->code = PW_AUTHENTICATION_REQUEST;
+
+ vp = pairmake("User-Name", home->ping_user_name, T_OP_SET);
+ if (!vp) rad_panic("Out of memory");
+ pairadd(&request->proxy->vps, vp);
+
+ vp = pairmake("User-Password", home->ping_user_password, T_OP_SET);
+ if (!vp) rad_panic("Out of memory");
+ pairadd(&request->proxy->vps, vp);
+
+ vp = pairmake("Service-Type", "Authenticate-Only", T_OP_SET);
+ if (!vp) rad_panic("Out of memory");
+ pairadd(&request->proxy->vps, vp);
+
+ vp = pairmake("Message-Authenticator", "0x00", T_OP_SET);
+ if (!vp) rad_panic("Out of memory");
+ pairadd(&request->proxy->vps, vp);
+
+ } else {
+ request->proxy->code = PW_ACCOUNTING_REQUEST;
+
+ vp = pairmake("User-Name", home->ping_user_name, T_OP_SET);
+ if (!vp) rad_panic("Out of memory");
+ pairadd(&request->proxy->vps, vp);
+
+ vp = pairmake("Acct-Status-Type", "Stop", T_OP_SET);
+ if (!vp) rad_panic("Out of memory");
+ pairadd(&request->proxy->vps, vp);
+
+ vp = pairmake("Acct-Session-Id", "00000000", T_OP_SET);
+ if (!vp) rad_panic("Out of memory");
+ pairadd(&request->proxy->vps, vp);
+
+ vp = pairmake("Event-Timestamp", "0", T_OP_SET);
+ if (!vp) rad_panic("Out of memory");
+ vp->vp_date = now.tv_sec;
+ pairadd(&request->proxy->vps, vp);
+ }
+
+ vp = pairmake("NAS-Identifier", "Ping! Are you alive?", T_OP_SET);
+ if (!vp) rad_panic("Out of memory");
+ pairadd(&request->proxy->vps, vp);
+
+ request->proxy->dst_ipaddr = home->ipaddr;
+ request->proxy->dst_port = home->port;
+ request->home_server = home;
+
+ rad_assert(request->proxy_listener == NULL);
+
+ if (!insert_into_proxy_hash(request)) {
+ DEBUG2("Failed inserting ping %d into proxy hash. Discarding it.",
+ request->number);
+ request_free(&request);
+ return;
+ }
+ rad_assert(request->proxy_listener != NULL);
+ request->proxy_listener->send(request->proxy_listener,
+ request);
+
+ /*
+ * FIXME: add a separate timeout for ping packets!
+ */
+ request->child_state = REQUEST_PROXIED;
+ request->when.tv_sec += mainconfig.cleanup_delay;
+
+ if (!lrad_event_insert(el, no_response_to_ping,
+ request, &request->when)) {
+ rad_panic("Failed to insert event");
+ }
+
+ /*
+ * Add +/- 2s of jitter, as suggested in RFC 3539
+ * and in the Issues and Fixes draft.
+ */
+ home->when.tv_sec += home->ping_interval - 2;
+
+ jitter = lrad_rand();
+ jitter ^= (jitter >> 10);
+ jitter &= ((1 << 23) - 1); /* 22 bits of 1 */
+
+ tv_add(&home->when, jitter);
+
+ if (!lrad_event_insert(el, ping_home_server,
+ home, &home->when)) {
+ rad_panic("Failed to insert event");
+ }
+}
+
+
+/* maybe check this against wait_for_proxy_id_to_expire? */
+static void no_response_to_proxied_request(void *ctx)
+{
+ REQUEST *request = ctx;
+ home_server *home;
+ struct timeval when;
+ char buffer[128];
+
+ rad_assert(request->magic == REQUEST_MAGIC);
+ rad_assert(request->child_state == REQUEST_PROXIED);
+
+ radlog(L_ERR, "Rejecting request %d due to lack of any response from home server %s port %d",
+ request->number,
+ inet_ntop(request->proxy->dst_ipaddr.af,
+ &request->proxy->dst_ipaddr.ipaddr,
+ buffer, sizeof(buffer)),
+ request->proxy->dst_port);
+
+ if ((request->proxy->code == PW_ACCOUNTING_REQUEST) ||
+ (request->proxy->code == PW_STATUS_SERVER)) {
+ remove_from_request_hash(request);
+ wait_for_proxy_id_to_expire(request);
+
+ } else {
+ /* FIXME: run it through post-auth reject section? */
+
+ request->reply->code = PW_AUTHENTICATION_REJECT;
+
+ request->listener->send(request->listener, request);
+
+ request->child_state = REQUEST_CLEANUP_DELAY;
+ request->when.tv_sec += mainconfig.cleanup_delay;
+
+ /* cleanup_delay calls wait_for_proxy_id_to_expire */
+ if (!lrad_event_insert(el, cleanup_delay,
+ request, &request->when)) {
+ rad_panic("Failed to insert event");
+ }
+ }
+
+ home = request->home_server;
+ if (home->state == HOME_STATE_IS_DEAD) {
+ /* FIXME: assert that some event is set for the home server */
+ return;
+ }
+
+ /*
+ * Enable the zombie period when we notice that the home
+ * server hasn't responded. We also back-date the start
+ * of the zombie period to when the proxied request was
+ * sent.
+ */
+ if (home->state == HOME_STATE_ALIVE) {
+ DEBUG2("WARNING: Home server %s port %d may be dead.",
+ inet_ntop(request->proxy->dst_ipaddr.af,
+ &request->proxy->dst_ipaddr.ipaddr,
+ buffer, sizeof(buffer)),
+ request->proxy->dst_port);
+ home->state = HOME_STATE_ZOMBIE;
+ home->zombie_period_start = now;
+ home->zombie_period_start.tv_sec -= home->response_window;
+ return;
+ }
+
+ when = home->zombie_period_start;
+ when.tv_sec += home->zombie_period;
+
+ if (timercmp(&now, &when, <)) {
+ return;
+ }
+
+ /*
+ * It's been a zombie for too long, mark it as
+ * dead.
+ */
+ home->state = HOME_STATE_IS_DEAD;
+ home->num_received_pings = 0;
+ home->when = request->when;
+
+ if (home->ping_check != HOME_PING_CHECK_NONE) {
+ rad_assert((home->ping_check == HOME_PING_CHECK_STATUS_SERVER) ||
+ (home->ping_user_name != NULL));
+ home->when.tv_sec += home->ping_interval;
+ if (!lrad_event_insert(el, ping_home_server,
+ home, &home->when)) {
+ rad_panic("Failed to insert event");
+ }
+ } else {
+ home->when.tv_sec += home->revive_interval;
+ if (!lrad_event_insert(el, revive_home_server,
+ home, &home->when)) {
+ rad_panic("Failed to insert event");
+ }
+ }
+}
+
+
+static void wait_a_bit(void *ctx)
+{
+ struct timeval when;
+ REQUEST *request = ctx;
+ lrad_event_callback_t callback = NULL;
+
+ rad_assert(request->magic == REQUEST_MAGIC);
+
+ switch (request->child_state) {
+ case REQUEST_QUEUED:
+ case REQUEST_RUNNING:
+ when = request->received;
+ when.tv_sec += mainconfig.max_request_time;
+
+ if (timercmp(&now, &when, <)) {
+ callback = wait_a_bit;
+ } else {
+ /* FIXME: kill unresponsive children? */
+ radlog(L_ERR, "WARNING: Unresponsive child (id %lu) for request %d, in module %s component %s",
+ (unsigned long)request->child_pid, request->number,
+ request->module ? request->module : "<server core>",
+ request->component ? request->component : "<server core>");
+
+ request->master_state = REQUEST_STOP_PROCESSING;
+
+ request->delay = 500000;
+ tv_add(&request->when, request->delay);
+ callback = wait_for_child_to_die;
+ }
+ request->delay += request->delay >> 1;
+ break;
+
+ case REQUEST_PROXIED:
+ case REQUEST_REJECT_DELAY:
+ case REQUEST_CLEANUP_DELAY:
+ rad_assert(request->next_callback != NULL);
+
+ request->when = request->next_when;
+ callback = request->next_callback;
+ request->next_callback = NULL;
+ break;
+
+ case REQUEST_DONE:
+ request->child_pid = NO_SUCH_CHILD_PID;
+ if (request->proxy) {
+ return wait_for_proxy_id_to_expire(request);
+ }
+ return;
+
+ default:
+ rad_panic("Internal sanity check failure");
+ return;
+ }
+
+#if 0
+ /*
+ * FIXME: Print time as "+%d.%06d", as that's more
+ * understandable than 32-bit numbers.
+ */
+ DEBUG2("Inserting event for request %d at time %d.%06d",
+ request->number,
+ (int) request->when.tv_sec, (int) request->when.tv_usec);
+#endif
+
+ if (!lrad_event_insert(el, callback,
+ request, &request->when)) {
+ rad_panic("Failed to insert event");
+ }
+}
+
+
+static int request_pre_handler(REQUEST *request)
+{
+ int rcode;
+
+ rad_assert(request->magic == REQUEST_MAGIC);
+ rad_assert(request->packet != NULL);
+ rad_assert(request->packet->dst_port != 0);
+
+ request->child_state = REQUEST_RUNNING;
+
+ /*
+ * Don't decode the packet if it's an internal "fake"
+ * request. Instead, just return so that the caller can
+ * process it.
+ */
+ if (request->packet->dst_port == 0) {
+ request->username = pairfind(request->packet->vps,
+ PW_USER_NAME);
+ request->password = pairfind(request->packet->vps,
+ PW_USER_PASSWORD);
+ return 1;
+ }
+
+ /*
+ * Put the decoded packet into it's proper place.
+ */
+ if (request->proxy_reply != NULL) {
+ rcode = request->proxy_listener->decode(request->proxy_listener,
+ request);
+ } else {
+ rcode = request->listener->decode(request->listener, request);
+ }
+
+ if (rcode < 0) {
+ radlog(L_ERR, "%s Dropping packet without response.", librad_errstr);
+ request->child_state = REQUEST_DONE;
+ return 0;
+ }
+
+ if (!request->proxy) {
+ request->username = pairfind(request->packet->vps,
+ PW_USER_NAME);
+ } else {
+ int post_proxy_type = 0;
+ VALUE_PAIR *vp;
+
+ /*
+ * Delete any reply we had accumulated until now.
+ */
+ pairfree(&request->reply->vps);
+
+ /*
+ * Run the packet through the post-proxy stage,
+ * BEFORE playing games with the attributes.
+ */
+ vp = pairfind(request->config_items, PW_POST_PROXY_TYPE);
+ if (vp) {
+ DEBUG2(" Found Post-Proxy-Type %s", vp->vp_strvalue);
+ post_proxy_type = vp->lvalue;
+ }
+ rcode = module_post_proxy(post_proxy_type, request);
+
+ /*
+ * Delete the Proxy-State Attributes from the reply.
+ * These include Proxy-State attributes from us and
+ * remote server.
+ */
+ pairdelete(&request->proxy_reply->vps, PW_PROXY_STATE);
+
+ /*
+ * Add the attributes left in the proxy reply to
+ * the reply list.
+ */
+ pairadd(&request->reply->vps, request->proxy_reply->vps);
+ request->proxy_reply->vps = NULL;
+
+ /*
+ * Free proxy request pairs.
+ */
+ pairfree(&request->proxy->vps);
+
+ /*
+ * FIXME: If the packet is an Access-Challenge,
+ * THEN add it to a cache, which does:
+ *
+ * (src IP, State) -> (home server ip/port)
+ *
+ * This allows the load-balancing code to
+ * work for EAP...
+ *
+ * Alternately, we can delete the State from the home
+ * server, and use our own.. that might be better.
+ */
+
+ switch (rcode) {
+ default: /* Don't Do Anything */
+ break;
+ case RLM_MODULE_FAIL:
+ /* FIXME: debug print stuff */
+ request->child_state = REQUEST_DONE;
+ return 0;
+
+ case RLM_MODULE_HANDLED:
+ /* FIXME: debug print stuff */
+ request->child_state = REQUEST_DONE;
+ return 0;
+ }
+ }
+
+ return 1;
+}
+
+
+/*
+ * Return 1 if we did proxy it, or the proxy attempt failed
+ * completely. Either way, the caller doesn't touch the request
+ * any more if we return 1.
+ */
+static int successfully_proxied_request(REQUEST *request)
+{
+ int rcode;
+ int pre_proxy_type = 0;
+ VALUE_PAIR *realmpair;
+ VALUE_PAIR *strippedname;
+ VALUE_PAIR *vp;
+ char *realmname;
+ home_server *home;
+ struct timeval when;
+ REALM *realm = NULL;
+ char buffer[128];
+
+ realmpair = pairfind(request->config_items, PW_PROXY_TO_REALM);
+ if (!realmpair ||
+ (realmpair->length == 0)) {
+ return 0;
+ }
+
+ realmname = (char *) realmpair->vp_strvalue;
+
+ realm = realm_find(realmname);
+ if (!realm) {
+ DEBUG2("ERROR: Cannot proxy to unknown realm %s",
+ realmname);
+ reject_request:
+ /*
+ * Failed proxying means silently discard
+ * accounting responses.
+ */
+ if (request->packet->code == PW_ACCOUNTING_REQUEST) {
+ request->child_state = REQUEST_DONE;
+ return 1;
+ }
+
+ rad_assert(request->packet->code == PW_AUTHENTICATION_REQUEST);
+
+ request->reply->code = PW_AUTHENTICATION_REJECT;
+
+ return 0;
+ }
+
+ if (((request->packet->code == PW_AUTHENTICATION_REQUEST) &&
+ !realm->auth_pool) ||
+ ((request->packet->code == PW_ACCOUNTING_REQUEST) &&
+ !realm->acct_pool)) {
+ DEBUG2(" WARNING: Cancelling proxy to Realm %s, as the realm is local.",
+ realmname);
+ return 0;
+ }
+
+ home = home_server_ldb(realm, request->packet->code);
+ if (!home) {
+ DEBUG2("ERROR: Failed to find live home server for realm %s",
+ realmname);
+ goto reject_request;
+ }
+
+ /*
+ * Remember that we sent the request to a Realm.
+ */
+ pairadd(&request->packet->vps,
+ pairmake("Realm", realmname, T_OP_EQ));
+
+ /*
+ * We read the packet from a detail file, AND it came from
+ * the server we're about to send it to. Don't do that.
+ */
+ if ((request->packet->code == PW_ACCOUNTING_REQUEST) &&
+ (request->listener->type == RAD_LISTEN_DETAIL) &&
+ (home->ipaddr.af == AF_INET) &&
+ (request->packet->src_ipaddr.af == AF_INET) &&
+ (home->ipaddr.ipaddr.ip4addr.s_addr == request->packet->src_ipaddr.ipaddr.ip4addr.s_addr)) {
+ DEBUG2(" rlm_realm: Packet came from realm %s, proxy cancelled", realmname);
+ return 0;
+ }
+
+ /*
+ * Allocate the proxy packet, only if it wasn't already
+ * allocated by a module. This check is mainly to support
+ * the proxying of EAP-TTLS and EAP-PEAP tunneled requests.
+ *
+ * In those cases, the EAP module creates a "fake"
+ * request, and recursively passes it through the
+ * authentication stage of the server. The module then
+ * checks if the request was supposed to be proxied, and
+ * if so, creates a proxy packet from the TUNNELED request,
+ * and not from the EAP request outside of the tunnel.
+ *
+ * The proxy then works like normal, except that the response
+ * packet is "eaten" by the EAP module, and encapsulated into
+ * an EAP packet.
+ */
+ if (!request->proxy) {
+ if ((request->proxy = rad_alloc(TRUE)) == NULL) {
+ radlog(L_ERR|L_CONS, "no memory");
+ exit(1);
+ }
+
+ /*
+ * Copy the request, then look up name and
+ * plain-text password in the copy.
+ *
+ * Note that the User-Name attribute is the
+ * *original* as sent over by the client. The
+ * Stripped-User-Name attribute is the one hacked
+ * through the 'hints' file.
+ */
+ request->proxy->vps = paircopy(request->packet->vps);
+ }
+
+ /*
+ * Strip the name, if told to.
+ *
+ * Doing it here catches the case of proxied tunneled
+ * requests.
+ */
+ if (realm->striprealm == TRUE &&
+ (strippedname = pairfind(request->proxy->vps, PW_STRIPPED_USER_NAME)) != NULL) {
+ /*
+ * If there's a Stripped-User-Name attribute in
+ * the request, then use THAT as the User-Name
+ * for the proxied request, instead of the
+ * original name.
+ *
+ * This is done by making a copy of the
+ * Stripped-User-Name attribute, turning it into
+ * a User-Name attribute, deleting the
+ * Stripped-User-Name and User-Name attributes
+ * from the vps list, and making the new
+ * User-Name the head of the vps list.
+ */
+ vp = pairfind(request->proxy->vps, PW_USER_NAME);
+ if (!vp) {
+ vp = paircreate(PW_USER_NAME, PW_TYPE_STRING);
+ if (!vp) {
+ radlog(L_ERR|L_CONS, "no memory");
+ exit(1);
+ }
+ vp->next = request->proxy->vps;
+ request->proxy->vps = vp;
+ }
+ memcpy(vp->vp_strvalue, strippedname->vp_strvalue,
+ sizeof(vp->vp_strvalue));
+ vp->length = strippedname->length;
+
+ /*
+ * Do NOT delete Stripped-User-Name.
+ */
+ }
+
+ /*
+ * If there is no PW_CHAP_CHALLENGE attribute but
+ * there is a PW_CHAP_PASSWORD we need to add it
+ * since we can't use the request authenticator
+ * anymore - we changed it.
+ */
+ if (pairfind(request->proxy->vps, PW_CHAP_PASSWORD) &&
+ pairfind(request->proxy->vps, PW_CHAP_CHALLENGE) == NULL) {
+ vp = paircreate(PW_CHAP_CHALLENGE, PW_TYPE_STRING);
+ if (!vp) {
+ radlog(L_ERR|L_CONS, "no memory");
+ exit(1);
+ }
+ vp->length = AUTH_VECTOR_LEN;
+ memcpy(vp->vp_strvalue, request->packet->vector, AUTH_VECTOR_LEN);
+ pairadd(&(request->proxy->vps), vp);
+ }
+
+ /*
+ * The RFC's say we have to do this, but FreeRADIUS
+ * doesn't need it.
+ */
+ vp = paircreate(PW_PROXY_STATE, PW_TYPE_STRING);
+ if (!vp) {
+ radlog(L_ERR|L_CONS, "no memory");
+ exit(1);
+ }
+ sprintf(vp->vp_strvalue, "%d", request->packet->id);
+ vp->length = strlen(vp->vp_strvalue);
+
+ pairadd(&request->proxy->vps, vp);
+
+ request->proxy->code = request->packet->code;
+ request->proxy->dst_ipaddr = home->ipaddr;
+ request->proxy->dst_port = home->port;
+ request->home_server = home;
+
+ /*
+ * Call the pre-proxy routines.
+ */
+ vp = pairfind(request->config_items, PW_PRE_PROXY_TYPE);
+ if (vp) {
+ DEBUG2(" Found Pre-Proxy-Type %s", vp->vp_strvalue);
+ pre_proxy_type = vp->lvalue;
+ }
+ rcode = module_pre_proxy(pre_proxy_type, request);
+ switch (rcode) {
+ case RLM_MODULE_FAIL:
+ case RLM_MODULE_INVALID:
+ case RLM_MODULE_NOTFOUND:
+ case RLM_MODULE_REJECT:
+ case RLM_MODULE_USERLOCK:
+ default:
+ /* FIXME: debug print failed stuff */
+ goto reject_request;
+
+ case RLM_MODULE_HANDLED:
+ return 0;
+
+ /*
+ * Only proxy the packet if the pre-proxy code succeeded.
+ */
+ case RLM_MODULE_NOOP:
+ case RLM_MODULE_OK:
+ case RLM_MODULE_UPDATED:
+ break;
+ }
+
+ /*
+ * If it's a fake request, don't send the proxy
+ * packet. The outer tunnel session will take
+ * care of doing that.
+ */
+ if (request->packet->dst_port == 0) {
+ return 1;
+ }
+
+ if (!insert_into_proxy_hash(request)) {
+ DEBUG("ERROR: Failed to proxy request %d", request->number);
+ goto reject_request;
+ }
+
+ request->proxy_listener->encode(request->proxy_listener, request);
+
+ when = request->received;
+ when.tv_sec += mainconfig.max_request_time;
+
+ gettimeofday(&request->proxy_when, NULL);
+
+ request->next_when = request->proxy_when;
+ request->next_when.tv_sec += home->response_window;
+
+ rad_assert(home->response_window > 0);
+
+ if (timercmp(&when, &request->next_when, <)) {
+ request->next_when = when;
+ }
+ request->next_callback = no_response_to_proxied_request;
+
+ DEBUG2("Proxying request %d to realm %s, home server %s port %d",
+ request->number, realmname,
+ inet_ntop(request->proxy->dst_ipaddr.af,
+ &request->proxy->dst_ipaddr.ipaddr,
+ buffer, sizeof(buffer)),
+ request->proxy->dst_port);
+
+ /*
+ * Note that we set proxied AFTER creating the packet,
+ * but BEFORE actually sending it.
+ *
+ * Once we send it, the request is tainted, as
+ * another thread may have picked it up. Don't
+ * touch it!
+ */
+ request->num_proxied_requests = 1;
+ request->num_proxied_responses = 0;
+ request->child_state = REQUEST_PROXIED;
+ request->proxy_listener->send(request->proxy_listener,
+ request);
+ return 1;
+}
+
+
+static void request_post_handler(REQUEST *request)
+{
+ int child_state = -1;
+ struct timeval when;
+ VALUE_PAIR *vp;
+
+ if (request->master_state == REQUEST_STOP_PROCESSING) {
+ DEBUG2("Request %d was cancelled.", request->number);
+ request->child_state = REQUEST_DONE;
+ return;
+ }
+
+ if (request->child_state != REQUEST_RUNNING) {
+ rad_panic("Internal sanity check failed");
+ }
+
+ /*
+ * FIXME: check for Auth-Type = Reject, and reject it if so?
+ * this means don't proxy it.
+ *
+ * FIXME: check if we can't proxy it because all of the
+ * home servers are dead or busy, and set post-auth-type
+ * "proxy fail", or something like that.
+ */
+
+ if (mainconfig.proxy_requests &&
+ (request->packet->code != PW_STATUS_SERVER) &&
+ (request->reply->code == 0) &&
+ !request->proxy &&
+ successfully_proxied_request(request)) {
+ return;
+ }
+
+ /*
+ * Fake requests don't get encoded or signed. The caller
+ * also requires the reply VP's, so we don't free them
+ * here!
+ */
+ if (request->packet->dst_port == 0) {
+ /* FIXME: DEBUG going to the next request */
+ request->child_state = REQUEST_DONE;
+ return;
+ }
+
+ /*
+ * Copy Proxy-State from the request to the reply.
+ */
+ vp = paircopy2(request->packet->vps, PW_PROXY_STATE);
+ if (vp) pairadd(&request->reply->vps, vp);
+
+ /*
+ * Access-Requests get delayed or cached.
+ */
+ if (request->packet->code == PW_AUTHENTICATION_REQUEST) {
+ gettimeofday(&request->next_when, NULL);
+
+ if (request->reply->code == 0) {
+ DEBUG2("There was no response configured: rejecting request %d",
+ request->number);
+ request->reply->code = PW_AUTHENTICATION_REJECT;
+ }
+
+ /*
+ * If configured, delay Access-Reject packets.
+ */
+ if ((request->reply->code == PW_AUTHENTICATION_REJECT) &&
+ (mainconfig.reject_delay > 0)) {
+ when = request->received;
+ when.tv_sec += mainconfig.reject_delay;
+
+ if (timercmp(&when, &request->next_when, >)) {
+ DEBUG2("Delaying reject of request %d for %d seconds",
+ request->number,
+ mainconfig.reject_delay);
+ request->next_when = when;
+ request->next_callback = reject_delay;
+ request->child_state = REQUEST_REJECT_DELAY;
+ return;
+ }
+ }
+
+ request->next_when.tv_sec += mainconfig.cleanup_delay;
+ request->next_callback = cleanup_delay;
+ child_state = REQUEST_CLEANUP_DELAY;
+
+ } else if (request->packet->code == PW_ACCOUNTING_REQUEST) {
+ request->next_callback = NULL; /* just to be safe */
+ child_state = REQUEST_DONE;
+
+ /*
+ * FIXME: Status-Server should probably not be
+ * handled here...
+ */
+ } else if (request->packet->code == PW_STATUS_SERVER) {
+ request->next_callback = NULL;
+ child_state = REQUEST_DONE;
+
+ } else {
+ rad_panic("Unknown packet type");
+ }
+
+ /*
+ * Encode, sign, and send. The accounting request
+ * handler takes care of suppressing responses when
+ * request->reply->code == 0.
+ */
+ request->listener->send(request->listener, request);
+
+ /*
+ * Clean up. These are no longer needed.
+ */
+ pairfree(&request->config_items);
+
+ pairfree(&request->packet->vps);
+ request->username = NULL;
+ request->password = NULL;
+
+ pairfree(&request->reply->vps);
+
+ if (request->proxy) {
+ pairfree(&request->proxy->vps);
+ }
+ if (request->proxy_reply) {
+ pairfree(&request->proxy_reply->vps);
+ }
+
+ DEBUG2("Finished request %d state %d", request->number, child_state);
+
+ request->child_state = child_state;
+}
+
+
+static void received_retransmit(REQUEST *request, const RADCLIENT *client)
+{
+ char buffer[128];
+
+ switch (request->child_state) {
+ case REQUEST_QUEUED:
+ case REQUEST_RUNNING:
+ radlog(L_ERR, "Discarding duplicate request from "
+ "client %s port %d - ID: %d due to unfinished request %d",
+ client->shortname,
+ request->packet->src_port,request->packet->id,
+ request->number);
+ break;
+
+ case REQUEST_PROXIED:
+ DEBUG2("Sending duplicate proxied request to home server %s port %d - ID: %d",
+ inet_ntop(request->proxy->dst_ipaddr.af,
+ &request->proxy->dst_ipaddr.ipaddr,
+ buffer, sizeof(buffer)),
+ request->proxy->dst_port,
+ request->proxy->id);
+ request->num_proxied_requests++;
+ request->proxy_listener->send(request->proxy_listener,
+ request);
+ break;
+
+ case REQUEST_REJECT_DELAY:
+ DEBUG2("Waiting to send Access-Reject "
+ "to client %s port %d - ID: %d",
+ client->shortname,
+ request->packet->src_port, request->packet->id);
+ break;
+
+ case REQUEST_CLEANUP_DELAY:
+ case REQUEST_DONE:
+ DEBUG2("Sending duplicate reply "
+ "to client %s port %d - ID: %d",
+ client->shortname,
+ request->packet->src_port, request->packet->id);
+ request->listener->send(request->listener, request);
+ break;
+ }
+}
+
+
+static void received_conflicting_request(REQUEST *request,
+ const RADCLIENT *client)
+{
+ radlog(L_ERR, "Received conflicting packet from "
+ "client %s port %d - ID: %d due to unfinished request %d. Giving up on old request.",
+ client->shortname,
+ request->packet->src_port, request->packet->id,
+ request->number);
+
+ switch (request->child_state) {
+ case REQUEST_QUEUED:
+ case REQUEST_RUNNING:
+ request->master_state = REQUEST_STOP_PROCESSING;
+ request->delay += request->delay >> 1;
+
+ tv_add(&request->when, request->delay);
+
+ if (!lrad_event_insert(el,
+ wait_for_child_to_die,
+ request, &request->when)) {
+ rad_panic("Failed to insert event");
+ }
+ return;
+
+ default:
+ break;
+ }
+
+ remove_from_request_hash(request);
+
+ /*
+ * The request stays in the event queue. At some point,
+ * the child will notice, and we can then delete it.
+ */
+}
+
+
+static int can_handle_new_request(RADIUS_PACKET *packet,
+ const RADCLIENT *client)
+{
+ /*
+ * Count the total number of requests, to see if
+ * there are too many. If so, return with an
+ * error.
+ */
+ if (mainconfig.max_requests) {
+ int request_count = lrad_packet_list_num_elements(pl);
+
+ /*
+ * This is a new request. Let's see if
+ * it makes us go over our configured
+ * bounds.
+ */
+ if (request_count > mainconfig.max_requests) {
+ radlog(L_ERR, "Dropping request (%d is too many): "
+ "from client %s port %d - ID: %d", request_count,
+ client->shortname,
+ packet->src_port, packet->id);
+ radlog(L_INFO, "WARNING: Please check the %s file.\n"
+ "\tThe value for 'max_requests' is probably set too low.\n", mainconfig.radiusd_conf);
+ return 0;
+ } /* else there were a small number of requests */
+ } /* else there was no configured limit for requests */
+
+ /*
+ * FIXME: Add per-client checks. If one client is sending
+ * too many packets, start discarding them.
+ *
+ * We increment the counters here, and decrement them
+ * when the response is sent... somewhere in this file.
+ */
+
+ /*
+ * FUTURE: Add checks for system load. If the system is
+ * busy, start dropping requests...
+ *
+ * We can probably keep some statistics ourselves... if
+ * there are more requests coming in than we can handle,
+ * start dropping some.
+ */
+
+ return 1;
+}
+
+
+int received_request(rad_listen_t *listener,
+ RADIUS_PACKET *packet, REQUEST **prequest,
+ const RADCLIENT *client)
+{
+ RADIUS_PACKET **packet_p;
+ REQUEST *request = NULL;
+
+ packet_p = lrad_packet_list_find(pl, packet);
+ if (packet_p) {
+ request = lrad_packet2myptr(REQUEST, packet, packet_p);
+
+ if (memcmp(request->packet->vector, packet->vector,
+ sizeof(packet->vector)) == 0) {
+ received_retransmit(request, client);
+ return 0;
+ }
+ received_conflicting_request(request, client);
+ request = NULL;
+
+ } else if (!can_handle_new_request(packet, client)) {
+ return 0;
+ }
+
+ /*
+ * Create and initialize the new request.
+ */
+ request = request_alloc(); /* never fails */
+
+ if ((request->reply = rad_alloc(0)) == NULL) {
+ radlog(L_ERR, "No memory");
+ exit(1);
+ }
+
+ request->listener = listener;
+ request->packet = packet;
+ request->packet->timestamp = request->timestamp;
+ request->number = request_num_counter++;
+ strlcpy(request->secret, client->secret, sizeof(request->secret));
+
+ /*
+ * Remember the request in the list.
+ */
+ if (!lrad_packet_list_insert(pl, &request->packet)) {
+ radlog(L_ERR, "Failed to insert request %d in the list of live requests: discarding", request->number);
+ request_free(&request);
+ return 0;
+ }
+ request->in_request_hash = TRUE;
+
+ /*
+ * The request passes many of our sanity checks.
+ * From here on in, if anything goes wrong, we
+ * send a reject message, instead of dropping the
+ * packet.
+ */
+
+ /*
+ * Build the reply template from the request.
+ */
+
+ request->reply->sockfd = request->packet->sockfd;
+ request->reply->dst_ipaddr = request->packet->src_ipaddr;
+ request->reply->src_ipaddr = request->packet->dst_ipaddr;
+ request->reply->dst_port = request->packet->src_port;
+ request->reply->src_port = request->packet->dst_port;
+ request->reply->id = request->packet->id;
+ request->reply->code = 0; /* UNKNOWN code */
+ memcpy(request->reply->vector, request->packet->vector,
+ sizeof(request->reply->vector));
+ request->reply->vps = NULL;
+ request->reply->data = NULL;
+ request->reply->data_len = 0;
+
+ request->master_state = REQUEST_ACTIVE;
+ request->child_state = REQUEST_QUEUED;
+ request->next_callback = NULL;
+
+ gettimeofday(&request->received, NULL);
+ request->when = request->received;
+ request->delay = USEC / 10;
+
+ tv_add(&request->when, request->delay);
+
+ if (!lrad_event_insert(el, wait_a_bit,
+ request, &request->when)) {
+ rad_panic("Failed to insert event");
+ }
+
+ *prequest = request;
+ return 1;
+}
+
+
+REQUEST *received_proxy_response(RADIUS_PACKET *packet)
+{
+ char buffer[128];
+ home_server *home;
+ REQUEST *request;
+
+ if (!home_server_find(&packet->src_ipaddr, packet->src_port)) {
+ radlog(L_ERR, "Ignoring request from unknown home server %s port %d",
+ inet_ntop(packet->src_ipaddr.af,
+ &packet->src_ipaddr.ipaddr,
+ buffer, sizeof(buffer)),
+ packet->src_port);
+ rad_free(&packet);
+ return NULL;
+ }
+
+ /*
+ * Also removes from the proxy hash if responses == requests
+ */
+ request = lookup_in_proxy_hash(packet);
+
+ if (!request) {
+ radlog(L_PROXY, "No outstanding request was found for proxy reply from home server %s port %d - ID %d",
+ inet_ntop(packet->src_ipaddr.af,
+ &packet->src_ipaddr.ipaddr,
+ buffer, sizeof(buffer)),
+ packet->src_port, packet->id);
+ rad_free(&packet);
+ return NULL;
+ }
+
+ home = request->home_server;
+
+ gettimeofday(&now, NULL);
+ home->state = HOME_STATE_ALIVE;
+
+ if (request->reply && request->reply->code != 0) {
+ DEBUG2("We already replied to this request. Discarding response from home server.");
+ rad_free(&packet);
+ return NULL;
+ }
+
+ /*
+ * We had previously received a reply, so we don't need
+ * to do anything here.
+ */
+ if (request->proxy_reply) {
+ if (memcmp(request->proxy_reply->vector,
+ packet->vector,
+ sizeof(request->proxy_reply->vector)) == 0) {
+ DEBUG2("Discarding duplicate reply from home server %s port %d - ID: %d for request %d",
+ inet_ntop(packet->src_ipaddr.af,
+ &packet->src_ipaddr.ipaddr,
+ buffer, sizeof(buffer)),
+ packet->src_port, packet->id,
+ request->number);
+ } else {
+ /*
+ * ? The home server gave us a new proxy
+ * reply, which doesn't match the old
+ * one. Delete it.
+ */
+ DEBUG2("Ignoring conflicting proxy reply");
+ }
+
+ /* assert that there's an event queued for request? */
+ rad_free(&packet);
+ return NULL;
+ }
+
+ switch (request->child_state) {
+ case REQUEST_QUEUED:
+ case REQUEST_RUNNING:
+ rad_panic("Internal sanity check failed for child state");
+ break;
+
+ case REQUEST_REJECT_DELAY:
+ case REQUEST_CLEANUP_DELAY:
+ case REQUEST_DONE:
+ radlog(L_ERR, "Reply from home server %s port %d - ID: %d arrived too late for request %d. Try increasing 'retry_delay' or 'max_request_time'",
+ inet_ntop(packet->src_ipaddr.af,
+ &packet->src_ipaddr.ipaddr,
+ buffer, sizeof(buffer)),
+ packet->src_port, packet->id,
+ request->number);
+ /* assert that there's an event queued for request? */
+ rad_free(&packet);
+ return NULL;
+
+ case REQUEST_PROXIED:
+ break;
+ }
+
+ request->proxy_reply = packet;
+
+ /*
+ * There's no incoming request, so it's a proxied packet
+ * we originated.
+ */
+ if (!request->packet) {
+ received_response_to_ping(request);
+ return NULL;
+ }
+
+ request->child_state = REQUEST_QUEUED;
+ request->when = now;
+ request->delay = USEC / 10;
+ tv_add(&request->when, request->delay);
+
+ /*
+ * Wait a bit will take care of max_request_time
+ */
+ if (!lrad_event_insert(el, wait_a_bit,
+ request, &request->when)) {
+ rad_panic("Failed to insert event");
+ }
+
+ return request;
+}
+
+
+/*
+ * Externally-visibly functions.
+ */
+int radius_event_init(int spawn_flag)
+{
+ if (el) return 0;
+
+ time(&start_time);
+
+ el = lrad_event_list_create();
+ if (!el) return 0;
+
+ pl = lrad_packet_list_create(0);
+ if (!el) return 0;
+
+ request_num_counter = 0;
+
+ /*
+ * Move all of the thread calls to this file?
+ *
+ * It may be best for the mutexes to be in this file...
+ */
+ have_children = spawn_flag;
+
+ if (mainconfig.proxy_requests) {
+ int i;
+ rad_listen_t *listener;
+
+ /*
+ * Create the tree for managing proxied requests and
+ * responses.
+ */
+ proxy_list = lrad_packet_list_create(1);
+ if (!proxy_list) return 0;
+
+#ifdef HAVE_PTHREAD_H
+ if (pthread_mutex_init(&proxy_mutex, NULL) != 0) {
+ radlog(L_ERR, "FATAL: Failed to initialize proxy mutex: %s",
+ strerror(errno));
+ exit(1);
+ }
+#endif
+
+ /*
+ * Mark the Fd's as unused.
+ */
+ for (i = 0; i < 32; i++) proxy_fds[i] = -1;
+
+ i = -1;
+
+ for (listener = mainconfig.listen;
+ listener != NULL;
+ listener = listener->next) {
+ if (listener->type == RAD_LISTEN_PROXY) {
+ /*
+ * FIXME: This works only because we
+ * start off with one proxy socket.
+ */
+ rad_assert(proxy_fds[listener->fd & 0x1f] == -1);
+ rad_assert(proxy_listeners[listener->fd & 0x1f] == NULL);
+
+ proxy_fds[listener->fd & 0x1f] = listener->fd;
+ proxy_listeners[listener->fd & 0x1f] = listener;
+ if (!lrad_packet_list_socket_add(proxy_list, listener->fd)) {
+ rad_assert(0 == 1);
+ }
+ i = listener->fd;
+ }
+ }
+
+ if (mainconfig.proxy_requests) rad_assert(i >= 0);
+ }
+
+ thread_pool_init(spawn_flag);
+
+ return 1;
+}
+
+
+static int request_hash_cb(void *ctx, void *data)
+{
+ ctx = ctx; /* -Wunused */
+ REQUEST *request = lrad_packet2myptr(REQUEST, packet, data);
+
+ rad_assert(request->in_proxy_hash == FALSE);
+
+ lrad_event_delete(el, request);
+ remove_from_request_hash(request);
+ request_free(&request);
+
+ return 0;
+}
+
+
+static int proxy_hash_cb(void *ctx, void *data)
+{
+ ctx = ctx; /* -Wunused */
+ REQUEST *request = lrad_packet2myptr(REQUEST, proxy, data);
+
+ lrad_packet_list_yank(proxy_list, request->proxy);
+ request->in_proxy_hash = FALSE;
+
+ if (!request->in_request_hash) {
+ lrad_event_delete(el, request);
+ request_free(&request);
+ }
+
+ return 0;
+}
+
+
+void radius_event_free(void)
+{
+ /*
+ * FIXME: Stop all threads, or at least check that
+ * they're all waiting on the semaphore, and the queues
+ * are empty.
+ */
+
+ /*
+ * There are requests in the proxy hash that aren't
+ * referenced from anywhere else. Remove them first.
+ */
+ if (proxy_list) {
+ PTHREAD_MUTEX_LOCK(&proxy_mutex);
+ lrad_packet_list_walk(proxy_list, NULL, proxy_hash_cb);
+ PTHREAD_MUTEX_UNLOCK(&proxy_mutex);
+ lrad_packet_list_free(proxy_list);
+ proxy_list = NULL;
+ }
+
+ lrad_packet_list_walk(pl, NULL, request_hash_cb);
+
+ lrad_packet_list_free(pl);
+ pl = NULL;
+
+ lrad_event_list_free(el);
+}
+
+int radius_event_process(struct timeval **pptv)
+{
+ int rcode;
+ struct timeval when;
+
+ if (!el) return 0;
+
+ if (lrad_event_list_num_elements(el) == 0) {
+ *pptv = NULL;
+ return 1;
+ }
+
+ gettimeofday(&now, NULL);
+ when = now;
+
+ do {
+ rcode = lrad_event_run(el, &when);
+ } while (rcode == 1);
+
+ gettimeofday(&now, NULL);
+
+ if ((when.tv_sec == 0) && (when.tv_usec == 0)) {
+ if (lrad_event_list_num_elements(el) == 0) {
+ *pptv = NULL;
+ return 1;
+ }
+ rad_panic("Internal sanity check failed");
+
+ } else if (timercmp(&now, &when, >)) {
+ DEBUG2("WTF?");
+ when.tv_sec = 0;
+ when.tv_usec = 1;
+
+ } else {
+ when.tv_sec -= now.tv_sec;
+ when.tv_usec -= now.tv_usec;
+ if (when.tv_usec < 0) {
+ when.tv_sec--;
+ when.tv_usec += USEC;
+ }
+ }
+ **pptv = when;
+
+ return 1;
+}
+
+void radius_handle_request(REQUEST *request, RAD_REQUEST_FUNP fun)
+{
+ if (!request_pre_handler(request)) {
+ DEBUG2("Going to the next request at X");
+ return;
+ }
+
+ rad_assert(fun != NULL);
+ rad_assert(request != NULL);
+
+ fun(request);
+
+ request_post_handler(request);
+ DEBUG2("Going to the next request");
+ return;
+}
+
}
}
#endif
-
-#ifndef BUILDDBM /* HACK HACK */
-
-/*
- * Free a REALM list.
- */
-void realm_free(REALM *cl)
-{
- REALM *next;
-
- while(cl) {
- next = cl->next;
- free(cl);
- cl = next;
- }
-}
-
-/*
- * Read the realms file.
- */
-int read_realms_file(const char *file)
-{
- FILE *fp;
- char buffer[256];
- char realm[256];
- char hostnm[256];
- char opts[256];
- char *s, *p;
- int lineno = 0;
- REALM *c, **tail;
- int got_realm = FALSE;
-
- realm_free(mainconfig.realms);
- mainconfig.realms = NULL;
- tail = &mainconfig.realms;
-
- if ((fp = fopen(file, "r")) == NULL) {
- /* The realms file is not mandatory. If it exists it will
- be used, however, since the new style config files are
- more robust and flexible they are more likely to get used.
- So this is a non-fatal error. */
- return 0;
- }
-
- while(fgets(buffer, 256, fp) != NULL) {
- lineno++;
- if (!feof(fp) && (strchr(buffer, '\n') == NULL)) {
- radlog(L_ERR, "%s[%d]: line too long", file, lineno);
- return -1;
- }
- if (buffer[0] == '#' || buffer[0] == '\n')
- continue;
- p = buffer;
- if (!getword(&p, realm, sizeof(realm)) ||
- !getword(&p, hostnm, sizeof(hostnm))) {
- radlog(L_ERR, "%s[%d]: syntax error", file, lineno);
- continue;
- }
-
- got_realm = TRUE;
- c = rad_malloc(sizeof(REALM));
- memset(c, 0, sizeof(REALM));
-
- if ((s = strchr(hostnm, ':')) != NULL) {
- *s++ = 0;
- c->auth_port = atoi(s);
- c->acct_port = c->auth_port + 1;
- } else {
- c->auth_port = PW_AUTH_UDP_PORT;
- c->acct_port = PW_ACCT_UDP_PORT;
- }
-
- if (strcmp(hostnm, "LOCAL") == 0) {
- /*
- * Local realms don't have an IP address,
- * secret, or port.
- */
- c->ipaddr.af = c->acct_ipaddr.af = AF_INET;
- c->ipaddr.ipaddr.ip4addr.s_addr = c->acct_ipaddr.ipaddr.ip4addr.s_addr = htonl(INADDR_NONE);
- c->secret[0] = '\0';
- c->auth_port = 0;
- c->acct_port = 0;
-
- } else {
- RADCLIENT *client;
-
- if (ip_hton(hostnm, AF_INET, &c->ipaddr) < 0) {
- radlog(L_CONS|L_ERR, "%s[%d]: Failed to look up hostname %s",
- file, lineno, hostnm);
- return -1;
- }
- c->acct_ipaddr = c->ipaddr;
-
- /*
- * Find the remote server in the "clients" list.
- * If we can't find it, there's a big problem...
- */
- client = client_find_old(&c->ipaddr);
- if (client == NULL) {
- radlog(L_CONS|L_ERR, "%s[%d]: Cannot find 'clients' file entry of remote server %s for realm \"%s\"",
- file, lineno, hostnm, realm);
- return -1;
- }
- memcpy(c->secret, client->secret, sizeof(c->secret));
- }
-
- /*
- * Double-check lengths to be sure they're sane
- */
- if (strlen(hostnm) >= sizeof(c->server)) {
- radlog(L_ERR, "%s[%d]: server name of length %u is greater than the allowed maximum of %u.",
- file, lineno,
- strlen(hostnm),
- sizeof(c->server) - 1);
- return -1;
- }
- if (strlen(realm) > sizeof(c->realm)) {
- radlog(L_ERR, "%s[%d]: realm of length %u is greater than the allowed maximum of %u.",
- file, lineno,
- strlen(realm),
- sizeof(c->realm) - 1);
- return -1;
- }
-
- /*
- * OK, they're sane, copy them over.
- */
- strcpy(c->realm, realm);
- strcpy(c->server, hostnm);
- c->striprealm = TRUE;
- c->active = TRUE;
- c->acct_active = TRUE;
-
- while (getword(&p, opts, sizeof(opts))) {
- if (strcmp(opts, "nostrip") == 0)
- c->striprealm = FALSE;
- if (strstr(opts, "noacct") != NULL)
- c->acct_port = 0;
- if (strstr(opts, "trusted") != NULL)
- c->trusted = 1;
- if (strstr(opts, "notrealm") != NULL)
- c->notrealm = 1;
- if (strstr(opts, "notsuffix") != NULL)
- c->notrealm = 1;
- }
-
- c->next = NULL;
- *tail = c;
- tail = &c->next;
- }
- fclose(fp);
-
- /*
- * Complain only if the realms file has content.
- */
- if (got_realm) {
- radlog(L_INFO, "Using deprecated realms file. Support for this will go away soon.");
- }
-
- return 0;
-}
-#endif /* BUILDDBM */
-
-/*
- * Mark a host inactive
- */
-void realm_disable(REQUEST *request)
-{
- REALM *cl;
- time_t now;
- lrad_ipaddr_t *ipaddr;
- int port;
-
- now = time(NULL);
-
- if (!request || !request->proxy ||
- (request->proxy->dst_ipaddr.af != AF_INET)) return;
-
- ipaddr = &request->proxy->dst_ipaddr;
- port = request->proxy->dst_port;
-
- for (cl = mainconfig.realms; cl; cl = cl->next) {
- if (ipaddr->af != cl->ipaddr.af) continue;
-
- /*
- * FIXME: Switch over AF
- */
- if ((ipaddr->ipaddr.ip4addr.s_addr == cl->ipaddr.ipaddr.ip4addr.s_addr) &&
- (port == cl->auth_port)) {
- /*
- * If we've received a reply (any reply)
- * from the home server in the time spent
- * re-sending this request, then don't mark
- * the realm as dead.
- */
- if (cl->last_reply > (( now - mainconfig.proxy_retry_delay * mainconfig.proxy_retry_count ))) {
- continue;
- }
-
- cl->active = FALSE;
- cl->wakeup = now + mainconfig.proxy_dead_time;
- radlog(L_PROXY, "marking authentication server %s:%d for realm %s dead",
- cl->server, port, cl->realm);
- } else if ((ipaddr->ipaddr.ip4addr.s_addr == cl->acct_ipaddr.ipaddr.ip4addr.s_addr) &&
- (port == cl->acct_port)) {
- if (cl->last_reply > (( now - mainconfig.proxy_retry_delay * mainconfig.proxy_retry_count ))) {
- continue;
- }
-
- cl->acct_active = FALSE;
- cl->acct_wakeup = now + mainconfig.proxy_dead_time;
- radlog(L_PROXY, "marking accounting server %s:%d for realm %s dead",
- cl->acct_server, port, cl->realm);
- }
- }
-}
-
-/*
- * Find a realm in the REALM list.
- */
-REALM *realm_find(const char *realm, int accounting)
-{
- REALM *cl;
- REALM *default_realm = NULL;
- time_t now;
- int dead_match = 0;
-
- now = time(NULL);
-
- /*
- * If we're passed a NULL realm pointer,
- * then look for a "NULL" realm string.
- */
- if (realm == NULL) {
- realm = "NULL";
- }
-
- for (cl = mainconfig.realms; cl; cl = cl->next) {
- /*
- * Wake up any sleeping realm.
- */
- if (cl->wakeup <= now) {
- cl->active = TRUE;
- }
- if (cl->acct_wakeup <= now) {
- cl->acct_active = TRUE;
- }
-
- /*
- * Asked for auth/acct, and the auth/acct server
- * is not active. Skip it.
- */
- if ((!accounting && !cl->active) ||
- (accounting && !cl->acct_active)) {
-
- /*
- * We've been asked to NOT fall through
- * to the DEFAULT realm if there are
- * exact matches for this realm which are
- * dead.
- */
- if ((!mainconfig.proxy_fallback) &&
- (strcasecmp(cl->realm, realm) == 0)) {
- dead_match = 1;
- }
- continue;
- }
-
- /*
- * If it matches exactly, return it.
- *
- * Note that we just want ONE live realm
- * here. We don't care about round-robin, or
- * scatter techniques, as that's more properly
- * the responsibility of the proxying code.
- */
- if (strcasecmp(cl->realm, realm) == 0) {
- return cl;
- }
-
- /*
- * No default realm, try to set one.
- */
- if ((default_realm == NULL) &&
- (strcmp(cl->realm, "DEFAULT") == 0)) {
- default_realm = cl;
- }
- } /* loop over all realms */
-
- /*
- * There WAS one or more matches which were marked dead,
- * AND there were NO live matches, AND we've been asked
- * to NOT fall through to the DEFAULT realm. Therefore,
- * we return NULL, which means "no match found".
- */
- if (!mainconfig.proxy_fallback && dead_match) {
- if (mainconfig.wake_all_if_all_dead) {
- REALM *rcl = NULL;
- for (cl = mainconfig.realms; cl; cl = cl->next) {
- if(strcasecmp(cl->realm,realm) == 0) {
- if (!accounting && !cl->active) {
- cl->active = TRUE;
- rcl = cl;
- }
- else if (accounting &&
- !cl->acct_active) {
- cl->acct_active = TRUE;
- rcl = cl;
- }
- }
- }
- return rcl;
- }
- else {
- return NULL;
- }
- }
-
- /* If we didn't find the realm 'NULL' don't return the
- * DEFAULT entry.
- */
- if ((strcmp(realm, "NULL")) == 0) {
- return NULL;
- }
-
- /*
- * Didn't find anything that matched exactly, return the
- * DEFAULT realm. We also return the DEFAULT realm if
- * all matching realms were marked dead, and we were
- * asked to fall through to the DEFAULT realm in this
- * case.
- */
- return default_realm;
-}
-
-/*
- * Find a realm for a proxy reply by proxy's IP
- *
- * Note that we don't do anything else.
- */
-REALM *realm_findbyaddr(uint32_t ipaddr, int port)
-{
- REALM *cl;
-
- /*
- * Note that we do NOT check for inactive realms!
- *
- * The purpose of this code is simply to find a matching
- * source IP/Port pair, for a home server which is allowed
- * to send us proxy replies. If we get a reply, then it
- * doesn't matter if we think the realm is inactive.
- */
- for (cl = mainconfig.realms; cl != NULL; cl = cl->next) {
- if (cl->ipaddr.af != AF_INET) rad_assert(0 == 1);
-
- if ((ipaddr == cl->ipaddr.ipaddr.ip4addr.s_addr) &&
- (port == cl->auth_port)) {
- return cl;
-
- } else if ((ipaddr == cl->acct_ipaddr.ipaddr.ip4addr.s_addr) &&
- (port == cl->acct_port)) {
- return cl;
- }
- }
-
- return NULL;
-}
-
#include <freeradius-devel/rad_assert.h>
#include <freeradius-devel/radius_snmp.h>
-#include <freeradius-devel/request_list.h>
static time_t start_time = 0;
rad_listen_free_t free;
rad_listen_recv_t recv;
rad_listen_send_t send;
- rad_listen_update_t update;
rad_listen_print_t print;
+ rad_listen_encode_t encode;
+ rad_listen_decode_t decode;
} rad_listen_master_t;
typedef struct listen_socket_t {
return 0;
}
-static int request_num_counter = 0;
-
-/*
- * Check for dups, etc. Common to Access-Request &&
- * Accounting-Request packets.
- */
-static int common_checks(rad_listen_t *listener,
- RADIUS_PACKET *packet, REQUEST **prequest,
- const RADCLIENT *client)
-{
- REQUEST *curreq;
- char buffer[128];
-
- rad_assert(listener->rl != NULL);
-
- /*
- * If there is no existing request of id, code, etc.,
- * then we can return, and let it be processed.
- */
- if ((curreq = rl_find(listener->rl, packet)) == NULL) {
- /*
- * Count the total number of requests, to see if
- * there are too many. If so, return with an
- * error.
- */
- if (mainconfig.max_requests) {
- /*
- * FIXME: This is now per-socket,
- * when it should really be global
- * to the server!
- */
- int request_count = rl_num_requests(listener->rl);
-
- /*
- * This is a new request. Let's see if
- * it makes us go over our configured
- * bounds.
- */
- if (request_count > mainconfig.max_requests) {
- radlog(L_ERR, "Dropping request (%d is too many): "
- "from client %s port %d - ID: %d", request_count,
- client->shortname,
- packet->src_port, packet->id);
- radlog(L_INFO, "WARNING: Please check the %s file.\n"
- "\tThe value for 'max_requests' is probably set too low.\n", mainconfig.radiusd_conf);
- return 0;
- } /* else there were a small number of requests */
- } /* else there was no configured limit for requests */
-
- /*
- * FIXME: Add checks for system load. If the
- * system is busy, start dropping requests...
- *
- * We can probably keep some statistics
- * ourselves... if there are more requests
- * coming in than we can handle, start dropping
- * some.
- */
-
- /*
- * The current request isn't finished, which
- * means that the NAS sent us a new packet, while
- * we are still processing the old request.
- */
- } else if (!curreq->finished) {
- /*
- * If the authentication vectors are identical,
- * then the NAS is re-transmitting it, trying to
- * kick us into responding to the request.
- */
- if (memcmp(curreq->packet->vector, packet->vector,
- sizeof(packet->vector)) == 0) {
- RAD_SNMP_INC(rad_snmp.auth.total_dup_requests);
-
- /*
- * It's not finished because the request
- * was proxied, but there was no reply
- * from the home server.
- *
- * This code will never get hit for
- * accounting packets, as they're always
- * updated, and never re-transmitted.
- */
- if (curreq->proxy && !curreq->proxy_reply) {
- DEBUG2("Sending duplicate proxied request to home server %s port %d - ID: %d",
- inet_ntop(curreq->proxy->dst_ipaddr.af,
- &curreq->proxy->dst_ipaddr.ipaddr,
- buffer, sizeof(buffer)), curreq->proxy->dst_port,
-
- curreq->proxy->id);
- curreq->proxy_listener->send(curreq->proxy_listener, curreq);
- return 0;
- } /* else the packet was not proxied */
-
- /*
- * Someone's still working on it, so we
- * ignore the duplicate request.
- */
- radlog(L_ERR, "Discarding duplicate request from "
- "client %s port %d - ID: %d due to unfinished request %d",
- client->shortname,
- packet->src_port, packet->id,
- curreq->number);
- return 0;
- } /* else the authentication vectors were different */
-
- /*
- * We're waiting for a proxy reply, but no one is
- * currently processing the request. We can
- * discard the old request, and ignore any reply
- * from the home server, because the NAS will
- * never care...
- */
- if (curreq->proxy && !curreq->proxy_reply &&
- (curreq->child_pid == NO_SUCH_CHILD_PID)) {
- radlog(L_ERR, "Discarding old proxied request %d from "
- "client %s port %d - ID: %d due to new request from the client",
- curreq->number, client->shortname,
- packet->src_port, packet->id);
- } else {
- /*
- * The authentication vectors are different, so
- * the NAS has given up on us, as we've taken too
- * long to process the request. This is a
- * SERIOUS problem!
- */
- RAD_SNMP_TYPE_INC(listener, total_packets_dropped);
-
- radlog(L_ERR, "Dropping conflicting packet from "
- "client %s port %d - ID: %d due to unfinished request %d",
- client->shortname,
- packet->src_port, packet->id,
- curreq->number);
- return 0;
- }
-
- /*
- * The old request is finished. We now check the
- * authentication vectors. If the client has sent us a
- * request with identical code && ID, but different
- * vector, then they MUST have gotten our response, so we
- * can delete the original request, and process the new
- * one.
- *
- * If the vectors are the same, then it's a duplicate
- * request, and we can send a duplicate reply.
- */
- } else if (memcmp(curreq->packet->vector, packet->vector,
- sizeof(packet->vector)) == 0) {
- RAD_SNMP_INC(rad_snmp.auth.total_dup_requests);
-
- /*
- * If the packet has been delayed, then silently
- * send a response, and clear the delayed flag.
- *
- * Note that this means if the NAS kicks us while
- * we're delaying a reject, then the reject may
- * be sent sooner than otherwise.
- *
- * This COULD be construed as a bug. Maybe what
- * we want to do is to ignore the duplicate
- * packet, and send the reject later.
- */
- if (curreq->options & RAD_REQUEST_OPTION_DELAYED_REJECT) {
- curreq->options &= ~RAD_REQUEST_OPTION_DELAYED_REJECT;
- rad_assert(curreq->listener == listener);
- listener->send(listener, curreq);
- return 0;
- }
-
- /*
- * Maybe we've saved a reply packet. If so,
- * re-send it. Otherwise, just complain.
- */
- if (curreq->reply->code != 0) {
- DEBUG2("Sending duplicate reply "
- "to client %s port %d - ID: %d",
- client->shortname,
- packet->src_port, packet->id);
- rad_assert(curreq->listener == listener);
- listener->send(listener, curreq);
- return 0;
- }
-
- /*
- * Else we never sent a reply to the NAS,
- * as we decided somehow we didn't like the request.
- *
- * This shouldn't happen, in general...
- */
- DEBUG2("Discarding duplicate request from client %s port %d - ID: %d",
- client->shortname, packet->src_port, packet->id);
- return 0;
- } /* else the vectors were different, so we discard the old request. */
-
- /*
- * 'packet' has the same source IP, source port, code,
- * and Id as 'curreq', but a different authentication
- * vector. We can therefore delete 'curreq', as we were
- * only keeping it around to send out duplicate replies,
- * if the first reply got lost in the network.
- */
- if (curreq) {
- rl_yank(listener->rl, curreq);
- request_free(&curreq);
- }
-
- /*
- * A unique per-request counter.
- */
- curreq = request_alloc(); /* never fails */
-
- if ((curreq->reply = rad_alloc(0)) == NULL) {
- radlog(L_ERR, "No memory");
- exit(1);
- }
- curreq->listener = listener;
- curreq->packet = packet;
- curreq->packet->timestamp = curreq->timestamp;
- curreq->number = request_num_counter++;
- strlcpy(curreq->secret, client->secret, sizeof(curreq->secret));
-
- /*
- * Remember the request in the list.
- */
- if (!rl_add(listener->rl, curreq)) {
- radlog(L_ERR, "Failed to insert request %d in the list of live requests: discarding", curreq->number);
- request_free(&curreq);
- return 0;
- }
-
- /*
- * The request passes many of our sanity checks.
- * From here on in, if anything goes wrong, we
- * send a reject message, instead of dropping the
- * packet.
- */
-
- /*
- * Build the reply template from the request.
- */
-
- curreq->reply->sockfd = curreq->packet->sockfd;
- curreq->reply->dst_ipaddr = curreq->packet->src_ipaddr;
- curreq->reply->src_ipaddr = curreq->packet->dst_ipaddr;
- curreq->reply->dst_port = curreq->packet->src_port;
- curreq->reply->src_port = curreq->packet->dst_port;
- curreq->reply->id = curreq->packet->id;
- curreq->reply->code = 0; /* UNKNOWN code */
- memcpy(curreq->reply->vector, curreq->packet->vector,
- sizeof(curreq->reply->vector));
- curreq->reply->vps = NULL;
- curreq->reply->data = NULL;
- curreq->reply->data_len = 0;
-
- *prequest = curreq;
- return 1;
-}
-
-
static int socket_print(rad_listen_t *this, char *buffer, size_t bufsize)
{
size_t len;
rad_assert(request->listener == listener);
rad_assert(listener->send == auth_socket_send);
- /*
- * Ensure that the reply is sane
- */
- if (request->reply->code == 0) {
- DEBUG2("There was no response configured: rejecting request %d", request->number);
- request->reply->code = PW_AUTHENTICATION_REJECT;
- }
-
- /*
- * If we're delaying authentication rejects, then
- * mark the request as delayed, and do NOT send a
- * response right now.
- *
- * However, if it's already marked as delayed, then
- * send it now.
- */
- if ((request->reply->code == PW_AUTHENTICATION_REJECT) &&
- ((request->options & RAD_REQUEST_OPTION_DELAYED_REJECT) == 0) &&
- (mainconfig.reject_delay > 0) &&
- ((request->options & RAD_REQUEST_OPTION_FAKE_REQUEST) == 0)) {
- DEBUG2("Delaying request %d for %d seconds",
- request->number, mainconfig.reject_delay);
- request->options |= RAD_REQUEST_OPTION_DELAYED_REJECT;
- return 0;
- }
-
return rad_send(request->reply, request->packet, request->secret);
}
request->proxy->src_ipaddr = sock->ipaddr;
request->proxy->src_port = sock->port;
- return rad_send(request->proxy, request->packet, request->proxysecret);
+ return rad_send(request->proxy, request->packet,
+ request->home_server->secret);
}
break;
} /* switch over packet types */
- if (!common_checks(listener, packet, prequest, client)) {
+ if (!received_request(listener, packet, prequest, client)) {
rad_free(&packet);
return 0;
}
* FIXME: Accounting duplicates should be handled
* differently than authentication duplicates.
*/
- if (!common_checks(listener, packet, prequest, client)) {
+ if (!received_request(listener, packet, prequest, client)) {
rad_free(&packet);
return 0;
}
static int proxy_socket_recv(rad_listen_t *listener,
RAD_REQUEST_FUNP *pfun, REQUEST **prequest)
{
- REALM *cl;
- REQUEST *oldreq;
+ REQUEST *request;
RADIUS_PACKET *packet;
RAD_REQUEST_FUNP fun = NULL;
char buffer[128];
}
/*
- * Unsupported stuff
- */
- if (packet->src_ipaddr.af != AF_INET) {
- rad_assert("PROXY IPV6 NOT SUPPORTED" == NULL);
- }
-
- /*
- * FIXME: Add support for home servers!
- */
- if ((cl = realm_findbyaddr(packet->src_ipaddr.ipaddr.ip4addr.s_addr,
- packet->src_port)) == NULL) {
- radlog(L_ERR, "Ignoring request from unknown home server %s port %d",
- inet_ntop(packet->src_ipaddr.af,
- &packet->src_ipaddr.ipaddr,
- buffer, sizeof(buffer)),
- packet->src_port);
- rad_free(&packet);
- return 0;
- }
-
- /*
* FIXME: Client MIB updates?
*/
switch(packet->code) {
return 0;
}
- /*
- * Find the original request in the request list
- */
- oldreq = rl_find_proxy(packet);
-
- /*
- * If we haven't found the original request which was
- * sent, to get this reply. Complain, and discard this
- * request, as there's no way for us to send it to a NAS.
- */
- if (!oldreq) {
- radlog(L_PROXY, "No outstanding request was found for proxy reply from home server %s port %d - ID %d",
- inet_ntop(packet->src_ipaddr.af,
- &packet->src_ipaddr.ipaddr,
- buffer, sizeof(buffer)),
- packet->src_port, packet->id);
- rad_free(&packet);
+ request = received_proxy_response(packet);
+ if (!request) {
return 0;
}
- /*
- * The proxy reply has arrived too late, as the original
- * (old) request has timed out, been rejected, and marked
- * as finished. The client has already received a
- * response, so there is nothing that can be done. Delete
- * the tardy reply from the home server, and return nothing.
- */
- if ((oldreq->reply->code != 0) ||
- (oldreq->finished)) {
- radlog(L_ERR, "Reply from home server %s port %d - ID: %d arrived too late for request %d. Try increasing 'retry_delay' or 'max_request_time'",
- inet_ntop(packet->src_ipaddr.af,
- &packet->src_ipaddr.ipaddr,
- buffer, sizeof(buffer)),
- packet->src_port, packet->id,
- oldreq->number);
- rad_free(&packet);
- return 0;
- }
+ rad_assert(fun != NULL);
+ *pfun = fun;
+ *prequest = request;
- /*
- * If there is already a reply, maybe this one is a
- * duplicate?
- */
- if (oldreq->proxy_reply) {
- if (memcmp(oldreq->proxy_reply->vector,
- packet->vector,
- sizeof(oldreq->proxy_reply->vector)) == 0) {
- radlog(L_ERR, "Discarding duplicate reply from home server %s port %d - ID: %d for request %d",
- inet_ntop(packet->src_ipaddr.af,
- &packet->src_ipaddr.ipaddr,
- buffer, sizeof(buffer)),
- packet->src_port, packet->id,
- oldreq->number);
- } else {
- /*
- * ? The home server gave us a new proxy
- * reply, which doesn't match the old
- * one. Delete it.
- */
- DEBUG2("Ignoring conflicting proxy reply");
- }
+ return 1;
+}
- /*
- * We've already received a reply, so
- * we discard this one, as we don't want
- * to do duplicate work.
- */
- rad_free(&packet);
- return 0;
- } /* else there wasn't a proxy reply yet, so we can process it */
- /*
- * Refresh the old request, and update it with the proxy
- * reply.
- *
- * ? Can we delete the proxy request here? * Is there
- * any more need for it?
- *
- * FIXME: we probably shouldn't be updating the time
- * stamp here.
- */
- oldreq->timestamp = time_now;
- oldreq->proxy_reply = packet;
+static int client_socket_encode(rad_listen_t *listener, REQUEST *request)
+{
+ if (!request->reply->code) return 0;
- /*
- * FIXME: we should really verify the digest here,
- * before marking this packet as a valid response.
- *
- * This is a security problem, I think...
- */
+ rad_encode(request->reply, request->packet, request->secret);
+ rad_sign(request->reply, request->packet, request->secret);
- /*
- * Now that we've verified the packet IS actually from
- * that home server, and not forged, we can go mark the
- * entries for this home server as active.
- *
- * If we had done this check in the 'find realm by IP address'
- * function, then an attacker could force us to use a home
- * server which was inactive, by forging reply packets
- * which didn't match any request. We would think that
- * the reply meant the home server was active, would
- * re-activate the realms, and THEN bounce the packet
- * as garbage.
- */
- for (cl = mainconfig.realms; cl != NULL; cl = cl->next) {
- if (oldreq->proxy_reply->src_ipaddr.af != cl->ipaddr.af) continue;
- if (cl->ipaddr.af != AF_INET) continue; /* FIXME */
-
- if (oldreq->proxy_reply->src_ipaddr.ipaddr.ip4addr.s_addr == cl->ipaddr.ipaddr.ip4addr.s_addr) {
- if (oldreq->proxy_reply->src_port == cl->auth_port) {
- cl->active = TRUE;
- cl->last_reply = oldreq->timestamp;
- } else if (oldreq->proxy_reply->src_port == cl->acct_port) {
- cl->acct_active = TRUE;
- cl->last_reply = oldreq->timestamp;
- }
- }
+ return 0;
+}
+
+
+static int client_socket_decode(rad_listen_t *listener, REQUEST *request)
+{
+ if (rad_verify(request->packet, NULL, request->secret) < 0) {
+ return -1;
}
- rad_assert(fun != NULL);
- *pfun = fun;
- *prequest = oldreq;
+ return rad_decode(request->packet, NULL, request->secret);
+}
- return 1;
+static int proxy_socket_encode(rad_listen_t *listener, REQUEST *request)
+{
+ rad_encode(request->proxy, NULL, request->home_server->secret);
+ rad_sign(request->proxy, NULL, request->home_server->secret);
+
+ return 0;
}
+
+static int proxy_socket_decode(rad_listen_t *listener, REQUEST *request)
+{
+ if (rad_verify(request->proxy_reply, request->proxy,
+ request->home_server->secret) < 0) {
+ return -1;
+ }
+
+ return rad_decode(request->proxy_reply, request->proxy,
+ request->home_server->secret);
+}
+
+
#define STATE_UNOPENED (0)
#define STATE_UNLOCKED (1)
#define STATE_HEADER (2)
}
/*
- * If we keep track of the outstanding requests, do so
- * here. Note that to minimize potential work, we do
- * so only once the file is opened & locked.
- */
- if (data->max_outstanding) {
- int i;
-
- for (i = 0; i < data->max_outstanding; i++) {
- if (!data->outstanding[i]) {
- free_slot = i;
- break;
- }
- }
-
- /*
- * All of the slots are full, don't read data.
- */
- if (free_slot < 0) return 0;
- }
-
- /*
* Catch an out of memory condition which will most likely
* never be met.
*/
* problem.
*/
alloc_packet:
+ /*
+ * If we keep track of the outstanding requests, do so
+ * here. Note that to minimize potential work, we do
+ * so only once the file is opened & locked.
+ */
+ if (data->max_outstanding) {
+ int i;
+
+ for (i = 0; i < data->max_outstanding; i++) {
+ if (!data->outstanding[i]) {
+ free_slot = i;
+ break;
+ }
+ }
+
+ /*
+ * All of the slots are full. Put the LF back,
+ * so select will say that data is ready, and set
+ * the state back to reading. Then, return so
+ * that we don't overload the server.
+ */
+ if (free_slot < 0) {
+ ungetc('\n', data->fp);
+ data->state = STATE_READING;
+ return 0;
+ }
+ }
+
packet = rad_alloc(1);
if (!packet) {
return 0; /* maybe memory will magically free up... */
* However, we don't want to keep track of used/unused
* id's and ports, as that's a lot of work. This hack
* ensures that (if we have real random numbers), that
- * there will be a collision on every (2^(16+16+2+24))/2
- * packets, on average. That means we can read 2^32 (4G)
+ * there will be a collision on every 2^(16+15+15+24 - 1)
+ * packets, on average. That means we can read 2^37
* packets before having a collision, which means it's
- * effectively impossible. Having 4G packets currently
- * being process is ridiculous.
+ * effectively impossible.
*/
- packet->id = lrad_rand() & 0xff;
- packet->src_port = lrad_rand() & 0xffff;
- packet->dst_port = lrad_rand() & 0xffff;
+ packet->id = lrad_rand() & 0xffff;
+ packet->src_port = 1024 + (lrad_rand() & 0x7fff);
+ packet->dst_port = 1024 + (lrad_rand() & 0x7fff);
packet->dst_ipaddr.af = AF_INET;
packet->dst_ipaddr.ipaddr.ip4addr.s_addr = htonl((INADDR_LOOPBACK & ~0xffffff) | (lrad_rand() & 0xffffff));
data->state = STATE_HEADER;
/*
- * FIXME: many of these checks may not be necessary...
- */
- if (!common_checks(listener, packet, prequest, &detail_client)) {
- rad_free(&packet);
- return 0;
- }
-
- /*
* Keep track of free slots, as a hack, in an otherwise
* unused 'int'
*/
(*prequest)->simul_max = free_slot;
- if (free_slot) data->outstanding[free_slot] = 1;
+ data->outstanding[free_slot] = 1;
*pfun = rad_accounting;
}
}
+ /*
+ * FIXME: many of these checks may not be necessary when
+ * reading from the detail file.
+ */
+ if (!received_request(listener, packet, prequest, &detail_client)) {
+ rad_free(&packet);
+ return 0;
+ }
+
return 1;
}
((listen_detail_t *)(this->data))->detail);
}
+static int detail_encode(rad_listen_t *this, REQUEST *request)
+{
+ /*
+ * We never encode responses "sent to" the detail file.
+ */
+ return 0;
+}
+
+static int detail_decode(rad_listen_t *this, REQUEST *request)
+{
+ /*
+ * We never decode responses read from the detail file.
+ */
+ return 0;
+}
+
static const CONF_PARSER detail_config[] = {
{ "detail", PW_TYPE_STRING_PTR,
return 0;
}
-
-/*
- * See radiusd.c & request_list.c
- */
-#define SLEEP_FOREVER (65536)
-/*
- * A generic "update the request list once a second" function.
- */
-static int generic_update(rad_listen_t *this, time_t now)
-{
- if (!this->rl) return SLEEP_FOREVER;
-
- return rl_clean_list(this->rl, now);
-}
-
-
-
static const rad_listen_master_t master_listen[RAD_LISTEN_MAX] = {
- { NULL, NULL, NULL, NULL, NULL, NULL}, /* RAD_LISTEN_NONE */
+ { NULL, NULL, NULL, NULL, NULL, NULL, NULL}, /* RAD_LISTEN_NONE */
/* authentication */
{ common_socket_parse, NULL,
auth_socket_recv, auth_socket_send,
- generic_update, socket_print },
+ socket_print, client_socket_encode, client_socket_decode },
/* accounting */
{ common_socket_parse, NULL,
acct_socket_recv, acct_socket_send,
- generic_update, socket_print},
+ socket_print, client_socket_encode, client_socket_decode},
/* proxying */
{ NULL, NULL,
proxy_socket_recv, proxy_socket_send,
- generic_update, socket_print }, /* FIXME: update func is wrong! */
+ socket_print, proxy_socket_encode, proxy_socket_decode },
/* detail */
{ detail_parse, detail_free,
detail_recv, detail_send,
- generic_update, detail_print }
+ detail_print, detail_encode, detail_decode }
};
+
/*
* Binds a listener to a socket.
*/
}
if (equal) {
- this->rl = (*last)->rl;
this->fd = (*last)->fd;
(*last)->fd = -1;
- (*last)->rl = NULL;
return 0;
}
}
return -1;
}
+#if 0
+#ifdef O_NONBLOCK
+ if ((flags = fcntl(this->fd, F_GETFL, NULL)) < 0) {
+ radlog(L_ERR, "Failure in fcntl: %s)\n", strerror(errno));
+ return -1;
+ }
+
+ flags |= O_NONBLOCK;
+ if( fcntl(this->fd, F_SETFL, flags) < 0) {
+ radlog(L_ERR, "Failure in fcntl: %s)\n", strerror(errno));
+ return -1;
+ }
+#endif
+#endif
+
return 0;
}
this->type = type;
this->recv = master_listen[this->type].recv;
this->send = master_listen[this->type].send;
- this->update = master_listen[this->type].update;
this->print = master_listen[this->type].print;
+ this->encode = master_listen[this->type].encode;
+ this->decode = master_listen[this->type].decode;
switch (type) {
case RAD_LISTEN_AUTH:
*/
rcode = 0;
for (this = *head; this != NULL; this = this->next) {
- if ((this->type != RAD_LISTEN_PROXY) &&
- !this->rl) {
- /*
- * FIXME: Pass type to rl_init, so that
- * it knows how to deal with accounting
- * packets. i.e. it caches them, but
- * doesn't bother trying to re-transmit.
- */
- this->rl = rl_init();
- if (!this->rl) {
- rad_assert(0 == 1); /* FIXME: */
- }
- }
-
if (((this->type == RAD_LISTEN_ACCT) &&
(rcode == RAD_LISTEN_DETAIL)) ||
((this->type == RAD_LISTEN_DETAIL) &&
free(this->identity);
- rl_deinit(this->rl);
-
/*
* Other code may have eaten the FD.
*/
#include <freeradius-devel/radiusd.h>
#include <freeradius-devel/rad_assert.h>
#include <freeradius-devel/modules.h>
-#include <freeradius-devel/request_list.h>
#include <sys/resource.h>
#include <netdb.h>
}
-/*
- * Create the linked list of realms from the new configuration type
- * This way we don't have to change to much in the other source-files
- */
-static int generate_realms(const char *filename)
-{
- CONF_SECTION *cs;
- REALM *my_realms = NULL;
- REALM *c, **tail;
- char *s, *t, *authhost, *accthost;
- const char *name2;
-
- tail = &my_realms;
- for (cs = cf_subsection_find_next(mainconfig.config, NULL, "realm");
- cs != NULL;
- cs = cf_subsection_find_next(mainconfig.config, cs, "realm")) {
- name2 = cf_section_name2(cs);
- if (!name2) {
- radlog(L_CONS|L_ERR, "%s[%d]: Missing realm name",
- filename, cf_section_lineno(cs));
- return -1;
- }
- /*
- * We've found a realm, allocate space for it
- */
- c = rad_malloc(sizeof(REALM));
- memset(c, 0, sizeof(REALM));
-
- c->secret[0] = '\0';
-
- /*
- * No authhost means LOCAL.
- */
- if ((authhost = cf_section_value_find(cs, "authhost")) == NULL) {
- c->ipaddr.af = AF_INET;
- c->ipaddr.ipaddr.ip4addr.s_addr = htonl(INADDR_NONE);
- c->auth_port = 0;
- } else {
- if ((s = strchr(authhost, ':')) != NULL) {
- *s++ = 0;
- c->auth_port = atoi(s);
- } else {
- c->auth_port = PW_AUTH_UDP_PORT;
- }
- if (strcmp(authhost, "LOCAL") == 0) {
- /*
- * Local realms don't have an IP address,
- * secret, or port.
- */
- c->ipaddr.af = AF_INET;
- c->ipaddr.ipaddr.ip4addr.s_addr = htonl(INADDR_NONE);
- c->auth_port = 0;
- } else {
- if (ip_hton(authhost, AF_INET,
- &c->ipaddr) < 0) {
- radlog(L_ERR, "%s[%d]: Host %s not found",
- filename, cf_section_lineno(cs),
- authhost);
- return -1;
- }
- }
-
- /*
- * Double check length, just to be sure!
- */
- if (strlen(authhost) >= sizeof(c->server)) {
- radlog(L_ERR, "%s[%d]: Server name of length %u is greater than allowed: %u",
- filename, cf_section_lineno(cs),
- strlen(authhost),
- sizeof(c->server) - 1);
- return -1;
- }
- }
-
- /*
- * No accthost means LOCAL
- */
- if ((accthost = cf_section_value_find(cs, "accthost")) == NULL) {
- c->acct_ipaddr.af = AF_INET;
- c->acct_ipaddr.ipaddr.ip4addr.s_addr = htonl(INADDR_NONE);
- c->acct_port = 0;
- } else {
- if ((s = strchr(accthost, ':')) != NULL) {
- *s++ = 0;
- c->acct_port = atoi(s);
- } else {
- c->acct_port = PW_ACCT_UDP_PORT;
- }
- if (strcmp(accthost, "LOCAL") == 0) {
- /*
- * Local realms don't have an IP address,
- * secret, or port.
- */
- c->acct_ipaddr.af = AF_INET;
- c->acct_ipaddr.ipaddr.ip4addr.s_addr = htonl(INADDR_NONE);
- c->acct_port = 0;
- } else {
- if (ip_hton(accthost, AF_INET,
- &c->acct_ipaddr) < 0) {
- radlog(L_ERR, "%s[%d]: Host %s not found",
- filename, cf_section_lineno(cs),
- accthost);
- return -1;
- }
- }
-
- if (strlen(accthost) >= sizeof(c->acct_server)) {
- radlog(L_ERR, "%s[%d]: Server name of length %u is greater than allowed: %u",
- filename, cf_section_lineno(cs),
- strlen(accthost),
- sizeof(c->acct_server) - 1);
- return -1;
- }
- }
-
- if (strlen(name2) >= sizeof(c->realm)) {
- radlog(L_ERR, "%s[%d]: Realm name of length %u is greater than allowed %u",
- filename, cf_section_lineno(cs),
- strlen(name2),
- sizeof(c->server) - 1);
- return -1;
- }
-
- strcpy(c->realm, name2);
- if (authhost) strcpy(c->server, authhost);
- if (accthost) strcpy(c->acct_server, accthost);
-
- /*
- * If one or the other of authentication/accounting
- * servers is set to LOCALHOST, then don't require
- * a shared secret.
- */
- rad_assert(c->ipaddr.af == AF_INET);
- rad_assert(c->acct_ipaddr.af == AF_INET);
- if ((c->ipaddr.ipaddr.ip4addr.s_addr != htonl(INADDR_NONE)) ||
- (c->acct_ipaddr.ipaddr.ip4addr.s_addr != htonl(INADDR_NONE))) {
- if ((s = cf_section_value_find(cs, "secret")) == NULL ) {
- radlog(L_ERR, "%s[%d]: No shared secret supplied for realm: %s",
- filename, cf_section_lineno(cs), name2);
- return -1;
- }
-
- if (strlen(s) >= sizeof(c->secret)) {
- radlog(L_ERR, "%s[%d]: Secret of length %u is greater than the allowed maximum of %u.",
- filename, cf_section_lineno(cs),
- strlen(s), sizeof(c->secret) - 1);
- return -1;
- }
- strlcpy((char *)c->secret, s, sizeof(c->secret));
- }
-
- c->striprealm = 1;
-
- if ((cf_section_value_find(cs, "nostrip")) != NULL)
- c->striprealm = 0;
- if ((cf_section_value_find(cs, "noacct")) != NULL)
- c->acct_port = 0;
- if ((cf_section_value_find(cs, "trusted")) != NULL)
- c->trusted = 1;
- if ((cf_section_value_find(cs, "notrealm")) != NULL)
- c->notrealm = 1;
- if ((cf_section_value_find(cs, "notsuffix")) != NULL)
- c->notrealm = 1;
- if ((t = cf_section_value_find(cs,"ldflag")) != NULL) {
- static const LRAD_NAME_NUMBER ldflags[] = {
- { "fail_over", 0 },
- { "round_robin", 1 },
- { NULL, 0 }
- };
-
- c->ldflag = lrad_str2int(ldflags, t, -1);
- if (c->ldflag == -1) {
- radlog(L_ERR, "%s[%d]: Unknown value \"%s\" for ldflag",
- filename, cf_section_lineno(cs),
- t);
- return -1;
- }
-
- } else {
- c->ldflag = 0; /* non, make it fail-over */
- }
- c->active = TRUE;
- c->acct_active = TRUE;
-
- c->next = NULL;
- *tail = c;
- tail = &c->next;
- }
-
- /*
- * And make these realms preferred over the ones
- * in the 'realms' file.
- */
- *tail = mainconfig.realms;
- mainconfig.realms = my_realms;
-
- /*
- * Ensure that all of the flags agree for the realms.
- *
- * Yeah, it's O(N^2), but it's only once, and the
- * maximum number of realms is small.
- */
- for(c = mainconfig.realms; c != NULL; c = c->next) {
- REALM *this;
-
- /*
- * Check that we cannot load balance to LOCAL
- * realms, as that doesn't make any sense.
- */
- rad_assert(c->ipaddr.af == AF_INET);
- rad_assert(c->acct_ipaddr.af == AF_INET);
- if ((c->ldflag == 1) &&
- ((c->ipaddr.ipaddr.ip4addr.s_addr == htonl(INADDR_NONE)) ||
- (c->acct_ipaddr.ipaddr.ip4addr.s_addr == htonl(INADDR_NONE)))) {
- radlog(L_ERR | L_CONS, "ERROR: Realm %s cannot be load balanced to LOCAL",
- c->realm);
- exit(1);
- }
-
- /*
- * Compare this realm to all others, to ensure
- * that the configuration is consistent.
- */
- for (this = c->next; this != NULL; this = this->next) {
- if (strcasecmp(c->realm, this->realm) != 0) {
- continue;
- }
-
- /*
- * Same realm: Different load balancing
- * flag: die.
- */
- if (c->ldflag != this->ldflag) {
- radlog(L_ERR | L_CONS, "ERROR: Inconsistent value in realm %s for load balancing 'ldflag' attribute",
- c->realm);
- exit(1);
- }
- }
- }
-
- return 0;
-}
-
-
static const LRAD_NAME_NUMBER str2dest[] = {
{ "null", RADLOG_NULL },
{ "files", RADLOG_FILES },
mainconfig.config = cs;
cf_section_free(&oldcs);
- /*
- * Old-style realms file.
- */
- snprintf(buffer, sizeof(buffer), "%.200s/%.50s", radius_dir, RADIUS_REALMS);
- DEBUG2("read_config_files: reading realms");
- if (read_realms_file(buffer) < 0) {
- radlog(L_ERR|L_CONS, "Errors reading realms");
- return -1;
- }
-
- /*
- * If there isn't any realms it isn't fatal..
- */
snprintf(buffer, sizeof(buffer), "%.200s/%.50s",
radius_dir, mainconfig.radiusd_conf);
- if (generate_realms(buffer) < 0) {
+ if (!realms_init(buffer)) {
return -1;
}
clients_free(old_clients);
}
- rl_init_proxy();
-
/* Reload the modules. */
DEBUG2("radiusd: entering modules setup");
if (setup_modules(reload) < 0) {
*/
cf_section_free(&mainconfig.config);
free(mainconfig.radiusd_conf);
- realm_free(mainconfig.realms);
+ realms_free();
listen_free(&mainconfig.listen);
xlat_free();
dict_free();
* A module has taken too long to process the request,
* and we've been told to stop processing it.
*/
- if (request->options & RAD_REQUEST_OPTION_STOP_NOW) {
+ if (request->master_state == REQUEST_STOP_PROCESSING) {
myresult = RLM_MODULE_FAIL;
break;
}
do_component[RLM_COMPONENT_POST_PROXY] = 1;
break;
+
default:
rad_assert(0 == 1);
break;
*/
int module_authorize(int autz_type, REQUEST *request)
{
- /*
- * Older versions of the server would pass proxy requests
- * through the 'authorize' sections twice; once when the
- * packet was received from the NAS, and again after the
- * reply was received from the home server. Now that we
- * have a 'post_proxy' section, the replies from the home
- * server should be sent through that, instead of through
- * the 'authorize' section again.
- */
- if (request->proxy != NULL) {
- DEBUG2(" authorize: Skipping authorize in post-proxy stage");
- return RLM_MODULE_NOOP;
- }
-
return indexed_modcall(RLM_COMPONENT_AUTZ, autz_type, request);
}
+++ /dev/null
-/*
- * proxy.c Proxy stuff.
- *
- * Version: $Id$
- *
- * 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
- *
- * Copyright 2000,2006 The FreeRADIUS server project
- * Copyright 2000 Miquel van Smoorenburg <miquels@cistron.nl>
- * Copyright 2000 Chris Parker <cparker@starnetusa.com>
- */
-
-#include <freeradius-devel/ident.h>
-RCSID("$Id$")
-
-#include <freeradius-devel/autoconf.h>
-
-#include <sys/socket.h>
-
-#ifdef HAVE_NETINET_IN_H
-# include <netinet/in.h>
-#endif
-
-#include <stdio.h>
-#include <stdlib.h>
-#include <ctype.h>
-#include <string.h>
-
-#include <freeradius-devel/radiusd.h>
-#include <freeradius-devel/rad_assert.h>
-#include <freeradius-devel/modules.h>
-#include <freeradius-devel/request_list.h>
-
-/*
- * We received a response from a remote radius server.
- * Call the post-proxy modules.
- */
-int proxy_receive(REQUEST *request)
-{
- int rcode;
- int post_proxy_type = 0;
- VALUE_PAIR *vp;
-
- /*
- * Delete any reply we had accumulated until now.
- */
- pairfree(&request->reply->vps);
-
- /*
- * Run the packet through the post-proxy stage,
- * BEFORE playing games with the attributes.
- */
- vp = pairfind(request->config_items, PW_POST_PROXY_TYPE);
- if (vp) {
- DEBUG2(" Found Post-Proxy-Type %s", vp->vp_strvalue);
- post_proxy_type = vp->lvalue;
- }
- rcode = module_post_proxy(post_proxy_type, request);
-
- /*
- * Delete the Proxy-State Attributes from the reply.
- * These include Proxy-State attributes from us and
- * remote server.
- */
- pairdelete(&request->proxy_reply->vps, PW_PROXY_STATE);
-
- /*
- * Add the attributes left in the proxy reply to
- * the reply list.
- */
- pairadd(&request->reply->vps, request->proxy_reply->vps);
- request->proxy_reply->vps = NULL;
-
- /*
- * Free proxy request pairs.
- */
- pairfree(&request->proxy->vps);
-
- /*
- * FIXME: If the packet is an Access-Challenge,
- * THEN add it to a cache, which does:
- *
- * (src IP, State) -> (home server ip/port)
- *
- * This allows the load-balancing code to
- * work for EAP...
- *
- * Alternately, we can delete the State from the home
- * server, and use our own.. that might be better.
- */
-
- return rcode;
-}
-
-/*
- * Add a proxy-pair to the end of the request.
- */
-static void proxy_addinfo(REQUEST *request)
-{
- VALUE_PAIR *proxy_pair;
-
- proxy_pair = paircreate(PW_PROXY_STATE, PW_TYPE_STRING);
- if (proxy_pair == NULL) {
- radlog(L_ERR|L_CONS, "no memory");
- exit(1);
- }
- sprintf(proxy_pair->vp_strvalue, "%d", request->packet->id);
- proxy_pair->length = strlen(proxy_pair->vp_strvalue);
-
- pairadd(&request->proxy->vps, proxy_pair);
-}
-
-
-/*
- * Like realm find, but does load balancing, and we don't
- * wake up any sleeping realms. Someone should already have
- * done that.
- *
- * It also does NOT do fail-over to default if the realms are dead,
- * as that decision has already been made.
- */
-static REALM *proxy_realm_ldb(REQUEST *request, const char *realm_name,
- int accounting)
-{
- REALM *cl, *lb;
- uint32_t count;
-
- /*
- * FIXME: If the packet contains a State attribute,
- * AND the realm is load-balance,
- * AND there is a matching
- * State attribute in the cached entry, THEN proxy it to
- * that realm.
- */
-
- lb = NULL;
- count = 0;
- for (cl = mainconfig.realms; cl; cl = cl->next) {
- /*
- * Wake up any sleeping realm.
- *
- * Note that the 'realm find' function will only
- * wake up the FIRST realm which matches. We've
- * got to wake up ALL of the matching realms.
- */
- if (cl->wakeup <= request->timestamp) {
- cl->active = TRUE;
- }
- if (cl->acct_wakeup <= request->timestamp) {
- cl->acct_active = TRUE;
- }
-
- /*
- * Asked for auth/acct, and the auth/acct server
- * is not active. Skip it.
- */
- if ((!accounting && !cl->active) ||
- (accounting && !cl->acct_active)) {
- continue;
- }
-
- /*
- * The realm name doesn't match, skip it.
- */
- if (strcasecmp(cl->realm, realm_name) != 0) {
- continue;
- }
-
- /*
- * Fail-over, pick the first one that matches.
- */
- if ((count == 0) && /* if size > 0, we have round-robin */
- (cl->ldflag == 0)) {
- return cl;
- }
-
- /*
- * We're doing load-balancing. Pick a random
- * number, which will be used to determine which
- * home server is chosen.
- */
- if (!lb) {
- lb = cl;
- count = 1;
- continue;
- }
-
- /*
- * Keep track of how many load balancing servers
- * we've gone through.
- */
- count++;
-
- /*
- * See the "camel book" for why this works.
- *
- * If (rand(0..n) < 1), pick the current realm.
- * We add a scale factor of 65536, to avoid
- * floating point.
- */
- if ((count * (lrad_rand() & 0xffff)) < (uint32_t) 0x10000) {
- lb = cl;
- }
- } /* loop over the realms */
-
- /*
- * Return the load-balanced realm.
- */
- return lb;
-}
-
-/*
- * Relay the request to a remote server.
- * Returns:
- *
- * RLM_MODULE_FAIL: we don't reply, caller returns without replying
- * RLM_MODULE_NOOP: caller falls through to normal processing
- * RLM_MODULE_HANDLED : we reply, caller returns without replying
- */
-int proxy_send(REQUEST *request)
-{
- int rcode;
- int pre_proxy_type = 0;
- VALUE_PAIR *realmpair;
- VALUE_PAIR *strippedname;
- VALUE_PAIR *vp;
- REALM *realm;
- char *realmname;
-
- /*
- * Not authentication or accounting. Stop it.
- */
- if ((request->packet->code != PW_AUTHENTICATION_REQUEST) &&
- (request->packet->code != PW_ACCOUNTING_REQUEST)) {
- DEBUG2(" ERROR: Cannot proxy packets of type %d",
- request->packet->code);
- return RLM_MODULE_FAIL;
- }
-
- /*
- * The timestamp is used below to figure the
- * next_try. The request needs to "hang around" until
- * either the other server sends a reply or the retry
- * count has been exceeded. Until then, it should not
- * be eligible for the time-based cleanup. --Pac. */
-
- realmpair = pairfind(request->config_items, PW_PROXY_TO_REALM);
- if (!realmpair) {
- /*
- * Not proxying, so we can exit from the proxy
- * code.
- */
- return RLM_MODULE_NOOP;
- }
-
- /*
- * If the server has already decided to reject the request,
- * then don't try to proxy it.
- */
- if (request->reply->code == PW_AUTHENTICATION_REJECT) {
- DEBUG2("Cancelling proxy as request was already rejected");
- return RLM_MODULE_REJECT;
- }
- if (((vp = pairfind(request->config_items, PW_AUTH_TYPE)) != NULL) &&
- (vp->lvalue == PW_AUTHTYPE_REJECT)) {
- DEBUG2("Cancelling proxy as request was already rejected");
- return RLM_MODULE_REJECT;
- }
- /*
- * Length == 0 means it exists, but there's no realm.
- * Don't proxy it.
- */
- if (realmpair->length == 0) {
- return RLM_MODULE_NOOP;
- }
-
- realmname = (char *)realmpair->vp_strvalue;
-
- /*
- * Look for the realm, using the load balancing
- * version of realm find.
- */
- realm = proxy_realm_ldb(request, realmname,
- (request->packet->code == PW_ACCOUNTING_REQUEST));
- if (realm == NULL) {
- DEBUG2(" ERROR: Failed to find live home server for realm %s",
- realmname);
- return RLM_MODULE_FAIL;
- }
-
- /*
- * Remember that we sent the request to a Realm.
- */
- pairadd(&request->packet->vps,
- pairmake("Realm", realm->realm, T_OP_EQ));
-
- /*
- * Access-Request: look for LOCAL realm.
- * Accounting-Request: look for LOCAL realm.
- */
- if (((request->packet->code == PW_AUTHENTICATION_REQUEST) &&
- (realm->ipaddr.af == AF_INET) &&
- (realm->ipaddr.ipaddr.ip4addr.s_addr == htonl(INADDR_NONE))) ||
- ((request->packet->code == PW_ACCOUNTING_REQUEST) &&
- (realm->acct_ipaddr.af == AF_INET) &&
- (realm->acct_ipaddr.ipaddr.ip4addr.s_addr == htonl(INADDR_NONE)))) {
- DEBUG2(" WARNING: Cancelling proxy to Realm %s, as the realm is local.",
- realm->realm);
- return RLM_MODULE_NOOP;
- }
-
- /*
- * This is mainly for radrelay. Don't proxy packets back
- * to servers which sent them to us.
- */
- if ((request->packet->code == PW_ACCOUNTING_REQUEST) &&
- (request->listener->type == RAD_LISTEN_DETAIL) &&
- (realm->acct_ipaddr.af == AF_INET) &&
- (request->packet->src_ipaddr.af == AF_INET) &&
- (realm->acct_ipaddr.ipaddr.ip4addr.s_addr == request->packet->src_ipaddr.ipaddr.ip4addr.s_addr)) {
- DEBUG2(" rlm_realm: Packet came from realm %s, proxy cancelled", realm->realm);
- return RLM_MODULE_NOOP;
- }
-
- /*
- * Allocate the proxy packet, only if it wasn't already
- * allocated by a module. This check is mainly to support
- * the proxying of EAP-TTLS and EAP-PEAP tunneled requests.
- *
- * In those cases, the EAP module creates a "fake"
- * request, and recursively passes it through the
- * authentication stage of the server. The module then
- * checks if the request was supposed to be proxied, and
- * if so, creates a proxy packet from the TUNNELED request,
- * and not from the EAP request outside of the tunnel.
- *
- * The proxy then works like normal, except that the response
- * packet is "eaten" by the EAP module, and encapsulated into
- * an EAP packet.
- */
- if (!request->proxy) {
- /*
- * Now build a new RADIUS_PACKET.
- *
- * FIXME: it could be that the id wraps around
- * too fast if we have a lot of requests, it
- * might be better to keep a seperate ID value
- * per remote server.
- *
- * OTOH the remote radius server should be smart
- * enough to compare _both_ ID and vector.
- * Right?
- */
- if ((request->proxy = rad_alloc(TRUE)) == NULL) {
- radlog(L_ERR|L_CONS, "no memory");
- exit(1);
- }
-
- /*
- * We now massage the attributes to be proxied...
- */
-
- /*
- * Copy the request, then look up name and
- * plain-text password in the copy.
- *
- * Note that the User-Name attribute is the
- * *original* as sent over by the client. The
- * Stripped-User-Name attribute is the one hacked
- * through the 'hints' file.
- */
- request->proxy->vps = paircopy(request->packet->vps);
- }
-
- /*
- * Strip the name, if told to.
- *
- * Doing it here catches the case of proxied tunneled
- * requests.
- */
- if (realm->striprealm == TRUE &&
- (strippedname = pairfind(request->proxy->vps, PW_STRIPPED_USER_NAME)) != NULL) {
- /*
- * If there's a Stripped-User-Name attribute in
- * the request, then use THAT as the User-Name
- * for the proxied request, instead of the
- * original name.
- *
- * This is done by making a copy of the
- * Stripped-User-Name attribute, turning it into
- * a User-Name attribute, deleting the
- * Stripped-User-Name and User-Name attributes
- * from the vps list, and making the new
- * User-Name the head of the vps list.
- */
- vp = pairfind(request->proxy->vps, PW_USER_NAME);
- if (!vp) {
- vp = paircreate(PW_USER_NAME, PW_TYPE_STRING);
- if (!vp) {
- radlog(L_ERR|L_CONS, "no memory");
- exit(1);
- }
- vp->next = request->proxy->vps;
- request->proxy->vps = vp;
- }
- memcpy(vp->vp_strvalue, strippedname->vp_strvalue,
- sizeof(vp->vp_strvalue));
- vp->length = strippedname->length;
-
- /*
- * Do NOT delete Stripped-User-Name.
- */
- }
-
- /*
- * If there is no PW_CHAP_CHALLENGE attribute but
- * there is a PW_CHAP_PASSWORD we need to add it
- * since we can't use the request authenticator
- * anymore - we changed it.
- */
- if (pairfind(request->proxy->vps, PW_CHAP_PASSWORD) &&
- pairfind(request->proxy->vps, PW_CHAP_CHALLENGE) == NULL) {
- vp = paircreate(PW_CHAP_CHALLENGE, PW_TYPE_STRING);
- if (!vp) {
- radlog(L_ERR|L_CONS, "no memory");
- exit(1);
- }
- vp->length = AUTH_VECTOR_LEN;
- memcpy(vp->vp_strvalue, request->packet->vector, AUTH_VECTOR_LEN);
- pairadd(&(request->proxy->vps), vp);
- }
-
- request->proxy->code = request->packet->code;
- if (request->packet->code == PW_AUTHENTICATION_REQUEST) {
- request->proxy->dst_port = realm->auth_port;
- request->proxy->dst_ipaddr = realm->ipaddr;
- } else if (request->packet->code == PW_ACCOUNTING_REQUEST) {
- request->proxy->dst_port = realm->acct_port;
- request->proxy->dst_ipaddr = realm->acct_ipaddr;
- }
-
- /*
- * Add PROXY_STATE attribute, before pre-proxy stage,
- * so the pre-proxy modules have access to it.
- *
- * Note that, at this point, the proxied request HAS NOT
- * been assigned a RADIUS Id.
- */
- proxy_addinfo(request);
-
- /*
- * Set up for sending the request.
- */
- memcpy(request->proxysecret, realm->secret, sizeof(request->proxysecret));
- request->proxy_try_count = mainconfig.proxy_retry_count - 1;
-
- vp = NULL;
- if (request->packet->code == PW_ACCOUNTING_REQUEST) {
- vp = pairfind(request->proxy->vps, PW_ACCT_DELAY_TIME);
- }
- if (vp) {
- request->proxy->timestamp = request->timestamp - vp->lvalue;
- } else {
- request->proxy->timestamp = request->timestamp;
- }
- request->proxy_start_time = request->timestamp;
-
- /*
- * Do pre-proxying.
- */
- vp = pairfind(request->config_items, PW_PRE_PROXY_TYPE);
- if (vp) {
- DEBUG2(" Found Pre-Proxy-Type %s", vp->vp_strvalue);
- pre_proxy_type = vp->lvalue;
- }
- rcode = module_pre_proxy(pre_proxy_type, request);
- switch (rcode) {
- /*
- * Only proxy the packet if the pre-proxy code succeeded.
- */
- case RLM_MODULE_NOOP:
- case RLM_MODULE_OK:
- case RLM_MODULE_UPDATED:
- /*
- * Delay sending the proxy packet until after we've
- * done the work above, playing with the request.
- *
- * After this point, it becomes dangerous to play with
- * the request data structure, as the reply MAY come in
- * and get processed before we're done with it here.
- */
- request->options |= RAD_REQUEST_OPTION_PROXIED;
-
- /*
- * If it's a fake request, don't send the proxy
- * packet. The outer tunnel session will take
- * care of doing that.
- */
- if ((request->options & RAD_REQUEST_OPTION_FAKE_REQUEST) == 0) {
- /*
- * Add the proxied request to the
- * list of outstanding proxied
- * requests, BEFORE we send it, so
- * we have fewer problems with race
- * conditions when the responses come
- * back very quickly.
- */
- if (!rl_add_proxy(request)) {
- DEBUG("ERROR: Failed to proxy request %d",
- request->number);
- return RLM_MODULE_FAIL; /* caller doesn't reply */
- }
-
- /*
- * We're still running, encode & sign the
- * packet outside of the critical section.
- */
- if (request->child_pid != NO_SUCH_CHILD_PID) {
- rad_encode(request->proxy, NULL,
- (char *)request->proxysecret);
- rad_sign(request->proxy, NULL,
- (char *)request->proxysecret);
- } else {
- request->proxy_listener->send(request->proxy_listener,
- request);
- }
- }
- rcode = RLM_MODULE_HANDLED; /* caller doesn't reply */
- break;
- /*
- * The module handled the request, don't reply.
- */
- case RLM_MODULE_HANDLED:
- break;
- /*
- * Neither proxy, nor reply to invalid requests.
- */
- case RLM_MODULE_FAIL:
- case RLM_MODULE_INVALID:
- case RLM_MODULE_NOTFOUND:
- case RLM_MODULE_REJECT:
- case RLM_MODULE_USERLOCK:
- default:
- rcode = RLM_MODULE_FAIL; /* caller doesn't reply */
- break;
- }
-
- /*
- * Do NOT free request->proxy->vps, the pairs are needed
- * for the retries!
- */
- return rcode;
-}
#include <freeradius-devel/radiusd.h>
#include <freeradius-devel/rad_assert.h>
#include <freeradius-devel/modules.h>
-#include <freeradius-devel/request_list.h>
#include <freeradius-devel/radius_snmp.h>
-#define SLEEP_FOREVER (65536)
-
/*
* Global variables.
*/
int pid;
int max_fd;
int status;
- int sleep_time = SLEEP_FOREVER;
int spawn_flag = TRUE;
int dont_fork = FALSE;
int sig_hup_block = FALSE;
- time_t last_cleaned_lists = 0;
+ struct timeval tv, *ptv = NULL;
#ifdef HAVE_SIGACTION
struct sigaction act;
* It's called the thread pool, but it does a little
* more than that.
*/
- thread_pool_init(spawn_flag);
+ radius_event_init(spawn_flag);
/*
* Use linebuffered or unbuffered stdout if
*/
detach_modules();
+ radius_event_free();
+
free(radius_dir);
/*
}
#endif
- if (sleep_time == SLEEP_FOREVER) {
+ if (!ptv) {
DEBUG2("Nothing to do. Sleeping until we see a request.");
- status = select(max_fd + 1, &readfds, NULL, NULL, NULL);
- } else {
- struct timeval tv;
-
- DEBUG2("Waking up in %d seconds...", sleep_time);
-
- tv.tv_sec = sleep_time;
- tv.tv_usec = 0;
- status = select(max_fd + 1, &readfds, NULL, NULL, &tv);
+ } else if (tv.tv_sec) {
+#if 0
+ DEBUG2("Waking up in %d.%06d seconds...",
+ (int) tv.tv_sec, (int) tv.tv_usec);
+#else
+ DEBUG2("Waking up in %d seconds...",
+ (int) tv.tv_sec);
+#endif
}
+ status = select(max_fd + 1, &readfds, NULL, NULL, ptv);
if (status == -1) {
/*
* On interrupts, we clean up the request
}
/*
- * Do per-socket receive processing of the
- * packet.
+ * Do per-socket receive processing of
+ * the packet. This also takes care of
+ * inserting the request into the event
+ * tree, and adding it to the queue for
+ * threads.
*/
if (!listener->recv(listener, &fun, &request)) {
continue;
}
+ // EVENT FIX FIXME! Nuke this!
+
/*
* Drop the request into the thread pool,
* and let the thread pool take care of
* doing something with it.
*/
if (!thread_pool_addrequest(request, fun)) {
- /*
- * FIXME: Maybe just drop
- * the packet on the floor?
- */
- request_reject(request, REQUEST_FAIL_NO_THREADS);
- request->finished = TRUE;
+ request->child_state = REQUEST_DONE;
}
} /* loop over listening sockets*/
}
#endif
- /*
- * Loop through the request lists once per
- * second, to clean up old requests.
- */
- if (last_cleaned_lists != time_now) {
- last_cleaned_lists = time_now;
-
- DEBUG2("--- Walking the entire request list ---");
- sleep_time = SLEEP_FOREVER;
- for (listener = mainconfig.listen;
- listener != NULL;
- listener = listener->next) {
- int next;
-
- next = listener->update(listener, time_now);
- if (next < sleep_time) {
- sleep_time = next;
- }
- }
- }
+ ptv = &tv;
+ radius_event_process(&ptv);
#ifdef HAVE_PTHREAD_H
--- /dev/null
+/*
+ * realms.c Realm handling code
+ *
+ * Version: $Id$
+ *
+ * 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
+ *
+ * Copyright 2000,2006 The FreeRADIUS server project
+ * Copyright 2000 Miquel van Smoorenburg <miquels@cistron.nl>
+ * Copyright 2000 Alan DeKok <aland@ox.org>
+ */
+
+#include <freeradius-devel/ident.h>
+RCSID("$Id$")
+
+#include <freeradius-devel/autoconf.h>
+
+#include <sys/stat.h>
+
+#include <stdlib.h>
+#include <string.h>
+#include <netdb.h>
+#include <ctype.h>
+#include <fcntl.h>
+
+#include <freeradius-devel/radiusd.h>
+#include <freeradius-devel/rad_assert.h>
+
+static rbtree_t *realms_byname = NULL;
+
+static rbtree_t *home_servers_byaddr = NULL;
+static rbtree_t *home_servers_byname = NULL;
+
+static rbtree_t *home_pools_byname = NULL;
+
+static int realm_name_cmp(const void *one, const void *two)
+{
+ const REALM *a = one;
+ const REALM *b = two;
+
+ return strcasecmp(a->name, b->name);
+}
+
+
+static int home_server_name_cmp(const void *one, const void *two)
+{
+ const home_server *a = one;
+ const home_server *b = two;
+
+ return strcasecmp(a->name, b->name);
+}
+
+static int home_server_addr_cmp(const void *one, const void *two)
+{
+ const home_server *a = one;
+ const home_server *b = two;
+
+ if (a->ipaddr.af < b->ipaddr.af) return -1;
+ if (a->ipaddr.af > b->ipaddr.af) return +1;
+
+ if (a->port < b->port) return -1;
+ if (a->port > b->port) return +1;
+
+ switch (a->ipaddr.af) {
+ case AF_INET:
+ return memcmp(&a->ipaddr.ipaddr.ip4addr,
+ &b->ipaddr.ipaddr.ip4addr,
+ sizeof(a->ipaddr.ipaddr.ip4addr));
+ break;
+ case AF_INET6:
+ return memcmp(&a->ipaddr.ipaddr.ip6addr,
+ &b->ipaddr.ipaddr.ip6addr,
+ sizeof(a->ipaddr.ipaddr.ip6addr));
+ break;
+ default:
+ break;
+ }
+
+ return -1;
+}
+
+
+static int home_pool_name_cmp(const void *one, const void *two)
+{
+ const home_pool_t *a = one;
+ const home_pool_t *b = two;
+
+ return strcasecmp(a->name, b->name);
+}
+
+
+void realms_free(void)
+{
+ rbtree_free(home_servers_byname);
+ home_servers_byname = NULL;
+
+ rbtree_free(home_servers_byaddr);
+ home_servers_byaddr = NULL;
+
+ rbtree_free(home_pools_byname);
+ home_pools_byname = NULL;
+
+ rbtree_free(realms_byname);
+ realms_byname = NULL;
+}
+
+
+int realms_init(const char *filename)
+{
+ CONF_SECTION *cs;
+
+ if (realms_byname) return 1;
+
+ realms_byname = rbtree_create(realm_name_cmp, free, 0);
+ if (!realms_byname) {
+ realms_free();
+ return 0;
+ }
+
+ home_servers_byaddr = rbtree_create(home_server_addr_cmp, free, 0);
+ if (!home_servers_byaddr) {
+ realms_free();
+ return 0;
+ }
+
+ home_servers_byname = rbtree_create(home_server_name_cmp, NULL, 0);
+ if (!home_servers_byname) {
+ realms_free();
+ return 0;
+ }
+
+ home_pools_byname = rbtree_create(home_pool_name_cmp, free, 0);
+ if (!home_pools_byname) {
+ realms_free();
+ return 0;
+ }
+
+ for (cs = cf_subsection_find_next(mainconfig.config, NULL, "realm");
+ cs != NULL;
+ cs = cf_subsection_find_next(mainconfig.config, cs, "realm")) {
+ if (!realm_add(filename, cs)) {
+ realms_free();
+ return 0;
+ }
+ }
+
+ return 1;
+}
+
+static struct in_addr hs_ip4addr;
+static struct in6_addr hs_ip6addr;
+static char *hs_type = NULL;
+static char *hs_check = NULL;
+
+static CONF_PARSER home_server_config[] = {
+ { "ipaddr", PW_TYPE_IPADDR,
+ 0, &hs_ip4addr, NULL },
+ { "ipv6addr", PW_TYPE_IPV6ADDR,
+ 0, &hs_ip6addr, NULL },
+
+ { "hostname", PW_TYPE_STRING_PTR,
+ offsetof(home_server,hostname), NULL, NULL},
+ { "port", PW_TYPE_INTEGER,
+ offsetof(home_server,port), NULL, "0" },
+
+ { "type", PW_TYPE_STRING_PTR,
+ 0, &hs_type, NULL },
+
+ { "secret", PW_TYPE_STRING_PTR,
+ offsetof(home_server,secret), NULL, NULL},
+
+ { "response_window", PW_TYPE_INTEGER,
+ offsetof(home_server,response_window), NULL, "30" },
+ { "max_outstanding", PW_TYPE_INTEGER,
+ offsetof(home_server,max_outstanding), NULL, "65536" },
+
+ { "zombie_period", PW_TYPE_INTEGER,
+ offsetof(home_server,zombie_period), NULL, "40" },
+ { "ping_check", PW_TYPE_STRING_PTR,
+ 0, &hs_check, "none" },
+
+ { "ping_interval", PW_TYPE_INTEGER,
+ offsetof(home_server,ping_interval), NULL, "30" },
+ { "num_pings_to_alive", PW_TYPE_INTEGER,
+ offsetof(home_server,num_pings_to_alive), NULL, "3" },
+ { "revive_interval", PW_TYPE_INTEGER,
+ offsetof(home_server,revive_interval), NULL, "300" },
+
+ { "username", PW_TYPE_STRING_PTR,
+ offsetof(home_server,ping_user_name), NULL, NULL},
+ { "password", PW_TYPE_STRING_PTR,
+ offsetof(home_server,ping_user_password), NULL, NULL},
+
+ { NULL, -1, 0, NULL, NULL } /* end the list */
+
+};
+
+
+static int home_server_add(const char *filename, CONF_SECTION *cs)
+{
+ const char *name2;
+ home_server *home;
+
+ name2 = cf_section_name1(cs);
+ if (!name2 || (strcasecmp(name2, "home_server") != 0)) {
+ radlog(L_ERR, "%s[%d]: Section is not a home_server.",
+ filename, cf_section_lineno(cs));
+ return 0;
+ }
+
+ name2 = cf_section_name2(cs);
+ if (!name2) {
+ radlog(L_ERR, "%s[%d]: Home server section is missing a name.",
+ filename, cf_section_lineno(cs));
+ return 0;
+ }
+
+ home = rad_malloc(sizeof(*home));
+ memset(home, 0, sizeof(*home));
+
+ home->name = name2;
+
+ memset(&hs_ip4addr, 0, sizeof(hs_ip4addr));
+ memset(&hs_ip6addr, 0, sizeof(hs_ip6addr));
+ cf_section_parse(cs, home, home_server_config);
+
+ if (!home->hostname && (htonl(hs_ip4addr.s_addr) == INADDR_NONE) &&
+ IN6_IS_ADDR_UNSPECIFIED(&hs_ip6addr)) {
+ radlog(L_ERR, "%s[%d]: No hostname, IPv4 address, or IPv6 address defined for home server %s.",
+ filename, cf_section_lineno(cs), name2);
+ free(home);
+ free(hs_type);
+ hs_type = NULL;
+ free(hs_check);
+ hs_check = NULL;
+ return 0;
+ }
+
+ /*
+ * FIXME: Parse home->hostname!
+ *
+ * Right now, only ipaddr && ip6addr are used.
+ * The old-style parsing still allows hostnames.
+ */
+ if (htonl(hs_ip4addr.s_addr) != INADDR_NONE) {
+ home->ipaddr.af = AF_INET;
+ home->ipaddr.ipaddr.ip4addr = hs_ip4addr;
+
+ } else if (!IN6_IS_ADDR_UNSPECIFIED(&hs_ip6addr)) {
+ home->ipaddr.af = AF_INET6;
+ home->ipaddr.ipaddr.ip6addr = hs_ip6addr;
+
+ } else {
+ radlog(L_ERR, "%s[%d]: FIXME: parse hostname for home server %s.",
+ filename, cf_section_lineno(cs), name2);
+ free(home);
+ free(hs_type);
+ hs_type = NULL;
+ free(hs_check);
+ hs_check = NULL;
+ return 0;
+ }
+
+ if (!home->port || (home->port > 65535)) {
+ radlog(L_ERR, "%s[%d]: No port, or invalid port defined for home server %s.",
+ filename, cf_section_lineno(cs), name2);
+ free(home);
+ free(hs_type);
+ hs_type = NULL;
+ free(hs_check);
+ hs_check = NULL;
+ return 0;
+ }
+
+ if (0) {
+ radlog(L_ERR, "%s[%d]: Fatal error! Home server %s is ourselves!",
+ filename, cf_section_lineno(cs), name2);
+ free(home);
+ free(hs_type);
+ hs_type = NULL;
+ free(hs_check);
+ hs_check = NULL;
+ return 0;
+ }
+
+ if (strcasecmp(hs_type, "auth") == 0) {
+ home->type = HOME_TYPE_AUTH;
+
+ } else if (strcasecmp(hs_type, "acct") == 0) {
+ home->type = HOME_TYPE_ACCT;
+
+ } else {
+ radlog(L_ERR, "%s[%d]: Invalid type \"%s\" for home server %s.",
+ filename, cf_section_lineno(cs), hs_type, name2);
+ free(home);
+ free(hs_type);
+ hs_type = NULL;
+ free(hs_check);
+ hs_check = NULL;
+ return 0;
+ }
+ free(hs_type);
+ hs_type = NULL;
+
+ if (!home->secret) {
+ radlog(L_ERR, "%s[%d]: No shared secret defined for home server %s.",
+ filename, cf_section_lineno(cs), name2);
+ free(home);
+ return 0;
+ }
+
+ if (strcasecmp(hs_check, "none") == 0) {
+ home->ping_check = HOME_PING_CHECK_NONE;
+
+ } else if (strcasecmp(hs_check, "status-server") == 0) {
+ home->ping_check = HOME_PING_CHECK_STATUS_SERVER;
+
+ } else if (strcasecmp(hs_check, "request") == 0) {
+ home->ping_check = HOME_PING_CHECK_REQUEST;
+
+ } else {
+ radlog(L_ERR, "%s[%d]: Invalid ping_check \"%s\" for home server %s.",
+ filename, cf_section_lineno(cs), hs_check, name2);
+ free(home);
+ free(hs_check);
+ hs_check = NULL;
+ return 0;
+ }
+ free(hs_check);
+ hs_check = NULL;
+
+ if ((home->ping_check != HOME_PING_CHECK_NONE) &&
+ (home->ping_check != HOME_PING_CHECK_STATUS_SERVER)) {
+ if (!home->ping_user_name) {
+ radlog(L_INFO, "%s[%d]: You must supply a user name to enable ping checks",
+ filename, cf_section_lineno(cs));
+ free(home);
+ return 0;
+ }
+
+ if ((home->type == HOME_TYPE_AUTH) &&
+ !home->ping_user_password) {
+ radlog(L_INFO, "%s[%d]: You must supply a password to enable ping checks",
+ filename, cf_section_lineno(cs));
+ free(home);
+ return 0;
+ }
+ }
+
+ if (rbtree_finddata(home_servers_byaddr, home)) {
+ radlog(L_INFO, "%s[%d]: Ignoring duplicate home server %s.",
+ filename, cf_section_lineno(cs), name2);
+ return 1;
+ }
+
+ if (!rbtree_insert(home_servers_byname, home)) {
+ radlog(L_ERR, "%s[%d]: Internal error adding home server %s.",
+ filename, cf_section_lineno(cs), name2);
+ free(home);
+ return 0;
+ }
+
+ if (!rbtree_insert(home_servers_byaddr, home)) {
+ rbtree_deletebydata(home_servers_byname, home);
+ radlog(L_ERR, "%s[%d]: Internal error adding home server %s.",
+ filename, cf_section_lineno(cs), name2);
+ free(home);
+ return 0;
+ }
+
+ if (home->response_window < 5) home->response_window = 5;
+ if (home->response_window > 60) home->response_window = 60;
+
+ if (home->max_outstanding < 8) home->max_outstanding = 8;
+ if (home->max_outstanding > 65536*16) home->max_outstanding = 65536*16;
+
+ if (home->ping_interval < 6) home->ping_interval = 6;
+ if (home->ping_interval > 120) home->ping_interval = 120;
+
+ if (home->zombie_period < 20) home->zombie_period = 20;
+ if (home->zombie_period > 120) home->zombie_period = 120;
+
+ if (home->zombie_period < home->response_window) {
+ home->zombie_period = home->response_window;
+ }
+
+ if (home->num_pings_to_alive < 3) home->num_pings_to_alive = 3;
+ if (home->num_pings_to_alive > 10) home->num_pings_to_alive = 10;
+
+ if (home->revive_interval < 60) home->revive_interval = 60;
+ if (home->revive_interval > 3600) home->revive_interval = 3600;
+
+ return 1;
+}
+
+
+static int server_pool_add(const char *filename, CONF_SECTION *cs)
+{
+ const char *name2;
+ home_pool_t *pool;
+ const char *value;
+ CONF_PAIR *cp;
+ int num_home_servers;
+
+ name2 = cf_section_name1(cs);
+ if (!name2 || (strcasecmp(name2, "server_pool") != 0)) {
+ radlog(L_ERR, "%s[%d]: Section is not a server_pool.",
+ filename, cf_section_lineno(cs));
+ return 0;
+ }
+
+ name2 = cf_section_name2(cs);
+ if (!name2) {
+ radlog(L_ERR, "%s[%d]: Server pool section is missing a name.",
+ filename, cf_section_lineno(cs));
+ return 0;
+ }
+
+ num_home_servers = 0;
+ for (cp = cf_pair_find(cs, "home_server");
+ cp != NULL;
+ cp = cf_pair_find_next(cs, cp, "home_server")) {
+ num_home_servers++;
+ }
+
+ if (num_home_servers == 0) {
+ radlog(L_ERR, "%s[%d]: No home servers defined in pool %s",
+ filename, cf_section_lineno(cs), name2);
+ return 0;
+ }
+
+ pool = rad_malloc(sizeof(*pool) + num_home_servers * sizeof(pool->servers[0]));
+ memset(pool, 0, sizeof(*pool) + num_home_servers * sizeof(pool->servers[0]));
+
+ pool->type = HOME_POOL_FAIL_OVER;
+ pool->name = name2;
+
+ cp = cf_pair_find(cs, "type");
+ if (cp) {
+ value = cf_pair_value(cp);
+ if (!value) {
+ radlog(L_ERR, "%s[%d]: No value given for type.",
+ filename, cf_pair_lineno(cp));
+ free(pool);
+ return 0;
+ }
+
+ if (strcmp(value, "load-balance") == 0) {
+ pool->type = HOME_POOL_LOAD_BALANCE;
+
+ } else if (strcmp(value, "fail-over") == 0) {
+ pool->type = HOME_POOL_FAIL_OVER;
+
+ } else {
+ radlog(L_ERR, "%s[%d]: Unknown type \"%s\".",
+ filename, cf_pair_lineno(cp), value);
+ free(pool);
+ return 0;
+ }
+
+ DEBUG2(" server_pool %s: type = %s", name2, value);
+ }
+
+ for (cp = cf_pair_find(cs, "home_server");
+ cp != NULL;
+ cp = cf_pair_find_next(cs, cp, "home_server")) {
+ home_server myhome, *home;
+
+ value = cf_pair_value(cp);
+ if (!value) {
+ radlog(L_ERR, "%s[%d]: No value given for home_server.",
+ filename, cf_pair_lineno(cp));
+ free(pool);
+ return 0;
+ }
+
+ myhome.name = value;
+
+ home = rbtree_finddata(home_servers_byname, &myhome);
+ if (!home) {
+ CONF_SECTION *server_cs;
+
+ server_cs = cf_section_sub_find_name2(NULL,
+ "home_server",
+ value);
+ if (!server_cs) {
+ radlog(L_ERR, "%s[%d]: Unknown home_server \"%s\".",
+ filename, cf_pair_lineno(cp), value);
+ free(pool);
+ return 0;
+ }
+
+ if (!home_server_add(filename, server_cs)) {
+ free(pool);
+ return 0;
+ }
+
+ home = rbtree_finddata(home_servers_byname, &myhome);
+ if (!home) {
+ rad_assert("Internal sanity check failed");
+ return 0;
+ }
+ }
+
+ if (!pool->server_type) {
+ rad_assert(home->type != 0);
+ pool->server_type = home->type;
+
+ } else if (pool->server_type != home->type) {
+ radlog(L_ERR, "%s[%d]: Home server \"%s\" is not of the same type as previous servers in server pool %s",
+ filename, cf_pair_lineno(cp), value, pool->name);
+ free(pool);
+ return 0;
+ }
+
+ if (0) {
+ DEBUG2("Warning: Duplicate home server %s in server pool %s", home->name, pool->name);
+ continue;
+ }
+
+ DEBUG2(" server_pool %s: home_server = %s", name2, home->name);
+ pool->servers[pool->num_home_servers] = home;
+ pool->num_home_servers++;
+
+ } /* loop over home_server's */
+
+ if (!rbtree_insert(home_pools_byname, pool)) {
+ rad_assert("Internal sanity check failed");
+ return 0;
+ }
+
+ rad_assert(pool->server_type != 0);
+
+ return 1;
+}
+
+
+static int old_server_add(const char *filename, int lineno,
+ const char *name, const char *secret,
+ home_pool_type_t ldflag, home_pool_t **pool_p,
+ int type)
+{
+ int i, insert_point, num_home_servers;
+ home_server myhome, *home;
+ home_pool_t mypool, *pool;
+ CONF_SECTION *cs;
+
+ /*
+ * LOCAL realms get sanity checked, and nothing else happens.
+ */
+ if (strcmp(name, "LOCAL") == 0) {
+ if (*pool_p) {
+ radlog(L_ERR, "%s[%d]: Realm \"%s\" cannot be both LOCAL and remote", filename, lineno, name);
+ return 0;
+ }
+ return 1;
+ }
+
+ mypool.name = name;
+ pool = rbtree_finddata(home_pools_byname, &mypool);
+ if (pool) {
+ if (pool->type != ldflag) {
+ radlog(L_ERR, "%s[%d]: Inconsistent ldflag for server pool \"%s\"", filename, lineno, name);
+ return 0;
+ }
+
+ if (pool->server_type != type) {
+ radlog(L_ERR, "%s[%d]: Inconsistent home server type for server pool \"%s\"", filename, lineno, name);
+ return 0;
+ }
+ }
+
+ myhome.name = name;
+ home = rbtree_finddata(home_servers_byname, &myhome);
+ if (home) {
+ if (strcmp(home->secret, secret) != 0) {
+ radlog(L_ERR, "%s[%d]: Inconsistent shared secret for home server \"%s\"", filename, lineno, name);
+ return 0;
+ }
+
+ if (home->type != type) {
+ radlog(L_ERR, "%s[%d]: Inconsistent type for home server \"%s\"", filename, lineno, name);
+ return 0;
+ }
+
+ /*
+ * See if the home server is already listed
+ * in the pool. If so, do nothing else.
+ */
+ if (pool) for (i = 0; i < pool->num_home_servers; i++) {
+ if (pool->servers[i] == home) {
+ return 1;
+ }
+ }
+ }
+
+ /*
+ * If we do have a pool, check that there is room to
+ * insert the home server we've found, or the one that we
+ * create here.
+ *
+ * Note that we insert it into the LAST available
+ * position, in order to maintain the same order as in
+ * the configuration files.
+ */
+ insert_point = -1;
+ if (pool) {
+ for (i = pool->num_home_servers - 1; i >= 0; i--) {
+ if (pool->servers[i]) break;
+
+ if (!pool->servers[i]) {
+ insert_point = i;
+ }
+ }
+
+ if (insert_point < 0) {
+ radlog(L_ERR, "%s[%d]: No room in pool to add home server \"%s\". Please update the realm configuration to use the new-style home servers and server pools.", filename, lineno, name);
+ return 0;
+ }
+ }
+
+ /*
+ * No home server, allocate one.
+ */
+ if (!home) {
+ const char *p;
+ char *q;
+
+ home = rad_malloc(sizeof(*home));
+ memset(home, 0, sizeof(*home));
+
+ home->name = name;
+ home->hostname = name;
+ home->type = type;
+ home->secret = secret;
+
+ p = strchr(name, ':');
+ if (!p) {
+ if (type == HOME_TYPE_AUTH) {
+ home->port = PW_AUTH_UDP_PORT;
+ } else {
+ home->port = PW_ACCT_UDP_PORT;
+ }
+
+ p = name;
+ q = NULL;
+
+ } else if (p == name) {
+ radlog(L_ERR, "%s[%d]: Invalid hostname %s.",
+ filename, lineno, name);
+ free(home);
+ return 0;
+
+ } else {
+ home->port = atoi(p + 1);
+ if ((home->port == 0) || (home->port > 65535)) {
+ radlog(L_ERR, "%s[%d]: Invalid port %s.",
+ filename, lineno, p + 1);
+ free(home);
+ return 0;
+ }
+
+ q = rad_malloc((p - name) + 1);
+ memcpy(q, name, (p - name));
+ q[p - name] = '\0';
+ p = q;
+ }
+
+ if (ip_hton(p, AF_UNSPEC, &home->ipaddr) < 0) {
+ radlog(L_ERR, "%s[%d]: Failed looking up hostname %s.",
+ filename, lineno, p);
+ free(home);
+ free(q);
+ return 0;
+ }
+ free(q);
+
+ /*
+ * Use the old-style configuration.
+ */
+ home->max_outstanding = 65535*16;
+ home->zombie_period = mainconfig.proxy_retry_delay * mainconfig.proxy_retry_count;
+ if (home->zombie_period == 0) home->zombie_period =30;
+ home->response_window = home->zombie_period - 1;
+
+ home->ping_check = HOME_PING_CHECK_NONE;
+
+ home->revive_interval = mainconfig.proxy_dead_time;
+
+ if (rbtree_finddata(home_servers_byaddr, home)) {
+ radlog(L_ERR, "%s[%d]: Home server %s has the same IP address as another home server.",
+ filename, lineno, name);
+ free(home);
+ return 0;
+ }
+
+ if (!rbtree_insert(home_servers_byname, home)) {
+ radlog(L_ERR, "%s[%d]: Internal error adding home server %s.",
+ filename, lineno, name);
+ free(home);
+ return 0;
+ }
+
+ if (!rbtree_insert(home_servers_byaddr, home)) {
+ rbtree_deletebydata(home_servers_byname, home);
+ radlog(L_ERR, "%s[%d]: Internal error adding home server %s.",
+ filename, lineno, name);
+ free(home);
+ return 0;
+ }
+ }
+
+ /*
+ * We now have a home server, see if we can insert it
+ * into pre-existing pool.
+ */
+ if (insert_point >= 0) {
+ rad_assert(pool != NULL);
+ pool->servers[insert_point] = home;
+ return 1;
+ }
+
+ rad_assert(pool == NULL);
+ rad_assert(home != NULL);
+
+ /*
+ * Count the old-style realms of this name.
+ */
+ num_home_servers = 0;
+ for (cs = cf_section_sub_find_name2(mainconfig.config, "realm", name);
+ cs != NULL;
+ cs = cf_section_sub_find_name2(cs, "realm", name)) {
+ num_home_servers++;
+ }
+
+
+ pool = rad_malloc(sizeof(*pool) + num_home_servers * sizeof(pool->servers[0]));
+ memset(pool, 0, sizeof(*pool) + num_home_servers * sizeof(pool->servers[0]));
+
+ pool->name = name;
+ pool->type = ldflag;
+ pool->server_type = type;
+ pool->num_home_servers = num_home_servers;
+ pool->servers[0] = home;
+
+ if (!rbtree_insert(home_pools_byname, pool)) {
+ rad_assert("Internal sanity check failed");
+ return 0;
+ }
+
+ *pool_p = pool;
+
+ return 1;
+}
+
+static int old_realm_config(const char *filename, CONF_SECTION *cs, REALM *r)
+{
+ char *host;
+ const char *secret;
+ home_pool_type_t ldflag;
+
+ secret = cf_section_value_find(cs, "secret");
+
+ host = cf_section_value_find(cs, "ldflag");
+ if (!host ||
+ (strcasecmp(host, "fail_over") == 0)) {
+ ldflag = HOME_POOL_FAIL_OVER;
+ DEBUG2(" realm %s: ldflag = fail_over", r->name);
+
+ } else if (strcasecmp(host, "round_robin") == 0) {
+ ldflag = HOME_POOL_LOAD_BALANCE;
+ DEBUG2(" realm %s: ldflag = round_robin", r->name);
+
+ } else {
+ radlog(L_ERR, "%s[%d]: Unknown value \"%s\" for ldflag",
+ filename, cf_section_lineno(cs), host);
+ return 0;
+ }
+
+ /*
+ * Allow old-style if it doesn't exist, or if it exists and
+ * it's LOCAL.
+ */
+ if (((host = cf_section_value_find(cs, "authhost")) != NULL) &&
+ (strcmp(host, "LOCAL") != 0)) {
+ if (!secret) {
+ radlog(L_ERR, "%s[%d]: No shared secret supplied for realm: %s",
+ filename, cf_section_lineno(cs), r->name);
+ return 0;
+ }
+
+ DEBUG2(" realm %s: authhost = %s", r->name, host);
+
+ if (!old_server_add(filename, cf_section_lineno(cs),
+ host, secret, ldflag,
+ &r->auth_pool, HOME_TYPE_AUTH)) {
+ return 0;
+ }
+ }
+
+ if (((host = cf_section_value_find(cs, "accthost")) != NULL) &&
+ (strcmp(host, "LOCAL") != 0)) {
+ if (!secret) {
+ radlog(L_ERR, "%s[%d]: No shared secret supplied for realm: %s",
+ filename, cf_section_lineno(cs), r->name);
+ return 0;
+ }
+
+ DEBUG2(" realm %s: accthost = %s", r->name, host);
+
+ if (!old_server_add(filename, cf_section_lineno(cs),
+ host, secret, ldflag,
+ &r->auth_pool, HOME_TYPE_ACCT)) {
+ return 0;
+ }
+ }
+
+ if (secret) DEBUG2(" realm %s: secret = %s", r->name, secret);
+
+ return 1;
+
+}
+
+
+static int add_pool_to_realm(const char *filename, int lineno,
+ const char *name, home_pool_t **dest,
+ int server_type)
+{
+ home_pool_t mypool, *pool;
+
+ mypool.name = name;
+ pool = rbtree_finddata(home_pools_byname, &mypool);
+ if (!pool) {
+ CONF_SECTION *pool_cs;
+
+ pool_cs = cf_section_sub_find_name2(NULL, "server_pool",
+ name);
+ if (!pool_cs) {
+ radlog(L_ERR, "%s[%d]: Failed to find server_pool \"%s\"",
+ filename, lineno, name);
+ return 0;
+ }
+
+ if (!server_pool_add(filename, pool_cs)) {
+ return 0;
+ }
+
+ pool = rbtree_finddata(home_pools_byname, &mypool);
+ if (!pool) {
+ rad_assert("Internal sanity check failed");
+ return 0;
+ }
+ }
+
+ if (pool->server_type != server_type) {
+ radlog(L_ERR, "%s[%d]: Incompatible server_pool \"%s\" (mixed auth_pool / acct_pool)",
+ filename, lineno, name);
+ return 0;
+ }
+
+ *dest = pool;
+
+ return 1;
+}
+
+int realm_add(const char *filename, CONF_SECTION *cs)
+{
+ const char *name2;
+ char *pool = NULL;
+ REALM *r;
+ CONF_PAIR *cp;
+
+ name2 = cf_section_name1(cs);
+ if (!name2 || (strcasecmp(name2, "realm") != 0)) {
+ radlog(L_ERR, "%s[%d]: Section is not a realm.",
+ filename, cf_section_lineno(cs));
+ return 0;
+ }
+
+ name2 = cf_section_name2(cs);
+ if (!name2) {
+ radlog(L_ERR, "%s[%d]: Realm section is missing the realm name.",
+ filename, cf_section_lineno(cs));
+ return 0;
+ }
+
+ /*
+ * The realm MAY already exist if it's an old-style realm.
+ * In that case, merge the old-style realm with this one.
+ */
+ r = realm_find(name2);
+ if (r) {
+ if (cf_pair_find(cs, "auth_pool") ||
+ cf_pair_find(cs, "acct_pool")) {
+ radlog(L_ERR, "%s[%d]: Duplicate realm \"%s\"",
+ filename, cf_section_lineno(cs), name2);
+ return 0;
+ }
+
+ if (!old_realm_config(filename, cs, r)) {
+ return 0;
+ }
+
+ return 1;
+ }
+
+ r = rad_malloc(sizeof(*r));
+ memset(r, 0, sizeof(*r));
+
+ r->name = name2;
+
+ /*
+ * Prefer new configuration to old one.
+ */
+ cp = cf_pair_find(cs, "auth_pool");
+ if (cp) pool = cf_pair_value(cp);
+ if (cp && pool) {
+ if (!add_pool_to_realm(filename, cf_pair_lineno(cp),
+ pool, &r->auth_pool, HOME_TYPE_AUTH)) {
+ free(r);
+ return 0;
+ }
+ DEBUG2(" realm %s: auth_pool = %s", name2, pool);
+ }
+
+ cp = cf_pair_find(cs, "acct_pool");
+ if (cp) pool = cf_pair_value(cp);
+ if (cp && pool) {
+ if (!add_pool_to_realm(filename, cf_pair_lineno(cp),
+ pool, &r->acct_pool, HOME_TYPE_ACCT)) {
+ free(r);
+ return 0;
+ }
+ DEBUG2(" realm %s: acct_pool = %s", name2, pool);
+ }
+
+ r->striprealm = 1;
+
+ if ((cf_section_value_find(cs, "nostrip")) != NULL) {
+ r->striprealm = 0;
+ DEBUG2(" realm %s: nostrip", name2);
+ }
+
+ /*
+ * We're a new-style realm. Complain if we see the old
+ * directives.
+ */
+ if (r->auth_pool || r->acct_pool) {
+ if (((cp = cf_pair_find(cs, "authhost")) != NULL) ||
+ ((cp = cf_pair_find(cs, "accthost")) != NULL) ||
+ ((cp = cf_pair_find(cs, "secret")) != NULL) ||
+ ((cp = cf_pair_find(cs, "ldflag")) != NULL)) {
+ DEBUG2("WARNING: Ignoring old-style configuration entry \"%s\" in realm \"%s\"", cf_pair_attr(cp), r->name);
+ }
+
+
+ /*
+ * The realm MAY be an old-style realm, as there
+ * was no auth_pool or acct_pool. Double-check
+ * it, just to be safe.
+ */
+ } else if (!old_realm_config(filename, cs, r)) {
+ free(r);
+ return 0;
+ }
+
+ if (!rbtree_insert(realms_byname, r)) {
+ rad_assert("Internal sanity check failed");
+ free(r);
+ return 0;
+ }
+
+ return 1;
+}
+
+
+/*
+ * Find a realm in the REALM list.
+ */
+REALM *realm_find(const char *name)
+{
+ REALM myrealm;
+
+ if (!name) name = "NULL";
+
+ myrealm.name = name;
+ return rbtree_finddata(realms_byname, &myrealm);
+}
+
+
+home_server *home_server_ldb(REALM *r, int code)
+{
+ uint32_t count;
+ home_server *lb;
+ home_pool_t *pool;
+
+ if (code == PW_AUTHENTICATION_REQUEST) {
+ pool = r->auth_pool;
+
+ } else if (code == PW_ACCOUNTING_REQUEST) {
+ pool = r->acct_pool;
+
+ } else {
+ rad_assert("Internal sanity check failed");
+ return NULL;
+ }
+
+ if (!pool) {
+ rad_assert("Internal sanity check failed");
+ return NULL;
+ }
+
+ lb = NULL;
+
+ for (count = 0; count < pool->num_home_servers; count++) {
+ home_server *home = pool->servers[count];
+
+ if (home->state == HOME_STATE_IS_DEAD) {
+ continue;
+ }
+
+ /*
+ * This home server is too busy. Choose another one.
+ */
+ if (home->currently_outstanding >= home->max_outstanding) {
+ continue;
+ }
+
+ /*
+ * Fail-over, pick the first one that matches.
+ */
+ if (pool->type == HOME_POOL_FAIL_OVER) {
+ return home;
+ }
+
+ /*
+ * FUTURE: If we're well into "zombie_period",
+ * then we probably want to think about
+ * load-balancing the packet somewhere else, as
+ * the home server is probably dead.
+ */
+
+ /*
+ * We're doing load-balancing. Pick a random
+ * number, which will be used to determine which
+ * home server is chosen.
+ */
+ if (!lb) {
+ lb = home;
+ continue;
+ }
+
+ /*
+ * See the "camel book" for why this works.
+ *
+ * If (rand(0..n) < 1), pick the current server.
+ * We add a scale factor of 65536, to avoid
+ * floating point.
+ */
+ if (((count + 1) * (lrad_rand() & 0xffff)) < (uint32_t) 0x10000) {
+ lb = home;
+ }
+ } /* loop over the realms */
+
+ /*
+ * No live match found, and no fallback to the "DEFAULT"
+ * realm. We fix this by blindly marking all servers as
+ * "live". But only do it for ones that don't support
+ * "pings", as they will be marked live when they
+ * actually are live.
+ */
+ if (!lb &&
+ !mainconfig.proxy_fallback &&
+ mainconfig.wake_all_if_all_dead) {
+ for (count = 0; count < pool->num_home_servers; count++) {
+ home_server *home = pool->servers[count];
+
+ if ((home->state == HOME_STATE_IS_DEAD) &&
+ (home->ping_check == HOME_PING_CHECK_NONE)) {
+ home->state = HOME_STATE_ALIVE;
+ if (!lb) lb = home;
+ }
+ }
+ }
+
+ /*
+ * Still nothing. Look up the DEFAULT realm, but only
+ * if we weren't looking up the NULL or DEFAULT realms.
+ */
+ if (!lb &&
+ mainconfig.proxy_fallback &&
+ (strcmp(r->name, "NULL") != 0) &&
+ (strcmp(r->name, "DEFAULT") != 0)) {
+ REALM *rd = realm_find("DEFAULT");
+
+ if (rd) {
+ DEBUG2(" Realm %s has no live home servers. Falling back to the DEFAULT realm.", r->name);
+ return home_server_ldb(rd, code);
+ }
+ }
+
+ /*
+ * Return the appropriate home server.
+ */
+ return lb;
+}
+
+
+home_server *home_server_find(lrad_ipaddr_t *ipaddr, int port)
+{
+ home_server myhome;
+
+ myhome.ipaddr = *ipaddr;
+ myhome.port = port;
+
+ return rbtree_finddata(home_servers_byaddr, &myhome);
+}
+++ /dev/null
-/*
- * request_list.c Hide the handling of the REQUEST list from
- * the main server.
- *
- * Version: $Id$
- *
- * 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
- *
- * Copyright 2003-2004,2006 The FreeRADIUS server project
- */
-
-#include <freeradius-devel/ident.h>
-RCSID("$Id$")
-
-#include <freeradius-devel/autoconf.h>
-
-#include <stdlib.h>
-#include <string.h>
-#include <signal.h>
-
-#include <freeradius-devel/radiusd.h>
-#include <freeradius-devel/rad_assert.h>
-#include <freeradius-devel/request_list.h>
-#include <freeradius-devel/radius_snmp.h>
-
-struct request_list_t {
- lrad_packet_list_t *pl;
-};
-
-#ifdef HAVE_PTHREAD_H
-static pthread_mutex_t proxy_mutex;
-#else
-/*
- * This is easier than ifdef's throughout the code.
- */
-#define pthread_mutex_lock(_x)
-#define pthread_mutex_unlock(_x)
-#endif
-
-static lrad_packet_list_t *proxy_list = NULL;
-
-/*
- * We keep the proxy FD's here. The RADIUS Id's are marked
- * "allocated" per Id, via a bit per proxy FD.
- */
-static int proxy_fds[32];
-static rad_listen_t *proxy_listeners[32];
-
-/*
- * Initialize the request list.
- */
-request_list_t *rl_init(void)
-{
- request_list_t *rl = rad_malloc(sizeof(*rl));
-
- /*
- * Initialize the request_list[] array.
- */
- memset(rl, 0, sizeof(*rl));
-
- rl->pl = lrad_packet_list_create(0);
- if (!rl->pl) {
- rad_assert("FAIL" == NULL);
- }
-
- return rl;
-}
-
-int rl_init_proxy(void)
-{
- /*
- * Hacks, so that multiple users can call rl_init,
- * and it won't get excited.
- *
- * FIXME: Move proxy stuff to another struct entirely.
- */
- if (proxy_list) return 0;
-
- /*
- * Create the tree for managing proxied requests and
- * responses.
- */
- proxy_list = lrad_packet_list_create(1);
- if (!proxy_list) {
- rad_assert("FAIL" == NULL);
- }
-
-#ifdef HAVE_PTHREAD_H
- /*
- * For now, always create the mutex.
- *
- * Later, we can only create it if there are multiple threads.
- */
- if (pthread_mutex_init(&proxy_mutex, NULL) != 0) {
- radlog(L_ERR, "FATAL: Failed to initialize proxy mutex: %s",
- strerror(errno));
- exit(1);
- }
-#endif
-
- {
- int i;
- rad_listen_t *listener;
-
- /*
- * Mark the Fd's as unused.
- */
- for (i = 0; i < 32; i++) proxy_fds[i] = -1;
-
- for (listener = mainconfig.listen;
- listener != NULL;
- listener = listener->next) {
- if (listener->type == RAD_LISTEN_PROXY) {
- /*
- * FIXME: This works only because we
- * start off with one proxy socket.
- */
- proxy_fds[listener->fd & 0x1f] = listener->fd;
- proxy_listeners[listener->fd & 0x1f] = listener;
- lrad_packet_list_socket_add(proxy_list, listener->fd);
- break;
- }
- }
- }
-
- return 1;
-}
-
-static int rl_free_entry(void *ctx, void *data)
-{
- ctx = ctx; /* -Wunused */
- REQUEST *request = lrad_packet2myptr(REQUEST, packet, data);
-
-#ifdef HAVE_PTHREAD_H
- /*
- * If someone is processing this request, kill
- * them, and mark the request as not being used.
- *
- * FIXME: Move the request to the "dead pool",
- * and don't kill the thread.
- */
- if (request->child_pid != NO_SUCH_CHILD_PID) {
- pthread_kill(request->child_pid, SIGKILL);
- request->child_pid = NO_SUCH_CHILD_PID;
- }
-#endif
- request_free(&request);
-
- return 0;
-}
-
-
-/*
- * Delete everything in the request list.
- *
- * This should be called only when debugging the server...
- */
-void rl_deinit(request_list_t *rl)
-{
- if (!rl) return;
-
- if (proxy_list) {
- lrad_packet_list_free(proxy_list);
- proxy_list = NULL;
- }
-
- /*
- * Delete everything in the table, too.
- */
- lrad_packet_list_walk(rl->pl, NULL, rl_free_entry);
-
- lrad_packet_list_free(rl->pl);
-
- /*
- * Just to ensure no one is using the memory.
- */
- memset(rl, 0, sizeof(*rl));
- free(rl);
-}
-
-
-/*
- * Yank a request from the tree, without free'ing it.
- */
-void rl_yank(request_list_t *rl, REQUEST *request)
-{
-#ifdef WITH_SNMP
- /*
- * Update the SNMP statistics.
- *
- * Note that we do NOT do this in rad_respond(),
- * as that function is called from child threads.
- * Instead, we update the stats when a request is
- * deleted, because only the main server thread calls
- * this function...
- */
- if (mainconfig.do_snmp) {
- switch (request->reply->code) {
- case PW_AUTHENTICATION_ACK:
- rad_snmp.auth.total_responses++;
- rad_snmp.auth.total_access_accepts++;
- break;
-
- case PW_AUTHENTICATION_REJECT:
- rad_snmp.auth.total_responses++;
- rad_snmp.auth.total_access_rejects++;
- break;
-
- case PW_ACCESS_CHALLENGE:
- rad_snmp.auth.total_responses++;
- rad_snmp.auth.total_access_challenges++;
- break;
-
- case PW_ACCOUNTING_RESPONSE:
- rad_snmp.acct.total_responses++;
- break;
-
- default:
- break;
- }
- }
-#endif
-
- /*
- * Delete the request from the list.
- */
- lrad_packet_list_yank(rl->pl, request->packet);
-
- /*
- * If there's a proxied packet, and we're still
- * waiting for a reply, then delete the packet
- * from the list of outstanding proxied requests.
- */
- if (request->proxy &&
- (request->proxy_outstanding > 0)) {
- pthread_mutex_lock(&proxy_mutex);
- lrad_packet_list_yank(proxy_list, request->proxy);
- lrad_packet_list_id_free(proxy_list, request->proxy);
- pthread_mutex_unlock(&proxy_mutex);
- }
-}
-
-
-/*
- * Delete a request from the tree.
- */
-void rl_delete(request_list_t *rl, REQUEST *request)
-{
- rl_yank(rl, request);
- request_free(&request);
-}
-
-
-/*
- * Add a request to the request list.
- */
-int rl_add(request_list_t *rl, REQUEST *request)
-{
- return lrad_packet_list_insert(rl->pl, &request->packet);
-}
-
-/*
- * Look up a particular request, using:
- *
- * Request ID, request code, source IP, source port,
- *
- * Note that we do NOT use the request vector to look up requests.
- *
- * We MUST NOT have two requests with identical (id/code/IP/port), and
- * different vectors. This is a serious error!
- */
-REQUEST *rl_find(request_list_t *rl, RADIUS_PACKET *packet)
-{
- RADIUS_PACKET **packet_p;
-
- packet_p = lrad_packet_list_find(rl->pl, packet);
- if (!packet_p) return NULL;
-
- return lrad_packet2myptr(REQUEST, packet, packet_p);
-}
-
-/*
- * Add an entry to the proxy tree.
- *
- * This is the ONLY function in this source file which may be called
- * from a child thread. It therefore needs mutexes...
- */
-int rl_add_proxy(REQUEST *request)
-{
- int i, proxy;
- char buf[128];
-
- request->proxy_outstanding = 1;
- request->proxy->sockfd = -1;
-
- pthread_mutex_lock(&proxy_mutex);
-
- if (!lrad_packet_list_id_alloc(proxy_list, request->proxy)) {
- int found;
- rad_listen_t *proxy_listener;
-
- /*
- * Allocate a new proxy Fd. This function adds it
- * into the list of listeners.
- */
- proxy_listener = proxy_new_listener();
- if (!proxy_listener) {
- pthread_mutex_unlock(&proxy_mutex);
- DEBUG2("ERROR: Failed to create a new socket for proxying requests.");
- return 0;
- }
-
- /*
- * Cache it locally.
- */
- found = -1;
- proxy = proxy_listener->fd;
- for (i = 0; i < 32; i++) {
- /*
- * Found a free entry. Save the socket,
- * and remember where we saved it.
- */
- if (proxy_fds[(proxy + i) & 0x1f] == -1) {
- found = (proxy + i) & 0x1f;
- proxy_fds[found] = proxy;
- proxy_listeners[found] = proxy_listener;
- break;
- }
- }
- rad_assert(found >= 0);
-
- if (!lrad_packet_list_socket_add(proxy_list, proxy_listener->fd)) {
- pthread_mutex_unlock(&proxy_mutex);
- DEBUG2("ERROR: Failed to create a new socket for proxying requests.");
- return 0; /* leak proxy_listener */
-
- }
-
- if (!lrad_packet_list_id_alloc(proxy_list, request->proxy)) {
- pthread_mutex_unlock(&proxy_mutex);
- DEBUG2("ERROR: Failed to create a new socket for proxying requests.");
- return 0;
- }
- }
-
- /*
- * FIXME: Hack until we get rid of rad_listen_t, and put
- * the information into the packet_list.
- */
- proxy = -1;
- for (i = 0; i < 32; i++) {
- if (proxy_fds[i] == request->proxy->sockfd) {
- proxy = i;
- break;
- }
- }
- rad_assert(proxy >= 0);
-
- rad_assert(proxy_fds[proxy] != -1);
- request->proxy_listener = proxy_listeners[proxy];
-
- if (!lrad_packet_list_insert(proxy_list, &request->proxy)) {
- pthread_mutex_unlock(&proxy_mutex);
- DEBUG2("ERROR: Failed to insert entry into proxy list");
- return 0;
- }
-
- pthread_mutex_unlock(&proxy_mutex);
-
- DEBUG3(" proxy: allocating destination %s port %d - Id %d",
- inet_ntop(request->proxy->dst_ipaddr.af,
- &request->proxy->dst_ipaddr.ipaddr, buf, sizeof(buf)),
- request->proxy->dst_port,
- request->proxy->id);
-
- return 1;
-}
-
-
-/*
- * Look up a particular request, using:
- *
- * Request Id, request code, source IP, source port,
- *
- * Note that we do NOT use the request vector to look up requests.
- *
- * We MUST NOT have two requests with identical (id/code/IP/port), and
- * different vectors. This is a serious error!
- */
-REQUEST *rl_find_proxy(RADIUS_PACKET *reply)
-{
- RADIUS_PACKET **proxy_p;
- REQUEST *request;
-
- pthread_mutex_lock(&proxy_mutex);
- proxy_p = lrad_packet_list_find_byreply(proxy_list, reply);
-
- if (!proxy_p) {
- pthread_mutex_unlock(&proxy_mutex);
- return NULL;
- }
-
- request = lrad_packet2myptr(REQUEST, proxy, proxy_p);
- rad_assert(request->proxy_outstanding > 0);
- request->proxy_outstanding--;
-
- /*
- * Received all of the replies we expect.
- * delete it from the managed list.
- */
- if (request->proxy_outstanding == 0) {
- lrad_packet_list_yank(proxy_list, request->proxy);
- lrad_packet_list_id_free(proxy_list, request->proxy);
- }
- pthread_mutex_unlock(&proxy_mutex);
-
- return request;
-}
-
-
-/*
- * Return the number of requests in the request list.
- */
-int rl_num_requests(request_list_t *rl)
-{
- return lrad_packet_list_num_elements(rl->pl);
-}
-
-
-/*
- * See also radiusd.c
- */
-#define SLEEP_FOREVER (65536)
-typedef struct rl_walk_t {
- time_t now;
- int sleep_time;
- request_list_t *rl;
-} rl_walk_t;
-
-
-/*
- * Refresh a request, by using cleanup_delay, max_request_time, etc.
- *
- * When walking over the request list, all of the per-request
- * magic is done here.
- */
-static int refresh_request(void *ctx, void *data)
-{
- int time_passed;
- rl_walk_t *info = (rl_walk_t *) ctx;
- child_pid_t child_pid;
- request_list_t *rl = info->rl;
- REQUEST *request = lrad_packet2myptr(REQUEST, packet, data);
-
- rad_assert(request->magic == REQUEST_MAGIC);
-
- time_passed = (int) (info->now - request->timestamp);
-
- /*
- * If the request is marked as a delayed reject, AND it's
- * time to send the reject, then do so now.
- */
- if (request->finished &&
- ((request->options & RAD_REQUEST_OPTION_DELAYED_REJECT) != 0)) {
- rad_assert(request->child_pid == NO_SUCH_CHILD_PID);
- if (time_passed < mainconfig.reject_delay) {
- goto reject_delay;
- }
-
- reject_packet:
- /*
- * Clear the 'delayed reject' bit, so that we
- * don't do this again, and fall through to
- * setting cleanup delay.
- */
- request->listener->send(request->listener, request);
- request->options &= ~RAD_REQUEST_OPTION_DELAYED_REJECT;
-
- /*
- * FIXME: Beware interaction with cleanup_delay,
- * where we might send a reject, and immediately
- * there-after clean it up!
- */
- }
-
- /*
- * If the request is finished, THEN
- * check that more than cleanup_delay seconds have passed
- * since it was received
- * OR, if this is a request which had the "don't cache"
- * option set, then it CANNOT have a duplicate
- * SO, clean it up
- */
- if (request->finished &&
- ((time_passed >= mainconfig.cleanup_delay) ||
- ((request->options & RAD_REQUEST_OPTION_DONT_CACHE) != 0))) {
- rad_assert(request->child_pid == NO_SUCH_CHILD_PID);
- /*
- * Request completed, delete it, and unlink it
- * from the currently 'alive' list of requests.
- */
- cleanup:
- DEBUG2("Cleaning up request %d ID %d with timestamp %08lx",
- request->number, request->packet->id,
- (unsigned long) request->timestamp);
-
- /*
- * Delete the request.
- */
- rl_delete(rl, request);
- return 0;
- }
-
- /*
- * If more than max_request_time has passed since
- * we received the request, kill it.
- */
- if (time_passed >= mainconfig.max_request_time) {
- int number;
-
- child_pid = request->child_pid;
- number = request->number;
-
- /*
- * There MUST be a RAD_PACKET reply.
- */
- rad_assert(request->reply != NULL);
-
- /*
- * If we've tried to proxy the request, and
- * the proxy server hasn't responded, then
- * we send a REJECT back to the caller.
- *
- * For safety, we assert that there is no child
- * handling the request. If the assertion fails,
- * it means that we've sent a proxied request to
- * the home server, and the child thread is still
- * sitting on the request!
- */
- if (request->proxy && !request->proxy_reply) {
- rad_assert(request->child_pid == NO_SUCH_CHILD_PID);
-
- radlog(L_ERR, "Rejecting request %d due to lack of any response from home server %s port %d",
- request->number,
- client_name_old(&request->packet->src_ipaddr),
- request->packet->src_port);
- request_reject(request, REQUEST_FAIL_HOME_SERVER);
- request->finished = TRUE;
- return 0;
- }
-
-#ifdef DELETE_BLOCKED_REQUESTS
- /*
- * Calling pthread_cancel() without a cancel handler is an
- * exceedingly bad idea. This code is left here in case
- * we implement per-module cancel handlers later.
- * See freeradius-devel archives,
- * "cancelling requests due to max_request_time"
- *
- * If implemented, just remove the #ifdef's for DELETE_BLOCKED_REQUESTS
- * scattered throughout the code (this file and others), and
- * add the option back to radiusd.conf.in.
- */
- if (mainconfig.kill_unresponsive_children) {
- if (child_pid != NO_SUCH_CHILD_PID) {
- /*
- * This request seems to have hung
- * - kill it
- */
-#ifdef HAVE_PTHREAD_H
- radlog(L_ERR, "Killing unresponsive thread for request %d",
- request->number);
- pthread_cancel(child_pid);
-#endif
- } /* else no proxy reply, quietly fail */
-
- /*
- * Maybe we haven't killed it. In that
- * case, print a warning.
- */
- } else
-#endif
- if ((child_pid != NO_SUCH_CHILD_PID) &&
- ((request->options & RAD_REQUEST_OPTION_LOGGED_CHILD) == 0)) {
- radlog(L_ERR, "WARNING: Unresponsive child (id %lu) for request %d",
- (unsigned long)child_pid, number);
-
- /*
- * Set the option that we've sent a log message,
- * so that we don't send more than one message
- * per request.
- */
- request->options |= RAD_REQUEST_OPTION_LOGGED_CHILD;
- }
-
- /*
- * Send a reject message for the request, mark it
- * finished, and forget about the child.
- */
- request_reject(request, REQUEST_FAIL_SERVER_TIMEOUT);
-
- request->child_pid = NO_SUCH_CHILD_PID;
-
-#ifdef DELETE_BLOCKED_REQUESTS
- if (mainconfig.kill_unresponsive_children)
- request->finished = TRUE;
-#endif
- return 0;
- } /* else the request is still allowed to be in the queue */
-
- /*
- * If the request is finished, set the cleanup delay.
- */
- if (request->finished) {
- time_passed = mainconfig.cleanup_delay - time_passed;
- goto setup_timeout;
- }
-
- /*
- * Accounting request. Don't re-send them, since the NAS
- * will take care of doing that, and we're not a NAS.
- * Instead, simply clean them up once we're pretty sure
- * that the home server won't be responding.
- */
- if ((request->packet->code == PW_ACCOUNTING_REQUEST) &&
- request->proxy && !request->proxy_reply &&
- (request->child_pid == NO_SUCH_CHILD_PID) &&
- ((info->now - request->proxy_start_time) > (mainconfig.proxy_retry_delay * 2))) {
- goto cleanup;
- }
-
-
- /*
- * Set reject delay, if appropriate.
- */
- if ((request->packet->code == PW_AUTHENTICATION_REQUEST) &&
- (mainconfig.reject_delay > 0)) {
- reject_delay:
- time_passed = mainconfig.reject_delay - time_passed;
-
- /*
- * This catches a corner case, apparently.
- */
- if ((request->reply->code == PW_AUTHENTICATION_REJECT) &&
- (time_passed == 0)) goto reject_packet;
- if (time_passed <= 0) time_passed = 1;
- goto setup_timeout;
- }
-
- /*
- * The request is still alive, wake up when it's
- * taken too long.
- */
- time_passed = mainconfig.max_request_time - time_passed;
-
-setup_timeout:
- if (time_passed < 0) time_passed = 1;
-
- if (time_passed < info->sleep_time) {
- info->sleep_time = time_passed;
- }
-
- return 0;
-}
-
-
-/*
- * Clean up the request list, every so often.
- *
- * This is done by walking through ALL of the list, and
- * - marking any requests which are finished, and expired
- * - killing any processes which are NOT finished after a delay
- * - deleting any marked requests.
- *
- * Returns the number of millisends to sleep, before processing
- * something.
- */
-int rl_clean_list(request_list_t *rl, time_t now)
-{
- rl_walk_t info;
-
- info.now = now;
- info.sleep_time = SLEEP_FOREVER;
- info.rl = rl;
-
- lrad_packet_list_walk(rl->pl, &info, refresh_request);
-
- if (info.sleep_time < 0) info.sleep_time = 0;
-
- return info.sleep_time;
-}
+++ /dev/null
-/*
- * proxy.c Proxy stuff.
- *
- * Version: $Id$
- *
- * 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
- *
- * Copyright 2000,2006 The FreeRADIUS server project
- * Copyright 2000 Miquel van Smoorenburg <miquels@cistron.nl>
- * Copyright 2000 Chris Parker <cparker@starnetusa.com>
- */
-
-#include <freeradius-devel/ident.h>
-RCSID("$Id$")
-
-#include <freeradius-devel/autoconf.h>
-
-#include <sys/socket.h>
-
-#ifdef HAVE_NETINET_IN_H
-# include <netinet/in.h>
-#endif
-
-#include <stdio.h>
-#include <stdlib.h>
-#include <ctype.h>
-#include <string.h>
-
-#include <freeradius-devel/radiusd.h>
-#include <freeradius-devel/rad_assert.h>
-#include <freeradius-devel/modules.h>
-
-
-/*
- * Reprocess the request in possibly a child thread, only through
- * a subsection of the post-proxy section of radiusd.conf.
- */
-static int process_post_proxy_fail(REQUEST *request)
-{
- VALUE_PAIR *vps;
-
- /*
- *
- */
-
-
- /*
- * Hmm... this code is copied from below, which isn't good,
- * and is similar to the code in rad_respond.
- */
- switch (request->packet->code) {
- /*
- * Accounting requests, etc. get dropped on the floor.
- */
- default:
- case PW_ACCOUNTING_REQUEST:
- case PW_STATUS_SERVER:
- break;
-
- /*
- * Authentication requests get their Proxy-State
- * attributes copied over, and an otherwise blank
- * reject message sent.
- */
- case PW_AUTHENTICATION_REQUEST:
- request->reply->code = PW_AUTHENTICATION_REJECT;
-
- /*
- * Need to copy Proxy-State from request->packet->vps
- */
- vps = paircopy2(request->packet->vps, PW_PROXY_STATE);
- if (vps != NULL)
- pairadd(&(request->reply->vps), vps);
- break;
- }
-
- /*
- * Send the reply. The sender takes care of quenching
- * packets.
- */
- request->listener->send(request->listener, request);
-
- return 0; /* ignored for now */
-}
-
-/*
- * For debugging
- */
-static const LRAD_NAME_NUMBER request_fail_reason[] = {
- { "no threads available to handle the request",
- REQUEST_FAIL_NO_THREADS },
-
- { "malformed RADIUS packet",
- REQUEST_FAIL_DECODE},
-
- { "pre-proxying failed",
- REQUEST_FAIL_PROXY},
-
- { "sending of the proxy packet failed",
- REQUEST_FAIL_PROXY_SEND},
-
- { "failure to be told how to respond",
- REQUEST_FAIL_NO_RESPONSE},
-
- { "no response from the home server",
- REQUEST_FAIL_HOME_SERVER},
-
- { "no response from the home server after multiple tries",
- REQUEST_FAIL_HOME_SERVER2},
-
- { "no response from the home server for a long period of time",
- REQUEST_FAIL_HOME_SERVER3},
-
- { "we were told to reject the request",
- REQUEST_FAIL_NORMAL_REJECT},
-
- { NULL, REQUEST_FAIL_UNKNOWN }
-};
-
-
-/*
- * Reject a request, by sending a trivial reply packet.
- */
- void request_reject(REQUEST *request, request_fail_t reason)
-{
- VALUE_PAIR *vps;
-
- /*
- * Already rejected. Don't do anything.
- */
- if (request->options & RAD_REQUEST_OPTION_REJECTED) {
- return;
- }
-
- DEBUG2("Server rejecting request %d due to %s.",
- request->number, lrad_int2str(request_fail_reason,
- reason, "unknown"));
-
- /*
- * Remember that it was rejected.
- */
- request->options |= RAD_REQUEST_OPTION_REJECTED;
-
- switch (reason) {
- case REQUEST_FAIL_NO_THREADS:
- DEBUG("WARNING: We recommend that you fix any TIMEOUT errors, or increase the value for \"max_servers\".");
- break;
-
- case REQUEST_FAIL_DECODE:
- DEBUG("WARNING: Someone may be attacking your RADIUS server.");
- break;
-
- case REQUEST_FAIL_NO_RESPONSE:
- DEBUG("WARNING: You did not configure the server to accept, or reject the user. Double-check Auth-Type.");
- break;
-
- /*
- * If the home server goes down for some reason,
- * we want to be able to know when. We do this
- * by calling a sub-section of the post_proxy section,
- * and processing any modules we find there.
- *
- * Note that this subsection CAN edit the response
- * to the NAS.
- */
- case REQUEST_FAIL_HOME_SERVER: /* Hmm... we may want only one */
- case REQUEST_FAIL_HOME_SERVER2:
- case REQUEST_FAIL_HOME_SERVER3:
- /*
- * Conditionally disable the home server we sent
- * packets to.
- */
- realm_disable(request);
-
- /*
- * Not supposed to re-process it,
- */
- if (mainconfig.proxy_fail_type) {
- DICT_VALUE *val;
-
- val = dict_valbyname(PW_POST_PROXY_TYPE, mainconfig.proxy_fail_type);
- if (!val) {
- DEBUG("ERROR: No such post-proxy type of \"%s\", cancelling post-proxy-failure call.", mainconfig.proxy_fail_type);
- return;
- }
-
- request->options |= RAD_REQUEST_OPTION_REPROCESS;
-
- thread_pool_addrequest(request, process_post_proxy_fail);
- return;
- }
- break;
-
- case REQUEST_FAIL_SERVER_TIMEOUT:
- radlog(L_ERR, "TIMEOUT for request %d in module %s, component %s",
- request->number,
- request->module ? request->module : "<server core>",
- request->component ? request->component : "<server core>");
- request->options |= RAD_REQUEST_OPTION_STOP_NOW;
- break;
-
- default: /* no additional messages, or things to do */
- break;
- }
-
- switch (request->packet->code) {
- /*
- * Accounting requests, etc. get dropped on the floor.
- */
- default:
- case PW_ACCOUNTING_REQUEST:
- case PW_STATUS_SERVER:
- break;
-
- /*
- * Authentication requests get their Proxy-State
- * attributes copied over, and an otherwise blank
- * reject message sent.
- */
- case PW_AUTHENTICATION_REQUEST:
- request->reply->code = PW_AUTHENTICATION_REJECT;
-
- /*
- * Need to copy Proxy-State from request->packet->vps
- */
- vps = paircopy2(request->packet->vps, PW_PROXY_STATE);
- if (vps != NULL)
- pairadd(&(request->reply->vps), vps);
- break;
- }
-
- /*
- * Reject the request. The sender will take care of delaying
- * or quenching rejects.
- */
- request->listener->send(request->listener, request);
-}
-
-
-/*
- * Respond to a request packet.
- *
- * Maybe we reply, maybe we don't.
- * Maybe we proxy the request to another server, or else maybe
- * we replicate it to another server.
- */
-int rad_respond(REQUEST *request, RAD_REQUEST_FUNP fun)
-{
- RADIUS_PACKET *packet, *original;
- const char *secret;
- int finished = FALSE;
-
- rad_assert(request->magic == REQUEST_MAGIC);
-
- /*
- * Don't decode the packet if it's an internal "fake"
- * request. Instead, just skip ahead to processing it.
- */
- if ((request->options & RAD_REQUEST_OPTION_FAKE_REQUEST) != 0) {
- goto skip_decode;
- }
-
- /*
- * Re-process the request.
- */
- if ((request->options & RAD_REQUEST_OPTION_REPROCESS) != 0) {
- goto skip_decode;
- }
-
- /*
- * Put the decoded packet into it's proper place.
- */
- if (request->proxy_reply != NULL) {
- packet = request->proxy_reply;
- secret = request->proxysecret;
- original = request->proxy;
- } else {
- packet = request->packet;
- secret = request->secret;
- original = NULL;
- }
-
- /*
- * Decode the packet, verifying it's signature,
- * and parsing the attributes into structures.
- *
- * Note that we do this CPU-intensive work in
- * a child thread, not the master. This helps to
- * spread the load a little bit.
- *
- * Internal requests (ones that never go on the
- * wire) have ->data==NULL (data is the wire
- * format) and don't need to be "decoded"
- */
- if (packet->data) {
- int decoderesult;
-
- /*
- * Fails verification: silently discard it.
- */
- decoderesult = rad_verify(packet, original, secret);
- if (decoderesult < 0) {
- radlog(L_ERR, "%s Dropping packet without response.", librad_errstr);
- /* Since accounting packets get this set in
- * request_reject but no response is sent...
- */
- request->options |= RAD_REQUEST_OPTION_REJECTED;
- goto finished_request;
- }
-
- /*
- * Can't decode it. This usually means we're out
- * of memory.
- */
- decoderesult = rad_decode(packet, original, secret);
- if (decoderesult < 0) {
- radlog(L_ERR, "%s", librad_errstr);
- request_reject(request, REQUEST_FAIL_DECODE);
- goto finished_request;
- }
- }
-
- /*
- * For proxy replies, remove non-allowed
- * attributes from the list of VP's.
- */
- if (request->proxy) {
- int rcode;
- rcode = proxy_receive(request);
- switch (rcode) {
- default: /* Don't Do Anything */
- break;
- case RLM_MODULE_FAIL:
- /* on error just continue with next request */
- goto next_request;
- case RLM_MODULE_HANDLED:
- /* if this was a replicated request, mark it as
- * finished first, because it was postponed
- */
- goto finished_request;
- }
-
- } else {
- /*
- * This is the initial incoming request which
- * we're processing.
- *
- * Some requests do NOT get cached, as they
- * CANNOT possibly have duplicates. Set the
- * magic option here.
- *
- * Status-Server messages are easy to generate,
- * so we toss them as soon as we see a reply.
- *
- * Accounting-Request packets WITHOUT an
- * Acct-Delay-Time attribute are NEVER
- * duplicated, as RFC 2866 Section 4.1 says that
- * the Acct-Delay-Time MUST be updated when the
- * packet is re-sent, which means the packet
- * changes, so it MUST have a new identifier and
- * Request Authenticator. */
- if ((request->packet->code == PW_STATUS_SERVER) ||
- ((request->packet->code == PW_ACCOUNTING_REQUEST) &&
- (pairfind(request->packet->vps, PW_ACCT_DELAY_TIME) == NULL))) {
- request->options |= RAD_REQUEST_OPTION_DONT_CACHE;
- }
- }
-
- skip_decode:
- /*
- * We should have a User-Name attribute now.
- */
- if (request->username == NULL) {
- request->username = pairfind(request->packet->vps,
- PW_USER_NAME);
- }
-
- (*fun)(request);
-
- /*
- * If the request took too long to process, don't do
- * anything else.
- */
- if (request->options & RAD_REQUEST_OPTION_STOP_NOW) {
- finished = TRUE;
- goto postpone_request;
- }
-
- /*
- * If the request took too long to process, don't do
- * anything else.
- */
- if (request->options & RAD_REQUEST_OPTION_REJECTED) {
- finished = TRUE;
- goto postpone_request;
- }
-
- /*
- * Status-Server requests NEVER get proxied.
- */
- if (mainconfig.proxy_requests) {
- if ((request->packet->code != PW_STATUS_SERVER) &&
- ((request->options & RAD_REQUEST_OPTION_PROXIED) == 0)) {
- int rcode;
-
- /*
- * Try to proxy this request.
- */
- rcode = proxy_send(request);
-
- switch (rcode) {
- default:
- break;
-
- /*
- * There was an error trying to proxy the request.
- * Drop it on the floor.
- */
- case RLM_MODULE_FAIL:
- DEBUG2("Error trying to proxy request %d: Rejecting it", request->number);
- request_reject(request, REQUEST_FAIL_PROXY);
- goto finished_request;
- break;
-
- /*
- * The pre-proxy module has decided to reject
- * the request. Do so.
- */
- case RLM_MODULE_REJECT:
- DEBUG2("Request %d rejected in proxy_send.", request->number);
- request_reject(request, REQUEST_FAIL_PROXY_SEND);
- goto finished_request;
- break;
-
- /*
- * If the proxy code has handled the request,
- * then postpone more processing, until we get
- * the reply packet from the home server.
- */
- case RLM_MODULE_HANDLED:
- goto postpone_request;
- break;
- }
-
- /*
- * Else rcode==RLM_MODULE_NOOP
- * and the proxy code didn't do anything, so
- * we continue handling the request here.
- */
- }
- } else if ((request->packet->code == PW_AUTHENTICATION_REQUEST) &&
- (request->reply->code == 0)) {
- /*
- * We're not configured to reply to the packet,
- * and we're not proxying, so the DEFAULT behaviour
- * is to REJECT the user.
- */
- request_reject(request, REQUEST_FAIL_NO_RESPONSE);
- goto finished_request;
- }
-
- /*
- * If we have a reply to send, copy the Proxy-State
- * attributes from the request to the tail of the reply,
- * and send the packet.
- */
- rad_assert(request->magic == REQUEST_MAGIC);
- if (request->reply->code != 0) {
- VALUE_PAIR *vp = NULL;
-
- /*
- * Need to copy Proxy-State from request->packet->vps
- */
- vp = paircopy2(request->packet->vps, PW_PROXY_STATE);
- if (vp) pairadd(&(request->reply->vps), vp);
- }
-
- /*
- * ALWAYS call the sender to send the reply. The sender
- * will take care of doing the appropriate work to
- * suppress packets which aren't supposed to be sent over
- * the wire, or to be delayed.
- */
- request->listener->send(request->listener, request);
-
- /*
- * We're done processing the request, set the
- * request to be finished, clean up as necessary,
- * and forget about the request.
- */
-
-finished_request:
-
- /*
- * Don't decode the packet if it's an internal "fake"
- * request. Instead, just skip ahead to processing it.
- */
- if ((request->options & RAD_REQUEST_OPTION_FAKE_REQUEST) != 0) {
- goto skip_free;
- }
-
- /*
- * We're done handling the request. Free up the linked
- * lists of value pairs. This might take a long time,
- * so it's more efficient to do it in a child thread,
- * instead of in the main handler when it eventually
- * gets around to deleting the request.
- *
- * Also, no one should be using these items after the
- * request is finished, and the reply is sent. Cleaning
- * them up here ensures that they're not being used again.
- *
- * Hmm... cleaning them up in the child thread also seems
- * to make the server run more efficiently!
- *
- * If we've delayed the REJECT, then do NOT clean up the request,
- * as we haven't created the REJECT message yet.
- */
- if ((request->options & RAD_REQUEST_OPTION_DELAYED_REJECT) == 0) {
- if (request->packet) {
- pairfree(&request->packet->vps);
- request->username = NULL;
- request->password = NULL;
- }
-
- /*
- * If we've sent a reply to the NAS, then this request is
- * pretty much finished, and we have no more need for any
- * of the value-pair's in it, including the proxy stuff.
- */
- if (request->reply->code != 0) {
- pairfree(&request->reply->vps);
- }
- }
-
- pairfree(&request->config_items);
- if (request->proxy) {
- pairfree(&request->proxy->vps);
- }
- if (request->proxy_reply) {
- pairfree(&request->proxy_reply->vps);
- }
-
- skip_free:
- DEBUG2("Finished request %d", request->number);
- finished = TRUE;
-
- /*
- * Go to the next request, without marking
- * the current one as finished.
- *
- * Hmm... this may not be the brightest thing to do.
- */
-next_request:
- DEBUG2("Going to the next request");
-
-postpone_request:
- return finished;
-}
* Mark the request as done.
*/
radlog(L_ERR|L_CONS, "!!! ERROR !!! The server is blocked: discarding new request %d", request->number);
- request->finished = TRUE;
+ request->child_state = REQUEST_DONE;
return 0;
}
if (!lrad_fifo_push(thread_pool.fifo[fifo], entry)) {
pthread_mutex_unlock(&thread_pool.queue_mutex);
radlog(L_ERR, "!!! ERROR !!! Failed inserting request %d into the queue", request->number);
- request->finished = TRUE;
+ request->child_state = REQUEST_DONE;
return 0;
}
* The main clean-up code won't delete the request from
* the request list, until it's marked "finished"
*/
- if ((*request)->options & RAD_REQUEST_OPTION_STOP_NOW) {
- (*request)->finished = 1;
+ if ((*request)->master_state == REQUEST_STOP_PROCESSING) {
+ (*request)->child_state = REQUEST_DONE;
goto retry;
}
* Loop forever, until told to exit.
*/
do {
- int finished;
-
/*
* Wait to be signalled.
*/
self->thread_num, self->request->number,
self->request_count);
- /*
- * Respond, and reset request->child_pid
- */
- finished = rad_respond(self->request, fun);
+ radius_handle_request(self->request, fun);
/*
* Update the active threads.
*/
pthread_mutex_lock(&thread_pool.queue_mutex);
-
- /*
- * We haven't replied to the client, but we HAVE
- * sent a proxied packet, and we have NOT
- * received a proxy response. In that case, send
- * the proxied packet now. Doing this in the mutex
- * avoids race conditions.
- *
- * FIXME: this work should really depend on a
- * "state", and "next handler", rather than
- * horrid hacks like thise.
- */
- if (!self->request->reply->data &&
- self->request->proxy && self->request->proxy->data
- && !self->request->proxy_reply)
- self->request->proxy_listener->send(self->request->proxy_listener,
- self->request);
-
- self->request->child_pid = NO_SUCH_CHILD_PID;
- self->request->finished = finished;
- self->request = NULL;
-
rad_assert(thread_pool.active_threads > 0);
thread_pool.active_threads--;
pthread_mutex_unlock(&thread_pool.queue_mutex);
* We've been told not to spawn threads, so don't.
*/
if (!thread_pool.spawn_flag) {
- request->finished = rad_respond(request, fun);
+ radius_handle_request(request, fun);
/*
* Requests that care about child process exit
#ifndef NDEBUG
request->magic = 0x01020304; /* set the request to be nonsense */
strcpy(request->secret, "REQUEST-DELETED");
- strcpy(request->proxysecret, "REQUEST-DELETED");
#endif
free(request);
request->password = NULL;
request->timestamp = time(NULL);
request->child_pid = NO_SUCH_CHILD_PID;
- request->container = NULL;
request->options = RAD_REQUEST_OPTION_NONE;
return request;
request->number = oldreq->number;
request->child_pid = NO_SUCH_CHILD_PID;
- request->options = RAD_REQUEST_OPTION_FAKE_REQUEST;
request->packet = rad_alloc(0);
rad_assert(request->packet != NULL);
request->packet->src_ipaddr.ipaddr.ip4addr.s_addr = htonl(INADDR_LOOPBACK);
request->packet->dst_ipaddr = request->packet->src_ipaddr;
request->packet->src_port = request->number >> 8;
+
+ /*
+ * This field is used by the rest of the code to notice that the
+ * request is "internal", and not from a real client.
+ */
request->packet->dst_port = 0;
/*
int locked;
int lock_count;
struct timeval tv;
- REALM *proxy_realm;
- char proxy_buffer[16];
- VALUE_PAIR *pair = packet->vps;
+ VALUE_PAIR *pair;
struct detail_instance *inst = instance;
}
/* Write each attribute/value to the log file */
- for (; pair != NULL; pair = pair->next) {
+ for (pair = packet->vps; pair != NULL; pair = pair->next) {
DICT_ATTR da;
da.attr = pair->attribute;
* Add non-protocol attibutes.
*/
if (compat) {
- if ((pair = pairfind(request->config_items,
- PW_PROXY_TO_REALM)) != NULL) {
- proxy_realm = realm_find(pair->vp_strvalue, TRUE);
- if (proxy_realm) {
- memset((char *) proxy_buffer, 0, 16);
-
- rad_assert(proxy_realm->acct_ipaddr.af == AF_INET);
-
- inet_ntop(proxy_realm->acct_ipaddr.af,
- &proxy_realm->acct_ipaddr.ipaddr,
- proxy_buffer, sizeof(proxy_buffer));
- fprintf(outfp, "\tFreeradius-Proxied-To = %s\n",
+ if (request->proxy) {
+ char proxy_buffer[128];
+
+ inet_ntop(request->proxy->dst_ipaddr.af,
+ &request->proxy->dst_ipaddr.ipaddr,
+ proxy_buffer, sizeof(proxy_buffer));
+ fprintf(outfp, "\tFreeradius-Proxied-To = %s\n",
proxy_buffer);
- DEBUG("rlm_detail: Freeradius-Proxied-To set to %s",
+ DEBUG("rlm_detail: Freeradius-Proxied-To = %s",
proxy_buffer);
- }
}
+
fprintf(outfp, "\tTimestamp = %ld\n",
(unsigned long) request->timestamp);
* We don't do TLS inside of TLS, as it's a bad
* idea...
*/
- if (((handler->request->options & RAD_REQUEST_OPTION_FAKE_REQUEST) != 0) &&
+ if ((handler->request->packet->dst_port == 0) &&
(default_eap_type == PW_EAP_TLS)) {
DEBUG2(" rlm_eap: Unable to tunnel TLS inside of TLS");
return EAP_INVALID;
* If it's a LOCAL realm, then we're not proxying
* to it.
*/
- realm = realm_find(proxy->vp_strvalue, 0);
- rad_assert(realm->ipaddr.af == AF_INET);
- if (realm && (realm->ipaddr.ipaddr.ip4addr.s_addr == htonl(INADDR_NONE))) {
+ realm = realm_find(proxy->vp_strvalue);
+ if (realm && (realm->auth_pool == NULL)) {
proxy = NULL;
}
}
* If it's a recursive request, then disallow
* TLS, TTLS, and PEAP, inside of the TLS tunnel.
*/
- if ((request->options & RAD_REQUEST_OPTION_FAKE_REQUEST) != 0) {
+ if (request->packet->dst_port == 0) {
switch(handler->eap_ds->response->type.type) {
case PW_EAP_TLS:
case PW_EAP_TTLS:
*/
i = 34; /* starts off with 34 octets */
len = rad_tunnel_pwdecode(vp->vp_strvalue + 17, &i,
- request->proxysecret,
+ request->home_server->secret,
request->proxy->vector);
/*
*/
case PW_PROXY_TO_REALM:
{
- REALM *realm = realm_find(vp->vp_strvalue, 0);
- if (realm &&
- (realm->ipaddr.af == AF_INET) &&
- (realm->ipaddr.ipaddr.ip4addr.s_addr != htonl(INADDR_NONE))) {
+ REALM *realm = realm_find(vp->vp_strvalue);
+ if (realm && !realm->auth_pool) {
return RLM_MODULE_NOOP;
}
break;
* Print helpful warnings if there was no password.
*/
if (!found_pw) {
+ /*
+ * Likely going to be proxied. Avoid printing
+ * warning message.
+ */
+ if (pairfind(request->config_items, PW_REALM) ||
+ (pairfind(request->config_items, PW_PROXY_TO_REALM))) {
+ return RLM_MODULE_NOOP;
+ }
DEBUG("rlm_pap: WARNING! No \"known good\" password found for the user. Authentication may fail because of this.");
return RLM_MODULE_NOOP;
}
/*
* Allow DEFAULT realms unless told not to.
*/
- realm = realm_find(realmname, (request->packet->code == PW_ACCOUNTING_REQUEST));
+ realm = realm_find(realmname);
if (!realm) {
DEBUG2(" rlm_realm: No such realm \"%s\"",
(realmname == NULL) ? "NULL" : realmname);
return 0;
}
if( inst->ignore_default &&
- (strcmp(realm->realm, "DEFAULT")) == 0) {
+ (strcmp(realm->name, "DEFAULT")) == 0) {
DEBUG2(" rlm_realm: Found DEFAULT, but skipping due to config.");
return 0;
}
- DEBUG2(" rlm_realm: Found realm \"%s\"", realm->realm);
+ DEBUG2(" rlm_realm: Found realm \"%s\"", realm->name);
/*
* If we've been told to strip the realm off, then do so.
}
DEBUG2(" rlm_realm: Proxying request from user %s to realm %s",
- username, realm->realm);
+ username, realm->name);
/*
* Add the realm name to the request.
*/
- pairadd(&request->packet->vps, pairmake("Realm", realm->realm,
+ pairadd(&request->packet->vps, pairmake("Realm", realm->name,
T_OP_EQ));
- DEBUG2(" rlm_realm: Adding Realm = \"%s\"", realm->realm);
+ DEBUG2(" rlm_realm: Adding Realm = \"%s\"", realm->name);
/*
* Figure out what to do with the request.
* Perhaps accounting proxying was turned off.
*/
case PW_ACCOUNTING_REQUEST:
- if (realm->acct_ipaddr.ipaddr.ip4addr.s_addr == htonl(INADDR_NONE)) {
+ if (!realm->acct_pool) {
DEBUG2(" rlm_realm: Accounting realm is LOCAL.");
return 0;
}
-
- if (realm->acct_port == 0) {
- DEBUG2(" rlm_realm: acct_port is not set. Proxy cancelled.");
- return 0;
- }
break;
/*
* Perhaps authentication proxying was turned off.
*/
case PW_AUTHENTICATION_REQUEST:
- if (realm->ipaddr.ipaddr.ip4addr.s_addr == htonl(INADDR_NONE)) {
+ if (!realm->auth_pool) {
DEBUG2(" rlm_realm: Authentication realm is LOCAL.");
return 0;
}
-
- if (realm->auth_port == 0) {
- DEBUG2(" rlm_realm: auth_port is not set. Proxy cancelled.");
- return 0;
- }
break;
}
* that has already proxied the request, we don't need to do
* it again.
*/
- for (vp = request->packet->vps; vp; vp = vp->next) {
- if (vp->attribute == PW_FREERADIUS_PROXIED_TO) {
- if (request->packet->code == PW_AUTHENTICATION_REQUEST &&
- vp->lvalue == realm->ipaddr.ipaddr.ip4addr.s_addr) {
- DEBUG2(" rlm_realm: Request not proxied due to Freeradius-Proxied-To");
- return 0;
- }
- if (request->packet->code == PW_ACCOUNTING_REQUEST &&
- vp->lvalue == realm->acct_ipaddr.ipaddr.ip4addr.s_addr) {
- DEBUG2(" rlm_realm: Request not proxied due to Freeradius-Proxied-To");
- return 0;
- }
+ vp = pairfind(request->packet->vps, PW_FREERADIUS_PROXIED_TO);
+ if (vp) {
+#if 0
+ /*
+ * FIXME: HOME SERVER
+ *
+ * What the heck is this code doing, and why?
+ */
+
+ if (request->packet->code == PW_AUTHENTICATION_REQUEST &&
+ vp->lvalue == realm->home_auth->ipaddr.ipaddr.ip4addr.s_addr) {
+ DEBUG2(" rlm_realm: Request not proxied due to Freeradius-Proxied-To");
+ return 0;
+ }
+ if (request->packet->code == PW_ACCOUNTING_REQUEST &&
+ vp->lvalue == realm->home_acct->ipaddr.ipaddr.ip4addr.s_addr) {
+ DEBUG2(" rlm_realm: Request not proxied due to Freeradius-Proxied-To");
+ return 0;
}
+#endif
}
/*
* Tell the server to proxy this request to another
* realm.
*/
- vp = pairmake("Proxy-To-Realm", realm->realm, T_OP_EQ);
+ vp = pairmake("Proxy-To-Realm", realm->name, T_OP_EQ);
if (!vp) {
radlog(L_ERR|L_CONS, "no memory");
exit(1);
* Maybe add a Proxy-To-Realm attribute to the request.
*/
DEBUG2(" rlm_realm: Preparing to proxy authentication request to realm \"%s\"\n",
- realm->realm);
+ realm->name);
add_proxy_to_realm(&request->config_items, realm);
return RLM_MODULE_UPDATED; /* try the next module */
* Maybe add a Proxy-To-Realm attribute to the request.
*/
DEBUG2(" rlm_realm: Preparing to proxy accounting request to realm \"%s\"\n",
- realm->realm);
+ realm->name);
add_proxy_to_realm(&request->config_items, realm);
return RLM_MODULE_UPDATED; /* try the next module */