#
#	Example of forbidding all attempts to login via
#	realms.
#
deny_realms {
	if (&User-Name && (&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 {
	if (&User-Name) {
		#
		#  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 request {
				&Module-Failure-Message += 'Rejected: User-Name contains whitespace'
			}
			reject
		}

		#
		#  reject Multiple @'s
		#  e.g. "user@site.com@site.com"
		#
		if (&User-Name =~ /@[^@]*@/ ) {
			update request {
				&Module-Failure-Message += 'Rejected: Multiple @ in User-Name'
			}
			reject
		}

		#
		#  reject double dots
		#  e.g. "user@site..com"
		#
		if (&User-Name =~ /\.\./ ) {
			update request {
				&Module-Failure-Message += 'Rejected: User-Name contains multiple ..s'
			}
			reject
		}

		#
		#  must have at least 1 string-dot-string after @
		#  e.g. "user@site.com"
		#
		if ((&User-Name =~ /@/) && (&User-Name !~ /@(.+)\.(.+)$/))  {
			update request {
				&Module-Failure-Message += 'Rejected: Realm does not have at least one dot separator'
			}
			reject
		}

		#
		#  Realm ends with a dot
		#  e.g. "user@site.com."
		#
		if (&User-Name =~ /\.$/)  {
			update request {
				&Module-Failure-Message += 'Rejected: Realm ends with a dot'
			}
			reject
		}

		#
		#  Realm begins with a dot
		#  e.g. "user@.site.com"
		#
		if (&User-Name =~ /@\./)  {
			update request {
				&Module-Failure-Message += 'Rejected: Realm begins with a dot'
			}
			reject
		}
	}
}

#
#	Filter the User-Password
#
#  Some equipment sends passwords with embedded zeros.
#  This policy filters them out.
#
filter_password {
	if (&User-Password && \
	   (&User-Password != "%{string:User-Password}")) {
		update request {
			&Tmp-String-0 := "%{string:User-Password}"
			&User-Password := "%{string:Tmp-String-0}"
			&Tmp-String-0 !* ""
		}
	 }
}

filter_inner_identity {
	#
	#  No names, reject.
	#
	if (!&outer.request:User-Name || !&User-Name) {
		update request {
			Module-Failure-Message = "User-Name is required for tunneled authentication"
		}
		reject
	}

	#
	#  Do detailed checks only if the inner and outer
	#  NAIs are different.
	#
	#  If the NAIs are the same, it violates user privacy,
	#  but is allowed.
	#
	if (&outer.request:User-Name != &User-Name) {
		#
		#  Get the outer realm.
		#
		if (&outer.request:User-Name =~ /@([^@]+)$/) {
			update request {
				Outer-Realm-Name = "%{1}"
			}

			#
			#  When we have an outer realm name, the user portion
			#  MUST either be empty, or begin with "anon".
			#
			#  We don't check for the full "anonymous", because
			#  some vendors don't follow the standards.
			#
			if (&outer.request:User-Name !~ /^(anon|@)/) {
				update request {
					Module-Failure-Message = "User-Name is not anonymized"
				}
				reject
			}
		}

		#
		#  There's no outer realm.  The outer NAI is different from the
		#  inner NAI.  The User-Name MUST be anonymized.
		#
		#  Otherwise, you could log in as outer "bob", and inner "doug",
		#  and we'd have no idea which one was correct.
		#
		elsif (&outer.request:User-Name !~ /^anon/) {
			update request {
				Module-Failure-Message = "User-Name is not anonymized"
			}
			reject
		}

		#
		#  Get the inner realm.
		#
		if (&User-Name =~ /@([^@]+)$/) {
			update request {
				Inner-Realm-Name = "%{1}"
			}

			#
			#  Note that we do EQUALITY checks for realm names.
			#  There is no simple way to do case insensitive checks
			#  on internationalized domain names.  There is no reason
			#  to allow outer "anonymous@EXAMPLE.COM" and inner
			#  "user@example.com".  The user should enter the same
			#  realm for both identities.
			#
			#  If the inner realm isn't the same as the outer realm,
			#  the inner realm MUST be a subdomain of the outer realm.
			#
			if (&Outer-Realm-Name && \
			    (&Inner-Realm-Name != &Outer-Realm-Name) && \
			    (&Inner-Realm-Name !~ /\.%{Outer-Realm-Name}$/)) {
				update request {
					Module-Failure-Message = "Inner realm '%{Inner-Realm-Name}' and outer realm '%{Outer-Realm-Name}' are not from the same domain."
				}
				reject
			}

			#
			#  It's OK to have an inner realm and no outer realm.
			#
			#  That won't work for roaming, but the local RADIUS server
			#  can still authenticate the user.
			#
		}

		#
		#  It's OK to have an outer realm and no inner realm.
		#
		#  It will work for roaming, and the local RADIUS server
		#  can authenticate the user without the realm.
		#
	}
}

#
#  This policy should be applied for all eduroam proxying
#
filter_username_nai {
	if (&User-Name && &EAP-Message && (&Called-Station-Id =~ /.*\:eduroam$/)) {
		#
		#  The User-Name must have exactly one "@" character.
		#
		if (&User-Name !~ /.*@.*/ ) {
			update request {
				&Module-Failure-Message += 'Rejected: No @ in User-Name'
			}
			reject
		}

		#
		#  The User-Name must match the 'official' eduroam
		#  regular expression.
		#
		if (&User-Name !~ /@[-a-zA-Z0-9_]+(\.[-a-zA-Z0-9_]+)+$/ ) {
			update request {
				&Module-Failure-Message += 'Rejected: Invalid eduroam User-Name format'
			}
			reject
		}
	}
}
