APM Kerberos Auth or fallback to another authentication method
Problem this snippet solves: This iRule can be used when it is required to offer both Kerberos authentication and for example SAML or another authentication method in a mixed environment for devices that are domain joined and devices that are not domain joined. This iRule uses javascript and HTML5 Web Workers to determine if the browser can successfully authenticate by using Kerberos or will need to fallback to another authentication method. I've been testing this iRule with Internet Explorer, Edge, Firefox and Chrome. All these browsers seem to be working fine. Only Chrome seems to do things a bit differently and is showing a login prompt for a split second, but it's working. How to use this snippet: The screenshot below shows an example of an Access Policy that uses either Kerberos or SAML authentication. The first agent in the policy is an 'Empty Agent' which will read the session.custom.domainjoined variable to determine which authentication method to use. The session.custom.domainjoined variable is set by the kerberos_auth_or_fallback_auth iRule. Tested this on version: 13.0 Link to iRule https://github.com/nvansluis/f5.kerberos_auth_or_fallback_auth720Views1like0CommentsAPM Customization: Password Change Validation
Problem this snippet solves: When an user is prompted to change the password, the user will be instructed to choose a password that matches your password policy. See the screenshots below. This updated version uses a progress bar instead of showing the requirements directly on the page. This should give some more protection against shoulder surfing. Please note that this code snippet will only customize the look-and-feel of the APM portal. It will *not* enforce a password security policy. The password policy should be enforced on the backend authentication server (active directory). Screenshot 1 - A password requirement progress bar is shown. Screenshot 2 - While typing the progress bar will grow towards 100% if the required character types are being used. Screenshot 3 - If you wonder why your new password doesn't meet the requirements, you can move your mouse over the progress bar to find out why. Screenshot 4 - When you meet the requirements, you must retype the password for validation. The progress bar will become green when both passwords match. Move your mouse over the progress bar to get more info. Screenshot 5 - All set, let's change the password. How to use this snippet: Use the F5 APM Advanced Customization Editor and reference this script from Common > footer.inc For example: Tested this on version: 13.0 Link to iRule https://github.com/nvansluis/f5.password_change_validation1.1KViews1like3CommentsTransparent Kerberos Authentication and APM fallback authentication
Problem this snippet solves: This iRule can be used when it is required to offer both Kerberos authentication (transparent, non-APM) and for example SAML or another APM authentication method in a mixed environment for devices that are domain joined and devices that are not domain joined. This iRule uses javascript and HTML5 Web Workers to determine if the browser can successfully authenticate by using Kerberos or will need to fallback to another authentication method. I've been testing this iRule with Internet Explorer, Edge, Firefox and Chrome. All these browsers seem to be working fine. Only Chrome seems to do things a bit differently and is showing a login prompt for a split second, but it's working. How to use this snippet: Create a Virtual Server that delivers a webserver that uses Kerberos Authentication. Create APM Access Policy that will perform the fallback authentication. Add this iRule to the Virtual Server that holds the APM access policy to perform the fallback authentication. Tested this on version: 13.0 Location of iRule https://github.com/nvansluis/f5.transparent_kerberos_auth_or_apm_authentication811Views0likes2CommentsBypass Azure Login Page by adding a login hint in the SAML Request
Problem this snippet solves: Enhance the login experience between F5 (SAML SP) and Azure (SAML IDP) by injecting the "email address" as a login hint on behalf of the user. This enhances the user experience because it allows to bypass the Azure Login Page and avoids the user to type two times his login/email address. Example of use Your application need to be accessed by both "domain users" and "federated users". Your application is protected by the F5 APM with a "Login Page" that asks for the user "email address". Based on the "email address" value you determine the domain: if the user is a "domain user", you authenticate him on the local directory (AD Auth, LDAP Auth or ...) if the user is a "federated user" (such as xxx@gmail.com), you send him to the Azure IDP that will manage all federated access This snippet is particularly interesting for the "federated user" scenario because: without this code, a "federated user" will need to type his "login" twice. First time on "F5 Login Page" and the second time on "Azure Login Page" with this code, a "federated user" will need to type his "login" only on the F5 Login Page How to use this snippet: Go to "Access > Federation > SAML Service Provider > External IDP Connectors" and edit the "External IdP Connectors" object that match with the Azure IDP app. On the "Single Sign On Service Settings" add at the end of the "Single Sign On Service URL" the following string "?login_hint=" as shown in the picture below. The string "?login_hint=" is added here only to be able to uniquely identify it later by the iRule and replaced it. 3. Finally, apply the iRule below on the VS that has the Access Policy enabled and for which the SAML SP role is attributed and is binded to the Azure IDP application. The iRule will simply catch the "Single Sign On Service URL" and replace it with "?login_hint=xxxx@gmail.com". Code : when CLIENT_ACCEPTED { ACCESS::restrict_irule_events disable } when HTTP_RESPONSE_RELEASE { if { [string tolower [HTTP::header value "Location"]] contains "/saml2/?login_hint="} { set user_login [ACCESS::session data get "session.logon.last.mail"] #log local0. "Before adding the hint [HTTP::header value "Location"]" set locationWithoutHint "?login_hint=" set locationWithHint "?login_hint=$user_login" HTTP::header replace Location [string map -nocase "${locationWithoutHint} ${locationWithHint}" [HTTP::header Location]] #log local0. "After adding the hint [HTTP::header value "Location"]" } } Tested this on version: No Version Found4.8KViews1like5CommentsAPM (& LTM) Session & Cookie Information - Chrome Extension
Problem this snippet solves: If you've ever troubleshooted APM Portal Access issues, you know how annoying it can be to find the decoded internal url. Note: This extension has been updated as more of an APM and LTM extension as opposed to just an APM one. This chrome extension seeks to make that quick and easy by showing the decoded information. It will also display the cookies for that site (including the ever-useful MRHSession and LastMRH_Session cookies) and allows you to delete cookies directly from the extension (useful for testing session timeout if you delete the MRHSession cookie). Version History 1.1 - Initial version Includes APM portal access decoded url information 1.2 - 2016.04.01 Added list of cookies associated with the current site (shows cookie name, domain and value) 1.3 - 2016.04.25 Added ability to delete cookies from the extension for the site (Known Issue: if you have multiple cookies with the same name that match the page, deleting one will delete all of them) Added decoded BIG-IP persistence cookie value in parenthesis to the list for quicker reference 1.4 - 2016.06.30 Rebuilt the popup page using AngularJS Introduced (but still disabled) options page and client-side functionality (will need iRules development as well) 1.5 - 2016.09.07 Enabled the options page again, and finished code to allow the extension to add a header to requests on specific domains (user specified) 1.5.1 - 2016.09.18 Updated the icon, and removed APM and replaced with debugging icon since this has morphed to APM and LTM usefulness 1.6 - 2016.12.19 Now enables the extension when it determines a persistence cookie (based on value format) Added a link that will popup APM session details (when management url specified in options page) (Note: must be logged into the management GUI already or else it won't redirect properly). Used alongside my APM Tampermonkey script you can see the session variables as well as the session detail 1.7 - 2017.09.03 Added local tracking of sites that appear to use F5 BigIP How to use this snippet: As Chrome doesn't really like unpublished extensions, and it's not in the Chrome App Store (yet), you'll have to install the extension in Developer Mode. Instructions Navigate to chrome://extensions Ensure that the Developer mode checkbox is enabled Sub-Method 1: Load unpacked extension (preferred method) Download all the code from the Github repository Click the Load unpacked extension button and select the src folder Sub-Method 2: Load the crx file (may not always be current) Download the crx from the Github repository From the file system, click and drag the .crx file onto the extension page to install it Code : https://github.com/jangins101/F5-APM-Session-Information Tested this on version: 11.5767Views0likes0CommentsAPM Full Step Up Authentication
Problem this snippet solves: By default, APM is not able to handle several authentication during a session. Once you are logged in, it’s finished, you can’t ask for authentication again. Since v12.1.0, we can see a new feature in EA called “Step-up Authentication” and the introduction of subroutines that is currently limited to ldap authentication or a confirm box. The irule and configuration below allow the administrator to define 2 levels of authentication based on URIs. The concept can be extended to have multiple authentication levels. This concept can be extended to define several Level of authentication. You can also change the element that trigger the additionnal authentication process. How to use this snippet: Installation irule To make it works, you need to install the irule on the Virtual Server that publish your application with APM authentication. datagroup You need to create a datagroup of string type. This dg must contains http path that need an additional authentication step. The dg is named loa3_uri in the irule example. access profile If you already have an existing access profile, you will need to modify it and include some additionnal configuration in your VPE. If you have no access profile, you can starts building your own based on the description we provide below. Scenarios 1) User try to reach strong uri after first authentication process In this scenario, the user first authenticate using a standard authentication mecanism. Once authenticated, if the user request content that is behing strong uris, the user restart an authentication process in the "Strong Auth" and "Already Auth" branch of the VPE. 2) User try to reach strong uri during the first authentication process If the user try to access a strong uri on its first attempt, he will need to complete the full authentication process. Then, he can access every part of the web application without any additional prompt. Special considerations Client certificate Authentication You may need to use Client certificate authentication as a primary factor or second factor. We highly recommend to use "SSl on-demand authentication" if you need it as primary factor. Client Certificate is not supported as a second factor, you need to use SSl on-demand authentication. WebSSO When first authentication has already been allowed and the user try to access a protected uri, the system will invite the user to complete the new authentication (second factor). This process will restart a webSSO action on the backend. Basic, NTLM and Kerberos webSSO have been tested with success. Configuring the Visual Policy Editor The printscreen below is a minimal Visual Policy Editor used to make Step up Authentication works properly : Strong Auth The strong Auth block is an "Empty Action" with two branch. The branch named "Strong" contains the following condition : expr { [mcget {session.server.landinguri}] starts_with "/strong" || [mcget {session.custom.last.strong}] == 1 } We check that the uri starts with strong (used in scenario 1) or if a custom variable is set to 1 (second scenario) Already Auth This is an empty action with two branch. The branch named "yes" contains the following expression : expr { [mcget {session.custom.last.authresult}] contains "true" } 2-factor Ending session.custom.last.authtype variable must be set to 1 session.policy.result.redirect.url must be changed. The session.server.landinguri contains the true origin uri. To set this variable, you must use the tcl script below : proc urldecode str { variable map variable alphanumeric a-zA-Z0-9 for {set i 0} {$i <= 256} {incr i} { set c [format %c $i] if {![string match \[$alphanumeric\] $c]} { set map($c) %[format %.2x $i] } } array set map { " " + \n %0d%0a } set str [string map [list + { } "\\" "\\\\"] $str] regsub -all -- {%([A-Fa-f0-9][A-Fa-f0-9])} $str {\\u00\1} str return [subst -novar -nocommand $str] } set decoded_uri [urldecode [string range [mcget {session.server.landinguri}] [expr { [string last = [mcget {session.server.landinguri}]] + 1 }] end]] return $decoded_uri Full strong Ending session.custom.last.authtype variable must be set to 1 Standard Ending session.custom.last.authtype variable must be set to 0 Session variables The following variables can be used in the 2-factor section of the Visual Policy Editor : session.custom.last.username session.custom.last.password Features 2-step authentication Retrieve username and password from first authentication Encrypt Session1 cookie to avoid session Hijacking External links Github : https://github.com/e-XpertSolutions/f5 Code : when RULE_INIT { # to be changed prior to any publishing set passphrase "hEuoYjmFUpB4PcpO3bUdQtLP4ic7jjm" } when HTTP_REQUEST { if { [HTTP::cookie exists MRHSession] and [ACCESS::session exists -state_allow -sid [HTTP::cookie MRHSession]] } { set strong_auth [ACCESS::session data get session.custom.last.authtype] if { [class match [HTTP::path] starts_with loa3_uri] and $strong_auth == 0 } { HTTP::cookie encrypt "MRHSession" $passphrase HTTP::respond 302 noserver "Location" "/strong?return_url=[URI::encode [HTTP::uri]]" "Cache-Control" "no-cache, must-revalidate" Set-Cookie "MRHSession=deleted;expires=Thu, 01-Jan-1970 00:00:10 GMT;path=/" Set-Cookie "LastMRH_Session=deleted;expires=Thu, 01-Jan-1970 00:00:10 GMT;path=/" Set-Cookie "Session1=[HTTP::cookie MRHSession];path=/" } } } when ACCESS_SESSION_STARTED { # decrypt Session1 cookie value set decrypted [HTTP::cookie decrypt "Session1" $passphrase] if { [HTTP::cookie exists Session1] and [ACCESS::session exists -state_allow -sid $decrypted] } { ## section : retrieve session variables from the first session ACCESS::session data set session.custom.last.username [ACCESS::session data get session.logon.last.username -sid $decrypted] ACCESS::session data set session.custom.last.password [ACCESS::session data get session.logon.last.password -sid $decrypted] ## End section ACCESS::session data set session.custom.last.authresult "true" # remove the first created session during standard authentication to avoid multiple active sessions ACCESS::session remove -sid $decrypted } elseif { [class match [HTTP::path] starts_with loa3_uri] } { ACCESS::session data set session.custom.last.strong 1 } } Tested this on version: 11.51.3KViews0likes7CommentsProvision IOS profile for Exchange ActiveSync with client certificate authentication
Problem this snippet solves: If you need to use client certificate authentication for ActiveSync services on IOS, you need to deploy custom profiles through a Mobile Device Management. MDM is maybe a little bit too much to achieve only this feature. The irule below provide necessary materials to provision a certificate and an exchange profile on IOS. Tested successfully on IOS 9. We use SCEP protocol for certificate enrollment. How to use this snippet: You need to define a Virtual Server and an access profile to publish ActiveSync. Then, you need to assign the irule on the Virtual Server. The certificate is retrieved using SCEP protocol on a Microsoft ADCS 2012 R2. The SCEP url should be changed in the Exchange payload. We configured APM to protect the access to this service and retrieve attributes from Active Directory but you can change the irule code to retrieve information and protect the service in a different manner. When a user reach /enroll uri with Safari browser, the provisioning process starts. /!\ I provide an IOS payload as example, but you need to modify it to fit your environment and save it as an ifile. Settings that need to be changed in the xml payload : <string>HOST.DOMAIN.COM</string> : Activesync FQDN <string>DOMAIN-Issuer-CA</string> : Issuing CA Name (if exists otherwise related code should be removed) <data>CERTIFICATE</data> : X.509 certificate in Base64 for Issuing CA <string>DOMAIN-Root-CA</string> : Root CA Name <data>CERTIFICATE</data> : X.509 certificate in Base64 for the Root CA <string>DOMAIN</string> : Organization name to be present in the user certificate <string>http://scep.domain.com/scep</string> : SCEP url External links Github : github.com/e-XpertSolutions/f5 Code : 68654 Tested this on version: 11.5789Views0likes1CommentAPM/Analytics - Log click on webtop resources [serverside]
Problem this snippet solves: When providing VPN SSL portal to the client, you publish several applications like Portal access, Remote Desktop, Webtop links, SAML Resources, Citrix/Vmware VDI, etc. APM log access to the webtop, once connected, there is no more visibility on who click on which icon. We provide Visibility and Analytics capabilities through a simple irule. How to use this snippet: Installation You just need to put this irule in the Virtual Server configuration that handle your access profile. Logging information Clicks are logged in the local0 (ltm logs). You can see below examples : virtual=/Common/test, apm=1239853, user=testuser, resource_type=portal, resourcename=owa virtual=/Common/test, apm=1239853, user=testuser, resource_type=remote_desktop, resourcename=ActiveDirectory Features You can currently log the following application types : Portal access Remote Desktop access The irule provide the additional features : Decode Portal access uri logging of username, apm session and resource name Credits Inspired from an original irule in a reply from Kevin Stewart : Logging for Portal Access External links Github : github.com/e-XpertSolutions/f5 Code : when ACCESS_ACL_ALLOWED { switch -glob [HTTP::uri] { "*resourcetype=remote_desktop*" { # # Basic logging. Remote or local logging settings can be configured # # log local0. "virtual=[virtual], apm=[string range [ACCESS::session sid] [expr [string length [ACCESS::session sid]] - 10] end], user=[ACCESS::session data get session.logon.last.username], resourcetype=remote_desktop, [findstr [HTTP::uri] "resourcename=" 0 "\%"]" # # ACCESS logging before v13.x # log -noname accesscontrol.local1.notice "$static::ACCESS_LOG_PREFIX /Common/ap-ad-auth:Common:$session: virtual=[virtual], apm=[string range [ACCESS::session sid] [expr [string length [ACCESS::session sid]] - 10] end], user=[ACCESS::session data get session.logon.last.username], resourcetype=remote_desktop, [findstr [HTTP::uri] "resourcename=" 0 "\%"]" # # ACCESS::log is available in v13.x. Log saved in Access report too. # # ACCESS::log accesscontrol.notice "virtual=[virtual], apm=[string range [ACCESS::session sid] [expr [string length [ACCESS::session sid]] - 10] end], user=[ACCESS::session data get session.logon.last.username], resourcetype=remote_desktop, [findstr [HTTP::uri] "resourcename=" 0 "\%"]" } "*f5-w-*" { catch { set resource [binary format H* [findstr [HTTP::uri] "/f5-w-" 6 "\$\$"]] if { [table lookup -subtable PORTALACCESS "[ACCESS::session sid]:[ACCESS::session data get session.logon.last.username]:$resource"] eq "" } { table set -subtable PORTALACCESS "[ACCESS::session sid]:[ACCESS::session data get session.logon.last.username]:$resource" [clock format [clock seconds] -format %Y%m%d-%H%M%S] 3600 # # Basic logging. Remote or local logging settings can be configured # # log local0. "virtual=[virtual], apm=[string range [ACCESS::session sid] [expr [string length [ACCESS::session sid]] - 10] end], user=[ACCESS::session data get session.logon.last.username], resource_type=portal, resourcename=$resource" # # ACCESS logging before v13.x # log -noname accesscontrol.local1.notice "$static::ACCESS_LOG_PREFIX /Common/ap-ad-auth:Common:$session: virtual=[virtual], apm=[string range [ACCESS::session sid] [expr [string length [ACCESS::session sid]] - 10] end], user=[ACCESS::session data get session.logon.last.username], resource_type=portal, resourcename=$resource" # # ACCESS::log is available in v13.x. Log saved in Access report too. # # ACCESS::log accesscontrol.notice "virtual=[virtual], apm=[string range [ACCESS::session sid] [expr [string length [ACCESS::session sid]] - 10] end], user=[ACCESS::session data get session.logon.last.username], resource_type=portal, resourcename=$resource" } } } } } Tested this on version: 11.5452Views0likes4CommentsAPM Google Authenticator HTTP API
Problem this snippet solves: George Watkins already shared various codes allowing to authenticate users with Google authenticator on APM by executing VPE irule event. This code create a HTTP API that respond if authenticator code is valid and can be used as an HTTP Auth server by APM. 3 URLs are included on this API : /authenticator : Authenticate user with cleartext userkey /secured-authenticator : Authenticate user with AES encrypted user key /encrypt : encode cleartext user key to encrypted user key. this feature is not secured and can be removed if VS is allowed from large network when requesting URL: http://{google api virtual ip}/authenticator?pass={passcode}&key={user key} if passcode is valid, the API respond 200 if passcode is not valid, the API respond 403 when requesting URL: http://{google api virtual ip}/secured-authenticator?pass={passcode}&key={encrypted user key} if passcode is valid, the API respond 200 if passcode is not valid, the API respond 403 This API can be configured to allow devices with 30 seconds offset. it calculate the previous, the current, and the next code. version 1.1 : This version includes new features : the Kai Wilke great optimization code using 11.1+ crypto commands ability to store in database (AD, LDAP ...) AES encrypted user key and decrypt it before OTP code validation version 1.2 : This version includes new features : the Kai Wilke security improvements with the authenticator keys AES encryption Powershell to add encrypted authenticator keys in AD user attribute How to use this snippet: Create a HTTP virtual server and assign the following irule. Change b64key value if user keys are encrypted with the provided powershell script. both keys in PS and irule must be the same. In APM, create a new HTTP AAA server with following parameters: Authentication Type : Form based Method : GET Form Action : http://{google api virtual ip}/authenticator (http://{google api virtual ip}/secured-authenticator if user key is encrypted) Form Parameter For User Name : {no value} Form Parameter For Password : {no value} Hidden Form Parameters/Values : pass %{session.logon.last.otp} userkey %{session.custom.key} -- Replace the variable containing the user key (from AD, LDAP, ... ) offset offset supported for token code (x means x*30 seconds offset) key_size 80 -- for algorithm evolution hmac_mode hmac-sha1 -- for algorithm evolution Successful Logon Detection Match Type = By specific string in response Successful Logon Detection Match Value : 200 OK this configuration use a unencrypted variable to store the OTP code. if the top code is in password variable, the parameter pass must be removed from hidden parameters and the Form Parameter For Password must be set to pass In VPE: provision the user key variable (from AD, LDAP, ... ) create a Logon page requesting the authenticator code (variable name otp) create a HTTP Auth box with the previously created HTTP server the following powershell script insert in AD user attribute "pager" the user encrypted key: function Encrypt-Data($AesKey, $Data) { $Data = [System.Text.Encoding]::UTF8.GetBytes($Data) $AesManaged = New-Object "System.Security.Cryptography.AesManaged" $AesManaged.Mode = [System.Security.Cryptography.CipherMode]::CBC $AesManaged.Padding = [System.Security.Cryptography.PaddingMode]::PKCS7 $AesManaged.BlockSize = 128 $AesManaged.KeySize = 256 $AesManaged.Key = [System.Convert]::FromBase64String($AesKey) $Encryptor = $AesManaged.CreateEncryptor() $EncryptedData = $Encryptor.TransformFinalBlock($Data, 0, $Data.Length); [byte[]] $EncryptedData = $AesManaged.IV + $EncryptedData $AesManaged.Dispose() [System.Convert]::ToBase64String($EncryptedData) } $encryptKey = "iTEEieok7//RyzrPe5mWwz1yroPPsm4e5cqghdEHIlU=" $userkey = $Args[1] $username = $Args[0] $encrypted = Encrypt-Data $encryptKey $userkey Set-AdUser -Identity $username -replace @{"pager"="$encrypted"} Thanks to Kai Wilke for this. it make the code less insecure. here is an example of how work the API with curl stanislas$ curl -k 'https://192.168.1.243/secured-authenticator?user=stan&userkey=IyQ+J6l88AW9NQmaRPhLrl0aNkLKw01+vBRpikVfVQk=&key_size=80&hmac_mode=hmac-sha1&offset=1&pass=790054' -i HTTP/1.0 200 OK Server: BigIP Connection: Keep-Alive Content-Length: 0 stanislas$ curl -k 'https://192.168.1.243/secured-authenticator?user=stan&userkey=IyQ+J6l88AW9NQmaRPhLrl0aNkLKw01+vBRpikVfVQk=&key_size=80&hmac_mode=hmac-sha1&offset=0&pass=790054' -i HTTP/1.0 403 Forbidden X-Error-Code: 3-wrong-password Server: BigIP Connection: Keep-Alive Content-Length: 0 Code : proc ga_validate {hmac_mode key_size b32_key token allowed_clock_skew_units} { ############################################################################################## # Initialize the Base32 alphabet to binary conversation (see RFC 4648) # set b32_to_binary [list \ A 00000 B 00001 C 00010 D 00011 E 00100 F 00101 G 00110 H 00111 \ I 01000 J 01001 K 01010 L 01011 M 01100 N 01101 O 01110 P 01111 \ Q 10000 R 10001 S 10010 T 10011 U 10100 V 10101 W 10110 X 10111 \ Y 11000 Z 11001 2 11010 3 11011 4 11100 5 11101 6 11110 7 11111 \ 0 "" 1 "" = "" " " "" \ ] if { [string length [set key [string map -nocase $b32_to_binary $b32_key]]] >= $key_size } then { # Convert the translated ga(key) binary string representation to binary set binary_key [binary format B$key_size $key] # Initialize clock timeframe based on Unix epoch time in seconds / 30 set clock [expr { [clock seconds] / 30 } ] ############################################################################################## # Configure the allowed clock skew units (1 unit = +/-30 seconds in both directions) # set allowed_clock_range {0} for {set i 1} {$i <= $allowed_clock_skew_units} {incr i} {lappend allowed_clock_range -$i $i} ############################################################################################## # Perform verification of the provided token foreach x $allowed_clock_range { ############################################################################################## # Perform verification of the provided token for time frame clock + $x # # Calculate hex encoded HMAC checksum value for wide-int value of time frame clock+ x using key_binary as secret binary scan [CRYPTO::sign -alg $hmac_mode -key $binary_key [binary format W* [expr { $clock + $x }]]] H* hmac_tmp # Parse offset based on the last nibble (= 4 bits / 1 hex) of the hmac_tmp HMAC checksum and multiply with 2 for byte to hex conversation set offset [expr { "0x[string index $hmac_tmp end]" * 2 } ] # Parse (= 4 bytes / 8 hex) from hmac_tmp starting at the offset value, then remove the most significant bit, perform the modulo 1000000 and format the result to a 6 digit number set token_calculated [format %06d [expr { ( "0x[string range $hmac_tmp $offset [expr { $offset + 7 } ]]" & 0x7FFFFFFF ) % 1000000 } ]] # Compare token_calculated with user provided token value if { $token_calculated equals $token } then { # The provided token is valid" return 1 break } } } else { return -1 } return 0 } when RULE_INIT { ############################################################################################## # Initialize the Encryption keyto decrypt encrypted user Key # set b64key "iTEEieok7//RyzrPe5mWwz1yroPPsm4e5cqghdEHIlU=" set static::KEY [b64decode $b64key] # create a passphrase used during IV random creation set static::IV_RANDOM_PWD "MyIVSeed" } when HTTP_REQUEST { switch [HTTP::path] { "/secured-authenticator" { set user [URI::query [HTTP::uri] user] set auth_code [URI::query [HTTP::uri] pass] if {[set allowed_clock_skew_units [URI::decode [URI::query [HTTP::uri] offset]]] equals ""} {set allowed_clock_skew_units "0"} if {[set key_size [URI::decode [URI::query [HTTP::uri] key_size]]] equals ""} {set key_size "80"} if {[set hmac_mode [URI::decode [URI::query [HTTP::uri] hmac_mode]]] equals ""} {set hmac_mode "hmac-sha1"} set aes_cipherstring [b64decode [URI::decode [URI::query [HTTP::uri] userkey]]] log local0. "[HTTP::uri] $user $aes_cipherstring" binary scan $aes_cipherstring a16a* aes_iv aes_ciphertext set userkey [CRYPTO::decrypt -alg aes-256-cbc -key $static::KEY -iv $aes_iv $aes_ciphertext] log local0. "$userkey / $auth_code" } "/authenticator" { set user [URI::query [HTTP::uri] user] set auth_code [URI::query [HTTP::uri] pass] if {[set allowed_clock_skew_units [URI::decode [URI::query [HTTP::uri] offset]]] equals ""} {set allowed_clock_skew_units "0"} if {[set key_size [URI::decode [URI::query [HTTP::uri] key_size]]] equals ""} {set key_size "80"} if {[set hmac_mode [URI::decode [URI::query [HTTP::uri] hmac_mode]]] equals ""} {set hmac_mode "hmac-sha1"} set userkey [URI::query [HTTP::uri] userkey] #log local0. "$userkey / $auth_code" } "/encrypt" { set userkey [URI::query [HTTP::uri] userkey] set aes_iv [CRYPTO::keygen -alg random -len 128] set aes_ciphertext [CRYPTO::encrypt -alg aes-256-cbc -key $static::KEY -iv $aes_iv $userkey] set aes_cipherstring [b64encode [binary format a*a* $aes_iv $aes_ciphertext]] HTTP::respond 200 content $aes_cipherstring return } default { HTTP::respond 403 X-Error-Code 1-wrong-URL log local0. "erreur 1 : Wrong URL" return } } # Call Authenticator procedure # valid arguments are: # - HMAC mode : "hmac-sha1", "hmac-sha256" or "hmac-sha512" # - Authenticator Key size : Google Authenticator uses a hardcoded 80 bit key length # - User key Base32 encoded # - User OTP code # - Allowed time shift (1 unit = 30 seconds) # procedure returns different values # - -1 : the key is not valid (key size too short) # - 0 : the user code is wrong # - 1 : the user code is rigth log local0. "call ga_validate $hmac_mode $key_size $userkey $auth_code $allowed_clock_skew_units" switch -- [call ga_validate $hmac_mode $key_size $userkey $auth_code $allowed_clock_skew_units] { -1 { # code verification failed HTTP::respond 403 X-Error-Code 2-wrong-user-key log local0. "erreur 2 : wrong User Key" } 0 { # code verification failed HTTP::respond 403 X-Error-Code 3-wrong-password log local0. "erreur 3 : wrong Password" } 1 { # code verification successful HTTP::respond 200 } default {set result "error: unknown"} } } Tested this on version: 11.51.8KViews1like2Comments