Forum Discussion
Issue with APM/ActiveSync cert auth
I'm having an issue with Certificate auth for activesync with APM. The problem seems isolated to IOS. Android devices don't seem to be having a problem. The issue is that some IOS users in the pilot group are getting mail sporadically from other users in the pilot group.
The gist of this is, if the user presents a cert, the domain and username are extracted from the DN of the client cert. The user is looked up in AD to make sure it's a valid user, then a Kerberos ticket is requested on the users behalf and presented to the CAS 2013 server.
If the user doesn't have a cert yet (we are in a transition period) I just forward the connection on to CAS to authenticate the user.
From my perspective (on the F5) I see all that working fine. Below is the virtual config. The persistence is cookie, and the irules are a header logging rule I'm using for troubleshooting and then a modified version of the _sys_APM_activesync iRule. The default one required BA. If a BA header wasn't provided, it will send a 401 back to the user. My modified version just removes that part.
ltm virtual activesync-ipv6-https-virtual {
destination 1::fffe.443
ip-protocol tcp
persist {
activesync-persistence-profile {
default yes
}
}
pool activesync-2013-https-pool
profiles {
activesync-access-policy { }
activesync-client-ssl-profile {
context clientside
}
activesync-http-profile { }
activesync-server-ssl-profile {
context serverside
}
rba { }
tcp { }
websso { }
}
rules {
logging-rule
ActiveSync-rule
}
source-address-translation {
type automap
}
}
I'm running 11.6 HF4
8 Replies
- R_Marc
Nimbostratus
The _sys_APM_activesync irule apparently had some other BA specific rules which were contributing to my issues. I've removed those as well.
if { $f_insert_clientless_mode == 1 } { HTTP::header insert "clientless-mode" 1 HTTP::header insert "username" $apm_username HTTP::header insert "password" $apm_password } unset f_insert_clientless_mode
That being said, if that's the issue I can see some issues with security. Those headers need to be blocked/reset on ingress.
It would assume that someone with a valid cert wanted to hack user X, so it wouldn't be an external vector, but I'm going to preempt that.
This all assumes this fixes the issue.
- Drew_24528
Nimbostratus
Fantastic, thank you for the replies. I have also got this working by using the clients MDM application to include the UPN as the password field. This is essentially ignored because all users are cert-based auth so the workaround is acceptable to the client. I would still be curious to see your rule 'R Marc' for future reference.
Thanks!
- R_Marc
Nimbostratus
It's not that much different that the stock one, just got rid of the 401 requirement and added more uniqueness to the session cookie. I think the session cookie still should be a little stronger, which I'll add the next time I get a window from the windows folks (they are very risk averse).when RULE_INIT { set static::actsync_401_http_body "Authentication FailuredError: Authentication Failure" set static::actsync_503_http_body "Service is not availableError: Service is not available" set static::ACCESS_LOG_PREFIX "01490000:7:" } when HTTP_REQUEST { set http_path [string tolower [HTTP::path]] set f_clientless_mode 0 HTTP::header replace X-Teros-Client-IP [IP::client_addr] HTTP::header replace Host mobilemail.mastercard.com if {[SSL::cert count] > 0}{ HTTP::header insert X-Client-Cert [X509::subject [SSL::cert 0]] } if { [HTTP::method] equals "OPTIONS" } { HTTP::respond 200 -version 1.1 noserver Cache-Control "private" Allow "OPTIONS,POST" Server "Microsoft-IIS/8.5" MS-Server-ActiveSync "14.3" MS-ASProtocolVersions "2.0,2.1,2.5,12.0,12.1,14.0,14.1" MS-ASProtocolCommands "Sync,SendMail,SmartForward,SmartReply,GetAttachment,GetHierarchy,CreateCollection,DeleteCollection,MoveCollection,FolderSync,FolderCreate,FolderDelete,FolderUpdate,MoveItems,GetItemEstimate,MeetingResponse,Search,Settings,Ping,ItemOperations,Provision,ResolveRecipients,ValidateCert" Public "OPTIONS,POST" X-AspNet-Version "4.0.30319" X-Powered-By: "ASP.NET" X-FEServer "STL3MSXCAS02" Content-Length 0 } if { $http_path == "/microsoft-server-activesync" } { } elseif { $http_path == "/autodiscover/autodiscover.xml" } { set f_auto_discover 1 } else return if { ! [ info exists src_ip ] } { set src_ip [IP::remote_addr] } if { ! [ info exists PROFILE_RESTRICT_SINGLE_IP ] } { set PROFILE_RESTRICT_SINGLE_IP 1 } Only allow HTTP Basic Authentication. set auth_info_b64enc "" set http_hdr_auth [HTTP::header Authorization] regexp -nocase {Basic (.*)} $http_hdr_auth match auth_info_b64enc if { $auth_info_b64enc == "" } { set http_hdr_auth "" } set MRHSession_cookie [HTTP::cookie value MRHSession] Do we have valid MRHSession cookie. if { $MRHSession_cookie != "" } { if { [ACCESS::session exists -state_allow -sid $MRHSession_cookie] } { log -noname accesscontrol.local1.debug "$static::ACCESS_LOG_PREFIX HTTP *VALID* MRHSession cookie: $MRHSession_cookie" Default profile access setting is false if { $PROFILE_RESTRICT_SINGLE_IP == 0 } { return } elseif { [ IP::addr $src_ip equals [ ACCESS::session data get -sid $MRHSession_cookie "session.user.clientip" ] ] } { log -noname accesscontrol.local1.debug "$static::ACCESS_LOG_PREFIX source IP matched" return } else { log -noname accesscontrol.local1.debug "$static::ACCESS_LOG_PREFIX source IP does not matched" } } else { log -noname accesscontrol.local1.debug "$static::ACCESS_LOG_PREFIX HTTP *INVALID* MRHSession cookie: $MRHSession_cookie" } set MRHSession_cookie "" HTTP::cookie remove MRHSession } if { [HTTP::header exists X-Client-Cert] } { set apm_username [ string tolower [HTTP::header X-Client-Cert] ] } else { set apm_username [ string tolower [HTTP::header username] ] } set apm_password [HTTP::password] if { $PROFILE_RESTRICT_SINGLE_IP == 0 } { binary scan [md5 "$apm_password$"] H* user_hash } else { binary scan [md5 "$apm_password$src_ip$apm_username"] H* user_hash } set user_key {} append user_key $apm_username "." $user_hash unset user_hash set f_insert_clientless_mode 0 set apm_cookie_list [ ACCESS::user getsid $user_key ] if { [ llength $apm_cookie_list ] != 0 } { set apm_cookie [ ACCESS::user getkey [ lindex $apm_cookie_list 0 ] ] if { $apm_cookie != "" } { HTTP::cookie insert name MRHSession value $apm_cookie } else { set f_insert_clientless_mode 1 } } else { set f_insert_clientless_mode 1 } if { $f_insert_clientless_mode == 1 } { HTTP::header insert "clientless-mode" 1 HTTP::header insert "username" $apm_username HTTP::header insert "password" $apm_password if { [HTTP::header exists X-Client-Cert] } { log local0. "if: HTTP::header X-Client-Cert: [HTTP::header X-Client-Cert] [IP::client_addr]:[TCP::client_port]" } else { if {[catch {b64decode [lindex [split [HTTP::header Authorization] " "] 1]} authorization] == 0 and $authorization ne ""}{ set username [lindex [split $authorization ":"] 0] log local0. "else: HTTP::header username: $username [IP::client_addr]:[TCP::client_port]" } else { log local0. "failed to decode [lindex [split [HTTP::header Authorization] " "] 1] [IP::client_addr]:[TCP::client_port]" } } } unset f_insert_clientless_mode } when ACCESS_SESSION_STARTED { if { [ info exists user_key ] } { ACCESS::session data set "session.user.uuid" $user_key ACCESS::session data set "session.user.microsoft-exchange-client" 1 ACCESS::session data set "session.user.activesync" 1 if { [ info exists f_auto_discover ] && $f_auto_discover == 1 } { set f_auto_discover 0 ACCESS::session data set "session.user.microsoft-autodiscover" 1 } } } when ACCESS_ACL_ALLOWED { if { [HTTP::header exists X-Client-Cert] } { foreach field [HTTP::header value X-Client-Cert] { if {$field contains "CN="} { set name [string range $field [expr { [string first "=" $field ] + 1} ] end ] HTTP::header remove username HTTP::header replace username $name } } } } when ACCESS_POLICY_COMPLETED { if { ! [ info exists user_key ] } { return } set policy_result [ACCESS::policy result] switch $policy_result { "allow" { } "deny" { ACCESS::respond 401 content $static::actsync_401_http_body Connection close ACCESS::session remove } default { ACCESS::respond 503 content $static::actsync_503_http_body Connection close ACCESS::session remove } } unset user_key } }
- R_Marc
Nimbostratus
Oh, I forgot, I also added a stock answer for OPTIONS requests. Some Apple devices request options which was causing a failure somewhere (I forget where). I just grabbed what Exhange 2013 returns for an OPTIONS query, and have it statically return that to the user. - laser
Altostratus
Awesome, I was attempting to workaround the same thing, it was actually the main issue of why my device would fail. Thanks for the follow-up!
Hi R Marc Is it possible for you to post your irule for this? - I am currently having a struggle with this. Also having issues with iPhones not sending the certicate when using Mail app. Works fine when using the Safari browser; any thoughts on this? I have tried with request/require on the SSL profile itself, usind Client Cert Inspection and with the SSL profile set to ignore with On-demand cert auth set to require. Both fails to deliver the cert. But would be much appreciated if you could please share the irule. The other one you posted was all scrambled.
Recent Discussions
Related Content
* Getting Started on DevCentral
* Community Guidelines
* Community Terms of Use / EULA
* Community Ranking Explained
* Community Resources
* Contact the DevCentral Team
* Update MFA on account.f5.com