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