blob: e9094c8d6351da69829fad936a932ca416202b5f [file] [log] [blame]
Matteo Scandolo48d3d2d2017-08-08 13:05:27 -07001
2# Copyright 2017-present Open Networking Foundation
3#
4# Licensed under the Apache License, Version 2.0 (the "License");
5# you may not use this file except in compliance with the License.
6# You may obtain a copy of the License at
7#
8# http://www.apache.org/licenses/LICENSE-2.0
9#
10# Unless required by applicable law or agreed to in writing, software
11# distributed under the License is distributed on an "AS IS" BASIS,
12# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
13# See the License for the specific language governing permissions and
14# limitations under the License.
15
16
A R Karthick05d9b5f2016-06-08 11:53:54 -070017#
Chetan Gaonkercfcce782016-05-10 10:10:42 -070018# Copyright 2016-present Ciena Corporation
19#
20# Licensed under the Apache License, Version 2.0 (the "License");
21# you may not use this file except in compliance with the License.
22# You may obtain a copy of the License at
A R Karthick05d9b5f2016-06-08 11:53:54 -070023#
Chetan Gaonkercfcce782016-05-10 10:10:42 -070024# http://www.apache.org/licenses/LICENSE-2.0
A R Karthick05d9b5f2016-06-08 11:53:54 -070025#
Chetan Gaonkercfcce782016-05-10 10:10:42 -070026# Unless required by applicable law or agreed to in writing, software
27# distributed under the License is distributed on an "AS IS" BASIS,
28# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
29# See the License for the specific language governing permissions and
30# limitations under the License.
31#
A R Karthicka2e53d62016-02-19 17:38:30 -080032#### Authentication parameters
Chetan Gaonker35cb16f2016-03-02 03:05:28 -080033from scapy.all import *
A R Karthickaa10a202016-08-15 15:06:21 -070034from scapy_ssl_tls.ssl_tls import *
A R Karthicka2e53d62016-02-19 17:38:30 -080035from socket import *
36from struct import *
A R Karthickefcf1ab2017-09-08 18:24:16 -070037import os
A R Karthicka2e53d62016-02-19 17:38:30 -080038import sys
A R Karthick8f930292017-07-07 12:36:22 -070039import binascii
A R Karthickefcf1ab2017-09-08 18:24:16 -070040import shutil
A R Karthicka2e53d62016-02-19 17:38:30 -080041from nose.tools import assert_equal, assert_not_equal, assert_raises, assert_true
A R Karthick76a497a2017-04-12 10:59:39 -070042from CordTestUtils import log_test
A R Karthicka2e53d62016-02-19 17:38:30 -080043
44USER = "raduser"
45PASS = "radpass"
46WRONG_USER = "XXXX"
47WRONG_PASS = "XXXX"
48NO_USER = ""
49NO_PASS = ""
50DEV = "tap0"
51ETHERTYPE_PAE = 0x888e
52PAE_GROUP_ADDR = "\xff\xff\xff\xff\xff\xff"
53EAPOL_VERSION = 1
54EAPOL_EAPPACKET = 0
55EAPOL_START = 1
56EAPOL_LOGOFF = 2
57EAPOL_KEY = 3
58EAPOL_ASF = 4
59EAP_REQUEST = 1
60EAP_RESPONSE = 2
61EAP_SUCCESS = 3
62EAP_FAILURE = 4
63EAP_TYPE_ID = 1
64EAP_TYPE_MD5 = 4
65EAP_TYPE_MSCHAP = 26
66EAP_TYPE_TLS = 13
67cCertMsg = '\x0b\x00\x00\x03\x00\x00\x00'
68TLS_LENGTH_INCLUDED = 0x80
A R Karthickaa10a202016-08-15 15:06:21 -070069TLS_MORE_FRAGMENTS = 0x40
A R Karthickefcf1ab2017-09-08 18:24:16 -070070RADIUS_USER_MAC_START = (0x02 << 40) | (0x03 << 32) | (0x04 << 24) | 1
71RADIUS_USER_MAC_END = (0x02 << 40) | (0x03 << 32) | (0x04 << 24) | (0xff << 16) | ( 0xff << 8 ) | 0xff
A R Karthicka2e53d62016-02-19 17:38:30 -080072
A R Karthicka2e53d62016-02-19 17:38:30 -080073class EapolPacket(object):
A R Karthick05d9b5f2016-06-08 11:53:54 -070074
ChetanGaonker6138fcd2016-08-18 17:56:39 -070075 src_mac_map = { 'bcast': 'ff:ff:ff:ff:ff:ff',
76 'mcast': '01:80:C2:00:00:03',
77 'zeros': '00:00:00:00:00:00',
78 'default': None
79 }
80
A R Karthicka2e53d62016-02-19 17:38:30 -080081 def __init__(self, intf = 'veth0'):
82 self.intf = intf
83 self.s = None
A R Karthickaa10a202016-08-15 15:06:21 -070084 self.max_recv_size = 1600
A R Karthicka2e53d62016-02-19 17:38:30 -080085
ChetanGaonker6138fcd2016-08-18 17:56:39 -070086 def setup(self, src_mac = 'default'):
A R Karthicka2e53d62016-02-19 17:38:30 -080087 self.s = socket(AF_PACKET, SOCK_RAW, htons(ETHERTYPE_PAE))
88 self.s.bind((self.intf, ETHERTYPE_PAE))
89 self.mymac = self.s.getsockname()[4]
ChetanGaonker6138fcd2016-08-18 17:56:39 -070090 mac = None
A R Karthick8f930292017-07-07 12:36:22 -070091 mac_str = None
92 if src_mac == 'random':
93 mac = RandMAC()._fix()
94 elif src_mac in self.src_mac_map:
ChetanGaonker6138fcd2016-08-18 17:56:39 -070095 mac = self.src_mac_map[src_mac]
96 if mac is None:
97 mac = self.mymac
A R Karthick8f930292017-07-07 12:36:22 -070098 mac_str = binascii.hexlify(mac)
99 if mac_str is None:
100 mac_str = mac
ChetanGaonker6138fcd2016-08-18 17:56:39 -0700101 self.llheader = Ether(dst = PAE_GROUP_ADDR, src = mac, type = ETHERTYPE_PAE)
A R Karthick76a497a2017-04-12 10:59:39 -0700102 log_test.info('llheader packet is %s'%self.llheader.show())
A R Karthick8f930292017-07-07 12:36:22 -0700103 log_test.info('source mac of packet is %s'%mac_str)
Chetan Gaonker5b366302016-03-21 16:18:21 -0700104 self.recv_sock = L2Socket(iface = self.intf, type = ETHERTYPE_PAE)
A R Karthicka2e53d62016-02-19 17:38:30 -0800105
106 def cleanup(self):
107 if self.s is not None:
108 self.s.close()
109 self.s = None
A R Karthick05d9b5f2016-06-08 11:53:54 -0700110
A R Karthicka2e53d62016-02-19 17:38:30 -0800111 def eapol(self, req_type, payload=""):
Chetan Gaonker35cb16f2016-03-02 03:05:28 -0800112 return EAPOL(version = EAPOL_VERSION, type = req_type)/payload
A R Karthicka2e53d62016-02-19 17:38:30 -0800113
114 def eap(self, code, pkt_id, req_type=0, data=""):
Chetan Gaonker35cb16f2016-03-02 03:05:28 -0800115 return EAP(code = code, id = pkt_id, type = req_type)/data
A R Karthicka2e53d62016-02-19 17:38:30 -0800116
A R Karthickaa10a202016-08-15 15:06:21 -0700117 def eapFragmentSend(self, code, pkt_id, flags = TLS_LENGTH_INCLUDED, payload = "", fragsize = 1024):
118 req_type = EAP_TYPE_TLS
119 if code in [ EAP_SUCCESS, EAP_FAILURE ]:
120 data = pack("!BBH", code, pkt_id, 4)
121 self.eapol_send(EAPOL_EAPPACKET, data)
122 return True
123
124 if len(payload) <= fragsize:
125 if flags & TLS_LENGTH_INCLUDED:
126 flags_dlen = pack("!BL", flags, len(payload))
127 data = pack("!BBHB", code, pkt_id, 5 + len(flags_dlen) + len(payload), req_type) \
128 + flags_dlen + payload
129 self.eapol_send(EAPOL_EAPPACKET, data)
130 return True
131 flags_str = pack("!B", flags)
132 data = pack("!BBHB", code, pkt_id, 5+len(flags_str)+len(payload), req_type) + flags_str + payload
133 self.eapol_send(EAPOL_EAPPACKET, data)
134 return True
135
136 fragments = []
137 data = payload[:]
138 frag = 0
139 def eapol_frag_cb(pkt):
140 r = str(pkt)
141 tls_data = r[self.TLS_OFFSET:]
142 frag_data = fragments[frag]
143 ##change packet id in response to match request
144 eap_payload = frag_data[:1] + pack("!B", pkt[EAP].id) + frag_data[2:]
145 self.eapol_send(EAPOL_EAPPACKET, eap_payload)
146
147 while len(data) > 0:
148 data_frag = data[:fragsize]
149 data = data[fragsize:]
150 if frag == 0:
151 ##first frag, include the total length
152 flags_dlen = pack("!BL", TLS_LENGTH_INCLUDED | TLS_MORE_FRAGMENTS, len(payload))
153 fragments.append(pack("!BBHB", code, pkt_id, 5 + len(flags_dlen) + len(data_frag), req_type) \
154 + flags_dlen + data_frag)
155 else:
156 if len(data) > 0:
157 flags = TLS_MORE_FRAGMENTS
158 else:
159 flags = 0
160 flags_str = pack("!B", flags)
161 fragments.append(pack("!BBHB", code, pkt_id, 5+len(flags_str)+len(data_frag), req_type) + \
162 flags_str + data_frag)
163 frag += 1
164
165 frag = 0
166 self.eapol_send(EAPOL_EAPPACKET, fragments[frag])
167 for frag in range(len(fragments)-1):
168 frag += 1
169 r = self.eapol_scapy_recv(cb = eapol_frag_cb,
170 lfilter = lambda pkt: EAP in pkt and pkt[EAP].type == EAP_TYPE_TLS and \
171 pkt[EAP].code == EAP.REQUEST)
172
173 return True
174
A R Karthicka2e53d62016-02-19 17:38:30 -0800175 def eapTLS(self, code, pkt_id, flags = TLS_LENGTH_INCLUDED, data=""):
176 req_type = EAP_TYPE_TLS
177 if code in [EAP_SUCCESS, EAP_FAILURE]:
178 return pack("!BBH", code, pkt_id, 4)
179 else:
180 if flags & TLS_LENGTH_INCLUDED:
181 flags_dlen = pack("!BL", flags, len(data))
182 return pack("!BBHB", code, pkt_id, 5+len(flags_dlen)+len(data), req_type) + flags_dlen + data
183 flags_str = pack("!B", flags)
184 return pack("!BBHB", code, pkt_id, 5+len(flags_str)+len(data), req_type) + flags_str + data
185
A R Karthickaa10a202016-08-15 15:06:21 -0700186 def eapTLSFragment(self, code, pkt_id, frag, data="", data_len = 0):
187 req_type = EAP_TYPE_TLS
188 if frag == 0:
189 flags = TLS_LENGTH_INCLUDED | TLS_MORE_FRAGMENTS
190 elif frag > 0:
191 flags = TLS_MORE_FRAGMENTS
192 else:
193 #last fragment
194 flags = 0
195 if data_len == 0:
196 data_len = len(data)
197 if flags & TLS_LENGTH_INCLUDED:
198 flags_dlen = pack("!BL", flags, data_len)
199 return pack("!BBHB", code, pkt_id, 5+len(flags_dlen)+len(data), req_type) + flags_dlen + data
200 flags_str = pack("!B", flags)
201 return pack("!BBHB", code, pkt_id, 5+len(flags_str)+len(data), req_type) + flags_str + data
202
A R Karthicka2e53d62016-02-19 17:38:30 -0800203 def eapol_send(self, eapol_type, eap_payload):
Chetan Gaonker35cb16f2016-03-02 03:05:28 -0800204 return sendp(self.llheader/self.eapol(eapol_type, eap_payload), iface=self.intf)
A R Karthicka2e53d62016-02-19 17:38:30 -0800205
206 def eapol_recv(self):
A R Karthickaa10a202016-08-15 15:06:21 -0700207 p = self.s.recv(self.max_recv_size)[14:]
A R Karthicka2e53d62016-02-19 17:38:30 -0800208 vers,pkt_type,eapollen = unpack("!BBH",p[:4])
209 print "Version %d, type %d, len %d" %(vers, pkt_type, eapollen)
210 assert_equal(pkt_type, EAPOL_EAPPACKET)
211 return p[4:]
212
A.R Karthickaa859b22017-06-12 14:50:35 -0700213 def eapol_scapy_recv(self, cb = None, lfilter = None, count = 1, timeout = 10):
Chetan Gaonker5b366302016-03-21 16:18:21 -0700214 def eapol_default_cb(pkt): pass
215 if cb is None:
216 cb = eapol_default_cb
A R Karthick05d9b5f2016-06-08 11:53:54 -0700217 return sniff(prn = cb, lfilter = lfilter, count = count, timeout = timeout, opened_socket = self.recv_sock)
Chetan Gaonker5b366302016-03-21 16:18:21 -0700218
A R Karthicka2e53d62016-02-19 17:38:30 -0800219 def eapol_start(self):
220 eap_payload = self.eap(EAPOL_START, 2)
221 return self.eapol_send(EAPOL_START, eap_payload)
222
A R Karthick307483c2016-06-06 17:05:19 -0700223 def eapol_logoff(self):
224 eap_payload = self.eap(EAPOL_LOGOFF, 2)
225 return self.eapol_send(EAPOL_LOGOFF, eap_payload)
226
A R Karthicka2e53d62016-02-19 17:38:30 -0800227 def eapol_id_req(self, pkt_id = 0, user = USER):
228 eap_payload = self.eap(EAP_RESPONSE, pkt_id, EAP_TYPE_ID, user)
229 return self.eapol_send(EAPOL_EAPPACKET, eap_payload)
230
Chetan Gaonker4a25e2b2016-03-04 14:45:15 -0800231 def eap_md5_challenge_recv(self,rad_pwd):
232 PASS = rad_pwd
233 print 'Inside EAP MD5 Challenge Exchange'
A R Karthickaa10a202016-08-15 15:06:21 -0700234 p = self.s.recv(self.max_recv_size)[14:]
Chetan Gaonker4a25e2b2016-03-04 14:45:15 -0800235 vers,pkt_type,eapollen = unpack("!BBH",p[:4])
236 print "EAPOL Version %d, type %d, len %d" %(vers, pkt_type, eapollen)
237 code, pkt_id, eaplen = unpack("!BBH", p[4:8])
238 print "EAP Code %d, id %d, len %d" %(code, pkt_id, eaplen)
239 assert_equal(code, EAP_REQUEST)
240 reqtype = unpack("!B", p[8:9])[0]
241 reqdata = p[9:4+eaplen]
242 print 'Request type is %d' %(reqtype)
243 assert_equal(reqtype, EAP_TYPE_MD5)
244 challenge=pack("!B",pkt_id)+PASS+reqdata[1:]
245 print "Generating md5 challenge for %s" % challenge
246 return (challenge,pkt_id)
247
248 def eap_Status(self):
249 print 'Inside EAP Status'
A R Karthickaa10a202016-08-15 15:06:21 -0700250 p = self.s.recv(self.max_recv_size)[14:]
Chetan Gaonker4a25e2b2016-03-04 14:45:15 -0800251 code, id, eaplen = unpack("!BBH", p[4:8])
252 return code
253
Thangavelu K Sef6f0a52016-12-14 19:57:05 +0000254 @classmethod
255 def eap_invalid_tls_packets_info(self, invalid_field_name = None, invalid_field_value = None):
A R Karthick76a497a2017-04-12 10:59:39 -0700256 log_test.info( 'Changing invalid field values in tls auth packets' )
Thangavelu K Sef6f0a52016-12-14 19:57:05 +0000257 if invalid_field_name == 'eapolTlsVersion':
258 global EAPOL_VERSION
A R Karthick76a497a2017-04-12 10:59:39 -0700259 log_test.info( 'Changing invalid field values in tls auth packets====== version changing' )
Thangavelu K Sef6f0a52016-12-14 19:57:05 +0000260 EAPOL_VERSION = invalid_field_value
261 if invalid_field_name == 'eapolTlsType':
262 global EAP_TYPE_TLS
A R Karthick76a497a2017-04-12 10:59:39 -0700263 log_test.info( 'Changing invalid field values in tls auth packets====== EAP TYPE TLS changing' )
Thangavelu K Sef6f0a52016-12-14 19:57:05 +0000264 EAP_TYPE_TLS = invalid_field_value
265 if invalid_field_name == 'eapolTypeID':
266 global EAP_TYPE_ID
A R Karthick76a497a2017-04-12 10:59:39 -0700267 log_test.info( 'Changing invalid field values in tls auth packets====== EAP TYPE TLS changing' )
Thangavelu K Sef6f0a52016-12-14 19:57:05 +0000268 EAP_TYPE_ID = invalid_field_value
269 if invalid_field_name == 'eapolResponse':
270 global EAP_RESPONSE
A R Karthick76a497a2017-04-12 10:59:39 -0700271 log_test.info( 'Changing invalid field values in tls auth packets====== EAP TYPE TLS changing' )
Thangavelu K Sef6f0a52016-12-14 19:57:05 +0000272 EAP_RESPONSE = invalid_field_value
273
274
275 @classmethod
276 def eap_tls_packets_field_value_replace(self, invalid_field_name = None):
A R Karthick76a497a2017-04-12 10:59:39 -0700277 log_test.info( 'Changing invalid field values in tls auth packets' )
Thangavelu K Sef6f0a52016-12-14 19:57:05 +0000278 if invalid_field_name == 'eapolTlsVersion':
279 global EAPOL_VERSION
280 EAPOL_VERSION = 1
A R Karthick76a497a2017-04-12 10:59:39 -0700281 log_test.info( 'Changing invalid field values in tls auth packets====== version changing' )
Thangavelu K Sef6f0a52016-12-14 19:57:05 +0000282 if invalid_field_name == 'eapolTlsType':
283 global EAP_TYPE_TLS
284 EAP_TYPE_TLS = 13
A R Karthick76a497a2017-04-12 10:59:39 -0700285 log_test.info( 'Changing invalid field values in tls auth packets====== version changing' )
Thangavelu K Sef6f0a52016-12-14 19:57:05 +0000286 if invalid_field_name == 'eapolTypeID':
287 global EAP_TYPE_ID
288 EAP_TYPE_ID = 1
A R Karthick76a497a2017-04-12 10:59:39 -0700289 log_test.info( 'Changing invalid field values in tls auth packets====== version changing' )
Thangavelu K Sef6f0a52016-12-14 19:57:05 +0000290 if invalid_field_name == 'eapolResponse':
291 global EAP_RESPONSE
292 EAP_RESPONSE = 2
A R Karthick76a497a2017-04-12 10:59:39 -0700293 log_test.info( 'Changing invalid field values in tls auth packets====== version changing' )
A R Karthick1555c7c2017-09-07 14:59:41 -0700294
A R Karthickefcf1ab2017-09-08 18:24:16 -0700295def get_radius_macs(num, start = 0, end = 0):
A R Karthick1555c7c2017-09-07 14:59:41 -0700296 """Generate radius server mac addresses"""
297 """Scope to generate 256*256*256 mac addresses"""
A R Karthickefcf1ab2017-09-08 18:24:16 -0700298 if start == 0 or end == 0:
299 s = (0x00 << 40) | (0x02 << 32) | ( 0x03 << 24) | (1)
300 e = (0x00 << 40) | (0x02 << 32) | ( 0x03 << 24) | (0xff << 16) | (0xff << 8) | (0xff)
301 else:
302 s = start
303 e = end
A R Karthick1555c7c2017-09-07 14:59:41 -0700304 n_macs = []
305 for v in xrange(s, e):
306 mask = (v & 0xff0000) == 0xff0000 or \
307 (v & 0x00ff00) == 0x00ff00 or \
308 (v & 0x0000ff) == 0x0000ff
309 if mask:
310 continue
311 n_macs.append(v)
312 if len(n_macs) == num:
313 break
314
315 def n_to_mac(n):
316 n_tuple = ( (n >> 40) & 0xff,
317 (n >> 32) & 0xff,
318 (n >> 24) & 0xff,
319 (n >> 16) & 0xff,
320 (n >> 8) & 0xff,
321 n & 0xff,
322 )
323 return '%02x:%02x:%02x:%02x:%02x:%02x' %(n_tuple)
324
325 #convert the number to macs
326 return map(n_to_mac, n_macs)
A R Karthickefcf1ab2017-09-08 18:24:16 -0700327
328def get_radius_networks(num):
329 PORT_SUBNET_START = '12.0.0.0'
330 PORT_SUBNET_MASK = '/24'
331 PORT_SUBNET_END = '220.0.0.0'
332 port_start_list = map(lambda ip: int(ip), PORT_SUBNET_START.split('.'))
333 port_end_list = map(lambda ip: int(ip), PORT_SUBNET_END.split('.'))
334 port_subnet_start = (port_start_list[0] << 24) | ( port_start_list[1] << 16 ) | ( port_start_list[2] << 8 ) | 0
335 port_subnet_end = (port_end_list[0] << 24) | ( port_end_list[1] << 16 ) | ( port_end_list[2] << 8 ) | 0
336 mask = int(PORT_SUBNET_MASK[1:])
337 net_list = []
338 for n in xrange(port_subnet_start, port_subnet_end, 256):
339 subnet = ((n >> 24) & 0xff, (n >> 16) & 0xff, (n >> 8) & 0xff, 0, mask)
340 prefix = subnet[:3]
341 gw = prefix + (1,)
342 subnet_s = '{}.{}.{}.{}/{}'.format(*subnet)
343 prefix_s = '{}.{}.{}'.format(*prefix)
344 gw_s = '{}.{}.{}.{}'.format(*gw)
345 net_list.append((prefix_s, subnet_s, gw_s))
346 if len(net_list) >= num:
347 break
348
349 return net_list
350
351def get_radius_user_file():
352 cur_dir = os.path.dirname(os.path.realpath(__file__))
353 radius_authorize = 'setup/radius-config/freeradius/mods-config/files/authorize'
354 radius_user_file = os.path.join(cur_dir, '..', *radius_authorize.split('/'))
355 return radius_user_file
356
357def radius_add_users(num):
358 global RADIUS_USER_MAC_START, RADIUS_USER_MAC_END
359 template = '''
360%s Cleartext-Password := "radpass"
361\tReply-Message := "Hello, %%{User-Name}"
362'''
363 radius_user_file = get_radius_user_file()
364 if not os.access(radius_user_file, os.F_OK):
365 return False
366 mac_start = RADIUS_USER_MAC_START
367 mac_end = RADIUS_USER_MAC_END
368 macs = get_radius_macs(num, start = mac_start, end = mac_end)
369 save_file = '{}.save'.format(radius_user_file)
370 new_file = '{}.new'.format(radius_user_file)
371 shutil.copy(radius_user_file, save_file)
372 with open(radius_user_file, 'r') as f:
373 lines = f.readlines()
374 for m in macs:
375 lines.append(template %(m))
376 with open(new_file, 'w') as f:
377 f.writelines(lines)
378 os.rename(new_file, radius_user_file)
379 return True
380
381def radius_restore_users():
382 radius_user_file = get_radius_user_file()
383 save_file = '{}.save'.format(radius_user_file)
384 if not os.access(save_file, os.F_OK):
385 return False
386 os.rename(save_file, radius_user_file)
387 return True