cancel
Showing results for 
Search instead for 
Did you mean: 
PeteWhite
F5 Employee
F5 Employee

Problem this snippet solves:

TMOS v12.1.4.1 and v13 have a change where the output of X509::subject is formatted to match OpenSSL output. See https://cdn.f5.com/product/bugtracker/ID607410.html


<= TMOS v12 is CN=USERNAME,OU=CONTRACTOR,OU=PKI,OU=DEPT,O=COMPANY,C=US

>= TMOS v13 is C=US,O=COMPANY,OU=DEPT,OU=PKI,OU=CONTRACTOR,CN=USERNAME

How to use this snippet:

This procedure will change the output format to either v12 or v13 ( or you can easily modify it to create your own output ). Default output is v12.


Use v13 output:

set serialNumber [ call formatSubject [ X509::subject [SSL::cert 0] ] 13 ]


Use v12 output:

set serialNumber [ call formatSubject [ X509::subject [SSL::cert 0] ] ]


Code :

proc formatSubject { subject { f 12 }} {
    # subject is the subject string eg CN=USERNAME,OU=CONTRACTOR,OU=PKI,OU=DEPT,O=COMPANY,C=US
    # f is format version and is either 12 or 13. Default 12
    # This procedure formats the subject field according to TMOS v12 or v13 format
    # v12 is CN=USERNAME,OU=CONTRACTOR,OU=PKI,OU=DEPT,O=COMPANY,C=US
    # v13 is C=US,O=COMPANY,OU=DEPT,OU=PKI,OU=CONTRACTOR,CN=USERNAME
    # See https://cdn.f5.com/product/bugtracker/ID607410.html
    # dn is an array with format [ CN O { OU } CN ]

    array set dn {
        OU {}
    } 
    set subject [regsub -all {(".*?),(.*?")} $subject "\\1--COMMA--\\2"]
    
    foreach i [ split $subject , ] {
        set j [ split [ string trim $i] = ]
        if { [lindex $j 0] == "OU" } {
            lappend dn(OU) [lindex $j 1]
        } else {
            array set dn [ list [lindex $j 0] [lindex $j 1] ]
        }
    }
    # Loop through OUs and create a string
    set ouString ""
    foreach ou $dn(OU) {
        append ouString "OU=$ou, "
    }
																		
    if { $f == 12 } {
        set order { CN OU O C }
    } else {
        set order { C O OU CN }
    }
							  
    set returnString ""
    foreach a $order {
        if { [info exists dn($a)] } {
            if { $a == "OU" } { 
                append returnString $ouString 
            } else {
                append returnString "$a=$dn($a), "
            }
        }
    }
    set returnString [string map {"--COMMA--" ","} $returnString]
    return [ string trimright $returnString ", " ]
}

Tested this on version:

13.0
Comments
uniqng
Nimbostratus
Nimbostratus

How is this proc going to behave in case the ','character is part of the CN or any other sub-string?

PeteWhite
F5 Employee
F5 Employee

uniqng this splits the DN on the comma so if there is an unexpected comma then it will break. If you have complex, specific requirements then I suggest you decode the field using the ASN1 commands

Stephen_Scott
F5 Employee
F5 Employee

Note that this behaviour change was also introduced in 12.1.4.1

PeteWhite
F5 Employee
F5 Employee

Thanks Steve, have updated the text to show this

Stanislas_Piro2
Cumulonimbus
Cumulonimbus

 to prevent this issue, you can replace lines 13 to 20 with:

array set dn [list] foreach {key val} [ split [string map {"\\," "\\," "," "|" "\\=" "\\=" "=" "|"} $subject] "|"] { lappend dn([string trim $key]) [string trim $val] }

 

 

 

uniqng
Nimbostratus
Nimbostratus

 it doesn't seem to work, can you explain how it suppose to work? What are the different string maps suppose to do?

 

Please note that the string with comma is enclosed withing double quotes and not escaped with \

i.e full string from x509 command is

 

C=US, OU=Example, O=Example2, CN="Example, Inc"

PeteWhite
F5 Employee
F5 Employee
Hi uniqng, The idea is that the string map changes the commas to | prior to doing the split on commas. The issue is that there are a lot of variations in how the comma can look. If you can PM an example of the broken string I can take a look and probably get around it but having a universal method which works on all strings is tricky
PeteWhite
F5 Employee
F5 Employee

Hi uniqng, I have changed it so that it handles commas within quotes. Tried it with your example and it seems to work so maybe you can try it out yourself.

uniqng
Nimbostratus
Nimbostratus

Thank you both, I've tried replace the comma inside "" with regsub but couldn't make it work. Now it works as expected

uniqng
Nimbostratus
Nimbostratus

It works but there's just one small issue (might be a bug?). BTW my version is 13.1.3 build 0.0.6

 

Here's the relevant code

  

set subject [regsub -all {(".*),(.*")} $subject "\\1--COMMA--\\2"] log local0. "Comma replaced: $subject"

 

 

In case there are 2 fields with commas in the subject it only replaces second comma occurrence inside double-quotes. This is what I get

 

Nov 20 01:24:26 example.lab info tmm5[12408]: Rule /Common/irule-subject-reverse <HTTP_REQUEST>: Original Subject: C=GB, ST="London, City of", L=Bar, O=Foo, OU=TEST, CN="F5lab, local"

Nov 20 01:24:26 example.lab info tmm5[12408]: Rule /Common/irule-subject-reverse <HTTP_REQUEST>: Comma replaced: C=GB, ST="London, City of", L=Bar, O=Foo, OU=TEST, CN="F5lab--COMMA-- local"

 

I believe the regsub -all option should replace all the occurrences of the comma(,) in the string right?

 

Any ideas what might be wrong?

 

PeteWhite
F5 Employee
F5 Employee
That’s correct, regsub - - all should modify both of those. I’ll test it out later
Stan_PIRON_F5
F5 Employee
F5 Employee

 : The regsub try to match the largest pattern...

The first match string is:

London, City of", L=Bar, O=Foo, OU=TEST, CN="F5lab

The second is

local"

So you have to force stoping at the first "... replace the .* with [^"]*

 

The problem with this expression is it will also match this string:

 

f", L=Bar, O=Foo, OU=TEST, CN="

 

This expression seems to work with your example:

 

% set subject [regsub -all {([a-zA-Z]+ ?= ?"[^"]+),([^"]+")} $subject "\\1--COMMA--\\2"]

 

C=GB, ST="London--COMMA-- City of", L=Bar, O=Foo, OU=TEST, CN="F5lab--COMMA-- local"

 

uniqng
Nimbostratus
Nimbostratus

OK I understand now, the regsub -all is greedy so it matches as much as it can.

 

Thanks again both of you for your help with that. All is good now

PeteWhite
F5 Employee
F5 Employee

Updated to work better with commas and made less greedy with the use of ? in the regsub regex

Stanislas_Piro2
Cumulonimbus
Cumulonimbus

Hi  , I had the same requirement and it seems the change is the following:

 

<= TMOS v12 is CN=LASTNAME\, FIRSTNAME,OU=CONTRACTOR,OU=PKI,OU=DEPT,O=COMPANY,C=US

>= TMOS v13 is C=US, O=COMPANY, OU=DEPT, OU=PKI, OU=CONTRACTOR, CN="LASTNAME, FIRSTNAME"

 

So differences are:

  • Reverse Ordered elements (full reverse may be better than defining the order in the irule)
  • space after comma separator
  • double quotes around fields with special characters (like comma)
  • unescaped special character between double quotes

 

I wrote following procs to manage conversion from v12 to v13 and from v13 to v12 :

 

proc subject_v13_to_v12 {subject_openssl} { # Create the empty subject set new_subject "" # For each subject element, do the following actions: # - remove double quotes arround the value # - escape the comma character # - remove space around comma separator # Insert the value at the begining of the subject (to reverse order) foreach {type value} [ split [regsub -all {(".*?),(.*?")} $subject_openssl "\\1--COMMA--\\2"] ",=" ] { set new_subject "{[string trim $type]=[string trim [string map {"--COMMA--" "\\," "+" "\\+" "\\" "\\\\" "<" "\\<" ">" "\\>" ";" "\\;" } [string trim $value {"}]]]} $new_subject" } # Return the joined values with comma return [join $new_subject ","] }

 

proc subject_v12_to_v13 {subject_rfc2253} { # Create the empty subject set new_subject "" # For each subject element, do the following actions: # - add double quotes arround the value # - unescape the comma character # - Add space around comma separator # Insert the value at the begining of the subject (to reverse order) foreach {type value} [ split [string map {"\\," "--COMMA--"} $subject_rfc2253] ",=" ] { set new_subject "{[string trim $type]=[expr {[string match "*--COMMA--*" $value] ? "\"[string trim [string map {"--COMMA--" ","} $value]]\"" : [string trim $value] }]} $new_subject" } # Return the joined values with comma and space return [join $new_subject ", "] }

I got the following results:

subject_v12_to_v13 {CN=F5lab\, local,OU=TEST,O=Foo,L=Bar,ST=London\, City of,C=GB} C=GB, ST="London, City of", L=Bar, O=Foo, OU=TEST, CN="F5lab, local"   % subject_v13_to_v12 {C=GB, ST="London, City of", L=Bar, O=Foo, OU=TEST, CN="F5lab, local"} CN=F5lab\, local,OU=TEST,O=Foo,L=Bar,ST=London\, City of,C=GB

 

Version history
Last update:
‎18-Oct-2019 05:09
Updated by:
Contributors