# -*- 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 { # spaces at the start: reject if (User-Name =~ /^ /) { reject } # spaces at the end: reject if (User-Name =~ / $$/) { reject } # Mixed case: reject if (User-Name != "%{tolower:%{User-Name}}") { 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 control { Chargable-User-Identity := "%{cui: SELECT cui FROM cui WHERE clientipaddress = '%{Client-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.postauth { # 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}" } } } }