diff --git a/etc/radiusclient.conf.in b/etc/radiusclient.conf.in index 5db11cbe..adf4f816 100644 --- a/etc/radiusclient.conf.in +++ b/etc/radiusclient.conf.in @@ -87,6 +87,10 @@ radius_deadtime 0 # local address from which radius packets have to be sent bindaddr * +# Transport Protocol Support +# Use "1" for using UDP and "2" for TCP protocol. +radius_proto 1 + # LOCAL settings # program to execute for local login diff --git a/include/freeradius-client.h b/include/freeradius-client.h index e1c6a8f7..6735f31b 100644 --- a/include/freeradius-client.h +++ b/include/freeradius-client.h @@ -419,6 +419,10 @@ typedef struct value_pair #define TIMEOUT_RC 1 #define REJECT_RC 2 +/* Transport Protocol Types */ +#define PROTO_TCP 1 +#define PROTO_UDP 2 + typedef struct send_data /* Used to pass information to sendserver() function */ { uint8_t code; //!< RADIUS packet code. @@ -430,6 +434,7 @@ typedef struct send_data /* Used to pass information to sendserver() function */ int retries; VALUE_PAIR *send_pairs; //!< More a/v pairs to send. VALUE_PAIR *receive_pairs; //!< Where to place received a/v pairs. + uint8_t radius_proto; //!< Transport protocol type. } SEND_DATA; #ifndef MIN diff --git a/lib/buildreq.c b/lib/buildreq.c index fdcb2f4b..493584fc 100644 --- a/lib/buildreq.c +++ b/lib/buildreq.c @@ -73,6 +73,7 @@ int rc_aaa(rc_handle *rh, uint32_t client_port, VALUE_PAIR *send, VALUE_PAIR **r double now = 0; time_t dtime; unsigned type; + int radius_proto = 0; if (request_type != PW_ACCOUNTING_REQUEST) { aaaserver = rc_conf_srv(rh, "authserver"); @@ -84,6 +85,12 @@ int rc_aaa(rc_handle *rh, uint32_t client_port, VALUE_PAIR *send, VALUE_PAIR **r if (aaaserver == NULL) return ERROR_RC; + radius_proto = rc_conf_int(rh, "radius_proto"); + if(1 == radius_proto) + data.radius_proto = PROTO_UDP; + else + data.radius_proto = PROTO_TCP; + data.send_pairs = send; data.receive_pairs = NULL; diff --git a/lib/options.h b/lib/options.h index 6587bdbc..69c52b15 100644 --- a/lib/options.h +++ b/lib/options.h @@ -50,6 +50,7 @@ static OPTION config_options_default[] = { {"radius_retries", OT_INT, ST_UNDEF, NULL}, {"radius_deadtime", OT_INT, ST_UNDEF, NULL}, {"bindaddr", OT_STR, ST_UNDEF, NULL}, +{"radius_proto", OT_INT, ST_UNDEF, NULL}, /* local options */ {"login_local", OT_STR, ST_UNDEF, NULL}, }; diff --git a/lib/sendserver.c b/lib/sendserver.c index ed9624ae..5f96d765 100644 --- a/lib/sendserver.c +++ b/lib/sendserver.c @@ -296,7 +296,11 @@ int rc_send_server (rc_handle *rh, SEND_DATA *data, char *msg, unsigned flags) } } - sockfd = socket (our_sockaddr.ss_family, SOCK_DGRAM, 0); + if(PROTO_TCP == data->radius_proto) + sockfd = socket (our_sockaddr.ss_family, SOCK_STREAM, 0); + else + sockfd = socket (our_sockaddr.ss_family, SOCK_DGRAM, 0); + if (sockfd < 0) { memset (secret, '\0', sizeof (secret)); @@ -386,6 +390,14 @@ int rc_send_server (rc_handle *rh, SEND_DATA *data, char *msg, unsigned flags) for (;;) { do { + if(PROTO_TCP == data->radius_proto) { + if((connect(sockfd, SA(auth_addr->ai_addr), auth_addr->ai_addrlen)) != 0) + { + rc_log(LOG_ERR, "%s: Connect Call Failed : %s", __FUNCTION__, strerror(errno)); + result = -1; + break; + } + } result = sendto (sockfd, (char *) auth, (unsigned int)total_length, (int) 0, SA(auth_addr->ai_addr), auth_addr->ai_addrlen); } while (result == -1 && errno == EINTR); diff --git a/tests/Makefile.am b/tests/Makefile.am index 0adf1427..f21b4478 100644 --- a/tests/Makefile.am +++ b/tests/Makefile.am @@ -4,10 +4,10 @@ SUBDIRS = docker EXTRA_DIST = radiusclient-ipv6.conf servers-ipv6 \ - radiusclient.conf servers README + radiusclient.conf servers radiusclient-tcp.conf servers-tcp README -nodist_check_SCRIPTS = basic-tests.sh ipv6-tests.sh -TESTS = basic-tests.sh ipv6-tests.sh +nodist_check_SCRIPTS = basic-tests.sh ipv6-tests.sh tcp-tests.sh +TESTS = basic-tests.sh ipv6-tests.sh tcp-tests.sh TESTS_ENVIRONMENT = \ top_builddir="$(top_builddir)" \ diff --git a/tests/docker/freeradius-users b/tests/docker/freeradius-users index 2d16bfde..a297c28d 100644 --- a/tests/docker/freeradius-users +++ b/tests/docker/freeradius-users @@ -23,4 +23,3 @@ test6 Cleartext-Password := "test" Framed-IP-Netmask = 255.255.255.0, Framed-Routing = Broadcast-Listen, Framed-MTU = 1500, - diff --git a/tests/docker/radius-clients.conf b/tests/docker/radius-clients.conf index a36140c4..8ed7918d 100644 --- a/tests/docker/radius-clients.conf +++ b/tests/docker/radius-clients.conf @@ -310,6 +310,150 @@ client localhost2 { # coa_server = coa } +client localhost { + # Allowed values are: + # dotted quad (1.2.3.4) + # hostname (radius.example.com) + ipaddr = 0.0.0.0/0 + proto = tcp + # OR, you can use an IPv6 address, but not both + # at the same time. +# ipv6addr = :: # any. ::1 == localhost + + # + # A note on DNS: We STRONGLY recommend using IP addresses + # rather than host names. Using host names means that the + # server will do DNS lookups when it starts, making it + # dependent on DNS. i.e. If anything goes wrong with DNS, + # the server won't start! + # + # The server also looks up the IP address from DNS once, and + # only once, when it starts. If the DNS record is later + # updated, the server WILL NOT see that update. + # + + # One client definition can be applied to an entire network. + # e.g. 127/8 should be defined with "ipaddr = 127.0.0.0" and + # "netmask = 8" + # + # If not specified, the default netmask is 32 (i.e. /32) + # + # We do NOT recommend using anything other than 32. There + # are usually other, better ways to achieve the same goal. + # Using netmasks of other than 32 can cause security issues. + # + # You can specify overlapping networks (127/8 and 127.0/16) + # In that case, the smallest possible network will be used + # as the "best match" for the client. + # + # Clients can also be defined dynamically at run time, based + # on any criteria. e.g. SQL lookups, keying off of NAS-Identifier, + # etc. + # See raddb/sites-available/dynamic-clients for details. + # + +# netmask = 32 + + # + # The shared secret use to "encrypt" and "sign" packets between + # the NAS and FreeRADIUS. You MUST change this secret from the + # default, otherwise it's not a secret any more! + # + # 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" + # + # A note on security: The security of the RADIUS protocol + # depends COMPLETELY on this secret! We recommend using a + # shared secret that is composed of: + # + # upper case letters + # lower case letters + # numbers + # + # And is at LEAST 8 characters long, preferably 16 characters in + # length. The secret MUST be random, and should not be words, + # phrase, or anything else that is recognizable. + # + # The default secret below is only for testing, and should + # not be used in any real environment. + # + secret = testing123 + + # + # Old-style clients do not send a Message-Authenticator + # in an Access-Request. RFC 5080 suggests that all clients + # SHOULD include it in an Access-Request. The configuration + # item below allows the server to require it. If a client + # is required to include a Message-Authenticator and it does + # not, then the packet will be silently discarded. + # + # allowed values: yes, no + require_message_authenticator = no + + # + # The short name is used as an alias for the fully qualified + # domain name, or the IP address. + # + # It is accepted for compatibility with 1.x, but it is no + # longer necessary in 2.0 + # +# shortname = localhost + + # + # the following three fields are optional, but may be used by + # checkrad.pl for simultaneous use checks + # + + # + # The nastype tells 'checkrad.pl' which NAS-specific method to + # use to query the NAS for simultaneous use. + # + # Permitted NAS types are: + # + # cisco + # computone + # livingston + # juniper + # max40xx + # multitech + # netserver + # pathras + # patton + # portslave + # tc + # usrhiper + # other # for all other types + + # + nas_type = other # localhost isn't usually a NAS... + + # + # The following two configurations are for future use. + # The 'naspasswd' file is currently used to store the NAS + # login name and password, which is used by checkrad.pl + # when querying the NAS for simultaneous use. + # +# login = !root +# password = someadminpas + + # + # As of 2.0, clients can also be tied to a virtual server. + # This is done by setting the "virtual_server" configuration + # item, as in the example below. + # +# virtual_server = home1 + + # + # A pointer to the "home_server_pool" OR a "home_server" + # section that contains the CoA configuration for this + # client. For an example of a coa home server or pool, + # see raddb/sites-available/originate-coa +# coa_server = coa +} # IPv6 Client #client ::1 { # secret = testing123 diff --git a/tests/radiusclient-ipv6.conf b/tests/radiusclient-ipv6.conf index 3534fa71..1f7d5495 100644 --- a/tests/radiusclient-ipv6.conf +++ b/tests/radiusclient-ipv6.conf @@ -78,6 +78,10 @@ radius_deadtime 0 # local address from which radius packets have to be sent bindaddr * +# Transport Protocol Support +# Use "1" for using UDP and "2" for TCP protocol. +radius_proto 1 + # file which holds sequence number for communication with the # RADIUS server seqfile /var/run/radius.seq diff --git a/tests/radiusclient-tcp.conf b/tests/radiusclient-tcp.conf new file mode 100644 index 00000000..243dcd0e --- /dev/null +++ b/tests/radiusclient-tcp.conf @@ -0,0 +1,93 @@ +# General settings + +# specify which authentication comes first respectively which +# authentication is used. possible values are: "radius" and "local". +# if you specify "radius,local" then the RADIUS server is asked +# first then the local one. if only one keyword is specified only +# this server is asked. +auth_order radius,local + +# maximum login tries a user has +login_tries 4 + +# timeout for all login tries +# if this time is exceeded the user is kicked out +login_timeout 60 + +# name of the nologin file which when it exists disables logins. +# it may be extended by the ttyname which will result in +# a terminal specific lock (e.g. /etc/nologin.ttyS2 will disable +# logins on /dev/ttyS2) +nologin /etc/nologin + +# name of the issue file. it's only display when no username is passed +# on the radlogin command line +issue /usr/local/etc/radiusclient/issue + +# RADIUS settings + +# RADIUS server to use for authentication requests. this config +# item can appear more then one time. if multiple servers are +# defined they are tried in a round robin fashion if one +# server is not answering. +# optionally you can specify a the port number on which is remote +# RADIUS listens separated by a colon from the hostname. if +# no port is specified /etc/services is consulted of the radius +# service. if this fails also a compiled in default is used. +authserver localhost + +# RADIUS server to use for accouting requests. All that I +# said for authserver applies, too. +# +acctserver localhost + +# file holding shared secrets used for the communication +# between the RADIUS client and server +servers ./servers-tcp-temp + +# dictionary of allowed attributes and values +# just like in the normal RADIUS distributions +dictionary ../etc/dictionary + +# file which specifies mapping between ttyname and NAS-Port attribute +mapfile ../etc/port-id-map + +# default authentication realm to append to all usernames if no +# realm was explicitly specified by the user +# the radiusd directly form Livingston doesnt use any realms, so leave +# it blank then +default_realm + +# time to wait for a reply from the RADIUS server +radius_timeout 10 + +# resend request this many times before trying the next server +radius_retries 3 + +# The length of time in seconds that we skip a nonresponsive RADIUS +# server for transaction requests. Server(s) being in the "dead" state +# are tried only after all other non-dead servers have been tried and +# failed or timeouted. The deadtime interval starts when the server +# does not respond to an authentication/accounting request transmissions. +# When the interval expires, the "dead" server would be re-tried again, +# and if it's still down then it will be considered "dead" for another +# such interval and so on. This option is no-op if there is only one +# server in the list. Set to 0 in order to disable the feature. +radius_deadtime 0 + +# local address from which radius packets have to be sent +bindaddr * + +# Transport Protocol Support +# Use "1" for using UDP and "2" for TCP protocol. +radius_proto 1 + +# file which holds sequence number for communication with the +# RADIUS server +seqfile /var/run/radius.seq + +# LOCAL settings + +# program to execute for local login +# it must support the -f flag for preauthenticated login +login_local /bin/login diff --git a/tests/radiusclient.conf b/tests/radiusclient.conf index f943f1dc..50d74573 100644 --- a/tests/radiusclient.conf +++ b/tests/radiusclient.conf @@ -78,6 +78,10 @@ radius_deadtime 0 # local address from which radius packets have to be sent bindaddr * +# Transport Protocol Support +# Use "1" for using UDP and "2" for TCP protocol. +radius_proto 1 + # file which holds sequence number for communication with the # RADIUS server seqfile /var/run/radius.seq diff --git a/tests/servers-tcp b/tests/servers-tcp new file mode 100644 index 00000000..1ef4a10e --- /dev/null +++ b/tests/servers-tcp @@ -0,0 +1,3 @@ +## Server Name or Client/Server pair Key +## ---------------- --------------- +localhost/localhost testing123 diff --git a/tests/tcp-tests.sh b/tests/tcp-tests.sh new file mode 100755 index 00000000..36b77e94 --- /dev/null +++ b/tests/tcp-tests.sh @@ -0,0 +1,55 @@ +#!/bin/sh + +# Copyright (C) 2014 Nikos Mavrogiannopoulos +# +# License: BSD + +srcdir="${srcdir:-.}" + +echo "***********************************************" +echo "This test will use a radius server on localhost" +echo "and which can be executed with run-server.sh " +echo "***********************************************" + +TMPFILE=tmp.out + +if test -z "$SERVER_IP";then + echo "the variable SERVER_IP is not defined" + exit 77 +fi + +sed 's/localhost/'$SERVER_IP'/g' <$srcdir/radiusclient-tcp.conf >radiusclient-tcp-temp.conf +sed 's/localhost/'$SERVER_IP'/g' <$srcdir/servers-tcp >servers-tcp-temp + +../src/radiusclient -f radiusclient-tcp-temp.conf User-Name=test Password=test >$TMPFILE +if test $? != 0;then + echo "Error in PAP auth" + exit 1 +fi + +grep "^Framed-Protocol = 'PPP'$" $TMPFILE >/dev/null 2>&1 +if test $? != 0;then + echo "Error in data received by server (Framed-Protocol)" + cat $TMPFILE + exit 1 +fi + +grep "^Framed-IP-Address = '192.168.1.190'$" $TMPFILE >/dev/null 2>&1 +if test $? != 0;then + echo "Error in data received by server (Framed-IP-Address)" + cat $TMPFILE + exit 1 +fi + +grep "^Framed-Route = '192.168.100.5/24'$" $TMPFILE >/dev/null 2>&1 +if test $? != 0;then + echo "Error in data received by server (Framed-Route)" + cat $TMPFILE + exit 1 +fi + +rm -f servers-tcp-temp +rm -f $TMPFILE +rm -f radiusclient-tcp-temp.conf + +exit 0