\n
1: when CLIENT_ACCEPTED {
2: if { [PROFILE::exists clientssl] } {
3:
4: # We have a clientssl profile attached to this VIP but we need
5: # to find an SNI record in the client handshake. To do so, we'll
6: # disable SSL processing and collect the initial TCP payload.
7:
8: set default_tls_pool [LB::server pool]
9: set detect_handshake 1
10: SSL::disable
11: TCP::collect
12:
13: } else {
14:
15: # No clientssl profile means we're not going to work.
16:
17: log local0. \"This iRule is applied to a VS that has no clientssl profile.\"
18: set detect_handshake 0
19:
20: }
21:
22: }
23:
24: when CLIENT_DATA {
25:
26: if { ($detect_handshake) } {
27:
28: # If we're in a handshake detection, look for an SSL/TLS header.
29:
30: binary scan [TCP::payload] cSS tls_xacttype tls_version tls_recordlen
31:
32: # TLS is the only thing we want to process because it's the only
33: # version that allows the servername extension to be present. When we
34: # find a supported TLS version, we'll check to make sure we're getting
35: # only a Client Hello transaction -- those are the only ones we can pull
36: # the servername from prior to connection establishment.
37:
38: switch $tls_version {
39: \"769\" -
40: \"770\" -
41: \"771\" {
42: if { ($tls_xacttype == 22) } {
43: binary scan [TCP::payload] @5c tls_action
44: if { not (($tls_action == 1) && ([TCP::payload length] > $tls_recordlen)) } {
45: set detect_handshake 0
46: }
47: }
48: }
49: default {
50: set detect_handshake 0
51: }
52: }
53:
54: if { ($detect_handshake) } {
55:
56: # If we made it this far, we're still processing a TLS client hello.
57: #
58: # Skip the TLS header (43 bytes in) and process the record body. For TLS/1.0 we
59: # expect this to contain only the session ID, cipher list, and compression
60: # list. All but the cipher list will be null since we're handling a new transaction
61: # (client hello) here. We have to determine how far out to parse the initial record
62: # so we can find the TLS extensions if they exist.
63:
64: set record_offset 43
65: binary scan [TCP::payload] @${record_offset}c tls_sessidlen
66: set record_offset [expr {$record_offset + 1 + $tls_sessidlen}]
67: binary scan [TCP::payload] @${record_offset}S tls_ciphlen
68: set record_offset [expr {$record_offset + 2 + $tls_ciphlen}]
69: binary scan [TCP::payload] @${record_offset}c tls_complen
70: set record_offset [expr {$record_offset + 1 + $tls_complen}]
71:
72: # If we're in TLS and we've not parsed all the payload in the record
73: # at this point, then we have TLS extensions to process. We will detect
74: # the TLS extension package and parse each record individually.
75:
76: if { ([TCP::payload length] >= $record_offset) } {
77: binary scan [TCP::payload] @${record_offset}S tls_extenlen
78: set record_offset [expr {$record_offset + 2}]
79: binary scan [TCP::payload] @${record_offset}a* tls_extensions
80:
81: # Loop through the TLS extension data looking for a type 00 extension
82: # record. This is the IANA code for server_name in the TLS transaction.
83:
84: for { set x 0 } { $x < $tls_extenlen } { incr x 4 } {
85: set start [expr {$x}]
86: binary scan $tls_extensions @${start}SS etype elen
87: if { ($etype == \"00\") } {
88:
89: # A servername record is present. Pull this value out of the packet data
90: # and save it for later use. We start 9 bytes into the record to bypass
91: # type, length, and SNI encoding header (which is itself 5 bytes long), and
92: # capture the servername text (minus the header).
93:
94: set grabstart [expr {$start + 9}]
95: set grabend [expr {$elen - 5}]
96: binary scan $tls_extensions @${grabstart}A${grabend} tls_servername
97: set start [expr {$start + $elen}]
98: } else {
99:
100: # Bypass all other TLS extensions.
101:
102: set start [expr {$start + $elen}]
103: }
104: set x $start
105: }
106:
107: # Check to see whether we got a servername indication from TLS. If so,
108: # make the appropriate changes.
109:
110: if { ([info exists tls_servername] ) } {
111:
112: # Look for a matching servername in the Data Group and pool.
113:
114: set ssl_profile [class match -value [string tolower $tls_servername] equals tls_servername]
115: set tls_pool [class match -value [string tolower $tls_servername] equals tls_servername_pool]
116:
117: if { $ssl_profile == \"\" } {
118:
119: # No match, so we allow this to fall through to the \"default\"
120: # clientssl profile.
121:
122: SSL::enable
123: } else {
124:
125: # A match was found in the Data Group, so we will change the SSL
126: # profile to the one we found. Hide this activity from the iRules
127: # parser.
128:
129: set ssl_profile_enable \"SSL::profile $ssl_profile\"
130: catch { eval $ssl_profile_enable }
131: if { not ($tls_pool == \"\") } {
132: pool $tls_pool
133: } else {
134: pool $default_tls_pool
135: }
136: SSL::enable
137: }
138: } else {
139:
140: # No match because no SNI field was present. Fall through to the
141: # \"default\" SSL profile.
142:
143: SSL::enable
144: }
145:
146: } else {
147:
148: # We're not in a handshake. Keep on using the currently set SSL profile
149: # for this transaction.
150:
151: SSL::enable
152: }
153:
154: # Hold down any further processing and release the TCP session further
155: # down the event loop.
156:
157: set detect_handshake 0
158: TCP::release
159: } else {
160:
161: # We've not been able to match an SNI field to an SSL profile. We will
162: # fall back to the \"default\" SSL profile selected (this might lead to
163: # certificate validation errors on non SNI-capable browsers.
164:
165: set detect_handshake 0
166: SSL::enable
167: TCP::release
168:
169: }
170: }
171: }