# -*- 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 {
	#
	#	Forbid all EAP types.
	#
	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
			}
		}
	}

	#
	#	Forbid 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
	}

	#
	#  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 =~ /@.*@/ ) {
			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 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_postauth {
		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 there is a CUI attribute in the reply, add it to the DB.
	#
	cui_updatedb {
		if (reply:Chargeable-User-Identity) {
			cui
		}
	}

	#
	#  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 = '%{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
		}
	}

	#
	#  Normalize the MAC Addresses in the Calling/Called-Station-Id
	#
	mac-addr = ([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) && "%{Called-Station-Id}" =~ /^%{config:policy.mac-addr}(:(.+))?$/i) {
			update request {
				Called-Station-Id := "%{tolower:%{1}-%{2}-%{3}-%{4}-%{5}-%{6}}"
			}

			# SSID component?
			if ("%{8}") {
				update request {
					Called-Station-Id := "%{Called-Station-Id}:%{8}"
				}
			}
			updated
		}
		else {
			noop
		}
	}

	#  Add "rewrite.calling_station_id" in the "authorize" and "preacct"
	#  sections.
	rewrite.calling_station_id {
		if((Calling-Station-Id) && "%{Calling-Station-Id}" =~ /^%{config:policy.mac-addr}$/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
		#
		#  Uncomment this in order to really call it!
#		dhcp_sqlippool
		fail

		#  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}"
			}
		}
	}
}
