]> git.ipfire.org Git - thirdparty/hostap.git/blob - tests/hwsim/test_radius.py
tests: Verify CoA-Request behavior
[thirdparty/hostap.git] / tests / hwsim / test_radius.py
1 #!/usr/bin/python
2 #
3 # RADIUS tests
4 # Copyright (c) 2013-2014, Jouni Malinen <j@w1.fi>
5 #
6 # This software may be distributed under the terms of the BSD license.
7 # See README for more details.
8
9 import logging
10 logger = logging.getLogger()
11 import time
12
13 import hostapd
14
15 def connect(dev, ssid, wait_connect=True):
16 dev.connect(ssid, key_mgmt="WPA-EAP", scan_freq="2412",
17 eap="PSK", identity="psk.user@example.com",
18 password_hex="0123456789abcdef0123456789abcdef",
19 wait_connect=wait_connect)
20
21 def test_radius_auth_unreachable(dev, apdev):
22 """RADIUS Authentication server unreachable"""
23 params = hostapd.wpa2_eap_params(ssid="radius-auth")
24 params['auth_server_port'] = "18139"
25 hostapd.add_ap(apdev[0]['ifname'], params)
26 hapd = hostapd.Hostapd(apdev[0]['ifname'])
27 connect(dev[0], "radius-auth", wait_connect=False)
28 ev = dev[0].wait_event(["CTRL-EVENT-EAP-STARTED"])
29 if ev is None:
30 raise Exception("Timeout on EAP start")
31 logger.info("Checking for RADIUS retries")
32 time.sleep(4)
33 mib = hapd.get_mib()
34 if "radiusAuthClientAccessRequests" not in mib:
35 raise Exception("Missing MIB fields")
36 if int(mib["radiusAuthClientAccessRetransmissions"]) < 1:
37 raise Exception("Missing RADIUS Authentication retransmission")
38 if int(mib["radiusAuthClientPendingRequests"]) < 1:
39 raise Exception("Missing pending RADIUS Authentication request")
40
41 def test_radius_acct_unreachable(dev, apdev):
42 """RADIUS Accounting server unreachable"""
43 params = hostapd.wpa2_eap_params(ssid="radius-acct")
44 params['acct_server_addr'] = "127.0.0.1"
45 params['acct_server_port'] = "18139"
46 params['acct_server_shared_secret'] = "radius"
47 hostapd.add_ap(apdev[0]['ifname'], params)
48 hapd = hostapd.Hostapd(apdev[0]['ifname'])
49 connect(dev[0], "radius-acct")
50 logger.info("Checking for RADIUS retries")
51 time.sleep(4)
52 mib = hapd.get_mib()
53 if "radiusAccClientRetransmissions" not in mib:
54 raise Exception("Missing MIB fields")
55 if int(mib["radiusAccClientRetransmissions"]) < 2:
56 raise Exception("Missing RADIUS Accounting retransmissions")
57 if int(mib["radiusAccClientPendingRequests"]) < 2:
58 raise Exception("Missing pending RADIUS Accounting requests")
59
60 def test_radius_acct(dev, apdev):
61 """RADIUS Accounting"""
62 as_hapd = hostapd.Hostapd("as")
63 as_mib_start = as_hapd.get_mib(param="radius_server")
64 params = hostapd.wpa2_eap_params(ssid="radius-acct")
65 params['acct_server_addr'] = "127.0.0.1"
66 params['acct_server_port'] = "1813"
67 params['acct_server_shared_secret'] = "radius"
68 hostapd.add_ap(apdev[0]['ifname'], params)
69 hapd = hostapd.Hostapd(apdev[0]['ifname'])
70 connect(dev[0], "radius-acct")
71 logger.info("Checking for RADIUS counters")
72 count = 0
73 while True:
74 mib = hapd.get_mib()
75 if int(mib['radiusAccClientResponses']) >= 2:
76 break
77 time.sleep(0.1)
78 count += 1
79 if count > 10:
80 raise Exception("Did not receive Accounting-Response packets")
81
82 if int(mib['radiusAccClientRetransmissions']) > 0:
83 raise Exception("Unexpected Accounting-Request retransmission")
84
85 as_mib_end = as_hapd.get_mib(param="radius_server")
86
87 req_s = int(as_mib_start['radiusAccServTotalRequests'])
88 req_e = int(as_mib_end['radiusAccServTotalRequests'])
89 if req_e < req_s + 2:
90 raise Exception("Unexpected RADIUS server acct MIB value")
91
92 acc_s = int(as_mib_start['radiusAuthServAccessAccepts'])
93 acc_e = int(as_mib_end['radiusAuthServAccessAccepts'])
94 if acc_e < acc_s + 1:
95 raise Exception("Unexpected RADIUS server auth MIB value")
96
97 def test_radius_das_disconnect(dev, apdev):
98 """RADIUS Dynamic Authorization Extensions - Disconnect"""
99 try:
100 import pyrad.client
101 import pyrad.packet
102 import pyrad.dictionary
103 import radius_das
104 except ImportError:
105 return "skip"
106
107 params = hostapd.wpa2_eap_params(ssid="radius-das")
108 params['radius_das_port'] = "3799"
109 params['radius_das_client'] = "127.0.0.1 secret"
110 params['radius_das_require_event_timestamp'] = "1"
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
238 logger.info("Disconnect-Request with matching Acct-Session-Id")
239 req = radius_das.DisconnectPacket(dict=dict, secret="secret",
240 Acct_Session_Id=id,
241 Event_Timestamp=int(time.time()))
242 reply = srv.SendPacket(req)
243 logger.debug("RADIUS response from hostapd")
244 for i in reply.keys():
245 logger.debug("%s: %s" % (i, reply[i]))
246 if reply.code != pyrad.packet.DisconnectACK:
247 raise Exception("Unexpected response code")
248
249 ev = dev[0].wait_event(["CTRL-EVENT-DISCONNECTED"])
250 if ev is None:
251 raise Exception("Timeout while waiting for disconnection")
252 ev = dev[0].wait_event(["CTRL-EVENT-CONNECTED"])
253 if ev is None:
254 raise Exception("Timeout while waiting for re-connection")
255
256 logger.info("Disconnect-Request with matching User-Name")
257 req = radius_das.DisconnectPacket(dict=dict, secret="secret",
258 User_Name="psk.user@example.com",
259 Event_Timestamp=int(time.time()))
260 reply = srv.SendPacket(req)
261 logger.debug("RADIUS response from hostapd")
262 for i in reply.keys():
263 logger.debug("%s: %s" % (i, reply[i]))
264 if reply.code != pyrad.packet.DisconnectACK:
265 raise Exception("Unexpected response code")
266
267 ev = dev[0].wait_event(["CTRL-EVENT-DISCONNECTED"])
268 if ev is None:
269 raise Exception("Timeout while waiting for disconnection")
270 ev = dev[0].wait_event(["CTRL-EVENT-CONNECTED"])
271 if ev is None:
272 raise Exception("Timeout while waiting for re-connection")
273
274 logger.info("Disconnect-Request with matching Calling-Station-Id")
275 req = radius_das.DisconnectPacket(dict=dict, secret="secret",
276 Calling_Station_Id=addr,
277 Event_Timestamp=int(time.time()))
278 reply = srv.SendPacket(req)
279 logger.debug("RADIUS response from hostapd")
280 for i in reply.keys():
281 logger.debug("%s: %s" % (i, reply[i]))
282 if reply.code != pyrad.packet.DisconnectACK:
283 raise Exception("Unexpected response code")
284
285 ev = dev[0].wait_event(["CTRL-EVENT-DISCONNECTED"])
286 if ev is None:
287 raise Exception("Timeout while waiting for disconnection")
288 ev = dev[0].wait_event(["CTRL-EVENT-CONNECTED"])
289 if ev is None:
290 raise Exception("Timeout while waiting for re-connection")
291
292 logger.info("Disconnect-Request with matching Calling-Station-Id and non-matching CUI")
293 req = radius_das.DisconnectPacket(dict=dict, secret="secret",
294 Calling_Station_Id=addr,
295 Chargeable_User_Identity="foo@example.com",
296 Event_Timestamp=int(time.time()))
297 reply = srv.SendPacket(req)
298 logger.debug("RADIUS response from hostapd")
299 for i in reply.keys():
300 logger.debug("%s: %s" % (i, reply[i]))
301 if reply.code != pyrad.packet.DisconnectACK:
302 raise Exception("Unexpected response code")
303
304 ev = dev[0].wait_event(["CTRL-EVENT-DISCONNECTED"])
305 if ev is None:
306 raise Exception("Timeout while waiting for disconnection")
307 ev = dev[0].wait_event(["CTRL-EVENT-CONNECTED"])
308 if ev is None:
309 raise Exception("Timeout while waiting for re-connection")
310
311 def test_radius_das_coa(dev, apdev):
312 """RADIUS Dynamic Authorization Extensions - CoA"""
313 try:
314 import pyrad.client
315 import pyrad.packet
316 import pyrad.dictionary
317 import radius_das
318 except ImportError:
319 return "skip"
320
321 params = hostapd.wpa2_eap_params(ssid="radius-das")
322 params['radius_das_port'] = "3799"
323 params['radius_das_client'] = "127.0.0.1 secret"
324 params['radius_das_require_event_timestamp'] = "1"
325 hapd = hostapd.add_ap(apdev[0]['ifname'], params)
326 connect(dev[0], "radius-das")
327 addr = dev[0].p2p_interface_addr()
328 sta = hapd.get_sta(addr)
329 id = sta['dot1xAuthSessionId']
330
331 dict = pyrad.dictionary.Dictionary("dictionary.radius")
332
333 srv = pyrad.client.Client(server="127.0.0.1", acctport=3799,
334 secret="secret", dict=dict)
335 srv.retries = 1
336 srv.timeout = 1
337
338 # hostapd does not currently support CoA-Request, so NAK is expected
339 logger.info("CoA-Request with matching Acct-Session-Id")
340 req = radius_das.CoAPacket(dict=dict, secret="secret",
341 Acct_Session_Id=id,
342 Event_Timestamp=int(time.time()))
343 reply = srv.SendPacket(req)
344 logger.debug("RADIUS response from hostapd")
345 for i in reply.keys():
346 logger.debug("%s: %s" % (i, reply[i]))
347 if reply.code != pyrad.packet.CoANAK:
348 raise Exception("Unexpected response code")
349 if 'Error-Cause' not in reply:
350 raise Exception("Missing Error-Cause")
351 if reply['Error-Cause'][0] != 405:
352 raise Exception("Unexpected Error-Cause: {}".format(reply['Error-Cause']))