Forum Discussion

eric_haupt1's avatar
eric_haupt1
Icon for Nimbostratus rankNimbostratus
Oct 04, 2018

Client Certificate Auth x509x3 parsing irule for APM

I've been running this code for over a year with no issues. It's the second iteration of my x509x3 cert parsing irule that I call from within APM. I'm looking to add an enhancement. The regex looks for 10 or 16 digit patterns of numbers and only grabs 10 or 16. I need to add functionality that ensures only 10 digits are used for the temppiv. If a string of 10 numbers is matched - use that. If 16 numbers are matched - use only the first 10 numbers for the temppiv. The "else" is just a fallback looking for othername:UPN in the event that the first routine fails, but to be honest this is never called anymore. I just keep the code around as a catch-all. Any help or pointers is appreciated.

 

when ACCESS_POLICY_AGENT_EVENT {
     switch [ACCESS::policy agent_id] {
          "CERTPROCESSING" {

                if { ([regexp {([0-9]{16}|[0-9]{10})} [ACCESS::session data get session.ssl.cert.subject] temppiv ] == 1) and ([ACCESS::session data get session.ssl.cert.issuer] contains "OU=COMPANY") } {
                     set tempupn "$temppiv@com"
                     ACCESS::session data set session.custom.certupn $tempupn }

              else { if { [ACCESS::session data get session.ssl.cert.x509extension] contains "othername:UPN<" } {
                     set tempupn [findstr [ACCESS::session data get session.ssl.cert.x509extension] "othername:UPN<" 14 ">"]
                     ACCESS::session data set session.custom.certupn $tempupn }

                   }
              }
       }
}

3 Replies

  • Something like this maybe,

    set str1 "foo.bar.1234567890"
    set str2 "foo.bar.1234567890123456"
    
    if { ( [regexp {([0-9]{16}|[0-9]{10})} $str2 temppiv] == 1 ) and ( [set temppiv [string range $temppiv 0 9]] ne "" ) } {
        log local0. "TEST: temppiv = $temppiv"
    }
    
  • Can you explain exactly what is the subject format? does it contain some other characters than digits?

    The regular expression provided by kevin will match every string with at least 10 digits.

    set str "foo."
    set str_end ".bar"
    for {set i 1 } { $i<20} {incr i} {
    append str [expr {int(rand()*10)}]
    echo "result for string $str$str_end is [lindex {false true} [regexp {([0-9]{16}|[0-9]{10})} $str$str_end temppiv]]"
    }
    foo.
    % .bar
    % result for string foo.7.bar is false
    result for string foo.71.bar is false
    result for string foo.712.bar is false
    result for string foo.7129.bar is false
    result for string foo.71293.bar is false
    result for string foo.712939.bar is false
    result for string foo.7129391.bar is false
    result for string foo.71293917.bar is false
    result for string foo.712939174.bar is false
    result for string foo.7129391740.bar is true
    result for string foo.71293917401.bar is true
    result for string foo.712939174010.bar is true
    result for string foo.7129391740102.bar is true
    result for string foo.71293917401029.bar is true
    result for string foo.712939174010292.bar is true
    result for string foo.7129391740102928.bar is true
    result for string foo.71293917401029287.bar is true
    result for string foo.712939174010292879.bar is true
    result for string foo.7129391740102928792.bar is true
    

    the result is the same as if you only check for a 10 digits string..

    % set str "foo."
    set str_end ".bar"
    for {set i 1 } { $i<20} {incr i} {
    append str [expr {int(rand()*10)}]
    echo "result for string $str$str_end is [lindex {false true} [regexp {([0-9]{10})} $str$str_end temppiv]]"
    }
    foo.
    % .bar
    % result for string foo.4.bar is false
    result for string foo.41.bar is false
    result for string foo.419.bar is false
    result for string foo.4199.bar is false
    result for string foo.41991.bar is false
    result for string foo.419918.bar is false
    result for string foo.4199182.bar is false
    result for string foo.41991829.bar is false
    result for string foo.419918296.bar is false
    result for string foo.4199182963.bar is true
    result for string foo.41991829630.bar is true
    result for string foo.419918296309.bar is true
    result for string foo.4199182963098.bar is true
    result for string foo.41991829630985.bar is true
    result for string foo.419918296309854.bar is true
    result for string foo.4199182963098543.bar is true
    result for string foo.41991829630985439.bar is true
    result for string foo.419918296309854391.bar is true
    result for string foo.4199182963098543916.bar is true
    

    You can check that there is no digit before and after the searched string

    set str "foo."
    set str_end ".bar"
    for {set i 1 } { $i<20} {incr i} {
    append str [expr {int(rand()*10)}]
    echo "result for string $str$str_end is [lindex {false true} [regexp {^[^\d]*(\d{16}|\d{10})[^\d]*$} $str$str_end temppiv]]"
    }foo.
    % .bar
    % 
    result for string foo.4.bar is false
    result for string foo.43.bar is false
    result for string foo.430.bar is false
    result for string foo.4304.bar is false
    result for string foo.43048.bar is false
    result for string foo.430483.bar is false
    result for string foo.4304835.bar is false
    result for string foo.43048355.bar is false
    result for string foo.430483554.bar is false
    result for string foo.4304835549.bar is true
    result for string foo.43048355498.bar is false
    result for string foo.430483554980.bar is false
    result for string foo.4304835549809.bar is false
    result for string foo.43048355498095.bar is false
    result for string foo.430483554980954.bar is false
    result for string foo.4304835549809545.bar is true
    result for string foo.43048355498095452.bar is false
    result for string foo.430483554980954520.bar is false
    result for string foo.4304835549809545206.bar is false
    % 
    

    instead of the irule event, create a variable assign for variable session.custom.certupn with expression:

    if {[regexp {^[^\d]*(\d{16}|\d{10})[^\d]*$} [mcget {session.ssl.cert.subject}] temppiv]] == 1} {
    return [string range $temppiv 0 9]
    } elseif { [mcget {session.ssl.cert.x509extension}] contains "othername:UPN<" } {
    set extension [mcget {session.ssl.cert.x509extension}];
    return [string range $extension [expr {[string first "othername:UPN<" $extension] +14}] [expr {[string last ">" $extension] -1}] ];
    }
    
  • Here is my current version - Pulls digits from subject and builds UPN, grabs UPN from x509, or pulls email from x509. Been using this a long time.

    `   when ACCESS_POLICY_AGENT_EVENT {
        switch [ACCESS::policy agent_id] {
          "CACPROCESSING" {
                if { ([regexp {([0-9]{16}|[0-9]{10})} [ACCESS::session data get session.ssl.cert.subject] var_temp_piv ] == 1) and ([ACCESS::session data get session.ssl.cert.issuer] contains "COMPANY") } {
                     set var_temp_upn "$var_temp_piv@COMPANY"
                     set var_session_start [clock format [clock seconds] -format {%d %b %Y %T %Z}]
                     ACCESS::session data set session.custom.cert.upn $var_temp_upn
                     ACCESS::session data set session.custom.start.time $var_session_start
                     }
              else { if { [ACCESS::session data get session.ssl.cert.x509extension] contains "othername:UPN<" } {
                     set var_temp_upn [findstr [ACCESS::session data get session.ssl.cert.x509extension] "othername:UPN<" 14 ">"]
                     set var_session_start [clock format [clock seconds] -format {%d %b %Y %T %Z}]
                     ACCESS::session data set session.custom.cert.upn $var_temp_upn
                     ACCESS::session data set session.custom.start.time $var_session_start
                     }
              else { if { [ACCESS::session data get session.ssl.cert.x509extension] contains "email:" } {
                     set var_temp_x509extension ([string map -nocase {" " "" \n "" \r ""} [ACCESS::session data get session.ssl.cert.x509extension]])
                     set var_temp_email [findstr $var_temp_x509extension "email:" 6 "X509"]
                     set var_session_start [clock format [clock seconds] -format {%d %b %Y %T %Z}]
                     ACCESS::session data set session.custom.cert.email $var_temp_email
                     ACCESS::session data set session.custom.start.time $var_session_start
                     }
    

    }}}}}`