]> git.ipfire.org Git - thirdparty/hostap.git/blame - tests/hwsim/test_radius.py
tests: Remove unnecessary interpreter line from most python files
[thirdparty/hostap.git] / tests / hwsim / test_radius.py
CommitLineData
7fd15145 1# RADIUS tests
a3b2bdaf 2# Copyright (c) 2013-2014, Jouni Malinen <j@w1.fi>
7fd15145
JM
3#
4# This software may be distributed under the terms of the BSD license.
5# See README for more details.
6
7import logging
8logger = logging.getLogger()
9import time
10
11import hostapd
12
13def connect(dev, ssid, wait_connect=True):
14 dev.connect(ssid, key_mgmt="WPA-EAP", scan_freq="2412",
15 eap="PSK", identity="psk.user@example.com",
16 password_hex="0123456789abcdef0123456789abcdef",
17 wait_connect=wait_connect)
18
19def test_radius_auth_unreachable(dev, apdev):
20 """RADIUS Authentication server unreachable"""
21 params = hostapd.wpa2_eap_params(ssid="radius-auth")
22 params['auth_server_port'] = "18139"
23 hostapd.add_ap(apdev[0]['ifname'], params)
24 hapd = hostapd.Hostapd(apdev[0]['ifname'])
25 connect(dev[0], "radius-auth", wait_connect=False)
26 ev = dev[0].wait_event(["CTRL-EVENT-EAP-STARTED"])
27 if ev is None:
28 raise Exception("Timeout on EAP start")
29 logger.info("Checking for RADIUS retries")
30 time.sleep(4)
31 mib = hapd.get_mib()
32 if "radiusAuthClientAccessRequests" not in mib:
33 raise Exception("Missing MIB fields")
34 if int(mib["radiusAuthClientAccessRetransmissions"]) < 1:
35 raise Exception("Missing RADIUS Authentication retransmission")
36 if int(mib["radiusAuthClientPendingRequests"]) < 1:
37 raise Exception("Missing pending RADIUS Authentication request")
38
39def test_radius_acct_unreachable(dev, apdev):
40 """RADIUS Accounting server unreachable"""
41 params = hostapd.wpa2_eap_params(ssid="radius-acct")
42 params['acct_server_addr'] = "127.0.0.1"
43 params['acct_server_port'] = "18139"
44 params['acct_server_shared_secret'] = "radius"
45 hostapd.add_ap(apdev[0]['ifname'], params)
46 hapd = hostapd.Hostapd(apdev[0]['ifname'])
47 connect(dev[0], "radius-acct")
48 logger.info("Checking for RADIUS retries")
49 time.sleep(4)
50 mib = hapd.get_mib()
51 if "radiusAccClientRetransmissions" not in mib:
52 raise Exception("Missing MIB fields")
53 if int(mib["radiusAccClientRetransmissions"]) < 2:
54 raise Exception("Missing RADIUS Accounting retransmissions")
55 if int(mib["radiusAccClientPendingRequests"]) < 2:
56 raise Exception("Missing pending RADIUS Accounting requests")
4287bb76
JM
57
58def test_radius_acct(dev, apdev):
59 """RADIUS Accounting"""
4fcee244
JM
60 as_hapd = hostapd.Hostapd("as")
61 as_mib_start = as_hapd.get_mib(param="radius_server")
4287bb76
JM
62 params = hostapd.wpa2_eap_params(ssid="radius-acct")
63 params['acct_server_addr'] = "127.0.0.1"
64 params['acct_server_port'] = "1813"
65 params['acct_server_shared_secret'] = "radius"
66 hostapd.add_ap(apdev[0]['ifname'], params)
67 hapd = hostapd.Hostapd(apdev[0]['ifname'])
68 connect(dev[0], "radius-acct")
69 logger.info("Checking for RADIUS counters")
70 count = 0
71 while True:
72 mib = hapd.get_mib()
73 if int(mib['radiusAccClientResponses']) >= 2:
74 break
75 time.sleep(0.1)
76 count += 1
77 if count > 10:
78 raise Exception("Did not receive Accounting-Response packets")
79
80 if int(mib['radiusAccClientRetransmissions']) > 0:
81 raise Exception("Unexpected Accounting-Request retransmission")
4fcee244
JM
82
83 as_mib_end = as_hapd.get_mib(param="radius_server")
84
85 req_s = int(as_mib_start['radiusAccServTotalRequests'])
86 req_e = int(as_mib_end['radiusAccServTotalRequests'])
87 if req_e < req_s + 2:
88 raise Exception("Unexpected RADIUS server acct MIB value")
89
90 acc_s = int(as_mib_start['radiusAuthServAccessAccepts'])
91 acc_e = int(as_mib_end['radiusAuthServAccessAccepts'])
92 if acc_e < acc_s + 1:
93 raise Exception("Unexpected RADIUS server auth MIB value")
a3b2bdaf
JM
94
95def test_radius_das_disconnect(dev, apdev):
96 """RADIUS Dynamic Authorization Extensions - Disconnect"""
97 try:
98 import pyrad.client
99 import pyrad.packet
100 import pyrad.dictionary
101 import radius_das
102 except ImportError:
103 return "skip"
104
105 params = hostapd.wpa2_eap_params(ssid="radius-das")
106 params['radius_das_port'] = "3799"
107 params['radius_das_client'] = "127.0.0.1 secret"
108 params['radius_das_require_event_timestamp'] = "1"
41ff0fa6
JM
109 params['own_ip_addr'] = "127.0.0.1"
110 params['nas_identifier'] = "nas.example.com"
a3b2bdaf
JM
111 hapd = hostapd.add_ap(apdev[0]['ifname'], params)
112 connect(dev[0], "radius-das")
113 addr = dev[0].p2p_interface_addr()
114 sta = hapd.get_sta(addr)
115 id = sta['dot1xAuthSessionId']
116
117 dict = pyrad.dictionary.Dictionary("dictionary.radius")
118
119 srv = pyrad.client.Client(server="127.0.0.1", acctport=3799,
120 secret="secret", dict=dict)
121 srv.retries = 1
122 srv.timeout = 1
123
124 logger.info("Disconnect-Request with incorrect secret")
125 req = radius_das.DisconnectPacket(dict=dict, secret="incorrect",
126 User_Name="foo",
127 NAS_Identifier="localhost",
128 Event_Timestamp=int(time.time()))
129 logger.debug(req)
130 try:
131 reply = srv.SendPacket(req)
132 raise Exception("Unexpected response to Disconnect-Request")
133 except pyrad.client.Timeout:
134 logger.info("Disconnect-Request with incorrect secret properly ignored")
135
136 logger.info("Disconnect-Request without Event-Timestamp")
137 req = radius_das.DisconnectPacket(dict=dict, secret="secret",
138 User_Name="psk.user@example.com")
139 logger.debug(req)
140 try:
141 reply = srv.SendPacket(req)
142 raise Exception("Unexpected response to Disconnect-Request")
143 except pyrad.client.Timeout:
144 logger.info("Disconnect-Request without Event-Timestamp properly ignored")
145
146 logger.info("Disconnect-Request with non-matching Event-Timestamp")
147 req = radius_das.DisconnectPacket(dict=dict, secret="secret",
148 User_Name="psk.user@example.com",
149 Event_Timestamp=123456789)
150 logger.debug(req)
151 try:
152 reply = srv.SendPacket(req)
153 raise Exception("Unexpected response to Disconnect-Request")
154 except pyrad.client.Timeout:
155 logger.info("Disconnect-Request with non-matching Event-Timestamp properly ignored")
156
157 logger.info("Disconnect-Request with unsupported attribute")
158 req = radius_das.DisconnectPacket(dict=dict, secret="secret",
159 User_Name="foo",
160 User_Password="foo",
161 Event_Timestamp=int(time.time()))
162 reply = srv.SendPacket(req)
163 logger.debug("RADIUS response from hostapd")
164 for i in reply.keys():
165 logger.debug("%s: %s" % (i, reply[i]))
166 if reply.code != pyrad.packet.DisconnectNAK:
167 raise Exception("Unexpected response code")
168 if 'Error-Cause' not in reply:
169 raise Exception("Missing Error-Cause")
170 if reply['Error-Cause'][0] != 401:
171 raise Exception("Unexpected Error-Cause: {}".format(reply['Error-Cause']))
172
173 logger.info("Disconnect-Request with invalid Calling-Station-Id")
174 req = radius_das.DisconnectPacket(dict=dict, secret="secret",
175 User_Name="foo",
176 Calling_Station_Id="foo",
177 Event_Timestamp=int(time.time()))
178 reply = srv.SendPacket(req)
179 logger.debug("RADIUS response from hostapd")
180 for i in reply.keys():
181 logger.debug("%s: %s" % (i, reply[i]))
182 if reply.code != pyrad.packet.DisconnectNAK:
183 raise Exception("Unexpected response code")
184 if 'Error-Cause' not in reply:
185 raise Exception("Missing Error-Cause")
186 if reply['Error-Cause'][0] != 407:
187 raise Exception("Unexpected Error-Cause: {}".format(reply['Error-Cause']))
188
189 logger.info("Disconnect-Request with mismatching User-Name")
190 req = radius_das.DisconnectPacket(dict=dict, secret="secret",
191 User_Name="foo",
192 Event_Timestamp=int(time.time()))
193 reply = srv.SendPacket(req)
194 logger.debug("RADIUS response from hostapd")
195 for i in reply.keys():
196 logger.debug("%s: %s" % (i, reply[i]))
197 if reply.code != pyrad.packet.DisconnectNAK:
198 raise Exception("Unexpected response code")
199 if 'Error-Cause' not in reply:
200 raise Exception("Missing Error-Cause")
201 if reply['Error-Cause'][0] != 503:
202 raise Exception("Unexpected Error-Cause: {}".format(reply['Error-Cause']))
203
204 logger.info("Disconnect-Request with mismatching Calling-Station-Id")
205 req = radius_das.DisconnectPacket(dict=dict, secret="secret",
206 Calling_Station_Id="12:34:56:78:90:aa",
207 Event_Timestamp=int(time.time()))
208 reply = srv.SendPacket(req)
209 logger.debug("RADIUS response from hostapd")
210 for i in reply.keys():
211 logger.debug("%s: %s" % (i, reply[i]))
212 if reply.code != pyrad.packet.DisconnectNAK:
213 raise Exception("Unexpected response code")
214 if 'Error-Cause' not in reply:
215 raise Exception("Missing Error-Cause")
216 if reply['Error-Cause'][0] != 503:
217 raise Exception("Unexpected Error-Cause: {}".format(reply['Error-Cause']))
218
219 logger.info("Disconnect-Request with mismatching Acct-Session-Id")
220 req = radius_das.DisconnectPacket(dict=dict, secret="secret",
221 Acct_Session_Id="12345678-87654321",
222 Event_Timestamp=int(time.time()))
223 reply = srv.SendPacket(req)
224 logger.debug("RADIUS response from hostapd")
225 for i in reply.keys():
226 logger.debug("%s: %s" % (i, reply[i]))
227 if reply.code != pyrad.packet.DisconnectNAK:
228 raise Exception("Unexpected response code")
229 if 'Error-Cause' not in reply:
230 raise Exception("Missing Error-Cause")
231 if reply['Error-Cause'][0] != 503:
232 raise Exception("Unexpected Error-Cause: {}".format(reply['Error-Cause']))
233
234 ev = dev[0].wait_event(["CTRL-EVENT-DISCONNECTED"], timeout=1)
235 if ev is not None:
236 raise Exception("Unexpected disconnection")
237
41ff0fa6
JM
238 logger.info("Disconnect-Request with mismatching NAS-IP-Address")
239 req = radius_das.DisconnectPacket(dict=dict, secret="secret",
240 NAS_IP_Address="192.168.3.4",
241 Acct_Session_Id=id,
242 Event_Timestamp=int(time.time()))
243 reply = srv.SendPacket(req)
244 logger.debug("RADIUS response from hostapd")
245 for i in reply.keys():
246 logger.debug("%s: %s" % (i, reply[i]))
247 if reply.code != pyrad.packet.DisconnectNAK:
248 raise Exception("Unexpected response code")
249 if 'Error-Cause' not in reply:
250 raise Exception("Missing Error-Cause")
251 if reply['Error-Cause'][0] != 403:
252 raise Exception("Unexpected Error-Cause: {}".format(reply['Error-Cause']))
253
254 logger.info("Disconnect-Request with mismatching NAS-Identifier")
255 req = radius_das.DisconnectPacket(dict=dict, secret="secret",
256 NAS_Identifier="unknown.example.com",
257 Acct_Session_Id=id,
258 Event_Timestamp=int(time.time()))
259 reply = srv.SendPacket(req)
260 logger.debug("RADIUS response from hostapd")
261 for i in reply.keys():
262 logger.debug("%s: %s" % (i, reply[i]))
263 if reply.code != pyrad.packet.DisconnectNAK:
264 raise Exception("Unexpected response code")
265 if 'Error-Cause' not in reply:
266 raise Exception("Missing Error-Cause")
267 if reply['Error-Cause'][0] != 403:
268 raise Exception("Unexpected Error-Cause: {}".format(reply['Error-Cause']))
269
270 ev = dev[0].wait_event(["CTRL-EVENT-DISCONNECTED"], timeout=1)
271 if ev is not None:
272 raise Exception("Unexpected disconnection")
273
a3b2bdaf
JM
274 logger.info("Disconnect-Request with matching Acct-Session-Id")
275 req = radius_das.DisconnectPacket(dict=dict, secret="secret",
41ff0fa6
JM
276 NAS_IP_Address="127.0.0.1",
277 NAS_Identifier="nas.example.com",
a3b2bdaf
JM
278 Acct_Session_Id=id,
279 Event_Timestamp=int(time.time()))
280 reply = srv.SendPacket(req)
281 logger.debug("RADIUS response from hostapd")
282 for i in reply.keys():
283 logger.debug("%s: %s" % (i, reply[i]))
284 if reply.code != pyrad.packet.DisconnectACK:
285 raise Exception("Unexpected response code")
286
287 ev = dev[0].wait_event(["CTRL-EVENT-DISCONNECTED"])
288 if ev is None:
289 raise Exception("Timeout while waiting for disconnection")
290 ev = dev[0].wait_event(["CTRL-EVENT-CONNECTED"])
291 if ev is None:
292 raise Exception("Timeout while waiting for re-connection")
293
294 logger.info("Disconnect-Request with matching User-Name")
295 req = radius_das.DisconnectPacket(dict=dict, secret="secret",
41ff0fa6 296 NAS_Identifier="nas.example.com",
a3b2bdaf
JM
297 User_Name="psk.user@example.com",
298 Event_Timestamp=int(time.time()))
299 reply = srv.SendPacket(req)
300 logger.debug("RADIUS response from hostapd")
301 for i in reply.keys():
302 logger.debug("%s: %s" % (i, reply[i]))
303 if reply.code != pyrad.packet.DisconnectACK:
304 raise Exception("Unexpected response code")
305
306 ev = dev[0].wait_event(["CTRL-EVENT-DISCONNECTED"])
307 if ev is None:
308 raise Exception("Timeout while waiting for disconnection")
309 ev = dev[0].wait_event(["CTRL-EVENT-CONNECTED"])
310 if ev is None:
311 raise Exception("Timeout while waiting for re-connection")
312
313 logger.info("Disconnect-Request with matching Calling-Station-Id")
314 req = radius_das.DisconnectPacket(dict=dict, secret="secret",
41ff0fa6 315 NAS_IP_Address="127.0.0.1",
a3b2bdaf
JM
316 Calling_Station_Id=addr,
317 Event_Timestamp=int(time.time()))
318 reply = srv.SendPacket(req)
319 logger.debug("RADIUS response from hostapd")
320 for i in reply.keys():
321 logger.debug("%s: %s" % (i, reply[i]))
322 if reply.code != pyrad.packet.DisconnectACK:
323 raise Exception("Unexpected response code")
324
325 ev = dev[0].wait_event(["CTRL-EVENT-DISCONNECTED"])
326 if ev is None:
327 raise Exception("Timeout while waiting for disconnection")
e58f59cb 328 ev = dev[0].wait_event(["CTRL-EVENT-EAP-STARTED", "CTRL-EVENT-CONNECTED"])
a3b2bdaf
JM
329 if ev is None:
330 raise Exception("Timeout while waiting for re-connection")
e58f59cb
JM
331 if "CTRL-EVENT-EAP-STARTED" not in ev:
332 raise Exception("Unexpected skipping of EAP authentication in reconnection")
333 ev = dev[0].wait_event(["CTRL-EVENT-CONNECTED"])
334 if ev is None:
335 raise Exception("Timeout while waiting for re-connection to complete")
a3b2bdaf
JM
336
337 logger.info("Disconnect-Request with matching Calling-Station-Id and non-matching CUI")
338 req = radius_das.DisconnectPacket(dict=dict, secret="secret",
339 Calling_Station_Id=addr,
340 Chargeable_User_Identity="foo@example.com",
341 Event_Timestamp=int(time.time()))
342 reply = srv.SendPacket(req)
343 logger.debug("RADIUS response from hostapd")
344 for i in reply.keys():
345 logger.debug("%s: %s" % (i, reply[i]))
346 if reply.code != pyrad.packet.DisconnectACK:
347 raise Exception("Unexpected response code")
348
349 ev = dev[0].wait_event(["CTRL-EVENT-DISCONNECTED"])
350 if ev is None:
351 raise Exception("Timeout while waiting for disconnection")
352 ev = dev[0].wait_event(["CTRL-EVENT-CONNECTED"])
353 if ev is None:
354 raise Exception("Timeout while waiting for re-connection")
55497a51
JM
355
356def test_radius_das_coa(dev, apdev):
357 """RADIUS Dynamic Authorization Extensions - CoA"""
358 try:
359 import pyrad.client
360 import pyrad.packet
361 import pyrad.dictionary
362 import radius_das
363 except ImportError:
364 return "skip"
365
366 params = hostapd.wpa2_eap_params(ssid="radius-das")
367 params['radius_das_port'] = "3799"
368 params['radius_das_client'] = "127.0.0.1 secret"
369 params['radius_das_require_event_timestamp'] = "1"
370 hapd = hostapd.add_ap(apdev[0]['ifname'], params)
371 connect(dev[0], "radius-das")
372 addr = dev[0].p2p_interface_addr()
373 sta = hapd.get_sta(addr)
374 id = sta['dot1xAuthSessionId']
375
376 dict = pyrad.dictionary.Dictionary("dictionary.radius")
377
378 srv = pyrad.client.Client(server="127.0.0.1", acctport=3799,
379 secret="secret", dict=dict)
380 srv.retries = 1
381 srv.timeout = 1
382
383 # hostapd does not currently support CoA-Request, so NAK is expected
384 logger.info("CoA-Request with matching Acct-Session-Id")
385 req = radius_das.CoAPacket(dict=dict, secret="secret",
386 Acct_Session_Id=id,
387 Event_Timestamp=int(time.time()))
388 reply = srv.SendPacket(req)
389 logger.debug("RADIUS response from hostapd")
390 for i in reply.keys():
391 logger.debug("%s: %s" % (i, reply[i]))
392 if reply.code != pyrad.packet.CoANAK:
393 raise Exception("Unexpected response code")
394 if 'Error-Cause' not in reply:
395 raise Exception("Missing Error-Cause")
396 if reply['Error-Cause'][0] != 405:
397 raise Exception("Unexpected Error-Cause: {}".format(reply['Error-Cause']))