*~
*.o
*.a
-*.d
*.lo
*.loT
*.la
attrs.pre-proxy clients.conf dictionary templates.conf \
experimental.conf hints huntgroups ldap.attrmap \
policy.txt preproxy_users proxy.conf radiusd.conf trigger.conf \
- users policy.conf attrs.access_challenge README
+ users attrs.access_challenge README.rst
DEFAULT_SITES = default inner-tunnel
$(INSTALL) -d -m 750 $(R)$(raddbdir)/sites-enabled
$(INSTALL) -d -m 750 $(R)$(raddbdir)/mods-available
$(INSTALL) -d -m 750 $(R)$(raddbdir)/mods-enabled
+ $(INSTALL) -d -m 750 $(R)$(raddbdir)/policy.d
@echo "Creating/updating files in $(R)$(raddbdir)"; \
- for i in $(FILES) `find sites-available/ mods-available/ -type f -print | sed 's/.*~//;s/.*#.*//' `; do \
+ for i in $(FILES) `find sites-available/ mods-available/ policy.d/ -type f -print | sed 's/.*~//;s/.*#.*//' `; do \
[ ! -f $(R)$(raddbdir)/$$i ] && $(INSTALL) -m 640 $$i $(R)$(raddbdir)/$$i; \
if [ "`find $$i -newer $(R)$(raddbdir)/$$i`" ]; then \
echo "** $(R)$(raddbdir)/$$i"; \
+++ /dev/null
-# -*- text -*-
-##
-## policy.conf -- FreeRADIUS server configuration file.
-##
-## http://www.freeradius.org/
-## $Id$
-##
-
-#
-# Policies are virtual modules, similar to those defined in the
-# "instantate" section of radiusd.conf.
-#
-# Defining a policy here means that it can be referenced in multiple
-# places as a *name*, rather than as a series of conditions to match,
-# and actions to take.
-#
-# Policies are something like subroutines in a normal language, but
-# they cannot be called recursively. They MUST be defined in order.
-# If policy A calls policy B, then B MUST be defined before A.
-#
-policy {
- # We check for this prefix to determine whether the class
- # value was generated by this server. It should be changed
- # so that it is globally unique.
- class_value_prefix = 'ai:'
-
- #
- # Overload the default acct_unique module, it's not
- # smart enough.
- #
- acct_unique {
- #
- # If we have a class attribute in the format
- # 'auth_id:[0-9a-f]{32}' it'll have a local value
- # (defined by insert_acct_class), this ensures
- # uniqueness and suitability.
- #
- # We could just use the Class attribute as
- # Acct-Unique-Session-Id, but this may cause problems
- # with NAS that carry Class values across between
- # multiple linked sessions. So we rehash class with
- # Acct-Session-ID to provide a truely unique session
- # identifier.
- #
- # Using a Class/Session-ID combination is more robust
- # than using elements in the Accounting-Request,
- # which may be subject to change, such as
- # NAS-IP-Address, Client-IP-Address and
- # NAS-Port-ID/NAS-Port.
- #
- # This policy should ensure that session data is not
- # affected if NAS IP addresses change, or the client
- # roams to a different 'port' whilst maintaining its
- # initial authentication session (Common in a
- # wireless environment).
- #
- if("%{string:Class}" =~ /${policy.class_value_prefix}([0-9a-f]{32})/i) {
- update request {
- Acct-Unique-Session-Id := "%{md5:%{1}%{Acct-Session-ID}}"
- }
- }
-
- #
- # Not All devices respect RFC 2865 when dealing with
- # the class attribute, so be prepared to use the
- # older style of hashing scheme if a class attribute
- # is not included
- #
- else {
- update request {
- Acct-Unique-Session-Id := "%{md5:%{User-Name}%{Acct-Session-ID}%{NAS-IP-Address}%{NAS-Port-ID}%{NAS-Port}}"
- }
- }
- }
-
- #
- # Insert a (hopefully unique) value into class
- #
- insert_acct_class {
- update reply {
- Class = "${policy.class_value_prefix}%{md5:%t%I%{Packet-Src-Port}%{Packet-Src-IP-Address}%{NAS-IP-Address}%{Calling-Station-ID}%{User-Name}}"
- }
- }
-
- #
- # Forbid all EAP types. Enable this by putting "forbid_eap"
- # into the "authorize" section.
- #
- forbid_eap {
- if (EAP-Message) {
- reject
- }
- }
-
- #
- # Forbid all non-EAP types outside of an EAP tunnel.
- #
- permit_only_eap {
- if (!EAP-Message) {
- # We MAY be inside of a TTLS tunnel.
- # PEAP and EAP-FAST require EAP inside of
- # the tunnel, so this check is OK.
- # If so, then there MUST be an outer EAP message.
- if (!"%{outer.request:EAP-Message}") {
- reject
- }
- }
- }
-
- #
- # Remove Reply-Message from response if were doing EAP
- #
- # Be RFC 3579 2.6.5 compliant - EAP-Message and Reply-Message should
- # not be present in the same response.
- #
- remove_reply_message_if_eap {
- if(reply:EAP-Message && reply:Reply-Message) {
- update reply {
- Reply-Message !* ANY
- }
- }
- else {
- noop
- }
- }
-
- #
- # Split User-Name in NAI format (RFC 4282) into components
- #
- # This policy writes the Username and Domain portions of the
- # NAI into the Stripped-User-Name and Stripped-User-Domain
- # attributes.
- #
- # The regular expression to do this is not strictly compliant
- # with the standard, but it is not possible to write a
- # compliant regexp without perl style regular expressions (or
- # at least not a legible one).
- #
- nai_regexp = "^([^@]*)(@([-[:alnum:]]+\\.[-[:alnum:].]+))?$"
-
- split_username_nai {
- if(User-Name =~ /${policy.nai_regexp}/){
- update request {
- Stripped-User-Name := "%{1}"
- Stripped-User-Domain = "%{3}"
- }
-
- # If any of the expansions result in a null
- # string, the update section may return
- # something other than updated...
- updated
- }
- else {
- noop
- }
- }
-
- #
- # If called in post-proxy we modify the proxy-reply message
- #
- split_username_nai.post-proxy {
- if(proxy-reply:User-Name =~ /${policy.nai_regexp}/){
- update proxy-reply {
- Stripped-User-Name := "%{1}"
- Stripped-User-Domain = "%{3}"
- }
- updated
- }
- else {
- noop
- }
- }
-
- #
- # Example of forbidding all attempts to login via
- # realms.
- #
- deny_realms {
- if (User-Name =~ /@|\\/) {
- reject
- }
- }
-
- #
- # If you want the server to pretend that it is dead,
- # then use the "do_not_respond" policy.
- #
- do_not_respond {
- update control {
- Response-Packet-Type := Do-Not-Respond
- }
-
- handled
- }
-
- #
- # Filter the username
- #
- # Force some sanity on User-Name. This helps to avoid issues
- # issues where the back-end database is "forgiving" about
- # what constitutes a user name.
- #
- filter_username {
- #
- # reject mixed case
- # e.g. "UseRNaMe"
- #
- if (User-Name != "%{tolower:%{User-Name}}") {
- reject
- }
-
- #
- # reject all whitespace
- # e.g. "user@ site.com", or "us er", or " user", or "user "
- #
- if (User-Name =~ / /) {
- update reply {
- Reply-Message += "Rejected: Username contains whitespace"
- }
- reject
- }
-
- #
- # reject Multiple @'s
- # e.g. "user@site.com@site.com"
- #
- if(User-Name =~ /@(.+)?@/i ) {
- update reply {
- Reply-Message += "Rejected: Multiple @ in username"
- }
- reject
- }
-
- #
- # reject double dots
- # e.g. "user@site..com"
- #
- if (User-Name =~ /\\.\\./ ) {
- update reply {
- Reply-Message += "Rejected: Username comtains ..s"
- }
- reject
- }
-
- #
- # must have at least 1 string-dot-string after @
- # e.g. "user@site.com"
- #
- if (User-Name !~ /@(.+)\\.(.+)$/) {
- update reply {
- Reply-Message += "Rejected: Realm does not have at least one dot seperator"
- }
- reject
- }
-
- #
- # Realm ends with a dot
- # e.g. "user@site.com."
- #
- if (User-Name =~ /\\.$/) {
- update reply {
- Reply-Message += "Rejected: Realm ends with a dot"
- }
- reject
- }
-
- #
- # Realm begins with a dot
- # e.g. "user@.site.com"
- #
- if (User-Name =~ /@\\./) {
- update reply {
- Reply-Message += "Rejected: Realm begins with a dot"
- }
- reject
- }
- }
-
- #
- # The following policies are for the Chargeable-User-Identity
- # (CUI) configuration.
- #
- # The policies below can be called as just 'cui' (not
- # cui.authorize etc..) from the various config sections.
- #
-
- #
- # The client indicates it can do CUI by sending a CUI attribute
- # containing one zero byte
- #
- cui.authorize {
- update request {
- Chargeable-User-Identity:='\\000'
- }
- }
-
- #
- # Add a CUI attribute based on the User-Name, and a secret key
- # known only to this server.
- #
- cui.post-auth {
- if (FreeRadius-Proxied-To == 127.0.0.1) {
- if (outer.request:Chargeable-User-Identity) {
- update outer.reply {
- Chargeable-User-Identity:="%{md5:%{config:cui_hash_key}%{User-Name}}"
- }
- }
- }
- else {
- if (Chargeable-User-Identity) {
- update reply {
- Chargeable-User-Identity="%{md5:%{config:cui_hash_key}%{User-Name}}"
- }
- }
- }
- }
-
-
- #
- # If we had stored a CUI for the User, add it to the request.
- #
- cui.accounting {
- #
- # If the CUI isn't in the packet, see if we can find it
- # in the DB.
- #
- if (!Chargeable-User-Identity) {
- update request {
- Chargeable-User-Identity := "%{cui: SELECT cui FROM cui WHERE clientipaddress = '%{%{Packet-Src-IPv6-Address}:-%{Packet-Src-IP-Address}}' AND callingstationid = '%{Calling-Station-Id}' AND username = '%{User-Name}'}"
- }
- }
-
- #
- # If it exists now, then write out when we last saw
- # this CUI.
- #
- if (Chargeable-User-Identity && (Chargeable-User-Identity != "")) {
- cui
- }
- }
-
- #
- # If there is a CUI attribute in the reply, add it to the DB.
- #
- cui_updatedb {
- if (reply:Chargeable-User-Identity) {
- cui
- }
- }
-
- #
- # Normalize the MAC Addresses in the Calling/Called-Station-Id
- #
- mac-addr-regexp = ([0-9a-f]{2})[^0-9a-f]?([0-9a-f]{2})[^0-9a-f]?([0-9a-f]{2})[^0-9a-f]?([0-9a-f]{2})[^0-9a-f]?([0-9a-f]{2})[^0-9a-f]?([0-9a-f]{2})
-
- #
- # Add "rewrite_called_station_id" in the "authorize" and
- # "preacct" sections.
- #
- rewrite_called_station_id {
- if(Called-Station-Id =~ /^${policy.mac-addr-regexp}(:(.+))?$/i) {
- update request {
- Called-Station-Id := "%{tolower:%{1}-%{2}-%{3}-%{4}-%{5}-%{6}}"
- }
-
- # SSID component?
- if ("%{8}") {
- update request {
- Called-Station-SSID := "%{8}"
- }
- }
- updated
- }
- else {
- noop
- }
- }
-
- #
- # Add "rewrite_calling_station_id" in the "authorize" and
- # "preacct" sections.
- #
- rewrite_calling_station_id {
- if(Calling-Station-Id =~ /^${policy.mac-addr-regexp}$/i) {
- update request {
- Calling-Station-Id := "%{tolower:%{1}-%{2}-%{3}-%{4}-%{5}-%{6}}"
- }
- updated
- }
- else {
- noop
- }
- }
-
- # Assign compatibility data to request for sqlippool
- dhcp_sqlippool.post-auth {
-
-
- # Do some minor hacks to the request so that it looks
- # like a RADIUS request to the SQL IP Pool module.
- update request {
- User-Name = "DHCP-%{DHCP-Client-Hardware-Address}"
- Calling-Station-Id = "%{DHCP-Client-Hardware-Address}"
- NAS-IP-Address = "%{%{DHCP-Gateway-IP-Address}:-127.0.0.1}"
- Acct-Status-Type = Start
- }
-
- # Call the actual module
- dhcp_sqlippool
-
- # Convert Framed-IP-Address to DHCP, but only if we
- # actually allocated an address.
- if (ok) {
- update reply {
- DHCP-Your-IP-Address = "%{reply:Framed-IP-Address}"
- }
- }
- }
-}
--- /dev/null
+# We check for this prefix to determine whether the class
+# value was generated by this server. It should be changed
+# so that it is globally unique.
+class_value_prefix = 'ai:'
+
+#
+# Overload the default acct_unique module, it's not
+# smart enough.
+#
+acct_unique {
+ #
+ # If we have a class attribute in the format
+ # 'auth_id:[0-9a-f]{32}' it'll have a local value
+ # (defined by insert_acct_class), this ensures
+ # uniqueness and suitability.
+ #
+ # We could just use the Class attribute as
+ # Acct-Unique-Session-Id, but this may cause problems
+ # with NAS that carry Class values across between
+ # multiple linked sessions. So we rehash class with
+ # Acct-Session-ID to provide a truely unique session
+ # identifier.
+ #
+ # Using a Class/Session-ID combination is more robust
+ # than using elements in the Accounting-Request,
+ # which may be subject to change, such as
+ # NAS-IP-Address, Client-IP-Address and
+ # NAS-Port-ID/NAS-Port.
+ #
+ # This policy should ensure that session data is not
+ # affected if NAS IP addresses change, or the client
+ # roams to a different 'port' whilst maintaining its
+ # initial authentication session (Common in a
+ # wireless environment).
+ #
+ if("%{string:Class}" =~ /${policy.class_value_prefix}([0-9a-f]{32})/i) {
+ update request {
+ Acct-Unique-Session-Id := "%{md5:%{1}%{Acct-Session-ID}}"
+ }
+ }
+
+ #
+ # Not All devices respect RFC 2865 when dealing with
+ # the class attribute, so be prepared to use the
+ # older style of hashing scheme if a class attribute
+ # is not included
+ #
+ else {
+ update request {
+ Acct-Unique-Session-Id := "%{md5:%{User-Name}%{Acct-Session-ID}%{NAS-IP-Address}%{NAS-Port-ID}%{NAS-Port}}"
+ }
+ }
+}
+
+#
+# Insert a (hopefully unique) value into class
+#
+insert_acct_class {
+ update reply {
+ Class = "${policy.class_value_prefix}%{md5:%t%I%{Packet-Src-Port}%{Packet-Src-IP-Address}%{NAS-IP-Address}%{Calling-Station-ID}%{User-Name}}"
+ }
+}
+
--- /dev/null
+#
+# Split User-Name in NAI format (RFC 4282) into components
+#
+# This policy writes the Username and Domain portions of the
+# NAI into the Stripped-User-Name and Stripped-User-Domain
+# attributes.
+#
+# The regular expression to do this is not strictly compliant
+# with the standard, but it is not possible to write a
+# compliant regexp without perl style regular expressions (or
+# at least not a legible one).
+#
+nai_regexp = "^([^@]*)(@([-[:alnum:]]+\\.[-[:alnum:].]+))?$"
+
+split_username_nai {
+ if(User-Name =~ /${policy.nai_regexp}/){
+ update request {
+ Stripped-User-Name := "%{1}"
+ Stripped-User-Domain = "%{3}"
+ }
+
+ # If any of the expansions result in a null
+ # string, the update section may return
+ # something other than updated...
+ updated
+ }
+ else {
+ noop
+ }
+}
+
+#
+# If called in post-proxy we modify the proxy-reply message
+#
+split_username_nai.post-proxy {
+ if(proxy-reply:User-Name =~ /${policy.nai_regexp}/){
+ update proxy-reply {
+ Stripped-User-Name := "%{1}"
+ Stripped-User-Domain = "%{3}"
+ }
+ updated
+ }
+ else {
+ noop
+ }
+}
+
+#
+# Normalize the MAC Addresses in the Calling/Called-Station-Id
+#
+mac-addr-regexp = ([0-9a-f]{2})[^0-9a-f]?([0-9a-f]{2})[^0-9a-f]?([0-9a-f]{2})[^0-9a-f]?([0-9a-f]{2})[^0-9a-f]?([0-9a-f]{2})[^0-9a-f]?([0-9a-f]{2})
+
+#
+# Add "rewrite_called_station_id" in the "authorize" and
+# "preacct" sections.
+#
+rewrite_called_station_id {
+ if(Called-Station-Id =~ /^${policy.mac-addr-regexp}(:(.+))?$/i) {
+ update request {
+ Called-Station-Id := "%{tolower:%{1}-%{2}-%{3}-%{4}-%{5}-%{6}}"
+ }
+
+ # SSID component?
+ if ("%{8}") {
+ update request {
+ Called-Station-SSID := "%{8}"
+ }
+ }
+ updated
+ }
+ else {
+ noop
+ }
+}
+
+#
+# Add "rewrite_calling_station_id" in the "authorize" and
+# "preacct" sections.
+#
+rewrite_calling_station_id {
+ if(Calling-Station-Id =~ /^${policy.mac-addr-regexp}$/i) {
+ update request {
+ Calling-Station-Id := "%{tolower:%{1}-%{2}-%{3}-%{4}-%{5}-%{6}}"
+ }
+ updated
+ }
+ else {
+ noop
+ }
+}
+
--- /dev/null
+#
+# If you want the server to pretend that it is dead,
+# then use the "do_not_respond" policy.
+#
+do_not_respond {
+ update control {
+ Response-Packet-Type := Do-Not-Respond
+ }
+
+ handled
+}
+
--- /dev/null
+#
+# The following policies are for the Chargeable-User-Identity
+# (CUI) configuration.
+#
+# The policies below can be called as just 'cui' (not
+# cui.authorize etc..) from the various config sections.
+#
+
+#
+# The client indicates it can do CUI by sending a CUI attribute
+# containing one zero byte
+#
+cui.authorize {
+ update request {
+ Chargeable-User-Identity:='\\000'
+ }
+}
+
+#
+# Add a CUI attribute based on the User-Name, and a secret key
+# known only to this server.
+#
+cui.post-auth {
+ if (FreeRadius-Proxied-To == 127.0.0.1) {
+ if (outer.request:Chargeable-User-Identity) {
+ update outer.reply {
+ Chargeable-User-Identity:="%{md5:%{config:cui_hash_key}%{User-Name}}"
+ }
+ }
+ }
+ else {
+ if (Chargeable-User-Identity) {
+ update reply {
+ Chargeable-User-Identity="%{md5:%{config:cui_hash_key}%{User-Name}}"
+ }
+ }
+ }
+}
+
+
+#
+# If we had stored a CUI for the User, add it to the request.
+#
+cui.accounting {
+ #
+ # If the CUI isn't in the packet, see if we can find it
+ # in the DB.
+ #
+ if (!Chargeable-User-Identity) {
+ update request {
+ Chargeable-User-Identity := "%{cui: SELECT cui FROM cui WHERE clientipaddress = '%{%{Packet-Src-IPv6-Address}:-%{Packet-Src-IP-Address}}' AND callingstationid = '%{Calling-Station-Id}' AND username = '%{User-Name}'}"
+ }
+ }
+
+ #
+ # If it exists now, then write out when we last saw
+ # this CUI.
+ #
+ if (Chargeable-User-Identity && (Chargeable-User-Identity != "")) {
+ cui
+ }
+}
+
+#
+# If there is a CUI attribute in the reply, add it to the DB.
+#
+cui_updatedb {
+ if (reply:Chargeable-User-Identity) {
+ cui
+ }
+}
+
--- /dev/null
+# Assign compatibility data to request for sqlippool
+dhcp_sqlippool.post-auth {
+
+
+ # Do some minor hacks to the request so that it looks
+ # like a RADIUS request to the SQL IP Pool module.
+ update request {
+ User-Name = "DHCP-%{DHCP-Client-Hardware-Address}"
+ Calling-Station-Id = "%{DHCP-Client-Hardware-Address}"
+ NAS-IP-Address = "%{%{DHCP-Gateway-IP-Address}:-127.0.0.1}"
+ Acct-Status-Type = Start
+ }
+
+ # Call the actual module
+ dhcp_sqlippool
+
+ # Convert Framed-IP-Address to DHCP, but only if we
+ # actually allocated an address.
+ if (ok) {
+ update reply {
+ DHCP-Your-IP-Address = "%{reply:Framed-IP-Address}"
+ }
+ }
+}
+
--- /dev/null
+#
+# Forbid all EAP types. Enable this by putting "forbid_eap"
+# into the "authorize" section.
+#
+forbid_eap {
+ if (EAP-Message) {
+ reject
+ }
+}
+
+#
+# Forbid all non-EAP types outside of an EAP tunnel.
+#
+permit_only_eap {
+ if (!EAP-Message) {
+ # We MAY be inside of a TTLS tunnel.
+ # PEAP and EAP-FAST require EAP inside of
+ # the tunnel, so this check is OK.
+ # If so, then there MUST be an outer EAP message.
+ if (!"%{outer.request:EAP-Message}") {
+ reject
+ }
+ }
+}
+
+#
+# Remove Reply-Message from response if were doing EAP
+#
+# Be RFC 3579 2.6.5 compliant - EAP-Message and Reply-Message should
+# not be present in the same response.
+#
+remove_reply_message_if_eap {
+ if(reply:EAP-Message && reply:Reply-Message) {
+ update reply {
+ Reply-Message !* ANY
+ }
+ }
+ else {
+ noop
+ }
+}
+
+#
+# Example of forbidding all attempts to login via
+# realms.
+#
+deny_realms {
+ if (User-Name =~ /@|\\/) {
+ reject
+ }
+}
+
+#
+# Filter the username
+#
+# Force some sanity on User-Name. This helps to avoid issues
+# issues where the back-end database is "forgiving" about
+# what constitutes a user name.
+#
+filter_username {
+ #
+ # reject mixed case
+ # e.g. "UseRNaMe"
+ #
+ if (User-Name != "%{tolower:%{User-Name}}") {
+ reject
+ }
+
+ #
+ # reject all whitespace
+ # e.g. "user@ site.com", or "us er", or " user", or "user "
+ #
+ if (User-Name =~ / /) {
+ update reply {
+ Reply-Message += "Rejected: Username contains whitespace"
+ }
+ reject
+ }
+
+ #
+ # reject Multiple @'s
+ # e.g. "user@site.com@site.com"
+ #
+ if(User-Name =~ /@(.+)?@/i ) {
+ update reply {
+ Reply-Message += "Rejected: Multiple @ in username"
+ }
+ reject
+ }
+
+ #
+ # reject double dots
+ # e.g. "user@site..com"
+ #
+ if (User-Name =~ /\\.\\./ ) {
+ update reply {
+ Reply-Message += "Rejected: Username comtains ..s"
+ }
+ reject
+ }
+
+ #
+ # must have at least 1 string-dot-string after @
+ # e.g. "user@site.com"
+ #
+ if (User-Name !~ /@(.+)\\.(.+)$/) {
+ update reply {
+ Reply-Message += "Rejected: Realm does not have at least one dot seperator"
+ }
+ reject
+ }
+
+ #
+ # Realm ends with a dot
+ # e.g. "user@site.com."
+ #
+ if (User-Name =~ /\\.$/) {
+ update reply {
+ Reply-Message += "Rejected: Realm ends with a dot"
+ }
+ reject
+ }
+
+ #
+ # Realm begins with a dot
+ # e.g. "user@.site.com"
+ #
+ if (User-Name =~ /@\\./) {
+ update reply {
+ Reply-Message += "Rejected: Realm begins with a dot"
+ }
+ reject
+ }
+}
+
######################################################################
#
-# Policies that can be applied in multiple places are listed
-# globally. That way, they can be defined once, and referred
-# to multiple times.
+# Policies are virtual modules, similar to those defined in the
+# "instantate" section above.
+#
+# Defining a policy in one of the policy.d files means that it can be
+# referenced in multiple places as a *name*, rather than as a series of
+# conditions to match, and actions to take.
+#
+# Policies are something like subroutines in a normal language, but
+# they cannot be called recursively. They MUST be defined in order.
+# If policy A calls policy B, then B MUST be defined before A.
#
######################################################################
-$INCLUDE policy.conf
+policy {
+ $INCLUDE policy.d/
+}
######################################################################
#