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