Forum Discussion

R_Marc_77962's avatar
R_Marc_77962
Icon for Nimbostratus rankNimbostratus
Apr 24, 2015

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

  • 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.

     

  • R_Marc's avatar
    R_Marc
    Icon for Nimbostratus rankNimbostratus

    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.

  • 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's avatar
      R_Marc
      Icon for Nimbostratus rankNimbostratus
      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's avatar
      R_Marc
      Icon for Nimbostratus rankNimbostratus
      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.
    • Drewv's avatar
      Drewv
      Icon for Nimbostratus rankNimbostratus
      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!