2 # Copyright (c) 2014, Jouni Malinen <j@w1.fi>
4 # This software may be distributed under the terms of the BSD license.
5 # See README for more details.
9 logger
= logging
.getLogger()
23 EAP_TYPE_NOTIFICATION
= 2
34 EAP_TYPE_MSCHAPV2
= 26
42 EAP_TYPE_AKA_PRIME
= 50
47 def run_pyrad_server(srv
, t_stop
, eap_handler
):
48 srv
.RunWithStop(t_stop
, eap_handler
)
50 def start_radius_server(eap_handler
):
54 import pyrad
.dictionary
58 class TestServer(pyrad
.server
.Server
):
59 def _HandleAuthPacket(self
, pkt
):
60 pyrad
.server
.Server
._HandleAuthPacket
(self
, pkt
)
62 logger
.info("Multiple EAP-Message attributes")
65 eap_req
= self
.eap_handler(self
.ctx
, eap
)
66 reply
= self
.CreateReplyPacket(pkt
)
68 if len(eap_req
) > 253:
69 logger
.info("Need to fragment EAP-Message")
71 reply
.AddAttribute("EAP-Message", eap_req
)
73 logger
.info("No EAP request available")
74 reply
.code
= pyrad
.packet
.AccessChallenge
76 hmac_obj
= hmac
.new(reply
.secret
)
77 hmac_obj
.update(struct
.pack("B", reply
.code
))
78 hmac_obj
.update(struct
.pack("B", reply
.id))
81 reply
.AddAttribute("Message-Authenticator",
82 "\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00")
83 attrs
= reply
._PktEncodeAttributes
()
86 flen
= 4 + 16 + len(attrs
)
87 hmac_obj
.update(struct
.pack(">H", flen
))
88 hmac_obj
.update(pkt
.authenticator
)
89 hmac_obj
.update(attrs
)
91 reply
.AddAttribute("Message-Authenticator", hmac_obj
.digest())
93 self
.SendReplyPacket(pkt
.fd
, reply
)
95 def RunWithStop(self
, t_stop
, eap_handler
):
96 self
._poll
= select
.poll()
98 self
._PrepareSockets
()
100 self
.eap_handler
= eap_handler
103 while not t_stop
.is_set():
104 for (fd
, event
) in self
._poll
.poll(1000):
105 if event
== select
.POLLIN
:
107 fdo
= self
._fdmap
[fd
]
108 self
._ProcessInput
(fdo
)
109 except pyrad
.server
.ServerPacketError
as err
:
110 logger
.info("pyrad server dropping packet: " + str(err
))
111 except pyrad
.packet
.PacketError
as err
:
112 logger
.info("pyrad server received invalid packet: " + str(err
))
114 logger
.error("Unexpected event in pyrad server main loop")
116 srv
= TestServer(dict=pyrad
.dictionary
.Dictionary("dictionary.radius"),
117 authport
=18138, acctport
=18139)
118 srv
.hosts
["127.0.0.1"] = pyrad
.server
.RemoteHost("127.0.0.1",
121 srv
.BindToAddress("")
122 t_stop
= threading
.Event()
123 t
= threading
.Thread(target
=run_pyrad_server
, args
=(srv
, t_stop
, eap_handler
))
126 return { 'srv': srv
, 'stop': t_stop
, 'thread': t
}
128 def stop_radius_server(srv
):
132 def start_ap(ifname
):
133 params
= hostapd
.wpa2_eap_params(ssid
="eap-test")
134 params
['auth_server_port'] = "18138"
135 hapd
= hostapd
.add_ap(ifname
, params
)
140 EAP_SAKE_SUBTYPE_CHALLENGE
= 1
141 EAP_SAKE_SUBTYPE_CONFIRM
= 2
142 EAP_SAKE_SUBTYPE_AUTH_REJECT
= 3
143 EAP_SAKE_SUBTYPE_IDENTITY
= 4
145 EAP_SAKE_AT_RAND_S
= 1
146 EAP_SAKE_AT_RAND_P
= 2
147 EAP_SAKE_AT_MIC_S
= 3
148 EAP_SAKE_AT_MIC_P
= 4
149 EAP_SAKE_AT_SERVERID
= 5
150 EAP_SAKE_AT_PEERID
= 6
151 EAP_SAKE_AT_SPI_S
= 7
152 EAP_SAKE_AT_SPI_P
= 8
153 EAP_SAKE_AT_ANY_ID_REQ
= 9
154 EAP_SAKE_AT_PERM_ID_REQ
= 10
155 EAP_SAKE_AT_ENCR_DATA
= 128
157 EAP_SAKE_AT_PADDING
= 130
158 EAP_SAKE_AT_NEXT_TMPID
= 131
159 EAP_SAKE_AT_MSK_LIFE
= 132
161 def test_eap_proto_sake(dev
, apdev
):
162 """EAP-SAKE protocol tests"""
163 def sake_challenge(ctx
):
164 logger
.info("Test: Challenge subtype")
165 return struct
.pack(">BBHBBBBBBLLLL", EAP_CODE_REQUEST
, ctx
['id'],
168 EAP_SAKE_VERSION
, 0, EAP_SAKE_SUBTYPE_CHALLENGE
,
169 EAP_SAKE_AT_RAND_S
, 18, 0, 0, 0, 0)
171 def sake_handler(ctx
, req
):
172 logger
.info("sake_handler - RX " + req
.encode("hex"))
175 ctx
['num'] = ctx
['num'] + 1
178 ctx
['id'] = (ctx
['id'] + 1) % 256
181 logger
.info("Test: Missing payload")
182 return struct
.pack(">BBHB", EAP_CODE_REQUEST
, ctx
['id'], 4 + 1,
186 logger
.info("Test: Identity subtype without any attributes")
187 return struct
.pack(">BBHBBBB", EAP_CODE_REQUEST
, ctx
['id'],
190 EAP_SAKE_VERSION
, 0, EAP_SAKE_SUBTYPE_IDENTITY
)
193 logger
.info("Test: Identity subtype")
194 return struct
.pack(">BBHBBBBBBH", EAP_CODE_REQUEST
, ctx
['id'],
197 EAP_SAKE_VERSION
, 0, EAP_SAKE_SUBTYPE_IDENTITY
,
198 EAP_SAKE_AT_ANY_ID_REQ
, 4, 0)
200 logger
.info("Test: Identity subtype (different session id)")
201 return struct
.pack(">BBHBBBBBBH", EAP_CODE_REQUEST
, ctx
['id'],
204 EAP_SAKE_VERSION
, 1, EAP_SAKE_SUBTYPE_IDENTITY
,
205 EAP_SAKE_AT_PERM_ID_REQ
, 4, 0)
208 logger
.info("Test: Identity subtype with too short attribute")
209 return struct
.pack(">BBHBBBBBB", EAP_CODE_REQUEST
, ctx
['id'],
212 EAP_SAKE_VERSION
, 0, EAP_SAKE_SUBTYPE_IDENTITY
,
213 EAP_SAKE_AT_ANY_ID_REQ
, 2)
216 logger
.info("Test: Identity subtype with truncated attribute")
217 return struct
.pack(">BBHBBBBBB", EAP_CODE_REQUEST
, ctx
['id'],
220 EAP_SAKE_VERSION
, 0, EAP_SAKE_SUBTYPE_IDENTITY
,
221 EAP_SAKE_AT_ANY_ID_REQ
, 4)
224 logger
.info("Test: Unknown subtype")
225 return struct
.pack(">BBHBBBB", EAP_CODE_REQUEST
, ctx
['id'],
228 EAP_SAKE_VERSION
, 0, 123)
231 logger
.info("Test: Challenge subtype without any attributes")
232 return struct
.pack(">BBHBBBB", EAP_CODE_REQUEST
, ctx
['id'],
235 EAP_SAKE_VERSION
, 0, EAP_SAKE_SUBTYPE_CHALLENGE
)
238 logger
.info("Test: Challenge subtype with too short AT_RAND_S")
239 return struct
.pack(">BBHBBBBBB", EAP_CODE_REQUEST
, ctx
['id'],
242 EAP_SAKE_VERSION
, 0, EAP_SAKE_SUBTYPE_CHALLENGE
,
243 EAP_SAKE_AT_RAND_S
, 2)
246 return sake_challenge(ctx
)
248 logger
.info("Test: Unexpected Identity subtype")
249 return struct
.pack(">BBHBBBBBBH", EAP_CODE_REQUEST
, ctx
['id'],
252 EAP_SAKE_VERSION
, 0, EAP_SAKE_SUBTYPE_IDENTITY
,
253 EAP_SAKE_AT_ANY_ID_REQ
, 4, 0)
256 return sake_challenge(ctx
)
258 logger
.info("Test: Unexpected Challenge subtype")
259 return struct
.pack(">BBHBBBBBBLLLL", EAP_CODE_REQUEST
, ctx
['id'],
262 EAP_SAKE_VERSION
, 0, EAP_SAKE_SUBTYPE_CHALLENGE
,
263 EAP_SAKE_AT_RAND_S
, 18, 0, 0, 0, 0)
266 return sake_challenge(ctx
)
268 logger
.info("Test: Confirm subtype without any attributes")
269 return struct
.pack(">BBHBBBB", EAP_CODE_REQUEST
, ctx
['id'],
272 EAP_SAKE_VERSION
, 0, EAP_SAKE_SUBTYPE_CONFIRM
)
275 return sake_challenge(ctx
)
277 logger
.info("Test: Confirm subtype with too short AT_MIC_S")
278 return struct
.pack(">BBHBBBBBB", EAP_CODE_REQUEST
, ctx
['id'],
281 EAP_SAKE_VERSION
, 0, EAP_SAKE_SUBTYPE_CONFIRM
,
282 EAP_SAKE_AT_MIC_S
, 2)
285 logger
.info("Test: Unexpected Confirm subtype")
286 return struct
.pack(">BBHBBBBBBLLLL", EAP_CODE_REQUEST
, ctx
['id'],
289 EAP_SAKE_VERSION
, 0, EAP_SAKE_SUBTYPE_CONFIRM
,
290 EAP_SAKE_AT_MIC_S
, 18, 0, 0, 0, 0)
293 return sake_challenge(ctx
)
295 logger
.info("Test: Confirm subtype with incorrect AT_MIC_S")
296 return struct
.pack(">BBHBBBBBBLLLL", EAP_CODE_REQUEST
, ctx
['id'],
299 EAP_SAKE_VERSION
, 0, EAP_SAKE_SUBTYPE_CONFIRM
,
300 EAP_SAKE_AT_MIC_S
, 18, 0, 0, 0, 0)
302 return sake_challenge(ctx
)
304 srv
= start_radius_server(sake_handler
)
309 hapd
= start_ap(apdev
[0]['ifname'])
311 for i
in range(0, 14):
312 dev
[0].connect("eap-test", key_mgmt
="WPA-EAP", scan_freq
="2412",
313 eap
="SAKE", identity
="sake user",
314 password_hex
="0123456789abcdef0123456789abcdef0123456789abcdef0123456789abcdef",
316 ev
= dev
[0].wait_event(["CTRL-EVENT-EAP-PROPOSED-METHOD"], timeout
=15)
318 raise Exception("Timeout on EAP start")
320 dev
[0].request("REMOVE_NETWORK all")
322 logger
.info("Too short password")
323 dev
[0].connect("eap-test", key_mgmt
="WPA-EAP", scan_freq
="2412",
324 eap
="SAKE", identity
="sake user",
325 password_hex
="0123456789abcdef0123456789abcdef0123456789abcdef0123456789abcd",
327 ev
= dev
[0].wait_event(["CTRL-EVENT-EAP-PROPOSED-METHOD"], timeout
=15)
329 raise Exception("Timeout on EAP start")
332 stop_radius_server(srv
)
334 def test_eap_proto_leap(dev
, apdev
):
335 """EAP-LEAP protocol tests"""
336 def leap_handler(ctx
, req
):
337 logger
.info("leap_handler - RX " + req
.encode("hex"))
340 ctx
['num'] = ctx
['num'] + 1
343 ctx
['id'] = (ctx
['id'] + 1) % 256
346 logger
.info("Test: Missing payload")
347 return struct
.pack(">BBHB", EAP_CODE_REQUEST
, ctx
['id'],
352 logger
.info("Test: Unexpected version")
353 return struct
.pack(">BBHBBBB", EAP_CODE_REQUEST
, ctx
['id'],
359 logger
.info("Test: Invalid challenge length")
360 return struct
.pack(">BBHBBBB", EAP_CODE_REQUEST
, ctx
['id'],
366 logger
.info("Test: Truncated challenge")
367 return struct
.pack(">BBHBBBB", EAP_CODE_REQUEST
, ctx
['id'],
373 logger
.info("Test: Valid challenge")
374 return struct
.pack(">BBHBBBBLL", EAP_CODE_REQUEST
, ctx
['id'],
379 logger
.info("Test: Missing payload in Response")
380 return struct
.pack(">BBHB", EAP_CODE_RESPONSE
, ctx
['id'],
385 logger
.info("Test: Valid challenge")
386 return struct
.pack(">BBHBBBBLL", EAP_CODE_REQUEST
, ctx
['id'],
391 logger
.info("Test: Unexpected version in Response")
392 return struct
.pack(">BBHBBBB", EAP_CODE_RESPONSE
, ctx
['id'],
398 logger
.info("Test: Valid challenge")
399 return struct
.pack(">BBHBBBBLL", EAP_CODE_REQUEST
, ctx
['id'],
404 logger
.info("Test: Invalid challenge length in Response")
405 return struct
.pack(">BBHBBBB", EAP_CODE_RESPONSE
, ctx
['id'],
411 logger
.info("Test: Valid challenge")
412 return struct
.pack(">BBHBBBBLL", EAP_CODE_REQUEST
, ctx
['id'],
417 logger
.info("Test: Truncated challenge in Response")
418 return struct
.pack(">BBHBBBB", EAP_CODE_RESPONSE
, ctx
['id'],
424 logger
.info("Test: Valid challenge")
425 return struct
.pack(">BBHBBBBLL", EAP_CODE_REQUEST
, ctx
['id'],
430 logger
.info("Test: Invalid challange value in Response")
431 return struct
.pack(">BBHBBBB6L", EAP_CODE_RESPONSE
, ctx
['id'],
438 logger
.info("Test: Valid challenge")
439 return struct
.pack(">BBHBBBBLL", EAP_CODE_REQUEST
, ctx
['id'],
444 logger
.info("Test: Valid challange value in Response")
445 return struct
.pack(">BBHBBBB24B", EAP_CODE_RESPONSE
, ctx
['id'],
449 0x48, 0x4e, 0x46, 0xe3, 0x88, 0x49, 0x46, 0xbd,
450 0x28, 0x48, 0xf8, 0x53, 0x82, 0x50, 0x00, 0x04,
451 0x93, 0x50, 0x30, 0xd7, 0x25, 0xea, 0x5f, 0x66)
454 logger
.info("Test: Valid challenge")
455 return struct
.pack(">BBHBBBBLL", EAP_CODE_REQUEST
, ctx
['id'],
460 logger
.info("Test: Success")
461 return struct
.pack(">BBHB", EAP_CODE_SUCCESS
, ctx
['id'],
464 # hostapd will drop the next frame in the sequence
467 logger
.info("Test: Valid challenge")
468 return struct
.pack(">BBHBBBBLL", EAP_CODE_REQUEST
, ctx
['id'],
473 logger
.info("Test: Failure")
474 return struct
.pack(">BBHB", EAP_CODE_FAILURE
, ctx
['id'],
480 srv
= start_radius_server(leap_handler
)
485 hapd
= start_ap(apdev
[0]['ifname'])
487 for i
in range(0, 12):
488 dev
[0].connect("eap-test", key_mgmt
="WPA-EAP", scan_freq
="2412",
489 eap
="LEAP", identity
="user", password
="password",
491 ev
= dev
[0].wait_event(["CTRL-EVENT-EAP-PROPOSED-METHOD"], timeout
=15)
493 raise Exception("Timeout on EAP start")
496 logger
.info("Wait for additional roundtrip")
498 dev
[0].request("REMOVE_NETWORK all")
500 stop_radius_server(srv
)
502 def test_eap_proto_md5(dev
, apdev
):
503 """EAP-MD5 protocol tests"""
504 def md5_handler(ctx
, req
):
505 logger
.info("md5_handler - RX " + req
.encode("hex"))
508 ctx
['num'] = ctx
['num'] + 1
511 ctx
['id'] = (ctx
['id'] + 1) % 256
514 logger
.info("Test: Missing payload")
515 return struct
.pack(">BBHB", EAP_CODE_REQUEST
, ctx
['id'],
520 logger
.info("Test: Zero-length challenge")
521 return struct
.pack(">BBHBB", EAP_CODE_REQUEST
, ctx
['id'],
527 logger
.info("Test: Truncated challenge")
528 return struct
.pack(">BBHBB", EAP_CODE_REQUEST
, ctx
['id'],
534 logger
.info("Test: Shortest possible challenge and name")
535 return struct
.pack(">BBHBBBB", EAP_CODE_REQUEST
, ctx
['id'],
542 srv
= start_radius_server(md5_handler
)
547 hapd
= start_ap(apdev
[0]['ifname'])
549 for i
in range(0, 4):
550 dev
[0].connect("eap-test", key_mgmt
="WPA-EAP", scan_freq
="2412",
551 eap
="MD5", identity
="user", password
="password",
553 ev
= dev
[0].wait_event(["CTRL-EVENT-EAP-PROPOSED-METHOD"], timeout
=15)
555 raise Exception("Timeout on EAP start")
557 dev
[0].request("REMOVE_NETWORK all")
559 stop_radius_server(srv
)