Forum Discussion

MikeRobinson_64's avatar
MikeRobinson_64
Icon for Nimbostratus rankNimbostratus
Jul 14, 2009

CAC authentication and http headers

Hello everyone,

 

 

I am trying to get a CAC authentication irule working properly and have run into a glitch. The initial pass through the rule identifies the folder and requires a CAC for specific folders (the folder names have been changed to protect the innocent). This part of the irule works properly but once the user puts in their CAC the second pass does now work as intended as it appears to not load the certificate status or fields into the fields and array I have set up. As a consequence, the traffic just passes right through to the pool without valid revocation checking. The irule is listed below. Thank you in advance for any help you can provide!

 

 

Mike

 

 

when CLIENT_ACCEPTED {

 

set needcert 0

 

set gotcert 0

 

set badcert 0

 

set tmm_auth_ssl_ocsp_sid 0

 

set tmm_auth_ssl_ocsp_done 0

 

}

 

when CLIENTSSL_HANDSHAKE {

 

set certcnt [SSL::cert count]

 

if { $certcnt > 0 } {

 

HTTP::release

 

}

 

}

 

when CLIENTSSL_CLIENTCERT {

 

set tmm_auth_ssl_ocsp_done 0

 

if { $needcert == 1 and $tmm_auth_ssl_ocsp_sid == 0} {

 

set tmm_auth_ssl_ocsp_sid [AUTH::start pam ssl_ocsp]

 

set somecert [SSL::cert 0]

 

AUTH::cert_credential $tmm_auth_ssl_ocsp_sid $somecert

 

AUTH::cert_issuer_credential $tmm_auth_ssl_ocsp_sid [SSL::cert issuer 0]

 

if {[X509::issuer $somecert] contains "CN=XXXXXXX"} {

 

AUTH::authenticate $tmm_auth_ssl_ocsp_sid

 

set id [SSL::sessionid]

 

set ssl_array [list blah1 blah2]

 

lset ssl_array 0 [X509::verify_cert_error_string [SSL::verify_result]]

 

SSL::handshake hold

 

}

 

else {

 

lset ssl_array 0 "ok"

 

lset ssl_array 1 "auth_success"

 

}

 

}

 

}

 

when AUTH_SUCCESS {

 

if {$tmm_auth_ssl_ocsp_sid eq [AUTH::last_event_session_id]} {

 

SSL::handshake resume

 

lset ssl_array 1 "auth_success"

 

session add ssl $id $ssl_array 21600

 

}

 

}

 

when AUTH_FAILURE {

 

if {$tmm_auth_ssl_ocsp_sid eq [AUTH::last_event_session_id]} {

 

SSL::handshake resume

 

lset ssl_array 1 "auth_failure"

 

session add ssl $id $ssl_array 21600

 

}

 

}

 

when AUTH_WANTCREDENTIAL {

 

if {$tmm_auth_ssl_ocsp_sid eq [AUTH::last_event_session_id]} {

 

reject

 

}

 

}

 

when AUTH_ERROR {

 

if {$tmm_auth_ssl_ocsp_sid eq [AUTH::last_event_session_id]} {

 

SSL::handshake resume

 

lset ssl_array 1 "auth_failure"

 

session add ssl $id $ssl_array 21600

 

}

 

}

 

when HTTP_REQUEST {

 

switch -glob [string tolower [HTTP::uri]] {

 

"/folder1/*" -

 

"/folder2/*" -

 

"/folder3/*" -

 

"/folder4/*" -

 

"/folder5/*" -

 

"/folder6/*" -

 

"/folder7/*" -

 

"/folder8/*" {

 

if {$gotcert == 0} {

 

if { [SSL::cert count] == 0} {

 

HTTP::collect

 

set needcert 1

 

SSL::authenticate always

 

SSL::authenticate depth 9

 

SSL::cert mode require

 

SSL::renegotiate

 

}

 

elseif { $ssl_data0 contains "expired" } {

 

set fail_payload_part1 ""

 

set fail_payload_part2 "You appear to have a certificate that has expired."

 

set fail_payload_part3 "If you feel you have received this message in error,"

 

set fail_payload_part4 "please contact the appropriate help center"

 

set fail_payload [concat $fail_payload_part1 $fail_payload_part2 $fail_payload_part3 $fail_payload_part4]

 

HTTP::respond 200 content $fail_payload

 

}

 

elseif { $ssl_data1 contains "auth_failure" } {

 

set fail_payload_part1 ""

 

set fail_payload_part2 "You appear to have a certificate that has been revoked."

 

set fail_payload_part3 "If you feel you have received this message in error,"

 

set fail_payload_part4 "please contact the appropriate help center."

 

set fail_payload [concat $fail_payload_part1 $fail_payload_part2 $fail_payload_part3 $fail_payload_part4]

 

HTTP::respond 200 content $fail_payload

 

}

 

elseif { $ssl_data0 contains "ok" } {

 

HTTP::header replace "SSLCLientCertStatus" $ssl_data1

 

HTTP::header replace "SSLClientCertVersion" [X509::version $somecert]

 

HTTP::header replace "SSLClientCertSerialNumber" [X509::serial_number $somecert]

 

HTTP::header replace "SSLClientCertIssuer" [X509::issuer $somecert]

 

HTTP::header replace "SSLClientCertNotValidBefore" [X509::not_valid_before $somecert]

 

HTTP::header replace "SSLClientCertNotValidAfter" [X509::not_valid_after $somecert]

 

HTTP::header replace "SSLClientCertSubject" [X509::subject $somecert]

 

pool test_pool

 

}

 

else {

 

set fail_payload_part1 ""

 

set fail_payload_part2 "It appears that you either do not have a valid certificate installed"

 

set fail_payload_part3 "and functioning in your browser or your session has timed-out. If you feel you have received"

 

set fail_payload_part4 "this message in error, please try connecting again or contact the appropriate help center"

 

set fail_payload [concat $fail_payload_part1 $fail_payload_part2 $fail_payload_part3 $fail_payload_part4]

 

HTTP::respond 200 content $fail_payload

 

}

 

}

 

else {

 

pool test_pool

 

}

 

}

 

}

 

}

4 Replies

  • Which version are you testing on? Can you post anonymised log output from a failure?

     

     

    Aaron
  • Thanks for asking Aaron. The version is 9.3.1 and the log output is shown below after adding in a few log statements to the irule:

     

     

    Jul 16 22:17:06 tmm tmm[15463]: Rule irule_CAC_Required_OCSP : cert count=0 result=0

     

    Jul 16 22:17:07 tmm tmm[15463]: Rule irule_CAC_Required_OCSP : cert count=0 result=0

     

    Jul 16 22:17:08 tmm tmm[15463]: Rule irule_CAC_Required_OCSP : cert count=0 result=0

     

    Jul 16 22:17:08 tmm tmm[15463]: Rule irule_CAC_Required_OCSP : gotcert = 0

     

    Jul 16 22:17:12 tmm tmm[15463]: Rule irule_CAC_Required_OCSP : cert count=0 result=0

     

    Jul 16 22:17:12 tmm tmm[15463]: Rule irule_CAC_Required_OCSP : gotcert = 0

     

    Jul 16 22:17:19 tmm tmm[15463]: Rule irule_CAC_Required_OCSP : Clientssl_clientcert section

     

    Jul 16 22:17:19 tmm tmm[15463]: Rule irule_CAC_Required_OCSP : OCSP Auth_Failure Recorded

     

    Jul 16 22:17:19 tmm tmm[15463]: Rule irule_CAC_Required_OCSP : cert count=2 result=0

     

    Jul 16 22:17:19 tmm tmm[15463]: Rule irule_CAC_Required_OCSP : cert count=0 result=0

     

     

    That's it. Even the auth failure should have produced a response to the user and it appears that it does not go through the http request section when auth failure occurs.

     

     

    Mike
  • Hi Mike,

     

     

    If you want to send back an HTTP response even if the client doesn't present a cert when prompted, I think you'll need to set the cert mode to request when you call SSL::renogiate. Else, the client SSL profile might trigger a RST to the client if no cert is provided.

     

     

    Can you add the client IP and port to the log statements and then post the example iRule with the log statements included to correlate the iRule code with the logging? Also, f you wrap the iRule text in [ code ] [/ code ] tags (without the spaces), the spacing of the rule will be preserved.

     

     

    Aaron
  • Ok, so the fix for this was twofold...

     

     

    One: Add an LB_SELECTED function to pass the headers to the server (see below)

     

     

    when LB_SELECTED {

     

    if {$needcert ==1} {

     

    set ssl_array1 [session lookup ssl $id]

     

    set ssl_data0 [lindex $ssl_array1 0]

     

    set ssl_data1 [lindex $ssl_array1 1]

     

    if { $ssl_data0 contains "ok" } {

     

    HTTP::header replace "SSLCLientCertStatus" $ssl_data1

     

    HTTP::header replace "SSLClientCertVersion" [X509::version $somecert]

     

    HTTP::header replace "SSLClientCertSerialNumber" [X509::serial_number $somecert]

     

    HTTP::header replace "SSLClientCertIssuer" [X509::issuer $somecert]

     

    HTTP::header replace "SSLClientCertNotValidBefore" [X509::not_valid_before $somecert]

     

    HTTP::header replace "SSLClientCertNotValidAfter" [X509::not_valid_after $somecert]

     

    HTTP::header replace "SSLClientCertSubject" [X509::subject $somecert]

     

    }

     

    }

     

    }

     

     

    You could even send the failures through as headers this way too if you want to customize a response page back on the server.

     

     

    Two: Add in a HTTP_RESPONSE section to handle the failure responses back to the client.

     

     

    It works as intended now!

     

     

    Mike