test_ap_config.py 18 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402403404405406407408409410411412413414415416417418419420421
  1. # hostapd configuration tests
  2. # Copyright (c) 2014-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. import os
  7. import signal
  8. import time
  9. import logging
  10. logger = logging.getLogger(__name__)
  11. import subprocess
  12. from remotehost import remote_compatible
  13. import hostapd
  14. from utils import alloc_fail, fail_test
  15. @remote_compatible
  16. def test_ap_config_errors(dev, apdev):
  17. """Various hostapd configuration errors"""
  18. # IEEE 802.11d without country code
  19. params = { "ssid": "foo", "ieee80211d": "1" }
  20. hapd = hostapd.add_ap(apdev[0], params, no_enable=True)
  21. if "FAIL" not in hapd.request("ENABLE"):
  22. raise Exception("Unexpected ENABLE success (ieee80211d without country_code)")
  23. hostapd.remove_bss(apdev[0])
  24. # IEEE 802.11h without IEEE 802.11d
  25. params = { "ssid": "foo", "ieee80211h": "1" }
  26. hapd = hostapd.add_ap(apdev[0], params, no_enable=True)
  27. if "FAIL" not in hapd.request("ENABLE"):
  28. raise Exception("Unexpected ENABLE success (ieee80211h without ieee80211d")
  29. hostapd.remove_bss(apdev[0])
  30. # Power Constraint without IEEE 802.11d
  31. params = { "ssid": "foo", "local_pwr_constraint": "1" }
  32. hapd = hostapd.add_ap(apdev[0], params, no_enable=True)
  33. if "FAIL" not in hapd.request("ENABLE"):
  34. raise Exception("Unexpected ENABLE success (local_pwr_constraint without ieee80211d)")
  35. hostapd.remove_bss(apdev[0])
  36. # Spectrum management without Power Constraint
  37. params = { "ssid": "foo", "spectrum_mgmt_required": "1" }
  38. hapd = hostapd.add_ap(apdev[0], params, no_enable=True)
  39. if "FAIL" not in hapd.request("ENABLE"):
  40. raise Exception("Unexpected ENABLE success (spectrum_mgmt_required without local_pwr_constraint)")
  41. hostapd.remove_bss(apdev[0])
  42. # IEEE 802.1X without authentication server
  43. params = { "ssid": "foo", "ieee8021x": "1" }
  44. hapd = hostapd.add_ap(apdev[0], params, no_enable=True)
  45. if "FAIL" not in hapd.request("ENABLE"):
  46. raise Exception("Unexpected ENABLE success (ieee8021x)")
  47. hostapd.remove_bss(apdev[0])
  48. # RADIUS-PSK without macaddr_acl=2
  49. params = hostapd.wpa2_params(ssid="foo", passphrase="12345678")
  50. params["wpa_psk_radius"] = "1"
  51. hapd = hostapd.add_ap(apdev[0], params, no_enable=True)
  52. if "FAIL" not in hapd.request("ENABLE"):
  53. raise Exception("Unexpected ENABLE success (wpa_psk_radius)")
  54. hostapd.remove_bss(apdev[0])
  55. # FT without NAS-Identifier
  56. params = { "wpa": "2",
  57. "wpa_key_mgmt": "FT-PSK",
  58. "rsn_pairwise": "CCMP",
  59. "wpa_passphrase": "12345678" }
  60. hapd = hostapd.add_ap(apdev[0], params, no_enable=True)
  61. if "FAIL" not in hapd.request("ENABLE"):
  62. raise Exception("Unexpected ENABLE success (FT without nas_identifier)")
  63. hostapd.remove_bss(apdev[0])
  64. # Hotspot 2.0 without WPA2/CCMP
  65. params = hostapd.wpa2_params(ssid="foo")
  66. params['wpa_key_mgmt'] = "WPA-EAP"
  67. params['ieee8021x'] = "1"
  68. params['auth_server_addr'] = "127.0.0.1"
  69. params['auth_server_port'] = "1812"
  70. params['auth_server_shared_secret'] = "radius"
  71. params['interworking'] = "1"
  72. params['hs20'] = "1"
  73. params['wpa'] = "1"
  74. hapd = hostapd.add_ap(apdev[0], params, no_enable=True)
  75. if "FAIL" not in hapd.request("ENABLE"):
  76. raise Exception("Unexpected ENABLE success (HS 2.0 without WPA2/CCMP)")
  77. hostapd.remove_bss(apdev[0])
  78. def test_ap_config_reload(dev, apdev, params):
  79. """hostapd configuration reload"""
  80. hapd = hostapd.add_ap(apdev[0], { "ssid": "foo" })
  81. hapd.set("ssid", "foobar")
  82. with open(os.path.join(params['logdir'], 'hostapd-test.pid'), "r") as f:
  83. pid = int(f.read())
  84. os.kill(pid, signal.SIGHUP)
  85. time.sleep(0.1)
  86. dev[0].connect("foobar", key_mgmt="NONE", scan_freq="2412")
  87. hapd.set("ssid", "foo")
  88. os.kill(pid, signal.SIGHUP)
  89. dev[0].wait_disconnected()
  90. dev[0].request("DISCONNECT")
  91. def test_ap_config_reload_file(dev, apdev, params):
  92. """hostapd configuration reload from file"""
  93. hapd = hostapd.add_iface(apdev[0], "bss-1.conf")
  94. hapd.enable()
  95. hapd.set("ssid", "foobar")
  96. with open(os.path.join(params['logdir'], 'hostapd-test.pid'), "r") as f:
  97. pid = int(f.read())
  98. os.kill(pid, signal.SIGHUP)
  99. time.sleep(0.1)
  100. dev[0].connect("foobar", key_mgmt="NONE", scan_freq="2412")
  101. hapd.set("ssid", "foo")
  102. os.kill(pid, signal.SIGHUP)
  103. dev[0].wait_disconnected()
  104. dev[0].request("DISCONNECT")
  105. def write_hostapd_config(conffile, ifname, ssid):
  106. with open(conffile, "w") as f:
  107. f.write("driver=nl80211\n")
  108. f.write("hw_mode=g\n")
  109. f.write("channel=1\n")
  110. f.write("ieee80211n=1\n")
  111. f.write("interface=" + ifname + "\n")
  112. f.write("ssid=" + ssid + "\n")
  113. def test_ap_config_reload_on_sighup(dev, apdev, params):
  114. """hostapd configuration reload modification from file on SIGHUP"""
  115. pidfile = os.path.join(params['logdir'],
  116. "ap_config_reload_on_sighup-hostapd.pid")
  117. logfile = os.path.join(params['logdir'],
  118. "ap_config_reload_on_sighup-hostapd-log")
  119. conffile = os.path.join(os.getcwd(), params['logdir'],
  120. "ap_config_reload_on_sighup-hostapd.conf")
  121. prg = os.path.join(params['logdir'], 'alt-hostapd/hostapd/hostapd')
  122. if not os.path.exists(prg):
  123. prg = '../../hostapd/hostapd'
  124. write_hostapd_config(conffile, apdev[0]['ifname'], "test-1")
  125. cmd = [ prg, '-B', '-dddt', '-P', pidfile, '-f', logfile, conffile ]
  126. res = subprocess.check_call(cmd)
  127. if res != 0:
  128. raise Exception("Could not start hostapd: %s" % str(res))
  129. dev[0].connect("test-1", key_mgmt="NONE", scan_freq="2412")
  130. dev[0].request("REMOVE_NETWORK all")
  131. dev[0].wait_disconnected()
  132. write_hostapd_config(conffile, apdev[0]['ifname'], "test-2")
  133. with open(pidfile, "r") as f:
  134. pid = int(f.read())
  135. os.kill(pid, signal.SIGHUP)
  136. dev[0].connect("test-2", key_mgmt="NONE", scan_freq="2412")
  137. dev[0].request("REMOVE_NETWORK all")
  138. dev[0].wait_disconnected()
  139. os.kill(pid, signal.SIGTERM)
  140. removed = False
  141. for i in range(20):
  142. time.sleep(0.1)
  143. if not os.path.exists(pidfile):
  144. removed = True
  145. break
  146. if not removed:
  147. raise Exception("hostapd PID file not removed on SIGTERM")
  148. def test_ap_config_reload_before_enable(dev, apdev, params):
  149. """hostapd configuration reload before enable"""
  150. hapd = hostapd.add_iface(apdev[0], "bss-1.conf")
  151. with open(os.path.join(params['logdir'], 'hostapd-test.pid'), "r") as f:
  152. pid = int(f.read())
  153. os.kill(pid, signal.SIGHUP)
  154. hapd.ping()
  155. def test_ap_config_sigusr1(dev, apdev, params):
  156. """hostapd SIGUSR1"""
  157. hapd = hostapd.add_ap(apdev[0], { "ssid": "foobar" })
  158. with open(os.path.join(params['logdir'], 'hostapd-test.pid'), "r") as f:
  159. pid = int(f.read())
  160. os.kill(pid, signal.SIGUSR1)
  161. dev[0].connect("foobar", key_mgmt="NONE", scan_freq="2412")
  162. os.kill(pid, signal.SIGUSR1)
  163. def test_ap_config_invalid_value(dev, apdev, params):
  164. """Ignoring invalid hostapd configuration parameter updates"""
  165. hapd = hostapd.add_ap(apdev[0], { "ssid": "test" }, no_enable=True)
  166. not_exist = "/tmp/hostapd-test/does-not-exist"
  167. tests = [ ("driver", "foobar"),
  168. ("ssid2", "Q"),
  169. ("macaddr_acl", "255"),
  170. ("accept_mac_file", not_exist),
  171. ("deny_mac_file", not_exist),
  172. ("eapol_version", "255"),
  173. ("eap_user_file", not_exist),
  174. ("wep_key_len_broadcast", "-1"),
  175. ("wep_key_len_unicast", "-1"),
  176. ("wep_rekey_period", "-1"),
  177. ("eap_rekey_period", "-1"),
  178. ("radius_client_addr", "foo"),
  179. ("acs_chan_bias", "-1:0.8"),
  180. ("acs_chan_bias", "1"),
  181. ("acs_chan_bias", "1:p"),
  182. ("acs_chan_bias", "1:-0.8"),
  183. ("acs_chan_bias", "1:0.8p"),
  184. ("dtim_period", "0"),
  185. ("bss_load_update_period", "-1"),
  186. ("send_probe_response", "255"),
  187. ("beacon_rate", "ht:-1"),
  188. ("beacon_rate", "ht:32"),
  189. ("beacon_rate", "vht:-1"),
  190. ("beacon_rate", "vht:10"),
  191. ("beacon_rate", "9"),
  192. ("beacon_rate", "10001"),
  193. ("vlan_file", not_exist),
  194. ("bss", ""),
  195. ("bssid", "foo"),
  196. ("extra_cred", not_exist),
  197. ("anqp_elem", "265"),
  198. ("anqp_elem", "265"),
  199. ("anqp_elem", "265:1"),
  200. ("anqp_elem", "265:1q"),
  201. ("fst_priority", ""),
  202. ("fils_cache_id", "q"),
  203. ("unknown-item", "foo") ]
  204. for field, val in tests:
  205. if "FAIL" not in hapd.request("SET %s %s" % (field, val)):
  206. raise Exception("Invalid %s accepted" % field)
  207. hapd.enable()
  208. dev[0].connect("test", key_mgmt="NONE", scan_freq="2412")
  209. def test_ap_config_eap_user_file_parsing(dev, apdev, params):
  210. """hostapd eap_user_file parsing"""
  211. tmp = os.path.join(params['logdir'], 'ap_vlan_file_parsing.tmp')
  212. hapd = hostapd.add_ap(apdev[0], { "ssid": "foobar" })
  213. for i in range(2):
  214. if "OK" not in hapd.request("SET eap_user_file auth_serv/eap_user.conf"):
  215. raise Exception("eap_user_file rejected")
  216. tests = [ "#\n\n*\tTLS\nradius_accept_attr=:",
  217. "foo\n",
  218. "\"foo\n",
  219. "\"foo\"\n",
  220. "\"foo\" FOOBAR\n",
  221. "\"foo\" " + 10*"TLS," + "TLS \"\n",
  222. "\"foo\" TLS \nfoo\n",
  223. "\"foo\" PEAP hash:foo\n",
  224. "\"foo\" PEAP hash:8846f7eaee8fb117ad06bdd830b7586q\n",
  225. "\"foo\" PEAP 01020\n",
  226. "\"foo\" PEAP 010q\n",
  227. "\"foo\" TLS\nradius_accept_attr=123:x:012\n",
  228. "\"foo\" TLS\nradius_accept_attr=123:x:012q\n",
  229. "\"foo\" TLS\nradius_accept_attr=123:Q:01\n",
  230. "\"foo\" TLS\nradius_accept_attr=123\nfoo\n" ]
  231. for t in tests:
  232. with open(tmp, "w") as f:
  233. f.write(t)
  234. if "FAIL" not in hapd.request("SET eap_user_file " + tmp):
  235. raise Exception("Invalid eap_user_file accepted")
  236. tests = [ ("\"foo\" TLS\n", 2, "hostapd_config_read_eap_user"),
  237. ("\"foo\" PEAP \"foo\"\n", 3, "hostapd_config_read_eap_user"),
  238. ("\"foo\" PEAP hash:8846f7eaee8fb117ad06bdd830b75861\n", 3,
  239. "hostapd_config_read_eap_user"),
  240. ("\"foo\" PEAP 0102\n", 3, "hostapd_config_read_eap_user"),
  241. ("\"foo\" TLS\nradius_accept_attr=123\n", 1,
  242. "=hostapd_parse_radius_attr"),
  243. ("\"foo\" TLS\nradius_accept_attr=123\n", 1,
  244. "wpabuf_alloc;hostapd_parse_radius_attr"),
  245. ("\"foo\" TLS\nradius_accept_attr=123:s:foo\n", 2,
  246. "hostapd_parse_radius_attr"),
  247. ("\"foo\" TLS\nradius_accept_attr=123:x:0102\n", 2,
  248. "hostapd_parse_radius_attr"),
  249. ("\"foo\" TLS\nradius_accept_attr=123:d:1\n", 2,
  250. "hostapd_parse_radius_attr"),
  251. ("* TLS\n", 1, "hostapd_config_read_eap_user") ]
  252. for t, count, func in tests:
  253. with alloc_fail(hapd, count, func):
  254. with open(tmp, "w") as f:
  255. f.write(t)
  256. if "FAIL" not in hapd.request("SET eap_user_file " + tmp):
  257. raise Exception("eap_user_file accepted during OOM")
  258. def test_ap_config_set_oom(dev, apdev):
  259. """hostapd configuration parsing OOM"""
  260. hapd = hostapd.add_ap(apdev[0], { "ssid": "foobar" })
  261. tests = [ (1, "hostapd_parse_das_client",
  262. "SET radius_das_client 192.168.1.123 pw"),
  263. (1, "hostapd_config_read_wep", "SET wep_key0 \"hello\""),
  264. (1, "hostapd_config_read_wep", "SET wep_key0 0102030405"),
  265. (1, "hostapd_parse_chanlist", "SET chanlist 1 6 11-13"),
  266. (1, "hostapd_config_bss", "SET bss foo"),
  267. (2, "hostapd_config_bss", "SET bss foo"),
  268. (3, "hostapd_config_bss", "SET bss foo"),
  269. (1, "add_r0kh",
  270. "SET r0kh 02:01:02:03:04:05 r0kh-1.example.com 000102030405060708090a0b0c0d0e0f"),
  271. (1, "add_r1kh",
  272. "SET r1kh 02:01:02:03:04:05 02:11:22:33:44:55 000102030405060708090a0b0c0d0e0f"),
  273. (1, "parse_roaming_consortium", "SET roaming_consortium 021122"),
  274. (1, "parse_lang_string", "SET venue_name eng:Example venue"),
  275. (1, "parse_3gpp_cell_net",
  276. "SET anqp_3gpp_cell_net 244,91;310,026;234,56"),
  277. (1, "parse_nai_realm", "SET nai_realm 0,example.com;example.net"),
  278. (2, "parse_nai_realm", "SET nai_realm 0,example.com;example.net"),
  279. (1, "parse_anqp_elem", "SET anqp_elem 265:0000"),
  280. (2, "parse_anqp_elem", "SET anqp_elem 266:000000"),
  281. (1, "hs20_parse_conn_capab", "SET hs20_conn_capab 1:0:2"),
  282. (1, "hs20_parse_wan_metrics",
  283. "SET hs20_wan_metrics 01:8000:1000:80:240:3000"),
  284. (1, "hs20_parse_icon",
  285. "SET hs20_icon 32:32:eng:image/png:icon32:/tmp/icon32.png"),
  286. (1, "hs20_parse_osu_server_uri",
  287. "SET osu_server_uri https://example.com/osu/"),
  288. (1, "hostapd_config_parse_acs_chan_bias",
  289. "SET acs_chan_bias 1:0.8 6:0.8 11:0.8"),
  290. (2, "hostapd_config_parse_acs_chan_bias",
  291. "SET acs_chan_bias 1:0.8 6:0.8 11:0.8"),
  292. (1, "parse_wpabuf_hex", "SET vendor_elements 01020304"),
  293. (1, "parse_fils_realm", "SET fils_realm example.com"),
  294. (1, "hostapd_config_fill",
  295. "SET pac_opaque_encr_key 000102030405060708090a0b0c0d0e0f"),
  296. (1, "hostapd_config_fill", "SET eap_message hello"),
  297. (1, "hostapd_config_fill",
  298. "SET wpa_psk 0123456789abcdef0123456789abcdef0123456789abcdef0123456789abcdef"),
  299. (1, "hostapd_config_fill", "SET time_zone EST5"),
  300. (1, "hostapd_config_fill",
  301. "SET network_auth_type 02http://www.example.com/redirect/"),
  302. (1, "hostapd_config_fill", "SET domain_name example.com"),
  303. (1, "hostapd_config_fill", "SET hs20_operating_class 5173"),
  304. (1, "hostapd_config_fill", "SET own_ie_override 11223344"),
  305. (1, "hostapd_parse_intlist", "SET sae_groups 19 25"),
  306. (1, "hostapd_parse_intlist", "SET basic_rates 10 20 55 110"),
  307. (1, "hostapd_parse_intlist", "SET supported_rates 10 20 55 110") ]
  308. for count, func, cmd in tests:
  309. with alloc_fail(hapd, count, func):
  310. if "FAIL" not in hapd.request(cmd):
  311. raise Exception("Command accepted during OOM: " + cmd)
  312. hapd.set("hs20_icon", "32:32:eng:image/png:icon32:/tmp/icon32.png")
  313. hapd.set("hs20_conn_capab", "1:0:2")
  314. hapd.set("nai_realm", "0,example.com;example.net")
  315. hapd.set("venue_name", "eng:Example venue")
  316. hapd.set("roaming_consortium", "021122")
  317. hapd.set("osu_server_uri", "https://example.com/osu/")
  318. hapd.set("vendor_elements", "01020304")
  319. hapd.set("vendor_elements", "01020304")
  320. hapd.set("vendor_elements", "")
  321. hapd.set("lci", "11223344")
  322. hapd.set("civic", "11223344")
  323. hapd.set("lci", "")
  324. hapd.set("civic", "")
  325. tests = [ (1, "hs20_parse_icon",
  326. "SET hs20_icon 32:32:eng:image/png:icon32:/tmp/icon32.png"),
  327. (1, "parse_roaming_consortium", "SET roaming_consortium 021122"),
  328. (2, "parse_nai_realm", "SET nai_realm 0,example.com;example.net"),
  329. (1, "parse_lang_string", "SET venue_name eng:Example venue"),
  330. (1, "hs20_parse_osu_server_uri",
  331. "SET osu_server_uri https://example.com/osu/"),
  332. (1, "hs20_parse_osu_nai", "SET osu_nai anonymous@example.com"),
  333. (1, "hostapd_parse_intlist", "SET osu_method_list 1 0"),
  334. (1, "hs20_parse_osu_icon", "SET osu_icon icon32"),
  335. (2, "hs20_parse_osu_icon", "SET osu_icon icon32"),
  336. (2, "hs20_parse_osu_icon", "SET osu_icon icon32"),
  337. (1, "hs20_parse_conn_capab", "SET hs20_conn_capab 1:0:2") ]
  338. for count, func, cmd in tests:
  339. with alloc_fail(hapd, count, func):
  340. if "FAIL" not in hapd.request(cmd):
  341. raise Exception("Command accepted during OOM (2): " + cmd)
  342. tests = [ (1, "parse_fils_realm", "SET fils_realm example.com") ]
  343. for count, func, cmd in tests:
  344. with fail_test(hapd, count, func):
  345. if "FAIL" not in hapd.request(cmd):
  346. raise Exception("Command accepted during FAIL_TEST: " + cmd)
  347. def test_ap_config_set_errors(dev, apdev):
  348. """hostapd configuration parsing errors"""
  349. hapd = hostapd.add_ap(apdev[0], { "ssid": "foobar" })
  350. hapd.set("wep_key0", '"hello"')
  351. hapd.set("wep_key1", '"hello"')
  352. hapd.set("wep_key0", '')
  353. hapd.set("wep_key0", '"hello"')
  354. if "FAIL" not in hapd.request("SET wep_key1 \"hello\""):
  355. raise Exception("SET wep_key1 allowed to override existing key")
  356. hapd.set("wep_key1", '')
  357. hapd.set("wep_key1", '"hello"')
  358. hapd.set("auth_server_addr", "127.0.0.1")
  359. hapd.set("acct_server_addr", "127.0.0.1")
  360. tests = [ "SET eap_reauth_period -1",
  361. "SET fst_llt ",
  362. "SET auth_server_addr_replace foo",
  363. "SET acct_server_addr_replace foo" ]
  364. for t in tests:
  365. if "FAIL" not in hapd.request(t):
  366. raise Exception("Invalid command accepted: " + t)
  367. # Deprecated entries
  368. hapd.set("tx_queue_after_beacon_aifs", '2')
  369. hapd.set("tx_queue_beacon_aifs", '2')
  370. hapd.set("tx_queue_data9_aifs", '2')
  371. hapd.set("debug", '1')
  372. hapd.set("dump_file", '/tmp/hostapd-test-dump')
  373. hapd.set("eap_authenticator", '0')
  374. hapd.set("radio_measurements", '0')
  375. hapd.set("radio_measurements", '1')
  376. # Various extra coverage (not really errors)
  377. hapd.set("logger_syslog_level", '1')
  378. hapd.set("logger_syslog", '0')
  379. for i in range(50000):
  380. if "OK" not in hapd.request("SET hs20_conn_capab 17:5060:0"):
  381. logger.info("hs20_conn_capab limit at %d" % i)
  382. break
  383. if i < 1000 or i >= 49999:
  384. raise Exception("hs20_conn_capab limit not seen")