Updated through tag hostap_2_5 from git://w1.fi/hostap.git
[mech_eap.git] / libeap / tests / hwsim / test_radius.py
1 # RADIUS tests
2 # Copyright (c) 2013-2015, Jouni Malinen <j@w1.fi>
3 #
4 # This software may be distributed under the terms of the BSD license.
5 # See README for more details.
6
7 import hashlib
8 import hmac
9 import logging
10 logger = logging.getLogger()
11 import os
12 import select
13 import struct
14 import subprocess
15 import threading
16 import time
17
18 import hostapd
19 from utils import HwsimSkip, require_under_vm
20
21 def connect(dev, ssid, wait_connect=True):
22     dev.connect(ssid, key_mgmt="WPA-EAP", scan_freq="2412",
23                 eap="PSK", identity="psk.user@example.com",
24                 password_hex="0123456789abcdef0123456789abcdef",
25                 wait_connect=wait_connect)
26
27 def test_radius_auth_unreachable(dev, apdev):
28     """RADIUS Authentication server unreachable"""
29     params = hostapd.wpa2_eap_params(ssid="radius-auth")
30     params['auth_server_port'] = "18139"
31     hostapd.add_ap(apdev[0]['ifname'], params)
32     hapd = hostapd.Hostapd(apdev[0]['ifname'])
33     connect(dev[0], "radius-auth", wait_connect=False)
34     ev = dev[0].wait_event(["CTRL-EVENT-EAP-STARTED"])
35     if ev is None:
36         raise Exception("Timeout on EAP start")
37     logger.info("Checking for RADIUS retries")
38     time.sleep(4)
39     mib = hapd.get_mib()
40     if "radiusAuthClientAccessRequests" not in mib:
41         raise Exception("Missing MIB fields")
42     if int(mib["radiusAuthClientAccessRetransmissions"]) < 1:
43         raise Exception("Missing RADIUS Authentication retransmission")
44     if int(mib["radiusAuthClientPendingRequests"]) < 1:
45         raise Exception("Missing pending RADIUS Authentication request")
46
47 def test_radius_auth_unreachable2(dev, apdev):
48     """RADIUS Authentication server unreachable (2)"""
49     subprocess.call(['ip', 'ro', 'replace', '192.168.213.17', 'dev', 'lo'])
50     params = hostapd.wpa2_eap_params(ssid="radius-auth")
51     params['auth_server_addr'] = "192.168.213.17"
52     params['auth_server_port'] = "18139"
53     hostapd.add_ap(apdev[0]['ifname'], params)
54     hapd = hostapd.Hostapd(apdev[0]['ifname'])
55     subprocess.call(['ip', 'ro', 'del', '192.168.213.17', 'dev', 'lo'])
56     connect(dev[0], "radius-auth", wait_connect=False)
57     ev = dev[0].wait_event(["CTRL-EVENT-EAP-STARTED"])
58     if ev is None:
59         raise Exception("Timeout on EAP start")
60     logger.info("Checking for RADIUS retries")
61     time.sleep(4)
62     mib = hapd.get_mib()
63     if "radiusAuthClientAccessRequests" not in mib:
64         raise Exception("Missing MIB fields")
65     if int(mib["radiusAuthClientAccessRetransmissions"]) < 1:
66         raise Exception("Missing RADIUS Authentication retransmission")
67
68 def test_radius_auth_unreachable3(dev, apdev):
69     """RADIUS Authentication server initially unreachable, but then available"""
70     subprocess.call(['ip', 'ro', 'replace', 'blackhole', '192.168.213.18'])
71     params = hostapd.wpa2_eap_params(ssid="radius-auth")
72     params['auth_server_addr'] = "192.168.213.18"
73     hostapd.add_ap(apdev[0]['ifname'], params)
74     hapd = hostapd.Hostapd(apdev[0]['ifname'])
75     connect(dev[0], "radius-auth", wait_connect=False)
76     ev = dev[0].wait_event(["CTRL-EVENT-EAP-STARTED"])
77     if ev is None:
78         raise Exception("Timeout on EAP start")
79     subprocess.call(['ip', 'ro', 'del', 'blackhole', '192.168.213.18'])
80     time.sleep(0.1)
81     dev[0].request("DISCONNECT")
82     hapd.set('auth_server_addr_replace', '127.0.0.1')
83     dev[0].request("RECONNECT")
84
85     dev[0].wait_connected()
86
87 def test_radius_acct_unreachable(dev, apdev):
88     """RADIUS Accounting server unreachable"""
89     params = hostapd.wpa2_eap_params(ssid="radius-acct")
90     params['acct_server_addr'] = "127.0.0.1"
91     params['acct_server_port'] = "18139"
92     params['acct_server_shared_secret'] = "radius"
93     hostapd.add_ap(apdev[0]['ifname'], params)
94     hapd = hostapd.Hostapd(apdev[0]['ifname'])
95     connect(dev[0], "radius-acct")
96     logger.info("Checking for RADIUS retries")
97     time.sleep(4)
98     mib = hapd.get_mib()
99     if "radiusAccClientRetransmissions" not in mib:
100         raise Exception("Missing MIB fields")
101     if int(mib["radiusAccClientRetransmissions"]) < 2:
102         raise Exception("Missing RADIUS Accounting retransmissions")
103     if int(mib["radiusAccClientPendingRequests"]) < 2:
104         raise Exception("Missing pending RADIUS Accounting requests")
105
106 def test_radius_acct_unreachable2(dev, apdev):
107     """RADIUS Accounting server unreachable(2)"""
108     subprocess.call(['ip', 'ro', 'replace', '192.168.213.17', 'dev', 'lo'])
109     params = hostapd.wpa2_eap_params(ssid="radius-acct")
110     params['acct_server_addr'] = "192.168.213.17"
111     params['acct_server_port'] = "18139"
112     params['acct_server_shared_secret'] = "radius"
113     hostapd.add_ap(apdev[0]['ifname'], params)
114     hapd = hostapd.Hostapd(apdev[0]['ifname'])
115     subprocess.call(['ip', 'ro', 'del', '192.168.213.17', 'dev', 'lo'])
116     connect(dev[0], "radius-acct")
117     logger.info("Checking for RADIUS retries")
118     time.sleep(4)
119     mib = hapd.get_mib()
120     if "radiusAccClientRetransmissions" not in mib:
121         raise Exception("Missing MIB fields")
122     if int(mib["radiusAccClientRetransmissions"]) < 1 and int(mib["radiusAccClientPendingRequests"]) < 1:
123         raise Exception("Missing pending or retransmitted RADIUS Accounting requests")
124
125 def test_radius_acct_unreachable3(dev, apdev):
126     """RADIUS Accounting server initially unreachable, but then available"""
127     require_under_vm()
128     subprocess.call(['ip', 'ro', 'replace', 'blackhole', '192.168.213.18'])
129     as_hapd = hostapd.Hostapd("as")
130     as_mib_start = as_hapd.get_mib(param="radius_server")
131     params = hostapd.wpa2_eap_params(ssid="radius-acct")
132     params['acct_server_addr'] = "192.168.213.18"
133     params['acct_server_port'] = "1813"
134     params['acct_server_shared_secret'] = "radius"
135     hostapd.add_ap(apdev[0]['ifname'], params)
136     hapd = hostapd.Hostapd(apdev[0]['ifname'])
137     connect(dev[0], "radius-acct")
138     subprocess.call(['ip', 'ro', 'del', 'blackhole', '192.168.213.18'])
139     time.sleep(0.1)
140     dev[0].request("DISCONNECT")
141     hapd.set('acct_server_addr_replace', '127.0.0.1')
142     dev[0].request("RECONNECT")
143     dev[0].wait_connected()
144     time.sleep(1)
145     as_mib_end = as_hapd.get_mib(param="radius_server")
146     req_s = int(as_mib_start['radiusAccServTotalResponses'])
147     req_e = int(as_mib_end['radiusAccServTotalResponses'])
148     if req_e <= req_s:
149         raise Exception("Unexpected RADIUS server acct MIB value")
150
151 def test_radius_acct_unreachable4(dev, apdev):
152     """RADIUS Accounting server unreachable and multiple STAs"""
153     params = hostapd.wpa2_eap_params(ssid="radius-acct")
154     params['acct_server_addr'] = "127.0.0.1"
155     params['acct_server_port'] = "18139"
156     params['acct_server_shared_secret'] = "radius"
157     hostapd.add_ap(apdev[0]['ifname'], params)
158     hapd = hostapd.Hostapd(apdev[0]['ifname'])
159     for i in range(20):
160         connect(dev[0], "radius-acct")
161         dev[0].request("REMOVE_NETWORK all")
162         dev[0].wait_disconnected()
163
164 def test_radius_acct(dev, apdev):
165     """RADIUS Accounting"""
166     as_hapd = hostapd.Hostapd("as")
167     as_mib_start = as_hapd.get_mib(param="radius_server")
168     params = hostapd.wpa2_eap_params(ssid="radius-acct")
169     params['acct_server_addr'] = "127.0.0.1"
170     params['acct_server_port'] = "1813"
171     params['acct_server_shared_secret'] = "radius"
172     params['radius_auth_req_attr'] = [ "126:s:Operator", "77:s:testing" ]
173     params['radius_acct_req_attr'] = [ "126:s:Operator", "77:s:testing" ]
174     hostapd.add_ap(apdev[0]['ifname'], params)
175     hapd = hostapd.Hostapd(apdev[0]['ifname'])
176     connect(dev[0], "radius-acct")
177     dev[1].connect("radius-acct", key_mgmt="WPA-EAP", scan_freq="2412",
178                    eap="PAX", identity="test-class",
179                    password_hex="0123456789abcdef0123456789abcdef")
180     dev[2].connect("radius-acct", key_mgmt="WPA-EAP",
181                    eap="GPSK", identity="gpsk-cui",
182                    password="abcdefghijklmnop0123456789abcdef",
183                    scan_freq="2412")
184     logger.info("Checking for RADIUS counters")
185     count = 0
186     while True:
187         mib = hapd.get_mib()
188         if int(mib['radiusAccClientResponses']) >= 3:
189             break
190         time.sleep(0.1)
191         count += 1
192         if count > 10:
193             raise Exception("Did not receive Accounting-Response packets")
194
195     if int(mib['radiusAccClientRetransmissions']) > 0:
196         raise Exception("Unexpected Accounting-Request retransmission")
197
198     as_mib_end = as_hapd.get_mib(param="radius_server")
199
200     req_s = int(as_mib_start['radiusAccServTotalRequests'])
201     req_e = int(as_mib_end['radiusAccServTotalRequests'])
202     if req_e < req_s + 2:
203         raise Exception("Unexpected RADIUS server acct MIB value")
204
205     acc_s = int(as_mib_start['radiusAuthServAccessAccepts'])
206     acc_e = int(as_mib_end['radiusAuthServAccessAccepts'])
207     if acc_e < acc_s + 1:
208         raise Exception("Unexpected RADIUS server auth MIB value")
209
210 def test_radius_acct_pmksa_caching(dev, apdev):
211     """RADIUS Accounting with PMKSA caching"""
212     as_hapd = hostapd.Hostapd("as")
213     as_mib_start = as_hapd.get_mib(param="radius_server")
214     params = hostapd.wpa2_eap_params(ssid="radius-acct")
215     params['acct_server_addr'] = "127.0.0.1"
216     params['acct_server_port'] = "1813"
217     params['acct_server_shared_secret'] = "radius"
218     hapd = hostapd.add_ap(apdev[0]['ifname'], params)
219     connect(dev[0], "radius-acct")
220     dev[1].connect("radius-acct", key_mgmt="WPA-EAP", scan_freq="2412",
221                    eap="PAX", identity="test-class",
222                    password_hex="0123456789abcdef0123456789abcdef")
223     for d in [ dev[0], dev[1] ]:
224         d.request("REASSOCIATE")
225         d.wait_connected(timeout=15, error="Reassociation timed out")
226
227     count = 0
228     while True:
229         mib = hapd.get_mib()
230         if int(mib['radiusAccClientResponses']) >= 4:
231             break
232         time.sleep(0.1)
233         count += 1
234         if count > 10:
235             raise Exception("Did not receive Accounting-Response packets")
236
237     if int(mib['radiusAccClientRetransmissions']) > 0:
238         raise Exception("Unexpected Accounting-Request retransmission")
239
240     as_mib_end = as_hapd.get_mib(param="radius_server")
241
242     req_s = int(as_mib_start['radiusAccServTotalRequests'])
243     req_e = int(as_mib_end['radiusAccServTotalRequests'])
244     if req_e < req_s + 2:
245         raise Exception("Unexpected RADIUS server acct MIB value")
246
247     acc_s = int(as_mib_start['radiusAuthServAccessAccepts'])
248     acc_e = int(as_mib_end['radiusAuthServAccessAccepts'])
249     if acc_e < acc_s + 1:
250         raise Exception("Unexpected RADIUS server auth MIB value")
251
252 def test_radius_acct_interim(dev, apdev):
253     """RADIUS Accounting interim update"""
254     as_hapd = hostapd.Hostapd("as")
255     params = hostapd.wpa2_eap_params(ssid="radius-acct")
256     params['acct_server_addr'] = "127.0.0.1"
257     params['acct_server_port'] = "1813"
258     params['acct_server_shared_secret'] = "radius"
259     params['radius_acct_interim_interval'] = "1"
260     hostapd.add_ap(apdev[0]['ifname'], params)
261     hapd = hostapd.Hostapd(apdev[0]['ifname'])
262     connect(dev[0], "radius-acct")
263     logger.info("Checking for RADIUS counters")
264     as_mib_start = as_hapd.get_mib(param="radius_server")
265     time.sleep(3.1)
266     as_mib_end = as_hapd.get_mib(param="radius_server")
267     req_s = int(as_mib_start['radiusAccServTotalRequests'])
268     req_e = int(as_mib_end['radiusAccServTotalRequests'])
269     if req_e < req_s + 3:
270         raise Exception("Unexpected RADIUS server acct MIB value")
271
272 def test_radius_acct_interim_unreachable(dev, apdev):
273     """RADIUS Accounting interim update with unreachable server"""
274     params = hostapd.wpa2_eap_params(ssid="radius-acct")
275     params['acct_server_addr'] = "127.0.0.1"
276     params['acct_server_port'] = "18139"
277     params['acct_server_shared_secret'] = "radius"
278     params['radius_acct_interim_interval'] = "1"
279     hapd = hostapd.add_ap(apdev[0]['ifname'], params)
280     start = hapd.get_mib()
281     connect(dev[0], "radius-acct")
282     logger.info("Waiting for interium accounting updates")
283     time.sleep(3.1)
284     end = hapd.get_mib()
285     req_s = int(start['radiusAccClientTimeouts'])
286     req_e = int(end['radiusAccClientTimeouts'])
287     if req_e < req_s + 2:
288         raise Exception("Unexpected RADIUS server acct MIB value")
289
290 def send_and_check_reply(srv, req, code, error_cause=0):
291     reply = srv.SendPacket(req)
292     logger.debug("RADIUS response from hostapd")
293     for i in reply.keys():
294         logger.debug("%s: %s" % (i, reply[i]))
295     if reply.code != code:
296         raise Exception("Unexpected response code")
297     if error_cause:
298         if 'Error-Cause' not in reply:
299             raise Exception("Missing Error-Cause")
300             if reply['Error-Cause'][0] != error_cause:
301                 raise Exception("Unexpected Error-Cause: {}".format(reply['Error-Cause']))
302
303 def test_radius_das_disconnect(dev, apdev):
304     """RADIUS Dynamic Authorization Extensions - Disconnect"""
305     try:
306         import pyrad.client
307         import pyrad.packet
308         import pyrad.dictionary
309         import radius_das
310     except ImportError:
311         raise HwsimSkip("No pyrad modules available")
312
313     params = hostapd.wpa2_eap_params(ssid="radius-das")
314     params['radius_das_port'] = "3799"
315     params['radius_das_client'] = "127.0.0.1 secret"
316     params['radius_das_require_event_timestamp'] = "1"
317     params['own_ip_addr'] = "127.0.0.1"
318     params['nas_identifier'] = "nas.example.com"
319     hapd = hostapd.add_ap(apdev[0]['ifname'], params)
320     connect(dev[0], "radius-das")
321     addr = dev[0].p2p_interface_addr()
322     sta = hapd.get_sta(addr)
323     id = sta['dot1xAuthSessionId']
324
325     dict = pyrad.dictionary.Dictionary("dictionary.radius")
326
327     srv = pyrad.client.Client(server="127.0.0.1", acctport=3799,
328                               secret="secret", dict=dict)
329     srv.retries = 1
330     srv.timeout = 1
331
332     logger.info("Disconnect-Request with incorrect secret")
333     req = radius_das.DisconnectPacket(dict=dict, secret="incorrect",
334                                       User_Name="foo",
335                                       NAS_Identifier="localhost",
336                                       Event_Timestamp=int(time.time()))
337     logger.debug(req)
338     try:
339         reply = srv.SendPacket(req)
340         raise Exception("Unexpected response to Disconnect-Request")
341     except pyrad.client.Timeout:
342         logger.info("Disconnect-Request with incorrect secret properly ignored")
343
344     logger.info("Disconnect-Request without Event-Timestamp")
345     req = radius_das.DisconnectPacket(dict=dict, secret="secret",
346                                       User_Name="psk.user@example.com")
347     logger.debug(req)
348     try:
349         reply = srv.SendPacket(req)
350         raise Exception("Unexpected response to Disconnect-Request")
351     except pyrad.client.Timeout:
352         logger.info("Disconnect-Request without Event-Timestamp properly ignored")
353
354     logger.info("Disconnect-Request with non-matching Event-Timestamp")
355     req = radius_das.DisconnectPacket(dict=dict, secret="secret",
356                                       User_Name="psk.user@example.com",
357                                       Event_Timestamp=123456789)
358     logger.debug(req)
359     try:
360         reply = srv.SendPacket(req)
361         raise Exception("Unexpected response to Disconnect-Request")
362     except pyrad.client.Timeout:
363         logger.info("Disconnect-Request with non-matching Event-Timestamp properly ignored")
364
365     logger.info("Disconnect-Request with unsupported attribute")
366     req = radius_das.DisconnectPacket(dict=dict, secret="secret",
367                                       User_Name="foo",
368                                       User_Password="foo",
369                                       Event_Timestamp=int(time.time()))
370     send_and_check_reply(srv, req, pyrad.packet.DisconnectNAK, 401)
371
372     logger.info("Disconnect-Request with invalid Calling-Station-Id")
373     req = radius_das.DisconnectPacket(dict=dict, secret="secret",
374                                       User_Name="foo",
375                                       Calling_Station_Id="foo",
376                                       Event_Timestamp=int(time.time()))
377     send_and_check_reply(srv, req, pyrad.packet.DisconnectNAK, 407)
378
379     logger.info("Disconnect-Request with mismatching User-Name")
380     req = radius_das.DisconnectPacket(dict=dict, secret="secret",
381                                       User_Name="foo",
382                                       Event_Timestamp=int(time.time()))
383     send_and_check_reply(srv, req, pyrad.packet.DisconnectNAK, 503)
384
385     logger.info("Disconnect-Request with mismatching Calling-Station-Id")
386     req = radius_das.DisconnectPacket(dict=dict, secret="secret",
387                                       Calling_Station_Id="12:34:56:78:90:aa",
388                                       Event_Timestamp=int(time.time()))
389     send_and_check_reply(srv, req, pyrad.packet.DisconnectNAK, 503)
390
391     logger.info("Disconnect-Request with mismatching Acct-Session-Id")
392     req = radius_das.DisconnectPacket(dict=dict, secret="secret",
393                                       Acct_Session_Id="12345678-87654321",
394                                       Event_Timestamp=int(time.time()))
395     send_and_check_reply(srv, req, pyrad.packet.DisconnectNAK, 503)
396
397     logger.info("Disconnect-Request with mismatching Acct-Session-Id (len)")
398     req = radius_das.DisconnectPacket(dict=dict, secret="secret",
399                                       Acct_Session_Id="12345678",
400                                       Event_Timestamp=int(time.time()))
401     send_and_check_reply(srv, req, pyrad.packet.DisconnectNAK, 503)
402
403     logger.info("Disconnect-Request with mismatching Acct-Multi-Session-Id")
404     req = radius_das.DisconnectPacket(dict=dict, secret="secret",
405                                       Acct_Multi_Session_Id="12345678+87654321",
406                                       Event_Timestamp=int(time.time()))
407     send_and_check_reply(srv, req, pyrad.packet.DisconnectNAK, 503)
408
409     logger.info("Disconnect-Request with mismatching Acct-Multi-Session-Id (len)")
410     req = radius_das.DisconnectPacket(dict=dict, secret="secret",
411                                       Acct_Multi_Session_Id="12345678",
412                                       Event_Timestamp=int(time.time()))
413     send_and_check_reply(srv, req, pyrad.packet.DisconnectNAK, 503)
414
415     logger.info("Disconnect-Request with no session identification attributes")
416     req = radius_das.DisconnectPacket(dict=dict, secret="secret",
417                                       Event_Timestamp=int(time.time()))
418     send_and_check_reply(srv, req, pyrad.packet.DisconnectNAK, 503)
419
420     ev = dev[0].wait_event(["CTRL-EVENT-DISCONNECTED"], timeout=1)
421     if ev is not None:
422         raise Exception("Unexpected disconnection")
423
424     logger.info("Disconnect-Request with mismatching NAS-IP-Address")
425     req = radius_das.DisconnectPacket(dict=dict, secret="secret",
426                                       NAS_IP_Address="192.168.3.4",
427                                       Acct_Session_Id=id,
428                                       Event_Timestamp=int(time.time()))
429     send_and_check_reply(srv, req, pyrad.packet.DisconnectNAK, 403)
430
431     logger.info("Disconnect-Request with mismatching NAS-Identifier")
432     req = radius_das.DisconnectPacket(dict=dict, secret="secret",
433                                       NAS_Identifier="unknown.example.com",
434                                       Acct_Session_Id=id,
435                                       Event_Timestamp=int(time.time()))
436     send_and_check_reply(srv, req, pyrad.packet.DisconnectNAK, 403)
437
438     ev = dev[0].wait_event(["CTRL-EVENT-DISCONNECTED"], timeout=1)
439     if ev is not None:
440         raise Exception("Unexpected disconnection")
441
442     logger.info("Disconnect-Request with matching Acct-Session-Id")
443     req = radius_das.DisconnectPacket(dict=dict, secret="secret",
444                                       NAS_IP_Address="127.0.0.1",
445                                       NAS_Identifier="nas.example.com",
446                                       Acct_Session_Id=id,
447                                       Event_Timestamp=int(time.time()))
448     send_and_check_reply(srv, req, pyrad.packet.DisconnectACK)
449
450     dev[0].wait_disconnected(timeout=10)
451     dev[0].wait_connected(timeout=10, error="Re-connection timed out")
452
453     logger.info("Disconnect-Request with matching Acct-Multi-Session-Id")
454     sta = hapd.get_sta(addr)
455     multi_sess_id = sta['authMultiSessionId']
456     req = radius_das.DisconnectPacket(dict=dict, secret="secret",
457                                       NAS_IP_Address="127.0.0.1",
458                                       NAS_Identifier="nas.example.com",
459                                       Acct_Multi_Session_Id=multi_sess_id,
460                                       Event_Timestamp=int(time.time()))
461     send_and_check_reply(srv, req, pyrad.packet.DisconnectACK)
462
463     dev[0].wait_disconnected(timeout=10)
464     dev[0].wait_connected(timeout=10, error="Re-connection timed out")
465
466     logger.info("Disconnect-Request with matching User-Name")
467     req = radius_das.DisconnectPacket(dict=dict, secret="secret",
468                                       NAS_Identifier="nas.example.com",
469                                       User_Name="psk.user@example.com",
470                                       Event_Timestamp=int(time.time()))
471     send_and_check_reply(srv, req, pyrad.packet.DisconnectACK)
472
473     dev[0].wait_disconnected(timeout=10)
474     dev[0].wait_connected(timeout=10, error="Re-connection timed out")
475
476     logger.info("Disconnect-Request with matching Calling-Station-Id")
477     req = radius_das.DisconnectPacket(dict=dict, secret="secret",
478                                       NAS_IP_Address="127.0.0.1",
479                                       Calling_Station_Id=addr,
480                                       Event_Timestamp=int(time.time()))
481     send_and_check_reply(srv, req, pyrad.packet.DisconnectACK)
482
483     dev[0].wait_disconnected(timeout=10)
484     ev = dev[0].wait_event(["CTRL-EVENT-EAP-STARTED", "CTRL-EVENT-CONNECTED"])
485     if ev is None:
486         raise Exception("Timeout while waiting for re-connection")
487     if "CTRL-EVENT-EAP-STARTED" not in ev:
488         raise Exception("Unexpected skipping of EAP authentication in reconnection")
489     dev[0].wait_connected(timeout=10, error="Re-connection timed out")
490
491     logger.info("Disconnect-Request with matching Calling-Station-Id and non-matching CUI")
492     req = radius_das.DisconnectPacket(dict=dict, secret="secret",
493                                       Calling_Station_Id=addr,
494                                       Chargeable_User_Identity="foo@example.com",
495                                       Event_Timestamp=int(time.time()))
496     send_and_check_reply(srv, req, pyrad.packet.DisconnectNAK, error_cause=503)
497
498     logger.info("Disconnect-Request with matching CUI")
499     dev[1].connect("radius-das", key_mgmt="WPA-EAP",
500                    eap="GPSK", identity="gpsk-cui",
501                    password="abcdefghijklmnop0123456789abcdef",
502                    scan_freq="2412")
503     req = radius_das.DisconnectPacket(dict=dict, secret="secret",
504                                       Chargeable_User_Identity="gpsk-chargeable-user-identity",
505                                       Event_Timestamp=int(time.time()))
506     send_and_check_reply(srv, req, pyrad.packet.DisconnectACK)
507
508     dev[1].wait_disconnected(timeout=10)
509     dev[1].wait_connected(timeout=10, error="Re-connection timed out")
510
511     ev = dev[0].wait_event(["CTRL-EVENT-DISCONNECTED"], timeout=1)
512     if ev is not None:
513         raise Exception("Unexpected disconnection")
514
515     connect(dev[2], "radius-das")
516
517     logger.info("Disconnect-Request with matching User-Name - multiple sessions matching")
518     req = radius_das.DisconnectPacket(dict=dict, secret="secret",
519                                       NAS_Identifier="nas.example.com",
520                                       User_Name="psk.user@example.com",
521                                       Event_Timestamp=int(time.time()))
522     send_and_check_reply(srv, req, pyrad.packet.DisconnectNAK, error_cause=508)
523
524     logger.info("Disconnect-Request with User-Name matching multiple sessions, Calling-Station-Id only one")
525     req = radius_das.DisconnectPacket(dict=dict, secret="secret",
526                                       NAS_Identifier="nas.example.com",
527                                       Calling_Station_Id=addr,
528                                       User_Name="psk.user@example.com",
529                                       Event_Timestamp=int(time.time()))
530     send_and_check_reply(srv, req, pyrad.packet.DisconnectACK)
531
532     dev[0].wait_disconnected(timeout=10)
533     dev[0].wait_connected(timeout=10, error="Re-connection timed out")
534
535     ev = dev[2].wait_event(["CTRL-EVENT-DISCONNECTED"], timeout=1)
536     if ev is not None:
537         raise Exception("Unexpected disconnection")
538
539     logger.info("Disconnect-Request with matching Acct-Multi-Session-Id after disassociation")
540     sta = hapd.get_sta(addr)
541     multi_sess_id = sta['authMultiSessionId']
542     dev[0].request("DISCONNECT")
543     dev[0].wait_disconnected(timeout=10)
544     req = radius_das.DisconnectPacket(dict=dict, secret="secret",
545                                       NAS_IP_Address="127.0.0.1",
546                                       NAS_Identifier="nas.example.com",
547                                       Acct_Multi_Session_Id=multi_sess_id,
548                                       Event_Timestamp=int(time.time()))
549     send_and_check_reply(srv, req, pyrad.packet.DisconnectACK)
550
551     dev[0].request("RECONNECT")
552     ev = dev[0].wait_event(["CTRL-EVENT-EAP-STARTED"], timeout=15)
553     if ev is None:
554         raise Exception("Timeout on EAP start")
555     dev[0].wait_connected(timeout=15)
556
557     logger.info("Disconnect-Request with matching User-Name after disassociation")
558     dev[0].request("DISCONNECT")
559     dev[0].wait_disconnected(timeout=10)
560     dev[2].request("DISCONNECT")
561     dev[2].wait_disconnected(timeout=10)
562     req = radius_das.DisconnectPacket(dict=dict, secret="secret",
563                                       NAS_IP_Address="127.0.0.1",
564                                       NAS_Identifier="nas.example.com",
565                                       User_Name="psk.user@example.com",
566                                       Event_Timestamp=int(time.time()))
567     send_and_check_reply(srv, req, pyrad.packet.DisconnectACK)
568
569     logger.info("Disconnect-Request with matching CUI after disassociation")
570     dev[1].request("DISCONNECT")
571     dev[1].wait_disconnected(timeout=10)
572     req = radius_das.DisconnectPacket(dict=dict, secret="secret",
573                                       NAS_IP_Address="127.0.0.1",
574                                       NAS_Identifier="nas.example.com",
575                                       Chargeable_User_Identity="gpsk-chargeable-user-identity",
576                                       Event_Timestamp=int(time.time()))
577     send_and_check_reply(srv, req, pyrad.packet.DisconnectACK)
578
579     logger.info("Disconnect-Request with matching Calling-Station-Id after disassociation")
580     dev[0].request("RECONNECT")
581     ev = dev[0].wait_event(["CTRL-EVENT-EAP-STARTED"], timeout=15)
582     if ev is None:
583         raise Exception("Timeout on EAP start")
584     dev[0].wait_connected(timeout=15)
585     dev[0].request("DISCONNECT")
586     dev[0].wait_disconnected(timeout=10)
587     req = radius_das.DisconnectPacket(dict=dict, secret="secret",
588                                       NAS_IP_Address="127.0.0.1",
589                                       NAS_Identifier="nas.example.com",
590                                       Calling_Station_Id=addr,
591                                       Event_Timestamp=int(time.time()))
592     send_and_check_reply(srv, req, pyrad.packet.DisconnectACK)
593
594     logger.info("Disconnect-Request with mismatching Calling-Station-Id after disassociation")
595     req = radius_das.DisconnectPacket(dict=dict, secret="secret",
596                                       NAS_IP_Address="127.0.0.1",
597                                       NAS_Identifier="nas.example.com",
598                                       Calling_Station_Id=addr,
599                                       Event_Timestamp=int(time.time()))
600     send_and_check_reply(srv, req, pyrad.packet.DisconnectNAK, error_cause=503)
601
602 def test_radius_das_coa(dev, apdev):
603     """RADIUS Dynamic Authorization Extensions - CoA"""
604     try:
605         import pyrad.client
606         import pyrad.packet
607         import pyrad.dictionary
608         import radius_das
609     except ImportError:
610         raise HwsimSkip("No pyrad modules available")
611
612     params = hostapd.wpa2_eap_params(ssid="radius-das")
613     params['radius_das_port'] = "3799"
614     params['radius_das_client'] = "127.0.0.1 secret"
615     params['radius_das_require_event_timestamp'] = "1"
616     hapd = hostapd.add_ap(apdev[0]['ifname'], params)
617     connect(dev[0], "radius-das")
618     addr = dev[0].p2p_interface_addr()
619     sta = hapd.get_sta(addr)
620     id = sta['dot1xAuthSessionId']
621
622     dict = pyrad.dictionary.Dictionary("dictionary.radius")
623
624     srv = pyrad.client.Client(server="127.0.0.1", acctport=3799,
625                               secret="secret", dict=dict)
626     srv.retries = 1
627     srv.timeout = 1
628
629     # hostapd does not currently support CoA-Request, so NAK is expected
630     logger.info("CoA-Request with matching Acct-Session-Id")
631     req = radius_das.CoAPacket(dict=dict, secret="secret",
632                                Acct_Session_Id=id,
633                                Event_Timestamp=int(time.time()))
634     send_and_check_reply(srv, req, pyrad.packet.CoANAK, error_cause=405)
635
636 def test_radius_ipv6(dev, apdev):
637     """RADIUS connection over IPv6"""
638     params = {}
639     params['ssid'] = 'as'
640     params['beacon_int'] = '2000'
641     params['radius_server_clients'] = 'auth_serv/radius_clients_ipv6.conf'
642     params['radius_server_ipv6'] = '1'
643     params['radius_server_auth_port'] = '18129'
644     params['radius_server_acct_port'] = '18139'
645     params['eap_server'] = '1'
646     params['eap_user_file'] = 'auth_serv/eap_user.conf'
647     params['ca_cert'] = 'auth_serv/ca.pem'
648     params['server_cert'] = 'auth_serv/server.pem'
649     params['private_key'] = 'auth_serv/server.key'
650     hostapd.add_ap(apdev[1]['ifname'], params)
651
652     params = hostapd.wpa2_eap_params(ssid="radius-ipv6")
653     params['auth_server_addr'] = "::0"
654     params['auth_server_port'] = "18129"
655     params['acct_server_addr'] = "::0"
656     params['acct_server_port'] = "18139"
657     params['acct_server_shared_secret'] = "radius"
658     params['own_ip_addr'] = "::0"
659     hostapd.add_ap(apdev[0]['ifname'], params)
660     connect(dev[0], "radius-ipv6")
661
662 def test_radius_macacl(dev, apdev):
663     """RADIUS MAC ACL"""
664     params = hostapd.radius_params()
665     params["ssid"] = "radius"
666     params["macaddr_acl"] = "2"
667     hostapd.add_ap(apdev[0]['ifname'], params)
668     dev[0].connect("radius", key_mgmt="NONE", scan_freq="2412")
669
670 def test_radius_macacl_acct(dev, apdev):
671     """RADIUS MAC ACL and accounting enabled"""
672     params = hostapd.radius_params()
673     params["ssid"] = "radius"
674     params["macaddr_acl"] = "2"
675     params['acct_server_addr'] = "127.0.0.1"
676     params['acct_server_port'] = "1813"
677     params['acct_server_shared_secret'] = "radius"
678     hostapd.add_ap(apdev[0]['ifname'], params)
679     dev[0].connect("radius", key_mgmt="NONE", scan_freq="2412")
680     dev[1].connect("radius", key_mgmt="NONE", scan_freq="2412")
681     dev[1].request("DISCONNECT")
682     dev[1].wait_disconnected()
683     dev[1].request("RECONNECT")
684
685 def test_radius_failover(dev, apdev):
686     """RADIUS Authentication and Accounting server failover"""
687     subprocess.call(['ip', 'ro', 'replace', '192.168.213.17', 'dev', 'lo'])
688     as_hapd = hostapd.Hostapd("as")
689     as_mib_start = as_hapd.get_mib(param="radius_server")
690     params = hostapd.wpa2_eap_params(ssid="radius-failover")
691     params["auth_server_addr"] = "192.168.213.17"
692     params["auth_server_port"] = "1812"
693     params["auth_server_shared_secret"] = "testing"
694     params['acct_server_addr'] = "192.168.213.17"
695     params['acct_server_port'] = "1813"
696     params['acct_server_shared_secret'] = "testing"
697     params['radius_retry_primary_interval'] = "20"
698     hapd = hostapd.add_ap(apdev[0]['ifname'], params, no_enable=True)
699     hapd.set("auth_server_addr", "127.0.0.1")
700     hapd.set("auth_server_port", "1812")
701     hapd.set("auth_server_shared_secret", "radius")
702     hapd.set('acct_server_addr', "127.0.0.1")
703     hapd.set('acct_server_port', "1813")
704     hapd.set('acct_server_shared_secret', "radius")
705     hapd.enable()
706     ev = hapd.wait_event(["AP-ENABLED", "AP-DISABLED"], timeout=30)
707     if ev is None:
708         raise Exception("AP startup timed out")
709         if "AP-ENABLED" not in ev:
710             raise Exception("AP startup failed")
711     start = os.times()[4]
712
713     try:
714         subprocess.call(['ip', 'ro', 'replace', 'prohibit', '192.168.213.17'])
715         dev[0].request("SET EAPOL::authPeriod 5")
716         connect(dev[0], "radius-failover", wait_connect=False)
717         dev[0].wait_connected(timeout=20)
718     finally:
719         dev[0].request("SET EAPOL::authPeriod 30")
720         subprocess.call(['ip', 'ro', 'del', '192.168.213.17'])
721
722     as_mib_end = as_hapd.get_mib(param="radius_server")
723     req_s = int(as_mib_start['radiusAccServTotalRequests'])
724     req_e = int(as_mib_end['radiusAccServTotalRequests'])
725     if req_e <= req_s:
726         raise Exception("Unexpected RADIUS server acct MIB value")
727
728     end = os.times()[4]
729     try:
730         subprocess.call(['ip', 'ro', 'replace', 'prohibit', '192.168.213.17'])
731         dev[1].request("SET EAPOL::authPeriod 5")
732         if end - start < 21:
733             time.sleep(21 - (end - start))
734         connect(dev[1], "radius-failover", wait_connect=False)
735         dev[1].wait_connected(timeout=20)
736     finally:
737         dev[1].request("SET EAPOL::authPeriod 30")
738         subprocess.call(['ip', 'ro', 'del', '192.168.213.17'])
739
740 def run_pyrad_server(srv, t_events):
741     srv.RunWithStop(t_events)
742
743 def test_radius_protocol(dev, apdev):
744     """RADIUS Authentication protocol tests with a fake server"""
745     try:
746         import pyrad.server
747         import pyrad.packet
748         import pyrad.dictionary
749     except ImportError:
750         raise HwsimSkip("No pyrad modules available")
751
752     class TestServer(pyrad.server.Server):
753         def _HandleAuthPacket(self, pkt):
754             pyrad.server.Server._HandleAuthPacket(self, pkt)
755             logger.info("Received authentication request")
756             reply = self.CreateReplyPacket(pkt)
757             reply.code = pyrad.packet.AccessAccept
758             if self.t_events['msg_auth'].is_set():
759                 logger.info("Add Message-Authenticator")
760                 if self.t_events['wrong_secret'].is_set():
761                     logger.info("Use incorrect RADIUS shared secret")
762                     pw = "incorrect"
763                 else:
764                     pw = reply.secret
765                 hmac_obj = hmac.new(pw)
766                 hmac_obj.update(struct.pack("B", reply.code))
767                 hmac_obj.update(struct.pack("B", reply.id))
768
769                 # reply attributes
770                 reply.AddAttribute("Message-Authenticator",
771                                    "\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00")
772                 attrs = reply._PktEncodeAttributes()
773
774                 # Length
775                 flen = 4 + 16 + len(attrs)
776                 hmac_obj.update(struct.pack(">H", flen))
777                 hmac_obj.update(pkt.authenticator)
778                 hmac_obj.update(attrs)
779                 if self.t_events['double_msg_auth'].is_set():
780                     logger.info("Include two Message-Authenticator attributes")
781                 else:
782                     del reply[80]
783                 reply.AddAttribute("Message-Authenticator", hmac_obj.digest())
784             self.SendReplyPacket(pkt.fd, reply)
785
786         def RunWithStop(self, t_events):
787             self._poll = select.poll()
788             self._fdmap = {}
789             self._PrepareSockets()
790             self.t_events = t_events
791
792             while not t_events['stop'].is_set():
793                 for (fd, event) in self._poll.poll(1000):
794                     if event == select.POLLIN:
795                         try:
796                             fdo = self._fdmap[fd]
797                             self._ProcessInput(fdo)
798                         except ServerPacketError as err:
799                             logger.info("pyrad server dropping packet: " + str(err))
800                         except pyrad.packet.PacketError as err:
801                             logger.info("pyrad server received invalid packet: " + str(err))
802                     else:
803                         logger.error("Unexpected event in pyrad server main loop")
804
805     srv = TestServer(dict=pyrad.dictionary.Dictionary("dictionary.radius"),
806                      authport=18138, acctport=18139)
807     srv.hosts["127.0.0.1"] = pyrad.server.RemoteHost("127.0.0.1",
808                                                      "radius",
809                                                      "localhost")
810     srv.BindToAddress("")
811     t_events = {}
812     t_events['stop'] = threading.Event()
813     t_events['msg_auth'] = threading.Event()
814     t_events['wrong_secret'] = threading.Event()
815     t_events['double_msg_auth'] = threading.Event()
816     t = threading.Thread(target=run_pyrad_server, args=(srv, t_events))
817     t.start()
818
819     try:
820         params = hostapd.wpa2_eap_params(ssid="radius-test")
821         params['auth_server_port'] = "18138"
822         hapd = hostapd.add_ap(apdev[0]['ifname'], params)
823         connect(dev[0], "radius-test", wait_connect=False)
824         ev = dev[0].wait_event(["CTRL-EVENT-EAP-STARTED"], timeout=15)
825         if ev is None:
826             raise Exception("Timeout on EAP start")
827         time.sleep(1)
828         dev[0].request("REMOVE_NETWORK all")
829         time.sleep(0.1)
830         dev[0].dump_monitor()
831         t_events['msg_auth'].set()
832         t_events['wrong_secret'].set()
833         connect(dev[0], "radius-test", wait_connect=False)
834         time.sleep(1)
835         dev[0].request("REMOVE_NETWORK all")
836         time.sleep(0.1)
837         dev[0].dump_monitor()
838         t_events['wrong_secret'].clear()
839         connect(dev[0], "radius-test", wait_connect=False)
840         time.sleep(1)
841         dev[0].request("REMOVE_NETWORK all")
842         time.sleep(0.1)
843         dev[0].dump_monitor()
844         t_events['double_msg_auth'].set()
845         connect(dev[0], "radius-test", wait_connect=False)
846         time.sleep(1)
847     finally:
848         t_events['stop'].set()
849         t.join()
850
851 def test_radius_psk(dev, apdev):
852     """WPA2 with PSK from RADIUS"""
853     try:
854         import pyrad.server
855         import pyrad.packet
856         import pyrad.dictionary
857     except ImportError:
858         raise HwsimSkip("No pyrad modules available")
859
860     class TestServer(pyrad.server.Server):
861         def _HandleAuthPacket(self, pkt):
862             pyrad.server.Server._HandleAuthPacket(self, pkt)
863             logger.info("Received authentication request")
864             reply = self.CreateReplyPacket(pkt)
865             reply.code = pyrad.packet.AccessAccept
866             a = "\xab\xcd"
867             secret = reply.secret
868             if self.t_events['long'].is_set():
869                 p = b'\x10' + "0123456789abcdef" + 15 * b'\x00'
870                 b = hashlib.md5(secret + pkt.authenticator + a).digest()
871                 pp = bytearray(p[0:16])
872                 bb = bytearray(b)
873                 cc = bytearray(pp[i] ^ bb[i] for i in range(len(bb)))
874
875                 b = hashlib.md5(reply.secret + bytes(cc)).digest()
876                 pp = bytearray(p[16:32])
877                 bb = bytearray(b)
878                 cc += bytearray(pp[i] ^ bb[i] for i in range(len(bb)))
879
880                 data = '\x00' + a + bytes(cc)
881             else:
882                 p = b'\x08' + "12345678" + 7 * b'\x00'
883                 b = hashlib.md5(secret + pkt.authenticator + a).digest()
884                 pp = bytearray(p)
885                 bb = bytearray(b)
886                 cc = bytearray(pp[i] ^ bb[i] for i in range(len(bb)))
887                 data = '\x00' + a + bytes(cc)
888             reply.AddAttribute("Tunnel-Password", data)
889             self.SendReplyPacket(pkt.fd, reply)
890
891         def RunWithStop(self, t_events):
892             self._poll = select.poll()
893             self._fdmap = {}
894             self._PrepareSockets()
895             self.t_events = t_events
896
897             while not t_events['stop'].is_set():
898                 for (fd, event) in self._poll.poll(1000):
899                     if event == select.POLLIN:
900                         try:
901                             fdo = self._fdmap[fd]
902                             self._ProcessInput(fdo)
903                         except ServerPacketError as err:
904                             logger.info("pyrad server dropping packet: " + str(err))
905                         except pyrad.packet.PacketError as err:
906                             logger.info("pyrad server received invalid packet: " + str(err))
907                     else:
908                         logger.error("Unexpected event in pyrad server main loop")
909
910     srv = TestServer(dict=pyrad.dictionary.Dictionary("dictionary.radius"),
911                      authport=18138, acctport=18139)
912     srv.hosts["127.0.0.1"] = pyrad.server.RemoteHost("127.0.0.1",
913                                                      "radius",
914                                                      "localhost")
915     srv.BindToAddress("")
916     t_events = {}
917     t_events['stop'] = threading.Event()
918     t_events['long'] = threading.Event()
919     t = threading.Thread(target=run_pyrad_server, args=(srv, t_events))
920     t.start()
921
922     try:
923         ssid = "test-wpa2-psk"
924         params = hostapd.radius_params()
925         params['ssid'] = ssid
926         params["wpa"] = "2"
927         params["wpa_key_mgmt"] = "WPA-PSK"
928         params["rsn_pairwise"] = "CCMP"
929         params['macaddr_acl'] = '2'
930         params['wpa_psk_radius'] = '2'
931         params['auth_server_port'] = "18138"
932         hapd = hostapd.add_ap(apdev[0]['ifname'], params)
933         dev[0].connect(ssid, psk="12345678", scan_freq="2412")
934         t_events['long'].set()
935         dev[1].connect(ssid, psk="0123456789abcdef", scan_freq="2412")
936     finally:
937         t_events['stop'].set()
938         t.join()
939
940 def test_radius_auth_force_client_addr(dev, apdev):
941     """RADIUS client address specified"""
942     params = hostapd.wpa2_eap_params(ssid="radius-auth")
943     params['radius_client_addr'] = "127.0.0.1"
944     hapd = hostapd.add_ap(apdev[0]['ifname'], params)
945     connect(dev[0], "radius-auth")
946
947 def test_radius_auth_force_invalid_client_addr(dev, apdev):
948     """RADIUS client address specified and invalid address"""
949     params = hostapd.wpa2_eap_params(ssid="radius-auth")
950     #params['radius_client_addr'] = "10.11.12.14"
951     params['radius_client_addr'] = "1::2"
952     hapd = hostapd.add_ap(apdev[0]['ifname'], params)
953     connect(dev[0], "radius-auth", wait_connect=False)
954     ev = dev[0].wait_event(["CTRL-EVENT-EAP-STARTED"])
955     if ev is None:
956         raise Exception("Timeout on EAP start")
957     ev = dev[0].wait_event(["CTRL-EVENT-CONNECTED"], timeout=1)
958     if ev is not None:
959         raise Exception("Unexpected connection")
960
961 def add_message_auth(req):
962     req.authenticator = req.CreateAuthenticator()
963     hmac_obj = hmac.new(req.secret)
964     hmac_obj.update(struct.pack("B", req.code))
965     hmac_obj.update(struct.pack("B", req.id))
966
967     # request attributes
968     req.AddAttribute("Message-Authenticator",
969                      "\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00")
970     attrs = req._PktEncodeAttributes()
971
972     # Length
973     flen = 4 + 16 + len(attrs)
974     hmac_obj.update(struct.pack(">H", flen))
975     hmac_obj.update(req.authenticator)
976     hmac_obj.update(attrs)
977     del req[80]
978     req.AddAttribute("Message-Authenticator", hmac_obj.digest())
979
980 def test_radius_server_failures(dev, apdev):
981     """RADIUS server failure cases"""
982     try:
983         import pyrad.client
984         import pyrad.packet
985         import pyrad.dictionary
986     except ImportError:
987         raise HwsimSkip("No pyrad modules available")
988
989     dict = pyrad.dictionary.Dictionary("dictionary.radius")
990     client = pyrad.client.Client(server="127.0.0.1", authport=1812,
991                                  secret="radius", dict=dict)
992     client.retries = 1
993     client.timeout = 1
994
995     # unexpected State
996     req = client.CreateAuthPacket(code=pyrad.packet.AccessRequest,
997                                   User_Name="foo")
998     req['State'] = 'foo-state'
999     add_message_auth(req)
1000     reply = client.SendPacket(req)
1001     if reply.code != pyrad.packet.AccessReject:
1002         raise Exception("Unexpected RADIUS response code " + str(reply.code))
1003
1004     # no EAP-Message
1005     req = client.CreateAuthPacket(code=pyrad.packet.AccessRequest,
1006                                   User_Name="foo")
1007     add_message_auth(req)
1008     try:
1009         reply = client.SendPacket(req)
1010         raise Exception("Unexpected response")
1011     except pyrad.client.Timeout:
1012         pass
1013
1014 def test_ap_vlan_wpa2_psk_radius_required(dev, apdev):
1015     """AP VLAN with WPA2-PSK and RADIUS attributes required"""
1016     try:
1017         import pyrad.server
1018         import pyrad.packet
1019         import pyrad.dictionary
1020     except ImportError:
1021         raise HwsimSkip("No pyrad modules available")
1022
1023     class TestServer(pyrad.server.Server):
1024         def _HandleAuthPacket(self, pkt):
1025             pyrad.server.Server._HandleAuthPacket(self, pkt)
1026             logger.info("Received authentication request")
1027             reply = self.CreateReplyPacket(pkt)
1028             reply.code = pyrad.packet.AccessAccept
1029             secret = reply.secret
1030             if self.t_events['extra'].is_set():
1031                 reply.AddAttribute("Chargeable-User-Identity", "test-cui")
1032                 reply.AddAttribute("User-Name", "test-user")
1033             if self.t_events['long'].is_set():
1034                 reply.AddAttribute("Tunnel-Type", 13)
1035                 reply.AddAttribute("Tunnel-Medium-Type", 6)
1036                 reply.AddAttribute("Tunnel-Private-Group-ID", "1")
1037             self.SendReplyPacket(pkt.fd, reply)
1038
1039         def RunWithStop(self, t_events):
1040             self._poll = select.poll()
1041             self._fdmap = {}
1042             self._PrepareSockets()
1043             self.t_events = t_events
1044
1045             while not t_events['stop'].is_set():
1046                 for (fd, event) in self._poll.poll(1000):
1047                     if event == select.POLLIN:
1048                         try:
1049                             fdo = self._fdmap[fd]
1050                             self._ProcessInput(fdo)
1051                         except ServerPacketError as err:
1052                             logger.info("pyrad server dropping packet: " + str(err))
1053                         except pyrad.packet.PacketError as err:
1054                             logger.info("pyrad server received invalid packet: " + str(err))
1055                     else:
1056                         logger.error("Unexpected event in pyrad server main loop")
1057
1058     srv = TestServer(dict=pyrad.dictionary.Dictionary("dictionary.radius"),
1059                      authport=18138, acctport=18139)
1060     srv.hosts["127.0.0.1"] = pyrad.server.RemoteHost("127.0.0.1",
1061                                                      "radius",
1062                                                      "localhost")
1063     srv.BindToAddress("")
1064     t_events = {}
1065     t_events['stop'] = threading.Event()
1066     t_events['long'] = threading.Event()
1067     t_events['extra'] = threading.Event()
1068     t = threading.Thread(target=run_pyrad_server, args=(srv, t_events))
1069     t.start()
1070
1071     try:
1072         ssid = "test-wpa2-psk"
1073         params = hostapd.radius_params()
1074         params['ssid'] = ssid
1075         params["wpa"] = "2"
1076         params["wpa_key_mgmt"] = "WPA-PSK"
1077         params["rsn_pairwise"] = "CCMP"
1078         params['macaddr_acl'] = '2'
1079         params['dynamic_vlan'] = "2"
1080         params['wpa_passphrase'] = '0123456789abcdefghi'
1081         params['auth_server_port'] = "18138"
1082         hapd = hostapd.add_ap(apdev[0]['ifname'], params)
1083
1084         logger.info("connecting without VLAN")
1085         dev[0].connect(ssid, psk="0123456789abcdefghi", scan_freq="2412",
1086                        wait_connect=False)
1087         ev = dev[0].wait_event(["CTRL-EVENT-CONNECTED",
1088                                 "CTRL-EVENT-SSID-TEMP-DISABLED"], timeout=20)
1089         if ev is None:
1090             raise Exception("Timeout on connection attempt")
1091         if "CTRL-EVENT-CONNECTED" in ev:
1092             raise Exception("Unexpected success without vlan parameters")
1093         logger.info("connecting without VLAN failed as expected")
1094
1095         logger.info("connecting without VLAN (CUI/User-Name)")
1096         t_events['extra'].set()
1097         dev[1].connect(ssid, psk="0123456789abcdefghi", scan_freq="2412",
1098                        wait_connect=False)
1099         ev = dev[1].wait_event(["CTRL-EVENT-CONNECTED",
1100                                 "CTRL-EVENT-SSID-TEMP-DISABLED"], timeout=20)
1101         if ev is None:
1102             raise Exception("Timeout on connection attempt")
1103         if "CTRL-EVENT-CONNECTED" in ev:
1104             raise Exception("Unexpected success without vlan parameters(2)")
1105         logger.info("connecting without VLAN failed as expected(2)")
1106         t_events['extra'].clear()
1107
1108         t_events['long'].set()
1109         logger.info("connecting with VLAN")
1110         dev[2].connect(ssid, psk="0123456789abcdefghi", scan_freq="2412",
1111                        wait_connect=False)
1112         ev = dev[2].wait_event(["CTRL-EVENT-CONNECTED",
1113                                 "CTRL-EVENT-SSID-TEMP-DISABLED"], timeout=20)
1114         if ev is None:
1115             raise Exception("Timeout on connection attempt")
1116         if "CTRL-EVENT-SSID-TEMP-DISABLED" in ev:
1117             raise Exception("Unexpected failure with vlan parameters")
1118         logger.info("connecting with VLAN succeeded as expected")
1119     finally:
1120         t_events['stop'].set()
1121         t.join()