Import Upstream version 2.9
This commit is contained in:
commit
19e879109e
|
@ -0,0 +1,35 @@
|
|||
*.a
|
||||
*.o
|
||||
*.d
|
||||
*.gcno
|
||||
*.gcda
|
||||
*.gcov
|
||||
*.pyc
|
||||
*~
|
||||
.config
|
||||
tests/hwsim/logs
|
||||
tests/remote/logs
|
||||
wpaspy/build
|
||||
wpa_supplicant/eapol_test
|
||||
wpa_supplicant/nfc_pw_token
|
||||
wpa_supplicant/preauth_test
|
||||
wpa_supplicant/wpa_cli
|
||||
wpa_supplicant/wpa_passphrase
|
||||
wpa_supplicant/wpa_supplicant
|
||||
wpa_supplicant/wpa_priv
|
||||
wpa_supplicant/wpa_gui/Makefile
|
||||
wpa_supplicant/wpa_gui/wpa_gui
|
||||
wpa_supplicant/wpa_gui-qt4/Makefile
|
||||
wpa_supplicant/wpa_gui-qt4/wpa_gui
|
||||
wpa_supplicant/libwpa_test1
|
||||
wpa_supplicant/libwpa_test2
|
||||
hostapd/hostapd
|
||||
hostapd/hostapd_cli
|
||||
hostapd/hlr_auc_gw
|
||||
hostapd/nt_password_hash
|
||||
mac80211_hwsim/tools/hwsim_test
|
||||
wlantest/libwlantest.a
|
||||
wlantest/test_vectors
|
||||
wlantest/wlantest
|
||||
wlantest/wlantest_cli
|
||||
**/parallel-vm.log
|
|
@ -0,0 +1,171 @@
|
|||
Contributions to hostap.git
|
||||
---------------------------
|
||||
|
||||
This software is distributed under a permissive open source license to
|
||||
allow it to be used in any projects, whether open source or proprietary.
|
||||
Contributions to the project are welcome and it is important to maintain
|
||||
clear record of contributions and terms under which they are licensed.
|
||||
To help with this, following procedure is used to allow acceptance and
|
||||
recording of the terms.
|
||||
|
||||
All contributions are expected to be licensed under the modified BSD
|
||||
license (see below). Acknowledgment of the terms is tracked through
|
||||
inclusion of Signed-off-by tag in the contributions at the end of the
|
||||
commit log message. This tag indicates that the contributor agrees with
|
||||
the Developer Certificate of Origin (DCO) version 1.1 terms (see below;
|
||||
also available from http://developercertificate.org/).
|
||||
|
||||
|
||||
The current requirements for contributions to hostap.git
|
||||
--------------------------------------------------------
|
||||
|
||||
To indicate your acceptance of Developer's Certificate of Origin 1.1
|
||||
terms, please add the following line to the end of the commit message
|
||||
for each contribution you make to the project:
|
||||
|
||||
Signed-off-by: Your Name <your@email.example.org>
|
||||
|
||||
using your real name. Pseudonyms or anonymous contributions cannot
|
||||
unfortunately be accepted.
|
||||
|
||||
|
||||
The preferred method of submitting the contribution to the project is by
|
||||
email to the hostap mailing list:
|
||||
hostap@lists.infradead.org
|
||||
Note that the list may require subscription before accepting message
|
||||
without moderation. You can subscribe to the list at this address:
|
||||
http://lists.infradead.org/mailman/listinfo/hostap
|
||||
|
||||
The message should contain an inlined patch against the current
|
||||
development branch (i.e., the master branch of
|
||||
git://w1.fi/hostap.git). Please make sure the software you use for
|
||||
sending the patch does not corrupt whitespace. If that cannot be fixed
|
||||
for some reason, it is better to include an attached version of the
|
||||
patch file than just send a whitespace damaged version in the message
|
||||
body.
|
||||
|
||||
The patches should be separate logical changes rather than doing
|
||||
everything in a single patch. In other words, please keep cleanup, new
|
||||
features, and bug fixes all in their own patches. Each patch needs a
|
||||
commit log that describes the changes (what the changes fix, what
|
||||
functionality is added, why the changes are useful, etc.).
|
||||
|
||||
Please try to follow the coding style used in the project.
|
||||
|
||||
In general, the best way of generating a suitable formatted patch file
|
||||
is by committing the changes to a cloned git repository and using git
|
||||
format-patch. The patch can then be sent, e.g., with git send-email.
|
||||
|
||||
|
||||
History of license and contributions terms
|
||||
------------------------------------------
|
||||
|
||||
Until February 11, 2012, in case of most files in hostap.git, "under the
|
||||
open source license indicated in the file" means that the contribution
|
||||
is licensed both under GPL v2 and modified BSD license (see below) and
|
||||
the choice between these licenses is given to anyone who redistributes
|
||||
or uses the software. As such, the contribution has to be licensed under
|
||||
both options to allow this choice.
|
||||
|
||||
As of February 11, 2012, the project has chosen to use only the BSD
|
||||
license option for future distribution. As such, the GPL v2 license
|
||||
option is no longer used and the contributions are not required to be
|
||||
licensed until GPL v2. In case of most files in hostap.git, "under the
|
||||
open source license indicated in the file" means that the contribution
|
||||
is licensed under the modified BSD license (see below).
|
||||
|
||||
Until February 13, 2014, the project used an extended version of the DCO
|
||||
that included the identical items (a) through (d) from DCO 1.1 and an
|
||||
additional item (e):
|
||||
|
||||
(e) The contribution can be licensed under the modified BSD license
|
||||
as shown below even in case of files that are currently licensed
|
||||
under other terms.
|
||||
|
||||
This was used during the period when some of the files included the old
|
||||
license terms. Acceptance of this extended DCO version was indicated
|
||||
with a Signed-hostap tag in the commit message. This additional item (e)
|
||||
was used to collect explicit approval to license the contribution with
|
||||
only the modified BSD license (see below), i.e., without the GPL v2
|
||||
option. This was done to allow simpler licensing terms to be used in the
|
||||
future. It should be noted that the modified BSD license is compatible
|
||||
with GNU GPL and as such, this possible move to simpler licensing option
|
||||
does not prevent use of this software in GPL projects.
|
||||
|
||||
|
||||
===[ start quote from http://developercertificate.org/ ]=======================
|
||||
|
||||
Developer Certificate of Origin
|
||||
Version 1.1
|
||||
|
||||
Copyright (C) 2004, 2006 The Linux Foundation and its contributors.
|
||||
660 York Street, Suite 102,
|
||||
San Francisco, CA 94110 USA
|
||||
|
||||
Everyone is permitted to copy and distribute verbatim copies of this
|
||||
license document, but changing it is not allowed.
|
||||
|
||||
|
||||
Developer's Certificate of Origin 1.1
|
||||
|
||||
By making a contribution to this project, I certify that:
|
||||
|
||||
(a) The contribution was created in whole or in part by me and I
|
||||
have the right to submit it under the open source license
|
||||
indicated in the file; or
|
||||
|
||||
(b) The contribution is based upon previous work that, to the best
|
||||
of my knowledge, is covered under an appropriate open source
|
||||
license and I have the right under that license to submit that
|
||||
work with modifications, whether created in whole or in part
|
||||
by me, under the same open source license (unless I am
|
||||
permitted to submit under a different license), as indicated
|
||||
in the file; or
|
||||
|
||||
(c) The contribution was provided directly to me by some other
|
||||
person who certified (a), (b) or (c) and I have not modified
|
||||
it.
|
||||
|
||||
(d) I understand and agree that this project and the contribution
|
||||
are public and that a record of the contribution (including all
|
||||
personal information I submit with it, including my sign-off) is
|
||||
maintained indefinitely and may be redistributed consistent with
|
||||
this project or the open source license(s) involved.
|
||||
|
||||
===[ end quote from http://developercertificate.org/ ]=========================
|
||||
|
||||
|
||||
The license terms used for hostap.git files
|
||||
-------------------------------------------
|
||||
|
||||
Modified BSD license (no advertisement clause):
|
||||
|
||||
Copyright (c) 2002-2019, Jouni Malinen <j@w1.fi> and contributors
|
||||
All Rights Reserved.
|
||||
|
||||
Redistribution and use in source and binary forms, with or without
|
||||
modification, are permitted provided that the following conditions are
|
||||
met:
|
||||
|
||||
1. Redistributions of source code must retain the above copyright
|
||||
notice, this list of conditions and the following disclaimer.
|
||||
|
||||
2. Redistributions in binary form must reproduce the above copyright
|
||||
notice, this list of conditions and the following disclaimer in the
|
||||
documentation and/or other materials provided with the distribution.
|
||||
|
||||
3. Neither the name(s) of the above-listed copyright holder(s) nor the
|
||||
names of its contributors may be used to endorse or promote products
|
||||
derived from this software without specific prior written permission.
|
||||
|
||||
THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
|
||||
"AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT
|
||||
LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR
|
||||
A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT
|
||||
OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL,
|
||||
SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT
|
||||
LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE,
|
||||
DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY
|
||||
THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
|
||||
(INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE
|
||||
OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
|
|
@ -0,0 +1,22 @@
|
|||
wpa_supplicant and hostapd
|
||||
--------------------------
|
||||
|
||||
Copyright (c) 2002-2019, Jouni Malinen <j@w1.fi> and contributors
|
||||
All Rights Reserved.
|
||||
|
||||
|
||||
See the README file for the current license terms.
|
||||
|
||||
This software was previously distributed under BSD/GPL v2 dual license
|
||||
terms that allowed either of those license alternatives to be
|
||||
selected. As of February 11, 2012, the project has chosen to use only
|
||||
the BSD license option for future distribution. As such, the GPL v2
|
||||
license option is no longer used. It should be noted that the BSD
|
||||
license option (the one with advertisement clause removed) is compatible
|
||||
with GPL and as such, does not prevent use of this software in projects
|
||||
that use GPL.
|
||||
|
||||
Some of the files may still include pointers to GPL version 2 license
|
||||
terms. However, such copyright and license notifications are maintained
|
||||
only for attribution purposes and any distribution of this software
|
||||
after February 11, 2012 is no longer under the GPL v2 option.
|
|
@ -0,0 +1,56 @@
|
|||
wpa_supplicant and hostapd
|
||||
--------------------------
|
||||
|
||||
Copyright (c) 2002-2019, Jouni Malinen <j@w1.fi> and contributors
|
||||
All Rights Reserved.
|
||||
|
||||
These programs are licensed under the BSD license (the one with
|
||||
advertisement clause removed).
|
||||
|
||||
If you are submitting changes to the project, please see CONTRIBUTIONS
|
||||
file for more instructions.
|
||||
|
||||
|
||||
This package may include either wpa_supplicant, hostapd, or both. See
|
||||
README file respective subdirectories (wpa_supplicant/README or
|
||||
hostapd/README) for more details.
|
||||
|
||||
Source code files were moved around in v0.6.x releases and compared to
|
||||
earlier releases, the programs are now built by first going to a
|
||||
subdirectory (wpa_supplicant or hostapd) and creating build
|
||||
configuration (.config) and running 'make' there (for Linux/BSD/cygwin
|
||||
builds).
|
||||
|
||||
|
||||
License
|
||||
-------
|
||||
|
||||
This software may be distributed, used, and modified under the terms of
|
||||
BSD license:
|
||||
|
||||
Redistribution and use in source and binary forms, with or without
|
||||
modification, are permitted provided that the following conditions are
|
||||
met:
|
||||
|
||||
1. Redistributions of source code must retain the above copyright
|
||||
notice, this list of conditions and the following disclaimer.
|
||||
|
||||
2. Redistributions in binary form must reproduce the above copyright
|
||||
notice, this list of conditions and the following disclaimer in the
|
||||
documentation and/or other materials provided with the distribution.
|
||||
|
||||
3. Neither the name(s) of the above-listed copyright holder(s) nor the
|
||||
names of its contributors may be used to endorse or promote products
|
||||
derived from this software without specific prior written permission.
|
||||
|
||||
THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
|
||||
"AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT
|
||||
LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR
|
||||
A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT
|
||||
OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL,
|
||||
SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT
|
||||
LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE,
|
||||
DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY
|
||||
THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
|
||||
(INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE
|
||||
OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
|
File diff suppressed because it is too large
Load Diff
File diff suppressed because it is too large
Load Diff
File diff suppressed because it is too large
Load Diff
|
@ -0,0 +1,354 @@
|
|||
hostapd - user space IEEE 802.11 AP and IEEE 802.1X/WPA/WPA2/EAP
|
||||
Authenticator and RADIUS authentication server
|
||||
================================================================
|
||||
|
||||
Copyright (c) 2002-2019, Jouni Malinen <j@w1.fi> and contributors
|
||||
All Rights Reserved.
|
||||
|
||||
This program is licensed under the BSD license (the one with
|
||||
advertisement clause removed).
|
||||
|
||||
If you are submitting changes to the project, please see CONTRIBUTIONS
|
||||
file for more instructions.
|
||||
|
||||
|
||||
|
||||
License
|
||||
-------
|
||||
|
||||
This software may be distributed, used, and modified under the terms of
|
||||
BSD license:
|
||||
|
||||
Redistribution and use in source and binary forms, with or without
|
||||
modification, are permitted provided that the following conditions are
|
||||
met:
|
||||
|
||||
1. Redistributions of source code must retain the above copyright
|
||||
notice, this list of conditions and the following disclaimer.
|
||||
|
||||
2. Redistributions in binary form must reproduce the above copyright
|
||||
notice, this list of conditions and the following disclaimer in the
|
||||
documentation and/or other materials provided with the distribution.
|
||||
|
||||
3. Neither the name(s) of the above-listed copyright holder(s) nor the
|
||||
names of its contributors may be used to endorse or promote products
|
||||
derived from this software without specific prior written permission.
|
||||
|
||||
THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
|
||||
"AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT
|
||||
LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR
|
||||
A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT
|
||||
OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL,
|
||||
SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT
|
||||
LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE,
|
||||
DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY
|
||||
THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
|
||||
(INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE
|
||||
OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
|
||||
|
||||
|
||||
|
||||
Introduction
|
||||
============
|
||||
|
||||
Originally, hostapd was an optional user space component for Host AP
|
||||
driver. It adds more features to the basic IEEE 802.11 management
|
||||
included in the kernel driver: using external RADIUS authentication
|
||||
server for MAC address based access control, IEEE 802.1X Authenticator
|
||||
and dynamic WEP keying, RADIUS accounting, WPA/WPA2 (IEEE 802.11i/RSN)
|
||||
Authenticator and dynamic TKIP/CCMP keying.
|
||||
|
||||
The current version includes support for other drivers, an integrated
|
||||
EAP server (i.e., allow full authentication without requiring
|
||||
an external RADIUS authentication server), and RADIUS authentication
|
||||
server for EAP authentication.
|
||||
|
||||
|
||||
Requirements
|
||||
------------
|
||||
|
||||
Current hardware/software requirements:
|
||||
- drivers:
|
||||
Host AP driver for Prism2/2.5/3.
|
||||
(http://w1.fi/hostap-driver.html)
|
||||
Please note that station firmware version needs to be 1.7.0 or newer
|
||||
to work in WPA mode.
|
||||
|
||||
mac80211-based drivers that support AP mode (with driver=nl80211).
|
||||
This includes drivers for Atheros (ath9k) and Broadcom (b43)
|
||||
chipsets.
|
||||
|
||||
Any wired Ethernet driver for wired IEEE 802.1X authentication
|
||||
(experimental code)
|
||||
|
||||
FreeBSD -current
|
||||
BSD net80211 layer (e.g., Atheros driver)
|
||||
|
||||
|
||||
Build configuration
|
||||
-------------------
|
||||
|
||||
In order to be able to build hostapd, you will need to create a build
|
||||
time configuration file, .config that selects which optional
|
||||
components are included. See defconfig file for example configuration
|
||||
and list of available options.
|
||||
|
||||
|
||||
|
||||
IEEE 802.1X
|
||||
===========
|
||||
|
||||
IEEE Std 802.1X-2001 is a standard for port-based network access
|
||||
control. In case of IEEE 802.11 networks, a "virtual port" is used
|
||||
between each associated station and the AP. IEEE 802.11 specifies
|
||||
minimal authentication mechanism for stations, whereas IEEE 802.1X
|
||||
introduces a extensible mechanism for authenticating and authorizing
|
||||
users.
|
||||
|
||||
IEEE 802.1X uses elements called Supplicant, Authenticator, Port
|
||||
Access Entity, and Authentication Server. Supplicant is a component in
|
||||
a station and it performs the authentication with the Authentication
|
||||
Server. An access point includes an Authenticator that relays the packets
|
||||
between a Supplicant and an Authentication Server. In addition, it has a
|
||||
Port Access Entity (PAE) with Authenticator functionality for
|
||||
controlling the virtual port authorization, i.e., whether to accept
|
||||
packets from or to the station.
|
||||
|
||||
IEEE 802.1X uses Extensible Authentication Protocol (EAP). The frames
|
||||
between a Supplicant and an Authenticator are sent using EAP over LAN
|
||||
(EAPOL) and the Authenticator relays these frames to the Authentication
|
||||
Server (and similarly, relays the messages from the Authentication
|
||||
Server to the Supplicant). The Authentication Server can be colocated with the
|
||||
Authenticator, in which case there is no need for additional protocol
|
||||
for EAP frame transmission. However, a more common configuration is to
|
||||
use an external Authentication Server and encapsulate EAP frame in the
|
||||
frames used by that server. RADIUS is suitable for this, but IEEE
|
||||
802.1X would also allow other mechanisms.
|
||||
|
||||
Host AP driver includes PAE functionality in the kernel driver. It
|
||||
is a relatively simple mechanism for denying normal frames going to
|
||||
or coming from an unauthorized port. PAE allows IEEE 802.1X related
|
||||
frames to be passed between the Supplicant and the Authenticator even
|
||||
on an unauthorized port.
|
||||
|
||||
User space daemon, hostapd, includes Authenticator functionality. It
|
||||
receives 802.1X (EAPOL) frames from the Supplicant using the wlan#ap
|
||||
device that is also used with IEEE 802.11 management frames. The
|
||||
frames to the Supplicant are sent using the same device.
|
||||
|
||||
The normal configuration of the Authenticator would use an external
|
||||
Authentication Server. hostapd supports RADIUS encapsulation of EAP
|
||||
packets, so the Authentication Server should be a RADIUS server, like
|
||||
FreeRADIUS (http://www.freeradius.org/). The Authenticator in hostapd
|
||||
relays the frames between the Supplicant and the Authentication
|
||||
Server. It also controls the PAE functionality in the kernel driver by
|
||||
controlling virtual port authorization, i.e., station-AP
|
||||
connection, based on the IEEE 802.1X state.
|
||||
|
||||
When a station would like to use the services of an access point, it
|
||||
will first perform IEEE 802.11 authentication. This is normally done
|
||||
with open systems authentication, so there is no security. After
|
||||
this, IEEE 802.11 association is performed. If IEEE 802.1X is
|
||||
configured to be used, the virtual port for the station is set in
|
||||
Unauthorized state and only IEEE 802.1X frames are accepted at this
|
||||
point. The Authenticator will then ask the Supplicant to authenticate
|
||||
with the Authentication Server. After this is completed successfully,
|
||||
the virtual port is set to Authorized state and frames from and to the
|
||||
station are accepted.
|
||||
|
||||
Host AP configuration for IEEE 802.1X
|
||||
-------------------------------------
|
||||
|
||||
The user space daemon has its own configuration file that can be used to
|
||||
define AP options. Distribution package contains an example
|
||||
configuration file (hostapd/hostapd.conf) that can be used as a basis
|
||||
for configuration. It includes examples of all supported configuration
|
||||
options and short description of each option. hostapd should be started
|
||||
with full path to the configuration file as the command line argument,
|
||||
e.g., './hostapd /etc/hostapd.conf'. If you have more that one wireless
|
||||
LAN card, you can use one hostapd process for multiple interfaces by
|
||||
giving a list of configuration files (one per interface) in the command
|
||||
line.
|
||||
|
||||
hostapd includes a minimal co-located IEEE 802.1X server which can be
|
||||
used to test IEEE 802.1X authentication. However, it should not be
|
||||
used in normal use since it does not provide any security. This can be
|
||||
configured by setting ieee8021x and minimal_eap options in the
|
||||
configuration file.
|
||||
|
||||
An external Authentication Server (RADIUS) is configured with
|
||||
auth_server_{addr,port,shared_secret} options. In addition,
|
||||
ieee8021x and own_ip_addr must be set for this mode. With such
|
||||
configuration, the co-located Authentication Server is not used and EAP
|
||||
frames will be relayed using EAPOL between the Supplicant and the
|
||||
Authenticator and RADIUS encapsulation between the Authenticator and
|
||||
the Authentication Server. Other than this, the functionality is similar
|
||||
to the case with the co-located Authentication Server.
|
||||
|
||||
Authentication Server
|
||||
---------------------
|
||||
|
||||
Any RADIUS server supporting EAP should be usable as an IEEE 802.1X
|
||||
Authentication Server with hostapd Authenticator. FreeRADIUS
|
||||
(http://www.freeradius.org/) has been successfully tested with hostapd
|
||||
Authenticator.
|
||||
|
||||
Automatic WEP key configuration
|
||||
-------------------------------
|
||||
|
||||
EAP/TLS generates a session key that can be used to send WEP keys from
|
||||
an AP to authenticated stations. The Authenticator in hostapd can be
|
||||
configured to automatically select a random default/broadcast key
|
||||
(shared by all authenticated stations) with wep_key_len_broadcast
|
||||
option (5 for 40-bit WEP or 13 for 104-bit WEP). In addition,
|
||||
wep_key_len_unicast option can be used to configure individual unicast
|
||||
keys for stations. This requires support for individual keys in the
|
||||
station driver.
|
||||
|
||||
WEP keys can be automatically updated by configuring rekeying. This
|
||||
will improve security of the network since same WEP key will only be
|
||||
used for a limited period of time. wep_rekey_period option sets the
|
||||
interval for rekeying in seconds.
|
||||
|
||||
|
||||
WPA/WPA2
|
||||
========
|
||||
|
||||
Features
|
||||
--------
|
||||
|
||||
Supported WPA/IEEE 802.11i features:
|
||||
- WPA-PSK ("WPA-Personal")
|
||||
- WPA with EAP (e.g., with RADIUS authentication server) ("WPA-Enterprise")
|
||||
- key management for CCMP, TKIP, WEP104, WEP40
|
||||
- RSN/WPA2 (IEEE 802.11i), including PMKSA caching and pre-authentication
|
||||
|
||||
WPA
|
||||
---
|
||||
|
||||
The original security mechanism of IEEE 802.11 standard was not
|
||||
designed to be strong and has proved to be insufficient for most
|
||||
networks that require some kind of security. Task group I (Security)
|
||||
of IEEE 802.11 working group (http://www.ieee802.org/11/) has worked
|
||||
to address the flaws of the base standard and has in practice
|
||||
completed its work in May 2004. The IEEE 802.11i amendment to the IEEE
|
||||
802.11 standard was approved in June 2004 and this amendment was
|
||||
published in July 2004.
|
||||
|
||||
Wi-Fi Alliance (http://www.wi-fi.org/) used a draft version of the
|
||||
IEEE 802.11i work (draft 3.0) to define a subset of the security
|
||||
enhancements that can be implemented with existing wlan hardware. This
|
||||
is called Wi-Fi Protected Access<TM> (WPA). This has now become a
|
||||
mandatory component of interoperability testing and certification done
|
||||
by Wi-Fi Alliance.
|
||||
|
||||
IEEE 802.11 standard defined wired equivalent privacy (WEP) algorithm
|
||||
for protecting wireless networks. WEP uses RC4 with 40-bit keys,
|
||||
24-bit initialization vector (IV), and CRC32 to protect against packet
|
||||
forgery. All these choices have proven to be insufficient: key space is
|
||||
too small against current attacks, RC4 key scheduling is insufficient
|
||||
(beginning of the pseudorandom stream should be skipped), IV space is
|
||||
too small and IV reuse makes attacks easier, there is no replay
|
||||
protection, and non-keyed authentication does not protect against bit
|
||||
flipping packet data.
|
||||
|
||||
WPA is an intermediate solution for the security issues. It uses
|
||||
Temporal Key Integrity Protocol (TKIP) to replace WEP. TKIP is a
|
||||
compromise on strong security and possibility to use existing
|
||||
hardware. It still uses RC4 for the encryption like WEP, but with
|
||||
per-packet RC4 keys. In addition, it implements replay protection,
|
||||
keyed packet authentication mechanism (Michael MIC).
|
||||
|
||||
Keys can be managed using two different mechanisms. WPA can either use
|
||||
an external authentication server (e.g., RADIUS) and EAP just like
|
||||
IEEE 802.1X is using or pre-shared keys without need for additional
|
||||
servers. Wi-Fi calls these "WPA-Enterprise" and "WPA-Personal",
|
||||
respectively. Both mechanisms will generate a master session key for
|
||||
the Authenticator (AP) and Supplicant (client station).
|
||||
|
||||
WPA implements a new key handshake (4-Way Handshake and Group Key
|
||||
Handshake) for generating and exchanging data encryption keys between
|
||||
the Authenticator and Supplicant. This handshake is also used to
|
||||
verify that both Authenticator and Supplicant know the master session
|
||||
key. These handshakes are identical regardless of the selected key
|
||||
management mechanism (only the method for generating master session
|
||||
key changes).
|
||||
|
||||
|
||||
IEEE 802.11i / WPA2
|
||||
-------------------
|
||||
|
||||
The design for parts of IEEE 802.11i that were not included in WPA has
|
||||
finished (May 2004) and this amendment to IEEE 802.11 was approved in
|
||||
June 2004. Wi-Fi Alliance is using the final IEEE 802.11i as a new
|
||||
version of WPA called WPA2. This includes, e.g., support for more
|
||||
robust encryption algorithm (CCMP: AES in Counter mode with CBC-MAC)
|
||||
to replace TKIP and optimizations for handoff (reduced number of
|
||||
messages in initial key handshake, pre-authentication, and PMKSA caching).
|
||||
|
||||
Some wireless LAN vendors are already providing support for CCMP in
|
||||
their WPA products. There is no "official" interoperability
|
||||
certification for CCMP and/or mixed modes using both TKIP and CCMP, so
|
||||
some interoperability issues can be expected even though many
|
||||
combinations seem to be working with equipment from different vendors.
|
||||
Testing for WPA2 is likely to start during the second half of 2004.
|
||||
|
||||
hostapd configuration for WPA/WPA2
|
||||
----------------------------------
|
||||
|
||||
TODO
|
||||
|
||||
# Enable WPA. Setting this variable configures the AP to require WPA (either
|
||||
# WPA-PSK or WPA-RADIUS/EAP based on other configuration). For WPA-PSK, either
|
||||
# wpa_psk or wpa_passphrase must be set and wpa_key_mgmt must include WPA-PSK.
|
||||
# For WPA-RADIUS/EAP, ieee8021x must be set (but without dynamic WEP keys),
|
||||
# RADIUS authentication server must be configured, and WPA-EAP must be included
|
||||
# in wpa_key_mgmt.
|
||||
# This field is a bit field that can be used to enable WPA (IEEE 802.11i/D3.0)
|
||||
# and/or WPA2 (full IEEE 802.11i/RSN):
|
||||
# bit0 = WPA
|
||||
# bit1 = IEEE 802.11i/RSN (WPA2)
|
||||
#wpa=1
|
||||
|
||||
# WPA pre-shared keys for WPA-PSK. This can be either entered as a 256-bit
|
||||
# secret in hex format (64 hex digits), wpa_psk, or as an ASCII passphrase
|
||||
# (8..63 characters) that will be converted to PSK. This conversion uses SSID
|
||||
# so the PSK changes when ASCII passphrase is used and the SSID is changed.
|
||||
#wpa_psk=0123456789abcdef0123456789abcdef0123456789abcdef0123456789abcdef
|
||||
#wpa_passphrase=secret passphrase
|
||||
|
||||
# Set of accepted key management algorithms (WPA-PSK, WPA-EAP, or both). The
|
||||
# entries are separated with a space.
|
||||
#wpa_key_mgmt=WPA-PSK WPA-EAP
|
||||
|
||||
# Set of accepted cipher suites (encryption algorithms) for pairwise keys
|
||||
# (unicast packets). This is a space separated list of algorithms:
|
||||
# CCMP = AES in Counter mode with CBC-MAC [RFC 3610, IEEE 802.11i]
|
||||
# TKIP = Temporal Key Integrity Protocol [IEEE 802.11i]
|
||||
# Group cipher suite (encryption algorithm for broadcast and multicast frames)
|
||||
# is automatically selected based on this configuration. If only CCMP is
|
||||
# allowed as the pairwise cipher, group cipher will also be CCMP. Otherwise,
|
||||
# TKIP will be used as the group cipher.
|
||||
#wpa_pairwise=TKIP CCMP
|
||||
|
||||
# Time interval for rekeying GTK (broadcast/multicast encryption keys) in
|
||||
# seconds.
|
||||
#wpa_group_rekey=600
|
||||
|
||||
# Time interval for rekeying GMK (master key used internally to generate GTKs
|
||||
# (in seconds).
|
||||
#wpa_gmk_rekey=86400
|
||||
|
||||
# Enable IEEE 802.11i/RSN/WPA2 pre-authentication. This is used to speed up
|
||||
# roaming be pre-authenticating IEEE 802.1X/EAP part of the full RSN
|
||||
# authentication and key handshake before actually associating with a new AP.
|
||||
#rsn_preauth=1
|
||||
#
|
||||
# Space separated list of interfaces from which pre-authentication frames are
|
||||
# accepted (e.g., 'eth0' or 'eth0 wlan0wds0'. This list should include all
|
||||
# interface that are used for connections to other APs. This could include
|
||||
# wired interfaces and WDS links. The normal wireless data interface towards
|
||||
# associated stations (e.g., wlan0) should not be added, since
|
||||
# pre-authentication is only used with APs other than the currently associated
|
||||
# one.
|
||||
#rsn_preauth_interfaces=eth0
|
|
@ -0,0 +1,160 @@
|
|||
hostapd, wpa_supplicant and the Multi-AP Specification
|
||||
======================================================
|
||||
|
||||
This document describes how hostapd and wpa_supplicant can be configured to
|
||||
support the Multi-AP Specification.
|
||||
|
||||
Introduction to Multi-AP
|
||||
------------------------
|
||||
|
||||
The Wi-Fi Alliance Multi-AP Specification is the technical specification for
|
||||
Wi-Fi CERTIFIED EasyMesh(TM) [1], the Wi-Fi Alliance® certification program for
|
||||
Multi-AP. It defines control protocols between Wi-Fi® access points (APs) to
|
||||
join them into a network with centralized control and operation. It is targeted
|
||||
only at routers (repeaters, gateways, ...), not at clients. Clients are not
|
||||
involved at all in the protocols.
|
||||
|
||||
Most of the Multi-AP specification falls outside of the scope of
|
||||
hostapd/wpa_supplicant. hostapd/wpa_supplicant is only involved for the items
|
||||
summarized below. The rest of the protocol must be implemented by a separate
|
||||
daemon, e.g., prplMesh [2]. That daemon also needs to communicate with hostapd,
|
||||
e.g., to get a list of associated clients, but this can be done using the normal
|
||||
hostapd interfaces.
|
||||
|
||||
hostapd/wpa_supplicant needs to be configured specifically to support:
|
||||
- the WPS onboarding process;
|
||||
- configuring backhaul links.
|
||||
|
||||
The text below refers to "Multi-AP Specification v1.0" [3].
|
||||
|
||||
|
||||
Fronthaul and backhaul links
|
||||
----------------------------
|
||||
|
||||
In a Multi-AP network, the central controller can configure the BSSs on the
|
||||
devices that are joined into the network. These are called fronthaul BSSs.
|
||||
From the point of view of hostapd, there is nothing special about these
|
||||
fronthaul BSSs.
|
||||
|
||||
In addition to fronthaul BSSs, the controller can also configure backhaul
|
||||
links. A backhaul link is a link between two access point devices, giving
|
||||
internet access to access point devices that don't have a wired link. The
|
||||
Multi-AP specification doesn't dictate this, but typically the backhaul link
|
||||
will be bridged into a LAN together with (one of) the fronthaul BSS(s) and the
|
||||
wired Ethernet ports.
|
||||
|
||||
A backhaul link must be treated specially by hostapd and wpa_supplicant. One
|
||||
side of the backhaul link is configured through the Multi-AP protocol as the
|
||||
"backhaul STA", i.e., the client side of the link. A backhaul STA is like any
|
||||
station and is handled appropriately by wpa_supplicant, but two additional
|
||||
features are required. It must send an additional information element in each
|
||||
(Re)Association Request frame ([3], section 5.2, paragraph 4). In addition, it
|
||||
must use 4-address mode for all frames sent over this link ([3], section 14).
|
||||
Therefore, wpa_supplicant must be configured explicitly as the backhaul STA
|
||||
role, by setting 'multi_ap_backhaul_sta=1' in the network configuration block
|
||||
or when configuring the network profile through the control interface. When
|
||||
'multi_ap_backhaul_sta=1', wpa_supplicant includes the Multi-AP IE in
|
||||
(Re)Association Request frame and verifies that it is included in the
|
||||
(Re)Association Response frame. If it is not, association fails. If it is,
|
||||
wpa_supplicant sets 4-address mode for this interface through a driver
|
||||
callback.
|
||||
|
||||
The AP side of the backhaul link is called a "backhaul BSS". Such a BSS must
|
||||
be handled specially by hostapd, because it must add an additional information
|
||||
element in each (Re)Association Response frame, but only to stations that have
|
||||
identified themselves as backhaul stations ([3], section 5.2, paragraph 5-6).
|
||||
This is important because it is possible to use the same BSS and SSID for
|
||||
fronthaul and backhaul at the same time. The additional information element must
|
||||
only be used for frames sent to a backhaul STA, not to a normal STA. Also,
|
||||
frames sent to a backhaul STA must use 4-address mode, while frames sent to a
|
||||
normal STA (fronthaul, when it's a fronthaul and backhaul BSS) must use
|
||||
3-address mode.
|
||||
|
||||
A BSS is configured in Multi-AP mode in hostapd by setting the 'multi_ap'
|
||||
configuration option to 1 (backhaul BSS), 2 (fronthaul BSS), or 3
|
||||
(simultaneous backhaul and fronthaul BSS). If this option is set, hostapd
|
||||
parses the Multi-AP information element in the Association Request frame. If the
|
||||
station is a backhaul STA and the BSS is configured as a backhaul BSS,
|
||||
hostapd sets up 4-address mode. Since there may be multiple stations connected
|
||||
simultaneously, and each of them has a different RA (receiver address), a VLAN
|
||||
is created for each backhaul STA and it is automatically added to a bridge.
|
||||
This is the same behavior as for WDS, and the relevant option ('bridge' or
|
||||
'wds_bridge') applies here as well.
|
||||
|
||||
If 'multi_ap' is 1 (backhaul BSS only), any station that tries to associate
|
||||
without the Multi-AP information element will be denied.
|
||||
|
||||
If 'multi_ap' is 2 (fronthaul BSS only), any station that tries to associate
|
||||
with the Multi-AP information element will be denied. That is also the only
|
||||
difference with 'multi_ap' set to 0: in the latter case, the Multi-AP
|
||||
information element is simply ignored.
|
||||
|
||||
In summary, this is the end-to-end behavior for a backhaul BSS (i.e.,
|
||||
multi_ap_backhaul_sta=1 in wpa_supplicant on STA, and multi_ap=1 or 3 in
|
||||
hostapd on AP). Note that point 1 means that hostapd must not be configured
|
||||
with WPS support on the backhaul BSS (multi_ap=1). hostapd does not check for
|
||||
that.
|
||||
|
||||
1. Backhaul BSS beacons do not advertise WPS support (other than that, nothing
|
||||
Multi-AP specific).
|
||||
2. STA sends Authentication frame (nothing Multi-AP specific).
|
||||
3. AP sends Authentication frame (nothing Multi-AP specific).
|
||||
4. STA sends Association Request frame with Multi-AP IE.
|
||||
5. AP sends Association Response frame with Multi-AP IE.
|
||||
6. STA and AP both use 4-address mode for Data frames.
|
||||
|
||||
|
||||
WPS support
|
||||
-----------
|
||||
|
||||
WPS requires more special handling. WPS must only be advertised on fronthaul
|
||||
BSSs, not on backhaul BSSs, so WPS should not be enabled on a backhaul-only
|
||||
BSS in hostapd.conf. The WPS configuration purely works on the fronthaul BSS.
|
||||
When a WPS M1 message has an additional subelement that indicates a request for
|
||||
a Multi-AP backhaul link, hostapd must not respond with the normal fronthaul
|
||||
BSS credentials; instead, it should respond with the (potentially different)
|
||||
backhaul BSS credentials.
|
||||
|
||||
To support this, hostapd has the 'multi_ap_backhaul_ssid',
|
||||
'multi_ap_backhaul_wpa_psk' and 'multi_ap_backhaul_wpa_passphrase' options.
|
||||
When these are set on an BSS with WPS, they are used instead of the normal
|
||||
credentials when hostapd receives a WPS M1 message with the Multi-AP IE. Only
|
||||
WPA2-Personal is supported in the Multi-AP specification, so there is no need
|
||||
to specify authentication or encryption options. For the backhaul credentials,
|
||||
per-device PSK is not supported.
|
||||
|
||||
If the BSS is a simultaneous backhaul and fronthaul BSS, there is no need to
|
||||
specify the backhaul credentials, since the backhaul and fronthaul credentials
|
||||
are identical.
|
||||
|
||||
To enable the Multi-AP backhaul STA feature when it performs WPS, a new
|
||||
parameter has been introduced to the WPS_PBC control interface call. When this
|
||||
"multi_ap=1" option is set, it adds the Multi-AP backhaul subelement to the
|
||||
Association Request frame and the M1 message. It then configures the new network
|
||||
profile with 'multi_ap_backhaul_sta=1'. Note that this means that if the AP does
|
||||
not follow the Multi-AP specification, wpa_supplicant will fail to associate.
|
||||
|
||||
In summary, this is the end-to-end behavior for WPS of a backhaul link (i.e.,
|
||||
multi_ap=1 option is given in the wps_pbc call on the STA side, and multi_ap=2
|
||||
and multi_ap_backhaul_ssid and either multi_ap_backhaul_wpa_psk or
|
||||
multi_ap_backhaul_wpa_passphrase are set to the credentials of a backhaul BSS
|
||||
in hostapd on Registrar AP).
|
||||
|
||||
1. Fronthaul BSS Beacon frames advertise WPS support (nothing Multi-AP
|
||||
specific).
|
||||
2. Enrollee sends Authentication frame (nothing Multi-AP specific).
|
||||
3. AP sends Authentication frame (nothing Multi-AP specific).
|
||||
4. Enrollee sends Association Request frame with Multi-AP IE.
|
||||
5. AP sends Association Response frame with Multi-AP IE.
|
||||
6. Enrollee sends M1 with additional Multi-AP subelement.
|
||||
7. AP sends M8 with backhaul instead of fronthaul credentials.
|
||||
8. Enrollee sends Deauthentication frame.
|
||||
|
||||
|
||||
References
|
||||
----------
|
||||
|
||||
[1] https://www.wi-fi.org/discover-wi-fi/wi-fi-easymesh
|
||||
[2] https://github.com/prplfoundation/prplMesh
|
||||
[3] https://www.wi-fi.org/file/multi-ap-specification-v10
|
||||
(requires registration)
|
|
@ -0,0 +1,352 @@
|
|||
hostapd and Wi-Fi Protected Setup (WPS)
|
||||
=======================================
|
||||
|
||||
This document describes how the WPS implementation in hostapd can be
|
||||
configured and how an external component on an AP (e.g., web UI) is
|
||||
used to enable enrollment of client devices.
|
||||
|
||||
|
||||
Introduction to WPS
|
||||
-------------------
|
||||
|
||||
Wi-Fi Protected Setup (WPS) is a mechanism for easy configuration of a
|
||||
wireless network. It allows automated generation of random keys (WPA
|
||||
passphrase/PSK) and configuration of an access point and client
|
||||
devices. WPS includes number of methods for setting up connections
|
||||
with PIN method and push-button configuration (PBC) being the most
|
||||
commonly deployed options.
|
||||
|
||||
While WPS can enable more home networks to use encryption in the
|
||||
wireless network, it should be noted that the use of the PIN and
|
||||
especially PBC mechanisms for authenticating the initial key setup is
|
||||
not very secure. As such, use of WPS may not be suitable for
|
||||
environments that require secure network access without chance for
|
||||
allowing outsiders to gain access during the setup phase.
|
||||
|
||||
WPS uses following terms to describe the entities participating in the
|
||||
network setup:
|
||||
- access point: the WLAN access point
|
||||
- Registrar: a device that control a network and can authorize
|
||||
addition of new devices); this may be either in the AP ("internal
|
||||
Registrar") or in an external device, e.g., a laptop, ("external
|
||||
Registrar")
|
||||
- Enrollee: a device that is being authorized to use the network
|
||||
|
||||
It should also be noted that the AP and a client device may change
|
||||
roles (i.e., AP acts as an Enrollee and client device as a Registrar)
|
||||
when WPS is used to configure the access point.
|
||||
|
||||
|
||||
More information about WPS is available from Wi-Fi Alliance:
|
||||
http://www.wi-fi.org/wifi-protected-setup
|
||||
|
||||
|
||||
hostapd implementation
|
||||
----------------------
|
||||
|
||||
hostapd includes an optional WPS component that can be used as an
|
||||
internal WPS Registrar to manage addition of new WPS enabled clients
|
||||
to the network. In addition, WPS Enrollee functionality in hostapd can
|
||||
be used to allow external WPS Registrars to configure the access
|
||||
point, e.g., for initial network setup. In addition, hostapd can proxy a
|
||||
WPS registration between a wireless Enrollee and an external Registrar
|
||||
(e.g., Microsoft Vista or Atheros JumpStart) with UPnP.
|
||||
|
||||
|
||||
hostapd configuration
|
||||
---------------------
|
||||
|
||||
WPS is an optional component that needs to be enabled in hostapd build
|
||||
configuration (.config). Here is an example configuration that
|
||||
includes WPS support and uses nl80211 driver interface:
|
||||
|
||||
CONFIG_DRIVER_NL80211=y
|
||||
CONFIG_WPS=y
|
||||
CONFIG_WPS_UPNP=y
|
||||
|
||||
Following parameter can be used to enable support for NFC config method:
|
||||
|
||||
CONFIG_WPS_NFC=y
|
||||
|
||||
|
||||
Following section shows an example runtime configuration
|
||||
(hostapd.conf) that enables WPS:
|
||||
|
||||
# Configure the driver and network interface
|
||||
driver=nl80211
|
||||
interface=wlan0
|
||||
|
||||
# WPA2-Personal configuration for the AP
|
||||
ssid=wps-test
|
||||
wpa=2
|
||||
wpa_key_mgmt=WPA-PSK
|
||||
wpa_pairwise=CCMP
|
||||
# Default WPA passphrase for legacy (non-WPS) clients
|
||||
wpa_passphrase=12345678
|
||||
# Enable random per-device PSK generation for WPS clients
|
||||
# Please note that the file has to exists for hostapd to start (i.e., create an
|
||||
# empty file as a starting point).
|
||||
wpa_psk_file=/etc/hostapd.psk
|
||||
|
||||
# Enable control interface for PBC/PIN entry
|
||||
ctrl_interface=/var/run/hostapd
|
||||
|
||||
# Enable internal EAP server for EAP-WSC (part of Wi-Fi Protected Setup)
|
||||
eap_server=1
|
||||
|
||||
# WPS configuration (AP configured, do not allow external WPS Registrars)
|
||||
wps_state=2
|
||||
ap_setup_locked=1
|
||||
# If UUID is not configured, it will be generated based on local MAC address.
|
||||
uuid=87654321-9abc-def0-1234-56789abc0000
|
||||
wps_pin_requests=/var/run/hostapd.pin-req
|
||||
device_name=Wireless AP
|
||||
manufacturer=Company
|
||||
model_name=WAP
|
||||
model_number=123
|
||||
serial_number=12345
|
||||
device_type=6-0050F204-1
|
||||
os_version=01020300
|
||||
config_methods=label display push_button keypad
|
||||
|
||||
# if external Registrars are allowed, UPnP support could be added:
|
||||
#upnp_iface=br0
|
||||
#friendly_name=WPS Access Point
|
||||
|
||||
|
||||
External operations
|
||||
-------------------
|
||||
|
||||
WPS requires either a device PIN code (usually, 8-digit number) or a
|
||||
pushbutton event (for PBC) to allow a new WPS Enrollee to join the
|
||||
network. hostapd uses the control interface as an input channel for
|
||||
these events.
|
||||
|
||||
The PIN value used in the commands must be processed by an UI to
|
||||
remove non-digit characters and potentially, to verify the checksum
|
||||
digit. "hostapd_cli wps_check_pin <PIN>" can be used to do such
|
||||
processing. It returns FAIL if the PIN is invalid, or FAIL-CHECKSUM if
|
||||
the checksum digit is incorrect, or the processed PIN (non-digit
|
||||
characters removed) if the PIN is valid.
|
||||
|
||||
When a client device (WPS Enrollee) connects to hostapd (WPS
|
||||
Registrar) in order to start PIN mode negotiation for WPS, an
|
||||
identifier (Enrollee UUID) is sent. hostapd will need to be configured
|
||||
with a device password (PIN) for this Enrollee. This is an operation
|
||||
that requires user interaction (assuming there are no pre-configured
|
||||
PINs on the AP for a set of Enrollee).
|
||||
|
||||
The PIN request with information about the device is appended to the
|
||||
wps_pin_requests file (/var/run/hostapd.pin-req in this example). In
|
||||
addition, hostapd control interface event is sent as a notification of
|
||||
a new device. The AP could use, e.g., a web UI for showing active
|
||||
Enrollees to the user and request a PIN for an Enrollee.
|
||||
|
||||
The PIN request file has one line for every Enrollee that connected to
|
||||
the AP, but for which there was no PIN. Following information is
|
||||
provided for each Enrollee (separated with tabulators):
|
||||
- timestamp (seconds from 1970-01-01)
|
||||
- Enrollee UUID
|
||||
- MAC address
|
||||
- Device name
|
||||
- Manufacturer
|
||||
- Model Name
|
||||
- Model Number
|
||||
- Serial Number
|
||||
- Device category
|
||||
|
||||
Example line in the /var/run/hostapd.pin-req file:
|
||||
1200188391 53b63a98-d29e-4457-a2ed-094d7e6a669c Intel(R) Centrino(R) Intel Corporation Intel(R) Centrino(R) - - 1-0050F204-1
|
||||
|
||||
Control interface data:
|
||||
WPS-PIN-NEEDED [UUID-E|MAC Address|Device Name|Manufacturer|Model Name|Model Number|Serial Number|Device Category]
|
||||
For example:
|
||||
<2>WPS-PIN-NEEDED [53b63a98-d29e-4457-a2ed-094d7e6a669c|02:12:34:56:78:9a|Device|Manuf|Model|Model Number|Serial Number|1-0050F204-1]
|
||||
|
||||
When the user enters a PIN for a pending Enrollee, e.g., on the web
|
||||
UI), hostapd needs to be notified of the new PIN over the control
|
||||
interface. This can be done either by using the UNIX domain socket
|
||||
-based control interface directly (src/common/wpa_ctrl.c provides
|
||||
helper functions for using the interface) or by calling hostapd_cli.
|
||||
|
||||
Example command to add a PIN (12345670) for an Enrollee:
|
||||
|
||||
hostapd_cli wps_pin 53b63a98-d29e-4457-a2ed-094d7e6a669c 12345670
|
||||
|
||||
If the UUID-E is not available (e.g., Enrollee waits for the Registrar
|
||||
to be selected before connecting), wildcard UUID may be used to allow
|
||||
the PIN to be used once with any UUID:
|
||||
|
||||
hostapd_cli wps_pin any 12345670
|
||||
|
||||
To reduce likelihood of PIN being used with other devices or of
|
||||
forgetting an active PIN available for potential attackers, expiration
|
||||
time in seconds can be set for the new PIN (value 0 indicates no
|
||||
expiration):
|
||||
|
||||
hostapd_cli wps_pin any 12345670 300
|
||||
|
||||
If the MAC address of the enrollee is known, it should be configured
|
||||
to allow the AP to advertise list of authorized enrollees:
|
||||
|
||||
hostapd_cli wps_pin 53b63a98-d29e-4457-a2ed-094d7e6a669c \
|
||||
12345670 300 00:11:22:33:44:55
|
||||
|
||||
|
||||
After this, the Enrollee can connect to the AP again and complete WPS
|
||||
negotiation. At that point, a new, random WPA PSK is generated for the
|
||||
client device and the client can then use that key to connect to the
|
||||
AP to access the network.
|
||||
|
||||
|
||||
If the AP includes a pushbutton, WPS PBC mode can be used. It is
|
||||
enabled by pushing a button on both the AP and the client at about the
|
||||
same time (2 minute window). hostapd needs to be notified about the AP
|
||||
button pushed event over the control interface, e.g., by calling
|
||||
hostapd_cli:
|
||||
|
||||
hostapd_cli wps_pbc
|
||||
|
||||
At this point, the client has two minutes to complete WPS negotiation
|
||||
which will generate a new WPA PSK in the same way as the PIN method
|
||||
described above.
|
||||
|
||||
|
||||
When an external Registrar is used, the AP can act as an Enrollee and
|
||||
use its AP PIN. A static AP PIN (e.g., one one a label in the AP
|
||||
device) can be configured in hostapd.conf (ap_pin parameter). A more
|
||||
secure option is to use hostapd_cli wps_ap_pin command to enable the
|
||||
AP PIN only based on user action (and even better security by using a
|
||||
random AP PIN for each session, i.e., by using "wps_ap_pin random"
|
||||
command with a timeout value). Following commands are available for
|
||||
managing the dynamic AP PIN operations:
|
||||
|
||||
hostapd_cli wps_ap_pin disable
|
||||
- disable AP PIN (i.e., do not allow external Registrars to use it to
|
||||
learn the current AP settings or to reconfigure the AP)
|
||||
|
||||
hostapd_cli wps_ap_pin random [timeout]
|
||||
- generate a random AP PIN and enable it
|
||||
- if the optional timeout parameter is given, the AP PIN will be enabled
|
||||
for the specified number of seconds
|
||||
|
||||
hostapd_cli wps_ap_pin get
|
||||
- fetch the current AP PIN
|
||||
|
||||
hostapd_cli wps_ap_pin set <PIN> [timeout]
|
||||
- set the AP PIN and enable it
|
||||
- if the optional timeout parameter is given, the AP PIN will be enabled
|
||||
for the specified number of seconds
|
||||
|
||||
hostapd_cli get_config
|
||||
- display the current configuration
|
||||
|
||||
hostapd_cli wps_config <new SSID> <auth> <encr> <new key>
|
||||
examples:
|
||||
hostapd_cli wps_config testing WPA2PSK CCMP 12345678
|
||||
hostapd_cli wps_config "no security" OPEN NONE ""
|
||||
|
||||
<auth> must be one of the following: OPEN WPAPSK WPA2PSK
|
||||
<encr> must be one of the following: NONE WEP TKIP CCMP
|
||||
|
||||
|
||||
Credential generation and configuration changes
|
||||
-----------------------------------------------
|
||||
|
||||
By default, hostapd generates credentials for Enrollees and processing
|
||||
AP configuration updates internally. However, it is possible to
|
||||
control these operations from external programs, if desired.
|
||||
|
||||
The internal credential generation can be disabled with
|
||||
skip_cred_build=1 option in the configuration. extra_cred option will
|
||||
then need to be used to provide pre-configured Credential attribute(s)
|
||||
for hostapd to use. The exact data from this binary file will be sent,
|
||||
i.e., it will have to include valid WPS attributes. extra_cred can
|
||||
also be used to add additional networks if the Registrar is used to
|
||||
configure credentials for multiple networks.
|
||||
|
||||
Processing of received configuration updates can be disabled with
|
||||
wps_cred_processing=1 option. When this is used, an external program
|
||||
is responsible for creating hostapd configuration files and processing
|
||||
configuration updates based on messages received from hostapd over
|
||||
control interface. This will also include the initial configuration on
|
||||
first successful registration if the AP is initially set in
|
||||
unconfigured state.
|
||||
|
||||
Following control interface messages are sent out for external programs:
|
||||
|
||||
WPS-REG-SUCCESS <Enrollee MAC address <UUID-E>
|
||||
For example:
|
||||
<2>WPS-REG-SUCCESS 02:66:a0:ee:17:27 2b7093f1-d6fb-5108-adbb-bea66bb87333
|
||||
|
||||
This can be used to trigger change from unconfigured to configured
|
||||
state (random configuration based on the first successful WPS
|
||||
registration). In addition, this can be used to update AP UI about the
|
||||
status of WPS registration progress.
|
||||
|
||||
|
||||
WPS-NEW-AP-SETTINGS <hexdump of AP Setup attributes>
|
||||
For example:
|
||||
<2>WPS-NEW-AP-SETTINGS 10260001011045000c6a6b6d2d7770732d74657374100300020020100f00020008102700403065346230343536633236366665306433396164313535346131663462663731323433376163666462376633393965353466316631623032306164343438623510200006024231cede15101e000844
|
||||
|
||||
This can be used to update the externally stored AP configuration and
|
||||
then update hostapd configuration (followed by restarting of hostapd).
|
||||
|
||||
|
||||
WPS with NFC
|
||||
------------
|
||||
|
||||
WPS can be used with NFC-based configuration method. An NFC tag
|
||||
containing a password token from the Enrollee can be used to
|
||||
authenticate the connection instead of the PIN. In addition, an NFC tag
|
||||
with a configuration token can be used to transfer AP settings without
|
||||
going through the WPS protocol.
|
||||
|
||||
When the AP acts as an Enrollee, a local NFC tag with a password token
|
||||
can be used by touching the NFC interface of an external Registrar. The
|
||||
wps_nfc_token command is used to manage use of the NFC password token
|
||||
from the AP. "wps_nfc_token enable" enables the use of the AP's NFC
|
||||
password token (in place of AP PIN) and "wps_nfc_token disable" disables
|
||||
the NFC password token.
|
||||
|
||||
The NFC password token that is either pre-configured in the
|
||||
configuration file (wps_nfc_dev_pw_id, wps_nfc_dh_pubkey,
|
||||
wps_nfc_dh_privkey, wps_nfc_dev_pw) or generated dynamically with
|
||||
"wps_nfc_token <WPS|NDEF>" command. The nfc_pw_token tool from
|
||||
wpa_supplicant can be used to generate NFC password tokens during
|
||||
manufacturing (each AP needs to have its own random keys).
|
||||
|
||||
The "wps_nfc_config_token <WPS/NDEF>" command can be used to build an
|
||||
NFC configuration token. The output value from this command is a hexdump
|
||||
of the current AP configuration (WPS parameter requests this to include
|
||||
only the WPS attributes; NDEF parameter requests additional NDEF
|
||||
encapsulation to be included). This data needs to be written to an NFC
|
||||
tag with an external program. Once written, the NFC configuration token
|
||||
can be used to touch an NFC interface on a station to provision the
|
||||
credentials needed to access the network.
|
||||
|
||||
When the NFC device on the AP reads an NFC tag with a MIME media type
|
||||
"application/vnd.wfa.wsc", the NDEF message payload (with or without
|
||||
NDEF encapsulation) can be delivered to hostapd using the
|
||||
following hostapd_cli command:
|
||||
|
||||
wps_nfc_tag_read <hexdump of payload>
|
||||
|
||||
If the NFC tag contains a password token, the token is added to the
|
||||
internal Registrar. This allows station Enrollee from which the password
|
||||
token was received to run through WPS protocol to provision the
|
||||
credential.
|
||||
|
||||
"nfc_get_handover_sel <NDEF> <WPS>" command can be used to build the
|
||||
contents of a Handover Select Message for connection handover when this
|
||||
does not depend on the contents of the Handover Request Message. The
|
||||
first argument selects the format of the output data and the second
|
||||
argument selects which type of connection handover is requested (WPS =
|
||||
Wi-Fi handover as specified in WSC 2.0).
|
||||
|
||||
"nfc_report_handover <INIT/RESP> WPS <carrier from handover request>
|
||||
<carrier from handover select>" is used to report completed NFC
|
||||
connection handover. The first parameter indicates whether the local
|
||||
device initiated or responded to the connection handover and the carrier
|
||||
records are the selected carrier from the handover request and select
|
||||
messages as a hexdump.
|
|
@ -0,0 +1,215 @@
|
|||
# Example hostapd build time configuration
|
||||
#
|
||||
# This file lists the configuration options that are used when building the
|
||||
# hostapd binary. All lines starting with # are ignored. Configuration option
|
||||
# lines must be commented out complete, if they are not to be included, i.e.,
|
||||
# just setting VARIABLE=n is not disabling that variable.
|
||||
#
|
||||
# This file is included in Makefile, so variables like CFLAGS and LIBS can also
|
||||
# be modified from here. In most cass, these lines should use += in order not
|
||||
# to override previous values of the variables.
|
||||
|
||||
# Driver interface for Host AP driver
|
||||
#CONFIG_DRIVER_HOSTAP=y
|
||||
|
||||
# Driver interface for wired authenticator
|
||||
#CONFIG_DRIVER_WIRED=y
|
||||
|
||||
# Driver interface for drivers using the nl80211 kernel interface
|
||||
#CONFIG_DRIVER_NL80211=y
|
||||
# driver_nl80211.c requires a rather new libnl (version 1.1) which may not be
|
||||
# shipped with your distribution yet. If that is the case, you need to build
|
||||
# newer libnl version and point the hostapd build to use it.
|
||||
#LIBNL=/usr/src/libnl
|
||||
#CFLAGS += -I$(LIBNL)/include
|
||||
#LIBS += -L$(LIBNL)/lib
|
||||
CONFIG_LIBNL20=y
|
||||
|
||||
# QCA vendor extensions to nl80211
|
||||
CONFIG_DRIVER_NL80211_QCA=y
|
||||
|
||||
# Driver interface for FreeBSD net80211 layer (e.g., Atheros driver)
|
||||
#CONFIG_DRIVER_BSD=y
|
||||
#CFLAGS += -I/usr/local/include
|
||||
#LIBS += -L/usr/local/lib
|
||||
#LIBS_p += -L/usr/local/lib
|
||||
#LIBS_c += -L/usr/local/lib
|
||||
|
||||
# Driver interface for no driver (e.g., RADIUS server only)
|
||||
#CONFIG_DRIVER_NONE=y
|
||||
|
||||
# IEEE 802.11F/IAPP
|
||||
#CONFIG_IAPP=y
|
||||
|
||||
# WPA2/IEEE 802.11i RSN pre-authentication
|
||||
#CONFIG_RSN_PREAUTH=y
|
||||
|
||||
# IEEE 802.11w (management frame protection)
|
||||
# This version is an experimental implementation based on IEEE 802.11w/D1.0
|
||||
# draft and is subject to change since the standard has not yet been finalized.
|
||||
# Driver support is also needed for IEEE 802.11w.
|
||||
CONFIG_IEEE80211W=y
|
||||
|
||||
# Support Operating Channel Validation
|
||||
#CONFIG_OCV=y
|
||||
|
||||
# Integrated EAP server
|
||||
#CONFIG_EAP=y
|
||||
|
||||
# EAP-MD5 for the integrated EAP server
|
||||
#CONFIG_EAP_MD5=y
|
||||
|
||||
# EAP-TLS for the integrated EAP server
|
||||
#CONFIG_EAP_TLS=y
|
||||
|
||||
# EAP-MSCHAPv2 for the integrated EAP server
|
||||
#CONFIG_EAP_MSCHAPV2=y
|
||||
|
||||
# EAP-PEAP for the integrated EAP server
|
||||
#CONFIG_EAP_PEAP=y
|
||||
|
||||
# EAP-GTC for the integrated EAP server
|
||||
#CONFIG_EAP_GTC=y
|
||||
|
||||
# EAP-TTLS for the integrated EAP server
|
||||
#CONFIG_EAP_TTLS=y
|
||||
|
||||
# EAP-SIM for the integrated EAP server
|
||||
#CONFIG_EAP_SIM=y
|
||||
|
||||
# EAP-AKA for the integrated EAP server
|
||||
#CONFIG_EAP_AKA=y
|
||||
|
||||
# EAP-AKA' for the integrated EAP server
|
||||
# This requires CONFIG_EAP_AKA to be enabled, too.
|
||||
#CONFIG_EAP_AKA_PRIME=y
|
||||
|
||||
# EAP-PAX for the integrated EAP server
|
||||
#CONFIG_EAP_PAX=y
|
||||
|
||||
# EAP-PSK for the integrated EAP server (this is _not_ needed for WPA-PSK)
|
||||
#CONFIG_EAP_PSK=y
|
||||
|
||||
# EAP-SAKE for the integrated EAP server
|
||||
#CONFIG_EAP_SAKE=y
|
||||
|
||||
# EAP-GPSK for the integrated EAP server
|
||||
#CONFIG_EAP_GPSK=y
|
||||
# Include support for optional SHA256 cipher suite in EAP-GPSK
|
||||
#CONFIG_EAP_GPSK_SHA256=y
|
||||
|
||||
# EAP-FAST for the integrated EAP server
|
||||
# Note: Default OpenSSL package does not include support for all the
|
||||
# functionality needed for EAP-FAST. If EAP-FAST is enabled with OpenSSL,
|
||||
# the OpenSSL library must be patched (openssl-0.9.9-session-ticket.patch)
|
||||
# to add the needed functions.
|
||||
#CONFIG_EAP_FAST=y
|
||||
|
||||
# Wi-Fi Protected Setup (WPS)
|
||||
CONFIG_WPS=y
|
||||
# Enable UPnP support for external WPS Registrars
|
||||
#CONFIG_WPS_UPNP=y
|
||||
|
||||
# EAP-IKEv2
|
||||
#CONFIG_EAP_IKEV2=y
|
||||
|
||||
# Trusted Network Connect (EAP-TNC)
|
||||
#CONFIG_EAP_TNC=y
|
||||
|
||||
# PKCS#12 (PFX) support (used to read private key and certificate file from
|
||||
# a file that usually has extension .p12 or .pfx)
|
||||
CONFIG_PKCS12=y
|
||||
|
||||
# RADIUS authentication server. This provides access to the integrated EAP
|
||||
# server from external hosts using RADIUS.
|
||||
#CONFIG_RADIUS_SERVER=y
|
||||
|
||||
# Build IPv6 support for RADIUS operations
|
||||
CONFIG_IPV6=y
|
||||
|
||||
# IEEE Std 802.11r-2008 (Fast BSS Transition)
|
||||
#CONFIG_IEEE80211R=y
|
||||
|
||||
# Use the hostapd's IEEE 802.11 authentication (ACL), but without
|
||||
# the IEEE 802.11 Management capability (e.g., FreeBSD/net80211)
|
||||
#CONFIG_DRIVER_RADIUS_ACL=y
|
||||
|
||||
# IEEE 802.11n (High Throughput) support
|
||||
CONFIG_IEEE80211N=y
|
||||
|
||||
# Remove debugging code that is printing out debug messages to stdout.
|
||||
# This can be used to reduce the size of the hostapd considerably if debugging
|
||||
# code is not needed.
|
||||
#CONFIG_NO_STDOUT_DEBUG=y
|
||||
|
||||
# Add support for writing debug log to Android logcat instead of standard output
|
||||
CONFIG_ANDROID_LOG=y
|
||||
|
||||
# Remove support for RADIUS accounting
|
||||
#CONFIG_NO_ACCOUNTING=y
|
||||
|
||||
# Remove support for RADIUS
|
||||
CONFIG_NO_RADIUS=y
|
||||
|
||||
# Remove support for VLANs
|
||||
#CONFIG_NO_VLAN=y
|
||||
|
||||
# Remove support for dumping internal state through control interface commands
|
||||
# This can be used to reduce binary size at the cost of disabling a debugging
|
||||
# option.
|
||||
#CONFIG_NO_DUMP_STATE=y
|
||||
|
||||
# Select wrapper for operatins system and C library specific functions
|
||||
# unix = UNIX/POSIX like systems (default)
|
||||
# win32 = Windows systems
|
||||
# none = Empty template
|
||||
CONFIG_OS=unix
|
||||
|
||||
# Enable tracing code for developer debugging
|
||||
# This tracks use of memory allocations and other registrations and reports
|
||||
# incorrect use with a backtrace of call (or allocation) location.
|
||||
#CONFIG_WPA_TRACE=y
|
||||
# For BSD, comment out these.
|
||||
#LIBS += -lexecinfo
|
||||
#LIBS_p += -lexecinfo
|
||||
#LIBS_c += -lexecinfo
|
||||
|
||||
# Use libbfd to get more details for developer debugging
|
||||
# This enables use of libbfd to get more detailed symbols for the backtraces
|
||||
# generated by CONFIG_WPA_TRACE=y.
|
||||
#CONFIG_WPA_TRACE_BFD=y
|
||||
# For BSD, comment out these.
|
||||
#LIBS += -lbfd -liberty -lz
|
||||
#LIBS_p += -lbfd -liberty -lz
|
||||
#LIBS_c += -lbfd -liberty -lz
|
||||
|
||||
# Should we use poll instead of select? Select is used by default.
|
||||
#CONFIG_ELOOP_POLL=y
|
||||
|
||||
# Should we use epoll instead of select? Select is used by default.
|
||||
#CONFIG_ELOOP_EPOLL=y
|
||||
|
||||
# Enable AP
|
||||
CONFIG_AP=y
|
||||
|
||||
# Enable Fast Session Transfer (FST)
|
||||
#CONFIG_FST=y
|
||||
|
||||
# Multiband Operation support
|
||||
# These extentions facilitate efficient use of multiple frequency bands
|
||||
# available to the AP and the devices that may associate with it.
|
||||
#CONFIG_MBO=y
|
||||
|
||||
# Include internal line edit mode in hostapd_cli.
|
||||
CONFIG_WPA_CLI_EDIT=y
|
||||
|
||||
# Opportunistic Wireless Encryption (OWE)
|
||||
# Experimental implementation of draft-harkins-owe-07.txt
|
||||
#CONFIG_OWE=y
|
||||
|
||||
# Wpa_supplicant's random pool is not necessary on Android. Randomness is
|
||||
# already provided by the entropymixer service which ensures sufficient
|
||||
# entropy is maintained across reboots. Commit b410eb1913 'Initialize
|
||||
# /dev/urandom earlier in boot' seeds /dev/urandom with that entropy before
|
||||
# either wpa_supplicant or hostapd are run.
|
||||
CONFIG_NO_RANDOM_POOL=y
|
File diff suppressed because it is too large
Load Diff
|
@ -0,0 +1,22 @@
|
|||
/*
|
||||
* hostapd / Configuration file parser
|
||||
* Copyright (c) 2003-2009, Jouni Malinen <j@w1.fi>
|
||||
*
|
||||
* This software may be distributed under the terms of the BSD license.
|
||||
* See README for more details.
|
||||
*/
|
||||
|
||||
#ifndef CONFIG_FILE_H
|
||||
#define CONFIG_FILE_H
|
||||
|
||||
struct hostapd_config * hostapd_config_read(const char *fname);
|
||||
int hostapd_set_iface(struct hostapd_config *conf,
|
||||
struct hostapd_bss_config *bss, const char *field,
|
||||
char *value);
|
||||
int hostapd_acl_comp(const void *a, const void *b);
|
||||
int hostapd_add_acl_maclist(struct mac_acl_entry **acl, int *num,
|
||||
int vlan_id, const u8 *addr);
|
||||
void hostapd_remove_acl_mac(struct mac_acl_entry **acl, int *num,
|
||||
const u8 *addr);
|
||||
|
||||
#endif /* CONFIG_FILE_H */
|
File diff suppressed because it is too large
Load Diff
|
@ -0,0 +1,39 @@
|
|||
/*
|
||||
* hostapd / UNIX domain socket -based control interface
|
||||
* Copyright (c) 2004, Jouni Malinen <j@w1.fi>
|
||||
*
|
||||
* This software may be distributed under the terms of the BSD license.
|
||||
* See README for more details.
|
||||
*/
|
||||
|
||||
#ifndef CTRL_IFACE_H
|
||||
#define CTRL_IFACE_H
|
||||
|
||||
#ifndef CONFIG_NO_CTRL_IFACE
|
||||
int hostapd_ctrl_iface_init(struct hostapd_data *hapd);
|
||||
void hostapd_ctrl_iface_deinit(struct hostapd_data *hapd);
|
||||
int hostapd_global_ctrl_iface_init(struct hapd_interfaces *interface);
|
||||
void hostapd_global_ctrl_iface_deinit(struct hapd_interfaces *interface);
|
||||
#else /* CONFIG_NO_CTRL_IFACE */
|
||||
static inline int hostapd_ctrl_iface_init(struct hostapd_data *hapd)
|
||||
{
|
||||
return 0;
|
||||
}
|
||||
|
||||
static inline void hostapd_ctrl_iface_deinit(struct hostapd_data *hapd)
|
||||
{
|
||||
}
|
||||
|
||||
static inline int
|
||||
hostapd_global_ctrl_iface_init(struct hapd_interfaces *interface)
|
||||
{
|
||||
return 0;
|
||||
}
|
||||
|
||||
static inline void
|
||||
hostapd_global_ctrl_iface_deinit(struct hapd_interfaces *interface)
|
||||
{
|
||||
}
|
||||
#endif /* CONFIG_NO_CTRL_IFACE */
|
||||
|
||||
#endif /* CTRL_IFACE_H */
|
|
@ -0,0 +1,391 @@
|
|||
# Example hostapd build time configuration
|
||||
#
|
||||
# This file lists the configuration options that are used when building the
|
||||
# hostapd binary. All lines starting with # are ignored. Configuration option
|
||||
# lines must be commented out complete, if they are not to be included, i.e.,
|
||||
# just setting VARIABLE=n is not disabling that variable.
|
||||
#
|
||||
# This file is included in Makefile, so variables like CFLAGS and LIBS can also
|
||||
# be modified from here. In most cass, these lines should use += in order not
|
||||
# to override previous values of the variables.
|
||||
|
||||
# Driver interface for Host AP driver
|
||||
CONFIG_DRIVER_HOSTAP=y
|
||||
|
||||
# Driver interface for wired authenticator
|
||||
#CONFIG_DRIVER_WIRED=y
|
||||
|
||||
# Driver interface for drivers using the nl80211 kernel interface
|
||||
CONFIG_DRIVER_NL80211=y
|
||||
|
||||
# QCA vendor extensions to nl80211
|
||||
#CONFIG_DRIVER_NL80211_QCA=y
|
||||
|
||||
# driver_nl80211.c requires libnl. If you are compiling it yourself
|
||||
# you may need to point hostapd to your version of libnl.
|
||||
#
|
||||
#CFLAGS += -I$<path to libnl include files>
|
||||
#LIBS += -L$<path to libnl library files>
|
||||
|
||||
# Use libnl v2.0 (or 3.0) libraries.
|
||||
#CONFIG_LIBNL20=y
|
||||
|
||||
# Use libnl 3.2 libraries (if this is selected, CONFIG_LIBNL20 is ignored)
|
||||
CONFIG_LIBNL32=y
|
||||
|
||||
|
||||
# Driver interface for FreeBSD net80211 layer (e.g., Atheros driver)
|
||||
#CONFIG_DRIVER_BSD=y
|
||||
#CFLAGS += -I/usr/local/include
|
||||
#LIBS += -L/usr/local/lib
|
||||
#LIBS_p += -L/usr/local/lib
|
||||
#LIBS_c += -L/usr/local/lib
|
||||
|
||||
# Driver interface for no driver (e.g., RADIUS server only)
|
||||
#CONFIG_DRIVER_NONE=y
|
||||
|
||||
# IEEE 802.11F/IAPP
|
||||
CONFIG_IAPP=y
|
||||
|
||||
# WPA2/IEEE 802.11i RSN pre-authentication
|
||||
CONFIG_RSN_PREAUTH=y
|
||||
|
||||
# IEEE 802.11w (management frame protection)
|
||||
CONFIG_IEEE80211W=y
|
||||
|
||||
# Support Operating Channel Validation
|
||||
#CONFIG_OCV=y
|
||||
|
||||
# Integrated EAP server
|
||||
CONFIG_EAP=y
|
||||
|
||||
# EAP Re-authentication Protocol (ERP) in integrated EAP server
|
||||
CONFIG_ERP=y
|
||||
|
||||
# EAP-MD5 for the integrated EAP server
|
||||
CONFIG_EAP_MD5=y
|
||||
|
||||
# EAP-TLS for the integrated EAP server
|
||||
CONFIG_EAP_TLS=y
|
||||
|
||||
# EAP-MSCHAPv2 for the integrated EAP server
|
||||
CONFIG_EAP_MSCHAPV2=y
|
||||
|
||||
# EAP-PEAP for the integrated EAP server
|
||||
CONFIG_EAP_PEAP=y
|
||||
|
||||
# EAP-GTC for the integrated EAP server
|
||||
CONFIG_EAP_GTC=y
|
||||
|
||||
# EAP-TTLS for the integrated EAP server
|
||||
CONFIG_EAP_TTLS=y
|
||||
|
||||
# EAP-SIM for the integrated EAP server
|
||||
#CONFIG_EAP_SIM=y
|
||||
|
||||
# EAP-AKA for the integrated EAP server
|
||||
#CONFIG_EAP_AKA=y
|
||||
|
||||
# EAP-AKA' for the integrated EAP server
|
||||
# This requires CONFIG_EAP_AKA to be enabled, too.
|
||||
#CONFIG_EAP_AKA_PRIME=y
|
||||
|
||||
# EAP-PAX for the integrated EAP server
|
||||
#CONFIG_EAP_PAX=y
|
||||
|
||||
# EAP-PSK for the integrated EAP server (this is _not_ needed for WPA-PSK)
|
||||
#CONFIG_EAP_PSK=y
|
||||
|
||||
# EAP-pwd for the integrated EAP server (secure authentication with a password)
|
||||
#CONFIG_EAP_PWD=y
|
||||
|
||||
# EAP-SAKE for the integrated EAP server
|
||||
#CONFIG_EAP_SAKE=y
|
||||
|
||||
# EAP-GPSK for the integrated EAP server
|
||||
#CONFIG_EAP_GPSK=y
|
||||
# Include support for optional SHA256 cipher suite in EAP-GPSK
|
||||
#CONFIG_EAP_GPSK_SHA256=y
|
||||
|
||||
# EAP-FAST for the integrated EAP server
|
||||
#CONFIG_EAP_FAST=y
|
||||
|
||||
# EAP-TEAP for the integrated EAP server
|
||||
# Note: The current EAP-TEAP implementation is experimental and should not be
|
||||
# enabled for production use. The IETF RFC 7170 that defines EAP-TEAP has number
|
||||
# of conflicting statements and missing details and the implementation has
|
||||
# vendor specific workarounds for those and as such, may not interoperate with
|
||||
# any other implementation. This should not be used for anything else than
|
||||
# experimentation and interoperability testing until those issues has been
|
||||
# resolved.
|
||||
#CONFIG_EAP_TEAP=y
|
||||
|
||||
# Wi-Fi Protected Setup (WPS)
|
||||
#CONFIG_WPS=y
|
||||
# Enable UPnP support for external WPS Registrars
|
||||
#CONFIG_WPS_UPNP=y
|
||||
# Enable WPS support with NFC config method
|
||||
#CONFIG_WPS_NFC=y
|
||||
|
||||
# EAP-IKEv2
|
||||
#CONFIG_EAP_IKEV2=y
|
||||
|
||||
# Trusted Network Connect (EAP-TNC)
|
||||
#CONFIG_EAP_TNC=y
|
||||
|
||||
# EAP-EKE for the integrated EAP server
|
||||
#CONFIG_EAP_EKE=y
|
||||
|
||||
# PKCS#12 (PFX) support (used to read private key and certificate file from
|
||||
# a file that usually has extension .p12 or .pfx)
|
||||
CONFIG_PKCS12=y
|
||||
|
||||
# RADIUS authentication server. This provides access to the integrated EAP
|
||||
# server from external hosts using RADIUS.
|
||||
#CONFIG_RADIUS_SERVER=y
|
||||
|
||||
# Build IPv6 support for RADIUS operations
|
||||
CONFIG_IPV6=y
|
||||
|
||||
# IEEE Std 802.11r-2008 (Fast BSS Transition)
|
||||
#CONFIG_IEEE80211R=y
|
||||
|
||||
# Use the hostapd's IEEE 802.11 authentication (ACL), but without
|
||||
# the IEEE 802.11 Management capability (e.g., FreeBSD/net80211)
|
||||
#CONFIG_DRIVER_RADIUS_ACL=y
|
||||
|
||||
# IEEE 802.11n (High Throughput) support
|
||||
#CONFIG_IEEE80211N=y
|
||||
|
||||
# Wireless Network Management (IEEE Std 802.11v-2011)
|
||||
# Note: This is experimental and not complete implementation.
|
||||
#CONFIG_WNM=y
|
||||
|
||||
# IEEE 802.11ac (Very High Throughput) support
|
||||
#CONFIG_IEEE80211AC=y
|
||||
|
||||
# IEEE 802.11ax HE support
|
||||
# Note: This is experimental and work in progress. The definitions are still
|
||||
# subject to change and this should not be expected to interoperate with the
|
||||
# final IEEE 802.11ax version.
|
||||
#CONFIG_IEEE80211AX=y
|
||||
|
||||
# Remove debugging code that is printing out debug messages to stdout.
|
||||
# This can be used to reduce the size of the hostapd considerably if debugging
|
||||
# code is not needed.
|
||||
#CONFIG_NO_STDOUT_DEBUG=y
|
||||
|
||||
# Add support for writing debug log to a file: -f /tmp/hostapd.log
|
||||
# Disabled by default.
|
||||
#CONFIG_DEBUG_FILE=y
|
||||
|
||||
# Send debug messages to syslog instead of stdout
|
||||
#CONFIG_DEBUG_SYSLOG=y
|
||||
|
||||
# Add support for sending all debug messages (regardless of debug verbosity)
|
||||
# to the Linux kernel tracing facility. This helps debug the entire stack by
|
||||
# making it easy to record everything happening from the driver up into the
|
||||
# same file, e.g., using trace-cmd.
|
||||
#CONFIG_DEBUG_LINUX_TRACING=y
|
||||
|
||||
# Remove support for RADIUS accounting
|
||||
#CONFIG_NO_ACCOUNTING=y
|
||||
|
||||
# Remove support for RADIUS
|
||||
#CONFIG_NO_RADIUS=y
|
||||
|
||||
# Remove support for VLANs
|
||||
#CONFIG_NO_VLAN=y
|
||||
|
||||
# Enable support for fully dynamic VLANs. This enables hostapd to
|
||||
# automatically create bridge and VLAN interfaces if necessary.
|
||||
#CONFIG_FULL_DYNAMIC_VLAN=y
|
||||
|
||||
# Use netlink-based kernel API for VLAN operations instead of ioctl()
|
||||
# Note: This requires libnl 3.1 or newer.
|
||||
#CONFIG_VLAN_NETLINK=y
|
||||
|
||||
# Remove support for dumping internal state through control interface commands
|
||||
# This can be used to reduce binary size at the cost of disabling a debugging
|
||||
# option.
|
||||
#CONFIG_NO_DUMP_STATE=y
|
||||
|
||||
# Enable tracing code for developer debugging
|
||||
# This tracks use of memory allocations and other registrations and reports
|
||||
# incorrect use with a backtrace of call (or allocation) location.
|
||||
#CONFIG_WPA_TRACE=y
|
||||
# For BSD, comment out these.
|
||||
#LIBS += -lexecinfo
|
||||
#LIBS_p += -lexecinfo
|
||||
#LIBS_c += -lexecinfo
|
||||
|
||||
# Use libbfd to get more details for developer debugging
|
||||
# This enables use of libbfd to get more detailed symbols for the backtraces
|
||||
# generated by CONFIG_WPA_TRACE=y.
|
||||
#CONFIG_WPA_TRACE_BFD=y
|
||||
# For BSD, comment out these.
|
||||
#LIBS += -lbfd -liberty -lz
|
||||
#LIBS_p += -lbfd -liberty -lz
|
||||
#LIBS_c += -lbfd -liberty -lz
|
||||
|
||||
# hostapd depends on strong random number generation being available from the
|
||||
# operating system. os_get_random() function is used to fetch random data when
|
||||
# needed, e.g., for key generation. On Linux and BSD systems, this works by
|
||||
# reading /dev/urandom. It should be noted that the OS entropy pool needs to be
|
||||
# properly initialized before hostapd is started. This is important especially
|
||||
# on embedded devices that do not have a hardware random number generator and
|
||||
# may by default start up with minimal entropy available for random number
|
||||
# generation.
|
||||
#
|
||||
# As a safety net, hostapd is by default trying to internally collect
|
||||
# additional entropy for generating random data to mix in with the data
|
||||
# fetched from the OS. This by itself is not considered to be very strong, but
|
||||
# it may help in cases where the system pool is not initialized properly.
|
||||
# However, it is very strongly recommended that the system pool is initialized
|
||||
# with enough entropy either by using hardware assisted random number
|
||||
# generator or by storing state over device reboots.
|
||||
#
|
||||
# hostapd can be configured to maintain its own entropy store over restarts to
|
||||
# enhance random number generation. This is not perfect, but it is much more
|
||||
# secure than using the same sequence of random numbers after every reboot.
|
||||
# This can be enabled with -e<entropy file> command line option. The specified
|
||||
# file needs to be readable and writable by hostapd.
|
||||
#
|
||||
# If the os_get_random() is known to provide strong random data (e.g., on
|
||||
# Linux/BSD, the board in question is known to have reliable source of random
|
||||
# data from /dev/urandom), the internal hostapd random pool can be disabled.
|
||||
# This will save some in binary size and CPU use. However, this should only be
|
||||
# considered for builds that are known to be used on devices that meet the
|
||||
# requirements described above.
|
||||
#CONFIG_NO_RANDOM_POOL=y
|
||||
|
||||
# Should we attempt to use the getrandom(2) call that provides more reliable
|
||||
# yet secure randomness source than /dev/random on Linux 3.17 and newer.
|
||||
# Requires glibc 2.25 to build, falls back to /dev/random if unavailable.
|
||||
#CONFIG_GETRANDOM=y
|
||||
|
||||
# Should we use poll instead of select? Select is used by default.
|
||||
#CONFIG_ELOOP_POLL=y
|
||||
|
||||
# Should we use epoll instead of select? Select is used by default.
|
||||
#CONFIG_ELOOP_EPOLL=y
|
||||
|
||||
# Should we use kqueue instead of select? Select is used by default.
|
||||
#CONFIG_ELOOP_KQUEUE=y
|
||||
|
||||
# Select TLS implementation
|
||||
# openssl = OpenSSL (default)
|
||||
# gnutls = GnuTLS
|
||||
# internal = Internal TLSv1 implementation (experimental)
|
||||
# linux = Linux kernel AF_ALG and internal TLSv1 implementation (experimental)
|
||||
# none = Empty template
|
||||
#CONFIG_TLS=openssl
|
||||
|
||||
# TLS-based EAP methods require at least TLS v1.0. Newer version of TLS (v1.1)
|
||||
# can be enabled to get a stronger construction of messages when block ciphers
|
||||
# are used.
|
||||
#CONFIG_TLSV11=y
|
||||
|
||||
# TLS-based EAP methods require at least TLS v1.0. Newer version of TLS (v1.2)
|
||||
# can be enabled to enable use of stronger crypto algorithms.
|
||||
#CONFIG_TLSV12=y
|
||||
|
||||
# Select which ciphers to use by default with OpenSSL if the user does not
|
||||
# specify them.
|
||||
#CONFIG_TLS_DEFAULT_CIPHERS="DEFAULT:!EXP:!LOW"
|
||||
|
||||
# If CONFIG_TLS=internal is used, additional library and include paths are
|
||||
# needed for LibTomMath. Alternatively, an integrated, minimal version of
|
||||
# LibTomMath can be used. See beginning of libtommath.c for details on benefits
|
||||
# and drawbacks of this option.
|
||||
#CONFIG_INTERNAL_LIBTOMMATH=y
|
||||
#ifndef CONFIG_INTERNAL_LIBTOMMATH
|
||||
#LTM_PATH=/usr/src/libtommath-0.39
|
||||
#CFLAGS += -I$(LTM_PATH)
|
||||
#LIBS += -L$(LTM_PATH)
|
||||
#LIBS_p += -L$(LTM_PATH)
|
||||
#endif
|
||||
# At the cost of about 4 kB of additional binary size, the internal LibTomMath
|
||||
# can be configured to include faster routines for exptmod, sqr, and div to
|
||||
# speed up DH and RSA calculation considerably
|
||||
#CONFIG_INTERNAL_LIBTOMMATH_FAST=y
|
||||
|
||||
# Interworking (IEEE 802.11u)
|
||||
# This can be used to enable functionality to improve interworking with
|
||||
# external networks.
|
||||
#CONFIG_INTERWORKING=y
|
||||
|
||||
# Hotspot 2.0
|
||||
#CONFIG_HS20=y
|
||||
|
||||
# Enable SQLite database support in hlr_auc_gw, EAP-SIM DB, and eap_user_file
|
||||
#CONFIG_SQLITE=y
|
||||
|
||||
# Enable Fast Session Transfer (FST)
|
||||
#CONFIG_FST=y
|
||||
|
||||
# Enable CLI commands for FST testing
|
||||
#CONFIG_FST_TEST=y
|
||||
|
||||
# Testing options
|
||||
# This can be used to enable some testing options (see also the example
|
||||
# configuration file) that are really useful only for testing clients that
|
||||
# connect to this hostapd. These options allow, for example, to drop a
|
||||
# certain percentage of probe requests or auth/(re)assoc frames.
|
||||
#
|
||||
#CONFIG_TESTING_OPTIONS=y
|
||||
|
||||
# Automatic Channel Selection
|
||||
# This will allow hostapd to pick the channel automatically when channel is set
|
||||
# to "acs_survey" or "0". Eventually, other ACS algorithms can be added in
|
||||
# similar way.
|
||||
#
|
||||
# Automatic selection is currently only done through initialization, later on
|
||||
# we hope to do background checks to keep us moving to more ideal channels as
|
||||
# time goes by. ACS is currently only supported through the nl80211 driver and
|
||||
# your driver must have survey dump capability that is filled by the driver
|
||||
# during scanning.
|
||||
#
|
||||
# You can customize the ACS survey algorithm with the hostapd.conf variable
|
||||
# acs_num_scans.
|
||||
#
|
||||
# Supported ACS drivers:
|
||||
# * ath9k
|
||||
# * ath5k
|
||||
# * ath10k
|
||||
#
|
||||
# For more details refer to:
|
||||
# http://wireless.kernel.org/en/users/Documentation/acs
|
||||
#
|
||||
#CONFIG_ACS=y
|
||||
|
||||
# Multiband Operation support
|
||||
# These extentions facilitate efficient use of multiple frequency bands
|
||||
# available to the AP and the devices that may associate with it.
|
||||
#CONFIG_MBO=y
|
||||
|
||||
# Client Taxonomy
|
||||
# Has the AP retain the Probe Request and (Re)Association Request frames from
|
||||
# a client, from which a signature can be produced which can identify the model
|
||||
# of client device like "Nexus 6P" or "iPhone 5s".
|
||||
#CONFIG_TAXONOMY=y
|
||||
|
||||
# Fast Initial Link Setup (FILS) (IEEE 802.11ai)
|
||||
#CONFIG_FILS=y
|
||||
# FILS shared key authentication with PFS
|
||||
#CONFIG_FILS_SK_PFS=y
|
||||
|
||||
# Include internal line edit mode in hostapd_cli. This can be used to provide
|
||||
# limited command line editing and history support.
|
||||
#CONFIG_WPA_CLI_EDIT=y
|
||||
|
||||
# Opportunistic Wireless Encryption (OWE)
|
||||
# Experimental implementation of draft-harkins-owe-07.txt
|
||||
#CONFIG_OWE=y
|
||||
|
||||
# Airtime policy support
|
||||
#CONFIG_AIRTIME_POLICY=y
|
||||
|
||||
# Override default value for the wpa_disable_eapol_key_retries configuration
|
||||
# parameter. See that parameter in hostapd.conf for more details.
|
||||
#CFLAGS += -DDEFAULT_WPA_DISABLE_EAPOL_KEY_RETRIES=1
|
|
@ -0,0 +1,155 @@
|
|||
/*
|
||||
* EAP method registration
|
||||
* Copyright (c) 2004-2009, Jouni Malinen <j@w1.fi>
|
||||
*
|
||||
* This software may be distributed under the terms of the BSD license.
|
||||
* See README for more details.
|
||||
*/
|
||||
|
||||
#include "includes.h"
|
||||
|
||||
#include "common.h"
|
||||
#include "eap_server/eap_methods.h"
|
||||
#include "eap_register.h"
|
||||
|
||||
|
||||
/**
|
||||
* eap_server_register_methods - Register statically linked EAP server methods
|
||||
* Returns: 0 on success, -1 or -2 on failure
|
||||
*
|
||||
* This function is called at program initialization to register all EAP
|
||||
* methods that were linked in statically.
|
||||
*/
|
||||
int eap_server_register_methods(void)
|
||||
{
|
||||
int ret = 0;
|
||||
|
||||
#ifdef EAP_SERVER_IDENTITY
|
||||
if (ret == 0)
|
||||
ret = eap_server_identity_register();
|
||||
#endif /* EAP_SERVER_IDENTITY */
|
||||
|
||||
#ifdef EAP_SERVER_MD5
|
||||
if (ret == 0)
|
||||
ret = eap_server_md5_register();
|
||||
#endif /* EAP_SERVER_MD5 */
|
||||
|
||||
#ifdef EAP_SERVER_TLS
|
||||
if (ret == 0)
|
||||
ret = eap_server_tls_register();
|
||||
#endif /* EAP_SERVER_TLS */
|
||||
|
||||
#ifdef EAP_SERVER_UNAUTH_TLS
|
||||
if (ret == 0)
|
||||
ret = eap_server_unauth_tls_register();
|
||||
#endif /* EAP_SERVER_TLS */
|
||||
|
||||
#ifdef EAP_SERVER_TLS
|
||||
#ifdef CONFIG_HS20
|
||||
if (ret == 0)
|
||||
ret = eap_server_wfa_unauth_tls_register();
|
||||
#endif /* CONFIG_HS20 */
|
||||
#endif /* EAP_SERVER_TLS */
|
||||
|
||||
#ifdef EAP_SERVER_MSCHAPV2
|
||||
if (ret == 0)
|
||||
ret = eap_server_mschapv2_register();
|
||||
#endif /* EAP_SERVER_MSCHAPV2 */
|
||||
|
||||
#ifdef EAP_SERVER_PEAP
|
||||
if (ret == 0)
|
||||
ret = eap_server_peap_register();
|
||||
#endif /* EAP_SERVER_PEAP */
|
||||
|
||||
#ifdef EAP_SERVER_TLV
|
||||
if (ret == 0)
|
||||
ret = eap_server_tlv_register();
|
||||
#endif /* EAP_SERVER_TLV */
|
||||
|
||||
#ifdef EAP_SERVER_GTC
|
||||
if (ret == 0)
|
||||
ret = eap_server_gtc_register();
|
||||
#endif /* EAP_SERVER_GTC */
|
||||
|
||||
#ifdef EAP_SERVER_TTLS
|
||||
if (ret == 0)
|
||||
ret = eap_server_ttls_register();
|
||||
#endif /* EAP_SERVER_TTLS */
|
||||
|
||||
#ifdef EAP_SERVER_SIM
|
||||
if (ret == 0)
|
||||
ret = eap_server_sim_register();
|
||||
#endif /* EAP_SERVER_SIM */
|
||||
|
||||
#ifdef EAP_SERVER_AKA
|
||||
if (ret == 0)
|
||||
ret = eap_server_aka_register();
|
||||
#endif /* EAP_SERVER_AKA */
|
||||
|
||||
#ifdef EAP_SERVER_AKA_PRIME
|
||||
if (ret == 0)
|
||||
ret = eap_server_aka_prime_register();
|
||||
#endif /* EAP_SERVER_AKA_PRIME */
|
||||
|
||||
#ifdef EAP_SERVER_PAX
|
||||
if (ret == 0)
|
||||
ret = eap_server_pax_register();
|
||||
#endif /* EAP_SERVER_PAX */
|
||||
|
||||
#ifdef EAP_SERVER_PSK
|
||||
if (ret == 0)
|
||||
ret = eap_server_psk_register();
|
||||
#endif /* EAP_SERVER_PSK */
|
||||
|
||||
#ifdef EAP_SERVER_SAKE
|
||||
if (ret == 0)
|
||||
ret = eap_server_sake_register();
|
||||
#endif /* EAP_SERVER_SAKE */
|
||||
|
||||
#ifdef EAP_SERVER_GPSK
|
||||
if (ret == 0)
|
||||
ret = eap_server_gpsk_register();
|
||||
#endif /* EAP_SERVER_GPSK */
|
||||
|
||||
#ifdef EAP_SERVER_VENDOR_TEST
|
||||
if (ret == 0)
|
||||
ret = eap_server_vendor_test_register();
|
||||
#endif /* EAP_SERVER_VENDOR_TEST */
|
||||
|
||||
#ifdef EAP_SERVER_FAST
|
||||
if (ret == 0)
|
||||
ret = eap_server_fast_register();
|
||||
#endif /* EAP_SERVER_FAST */
|
||||
|
||||
#ifdef EAP_SERVER_TEAP
|
||||
if (ret == 0)
|
||||
ret = eap_server_teap_register();
|
||||
#endif /* EAP_SERVER_TEAP */
|
||||
|
||||
#ifdef EAP_SERVER_WSC
|
||||
if (ret == 0)
|
||||
ret = eap_server_wsc_register();
|
||||
#endif /* EAP_SERVER_WSC */
|
||||
|
||||
#ifdef EAP_SERVER_IKEV2
|
||||
if (ret == 0)
|
||||
ret = eap_server_ikev2_register();
|
||||
#endif /* EAP_SERVER_IKEV2 */
|
||||
|
||||
#ifdef EAP_SERVER_TNC
|
||||
if (ret == 0)
|
||||
ret = eap_server_tnc_register();
|
||||
#endif /* EAP_SERVER_TNC */
|
||||
|
||||
#ifdef EAP_SERVER_PWD
|
||||
if (ret == 0)
|
||||
ret = eap_server_pwd_register();
|
||||
#endif /* EAP_SERVER_PWD */
|
||||
|
||||
#ifdef EAP_SERVER_EKE
|
||||
if (ret == 0)
|
||||
ret = eap_server_eke_register();
|
||||
#endif /* EAP_SERVER_EKE */
|
||||
|
||||
return ret;
|
||||
}
|
|
@ -0,0 +1,14 @@
|
|||
/*
|
||||
* EAP method registration
|
||||
* Copyright (c) 2004-2009, Jouni Malinen <j@w1.fi>
|
||||
*
|
||||
* This software may be distributed under the terms of the BSD license.
|
||||
* See README for more details.
|
||||
*/
|
||||
|
||||
#ifndef EAP_REGISTER_H
|
||||
#define EAP_REGISTER_H
|
||||
|
||||
int eap_server_register_methods(void);
|
||||
|
||||
#endif /* EAP_REGISTER_H */
|
|
@ -0,0 +1,77 @@
|
|||
Interoperability testing of hostapd's IEEE 802.1X/EAPOL authentication
|
||||
|
||||
Test matrix
|
||||
|
||||
+) tested successfully
|
||||
F) failed
|
||||
-) peer did not support
|
||||
?) not tested
|
||||
|
||||
XSupplicant --------------------------------.
|
||||
Intel PROSet ---------------------------. |
|
||||
Windows XP -------------------------. | |
|
||||
Mac OS X 10.4 ------------------. | | |
|
||||
Nokia S60 ------------------. | | | |
|
||||
wpa_supplicant ---------. | | | | |
|
||||
| | | | | |
|
||||
|
||||
EAP-MD5 + - ? ? -
|
||||
EAP-GTC + - ? - -
|
||||
EAP-MSCHAPv2 + - ? - -
|
||||
EAP-TLS + + +1 + +
|
||||
EAP-PEAPv0/MSCHAPv2 + + + + + +
|
||||
EAP-PEAPv0/GTC + + + - +
|
||||
EAP-PEAPv0/MD5 + - + - -
|
||||
EAP-PEAPv0/TLS + F - + +
|
||||
EAP-PEAPv0/SIM + + - - -
|
||||
EAP-PEAPv0/AKA + + - - -
|
||||
EAP-PEAPv0/PSK + - - - -
|
||||
EAP-PEAPv0/PAX + - - - -
|
||||
EAP-PEAPv0/SAKE + - - - -
|
||||
EAP-PEAPv0/GPSK + - - - -
|
||||
EAP-PEAPv1/MSCHAPv2 + + + - + +
|
||||
EAP-PEAPv1/GTC + + + - +
|
||||
EAP-PEAPv1/MD5 + - + - -
|
||||
EAP-PEAPv1/TLS + F - - +
|
||||
EAP-PEAPv1/SIM + + - - -
|
||||
EAP-PEAPv1/AKA + + - - -
|
||||
EAP-PEAPv1/PSK + - - - -
|
||||
EAP-PEAPv1/PAX + - - - -
|
||||
EAP-PEAPv1/SAKE + - - - -
|
||||
EAP-PEAPv1/GPSK + - - - -
|
||||
EAP-TTLS/CHAP + - + - + +
|
||||
EAP-TTLS/MSCHAP + - + - + +
|
||||
EAP-TTLS/MSCHAPv2 + + + - + +
|
||||
EAP-TTLS/PAP + - + - + +
|
||||
EAP-TTLS/EAP-MD5 + - - - - +
|
||||
EAP-TTLS/EAP-GTC + + - - -
|
||||
EAP-TTLS/EAP-MSCHAPv2 + + - - -
|
||||
EAP-TTLS/EAP-TLS + F - - -
|
||||
EAP-TTLS/EAP-SIM + + - - -
|
||||
EAP-TTLS/EAP-AKA + + - - -
|
||||
EAP-TTLS + TNC + - - - -
|
||||
EAP-SIM + + - - +
|
||||
EAP-AKA + + - - -
|
||||
EAP-PAX + - - - -
|
||||
EAP-SAKE + - - - -
|
||||
EAP-GPSK + - - - -
|
||||
EAP-FAST/MSCHAPv2(prov) + - F - F
|
||||
EAP-FAST/GTC(auth) + - + - +
|
||||
EAP-FAST/MSCHAPv2(aprov)+ - F - F
|
||||
EAP-FAST/GTC(aprov) + - F - F
|
||||
EAP-FAST/MD5(aprov) + - - - -
|
||||
EAP-FAST/TLS(aprov) + - - - -
|
||||
EAP-FAST/SIM(aprov) + - - - -
|
||||
EAP-FAST/AKA(aprov) + - - - -
|
||||
EAP-FAST/MSCHAPv2(auth) + - + - +
|
||||
EAP-FAST/MD5(auth) + - + - -
|
||||
EAP-FAST/TLS(auth) + - - - -
|
||||
EAP-FAST/SIM(auth) + - - - -
|
||||
EAP-FAST/AKA(auth) + - - - -
|
||||
EAP-FAST + TNC + - - - -
|
||||
EAP-IKEv2 + - - - -
|
||||
EAP-TNC + - - - -
|
||||
|
||||
1) EAP-TLS itself worked, but peer certificate validation failed at
|
||||
least when using the internal TLS server (peer included incorrect
|
||||
certificates in the chain?)
|
|
@ -0,0 +1,18 @@
|
|||
/*
|
||||
* hostapd module tests
|
||||
* Copyright (c) 2014, Jouni Malinen <j@w1.fi>
|
||||
*
|
||||
* This software may be distributed under the terms of the BSD license.
|
||||
* See README for more details.
|
||||
*/
|
||||
|
||||
#include "utils/includes.h"
|
||||
|
||||
#include "utils/common.h"
|
||||
#include "utils/module_tests.h"
|
||||
|
||||
int hapd_module_tests(void)
|
||||
{
|
||||
wpa_printf(MSG_INFO, "hostapd module tests");
|
||||
return 0;
|
||||
}
|
File diff suppressed because it is too large
Load Diff
|
@ -0,0 +1,15 @@
|
|||
# Parameters for Milenage (Example algorithms for AKA).
|
||||
# The example Ki, OPc, and AMF values here are from 3GPP TS 35.208 v6.0.0
|
||||
# 4.3.20 Test Set 20. SQN is the last used SQN value.
|
||||
# These values can be used for both UMTS (EAP-AKA) and GSM (EAP-SIM)
|
||||
# authentication. In case of GSM/EAP-SIM, AMF and SQN values are not used, but
|
||||
# dummy values will need to be included in this file.
|
||||
|
||||
# IMSI Ki OPc AMF SQN [RES_len]
|
||||
232010000000000 90dca4eda45b53cf0f12d7c9c3bc6a89 cb9cccc4b9258e6dca4760379fb82581 61df 000000000000
|
||||
# Example using truncated 32-bit RES instead of 64-bit default
|
||||
232010000000001 90dca4eda45b53cf0f12d7c9c3bc6a89 cb9cccc4b9258e6dca4760379fb82581 61df 000000000000 4
|
||||
|
||||
# These values are from Test Set 19 which has the AMF separation bit set to 1
|
||||
# and as such, is suitable for EAP-AKA' test.
|
||||
555444333222111 5122250214c33e723a5dd523fc145fc0 981d464c7c52eb6e5036234984ad0bcf c3ab 16f3b3f70fc1
|
|
@ -0,0 +1,104 @@
|
|||
HLR/AuC testing gateway for hostapd EAP-SIM/AKA database/authenticator
|
||||
|
||||
hlr_auc_gw is an example implementation of the EAP-SIM/AKA/AKA'
|
||||
database/authentication gateway interface to HLR/AuC. It could be
|
||||
replaced with an implementation of SS7 gateway to GSM/UMTS
|
||||
authentication center (HLR/AuC). hostapd will send SIM/AKA
|
||||
authentication queries over a UNIX domain socket to and external
|
||||
program, e.g., hlr_auc_gw.
|
||||
|
||||
hlr_auc_gw can be configured with GSM and UMTS authentication data with
|
||||
text files: GSM triplet file (see hostapd.sim_db) and Milenage file (see
|
||||
hlr_auc_gw.milenage_db). Milenage parameters can be used to generate
|
||||
dynamic authentication data for EAP-SIM, EAP-AKA, and EAP-AKA' while the
|
||||
GSM triplet data is used for a more static configuration (e.g., triplets
|
||||
extracted from a SIM card).
|
||||
|
||||
Alternatively, hlr_auc_gw can be built with support for an SQLite
|
||||
database for more dynamic operations. This is enabled by adding
|
||||
"CONFIG_SQLITE=y" into hostapd/.config before building hlr_auc_gw ("make
|
||||
clean; make hlr_auc_gw" in this directory).
|
||||
|
||||
hostapd is configured to use hlr_auc_gw with the eap_sim_db parameter in
|
||||
hostapd.conf (e.g., "eap_sim_db=unix:/tmp/hlr_auc_gw.sock"). hlr_auc_gw
|
||||
is configured with command line parameters:
|
||||
|
||||
hlr_auc_gw [-hu] [-s<socket path>] [-g<triplet file>] [-m<milenage file>] \
|
||||
[-D<DB file>] [-i<IND len in bits>]
|
||||
|
||||
options:
|
||||
-h = show this usage help
|
||||
-u = update SQN in Milenage file on exit
|
||||
-s<socket path> = path for UNIX domain socket
|
||||
(default: /tmp/hlr_auc_gw.sock)
|
||||
-g<triplet file> = path for GSM authentication triplets
|
||||
-m<milenage file> = path for Milenage keys
|
||||
-D<DB file> = path to SQLite database
|
||||
-i<IND len in bits> = IND length for SQN (default: 5)
|
||||
|
||||
|
||||
The SQLite database can be initialized with sqlite, e.g., by running
|
||||
following commands in "sqlite3 /path/to/hlr_auc_gw.db":
|
||||
|
||||
CREATE TABLE milenage(
|
||||
imsi INTEGER PRIMARY KEY NOT NULL,
|
||||
ki CHAR(32) NOT NULL,
|
||||
opc CHAR(32) NOT NULL,
|
||||
amf CHAR(4) NOT NULL,
|
||||
sqn CHAR(12) NOT NULL
|
||||
);
|
||||
INSERT INTO milenage(imsi,ki,opc,amf,sqn) VALUES(
|
||||
232010000000000,
|
||||
'90dca4eda45b53cf0f12d7c9c3bc6a89',
|
||||
'cb9cccc4b9258e6dca4760379fb82581',
|
||||
'61df',
|
||||
'000000000000'
|
||||
);
|
||||
INSERT INTO milenage(imsi,ki,opc,amf,sqn) VALUES(
|
||||
555444333222111,
|
||||
'5122250214c33e723a5dd523fc145fc0',
|
||||
'981d464c7c52eb6e5036234984ad0bcf',
|
||||
'c3ab',
|
||||
'16f3b3f70fc1'
|
||||
);
|
||||
|
||||
|
||||
hostapd (EAP server) can also be configured to store the EAP-SIM/AKA
|
||||
pseudonyms and reauth information into a SQLite database. This is
|
||||
configured with the db parameter within the eap_sim_db configuration
|
||||
option.
|
||||
|
||||
|
||||
"hlr_auc_gw -D /path/to/hlr_auc_gw.db" can then be used to fetch
|
||||
Milenage parameters based on IMSI from the database. The database can be
|
||||
updated dynamically while hlr_auc_gw is running to add/remove/modify
|
||||
entries.
|
||||
|
||||
|
||||
Example configuration files for hostapd to operate as a RADIUS
|
||||
authentication server for EAP-SIM/AKA/AKA':
|
||||
|
||||
hostapd.conf:
|
||||
|
||||
driver=none
|
||||
radius_server_clients=hostapd.radius_clients
|
||||
eap_server=1
|
||||
eap_user_file=hostapd.eap_user
|
||||
eap_sim_db=unix:/tmp/hlr_auc_gw.sock db=/tmp/eap_sim.db
|
||||
eap_sim_aka_result_ind=1
|
||||
|
||||
hostapd.radius_clients:
|
||||
|
||||
0.0.0.0/0 radius
|
||||
|
||||
hostapd.eap_user:
|
||||
|
||||
"0"* AKA
|
||||
"1"* SIM
|
||||
"2"* AKA
|
||||
"3"* SIM
|
||||
"4"* AKA
|
||||
"5"* SIM
|
||||
"6"* AKA'
|
||||
"7"* AKA'
|
||||
"8"* AKA'
|
|
@ -0,0 +1,59 @@
|
|||
.TH HOSTAPD 8 "April 7, 2005" hostapd hostapd
|
||||
.SH NAME
|
||||
hostapd \- IEEE 802.11 AP, IEEE 802.1X/WPA/WPA2/EAP/RADIUS Authenticator
|
||||
.SH SYNOPSIS
|
||||
.B hostapd
|
||||
[\-hdBKtv] [\-P <PID file>] <configuration file(s)>
|
||||
.SH DESCRIPTION
|
||||
This manual page documents briefly the
|
||||
.B hostapd
|
||||
daemon.
|
||||
.PP
|
||||
.B hostapd
|
||||
is a user space daemon for access point and authentication servers.
|
||||
It implements IEEE 802.11 access point management, IEEE 802.1X/WPA/WPA2/EAP Authenticators and RADIUS authentication server.
|
||||
The current version supports Linux (Host AP, mac80211-based drivers) and FreeBSD (net80211).
|
||||
|
||||
.B hostapd
|
||||
is designed to be a "daemon" program that runs in the background and acts as the backend component controlling authentication.
|
||||
.B hostapd
|
||||
supports separate frontend programs and an example text-based frontend,
|
||||
.BR hostapd_cli ,
|
||||
is included with
|
||||
.BR hostapd .
|
||||
.SH OPTIONS
|
||||
A summary of options is included below.
|
||||
For a complete description, run
|
||||
.BR hostapd
|
||||
from the command line.
|
||||
.TP
|
||||
.B \-h
|
||||
Show usage.
|
||||
.TP
|
||||
.B \-d
|
||||
Show more debug messages.
|
||||
.TP
|
||||
.B \-dd
|
||||
Show even more debug messages.
|
||||
.TP
|
||||
.B \-B
|
||||
Run daemon in the background.
|
||||
.TP
|
||||
.B \-P <PID file>
|
||||
Path to PID file.
|
||||
.TP
|
||||
.B \-K
|
||||
Include key data in debug messages.
|
||||
.TP
|
||||
.B \-t
|
||||
Include timestamps in some debug messages.
|
||||
.TP
|
||||
.B \-v
|
||||
Show hostapd version.
|
||||
.SH SEE ALSO
|
||||
.BR hostapd_cli (1).
|
||||
.SH AUTHOR
|
||||
hostapd was written by Jouni Malinen <j@w1.fi>.
|
||||
.PP
|
||||
This manual page was written by Faidon Liambotis <faidon@cube.gr>,
|
||||
for the Debian project (but may be used by others).
|
|
@ -0,0 +1,6 @@
|
|||
# List of MAC addresses that are allowed to authenticate (IEEE 802.11)
|
||||
# with the AP. Optional VLAN ID can be assigned for clients based on the
|
||||
# MAC address if dynamic VLANs (hostapd.conf dynamic_vlan option) are used.
|
||||
00:11:22:33:44:55
|
||||
00:66:77:88:99:aa
|
||||
00:00:22:33:44:55 1
|
|
@ -0,0 +1,19 @@
|
|||
#
|
||||
# init.rc fragment for hostapd on Android
|
||||
# Copyright (c) 2002-2016, Jouni Malinen <j@w1.fi>
|
||||
#
|
||||
# This software may be distributed under the terms of the BSD license.
|
||||
# See README for more details.
|
||||
#
|
||||
|
||||
on post-fs-data
|
||||
mkdir /data/misc/wifi/hostapd 0770 wifi wifi
|
||||
|
||||
service hostapd /vendor/bin/hostapd \
|
||||
/data/misc/wifi/hostapd.conf
|
||||
class main
|
||||
user wifi
|
||||
writepid /data/misc/wifi/hostapd.pid
|
||||
group wifi
|
||||
disabled
|
||||
oneshot
|
File diff suppressed because it is too large
Load Diff
|
@ -0,0 +1,5 @@
|
|||
# List of MAC addresses that are not allowed to authenticate (IEEE 802.11)
|
||||
# with the AP.
|
||||
00:20:30:40:50:60
|
||||
00:ab:cd:ef:12:34
|
||||
00:00:30:40:50:60
|
|
@ -0,0 +1,103 @@
|
|||
# hostapd user database for integrated EAP server
|
||||
|
||||
# Each line must contain an identity, EAP method(s), and an optional password
|
||||
# separated with whitespace (space or tab). The identity and password must be
|
||||
# double quoted ("user"). Password can alternatively be stored as
|
||||
# NtPasswordHash (16-byte MD4 hash of the unicode presentation of the password
|
||||
# in unicode) if it is used for MSCHAP or MSCHAPv2 authentication. This means
|
||||
# that the plaintext password does not need to be included in the user file.
|
||||
# Password hash is stored as hash:<16-octets of hex data> without quotation
|
||||
# marks.
|
||||
|
||||
# [2] flag in the end of the line can be used to mark users for tunneled phase
|
||||
# 2 authentication (e.g., within EAP-PEAP). In these cases, an anonymous
|
||||
# identity can be used in the unencrypted phase 1 and the real user identity
|
||||
# is transmitted only within the encrypted tunnel in phase 2. If non-anonymous
|
||||
# access is needed, two user entries is needed, one for phase 1 and another
|
||||
# with the same username for phase 2.
|
||||
#
|
||||
# EAP-TLS, EAP-PEAP, EAP-TTLS, EAP-FAST, EAP-SIM, and EAP-AKA do not use
|
||||
# password option.
|
||||
# EAP-MD5, EAP-MSCHAPV2, EAP-GTC, EAP-PAX, EAP-PSK, and EAP-SAKE require a
|
||||
# password.
|
||||
# EAP-PEAP, EAP-TTLS, and EAP-FAST require Phase 2 configuration.
|
||||
#
|
||||
# * can be used as a wildcard to match any user identity. The main purposes for
|
||||
# this are to set anonymous phase 1 identity for EAP-PEAP and EAP-TTLS and to
|
||||
# avoid having to configure every certificate for EAP-TLS authentication. The
|
||||
# first matching entry is selected, so * should be used as the last phase 1
|
||||
# user entry.
|
||||
#
|
||||
# "prefix"* can be used to match the given prefix and anything after this. The
|
||||
# main purpose for this is to be able to avoid EAP method negotiation when the
|
||||
# method is using known prefix in identities (e.g., EAP-SIM and EAP-AKA). This
|
||||
# is only allowed for phase 1 identities.
|
||||
#
|
||||
# Multiple methods can be configured to make the authenticator try them one by
|
||||
# one until the peer accepts one. The method names are separated with a
|
||||
# comma (,).
|
||||
#
|
||||
# [ver=0] and [ver=1] flags after EAP type PEAP can be used to force PEAP
|
||||
# version based on the Phase 1 identity. Without this flag, the EAP
|
||||
# authenticator advertises the highest supported version and select the version
|
||||
# based on the first PEAP packet from the supplicant.
|
||||
#
|
||||
# EAP-TTLS supports both EAP and non-EAP authentication inside the tunnel.
|
||||
# Tunneled EAP methods are configured with standard EAP method name and [2]
|
||||
# flag. Non-EAP methods can be enabled by following method names: TTLS-PAP,
|
||||
# TTLS-CHAP, TTLS-MSCHAP, TTLS-MSCHAPV2. TTLS-PAP and TTLS-CHAP require a
|
||||
# plaintext password while TTLS-MSCHAP and TTLS-MSCHAPV2 can use NT password
|
||||
# hash.
|
||||
#
|
||||
# Arbitrary RADIUS attributes can be added into Access-Accept packets similarly
|
||||
# to the way radius_auth_req_attr is used for Access-Request packet in
|
||||
# hostapd.conf. For EAP server, this is configured separately for each user
|
||||
# entry with radius_accept_attr=<value> line(s) following the main user entry
|
||||
# line.
|
||||
|
||||
# Phase 1 users
|
||||
"user" MD5 "password"
|
||||
"test user" MD5 "secret"
|
||||
"example user" TLS
|
||||
"DOMAIN\user" MSCHAPV2 "password"
|
||||
"gtc user" GTC "password"
|
||||
"pax user" PAX "unknown"
|
||||
"pax.user@example.com" PAX 0123456789abcdef0123456789abcdef
|
||||
"psk user" PSK "unknown"
|
||||
"psk.user@example.com" PSK 0123456789abcdef0123456789abcdef
|
||||
"sake.user@example.com" SAKE 0123456789abcdef0123456789abcdef0123456789abcdef0123456789abcdef
|
||||
"ttls" TTLS
|
||||
"not anonymous" PEAP
|
||||
# Default to EAP-SIM and EAP-AKA based on fixed identity prefixes
|
||||
"0"* AKA,TTLS,TLS,PEAP,SIM
|
||||
"1"* SIM,TTLS,TLS,PEAP,AKA
|
||||
"2"* AKA,TTLS,TLS,PEAP,SIM
|
||||
"3"* SIM,TTLS,TLS,PEAP,AKA
|
||||
"4"* AKA,TTLS,TLS,PEAP,SIM
|
||||
"5"* SIM,TTLS,TLS,PEAP,AKA
|
||||
"6"* AKA'
|
||||
"7"* AKA'
|
||||
"8"* AKA'
|
||||
|
||||
# Wildcard for all other identities
|
||||
* PEAP,TTLS,TLS,SIM,AKA
|
||||
|
||||
# Phase 2 (tunnelled within EAP-PEAP or EAP-TTLS) users
|
||||
"t-md5" MD5 "password" [2]
|
||||
"DOMAIN\t-mschapv2" MSCHAPV2 "password" [2]
|
||||
"t-gtc" GTC "password" [2]
|
||||
"not anonymous" MSCHAPV2 "password" [2]
|
||||
"user" MD5,GTC,MSCHAPV2 "password" [2]
|
||||
"test user" MSCHAPV2 hash:000102030405060708090a0b0c0d0e0f [2]
|
||||
"ttls-user" TTLS-PAP,TTLS-CHAP,TTLS-MSCHAP,TTLS-MSCHAPV2 "password" [2]
|
||||
|
||||
# Default to EAP-SIM and EAP-AKA based on fixed identity prefixes in phase 2
|
||||
"0"* AKA [2]
|
||||
"1"* SIM [2]
|
||||
"2"* AKA [2]
|
||||
"3"* SIM [2]
|
||||
"4"* AKA [2]
|
||||
"5"* SIM [2]
|
||||
"6"* AKA' [2]
|
||||
"7"* AKA' [2]
|
||||
"8"* AKA' [2]
|
|
@ -0,0 +1,42 @@
|
|||
CREATE TABLE users(
|
||||
identity TEXT PRIMARY KEY,
|
||||
methods TEXT,
|
||||
password TEXT,
|
||||
remediation TEXT,
|
||||
phase2 INTEGER,
|
||||
t_c_timestamp INTEGER
|
||||
);
|
||||
|
||||
CREATE TABLE wildcards(
|
||||
identity TEXT PRIMARY KEY,
|
||||
methods TEXT
|
||||
);
|
||||
|
||||
INSERT INTO users(identity,methods,password,phase2) VALUES ('user','TTLS-MSCHAPV2','password',1);
|
||||
INSERT INTO users(identity,methods,password,phase2) VALUES ('DOMAIN\mschapv2 user','TTLS-MSCHAPV2','password',1);
|
||||
|
||||
INSERT INTO wildcards(identity,methods) VALUES ('','TTLS,TLS');
|
||||
INSERT INTO wildcards(identity,methods) VALUES ('0','AKA');
|
||||
|
||||
CREATE TABLE authlog(
|
||||
timestamp TEXT,
|
||||
session TEXT,
|
||||
nas_ip TEXT,
|
||||
username TEXT,
|
||||
note TEXT
|
||||
);
|
||||
|
||||
CREATE TABLE pending_tc(
|
||||
mac_addr TEXT PRIMARY KEY,
|
||||
identity TEXT
|
||||
);
|
||||
|
||||
CREATE TABLE current_sessions(
|
||||
mac_addr TEXT PRIMARY KEY,
|
||||
identity TEXT,
|
||||
start_time TEXT,
|
||||
nas TEXT,
|
||||
hs20_t_c_filtering BOOLEAN,
|
||||
waiting_coa_ack BOOLEAN,
|
||||
coa_ack_received BOOLEAN
|
||||
);
|
|
@ -0,0 +1,4 @@
|
|||
# RADIUS client configuration for the RADIUS server
|
||||
10.1.2.3 secret passphrase
|
||||
192.168.1.0/24 another very secret passphrase
|
||||
0.0.0.0/0 radius
|
|
@ -0,0 +1,9 @@
|
|||
# Example GSM authentication triplet file for EAP-SIM authenticator
|
||||
# IMSI:Kc:SRES:RAND
|
||||
# IMSI: ASCII string (numbers)
|
||||
# Kc: hex, 8 octets
|
||||
# SRES: hex, 4 octets
|
||||
# RAND: hex, 16 octets
|
||||
234567898765432:A0A1A2A3A4A5A6A7:D1D2D3D4:AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA
|
||||
234567898765432:B0B1B2B3B4B5B6B7:E1E2E3E4:BBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBB
|
||||
234567898765432:C0C1C2C3C4C5C6C7:F1F2F3F4:CCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCC
|
|
@ -0,0 +1,9 @@
|
|||
# VLAN ID to network interface mapping
|
||||
1 vlan1
|
||||
2 vlan2
|
||||
3 vlan3
|
||||
100 guest
|
||||
# Optional wildcard entry matching all VLAN IDs. The first # in the interface
|
||||
# name will be replaced with the VLAN ID. The network interfaces are created
|
||||
# (and removed) dynamically based on the use.
|
||||
* vlan#
|
|
@ -0,0 +1,15 @@
|
|||
# List of WPA PSKs. Each line, except for empty lines and lines starting
|
||||
# with #, must contain a MAC address and PSK separated with a space.
|
||||
# Special MAC address 00:00:00:00:00:00 can be used to configure PSKs that
|
||||
# anyone can use. PSK can be configured as an ASCII passphrase of 8..63
|
||||
# characters or as a 256-bit hex PSK (64 hex digits).
|
||||
# An optional key identifier can be added by prefixing the line with
|
||||
# keyid=<keyid_string>
|
||||
# An optional VLAN ID can be specified by prefixing the line with
|
||||
# vlanid=<VLAN ID>.
|
||||
00:00:00:00:00:00 secret passphrase
|
||||
00:11:22:33:44:55 another passphrase
|
||||
00:22:33:44:55:66 0123456789abcdef0123456789abcdef0123456789abcdef0123456789abcdef
|
||||
keyid=example_id 00:11:22:33:44:77 passphrase with keyid
|
||||
vlanid=3 00:00:00:00:00:00 passphrase with vlanid
|
||||
00:00:00:00:00:00 another passphrase for all STAs
|
|
@ -0,0 +1,89 @@
|
|||
.TH HOSTAPD_CLI 1 "April 7, 2005" hostapd_cli "hostapd command-line interface"
|
||||
.SH NAME
|
||||
hostapd_cli \- hostapd command-line interface
|
||||
.SH SYNOPSIS
|
||||
.B hostapd_cli
|
||||
[\-p<path>] [\-i<ifname>] [\-a<path>] [\-hvB] [command..]
|
||||
.SH DESCRIPTION
|
||||
This manual page documents briefly the
|
||||
.B hostapd_cli
|
||||
utility.
|
||||
.PP
|
||||
.B hostapd_cli
|
||||
is a command-line interface for the
|
||||
.B hostapd
|
||||
daemon.
|
||||
|
||||
.B hostapd
|
||||
is a user space daemon for access point and authentication servers.
|
||||
It implements IEEE 802.11 access point management, IEEE 802.1X/WPA/WPA2/EAP Authenticators and RADIUS authentication server.
|
||||
For more information about
|
||||
.B hostapd
|
||||
refer to the
|
||||
.BR hostapd (8)
|
||||
man page.
|
||||
.SH OPTIONS
|
||||
A summary of options is included below.
|
||||
For a complete description, run
|
||||
.BR hostapd_cli
|
||||
from the command line.
|
||||
.TP
|
||||
.B \-p<path>
|
||||
Path to find control sockets.
|
||||
|
||||
Default: /var/run/hostapd
|
||||
.TP
|
||||
.B \-i<ifname>
|
||||
Interface to listen on.
|
||||
|
||||
Default: first interface found in socket path.
|
||||
.TP
|
||||
.B \-a<path>
|
||||
Run in daemon mode executing the action file based on events from hostapd.
|
||||
.TP
|
||||
.B \-B
|
||||
Run a daemon in the background.
|
||||
.TP
|
||||
.B \-h
|
||||
Show usage.
|
||||
.TP
|
||||
.B \-v
|
||||
Show hostapd_cli version.
|
||||
.SH COMMANDS
|
||||
A summary of commands is included below.
|
||||
For a complete description, run
|
||||
.BR hostapd_cli
|
||||
from the command line.
|
||||
.TP
|
||||
.B mib
|
||||
Get MIB variables (dot1x, dot11, radius).
|
||||
.TP
|
||||
.B sta <addr>
|
||||
Get MIB variables for one station.
|
||||
.TP
|
||||
.B all_sta
|
||||
Get MIB variables for all stations.
|
||||
.TP
|
||||
.B help
|
||||
Get usage help.
|
||||
.TP
|
||||
.B interface [ifname]
|
||||
Show interfaces/select interface.
|
||||
.TP
|
||||
.B level <debug level>
|
||||
Change debug level.
|
||||
.TP
|
||||
.B license
|
||||
Show full
|
||||
.B hostapd_cli
|
||||
license.
|
||||
.TP
|
||||
.B quit
|
||||
Exit hostapd_cli.
|
||||
.SH SEE ALSO
|
||||
.BR hostapd (8).
|
||||
.SH AUTHOR
|
||||
hostapd_cli was written by Jouni Malinen <j@w1.fi>.
|
||||
.PP
|
||||
This manual page was written by Faidon Liambotis <faidon@cube.gr>,
|
||||
for the Debian project (but may be used by others).
|
File diff suppressed because it is too large
Load Diff
|
@ -0,0 +1,9 @@
|
|||
Logwatch is a utility for analyzing system logs and provide a human
|
||||
readable summary. This directory has a configuration file and a log
|
||||
analyzer script for parsing hostapd system log entries for logwatch.
|
||||
These files can be installed by copying them to following locations:
|
||||
|
||||
/etc/log.d/conf/services/hostapd.conf
|
||||
/etc/log.d/scripts/services/hostapd
|
||||
|
||||
More information about logwatch is available from http://www.logwatch.org/
|
|
@ -0,0 +1,65 @@
|
|||
#!/usr/bin/perl -w
|
||||
#
|
||||
# Logwatch script for hostapd
|
||||
#
|
||||
# Copyright 2005 Henrik Brix Andersen <brix@gentoo.org>
|
||||
# Distributed under the terms of the GNU General Public License v2
|
||||
# Alternatively, this file may be distributed under the terms of the BSD License
|
||||
|
||||
use strict;
|
||||
|
||||
my $debug = $ENV{'LOGWATCH_DEBUG'} || 0;
|
||||
my $detail = $ENV{'LOGWATCH_DETAIL_LEVEL'} || 0;
|
||||
my $debugcounter = 1;
|
||||
|
||||
my %hostapd;
|
||||
my @unmatched;
|
||||
|
||||
if ($debug >= 5) {
|
||||
print STDERR "\n\nDEBUG: Inside HOSTAPD Filter\n\n";
|
||||
}
|
||||
|
||||
while (defined(my $line = <STDIN>)) {
|
||||
if ($debug >= 5) {
|
||||
print STDERR "DEBUG($debugcounter): $line";
|
||||
$debugcounter++;
|
||||
}
|
||||
chomp($line);
|
||||
|
||||
if (my ($iface,$mac,$layer,$details) = ($line =~ /(.*?): STA (.*?) (.*?): (.*?)$/i)) {
|
||||
unless ($detail == 10) {
|
||||
# collapse association events
|
||||
$details =~ s/^(associated) .*$/$1/i;
|
||||
}
|
||||
$hostapd{$iface}->{$mac}->{$layer}->{$details}++;
|
||||
} else {
|
||||
push @unmatched, "$line\n";
|
||||
}
|
||||
}
|
||||
|
||||
if (keys %hostapd) {
|
||||
foreach my $iface (sort keys %hostapd) {
|
||||
print "Interface $iface:\n";
|
||||
foreach my $mac (sort keys %{$hostapd{$iface}}) {
|
||||
print " Client MAC Address $mac:\n";
|
||||
foreach my $layer (sort keys %{$hostapd{$iface}->{$mac}}) {
|
||||
print " $layer:\n";
|
||||
foreach my $details (sort keys %{$hostapd{$iface}->{$mac}->{$layer}}) {
|
||||
print " $details";
|
||||
my $count = $hostapd{$iface}->{$mac}->{$layer}->{$details};
|
||||
if ($count > 1) {
|
||||
print ": " . $count . " Times";
|
||||
}
|
||||
print "\n";
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
if ($#unmatched >= 0) {
|
||||
print "\n**Unmatched Entries**\n";
|
||||
print @unmatched;
|
||||
}
|
||||
|
||||
exit(0);
|
|
@ -0,0 +1,10 @@
|
|||
# Logwatch configuration for hostapd
|
||||
#
|
||||
# Copyright 2005 Henrik Brix Andersen <brix@gentoo.org>
|
||||
# Distributed under the terms of the GNU General Public License v2
|
||||
# Alternatively, this file may be distributed under the terms of the BSD License
|
||||
|
||||
Title = "hostapd"
|
||||
LogFile = messages
|
||||
*OnlyService = hostapd
|
||||
*RemoveHeaders
|
|
@ -0,0 +1,936 @@
|
|||
/*
|
||||
* hostapd / main()
|
||||
* Copyright (c) 2002-2019, Jouni Malinen <j@w1.fi>
|
||||
*
|
||||
* This software may be distributed under the terms of the BSD license.
|
||||
* See README for more details.
|
||||
*/
|
||||
|
||||
#include "utils/includes.h"
|
||||
#ifndef CONFIG_NATIVE_WINDOWS
|
||||
#include <syslog.h>
|
||||
#include <grp.h>
|
||||
#endif /* CONFIG_NATIVE_WINDOWS */
|
||||
|
||||
#include "utils/common.h"
|
||||
#include "utils/eloop.h"
|
||||
#include "utils/uuid.h"
|
||||
#include "crypto/random.h"
|
||||
#include "crypto/tls.h"
|
||||
#include "common/version.h"
|
||||
#include "common/dpp.h"
|
||||
#include "drivers/driver.h"
|
||||
#include "eap_server/eap.h"
|
||||
#include "eap_server/tncs.h"
|
||||
#include "ap/hostapd.h"
|
||||
#include "ap/ap_config.h"
|
||||
#include "ap/ap_drv_ops.h"
|
||||
#include "ap/dpp_hostapd.h"
|
||||
#include "fst/fst.h"
|
||||
#include "config_file.h"
|
||||
#include "eap_register.h"
|
||||
#include "ctrl_iface.h"
|
||||
|
||||
|
||||
struct hapd_global {
|
||||
void **drv_priv;
|
||||
size_t drv_count;
|
||||
};
|
||||
|
||||
static struct hapd_global global;
|
||||
|
||||
|
||||
#ifndef CONFIG_NO_HOSTAPD_LOGGER
|
||||
static void hostapd_logger_cb(void *ctx, const u8 *addr, unsigned int module,
|
||||
int level, const char *txt, size_t len)
|
||||
{
|
||||
struct hostapd_data *hapd = ctx;
|
||||
char *format, *module_str;
|
||||
int maxlen;
|
||||
int conf_syslog_level, conf_stdout_level;
|
||||
unsigned int conf_syslog, conf_stdout;
|
||||
|
||||
maxlen = len + 100;
|
||||
format = os_malloc(maxlen);
|
||||
if (!format)
|
||||
return;
|
||||
|
||||
if (hapd && hapd->conf) {
|
||||
conf_syslog_level = hapd->conf->logger_syslog_level;
|
||||
conf_stdout_level = hapd->conf->logger_stdout_level;
|
||||
conf_syslog = hapd->conf->logger_syslog;
|
||||
conf_stdout = hapd->conf->logger_stdout;
|
||||
} else {
|
||||
conf_syslog_level = conf_stdout_level = 0;
|
||||
conf_syslog = conf_stdout = (unsigned int) -1;
|
||||
}
|
||||
|
||||
switch (module) {
|
||||
case HOSTAPD_MODULE_IEEE80211:
|
||||
module_str = "IEEE 802.11";
|
||||
break;
|
||||
case HOSTAPD_MODULE_IEEE8021X:
|
||||
module_str = "IEEE 802.1X";
|
||||
break;
|
||||
case HOSTAPD_MODULE_RADIUS:
|
||||
module_str = "RADIUS";
|
||||
break;
|
||||
case HOSTAPD_MODULE_WPA:
|
||||
module_str = "WPA";
|
||||
break;
|
||||
case HOSTAPD_MODULE_DRIVER:
|
||||
module_str = "DRIVER";
|
||||
break;
|
||||
case HOSTAPD_MODULE_IAPP:
|
||||
module_str = "IAPP";
|
||||
break;
|
||||
case HOSTAPD_MODULE_MLME:
|
||||
module_str = "MLME";
|
||||
break;
|
||||
default:
|
||||
module_str = NULL;
|
||||
break;
|
||||
}
|
||||
|
||||
if (hapd && hapd->conf && addr)
|
||||
os_snprintf(format, maxlen, "%s: STA " MACSTR "%s%s: %s",
|
||||
hapd->conf->iface, MAC2STR(addr),
|
||||
module_str ? " " : "", module_str ? module_str : "",
|
||||
txt);
|
||||
else if (hapd && hapd->conf)
|
||||
os_snprintf(format, maxlen, "%s:%s%s %s",
|
||||
hapd->conf->iface, module_str ? " " : "",
|
||||
module_str ? module_str : "", txt);
|
||||
else if (addr)
|
||||
os_snprintf(format, maxlen, "STA " MACSTR "%s%s: %s",
|
||||
MAC2STR(addr), module_str ? " " : "",
|
||||
module_str ? module_str : "", txt);
|
||||
else
|
||||
os_snprintf(format, maxlen, "%s%s%s",
|
||||
module_str ? module_str : "",
|
||||
module_str ? ": " : "", txt);
|
||||
|
||||
#ifdef CONFIG_DEBUG_SYSLOG
|
||||
if (wpa_debug_syslog)
|
||||
conf_stdout = 0;
|
||||
#endif /* CONFIG_DEBUG_SYSLOG */
|
||||
if ((conf_stdout & module) && level >= conf_stdout_level) {
|
||||
wpa_debug_print_timestamp();
|
||||
wpa_printf(MSG_INFO, "%s", format);
|
||||
}
|
||||
|
||||
#ifndef CONFIG_NATIVE_WINDOWS
|
||||
if ((conf_syslog & module) && level >= conf_syslog_level) {
|
||||
int priority;
|
||||
switch (level) {
|
||||
case HOSTAPD_LEVEL_DEBUG_VERBOSE:
|
||||
case HOSTAPD_LEVEL_DEBUG:
|
||||
priority = LOG_DEBUG;
|
||||
break;
|
||||
case HOSTAPD_LEVEL_INFO:
|
||||
priority = LOG_INFO;
|
||||
break;
|
||||
case HOSTAPD_LEVEL_NOTICE:
|
||||
priority = LOG_NOTICE;
|
||||
break;
|
||||
case HOSTAPD_LEVEL_WARNING:
|
||||
priority = LOG_WARNING;
|
||||
break;
|
||||
default:
|
||||
priority = LOG_INFO;
|
||||
break;
|
||||
}
|
||||
syslog(priority, "%s", format);
|
||||
}
|
||||
#endif /* CONFIG_NATIVE_WINDOWS */
|
||||
|
||||
os_free(format);
|
||||
}
|
||||
#endif /* CONFIG_NO_HOSTAPD_LOGGER */
|
||||
|
||||
|
||||
/**
|
||||
* hostapd_driver_init - Preparate driver interface
|
||||
*/
|
||||
static int hostapd_driver_init(struct hostapd_iface *iface)
|
||||
{
|
||||
struct wpa_init_params params;
|
||||
size_t i;
|
||||
struct hostapd_data *hapd = iface->bss[0];
|
||||
struct hostapd_bss_config *conf = hapd->conf;
|
||||
u8 *b = conf->bssid;
|
||||
struct wpa_driver_capa capa;
|
||||
|
||||
if (hapd->driver == NULL || hapd->driver->hapd_init == NULL) {
|
||||
wpa_printf(MSG_ERROR, "No hostapd driver wrapper available");
|
||||
return -1;
|
||||
}
|
||||
|
||||
/* Initialize the driver interface */
|
||||
if (!(b[0] | b[1] | b[2] | b[3] | b[4] | b[5]))
|
||||
b = NULL;
|
||||
|
||||
os_memset(¶ms, 0, sizeof(params));
|
||||
for (i = 0; wpa_drivers[i]; i++) {
|
||||
if (wpa_drivers[i] != hapd->driver)
|
||||
continue;
|
||||
|
||||
if (global.drv_priv[i] == NULL &&
|
||||
wpa_drivers[i]->global_init) {
|
||||
global.drv_priv[i] =
|
||||
wpa_drivers[i]->global_init(iface->interfaces);
|
||||
if (global.drv_priv[i] == NULL) {
|
||||
wpa_printf(MSG_ERROR, "Failed to initialize "
|
||||
"driver '%s'",
|
||||
wpa_drivers[i]->name);
|
||||
return -1;
|
||||
}
|
||||
}
|
||||
|
||||
params.global_priv = global.drv_priv[i];
|
||||
break;
|
||||
}
|
||||
params.bssid = b;
|
||||
params.ifname = hapd->conf->iface;
|
||||
params.driver_params = hapd->iconf->driver_params;
|
||||
params.use_pae_group_addr = hapd->conf->use_pae_group_addr;
|
||||
|
||||
params.num_bridge = hapd->iface->num_bss;
|
||||
params.bridge = os_calloc(hapd->iface->num_bss, sizeof(char *));
|
||||
if (params.bridge == NULL)
|
||||
return -1;
|
||||
for (i = 0; i < hapd->iface->num_bss; i++) {
|
||||
struct hostapd_data *bss = hapd->iface->bss[i];
|
||||
if (bss->conf->bridge[0])
|
||||
params.bridge[i] = bss->conf->bridge;
|
||||
}
|
||||
|
||||
params.own_addr = hapd->own_addr;
|
||||
|
||||
hapd->drv_priv = hapd->driver->hapd_init(hapd, ¶ms);
|
||||
os_free(params.bridge);
|
||||
if (hapd->drv_priv == NULL) {
|
||||
wpa_printf(MSG_ERROR, "%s driver initialization failed.",
|
||||
hapd->driver->name);
|
||||
hapd->driver = NULL;
|
||||
return -1;
|
||||
}
|
||||
|
||||
if (hapd->driver->get_capa &&
|
||||
hapd->driver->get_capa(hapd->drv_priv, &capa) == 0) {
|
||||
struct wowlan_triggers *triggs;
|
||||
|
||||
iface->drv_flags = capa.flags;
|
||||
iface->smps_modes = capa.smps_modes;
|
||||
iface->probe_resp_offloads = capa.probe_resp_offloads;
|
||||
/*
|
||||
* Use default extended capa values from per-radio information
|
||||
*/
|
||||
iface->extended_capa = capa.extended_capa;
|
||||
iface->extended_capa_mask = capa.extended_capa_mask;
|
||||
iface->extended_capa_len = capa.extended_capa_len;
|
||||
iface->drv_max_acl_mac_addrs = capa.max_acl_mac_addrs;
|
||||
|
||||
/*
|
||||
* Override extended capa with per-interface type (AP), if
|
||||
* available from the driver.
|
||||
*/
|
||||
hostapd_get_ext_capa(iface);
|
||||
|
||||
triggs = wpa_get_wowlan_triggers(conf->wowlan_triggers, &capa);
|
||||
if (triggs && hapd->driver->set_wowlan) {
|
||||
if (hapd->driver->set_wowlan(hapd->drv_priv, triggs))
|
||||
wpa_printf(MSG_ERROR, "set_wowlan failed");
|
||||
}
|
||||
os_free(triggs);
|
||||
}
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
|
||||
/**
|
||||
* hostapd_interface_init - Read configuration file and init BSS data
|
||||
*
|
||||
* This function is used to parse configuration file for a full interface (one
|
||||
* or more BSSes sharing the same radio) and allocate memory for the BSS
|
||||
* interfaces. No actual driver operations are started.
|
||||
*/
|
||||
static struct hostapd_iface *
|
||||
hostapd_interface_init(struct hapd_interfaces *interfaces, const char *if_name,
|
||||
const char *config_fname, int debug)
|
||||
{
|
||||
struct hostapd_iface *iface;
|
||||
int k;
|
||||
|
||||
wpa_printf(MSG_ERROR, "Configuration file: %s", config_fname);
|
||||
iface = hostapd_init(interfaces, config_fname);
|
||||
if (!iface)
|
||||
return NULL;
|
||||
|
||||
if (if_name) {
|
||||
os_strlcpy(iface->conf->bss[0]->iface, if_name,
|
||||
sizeof(iface->conf->bss[0]->iface));
|
||||
}
|
||||
|
||||
iface->interfaces = interfaces;
|
||||
|
||||
for (k = 0; k < debug; k++) {
|
||||
if (iface->bss[0]->conf->logger_stdout_level > 0)
|
||||
iface->bss[0]->conf->logger_stdout_level--;
|
||||
}
|
||||
|
||||
if (iface->conf->bss[0]->iface[0] == '\0' &&
|
||||
!hostapd_drv_none(iface->bss[0])) {
|
||||
wpa_printf(MSG_ERROR,
|
||||
"Interface name not specified in %s, nor by '-i' parameter",
|
||||
config_fname);
|
||||
hostapd_interface_deinit_free(iface);
|
||||
return NULL;
|
||||
}
|
||||
|
||||
return iface;
|
||||
}
|
||||
|
||||
|
||||
/**
|
||||
* handle_term - SIGINT and SIGTERM handler to terminate hostapd process
|
||||
*/
|
||||
static void handle_term(int sig, void *signal_ctx)
|
||||
{
|
||||
wpa_printf(MSG_DEBUG, "Signal %d received - terminating", sig);
|
||||
eloop_terminate();
|
||||
}
|
||||
|
||||
|
||||
#ifndef CONFIG_NATIVE_WINDOWS
|
||||
|
||||
static int handle_reload_iface(struct hostapd_iface *iface, void *ctx)
|
||||
{
|
||||
if (hostapd_reload_config(iface) < 0) {
|
||||
wpa_printf(MSG_WARNING, "Failed to read new configuration "
|
||||
"file - continuing with old.");
|
||||
}
|
||||
return 0;
|
||||
}
|
||||
|
||||
|
||||
/**
|
||||
* handle_reload - SIGHUP handler to reload configuration
|
||||
*/
|
||||
static void handle_reload(int sig, void *signal_ctx)
|
||||
{
|
||||
struct hapd_interfaces *interfaces = signal_ctx;
|
||||
wpa_printf(MSG_DEBUG, "Signal %d received - reloading configuration",
|
||||
sig);
|
||||
hostapd_for_each_interface(interfaces, handle_reload_iface, NULL);
|
||||
}
|
||||
|
||||
|
||||
static void handle_dump_state(int sig, void *signal_ctx)
|
||||
{
|
||||
/* Not used anymore - ignore signal */
|
||||
}
|
||||
#endif /* CONFIG_NATIVE_WINDOWS */
|
||||
|
||||
|
||||
static int hostapd_global_init(struct hapd_interfaces *interfaces,
|
||||
const char *entropy_file)
|
||||
{
|
||||
int i;
|
||||
|
||||
os_memset(&global, 0, sizeof(global));
|
||||
|
||||
hostapd_logger_register_cb(hostapd_logger_cb);
|
||||
|
||||
if (eap_server_register_methods()) {
|
||||
wpa_printf(MSG_ERROR, "Failed to register EAP methods");
|
||||
return -1;
|
||||
}
|
||||
|
||||
if (eloop_init()) {
|
||||
wpa_printf(MSG_ERROR, "Failed to initialize event loop");
|
||||
return -1;
|
||||
}
|
||||
interfaces->eloop_initialized = 1;
|
||||
|
||||
random_init(entropy_file);
|
||||
|
||||
#ifndef CONFIG_NATIVE_WINDOWS
|
||||
eloop_register_signal(SIGHUP, handle_reload, interfaces);
|
||||
eloop_register_signal(SIGUSR1, handle_dump_state, interfaces);
|
||||
#endif /* CONFIG_NATIVE_WINDOWS */
|
||||
eloop_register_signal_terminate(handle_term, interfaces);
|
||||
|
||||
#ifndef CONFIG_NATIVE_WINDOWS
|
||||
openlog("hostapd", 0, LOG_DAEMON);
|
||||
#endif /* CONFIG_NATIVE_WINDOWS */
|
||||
|
||||
for (i = 0; wpa_drivers[i]; i++)
|
||||
global.drv_count++;
|
||||
if (global.drv_count == 0) {
|
||||
wpa_printf(MSG_ERROR, "No drivers enabled");
|
||||
return -1;
|
||||
}
|
||||
global.drv_priv = os_calloc(global.drv_count, sizeof(void *));
|
||||
if (global.drv_priv == NULL)
|
||||
return -1;
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
|
||||
static void hostapd_global_deinit(const char *pid_file, int eloop_initialized)
|
||||
{
|
||||
int i;
|
||||
|
||||
for (i = 0; wpa_drivers[i] && global.drv_priv; i++) {
|
||||
if (!global.drv_priv[i])
|
||||
continue;
|
||||
wpa_drivers[i]->global_deinit(global.drv_priv[i]);
|
||||
}
|
||||
os_free(global.drv_priv);
|
||||
global.drv_priv = NULL;
|
||||
|
||||
#ifdef EAP_SERVER_TNC
|
||||
tncs_global_deinit();
|
||||
#endif /* EAP_SERVER_TNC */
|
||||
|
||||
random_deinit();
|
||||
|
||||
if (eloop_initialized)
|
||||
eloop_destroy();
|
||||
|
||||
#ifndef CONFIG_NATIVE_WINDOWS
|
||||
closelog();
|
||||
#endif /* CONFIG_NATIVE_WINDOWS */
|
||||
|
||||
eap_server_unregister_methods();
|
||||
|
||||
os_daemonize_terminate(pid_file);
|
||||
}
|
||||
|
||||
|
||||
static int hostapd_global_run(struct hapd_interfaces *ifaces, int daemonize,
|
||||
const char *pid_file)
|
||||
{
|
||||
#ifdef EAP_SERVER_TNC
|
||||
int tnc = 0;
|
||||
size_t i, k;
|
||||
|
||||
for (i = 0; !tnc && i < ifaces->count; i++) {
|
||||
for (k = 0; k < ifaces->iface[i]->num_bss; k++) {
|
||||
if (ifaces->iface[i]->bss[0]->conf->tnc) {
|
||||
tnc++;
|
||||
break;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
if (tnc && tncs_global_init() < 0) {
|
||||
wpa_printf(MSG_ERROR, "Failed to initialize TNCS");
|
||||
return -1;
|
||||
}
|
||||
#endif /* EAP_SERVER_TNC */
|
||||
|
||||
if (daemonize) {
|
||||
if (os_daemonize(pid_file)) {
|
||||
wpa_printf(MSG_ERROR, "daemon: %s", strerror(errno));
|
||||
return -1;
|
||||
}
|
||||
if (eloop_sock_requeue()) {
|
||||
wpa_printf(MSG_ERROR, "eloop_sock_requeue: %s",
|
||||
strerror(errno));
|
||||
return -1;
|
||||
}
|
||||
}
|
||||
|
||||
eloop_run();
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
|
||||
static void show_version(void)
|
||||
{
|
||||
fprintf(stderr,
|
||||
"hostapd v" VERSION_STR "\n"
|
||||
"User space daemon for IEEE 802.11 AP management,\n"
|
||||
"IEEE 802.1X/WPA/WPA2/EAP/RADIUS Authenticator\n"
|
||||
"Copyright (c) 2002-2019, Jouni Malinen <j@w1.fi> "
|
||||
"and contributors\n");
|
||||
}
|
||||
|
||||
|
||||
static void usage(void)
|
||||
{
|
||||
show_version();
|
||||
fprintf(stderr,
|
||||
"\n"
|
||||
"usage: hostapd [-hdBKtv] [-P <PID file>] [-e <entropy file>] "
|
||||
"\\\n"
|
||||
" [-g <global ctrl_iface>] [-G <group>]\\\n"
|
||||
" [-i <comma-separated list of interface names>]\\\n"
|
||||
" <configuration file(s)>\n"
|
||||
"\n"
|
||||
"options:\n"
|
||||
" -h show this usage\n"
|
||||
" -d show more debug messages (-dd for even more)\n"
|
||||
" -B run daemon in the background\n"
|
||||
" -e entropy file\n"
|
||||
" -g global control interface path\n"
|
||||
" -G group for control interfaces\n"
|
||||
" -P PID file\n"
|
||||
" -K include key data in debug messages\n"
|
||||
#ifdef CONFIG_DEBUG_FILE
|
||||
" -f log output to debug file instead of stdout\n"
|
||||
#endif /* CONFIG_DEBUG_FILE */
|
||||
#ifdef CONFIG_DEBUG_LINUX_TRACING
|
||||
" -T record to Linux tracing in addition to logging\n"
|
||||
" (records all messages regardless of debug verbosity)\n"
|
||||
#endif /* CONFIG_DEBUG_LINUX_TRACING */
|
||||
" -i list of interface names to use\n"
|
||||
#ifdef CONFIG_DEBUG_SYSLOG
|
||||
" -s log output to syslog instead of stdout\n"
|
||||
#endif /* CONFIG_DEBUG_SYSLOG */
|
||||
" -S start all the interfaces synchronously\n"
|
||||
" -t include timestamps in some debug messages\n"
|
||||
" -v show hostapd version\n");
|
||||
|
||||
exit(1);
|
||||
}
|
||||
|
||||
|
||||
static const char * hostapd_msg_ifname_cb(void *ctx)
|
||||
{
|
||||
struct hostapd_data *hapd = ctx;
|
||||
if (hapd && hapd->conf)
|
||||
return hapd->conf->iface;
|
||||
return NULL;
|
||||
}
|
||||
|
||||
|
||||
static int hostapd_get_global_ctrl_iface(struct hapd_interfaces *interfaces,
|
||||
const char *path)
|
||||
{
|
||||
#ifndef CONFIG_CTRL_IFACE_UDP
|
||||
char *pos;
|
||||
#endif /* !CONFIG_CTRL_IFACE_UDP */
|
||||
|
||||
os_free(interfaces->global_iface_path);
|
||||
interfaces->global_iface_path = os_strdup(path);
|
||||
if (interfaces->global_iface_path == NULL)
|
||||
return -1;
|
||||
|
||||
#ifndef CONFIG_CTRL_IFACE_UDP
|
||||
pos = os_strrchr(interfaces->global_iface_path, '/');
|
||||
if (pos == NULL) {
|
||||
wpa_printf(MSG_ERROR, "No '/' in the global control interface "
|
||||
"file");
|
||||
os_free(interfaces->global_iface_path);
|
||||
interfaces->global_iface_path = NULL;
|
||||
return -1;
|
||||
}
|
||||
|
||||
*pos = '\0';
|
||||
interfaces->global_iface_name = pos + 1;
|
||||
#endif /* !CONFIG_CTRL_IFACE_UDP */
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
|
||||
static int hostapd_get_ctrl_iface_group(struct hapd_interfaces *interfaces,
|
||||
const char *group)
|
||||
{
|
||||
#ifndef CONFIG_NATIVE_WINDOWS
|
||||
struct group *grp;
|
||||
grp = getgrnam(group);
|
||||
if (grp == NULL) {
|
||||
wpa_printf(MSG_ERROR, "Unknown group '%s'", group);
|
||||
return -1;
|
||||
}
|
||||
interfaces->ctrl_iface_group = grp->gr_gid;
|
||||
#endif /* CONFIG_NATIVE_WINDOWS */
|
||||
return 0;
|
||||
}
|
||||
|
||||
|
||||
static int hostapd_get_interface_names(char ***if_names,
|
||||
size_t *if_names_size,
|
||||
char *arg)
|
||||
{
|
||||
char *if_name, *tmp, **nnames;
|
||||
size_t i;
|
||||
|
||||
if (!arg)
|
||||
return -1;
|
||||
if_name = strtok_r(arg, ",", &tmp);
|
||||
|
||||
while (if_name) {
|
||||
nnames = os_realloc_array(*if_names, 1 + *if_names_size,
|
||||
sizeof(char *));
|
||||
if (!nnames)
|
||||
goto fail;
|
||||
*if_names = nnames;
|
||||
|
||||
(*if_names)[*if_names_size] = os_strdup(if_name);
|
||||
if (!(*if_names)[*if_names_size])
|
||||
goto fail;
|
||||
(*if_names_size)++;
|
||||
if_name = strtok_r(NULL, ",", &tmp);
|
||||
}
|
||||
|
||||
return 0;
|
||||
|
||||
fail:
|
||||
for (i = 0; i < *if_names_size; i++)
|
||||
os_free((*if_names)[i]);
|
||||
os_free(*if_names);
|
||||
*if_names = NULL;
|
||||
*if_names_size = 0;
|
||||
return -1;
|
||||
}
|
||||
|
||||
|
||||
#ifdef CONFIG_WPS
|
||||
static int gen_uuid(const char *txt_addr)
|
||||
{
|
||||
u8 addr[ETH_ALEN];
|
||||
u8 uuid[UUID_LEN];
|
||||
char buf[100];
|
||||
|
||||
if (hwaddr_aton(txt_addr, addr) < 0)
|
||||
return -1;
|
||||
|
||||
uuid_gen_mac_addr(addr, uuid);
|
||||
if (uuid_bin2str(uuid, buf, sizeof(buf)) < 0)
|
||||
return -1;
|
||||
|
||||
printf("%s\n", buf);
|
||||
|
||||
return 0;
|
||||
}
|
||||
#endif /* CONFIG_WPS */
|
||||
|
||||
|
||||
#ifndef HOSTAPD_CLEANUP_INTERVAL
|
||||
#define HOSTAPD_CLEANUP_INTERVAL 10
|
||||
#endif /* HOSTAPD_CLEANUP_INTERVAL */
|
||||
|
||||
static int hostapd_periodic_call(struct hostapd_iface *iface, void *ctx)
|
||||
{
|
||||
hostapd_periodic_iface(iface);
|
||||
return 0;
|
||||
}
|
||||
|
||||
|
||||
/* Periodic cleanup tasks */
|
||||
static void hostapd_periodic(void *eloop_ctx, void *timeout_ctx)
|
||||
{
|
||||
struct hapd_interfaces *interfaces = eloop_ctx;
|
||||
|
||||
eloop_register_timeout(HOSTAPD_CLEANUP_INTERVAL, 0,
|
||||
hostapd_periodic, interfaces, NULL);
|
||||
hostapd_for_each_interface(interfaces, hostapd_periodic_call, NULL);
|
||||
}
|
||||
|
||||
|
||||
int main(int argc, char *argv[])
|
||||
{
|
||||
struct hapd_interfaces interfaces;
|
||||
int ret = 1;
|
||||
size_t i, j;
|
||||
int c, debug = 0, daemonize = 0;
|
||||
char *pid_file = NULL;
|
||||
const char *log_file = NULL;
|
||||
const char *entropy_file = NULL;
|
||||
char **bss_config = NULL, **tmp_bss;
|
||||
size_t num_bss_configs = 0;
|
||||
#ifdef CONFIG_DEBUG_LINUX_TRACING
|
||||
int enable_trace_dbg = 0;
|
||||
#endif /* CONFIG_DEBUG_LINUX_TRACING */
|
||||
int start_ifaces_in_sync = 0;
|
||||
char **if_names = NULL;
|
||||
size_t if_names_size = 0;
|
||||
#ifdef CONFIG_DPP
|
||||
struct dpp_global_config dpp_conf;
|
||||
#endif /* CONFIG_DPP */
|
||||
|
||||
if (os_program_init())
|
||||
return -1;
|
||||
|
||||
os_memset(&interfaces, 0, sizeof(interfaces));
|
||||
interfaces.reload_config = hostapd_reload_config;
|
||||
interfaces.config_read_cb = hostapd_config_read;
|
||||
interfaces.for_each_interface = hostapd_for_each_interface;
|
||||
interfaces.ctrl_iface_init = hostapd_ctrl_iface_init;
|
||||
interfaces.ctrl_iface_deinit = hostapd_ctrl_iface_deinit;
|
||||
interfaces.driver_init = hostapd_driver_init;
|
||||
interfaces.global_iface_path = NULL;
|
||||
interfaces.global_iface_name = NULL;
|
||||
interfaces.global_ctrl_sock = -1;
|
||||
dl_list_init(&interfaces.global_ctrl_dst);
|
||||
#ifdef CONFIG_ETH_P_OUI
|
||||
dl_list_init(&interfaces.eth_p_oui);
|
||||
#endif /* CONFIG_ETH_P_OUI */
|
||||
#ifdef CONFIG_DPP
|
||||
os_memset(&dpp_conf, 0, sizeof(dpp_conf));
|
||||
/* TODO: dpp_conf.msg_ctx? */
|
||||
interfaces.dpp = dpp_global_init(&dpp_conf);
|
||||
if (!interfaces.dpp)
|
||||
return -1;
|
||||
#endif /* CONFIG_DPP */
|
||||
|
||||
for (;;) {
|
||||
c = getopt(argc, argv, "b:Bde:f:hi:KP:sSTtu:vg:G:");
|
||||
if (c < 0)
|
||||
break;
|
||||
switch (c) {
|
||||
case 'h':
|
||||
usage();
|
||||
break;
|
||||
case 'd':
|
||||
debug++;
|
||||
if (wpa_debug_level > 0)
|
||||
wpa_debug_level--;
|
||||
break;
|
||||
case 'B':
|
||||
daemonize++;
|
||||
break;
|
||||
case 'e':
|
||||
entropy_file = optarg;
|
||||
break;
|
||||
case 'f':
|
||||
log_file = optarg;
|
||||
break;
|
||||
case 'K':
|
||||
wpa_debug_show_keys++;
|
||||
break;
|
||||
case 'P':
|
||||
os_free(pid_file);
|
||||
pid_file = os_rel2abs_path(optarg);
|
||||
break;
|
||||
case 't':
|
||||
wpa_debug_timestamp++;
|
||||
break;
|
||||
#ifdef CONFIG_DEBUG_LINUX_TRACING
|
||||
case 'T':
|
||||
enable_trace_dbg = 1;
|
||||
break;
|
||||
#endif /* CONFIG_DEBUG_LINUX_TRACING */
|
||||
case 'v':
|
||||
show_version();
|
||||
exit(1);
|
||||
break;
|
||||
case 'g':
|
||||
if (hostapd_get_global_ctrl_iface(&interfaces, optarg))
|
||||
return -1;
|
||||
break;
|
||||
case 'G':
|
||||
if (hostapd_get_ctrl_iface_group(&interfaces, optarg))
|
||||
return -1;
|
||||
break;
|
||||
case 'b':
|
||||
tmp_bss = os_realloc_array(bss_config,
|
||||
num_bss_configs + 1,
|
||||
sizeof(char *));
|
||||
if (tmp_bss == NULL)
|
||||
goto out;
|
||||
bss_config = tmp_bss;
|
||||
bss_config[num_bss_configs++] = optarg;
|
||||
break;
|
||||
#ifdef CONFIG_DEBUG_SYSLOG
|
||||
case 's':
|
||||
wpa_debug_syslog = 1;
|
||||
break;
|
||||
#endif /* CONFIG_DEBUG_SYSLOG */
|
||||
case 'S':
|
||||
start_ifaces_in_sync = 1;
|
||||
break;
|
||||
#ifdef CONFIG_WPS
|
||||
case 'u':
|
||||
return gen_uuid(optarg);
|
||||
#endif /* CONFIG_WPS */
|
||||
case 'i':
|
||||
if (hostapd_get_interface_names(&if_names,
|
||||
&if_names_size, optarg))
|
||||
goto out;
|
||||
break;
|
||||
default:
|
||||
usage();
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
if (optind == argc && interfaces.global_iface_path == NULL &&
|
||||
num_bss_configs == 0)
|
||||
usage();
|
||||
|
||||
wpa_msg_register_ifname_cb(hostapd_msg_ifname_cb);
|
||||
|
||||
if (log_file)
|
||||
wpa_debug_open_file(log_file);
|
||||
else
|
||||
wpa_debug_setup_stdout();
|
||||
#ifdef CONFIG_DEBUG_SYSLOG
|
||||
if (wpa_debug_syslog)
|
||||
wpa_debug_open_syslog();
|
||||
#endif /* CONFIG_DEBUG_SYSLOG */
|
||||
#ifdef CONFIG_DEBUG_LINUX_TRACING
|
||||
if (enable_trace_dbg) {
|
||||
int tret = wpa_debug_open_linux_tracing();
|
||||
if (tret) {
|
||||
wpa_printf(MSG_ERROR, "Failed to enable trace logging");
|
||||
return -1;
|
||||
}
|
||||
}
|
||||
#endif /* CONFIG_DEBUG_LINUX_TRACING */
|
||||
|
||||
interfaces.count = argc - optind;
|
||||
if (interfaces.count || num_bss_configs) {
|
||||
interfaces.iface = os_calloc(interfaces.count + num_bss_configs,
|
||||
sizeof(struct hostapd_iface *));
|
||||
if (interfaces.iface == NULL) {
|
||||
wpa_printf(MSG_ERROR, "malloc failed");
|
||||
return -1;
|
||||
}
|
||||
}
|
||||
|
||||
if (hostapd_global_init(&interfaces, entropy_file)) {
|
||||
wpa_printf(MSG_ERROR, "Failed to initialize global context");
|
||||
return -1;
|
||||
}
|
||||
|
||||
eloop_register_timeout(HOSTAPD_CLEANUP_INTERVAL, 0,
|
||||
hostapd_periodic, &interfaces, NULL);
|
||||
|
||||
if (fst_global_init()) {
|
||||
wpa_printf(MSG_ERROR,
|
||||
"Failed to initialize global FST context");
|
||||
goto out;
|
||||
}
|
||||
|
||||
#if defined(CONFIG_FST) && defined(CONFIG_CTRL_IFACE)
|
||||
if (!fst_global_add_ctrl(fst_ctrl_cli))
|
||||
wpa_printf(MSG_WARNING, "Failed to add CLI FST ctrl");
|
||||
#endif /* CONFIG_FST && CONFIG_CTRL_IFACE */
|
||||
|
||||
/* Allocate and parse configuration for full interface files */
|
||||
for (i = 0; i < interfaces.count; i++) {
|
||||
char *if_name = NULL;
|
||||
|
||||
if (i < if_names_size)
|
||||
if_name = if_names[i];
|
||||
|
||||
interfaces.iface[i] = hostapd_interface_init(&interfaces,
|
||||
if_name,
|
||||
argv[optind + i],
|
||||
debug);
|
||||
if (!interfaces.iface[i]) {
|
||||
wpa_printf(MSG_ERROR, "Failed to initialize interface");
|
||||
goto out;
|
||||
}
|
||||
if (start_ifaces_in_sync)
|
||||
interfaces.iface[i]->need_to_start_in_sync = 1;
|
||||
}
|
||||
|
||||
/* Allocate and parse configuration for per-BSS files */
|
||||
for (i = 0; i < num_bss_configs; i++) {
|
||||
struct hostapd_iface *iface;
|
||||
char *fname;
|
||||
|
||||
wpa_printf(MSG_INFO, "BSS config: %s", bss_config[i]);
|
||||
fname = os_strchr(bss_config[i], ':');
|
||||
if (fname == NULL) {
|
||||
wpa_printf(MSG_ERROR,
|
||||
"Invalid BSS config identifier '%s'",
|
||||
bss_config[i]);
|
||||
goto out;
|
||||
}
|
||||
*fname++ = '\0';
|
||||
iface = hostapd_interface_init_bss(&interfaces, bss_config[i],
|
||||
fname, debug);
|
||||
if (iface == NULL)
|
||||
goto out;
|
||||
for (j = 0; j < interfaces.count; j++) {
|
||||
if (interfaces.iface[j] == iface)
|
||||
break;
|
||||
}
|
||||
if (j == interfaces.count) {
|
||||
struct hostapd_iface **tmp;
|
||||
tmp = os_realloc_array(interfaces.iface,
|
||||
interfaces.count + 1,
|
||||
sizeof(struct hostapd_iface *));
|
||||
if (tmp == NULL) {
|
||||
hostapd_interface_deinit_free(iface);
|
||||
goto out;
|
||||
}
|
||||
interfaces.iface = tmp;
|
||||
interfaces.iface[interfaces.count++] = iface;
|
||||
}
|
||||
}
|
||||
|
||||
/*
|
||||
* Enable configured interfaces. Depending on channel configuration,
|
||||
* this may complete full initialization before returning or use a
|
||||
* callback mechanism to complete setup in case of operations like HT
|
||||
* co-ex scans, ACS, or DFS are needed to determine channel parameters.
|
||||
* In such case, the interface will be enabled from eloop context within
|
||||
* hostapd_global_run().
|
||||
*/
|
||||
interfaces.terminate_on_error = interfaces.count;
|
||||
for (i = 0; i < interfaces.count; i++) {
|
||||
if (hostapd_driver_init(interfaces.iface[i]) ||
|
||||
hostapd_setup_interface(interfaces.iface[i]))
|
||||
goto out;
|
||||
}
|
||||
|
||||
hostapd_global_ctrl_iface_init(&interfaces);
|
||||
|
||||
if (hostapd_global_run(&interfaces, daemonize, pid_file)) {
|
||||
wpa_printf(MSG_ERROR, "Failed to start eloop");
|
||||
goto out;
|
||||
}
|
||||
|
||||
ret = 0;
|
||||
|
||||
out:
|
||||
hostapd_global_ctrl_iface_deinit(&interfaces);
|
||||
/* Deinitialize all interfaces */
|
||||
for (i = 0; i < interfaces.count; i++) {
|
||||
if (!interfaces.iface[i])
|
||||
continue;
|
||||
interfaces.iface[i]->driver_ap_teardown =
|
||||
!!(interfaces.iface[i]->drv_flags &
|
||||
WPA_DRIVER_FLAGS_AP_TEARDOWN_SUPPORT);
|
||||
hostapd_interface_deinit_free(interfaces.iface[i]);
|
||||
}
|
||||
os_free(interfaces.iface);
|
||||
|
||||
#ifdef CONFIG_DPP
|
||||
dpp_global_deinit(interfaces.dpp);
|
||||
#endif /* CONFIG_DPP */
|
||||
|
||||
if (interfaces.eloop_initialized)
|
||||
eloop_cancel_timeout(hostapd_periodic, &interfaces, NULL);
|
||||
hostapd_global_deinit(pid_file, interfaces.eloop_initialized);
|
||||
os_free(pid_file);
|
||||
|
||||
wpa_debug_close_syslog();
|
||||
if (log_file)
|
||||
wpa_debug_close_file();
|
||||
wpa_debug_close_linux_tracing();
|
||||
|
||||
os_free(bss_config);
|
||||
|
||||
for (i = 0; i < if_names_size; i++)
|
||||
os_free(if_names[i]);
|
||||
os_free(if_names);
|
||||
|
||||
fst_global_deinit();
|
||||
|
||||
os_program_deinit();
|
||||
|
||||
return ret;
|
||||
}
|
|
@ -0,0 +1,47 @@
|
|||
/*
|
||||
* hostapd - Plaintext password to NtPasswordHash
|
||||
* Copyright (c) 2005, Jouni Malinen <j@w1.fi>
|
||||
*
|
||||
* This software may be distributed under the terms of the BSD license.
|
||||
* See README for more details.
|
||||
*/
|
||||
|
||||
#include "includes.h"
|
||||
|
||||
#include "common.h"
|
||||
#include "crypto/ms_funcs.h"
|
||||
|
||||
|
||||
int main(int argc, char *argv[])
|
||||
{
|
||||
unsigned char password_hash[16];
|
||||
size_t i;
|
||||
char *password, buf[64], *pos;
|
||||
|
||||
if (argc > 1)
|
||||
password = argv[1];
|
||||
else {
|
||||
if (fgets(buf, sizeof(buf), stdin) == NULL) {
|
||||
printf("Failed to read password\n");
|
||||
return 1;
|
||||
}
|
||||
buf[sizeof(buf) - 1] = '\0';
|
||||
pos = buf;
|
||||
while (*pos != '\0') {
|
||||
if (*pos == '\r' || *pos == '\n') {
|
||||
*pos = '\0';
|
||||
break;
|
||||
}
|
||||
pos++;
|
||||
}
|
||||
password = buf;
|
||||
}
|
||||
|
||||
if (nt_password_hash((u8 *) password, strlen(password), password_hash))
|
||||
return -1;
|
||||
for (i = 0; i < sizeof(password_hash); i++)
|
||||
printf("%02x", password_hash[i]);
|
||||
printf("\n");
|
||||
|
||||
return 0;
|
||||
}
|
|
@ -0,0 +1,40 @@
|
|||
##### hostapd configuration file ##############################################
|
||||
# Empty lines and lines starting with # are ignored
|
||||
|
||||
# Example configuration file for wired authenticator. See hostapd.conf for
|
||||
# more details.
|
||||
|
||||
interface=eth0
|
||||
driver=wired
|
||||
logger_stdout=-1
|
||||
logger_stdout_level=1
|
||||
debug=2
|
||||
dump_file=/tmp/hostapd.dump
|
||||
|
||||
ieee8021x=1
|
||||
eap_reauth_period=3600
|
||||
|
||||
use_pae_group_addr=1
|
||||
|
||||
|
||||
##### RADIUS configuration ####################################################
|
||||
# for IEEE 802.1X with external Authentication Server, IEEE 802.11
|
||||
# authentication with external ACL for MAC addresses, and accounting
|
||||
|
||||
# The own IP address of the access point (used as NAS-IP-Address)
|
||||
own_ip_addr=127.0.0.1
|
||||
|
||||
# Optional NAS-Identifier string for RADIUS messages. When used, this should be
|
||||
# a unique to the NAS within the scope of the RADIUS server. For example, a
|
||||
# fully qualified domain name can be used here.
|
||||
nas_identifier=ap.example.com
|
||||
|
||||
# RADIUS authentication server
|
||||
auth_server_addr=127.0.0.1
|
||||
auth_server_port=1812
|
||||
auth_server_shared_secret=radius
|
||||
|
||||
# RADIUS accounting server
|
||||
acct_server_addr=127.0.0.1
|
||||
acct_server_port=1813
|
||||
acct_server_shared_secret=radius
|
|
@ -0,0 +1,342 @@
|
|||
#!/usr/bin/python
|
||||
#
|
||||
# Example nfcpy to hostapd wrapper for WPS NFC operations
|
||||
# Copyright (c) 2012-2013, Jouni Malinen <j@w1.fi>
|
||||
#
|
||||
# This software may be distributed under the terms of the BSD license.
|
||||
# See README for more details.
|
||||
|
||||
import os
|
||||
import sys
|
||||
import time
|
||||
import argparse
|
||||
|
||||
import nfc
|
||||
import nfc.ndef
|
||||
import nfc.llcp
|
||||
import nfc.handover
|
||||
|
||||
import logging
|
||||
|
||||
import wpaspy
|
||||
|
||||
wpas_ctrl = '/var/run/hostapd'
|
||||
continue_loop = True
|
||||
summary_file = None
|
||||
success_file = None
|
||||
|
||||
def summary(txt):
|
||||
print(txt)
|
||||
if summary_file:
|
||||
with open(summary_file, 'a') as f:
|
||||
f.write(txt + "\n")
|
||||
|
||||
def success_report(txt):
|
||||
summary(txt)
|
||||
if success_file:
|
||||
with open(success_file, 'a') as f:
|
||||
f.write(txt + "\n")
|
||||
|
||||
def wpas_connect():
|
||||
ifaces = []
|
||||
if os.path.isdir(wpas_ctrl):
|
||||
try:
|
||||
ifaces = [os.path.join(wpas_ctrl, i) for i in os.listdir(wpas_ctrl)]
|
||||
except OSError as error:
|
||||
print("Could not find hostapd: ", error)
|
||||
return None
|
||||
|
||||
if len(ifaces) < 1:
|
||||
print("No hostapd control interface found")
|
||||
return None
|
||||
|
||||
for ctrl in ifaces:
|
||||
try:
|
||||
wpas = wpaspy.Ctrl(ctrl)
|
||||
return wpas
|
||||
except Exception as e:
|
||||
pass
|
||||
return None
|
||||
|
||||
|
||||
def wpas_tag_read(message):
|
||||
wpas = wpas_connect()
|
||||
if (wpas == None):
|
||||
return False
|
||||
if "FAIL" in wpas.request("WPS_NFC_TAG_READ " + str(message).encode("hex")):
|
||||
return False
|
||||
return True
|
||||
|
||||
|
||||
def wpas_get_config_token():
|
||||
wpas = wpas_connect()
|
||||
if (wpas == None):
|
||||
return None
|
||||
ret = wpas.request("WPS_NFC_CONFIG_TOKEN NDEF")
|
||||
if "FAIL" in ret:
|
||||
return None
|
||||
return ret.rstrip().decode("hex")
|
||||
|
||||
|
||||
def wpas_get_password_token():
|
||||
wpas = wpas_connect()
|
||||
if (wpas == None):
|
||||
return None
|
||||
ret = wpas.request("WPS_NFC_TOKEN NDEF")
|
||||
if "FAIL" in ret:
|
||||
return None
|
||||
return ret.rstrip().decode("hex")
|
||||
|
||||
|
||||
def wpas_get_handover_sel():
|
||||
wpas = wpas_connect()
|
||||
if (wpas == None):
|
||||
return None
|
||||
ret = wpas.request("NFC_GET_HANDOVER_SEL NDEF WPS-CR")
|
||||
if "FAIL" in ret:
|
||||
return None
|
||||
return ret.rstrip().decode("hex")
|
||||
|
||||
|
||||
def wpas_report_handover(req, sel):
|
||||
wpas = wpas_connect()
|
||||
if (wpas == None):
|
||||
return None
|
||||
return wpas.request("NFC_REPORT_HANDOVER RESP WPS " +
|
||||
str(req).encode("hex") + " " +
|
||||
str(sel).encode("hex"))
|
||||
|
||||
|
||||
class HandoverServer(nfc.handover.HandoverServer):
|
||||
def __init__(self, llc):
|
||||
super(HandoverServer, self).__init__(llc)
|
||||
self.ho_server_processing = False
|
||||
self.success = False
|
||||
|
||||
# override to avoid parser error in request/response.pretty() in nfcpy
|
||||
# due to new WSC handover format
|
||||
def _process_request(self, request):
|
||||
summary("received handover request {}".format(request.type))
|
||||
response = nfc.ndef.Message("\xd1\x02\x01Hs\x12")
|
||||
if not request.type == 'urn:nfc:wkt:Hr':
|
||||
summary("not a handover request")
|
||||
else:
|
||||
try:
|
||||
request = nfc.ndef.HandoverRequestMessage(request)
|
||||
except nfc.ndef.DecodeError as e:
|
||||
summary("error decoding 'Hr' message: {}".format(e))
|
||||
else:
|
||||
response = self.process_request(request)
|
||||
summary("send handover response {}".format(response.type))
|
||||
return response
|
||||
|
||||
def process_request(self, request):
|
||||
summary("HandoverServer - request received")
|
||||
try:
|
||||
print("Parsed handover request: " + request.pretty())
|
||||
except Exception as e:
|
||||
print(e)
|
||||
print(str(request).encode("hex"))
|
||||
|
||||
sel = nfc.ndef.HandoverSelectMessage(version="1.2")
|
||||
|
||||
for carrier in request.carriers:
|
||||
print("Remote carrier type: " + carrier.type)
|
||||
if carrier.type == "application/vnd.wfa.wsc":
|
||||
summary("WPS carrier type match - add WPS carrier record")
|
||||
data = wpas_get_handover_sel()
|
||||
if data is None:
|
||||
summary("Could not get handover select carrier record from hostapd")
|
||||
continue
|
||||
print("Handover select carrier record from hostapd:")
|
||||
print(data.encode("hex"))
|
||||
if "OK" in wpas_report_handover(carrier.record, data):
|
||||
success_report("Handover reported successfully")
|
||||
else:
|
||||
summary("Handover report rejected")
|
||||
|
||||
message = nfc.ndef.Message(data);
|
||||
sel.add_carrier(message[0], "active", message[1:])
|
||||
|
||||
print("Handover select:")
|
||||
try:
|
||||
print(sel.pretty())
|
||||
except Exception as e:
|
||||
print(e)
|
||||
print(str(sel).encode("hex"))
|
||||
|
||||
summary("Sending handover select")
|
||||
self.success = True
|
||||
return sel
|
||||
|
||||
|
||||
def wps_tag_read(tag):
|
||||
success = False
|
||||
if len(tag.ndef.message):
|
||||
for record in tag.ndef.message:
|
||||
print("record type " + record.type)
|
||||
if record.type == "application/vnd.wfa.wsc":
|
||||
summary("WPS tag - send to hostapd")
|
||||
success = wpas_tag_read(tag.ndef.message)
|
||||
break
|
||||
else:
|
||||
summary("Empty tag")
|
||||
|
||||
if success:
|
||||
success_report("Tag read succeeded")
|
||||
|
||||
return success
|
||||
|
||||
|
||||
def rdwr_connected_write(tag):
|
||||
summary("Tag found - writing - " + str(tag))
|
||||
global write_data
|
||||
tag.ndef.message = str(write_data)
|
||||
success_report("Tag write succeeded")
|
||||
print("Done - remove tag")
|
||||
global only_one
|
||||
if only_one:
|
||||
global continue_loop
|
||||
continue_loop = False
|
||||
global write_wait_remove
|
||||
while write_wait_remove and tag.is_present:
|
||||
time.sleep(0.1)
|
||||
|
||||
def wps_write_config_tag(clf, wait_remove=True):
|
||||
summary("Write WPS config token")
|
||||
global write_data, write_wait_remove
|
||||
write_wait_remove = wait_remove
|
||||
write_data = wpas_get_config_token()
|
||||
if write_data == None:
|
||||
summary("Could not get WPS config token from hostapd")
|
||||
return
|
||||
|
||||
print("Touch an NFC tag")
|
||||
clf.connect(rdwr={'on-connect': rdwr_connected_write})
|
||||
|
||||
|
||||
def wps_write_password_tag(clf, wait_remove=True):
|
||||
summary("Write WPS password token")
|
||||
global write_data, write_wait_remove
|
||||
write_wait_remove = wait_remove
|
||||
write_data = wpas_get_password_token()
|
||||
if write_data == None:
|
||||
summary("Could not get WPS password token from hostapd")
|
||||
return
|
||||
|
||||
print("Touch an NFC tag")
|
||||
clf.connect(rdwr={'on-connect': rdwr_connected_write})
|
||||
|
||||
|
||||
def rdwr_connected(tag):
|
||||
global only_one, no_wait
|
||||
summary("Tag connected: " + str(tag))
|
||||
|
||||
if tag.ndef:
|
||||
print("NDEF tag: " + tag.type)
|
||||
try:
|
||||
print(tag.ndef.message.pretty())
|
||||
except Exception as e:
|
||||
print(e)
|
||||
success = wps_tag_read(tag)
|
||||
if only_one and success:
|
||||
global continue_loop
|
||||
continue_loop = False
|
||||
else:
|
||||
summary("Not an NDEF tag - remove tag")
|
||||
return True
|
||||
|
||||
return not no_wait
|
||||
|
||||
|
||||
def llcp_startup(clf, llc):
|
||||
print("Start LLCP server")
|
||||
global srv
|
||||
srv = HandoverServer(llc)
|
||||
return llc
|
||||
|
||||
def llcp_connected(llc):
|
||||
print("P2P LLCP connected")
|
||||
global wait_connection
|
||||
wait_connection = False
|
||||
global srv
|
||||
srv.start()
|
||||
return True
|
||||
|
||||
|
||||
def main():
|
||||
clf = nfc.ContactlessFrontend()
|
||||
|
||||
parser = argparse.ArgumentParser(description='nfcpy to hostapd integration for WPS NFC operations')
|
||||
parser.add_argument('-d', const=logging.DEBUG, default=logging.INFO,
|
||||
action='store_const', dest='loglevel',
|
||||
help='verbose debug output')
|
||||
parser.add_argument('-q', const=logging.WARNING, action='store_const',
|
||||
dest='loglevel', help='be quiet')
|
||||
parser.add_argument('--only-one', '-1', action='store_true',
|
||||
help='run only one operation and exit')
|
||||
parser.add_argument('--no-wait', action='store_true',
|
||||
help='do not wait for tag to be removed before exiting')
|
||||
parser.add_argument('--summary',
|
||||
help='summary file for writing status updates')
|
||||
parser.add_argument('--success',
|
||||
help='success file for writing success update')
|
||||
parser.add_argument('command', choices=['write-config',
|
||||
'write-password'],
|
||||
nargs='?')
|
||||
args = parser.parse_args()
|
||||
|
||||
global only_one
|
||||
only_one = args.only_one
|
||||
|
||||
global no_wait
|
||||
no_wait = args.no_wait
|
||||
|
||||
if args.summary:
|
||||
global summary_file
|
||||
summary_file = args.summary
|
||||
|
||||
if args.success:
|
||||
global success_file
|
||||
success_file = args.success
|
||||
|
||||
logging.basicConfig(level=args.loglevel)
|
||||
|
||||
try:
|
||||
if not clf.open("usb"):
|
||||
print("Could not open connection with an NFC device")
|
||||
raise SystemExit
|
||||
|
||||
if args.command == "write-config":
|
||||
wps_write_config_tag(clf, wait_remove=not args.no_wait)
|
||||
raise SystemExit
|
||||
|
||||
if args.command == "write-password":
|
||||
wps_write_password_tag(clf, wait_remove=not args.no_wait)
|
||||
raise SystemExit
|
||||
|
||||
global continue_loop
|
||||
while continue_loop:
|
||||
print("Waiting for a tag or peer to be touched")
|
||||
wait_connection = True
|
||||
try:
|
||||
if not clf.connect(rdwr={'on-connect': rdwr_connected},
|
||||
llcp={'on-startup': llcp_startup,
|
||||
'on-connect': llcp_connected}):
|
||||
break
|
||||
except Exception as e:
|
||||
print("clf.connect failed")
|
||||
|
||||
global srv
|
||||
if only_one and srv and srv.success:
|
||||
raise SystemExit
|
||||
|
||||
except KeyboardInterrupt:
|
||||
raise SystemExit
|
||||
finally:
|
||||
clf.close()
|
||||
|
||||
raise SystemExit
|
||||
|
||||
if __name__ == '__main__':
|
||||
main()
|
|
@ -0,0 +1,4 @@
|
|||
hs20-osu-client
|
||||
SP
|
||||
osu-ca.pem
|
||||
spp.xsd
|
|
@ -0,0 +1,81 @@
|
|||
LOCAL_PATH := $(call my-dir)
|
||||
|
||||
INCLUDES = $(LOCAL_PATH)
|
||||
INCLUDES += $(LOCAL_PATH)/../../src/utils
|
||||
INCLUDES += $(LOCAL_PATH)/../../src/common
|
||||
INCLUDES += $(LOCAL_PATH)/../../src
|
||||
INCLUDES += external/libxml2/include
|
||||
INCLUDES += external/curl/include
|
||||
INCLUDES += external/webkit/Source/WebKit/gtk
|
||||
|
||||
# We try to keep this compiling against older platform versions.
|
||||
# The new icu location (external/icu) exports its own headers, but
|
||||
# the older versions in external/icu4c don't, and we need to add those
|
||||
# headers to the include path by hand.
|
||||
ifeq ($(wildcard external/icu),)
|
||||
INCLUDES += external/icu4c/common
|
||||
else
|
||||
# The LOCAL_EXPORT_C_INCLUDE_DIRS from ICU did not seem to fully resolve the
|
||||
# build (e.g., "mm -B" failed to build, but following that with "mm" allowed
|
||||
# the build to complete). For now, add the include directory manually here for
|
||||
# Android 5.0.
|
||||
ver = $(filter 5.0%,$(PLATFORM_VERSION))
|
||||
ifneq (,$(strip $(ver)))
|
||||
INCLUDES += external/icu/icu4c/source/common
|
||||
endif
|
||||
endif
|
||||
|
||||
|
||||
L_CFLAGS += -DCONFIG_CTRL_IFACE
|
||||
L_CFLAGS += -DCONFIG_CTRL_IFACE_UNIX
|
||||
L_CFLAGS += -DCONFIG_CTRL_IFACE_CLIENT_DIR=\"/data/misc/wifi/sockets\"
|
||||
|
||||
OBJS = spp_client.c
|
||||
OBJS += oma_dm_client.c
|
||||
OBJS += osu_client.c
|
||||
OBJS += est.c
|
||||
OBJS += ../../src/common/wpa_ctrl.c
|
||||
OBJS += ../../src/common/wpa_helpers.c
|
||||
OBJS += ../../src/utils/xml-utils.c
|
||||
#OBJS += ../../src/utils/browser-android.c
|
||||
OBJS += ../../src/utils/browser-wpadebug.c
|
||||
OBJS += ../../src/utils/wpabuf.c
|
||||
OBJS += ../../src/utils/eloop.c
|
||||
OBJS += ../../src/wps/httpread.c
|
||||
OBJS += ../../src/wps/http_server.c
|
||||
OBJS += ../../src/utils/xml_libxml2.c
|
||||
OBJS += ../../src/utils/http_curl.c
|
||||
OBJS += ../../src/utils/base64.c
|
||||
OBJS += ../../src/utils/os_unix.c
|
||||
L_CFLAGS += -DCONFIG_DEBUG_FILE
|
||||
OBJS += ../../src/utils/wpa_debug.c
|
||||
OBJS += ../../src/utils/common.c
|
||||
OBJS += ../../src/crypto/crypto_internal.c
|
||||
OBJS += ../../src/crypto/md5-internal.c
|
||||
OBJS += ../../src/crypto/sha1-internal.c
|
||||
OBJS += ../../src/crypto/sha256-internal.c
|
||||
OBJS += ../../src/crypto/tls_openssl_ocsp.c
|
||||
|
||||
L_CFLAGS += -DEAP_TLS_OPENSSL
|
||||
|
||||
L_CFLAGS += -Wno-unused-parameter
|
||||
|
||||
|
||||
########################
|
||||
include $(CLEAR_VARS)
|
||||
LOCAL_MODULE := hs20-osu-client
|
||||
LOCAL_MODULE_TAGS := optional
|
||||
|
||||
LOCAL_SHARED_LIBRARIES := libc libcutils
|
||||
LOCAL_SHARED_LIBRARIES += libcrypto libssl
|
||||
#LOCAL_SHARED_LIBRARIES += libxml2
|
||||
LOCAL_STATIC_LIBRARIES += libxml2
|
||||
LOCAL_SHARED_LIBRARIES += libicuuc
|
||||
LOCAL_SHARED_LIBRARIES += libcurl
|
||||
|
||||
LOCAL_CFLAGS := $(L_CFLAGS)
|
||||
LOCAL_SRC_FILES := $(OBJS)
|
||||
LOCAL_C_INCLUDES := $(INCLUDES)
|
||||
include $(BUILD_EXECUTABLE)
|
||||
|
||||
########################
|
|
@ -0,0 +1,106 @@
|
|||
all: hs20-osu-client
|
||||
|
||||
ifndef CC
|
||||
CC=gcc
|
||||
endif
|
||||
|
||||
ifndef LDO
|
||||
LDO=$(CC)
|
||||
endif
|
||||
|
||||
ifeq ($(QUIET), 1)
|
||||
Q=@
|
||||
E=true
|
||||
else
|
||||
Q=@
|
||||
E=echo
|
||||
ifeq ($(V), 1)
|
||||
Q=
|
||||
E=true
|
||||
endif
|
||||
endif
|
||||
|
||||
ifndef CFLAGS
|
||||
CFLAGS = -MMD -O2 -Wall -g
|
||||
endif
|
||||
|
||||
CFLAGS += -I../../src/utils
|
||||
CFLAGS += -I../../src/common
|
||||
CFLAGS += -I../../src
|
||||
|
||||
ifndef CONFIG_NO_BROWSER
|
||||
ifndef CONFIG_BROWSER_SYSTEM
|
||||
GTKCFLAGS := $(shell pkg-config --cflags gtk+-3.0 webkitgtk-3.0)
|
||||
GTKLIBS := $(shell pkg-config --libs gtk+-3.0 webkitgtk-3.0)
|
||||
CFLAGS += $(GTKCFLAGS)
|
||||
LIBS += $(GTKLIBS)
|
||||
endif
|
||||
endif
|
||||
|
||||
OBJS=spp_client.o
|
||||
OBJS += oma_dm_client.o
|
||||
OBJS += osu_client.o
|
||||
OBJS += est.o
|
||||
OBJS += ../../src/utils/xml-utils.o
|
||||
CFLAGS += -DCONFIG_CTRL_IFACE
|
||||
CFLAGS += -DCONFIG_CTRL_IFACE_UNIX
|
||||
OBJS += ../../src/common/wpa_ctrl.o ../../src/common/wpa_helpers.o
|
||||
ifdef CONFIG_NO_BROWSER
|
||||
CFLAGS += -DCONFIG_NO_BROWSER
|
||||
else
|
||||
ifdef CONFIG_BROWSER_SYSTEM
|
||||
OBJS += ../../src/utils/eloop.o
|
||||
OBJS += ../../src/utils/wpabuf.o
|
||||
OBJS += ../../src/wps/httpread.o
|
||||
OBJS += ../../src/wps/http_server.o
|
||||
OBJS += ../../src/utils/browser-system.o
|
||||
else
|
||||
OBJS += ../../src/utils/browser.o
|
||||
endif
|
||||
endif
|
||||
OBJS += ../../src/utils/xml_libxml2.o
|
||||
OBJS += ../../src/utils/http_curl.o
|
||||
OBJS += ../../src/utils/base64.o
|
||||
OBJS += ../../src/utils/os_unix.o
|
||||
CFLAGS += -DCONFIG_DEBUG_FILE
|
||||
OBJS += ../../src/utils/wpa_debug.o
|
||||
OBJS += ../../src/utils/common.o
|
||||
OBJS += ../../src/crypto/crypto_internal.o
|
||||
OBJS += ../../src/crypto/md5-internal.o
|
||||
OBJS += ../../src/crypto/sha1-internal.o
|
||||
OBJS += ../../src/crypto/sha256-internal.o
|
||||
|
||||
CFLAGS += $(shell xml2-config --cflags)
|
||||
LIBS += $(shell xml2-config --libs)
|
||||
|
||||
# Allow static/custom linking of libcurl.
|
||||
ifdef CUST_CURL_LINKAGE
|
||||
LIBS += ${CUST_CURL_LINKAGE}
|
||||
else
|
||||
LIBS += -lcurl
|
||||
endif
|
||||
|
||||
CFLAGS += -DEAP_TLS_OPENSSL
|
||||
OBJS += ../../src/crypto/tls_openssl_ocsp.o
|
||||
LIBS += -lssl -lcrypto
|
||||
|
||||
hs20-osu-client: $(OBJS)
|
||||
$(Q)$(LDO) $(LDFLAGS) -o hs20-osu-client $(OBJS) $(LIBS)
|
||||
@$(E) " LD " $@
|
||||
|
||||
%.o: %.c
|
||||
$(Q)$(CC) -c -o $@ $(CFLAGS) $<
|
||||
@$(E) " CC " $<
|
||||
|
||||
clean:
|
||||
rm -f core *~ *.o *.d hs20-osu-client
|
||||
rm -f ../../src/utils/*.o
|
||||
rm -f ../../src/utils/*.d
|
||||
rm -f ../../src/common/*.o
|
||||
rm -f ../../src/common/*.d
|
||||
rm -f ../../src/crypto/*.o
|
||||
rm -f ../../src/crypto/*.d
|
||||
rm -f ../../src/wps/*.o
|
||||
rm -f ../../src/wps/*.d
|
||||
|
||||
-include $(OBJS:%.o=%.d)
|
|
@ -0,0 +1,47 @@
|
|||
<DevDetail xmlns="urn:oma:mo:oma-dm-devdetail:1.0">
|
||||
<Ext>
|
||||
<org.wi-fi>
|
||||
<Wi-Fi>
|
||||
<EAPMethodList>
|
||||
<EAPMethod1>
|
||||
<EAPType>13</EAPType>
|
||||
</EAPMethod1>
|
||||
<EAPMethod2>
|
||||
<EAPType>21</EAPType>
|
||||
<InnerMethod>MS-CHAP-V2</InnerMethod>
|
||||
</EAPMethod2>
|
||||
<EAPMethod3>
|
||||
<EAPType>18</EAPType>
|
||||
</EAPMethod3>
|
||||
<EAPMethod4>
|
||||
<EAPType>23</EAPType>
|
||||
</EAPMethod4>
|
||||
<EAPMethod5>
|
||||
<EAPType>50</EAPType>
|
||||
</EAPMethod5>
|
||||
</EAPMethodList>
|
||||
<ManufacturingCertificate>false</ManufacturingCertificate>
|
||||
<Wi-FiMACAddress>020102030405</Wi-FiMACAddress>
|
||||
<IMSI>310026000000000</IMSI>
|
||||
<IMEI_MEID>imei:490123456789012</IMEI_MEID>
|
||||
<ClientTriggerRedirectURI>http://localhost:12345/</ClientTriggerRedirectURI>
|
||||
<Ops>
|
||||
<launchBrowserToURI></launchBrowserToURI>
|
||||
<negotiateClientCertTLS></negotiateClientCertTLS>
|
||||
<getCertificate></getCertificate>
|
||||
</Ops>
|
||||
</Wi-Fi>
|
||||
</org.wi-fi>
|
||||
</Ext>
|
||||
<URI>
|
||||
<MaxDepth>0</MaxDepth>
|
||||
<MaxTotLen>0</MaxTotLen>
|
||||
<MaxSegLen>0</MaxSegLen>
|
||||
</URI>
|
||||
<DevType>MobilePhone</DevType>
|
||||
<OEM>Manufacturer</OEM>
|
||||
<FwV>1.0</FwV>
|
||||
<SwV>1.0</SwV>
|
||||
<HwV>1.0</HwV>
|
||||
<LrgObj>false</LrgObj>
|
||||
</DevDetail>
|
|
@ -0,0 +1,7 @@
|
|||
<DevInfo xmlns="urn:oma:mo:oma-dm-devinfo:1.0">
|
||||
<DevId>urn:Example:HS20-station:123456</DevId>
|
||||
<Man>Manufacturer</Man>
|
||||
<Mod>HS20-station</Mod>
|
||||
<DmV>1.2</DmV>
|
||||
<Lang>en</Lang>
|
||||
</DevInfo>
|
|
@ -0,0 +1,770 @@
|
|||
/*
|
||||
* Hotspot 2.0 OSU client - EST client
|
||||
* Copyright (c) 2012-2014, Qualcomm Atheros, Inc.
|
||||
*
|
||||
* This software may be distributed under the terms of the BSD license.
|
||||
* See README for more details.
|
||||
*/
|
||||
|
||||
#include "includes.h"
|
||||
#include <openssl/err.h>
|
||||
#include <openssl/evp.h>
|
||||
#include <openssl/pem.h>
|
||||
#include <openssl/pkcs7.h>
|
||||
#include <openssl/rsa.h>
|
||||
#include <openssl/asn1.h>
|
||||
#include <openssl/asn1t.h>
|
||||
#include <openssl/x509.h>
|
||||
#include <openssl/x509v3.h>
|
||||
#include <openssl/opensslv.h>
|
||||
#ifdef OPENSSL_IS_BORINGSSL
|
||||
#include <openssl/buf.h>
|
||||
#endif /* OPENSSL_IS_BORINGSSL */
|
||||
|
||||
#include "common.h"
|
||||
#include "utils/base64.h"
|
||||
#include "utils/xml-utils.h"
|
||||
#include "utils/http-utils.h"
|
||||
#include "osu_client.h"
|
||||
|
||||
|
||||
static int pkcs7_to_cert(struct hs20_osu_client *ctx, const u8 *pkcs7,
|
||||
size_t len, char *pem_file, char *der_file)
|
||||
{
|
||||
#ifdef OPENSSL_IS_BORINGSSL
|
||||
CBS pkcs7_cbs;
|
||||
#else /* OPENSSL_IS_BORINGSSL */
|
||||
PKCS7 *p7 = NULL;
|
||||
const unsigned char *p = pkcs7;
|
||||
#endif /* OPENSSL_IS_BORINGSSL */
|
||||
STACK_OF(X509) *certs;
|
||||
int i, num, ret = -1;
|
||||
BIO *out = NULL;
|
||||
|
||||
#ifdef OPENSSL_IS_BORINGSSL
|
||||
certs = sk_X509_new_null();
|
||||
if (!certs)
|
||||
goto fail;
|
||||
CBS_init(&pkcs7_cbs, pkcs7, len);
|
||||
if (!PKCS7_get_certificates(certs, &pkcs7_cbs)) {
|
||||
wpa_printf(MSG_INFO, "Could not parse PKCS#7 object: %s",
|
||||
ERR_error_string(ERR_get_error(), NULL));
|
||||
write_result(ctx, "Could not parse PKCS#7 object from EST");
|
||||
goto fail;
|
||||
}
|
||||
#else /* OPENSSL_IS_BORINGSSL */
|
||||
p7 = d2i_PKCS7(NULL, &p, len);
|
||||
if (p7 == NULL) {
|
||||
wpa_printf(MSG_INFO, "Could not parse PKCS#7 object: %s",
|
||||
ERR_error_string(ERR_get_error(), NULL));
|
||||
write_result(ctx, "Could not parse PKCS#7 object from EST");
|
||||
goto fail;
|
||||
}
|
||||
|
||||
switch (OBJ_obj2nid(p7->type)) {
|
||||
case NID_pkcs7_signed:
|
||||
certs = p7->d.sign->cert;
|
||||
break;
|
||||
case NID_pkcs7_signedAndEnveloped:
|
||||
certs = p7->d.signed_and_enveloped->cert;
|
||||
break;
|
||||
default:
|
||||
certs = NULL;
|
||||
break;
|
||||
}
|
||||
#endif /* OPENSSL_IS_BORINGSSL */
|
||||
|
||||
if (!certs || ((num = sk_X509_num(certs)) == 0)) {
|
||||
wpa_printf(MSG_INFO, "No certificates found in PKCS#7 object");
|
||||
write_result(ctx, "No certificates found in PKCS#7 object");
|
||||
goto fail;
|
||||
}
|
||||
|
||||
if (der_file) {
|
||||
FILE *f = fopen(der_file, "wb");
|
||||
if (f == NULL)
|
||||
goto fail;
|
||||
i2d_X509_fp(f, sk_X509_value(certs, 0));
|
||||
fclose(f);
|
||||
}
|
||||
|
||||
if (pem_file) {
|
||||
out = BIO_new(BIO_s_file());
|
||||
if (out == NULL ||
|
||||
BIO_write_filename(out, pem_file) <= 0)
|
||||
goto fail;
|
||||
|
||||
for (i = 0; i < num; i++) {
|
||||
X509 *cert = sk_X509_value(certs, i);
|
||||
X509_print(out, cert);
|
||||
PEM_write_bio_X509(out, cert);
|
||||
BIO_puts(out, "\n");
|
||||
}
|
||||
}
|
||||
|
||||
ret = 0;
|
||||
|
||||
fail:
|
||||
#ifdef OPENSSL_IS_BORINGSSL
|
||||
if (certs)
|
||||
sk_X509_pop_free(certs, X509_free);
|
||||
#else /* OPENSSL_IS_BORINGSSL */
|
||||
PKCS7_free(p7);
|
||||
#endif /* OPENSSL_IS_BORINGSSL */
|
||||
if (out)
|
||||
BIO_free_all(out);
|
||||
|
||||
return ret;
|
||||
}
|
||||
|
||||
|
||||
int est_load_cacerts(struct hs20_osu_client *ctx, const char *url)
|
||||
{
|
||||
char *buf, *resp;
|
||||
size_t buflen;
|
||||
unsigned char *pkcs7;
|
||||
size_t pkcs7_len, resp_len;
|
||||
int res;
|
||||
|
||||
buflen = os_strlen(url) + 100;
|
||||
buf = os_malloc(buflen);
|
||||
if (buf == NULL)
|
||||
return -1;
|
||||
|
||||
os_snprintf(buf, buflen, "%s/cacerts", url);
|
||||
wpa_printf(MSG_INFO, "Download EST cacerts from %s", buf);
|
||||
write_summary(ctx, "Download EST cacerts from %s", buf);
|
||||
ctx->no_osu_cert_validation = 1;
|
||||
http_ocsp_set(ctx->http, 1);
|
||||
res = http_download_file(ctx->http, buf, "Cert/est-cacerts.txt",
|
||||
ctx->ca_fname);
|
||||
http_ocsp_set(ctx->http,
|
||||
(ctx->workarounds & WORKAROUND_OCSP_OPTIONAL) ? 1 : 2);
|
||||
ctx->no_osu_cert_validation = 0;
|
||||
if (res < 0) {
|
||||
wpa_printf(MSG_INFO, "Failed to download EST cacerts from %s",
|
||||
buf);
|
||||
write_result(ctx, "Failed to download EST cacerts from %s",
|
||||
buf);
|
||||
os_free(buf);
|
||||
return -1;
|
||||
}
|
||||
os_free(buf);
|
||||
|
||||
resp = os_readfile("Cert/est-cacerts.txt", &resp_len);
|
||||
if (resp == NULL) {
|
||||
wpa_printf(MSG_INFO, "Could not read Cert/est-cacerts.txt");
|
||||
write_result(ctx, "Could not read EST cacerts");
|
||||
return -1;
|
||||
}
|
||||
|
||||
pkcs7 = base64_decode((unsigned char *) resp, resp_len, &pkcs7_len);
|
||||
if (pkcs7 && pkcs7_len < resp_len / 2) {
|
||||
wpa_printf(MSG_INFO, "Too short base64 decode (%u bytes; downloaded %u bytes) - assume this was binary",
|
||||
(unsigned int) pkcs7_len, (unsigned int) resp_len);
|
||||
os_free(pkcs7);
|
||||
pkcs7 = NULL;
|
||||
}
|
||||
if (pkcs7 == NULL) {
|
||||
wpa_printf(MSG_INFO, "EST workaround - Could not decode base64, assume this is DER encoded PKCS7");
|
||||
pkcs7 = os_malloc(resp_len);
|
||||
if (pkcs7) {
|
||||
os_memcpy(pkcs7, resp, resp_len);
|
||||
pkcs7_len = resp_len;
|
||||
}
|
||||
}
|
||||
os_free(resp);
|
||||
|
||||
if (pkcs7 == NULL) {
|
||||
wpa_printf(MSG_INFO, "Could not fetch PKCS7 cacerts");
|
||||
write_result(ctx, "Could not fetch EST PKCS#7 cacerts");
|
||||
return -1;
|
||||
}
|
||||
|
||||
res = pkcs7_to_cert(ctx, pkcs7, pkcs7_len, "Cert/est-cacerts.pem",
|
||||
NULL);
|
||||
os_free(pkcs7);
|
||||
if (res < 0) {
|
||||
wpa_printf(MSG_INFO, "Could not parse CA certs from PKCS#7 cacerts response");
|
||||
write_result(ctx, "Could not parse CA certs from EST PKCS#7 cacerts response");
|
||||
return -1;
|
||||
}
|
||||
unlink("Cert/est-cacerts.txt");
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
|
||||
/*
|
||||
* CsrAttrs ::= SEQUENCE SIZE (0..MAX) OF AttrOrOID
|
||||
*
|
||||
* AttrOrOID ::= CHOICE {
|
||||
* oid OBJECT IDENTIFIER,
|
||||
* attribute Attribute }
|
||||
*
|
||||
* Attribute ::= SEQUENCE {
|
||||
* type OBJECT IDENTIFIER,
|
||||
* values SET SIZE(1..MAX) OF OBJECT IDENTIFIER }
|
||||
*/
|
||||
|
||||
typedef struct {
|
||||
ASN1_OBJECT *type;
|
||||
STACK_OF(ASN1_OBJECT) *values;
|
||||
} Attribute;
|
||||
|
||||
typedef struct {
|
||||
int type;
|
||||
union {
|
||||
ASN1_OBJECT *oid;
|
||||
Attribute *attribute;
|
||||
} d;
|
||||
} AttrOrOID;
|
||||
|
||||
#if OPENSSL_VERSION_NUMBER >= 0x10100000L && !defined(OPENSSL_IS_BORINGSSL)
|
||||
DEFINE_STACK_OF(AttrOrOID)
|
||||
#endif
|
||||
|
||||
typedef struct {
|
||||
int type;
|
||||
STACK_OF(AttrOrOID) *attrs;
|
||||
} CsrAttrs;
|
||||
|
||||
ASN1_SEQUENCE(Attribute) = {
|
||||
ASN1_SIMPLE(Attribute, type, ASN1_OBJECT),
|
||||
ASN1_SET_OF(Attribute, values, ASN1_OBJECT)
|
||||
} ASN1_SEQUENCE_END(Attribute);
|
||||
|
||||
ASN1_CHOICE(AttrOrOID) = {
|
||||
ASN1_SIMPLE(AttrOrOID, d.oid, ASN1_OBJECT),
|
||||
ASN1_SIMPLE(AttrOrOID, d.attribute, Attribute)
|
||||
} ASN1_CHOICE_END(AttrOrOID);
|
||||
|
||||
ASN1_CHOICE(CsrAttrs) = {
|
||||
ASN1_SEQUENCE_OF(CsrAttrs, attrs, AttrOrOID)
|
||||
} ASN1_CHOICE_END(CsrAttrs);
|
||||
|
||||
IMPLEMENT_ASN1_FUNCTIONS(CsrAttrs);
|
||||
|
||||
|
||||
static void add_csrattrs_oid(struct hs20_osu_client *ctx, ASN1_OBJECT *oid,
|
||||
STACK_OF(X509_EXTENSION) *exts)
|
||||
{
|
||||
char txt[100];
|
||||
int res;
|
||||
|
||||
if (!oid)
|
||||
return;
|
||||
|
||||
res = OBJ_obj2txt(txt, sizeof(txt), oid, 1);
|
||||
if (res < 0 || res >= (int) sizeof(txt))
|
||||
return;
|
||||
|
||||
if (os_strcmp(txt, "1.2.840.113549.1.9.7") == 0) {
|
||||
wpa_printf(MSG_INFO, "TODO: csrattr challengePassword");
|
||||
} else if (os_strcmp(txt, "1.2.840.113549.1.1.11") == 0) {
|
||||
wpa_printf(MSG_INFO, "csrattr sha256WithRSAEncryption");
|
||||
} else {
|
||||
wpa_printf(MSG_INFO, "Ignore unsupported csrattr oid %s", txt);
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
static void add_csrattrs_ext_req(struct hs20_osu_client *ctx,
|
||||
STACK_OF(ASN1_OBJECT) *values,
|
||||
STACK_OF(X509_EXTENSION) *exts)
|
||||
{
|
||||
char txt[100];
|
||||
int i, num, res;
|
||||
|
||||
num = sk_ASN1_OBJECT_num(values);
|
||||
for (i = 0; i < num; i++) {
|
||||
ASN1_OBJECT *oid = sk_ASN1_OBJECT_value(values, i);
|
||||
|
||||
res = OBJ_obj2txt(txt, sizeof(txt), oid, 1);
|
||||
if (res < 0 || res >= (int) sizeof(txt))
|
||||
continue;
|
||||
|
||||
if (os_strcmp(txt, "1.3.6.1.1.1.1.22") == 0) {
|
||||
wpa_printf(MSG_INFO, "TODO: extReq macAddress");
|
||||
} else if (os_strcmp(txt, "1.3.6.1.4.1.40808.1.1.3") == 0) {
|
||||
wpa_printf(MSG_INFO, "TODO: extReq imei");
|
||||
} else if (os_strcmp(txt, "1.3.6.1.4.1.40808.1.1.4") == 0) {
|
||||
wpa_printf(MSG_INFO, "TODO: extReq meid");
|
||||
} else if (os_strcmp(txt, "1.3.6.1.4.1.40808.1.1.5") == 0) {
|
||||
wpa_printf(MSG_INFO, "TODO: extReq DevId");
|
||||
} else {
|
||||
wpa_printf(MSG_INFO, "Ignore unsupported cstattr extensionsRequest %s",
|
||||
txt);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
static void add_csrattrs_attr(struct hs20_osu_client *ctx, Attribute *attr,
|
||||
STACK_OF(X509_EXTENSION) *exts)
|
||||
{
|
||||
char txt[100], txt2[100];
|
||||
int i, num, res;
|
||||
|
||||
if (!attr || !attr->type || !attr->values)
|
||||
return;
|
||||
|
||||
res = OBJ_obj2txt(txt, sizeof(txt), attr->type, 1);
|
||||
if (res < 0 || res >= (int) sizeof(txt))
|
||||
return;
|
||||
|
||||
if (os_strcmp(txt, "1.2.840.113549.1.9.14") == 0) {
|
||||
add_csrattrs_ext_req(ctx, attr->values, exts);
|
||||
return;
|
||||
}
|
||||
|
||||
num = sk_ASN1_OBJECT_num(attr->values);
|
||||
for (i = 0; i < num; i++) {
|
||||
ASN1_OBJECT *oid = sk_ASN1_OBJECT_value(attr->values, i);
|
||||
|
||||
res = OBJ_obj2txt(txt2, sizeof(txt2), oid, 1);
|
||||
if (res < 0 || res >= (int) sizeof(txt2))
|
||||
continue;
|
||||
|
||||
wpa_printf(MSG_INFO, "Ignore unsupported cstattr::attr %s oid %s",
|
||||
txt, txt2);
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
static void add_csrattrs(struct hs20_osu_client *ctx, CsrAttrs *csrattrs,
|
||||
STACK_OF(X509_EXTENSION) *exts)
|
||||
{
|
||||
int i, num;
|
||||
|
||||
if (!csrattrs || ! csrattrs->attrs)
|
||||
return;
|
||||
|
||||
#ifdef OPENSSL_IS_BORINGSSL
|
||||
num = sk_num(CHECKED_CAST(_STACK *, STACK_OF(AttrOrOID) *,
|
||||
csrattrs->attrs));
|
||||
for (i = 0; i < num; i++) {
|
||||
AttrOrOID *ao = sk_value(
|
||||
CHECKED_CAST(_STACK *, const STACK_OF(AttrOrOID) *,
|
||||
csrattrs->attrs), i);
|
||||
switch (ao->type) {
|
||||
case 0:
|
||||
add_csrattrs_oid(ctx, ao->d.oid, exts);
|
||||
break;
|
||||
case 1:
|
||||
add_csrattrs_attr(ctx, ao->d.attribute, exts);
|
||||
break;
|
||||
}
|
||||
}
|
||||
#else /* OPENSSL_IS_BORINGSSL */
|
||||
#if OPENSSL_VERSION_NUMBER >= 0x10100000L && !defined(OPENSSL_IS_BORINGSSL)
|
||||
num = sk_AttrOrOID_num(csrattrs->attrs);
|
||||
#else
|
||||
num = SKM_sk_num(AttrOrOID, csrattrs->attrs);
|
||||
#endif
|
||||
for (i = 0; i < num; i++) {
|
||||
#if OPENSSL_VERSION_NUMBER >= 0x10100000L && !defined(OPENSSL_IS_BORINGSSL)
|
||||
AttrOrOID *ao = sk_AttrOrOID_value(csrattrs->attrs, i);
|
||||
#else
|
||||
AttrOrOID *ao = SKM_sk_value(AttrOrOID, csrattrs->attrs, i);
|
||||
#endif
|
||||
switch (ao->type) {
|
||||
case 0:
|
||||
add_csrattrs_oid(ctx, ao->d.oid, exts);
|
||||
break;
|
||||
case 1:
|
||||
add_csrattrs_attr(ctx, ao->d.attribute, exts);
|
||||
break;
|
||||
}
|
||||
}
|
||||
#endif /* OPENSSL_IS_BORINGSSL */
|
||||
}
|
||||
|
||||
|
||||
static int generate_csr(struct hs20_osu_client *ctx, char *key_pem,
|
||||
char *csr_pem, char *est_req, char *old_cert,
|
||||
CsrAttrs *csrattrs)
|
||||
{
|
||||
EVP_PKEY_CTX *pctx = NULL;
|
||||
EVP_PKEY *pkey = NULL;
|
||||
RSA *rsa;
|
||||
X509_REQ *req = NULL;
|
||||
int ret = -1;
|
||||
unsigned int val;
|
||||
X509_NAME *subj = NULL;
|
||||
char name[100];
|
||||
STACK_OF(X509_EXTENSION) *exts = NULL;
|
||||
X509_EXTENSION *ex;
|
||||
BIO *out;
|
||||
CONF *ctmp = NULL;
|
||||
|
||||
wpa_printf(MSG_INFO, "Generate RSA private key");
|
||||
write_summary(ctx, "Generate RSA private key");
|
||||
pctx = EVP_PKEY_CTX_new_id(EVP_PKEY_RSA, NULL);
|
||||
if (!pctx)
|
||||
return -1;
|
||||
|
||||
if (EVP_PKEY_keygen_init(pctx) <= 0)
|
||||
goto fail;
|
||||
|
||||
if (EVP_PKEY_CTX_set_rsa_keygen_bits(pctx, 2048) <= 0)
|
||||
goto fail;
|
||||
|
||||
if (EVP_PKEY_keygen(pctx, &pkey) <= 0)
|
||||
goto fail;
|
||||
EVP_PKEY_CTX_free(pctx);
|
||||
pctx = NULL;
|
||||
|
||||
rsa = EVP_PKEY_get1_RSA(pkey);
|
||||
if (rsa == NULL)
|
||||
goto fail;
|
||||
|
||||
if (key_pem) {
|
||||
FILE *f = fopen(key_pem, "wb");
|
||||
if (f == NULL)
|
||||
goto fail;
|
||||
if (!PEM_write_RSAPrivateKey(f, rsa, NULL, NULL, 0, NULL,
|
||||
NULL)) {
|
||||
wpa_printf(MSG_INFO, "Could not write private key: %s",
|
||||
ERR_error_string(ERR_get_error(), NULL));
|
||||
fclose(f);
|
||||
goto fail;
|
||||
}
|
||||
fclose(f);
|
||||
}
|
||||
|
||||
wpa_printf(MSG_INFO, "Generate CSR");
|
||||
write_summary(ctx, "Generate CSR");
|
||||
req = X509_REQ_new();
|
||||
if (req == NULL)
|
||||
goto fail;
|
||||
|
||||
if (old_cert) {
|
||||
FILE *f;
|
||||
X509 *cert;
|
||||
int res;
|
||||
|
||||
f = fopen(old_cert, "r");
|
||||
if (f == NULL)
|
||||
goto fail;
|
||||
cert = PEM_read_X509(f, NULL, NULL, NULL);
|
||||
fclose(f);
|
||||
|
||||
if (cert == NULL)
|
||||
goto fail;
|
||||
res = X509_REQ_set_subject_name(req,
|
||||
X509_get_subject_name(cert));
|
||||
X509_free(cert);
|
||||
if (!res)
|
||||
goto fail;
|
||||
} else {
|
||||
os_get_random((u8 *) &val, sizeof(val));
|
||||
os_snprintf(name, sizeof(name), "cert-user-%u", val);
|
||||
subj = X509_NAME_new();
|
||||
if (subj == NULL ||
|
||||
!X509_NAME_add_entry_by_txt(subj, "CN", MBSTRING_ASC,
|
||||
(unsigned char *) name,
|
||||
-1, -1, 0) ||
|
||||
!X509_REQ_set_subject_name(req, subj))
|
||||
goto fail;
|
||||
X509_NAME_free(subj);
|
||||
subj = NULL;
|
||||
}
|
||||
|
||||
if (!X509_REQ_set_pubkey(req, pkey))
|
||||
goto fail;
|
||||
|
||||
exts = sk_X509_EXTENSION_new_null();
|
||||
if (!exts)
|
||||
goto fail;
|
||||
|
||||
ex = X509V3_EXT_nconf_nid(ctmp, NULL, NID_basic_constraints,
|
||||
"CA:FALSE");
|
||||
if (ex == NULL ||
|
||||
!sk_X509_EXTENSION_push(exts, ex))
|
||||
goto fail;
|
||||
|
||||
ex = X509V3_EXT_nconf_nid(ctmp, NULL, NID_key_usage,
|
||||
"nonRepudiation,digitalSignature,keyEncipherment");
|
||||
if (ex == NULL ||
|
||||
!sk_X509_EXTENSION_push(exts, ex))
|
||||
goto fail;
|
||||
|
||||
ex = X509V3_EXT_nconf_nid(ctmp, NULL, NID_ext_key_usage,
|
||||
"1.3.6.1.4.1.40808.1.1.2");
|
||||
if (ex == NULL ||
|
||||
!sk_X509_EXTENSION_push(exts, ex))
|
||||
goto fail;
|
||||
|
||||
add_csrattrs(ctx, csrattrs, exts);
|
||||
|
||||
if (!X509_REQ_add_extensions(req, exts))
|
||||
goto fail;
|
||||
sk_X509_EXTENSION_pop_free(exts, X509_EXTENSION_free);
|
||||
exts = NULL;
|
||||
|
||||
if (!X509_REQ_sign(req, pkey, EVP_sha256()))
|
||||
goto fail;
|
||||
|
||||
out = BIO_new(BIO_s_mem());
|
||||
if (out) {
|
||||
char *txt;
|
||||
size_t rlen;
|
||||
|
||||
#if !defined(ANDROID) || !defined(OPENSSL_IS_BORINGSSL)
|
||||
X509_REQ_print(out, req);
|
||||
#endif
|
||||
rlen = BIO_ctrl_pending(out);
|
||||
txt = os_malloc(rlen + 1);
|
||||
if (txt) {
|
||||
int res = BIO_read(out, txt, rlen);
|
||||
if (res > 0) {
|
||||
txt[res] = '\0';
|
||||
wpa_printf(MSG_MSGDUMP, "OpenSSL: Certificate request:\n%s",
|
||||
txt);
|
||||
}
|
||||
os_free(txt);
|
||||
}
|
||||
BIO_free(out);
|
||||
}
|
||||
|
||||
if (csr_pem) {
|
||||
FILE *f = fopen(csr_pem, "w");
|
||||
if (f == NULL)
|
||||
goto fail;
|
||||
#if !defined(ANDROID) || !defined(OPENSSL_IS_BORINGSSL)
|
||||
X509_REQ_print_fp(f, req);
|
||||
#endif
|
||||
if (!PEM_write_X509_REQ(f, req)) {
|
||||
fclose(f);
|
||||
goto fail;
|
||||
}
|
||||
fclose(f);
|
||||
}
|
||||
|
||||
if (est_req) {
|
||||
BIO *mem = BIO_new(BIO_s_mem());
|
||||
BUF_MEM *ptr;
|
||||
char *pos, *end, *buf_end;
|
||||
FILE *f;
|
||||
|
||||
if (mem == NULL)
|
||||
goto fail;
|
||||
if (!PEM_write_bio_X509_REQ(mem, req)) {
|
||||
BIO_free(mem);
|
||||
goto fail;
|
||||
}
|
||||
|
||||
BIO_get_mem_ptr(mem, &ptr);
|
||||
pos = ptr->data;
|
||||
buf_end = pos + ptr->length;
|
||||
|
||||
/* Remove START/END lines */
|
||||
while (pos < buf_end && *pos != '\n')
|
||||
pos++;
|
||||
if (pos == buf_end) {
|
||||
BIO_free(mem);
|
||||
goto fail;
|
||||
}
|
||||
pos++;
|
||||
|
||||
end = pos;
|
||||
while (end < buf_end && *end != '-')
|
||||
end++;
|
||||
|
||||
f = fopen(est_req, "w");
|
||||
if (f == NULL) {
|
||||
BIO_free(mem);
|
||||
goto fail;
|
||||
}
|
||||
fwrite(pos, end - pos, 1, f);
|
||||
fclose(f);
|
||||
|
||||
BIO_free(mem);
|
||||
}
|
||||
|
||||
ret = 0;
|
||||
fail:
|
||||
if (exts)
|
||||
sk_X509_EXTENSION_pop_free(exts, X509_EXTENSION_free);
|
||||
if (subj)
|
||||
X509_NAME_free(subj);
|
||||
if (req)
|
||||
X509_REQ_free(req);
|
||||
if (pkey)
|
||||
EVP_PKEY_free(pkey);
|
||||
if (pctx)
|
||||
EVP_PKEY_CTX_free(pctx);
|
||||
return ret;
|
||||
}
|
||||
|
||||
|
||||
int est_build_csr(struct hs20_osu_client *ctx, const char *url)
|
||||
{
|
||||
char *buf;
|
||||
size_t buflen;
|
||||
int res;
|
||||
char old_cert_buf[200];
|
||||
char *old_cert = NULL;
|
||||
CsrAttrs *csrattrs = NULL;
|
||||
|
||||
buflen = os_strlen(url) + 100;
|
||||
buf = os_malloc(buflen);
|
||||
if (buf == NULL)
|
||||
return -1;
|
||||
|
||||
os_snprintf(buf, buflen, "%s/csrattrs", url);
|
||||
wpa_printf(MSG_INFO, "Download csrattrs from %s", buf);
|
||||
write_summary(ctx, "Download EST csrattrs from %s", buf);
|
||||
ctx->no_osu_cert_validation = 1;
|
||||
http_ocsp_set(ctx->http, 1);
|
||||
res = http_download_file(ctx->http, buf, "Cert/est-csrattrs.txt",
|
||||
ctx->ca_fname);
|
||||
http_ocsp_set(ctx->http,
|
||||
(ctx->workarounds & WORKAROUND_OCSP_OPTIONAL) ? 1 : 2);
|
||||
ctx->no_osu_cert_validation = 0;
|
||||
os_free(buf);
|
||||
if (res < 0) {
|
||||
wpa_printf(MSG_INFO, "Failed to download EST csrattrs - assume no extra attributes are needed");
|
||||
} else {
|
||||
size_t resp_len;
|
||||
char *resp;
|
||||
unsigned char *attrs;
|
||||
const unsigned char *pos;
|
||||
size_t attrs_len;
|
||||
|
||||
resp = os_readfile("Cert/est-csrattrs.txt", &resp_len);
|
||||
if (resp == NULL) {
|
||||
wpa_printf(MSG_INFO, "Could not read csrattrs");
|
||||
return -1;
|
||||
}
|
||||
|
||||
attrs = base64_decode((unsigned char *) resp, resp_len,
|
||||
&attrs_len);
|
||||
os_free(resp);
|
||||
|
||||
if (attrs == NULL) {
|
||||
wpa_printf(MSG_INFO, "Could not base64 decode csrattrs");
|
||||
return -1;
|
||||
}
|
||||
unlink("Cert/est-csrattrs.txt");
|
||||
|
||||
pos = attrs;
|
||||
csrattrs = d2i_CsrAttrs(NULL, &pos, attrs_len);
|
||||
os_free(attrs);
|
||||
if (csrattrs == NULL) {
|
||||
wpa_printf(MSG_INFO, "Failed to parse csrattrs ASN.1");
|
||||
/* Continue assuming no additional requirements */
|
||||
}
|
||||
}
|
||||
|
||||
if (ctx->client_cert_present) {
|
||||
os_snprintf(old_cert_buf, sizeof(old_cert_buf),
|
||||
"SP/%s/client-cert.pem", ctx->fqdn);
|
||||
old_cert = old_cert_buf;
|
||||
}
|
||||
|
||||
res = generate_csr(ctx, "Cert/privkey-plain.pem", "Cert/est-req.pem",
|
||||
"Cert/est-req.b64", old_cert, csrattrs);
|
||||
if (csrattrs)
|
||||
CsrAttrs_free(csrattrs);
|
||||
|
||||
return res;
|
||||
}
|
||||
|
||||
|
||||
int est_simple_enroll(struct hs20_osu_client *ctx, const char *url,
|
||||
const char *user, const char *pw)
|
||||
{
|
||||
char *buf, *resp, *req, *req2;
|
||||
size_t buflen, resp_len, len, pkcs7_len;
|
||||
unsigned char *pkcs7;
|
||||
char client_cert_buf[200];
|
||||
char client_key_buf[200];
|
||||
const char *client_cert = NULL, *client_key = NULL;
|
||||
int res;
|
||||
|
||||
req = os_readfile("Cert/est-req.b64", &len);
|
||||
if (req == NULL) {
|
||||
wpa_printf(MSG_INFO, "Could not read Cert/req.b64");
|
||||
return -1;
|
||||
}
|
||||
req2 = os_realloc(req, len + 1);
|
||||
if (req2 == NULL) {
|
||||
os_free(req);
|
||||
return -1;
|
||||
}
|
||||
req2[len] = '\0';
|
||||
req = req2;
|
||||
wpa_printf(MSG_DEBUG, "EST simpleenroll request: %s", req);
|
||||
|
||||
buflen = os_strlen(url) + 100;
|
||||
buf = os_malloc(buflen);
|
||||
if (buf == NULL) {
|
||||
os_free(req);
|
||||
return -1;
|
||||
}
|
||||
|
||||
if (ctx->client_cert_present) {
|
||||
os_snprintf(buf, buflen, "%s/simplereenroll", url);
|
||||
os_snprintf(client_cert_buf, sizeof(client_cert_buf),
|
||||
"SP/%s/client-cert.pem", ctx->fqdn);
|
||||
client_cert = client_cert_buf;
|
||||
os_snprintf(client_key_buf, sizeof(client_key_buf),
|
||||
"SP/%s/client-key.pem", ctx->fqdn);
|
||||
client_key = client_key_buf;
|
||||
} else
|
||||
os_snprintf(buf, buflen, "%s/simpleenroll", url);
|
||||
wpa_printf(MSG_INFO, "EST simpleenroll URL: %s", buf);
|
||||
write_summary(ctx, "EST simpleenroll URL: %s", buf);
|
||||
ctx->no_osu_cert_validation = 1;
|
||||
http_ocsp_set(ctx->http, 1);
|
||||
resp = http_post(ctx->http, buf, req, "application/pkcs10",
|
||||
"Content-Transfer-Encoding: base64",
|
||||
ctx->ca_fname, user, pw, client_cert, client_key,
|
||||
&resp_len);
|
||||
http_ocsp_set(ctx->http,
|
||||
(ctx->workarounds & WORKAROUND_OCSP_OPTIONAL) ? 1 : 2);
|
||||
ctx->no_osu_cert_validation = 0;
|
||||
os_free(buf);
|
||||
if (resp == NULL) {
|
||||
wpa_printf(MSG_INFO, "EST certificate enrollment failed");
|
||||
write_result(ctx, "EST certificate enrollment failed");
|
||||
return -1;
|
||||
}
|
||||
wpa_printf(MSG_DEBUG, "EST simpleenroll response: %s", resp);
|
||||
|
||||
pkcs7 = base64_decode((unsigned char *) resp, resp_len, &pkcs7_len);
|
||||
if (pkcs7 == NULL) {
|
||||
wpa_printf(MSG_INFO, "EST workaround - Could not decode base64, assume this is DER encoded PKCS7");
|
||||
pkcs7 = os_malloc(resp_len);
|
||||
if (pkcs7) {
|
||||
os_memcpy(pkcs7, resp, resp_len);
|
||||
pkcs7_len = resp_len;
|
||||
}
|
||||
}
|
||||
os_free(resp);
|
||||
|
||||
if (pkcs7 == NULL) {
|
||||
wpa_printf(MSG_INFO, "Failed to parse simpleenroll base64 response");
|
||||
write_result(ctx, "Failed to parse EST simpleenroll base64 response");
|
||||
return -1;
|
||||
}
|
||||
|
||||
res = pkcs7_to_cert(ctx, pkcs7, pkcs7_len, "Cert/est_cert.pem",
|
||||
"Cert/est_cert.der");
|
||||
os_free(pkcs7);
|
||||
|
||||
if (res < 0) {
|
||||
wpa_printf(MSG_INFO, "EST: Failed to extract certificate from PKCS7 file");
|
||||
write_result(ctx, "EST: Failed to extract certificate from EST PKCS7 file");
|
||||
return -1;
|
||||
}
|
||||
|
||||
wpa_printf(MSG_INFO, "EST simple%senroll completed successfully",
|
||||
ctx->client_cert_present ? "re" : "");
|
||||
write_summary(ctx, "EST simple%senroll completed successfully",
|
||||
ctx->client_cert_present ? "re" : "");
|
||||
|
||||
return 0;
|
||||
}
|
File diff suppressed because it is too large
Load Diff
File diff suppressed because it is too large
Load Diff
|
@ -0,0 +1,119 @@
|
|||
/*
|
||||
* Hotspot 2.0 - OSU client
|
||||
* Copyright (c) 2013-2014, Qualcomm Atheros, Inc.
|
||||
*
|
||||
* This software may be distributed under the terms of the BSD license.
|
||||
* See README for more details.
|
||||
*/
|
||||
|
||||
#ifndef OSU_CLIENT_H
|
||||
#define OSU_CLIENT_H
|
||||
|
||||
#define SPP_NS_URI "http://www.wi-fi.org/specifications/hotspot2dot0/v1.0/spp"
|
||||
|
||||
#define URN_OMA_DM_DEVINFO "urn:oma:mo:oma-dm-devinfo:1.0"
|
||||
#define URN_OMA_DM_DEVDETAIL "urn:oma:mo:oma-dm-devdetail:1.0"
|
||||
#define URN_HS20_DEVDETAIL_EXT "urn:wfa:mo-ext:hotspot2dot0-devdetail-ext:1.0"
|
||||
#define URN_HS20_PPS "urn:wfa:mo:hotspot2dot0-perprovidersubscription:1.0"
|
||||
|
||||
|
||||
#define MAX_OSU_VALS 10
|
||||
|
||||
struct osu_lang_text {
|
||||
char lang[4];
|
||||
char text[253];
|
||||
};
|
||||
|
||||
struct hs20_osu_client {
|
||||
struct xml_node_ctx *xml;
|
||||
struct http_ctx *http;
|
||||
int no_reconnect;
|
||||
char pps_fname[300];
|
||||
char *devid;
|
||||
const char *result_file;
|
||||
const char *summary_file;
|
||||
const char *ifname;
|
||||
const char *ca_fname;
|
||||
int no_osu_cert_validation; /* for EST operations */
|
||||
char *fqdn;
|
||||
char *server_url;
|
||||
struct osu_lang_text friendly_name[MAX_OSU_VALS];
|
||||
size_t friendly_name_count;
|
||||
size_t icon_count;
|
||||
char icon_filename[MAX_OSU_VALS][256];
|
||||
u8 icon_hash[MAX_OSU_VALS][32];
|
||||
int pps_cred_set;
|
||||
int pps_updated;
|
||||
int client_cert_present;
|
||||
char **server_dnsname;
|
||||
size_t server_dnsname_count;
|
||||
const char *osu_ssid; /* Enforced OSU_SSID for testing purposes */
|
||||
#define WORKAROUND_OCSP_OPTIONAL 0x00000001
|
||||
unsigned long int workarounds;
|
||||
};
|
||||
|
||||
|
||||
/* osu_client.c */
|
||||
|
||||
void write_result(struct hs20_osu_client *ctx, const char *fmt, ...)
|
||||
__attribute__ ((format (printf, 2, 3)));
|
||||
void write_summary(struct hs20_osu_client *ctx, const char *fmt, ...)
|
||||
__attribute__ ((format (printf, 2, 3)));
|
||||
|
||||
void debug_dump_node(struct hs20_osu_client *ctx, const char *title,
|
||||
xml_node_t *node);
|
||||
int osu_get_certificate(struct hs20_osu_client *ctx, xml_node_t *getcert);
|
||||
int hs20_add_pps_mo(struct hs20_osu_client *ctx, const char *uri,
|
||||
xml_node_t *add_mo, char *fname, size_t fname_len);
|
||||
void get_user_pw(struct hs20_osu_client *ctx, xml_node_t *pps,
|
||||
const char *alt_loc, char **user, char **pw);
|
||||
int update_pps_file(struct hs20_osu_client *ctx, const char *pps_fname,
|
||||
xml_node_t *pps);
|
||||
void cmd_set_pps(struct hs20_osu_client *ctx, const char *pps_fname);
|
||||
|
||||
|
||||
/* spp_client.c */
|
||||
|
||||
void spp_sub_rem(struct hs20_osu_client *ctx, const char *address,
|
||||
const char *pps_fname,
|
||||
const char *client_cert, const char *client_key,
|
||||
const char *cred_username, const char *cred_password,
|
||||
xml_node_t *pps);
|
||||
void spp_pol_upd(struct hs20_osu_client *ctx, const char *address,
|
||||
const char *pps_fname,
|
||||
const char *client_cert, const char *client_key,
|
||||
const char *cred_username, const char *cred_password,
|
||||
xml_node_t *pps);
|
||||
int cmd_prov(struct hs20_osu_client *ctx, const char *url);
|
||||
int cmd_sim_prov(struct hs20_osu_client *ctx, const char *url);
|
||||
|
||||
|
||||
/* oma_dm_client.c */
|
||||
|
||||
int cmd_oma_dm_prov(struct hs20_osu_client *ctx, const char *url);
|
||||
int cmd_oma_dm_sim_prov(struct hs20_osu_client *ctx, const char *url);
|
||||
void oma_dm_sub_rem(struct hs20_osu_client *ctx, const char *address,
|
||||
const char *pps_fname,
|
||||
const char *client_cert, const char *client_key,
|
||||
const char *cred_username, const char *cred_password,
|
||||
xml_node_t *pps);
|
||||
void oma_dm_pol_upd(struct hs20_osu_client *ctx, const char *address,
|
||||
const char *pps_fname,
|
||||
const char *client_cert, const char *client_key,
|
||||
const char *cred_username, const char *cred_password,
|
||||
xml_node_t *pps);
|
||||
void cmd_oma_dm_sub_rem(struct hs20_osu_client *ctx, const char *address,
|
||||
const char *pps_fname);
|
||||
void cmd_oma_dm_add(struct hs20_osu_client *ctx, const char *pps_fname,
|
||||
const char *add_fname);
|
||||
void cmd_oma_dm_replace(struct hs20_osu_client *ctx, const char *pps_fname,
|
||||
const char *replace_fname);
|
||||
|
||||
/* est.c */
|
||||
|
||||
int est_load_cacerts(struct hs20_osu_client *ctx, const char *url);
|
||||
int est_build_csr(struct hs20_osu_client *ctx, const char *url);
|
||||
int est_simple_enroll(struct hs20_osu_client *ctx, const char *url,
|
||||
const char *user, const char *pw);
|
||||
|
||||
#endif /* OSU_CLIENT_H */
|
File diff suppressed because it is too large
Load Diff
|
@ -0,0 +1,56 @@
|
|||
all: hs20_spp_server
|
||||
|
||||
ifndef CC
|
||||
CC=gcc
|
||||
endif
|
||||
|
||||
ifndef LDO
|
||||
LDO=$(CC)
|
||||
endif
|
||||
|
||||
ifndef CFLAGS
|
||||
CFLAGS = -MMD -O2 -Wall -g
|
||||
endif
|
||||
|
||||
CFLAGS += -I../../src
|
||||
CFLAGS += -I../../src/utils
|
||||
CFLAGS += -I../../src/crypto
|
||||
|
||||
LIBS += -lsqlite3
|
||||
|
||||
# Using glibc < 2.17 requires -lrt for clock_gettime()
|
||||
LIBS += -lrt
|
||||
|
||||
ifndef CONFIG_NO_GITVER
|
||||
# Add VERSION_STR postfix for builds from a git repository
|
||||
ifeq ($(wildcard ../../.git),../../.git)
|
||||
GITVER := $(shell git describe --dirty=+)
|
||||
ifneq ($(GITVER),)
|
||||
CFLAGS += -DGIT_VERSION_STR_POSTFIX=\"-$(GITVER)\"
|
||||
endif
|
||||
endif
|
||||
endif
|
||||
|
||||
OBJS=spp_server.o
|
||||
OBJS += hs20_spp_server.o
|
||||
OBJS += ../../src/utils/xml-utils.o
|
||||
OBJS += ../../src/utils/base64.o
|
||||
OBJS += ../../src/utils/common.o
|
||||
OBJS += ../../src/utils/os_unix.o
|
||||
OBJS += ../../src/utils/wpa_debug.o
|
||||
OBJS += ../../src/crypto/md5-internal.o
|
||||
CFLAGS += $(shell xml2-config --cflags)
|
||||
LIBS += $(shell xml2-config --libs)
|
||||
OBJS += ../../src/utils/xml_libxml2.o
|
||||
|
||||
hs20_spp_server: $(OBJS)
|
||||
$(LDO) $(LDFLAGS) -o hs20_spp_server $(OBJS) $(LIBS)
|
||||
|
||||
clean:
|
||||
rm -f core *~ *.o *.d hs20_spp_server
|
||||
rm -f ../../src/utils/*.o
|
||||
rm -f ../../src/utils/*.d
|
||||
rm -f ../../src/crypto/*.o
|
||||
rm -f ../../src/crypto/*.d
|
||||
|
||||
-include $(OBJS:%.o=%.d)
|
|
@ -0,0 +1,13 @@
|
|||
#!/bin/sh
|
||||
|
||||
for i in server-client server server-revoked user ocsp; do
|
||||
rm -f $i.csr $i.key $i.pem
|
||||
done
|
||||
|
||||
rm -f openssl.cnf.tmp
|
||||
if [ -d demoCA ]; then
|
||||
rm -r demoCA
|
||||
fi
|
||||
rm -f ca.pem logo.asn1 logo.der server.der ocsp-server-cache.der
|
||||
rm -f my-openssl.cnf my-openssl-root.cnf
|
||||
#rm -r rootCA
|
|
@ -0,0 +1,17 @@
|
|||
asn1 = SEQUENCE:attrs
|
||||
|
||||
[attrs]
|
||||
#oid1 = OID:challengePassword
|
||||
attr1 = SEQUENCE:extreq
|
||||
oid2 = OID:sha256WithRSAEncryption
|
||||
|
||||
[extreq]
|
||||
oid = OID:extensionRequest
|
||||
vals = SET:extreqvals
|
||||
|
||||
[extreqvals]
|
||||
|
||||
oid1 = OID:macAddress
|
||||
#oid2 = OID:imei
|
||||
#oid3 = OID:meid
|
||||
#oid4 = OID:DevId
|
|
@ -0,0 +1,4 @@
|
|||
#!/bin/sh
|
||||
|
||||
openssl asn1parse -genconf est-csrattrs.cnf -out est-csrattrs.der -oid hs20.oid
|
||||
base64 est-csrattrs.der > est-attrs.b64
|
|
@ -0,0 +1,7 @@
|
|||
1.3.6.1.1.1.1.22 macAddress
|
||||
1.2.840.113549.1.9.14 extensionRequest
|
||||
1.3.6.1.4.1.40808.1.1.1 id-wfa-hotspot-friendlyName
|
||||
1.3.6.1.4.1.40808.1.1.2 id-kp-HS2.0Auth
|
||||
1.3.6.1.4.1.40808.1.1.3 imei
|
||||
1.3.6.1.4.1.40808.1.1.4 meid
|
||||
1.3.6.1.4.1.40808.1.1.5 DevId
|
|
@ -0,0 +1,11 @@
|
|||
#!/bin/sh
|
||||
|
||||
for i in *.pem; do
|
||||
echo "===[ $i ]==================="
|
||||
openssl ocsp -text -CAfile ca.pem -verify_other demoCA/cacert.pem -trust_other -issuer demoCA/cacert.pem -cert $i -url http://localhost:8888/
|
||||
|
||||
# openssl ocsp -text -CAfile rootCA/cacert.pem -issuer demoCA/cacert.pem -cert $i -url http://localhost:8888/
|
||||
|
||||
# openssl ocsp -text -CAfile rootCA/cacert.pem -verify_other demoCA/cacert.pem -trust_other -issuer demoCA/cacert.pem -cert $i -url http://localhost:8888/
|
||||
# openssl ocsp -text -CAfile rootCA/cacert.pem -VAfile ca.pem -trust_other -issuer demoCA/cacert.pem -cert $i -url http://localhost:8888/
|
||||
done
|
|
@ -0,0 +1,3 @@
|
|||
#!/bin/sh
|
||||
|
||||
openssl ocsp -index demoCA/index.txt -port 8888 -nmin 5 -rsigner demoCA/cacert.pem -rkey demoCA/private/cakey-plain.pem -CA demoCA/cacert.pem -resp_no_certs -text
|
|
@ -0,0 +1,3 @@
|
|||
#!/bin/sh
|
||||
|
||||
openssl ocsp -index demoCA/index.txt -port 8888 -nmin 5 -rsigner ocsp.pem -rkey ocsp.key -CA demoCA/cacert.pem -text
|
|
@ -0,0 +1,10 @@
|
|||
#!/bin/sh
|
||||
|
||||
openssl ocsp \
|
||||
-no_nonce \
|
||||
-CAfile ca.pem \
|
||||
-verify_other demoCA/cacert.pem \
|
||||
-issuer demoCA/cacert.pem \
|
||||
-cert server.pem \
|
||||
-url http://localhost:8888/ \
|
||||
-respout ocsp-server-cache.der
|
|
@ -0,0 +1,125 @@
|
|||
# OpenSSL configuration file for Hotspot 2.0 PKI (Root CA)
|
||||
|
||||
HOME = .
|
||||
RANDFILE = $ENV::HOME/.rnd
|
||||
oid_section = new_oids
|
||||
|
||||
[ new_oids ]
|
||||
|
||||
#logotypeoid=1.3.6.1.5.5.7.1.12
|
||||
|
||||
####################################################################
|
||||
[ ca ]
|
||||
default_ca = CA_default # The default ca section
|
||||
|
||||
####################################################################
|
||||
[ CA_default ]
|
||||
|
||||
dir = ./rootCA # Where everything is kept
|
||||
certs = $dir/certs # Where the issued certs are kept
|
||||
crl_dir = $dir/crl # Where the issued crl are kept
|
||||
database = $dir/index.txt # database index file.
|
||||
#unique_subject = no # Set to 'no' to allow creation of
|
||||
# several certificates with same subject
|
||||
new_certs_dir = $dir/newcerts # default place for new certs.
|
||||
|
||||
certificate = $dir/cacert.pem # The CA certificate
|
||||
serial = $dir/serial # The current serial number
|
||||
crlnumber = $dir/crlnumber # the current crl number
|
||||
# must be commented out to leave a V1 CRL
|
||||
crl = $dir/crl.pem # The current CRL
|
||||
private_key = $dir/private/cakey.pem# The private key
|
||||
RANDFILE = $dir/private/.rand # private random number file
|
||||
|
||||
x509_extensions = usr_cert # The extentions to add to the cert
|
||||
|
||||
name_opt = ca_default # Subject Name options
|
||||
cert_opt = ca_default # Certificate field options
|
||||
|
||||
default_days = 365 # how long to certify for
|
||||
default_crl_days= 30 # how long before next CRL
|
||||
default_md = default # use public key default MD
|
||||
preserve = no # keep passed DN ordering
|
||||
|
||||
policy = policy_match
|
||||
|
||||
# For the CA policy
|
||||
[ policy_match ]
|
||||
countryName = match
|
||||
stateOrProvinceName = optional
|
||||
organizationName = match
|
||||
organizationalUnitName = optional
|
||||
commonName = supplied
|
||||
emailAddress = optional
|
||||
|
||||
[ policy_anything ]
|
||||
countryName = optional
|
||||
stateOrProvinceName = optional
|
||||
localityName = optional
|
||||
organizationName = optional
|
||||
organizationalUnitName = optional
|
||||
commonName = supplied
|
||||
emailAddress = optional
|
||||
|
||||
####################################################################
|
||||
[ req ]
|
||||
default_bits = 2048
|
||||
default_keyfile = privkey.pem
|
||||
distinguished_name = req_distinguished_name
|
||||
attributes = req_attributes
|
||||
x509_extensions = v3_ca # The extentions to add to the self signed cert
|
||||
|
||||
input_password = @PASSWORD@
|
||||
output_password = @PASSWORD@
|
||||
|
||||
string_mask = utf8only
|
||||
|
||||
[ req_distinguished_name ]
|
||||
countryName = Country Name (2 letter code)
|
||||
countryName_default = US
|
||||
countryName_min = 2
|
||||
countryName_max = 2
|
||||
|
||||
localityName = Locality Name (eg, city)
|
||||
localityName_default = Tuusula
|
||||
|
||||
0.organizationName = Organization Name (eg, company)
|
||||
0.organizationName_default = WFA Hotspot 2.0
|
||||
|
||||
##organizationalUnitName = Organizational Unit Name (eg, section)
|
||||
#organizationalUnitName_default =
|
||||
#@OU@
|
||||
|
||||
commonName = Common Name (e.g. server FQDN or YOUR name)
|
||||
#@CN@
|
||||
commonName_max = 64
|
||||
|
||||
emailAddress = Email Address
|
||||
emailAddress_max = 64
|
||||
|
||||
[ req_attributes ]
|
||||
|
||||
[ v3_req ]
|
||||
|
||||
# Extensions to add to a certificate request
|
||||
basicConstraints = CA:FALSE
|
||||
keyUsage = nonRepudiation, digitalSignature, keyEncipherment
|
||||
subjectAltName=DNS:example.com,DNS:another.example.com
|
||||
|
||||
[ v3_ca ]
|
||||
|
||||
# Hotspot 2.0 PKI requirements
|
||||
subjectKeyIdentifier=hash
|
||||
basicConstraints = critical,CA:true
|
||||
keyUsage = critical, cRLSign, keyCertSign
|
||||
|
||||
[ crl_ext ]
|
||||
|
||||
# issuerAltName=issuer:copy
|
||||
authorityKeyIdentifier=keyid:always
|
||||
|
||||
[ v3_OCSP ]
|
||||
|
||||
basicConstraints = CA:FALSE
|
||||
keyUsage = nonRepudiation, digitalSignature, keyEncipherment
|
||||
extendedKeyUsage = OCSPSigning
|
|
@ -0,0 +1,200 @@
|
|||
# OpenSSL configuration file for Hotspot 2.0 PKI (Intermediate CA)
|
||||
|
||||
HOME = .
|
||||
RANDFILE = $ENV::HOME/.rnd
|
||||
oid_section = new_oids
|
||||
|
||||
[ new_oids ]
|
||||
|
||||
#logotypeoid=1.3.6.1.5.5.7.1.12
|
||||
|
||||
####################################################################
|
||||
[ ca ]
|
||||
default_ca = CA_default # The default ca section
|
||||
|
||||
####################################################################
|
||||
[ CA_default ]
|
||||
|
||||
dir = ./demoCA # Where everything is kept
|
||||
certs = $dir/certs # Where the issued certs are kept
|
||||
crl_dir = $dir/crl # Where the issued crl are kept
|
||||
database = $dir/index.txt # database index file.
|
||||
#unique_subject = no # Set to 'no' to allow creation of
|
||||
# several certificates with same subject
|
||||
new_certs_dir = $dir/newcerts # default place for new certs.
|
||||
|
||||
certificate = $dir/cacert.pem # The CA certificate
|
||||
serial = $dir/serial # The current serial number
|
||||
crlnumber = $dir/crlnumber # the current crl number
|
||||
# must be commented out to leave a V1 CRL
|
||||
crl = $dir/crl.pem # The current CRL
|
||||
private_key = $dir/private/cakey.pem# The private key
|
||||
RANDFILE = $dir/private/.rand # private random number file
|
||||
|
||||
x509_extensions = ext_client # The extentions to add to the cert
|
||||
|
||||
name_opt = ca_default # Subject Name options
|
||||
cert_opt = ca_default # Certificate field options
|
||||
|
||||
# Extension copying option: use with caution.
|
||||
copy_extensions = copy
|
||||
|
||||
default_days = 365 # how long to certify for
|
||||
default_crl_days= 30 # how long before next CRL
|
||||
default_md = default # use public key default MD
|
||||
preserve = no # keep passed DN ordering
|
||||
|
||||
policy = policy_match
|
||||
|
||||
# For the CA policy
|
||||
[ policy_match ]
|
||||
countryName = supplied
|
||||
stateOrProvinceName = optional
|
||||
organizationName = supplied
|
||||
organizationalUnitName = optional
|
||||
commonName = supplied
|
||||
emailAddress = optional
|
||||
|
||||
[ policy_osu_server ]
|
||||
countryName = match
|
||||
stateOrProvinceName = optional
|
||||
organizationName = match
|
||||
organizationalUnitName = supplied
|
||||
commonName = supplied
|
||||
emailAddress = optional
|
||||
|
||||
[ policy_anything ]
|
||||
countryName = optional
|
||||
stateOrProvinceName = optional
|
||||
localityName = optional
|
||||
organizationName = optional
|
||||
organizationalUnitName = optional
|
||||
commonName = supplied
|
||||
emailAddress = optional
|
||||
|
||||
####################################################################
|
||||
[ req ]
|
||||
default_bits = 2048
|
||||
default_keyfile = privkey.pem
|
||||
distinguished_name = req_distinguished_name
|
||||
attributes = req_attributes
|
||||
x509_extensions = v3_ca # The extentions to add to the self signed cert
|
||||
|
||||
input_password = @PASSWORD@
|
||||
output_password = @PASSWORD@
|
||||
|
||||
string_mask = utf8only
|
||||
|
||||
[ req_distinguished_name ]
|
||||
countryName = Country Name (2 letter code)
|
||||
countryName_default = FI
|
||||
countryName_min = 2
|
||||
countryName_max = 2
|
||||
|
||||
localityName = Locality Name (eg, city)
|
||||
localityName_default = Tuusula
|
||||
|
||||
0.organizationName = Organization Name (eg, company)
|
||||
0.organizationName_default = @DOMAIN@
|
||||
|
||||
##organizationalUnitName = Organizational Unit Name (eg, section)
|
||||
#organizationalUnitName_default =
|
||||
#@OU@
|
||||
|
||||
commonName = Common Name (e.g. server FQDN or YOUR name)
|
||||
#@CN@
|
||||
commonName_max = 64
|
||||
|
||||
emailAddress = Email Address
|
||||
emailAddress_max = 64
|
||||
|
||||
[ req_attributes ]
|
||||
|
||||
[ v3_ca ]
|
||||
|
||||
# Hotspot 2.0 PKI requirements
|
||||
subjectKeyIdentifier=hash
|
||||
authorityKeyIdentifier=keyid:always,issuer
|
||||
basicConstraints = critical, CA:true, pathlen:0
|
||||
keyUsage = critical, cRLSign, keyCertSign
|
||||
authorityInfoAccess = OCSP;URI:@OCSP_URI@
|
||||
# For SP intermediate CA
|
||||
#subjectAltName=critical,otherName:1.3.6.1.4.1.40808.1.1.1;UTF8String:engExample OSU
|
||||
#nameConstraints=permitted;DNS:.@DOMAIN@
|
||||
#1.3.6.1.5.5.7.1.12=ASN1:SEQUENCE:LogotypeExtn
|
||||
|
||||
[ v3_osu_server ]
|
||||
|
||||
basicConstraints = critical, CA:true, pathlen:0
|
||||
keyUsage = critical, keyEncipherment
|
||||
#@ALTNAME@
|
||||
|
||||
#logotypeoid=ASN1:SEQUENCE:LogotypeExtn
|
||||
1.3.6.1.5.5.7.1.12=ASN1:SEQUENCE:LogotypeExtn
|
||||
[LogotypeExtn]
|
||||
communityLogos=EXP:0,SEQUENCE:LogotypeInfo
|
||||
[LogotypeInfo]
|
||||
# note: implicit tag converted to explicit for CHOICE
|
||||
direct=EXP:0,SEQUENCE:LogotypeData
|
||||
[LogotypeData]
|
||||
image=SEQUENCE:LogotypeImage
|
||||
[LogotypeImage]
|
||||
imageDetails=SEQUENCE:LogotypeDetails
|
||||
imageInfo=SEQUENCE:LogotypeImageInfo
|
||||
[LogotypeDetails]
|
||||
mediaType=IA5STRING:image/png
|
||||
logotypeHash=SEQUENCE:HashAlgAndValues
|
||||
logotypeURI=SEQUENCE:URI
|
||||
[HashAlgAndValues]
|
||||
value1=SEQUENCE:HashAlgAndValueSHA256
|
||||
#value2=SEQUENCE:HashAlgAndValueSHA1
|
||||
[HashAlgAndValueSHA256]
|
||||
hashAlg=SEQUENCE:sha256_alg
|
||||
hashValue=FORMAT:HEX,OCTETSTRING:@LOGO_HASH256@
|
||||
[HashAlgAndValueSHA1]
|
||||
hashAlg=SEQUENCE:sha1_alg
|
||||
hashValue=FORMAT:HEX,OCTETSTRING:@LOGO_HASH1@
|
||||
[sha256_alg]
|
||||
algorithm=OID:sha256
|
||||
[sha1_alg]
|
||||
algorithm=OID:sha1
|
||||
[URI]
|
||||
uri=IA5STRING:@LOGO_URI@
|
||||
[LogotypeImageInfo]
|
||||
# default value color(1), component optional
|
||||
#type=IMP:0,INTEGER:1
|
||||
fileSize=INTEGER:7549
|
||||
xSize=INTEGER:128
|
||||
ySize=INTEGER:80
|
||||
language=IMP:4,IA5STRING:zxx
|
||||
|
||||
[ crl_ext ]
|
||||
|
||||
# issuerAltName=issuer:copy
|
||||
authorityKeyIdentifier=keyid:always
|
||||
|
||||
[ v3_OCSP ]
|
||||
|
||||
basicConstraints = CA:FALSE
|
||||
keyUsage = nonRepudiation, digitalSignature, keyEncipherment
|
||||
extendedKeyUsage = OCSPSigning
|
||||
|
||||
[ ext_client ]
|
||||
|
||||
basicConstraints=CA:FALSE
|
||||
subjectKeyIdentifier=hash
|
||||
authorityKeyIdentifier=keyid,issuer
|
||||
authorityInfoAccess = OCSP;URI:@OCSP_URI@
|
||||
#@ALTNAME@
|
||||
extendedKeyUsage = clientAuth
|
||||
|
||||
[ ext_server ]
|
||||
|
||||
# Hotspot 2.0 PKI requirements
|
||||
basicConstraints=critical, CA:FALSE
|
||||
subjectKeyIdentifier=hash
|
||||
authorityKeyIdentifier=keyid,issuer
|
||||
authorityInfoAccess = OCSP;URI:@OCSP_URI@
|
||||
#@ALTNAME@
|
||||
extendedKeyUsage = critical, serverAuth
|
||||
keyUsage = critical, keyEncipherment
|
|
@ -0,0 +1,209 @@
|
|||
#!/bin/sh
|
||||
|
||||
if [ -z "$OPENSSL" ]; then
|
||||
OPENSSL=openssl
|
||||
fi
|
||||
export OPENSSL_CONF=$PWD/openssl.cnf
|
||||
PASS=whatever
|
||||
if [ -z "$DOMAIN" ]; then
|
||||
DOMAIN=w1.fi
|
||||
fi
|
||||
COMPANY=w1.fi
|
||||
OPER_ENG="engw1.fi TESTING USE"
|
||||
OPER_FI="finw1.fi TESTIKÄYTTÖ"
|
||||
CNR="Hotspot 2.0 Trust Root CA - 99"
|
||||
CNO="ocsp.$DOMAIN"
|
||||
CNV="osu-revoked.$DOMAIN"
|
||||
CNOC="osu-client.$DOMAIN"
|
||||
OSU_SERVER_HOSTNAME="osu.$DOMAIN"
|
||||
DEBUG=0
|
||||
OCSP_URI="http://$CNO:8888/"
|
||||
LOGO_URI="http://osu.w1.fi/w1fi_logo.png"
|
||||
LOGO_HASH256="4532f7ec36424381617c03c6ce87b55a51d6e7177ffafda243cebf280a68954d"
|
||||
LOGO_HASH1="5e1d5085676eede6b02da14d31c523ec20ffba0b"
|
||||
|
||||
# Command line overrides
|
||||
USAGE=$( cat <<EOF
|
||||
Usage:\n
|
||||
# -c: Company name, used to generate Subject name CN for Intermediate CA\n
|
||||
# -C: Subject name CN of the Root CA ($CNR)\n
|
||||
# -D: Enable debugging (set -x, etc)\n
|
||||
# -g: Logo sha1 hash ($LOGO_HASH1)\n
|
||||
# -G: Logo sha256 hash ($LOGO_HASH256)\n
|
||||
# -h: Show this help message\n
|
||||
# -l: Logo URI ($LOGO_URI)\n
|
||||
# -m: Domain ($DOMAIN)\n
|
||||
# -o: Subject name CN for OSU-Client Server ($CNOC)\n
|
||||
# -O: Subject name CN for OCSP Server ($CNO)\n
|
||||
# -p: passphrase for private keys ($PASS)\n
|
||||
# -r: Operator-english ($OPER_ENG)\n
|
||||
# -R: Operator-finish ($OPER_FI)\n
|
||||
# -S: OSU Server name ($OSU_SERVER_HOSTNAME)\n
|
||||
# -u: OCSP-URI ($OCSP_URI)\n
|
||||
# -V: Subject name CN for OSU-Revoked Server ($CNV)\n
|
||||
EOF
|
||||
)
|
||||
|
||||
while getopts "c:C:Dg:G:l:m:o:O:p:r:R:S:u:V:h" flag
|
||||
do
|
||||
case $flag in
|
||||
c) COMPANY=$OPTARG;;
|
||||
C) CNR=$OPTARG;;
|
||||
D) DEBUG=1;;
|
||||
g) LOGO_HASH1=$OPTARG;;
|
||||
G) LOGO_HASH256=$OPTARG;;
|
||||
h) echo -e $USAGE; exit 0;;
|
||||
l) LOGO_URI=$OPTARG;;
|
||||
m) DOMAIN=$OPTARG;;
|
||||
o) CNOC=$OPTARG;;
|
||||
O) CNO=$OPTARG;;
|
||||
p) PASS=$OPTARG;;
|
||||
r) OPER_ENG=$OPTARG;;
|
||||
R) OPER_FI=$OPTARG;;
|
||||
S) OSU_SERVER_HOSTNAME=$OPTARG;;
|
||||
u) OCSP_URI=$OPTARG;;
|
||||
V) CNV=$OPTARG;;
|
||||
*) echo "Unknown flag: $flag"; echo -e $USAGE; exit 1;;
|
||||
esac
|
||||
done
|
||||
|
||||
fail()
|
||||
{
|
||||
echo "$*"
|
||||
exit 1
|
||||
}
|
||||
|
||||
echo
|
||||
echo "---[ Root CA ]----------------------------------------------------------"
|
||||
echo
|
||||
|
||||
if [ $DEBUG = 1 ]
|
||||
then
|
||||
set -x
|
||||
fi
|
||||
|
||||
# Set the passphrase and some other common config accordingly.
|
||||
cat openssl-root.cnf | sed "s/@PASSWORD@/$PASS/" \
|
||||
> my-openssl-root.cnf
|
||||
|
||||
cat openssl.cnf | sed "s/@PASSWORD@/$PASS/" |
|
||||
sed "s,@OCSP_URI@,$OCSP_URI," |
|
||||
sed "s,@LOGO_URI@,$LOGO_URI," |
|
||||
sed "s,@LOGO_HASH1@,$LOGO_HASH1," |
|
||||
sed "s,@LOGO_HASH256@,$LOGO_HASH256," |
|
||||
sed "s/@DOMAIN@/$DOMAIN/" \
|
||||
> my-openssl.cnf
|
||||
|
||||
|
||||
cat my-openssl-root.cnf | sed "s/#@CN@/commonName_default = $CNR/" > openssl.cnf.tmp
|
||||
mkdir -p rootCA/certs rootCA/crl rootCA/newcerts rootCA/private
|
||||
touch rootCA/index.txt
|
||||
if [ -e rootCA/private/cakey.pem ]; then
|
||||
echo " * Use existing Root CA"
|
||||
else
|
||||
echo " * Generate Root CA private key"
|
||||
$OPENSSL req -config openssl.cnf.tmp -batch -new -newkey rsa:4096 -keyout rootCA/private/cakey.pem -out rootCA/careq.pem || fail "Failed to generate Root CA private key"
|
||||
echo " * Sign Root CA certificate"
|
||||
$OPENSSL ca -config openssl.cnf.tmp -md sha256 -create_serial -out rootCA/cacert.pem -days 10957 -batch -keyfile rootCA/private/cakey.pem -passin pass:$PASS -selfsign -extensions v3_ca -outdir rootCA/newcerts -infiles rootCA/careq.pem || fail "Failed to sign Root CA certificate"
|
||||
$OPENSSL x509 -in rootCA/cacert.pem -out rootCA/cacert.der -outform DER || fail "Failed to create rootCA DER"
|
||||
sha256sum rootCA/cacert.der > rootCA/cacert.fingerprint || fail "Failed to create rootCA fingerprint"
|
||||
fi
|
||||
if [ ! -e rootCA/crlnumber ]; then
|
||||
echo 00 > rootCA/crlnumber
|
||||
fi
|
||||
|
||||
echo
|
||||
echo "---[ Intermediate CA ]--------------------------------------------------"
|
||||
echo
|
||||
|
||||
cat my-openssl.cnf | sed "s/#@CN@/commonName_default = $COMPANY Hotspot 2.0 Intermediate CA/" > openssl.cnf.tmp
|
||||
mkdir -p demoCA/certs demoCA/crl demoCA/newcerts demoCA/private
|
||||
touch demoCA/index.txt
|
||||
if [ -e demoCA/private/cakey.pem ]; then
|
||||
echo " * Use existing Intermediate CA"
|
||||
else
|
||||
echo " * Generate Intermediate CA private key"
|
||||
$OPENSSL req -config openssl.cnf.tmp -batch -new -newkey rsa:2048 -keyout demoCA/private/cakey.pem -out demoCA/careq.pem || fail "Failed to generate Intermediate CA private key"
|
||||
echo " * Sign Intermediate CA certificate"
|
||||
$OPENSSL ca -config openssl.cnf.tmp -md sha256 -create_serial -out demoCA/cacert.pem -days 3652 -batch -keyfile rootCA/private/cakey.pem -cert rootCA/cacert.pem -passin pass:$PASS -extensions v3_ca -infiles demoCA/careq.pem || fail "Failed to sign Intermediate CA certificate"
|
||||
# horrible from security view point, but for testing purposes since OCSP responder does not seem to support -passin
|
||||
openssl rsa -in demoCA/private/cakey.pem -out demoCA/private/cakey-plain.pem -passin pass:$PASS
|
||||
$OPENSSL x509 -in demoCA/cacert.pem -out demoCA/cacert.der -outform DER || fail "Failed to create demoCA DER."
|
||||
sha256sum demoCA/cacert.der > demoCA/cacert.fingerprint || fail "Failed to create demoCA fingerprint"
|
||||
fi
|
||||
if [ ! -e demoCA/crlnumber ]; then
|
||||
echo 00 > demoCA/crlnumber
|
||||
fi
|
||||
|
||||
echo
|
||||
echo "OCSP responder"
|
||||
echo
|
||||
|
||||
cat my-openssl.cnf | sed "s/#@CN@/commonName_default = $CNO/" > openssl.cnf.tmp
|
||||
$OPENSSL req -config $PWD/openssl.cnf.tmp -batch -new -newkey rsa:2048 -nodes -out ocsp.csr -keyout ocsp.key -extensions v3_OCSP
|
||||
$OPENSSL ca -config $PWD/openssl.cnf.tmp -batch -md sha256 -keyfile demoCA/private/cakey.pem -passin pass:$PASS -in ocsp.csr -out ocsp.pem -days 730 -extensions v3_OCSP || fail "Could not generate ocsp.pem"
|
||||
|
||||
echo
|
||||
echo "---[ Server - to be revoked ] ------------------------------------------"
|
||||
echo
|
||||
|
||||
cat my-openssl.cnf | sed "s/#@CN@/commonName_default = $CNV/" > openssl.cnf.tmp
|
||||
$OPENSSL req -config $PWD/openssl.cnf.tmp -batch -new -newkey rsa:2048 -nodes -out server-revoked.csr -keyout server-revoked.key
|
||||
$OPENSSL ca -config $PWD/openssl.cnf.tmp -batch -md sha256 -in server-revoked.csr -out server-revoked.pem -key $PASS -days 730 -extensions ext_server
|
||||
$OPENSSL ca -revoke server-revoked.pem -key $PASS
|
||||
|
||||
echo
|
||||
echo "---[ Server - with client ext key use ] ---------------------------------"
|
||||
echo "---[ Only used for negative-testing for OSU-client implementation ] -----"
|
||||
echo
|
||||
|
||||
cat my-openssl.cnf | sed "s/#@CN@/commonName_default = $CNOC/" > openssl.cnf.tmp
|
||||
$OPENSSL req -config $PWD/openssl.cnf.tmp -batch -new -newkey rsa:2048 -nodes -out server-client.csr -keyout server-client.key || fail "Could not create server-client.key"
|
||||
$OPENSSL ca -config $PWD/openssl.cnf.tmp -batch -md sha256 -in server-client.csr -out server-client.pem -key $PASS -days 730 -extensions ext_client || fail "Could not create server-client.pem"
|
||||
|
||||
echo
|
||||
echo "---[ User ]-------------------------------------------------------------"
|
||||
echo
|
||||
|
||||
cat my-openssl.cnf | sed "s/#@CN@/commonName_default = User/" > openssl.cnf.tmp
|
||||
$OPENSSL req -config $PWD/openssl.cnf.tmp -batch -new -newkey rsa:2048 -nodes -out user.csr -keyout user.key || fail "Could not create user.key"
|
||||
$OPENSSL ca -config $PWD/openssl.cnf.tmp -batch -md sha256 -in user.csr -out user.pem -key $PASS -days 730 -extensions ext_client || fail "Could not create user.pem"
|
||||
|
||||
echo
|
||||
echo "---[ Server ]-----------------------------------------------------------"
|
||||
echo
|
||||
|
||||
ALT="DNS:$OSU_SERVER_HOSTNAME"
|
||||
ALT="$ALT,otherName:1.3.6.1.4.1.40808.1.1.1;UTF8String:$OPER_ENG"
|
||||
ALT="$ALT,otherName:1.3.6.1.4.1.40808.1.1.1;UTF8String:$OPER_FI"
|
||||
|
||||
cat my-openssl.cnf |
|
||||
sed "s/#@CN@/commonName_default = $OSU_SERVER_HOSTNAME/" |
|
||||
sed "s/^##organizationalUnitName/organizationalUnitName/" |
|
||||
sed "s/#@OU@/organizationalUnitName_default = Hotspot 2.0 Online Sign Up Server/" |
|
||||
sed "s/#@ALTNAME@/subjectAltName=critical,$ALT/" \
|
||||
> openssl.cnf.tmp
|
||||
echo $OPENSSL req -config $PWD/openssl.cnf.tmp -batch -sha256 -new -newkey rsa:2048 -nodes -out server.csr -keyout server.key -reqexts v3_osu_server
|
||||
$OPENSSL req -config $PWD/openssl.cnf.tmp -batch -sha256 -new -newkey rsa:2048 -nodes -out server.csr -keyout server.key -reqexts v3_osu_server || fail "Failed to generate server request"
|
||||
$OPENSSL ca -config $PWD/openssl.cnf.tmp -batch -md sha256 -in server.csr -out server.pem -key $PASS -days 730 -extensions ext_server -policy policy_osu_server || fail "Failed to sign server certificate"
|
||||
|
||||
#dump logotype details for debugging
|
||||
$OPENSSL x509 -in server.pem -out server.der -outform DER
|
||||
openssl asn1parse -in server.der -inform DER | grep HEX | tail -1 | sed 's/.*://' | xxd -r -p > logo.der
|
||||
openssl asn1parse -in logo.der -inform DER > logo.asn1
|
||||
|
||||
|
||||
echo
|
||||
echo "---[ CRL ]---------------------------------------------------------------"
|
||||
echo
|
||||
|
||||
$OPENSSL ca -config $PWD/my-openssl.cnf -gencrl -md sha256 -out demoCA/crl/crl.pem -passin pass:$PASS
|
||||
|
||||
echo
|
||||
echo "---[ Verify ]------------------------------------------------------------"
|
||||
echo
|
||||
|
||||
$OPENSSL verify -CAfile rootCA/cacert.pem demoCA/cacert.pem
|
||||
$OPENSSL verify -CAfile rootCA/cacert.pem -untrusted demoCA/cacert.pem *.pem
|
||||
|
||||
cat rootCA/cacert.pem demoCA/cacert.pem > ca.pem
|
Binary file not shown.
After Width: | Height: | Size: 7.4 KiB |
|
@ -0,0 +1,262 @@
|
|||
Hotspot 2.0 OSU server
|
||||
======================
|
||||
|
||||
The information in this document is based on the assumption that Ubuntu
|
||||
16.04 server (64-bit) distribution is used and the web server is
|
||||
Apache2. Neither of these are requirements for the installation, but if
|
||||
other combinations are used, the package names and configuration
|
||||
parameters may need to be adjusted.
|
||||
|
||||
NOTE: This implementation and the example configuration here is meant
|
||||
only for testing purposes in a lab environment. This design is not
|
||||
secure to be installed in a publicly available Internet server without
|
||||
considerable amount of modification and review for security issues.
|
||||
|
||||
|
||||
Build dependencies
|
||||
------------------
|
||||
|
||||
Ubuntu 16.04 server
|
||||
- default installation
|
||||
- upgraded to latest package versions
|
||||
sudo apt-get update
|
||||
sudo apt-get upgrade
|
||||
|
||||
Packages needed for running the service:
|
||||
sudo apt-get install sqlite3
|
||||
sudo apt-get install apache2
|
||||
sudo apt-get install php-sqlite3 php-xml libapache2-mod-php
|
||||
|
||||
Additional packages needed for building the components:
|
||||
sudo apt-get install build-essential
|
||||
sudo apt-get install libsqlite3-dev
|
||||
sudo apt-get install libssl-dev
|
||||
sudo apt-get install libxml2-dev
|
||||
|
||||
|
||||
Installation location
|
||||
---------------------
|
||||
|
||||
Select a location for the installation root directory. The example here
|
||||
assumes /home/user/hs20-server to be used, but this can be changed by
|
||||
editing couple of files as indicated below.
|
||||
|
||||
sudo mkdir -p /home/user/hs20-server
|
||||
sudo chown $USER /home/user/hs20-server
|
||||
mkdir -p /home/user/hs20-server/spp
|
||||
mkdir -p /home/user/hs20-server/AS
|
||||
|
||||
|
||||
Build
|
||||
-----
|
||||
|
||||
# hostapd as RADIUS server
|
||||
cd hostapd
|
||||
|
||||
#example build configuration
|
||||
cat > .config <<EOF
|
||||
CONFIG_DRIVER_NONE=y
|
||||
CONFIG_PKCS12=y
|
||||
CONFIG_RADIUS_SERVER=y
|
||||
CONFIG_EAP=y
|
||||
CONFIG_EAP_TLS=y
|
||||
CONFIG_EAP_MSCHAPV2=y
|
||||
CONFIG_EAP_PEAP=y
|
||||
CONFIG_EAP_GTC=y
|
||||
CONFIG_EAP_TTLS=y
|
||||
CONFIG_EAP_SIM=y
|
||||
CONFIG_EAP_AKA=y
|
||||
CONFIG_EAP_AKA_PRIME=y
|
||||
CONFIG_SQLITE=y
|
||||
CONFIG_HS20=y
|
||||
EOF
|
||||
|
||||
make hostapd hlr_auc_gw
|
||||
cp hostapd hlr_auc_gw /home/user/hs20-server/AS
|
||||
|
||||
# build hs20_spp_server
|
||||
cd ../hs20/server
|
||||
make clean
|
||||
make
|
||||
cp hs20_spp_server /home/user/hs20-server/spp
|
||||
# prepare database (web server user/group needs to have write access)
|
||||
mkdir -p /home/user/hs20-server/AS/DB
|
||||
sudo chgrp www-data /home/user/hs20-server/AS/DB
|
||||
sudo chmod g+w /home/user/hs20-server/AS/DB
|
||||
sqlite3 /home/user/hs20-server/AS/DB/eap_user.db < sql.txt
|
||||
sudo chgrp www-data /home/user/hs20-server/AS/DB/eap_user.db
|
||||
sudo chmod g+w /home/user/hs20-server/AS/DB/eap_user.db
|
||||
# add example configuration (note: need to update URLs to match the system)
|
||||
sqlite3 /home/user/hs20-server/AS/DB/eap_user.db < sql-example.txt
|
||||
|
||||
# copy PHP scripts
|
||||
# Modify config.php if different installation directory is used.
|
||||
# Modify PHP scripts to get the desired behavior for user interaction (or use
|
||||
# the examples as-is for initial testing).
|
||||
cp -r www /home/user/hs20-server
|
||||
|
||||
# Create /home/user/hs20-server/terms-and-conditions file (HTML segment to be
|
||||
# inserted within the BODY section of the page).
|
||||
cat > /home/user/hs20-server/terms-and-conditions <<EOF
|
||||
<P>Terms and conditions..</P>
|
||||
EOF
|
||||
|
||||
# Build local keys and certs
|
||||
cd ca
|
||||
# Display help options.
|
||||
./setup.sh -h
|
||||
|
||||
# Remove old keys, fill in appropriate values, and generate your keys.
|
||||
# For instance:
|
||||
./clean.sh
|
||||
rm -fr rootCA"
|
||||
old_hostname=myserver.local
|
||||
./setup.sh -C "Hotspot 2.0 Trust Root CA - CT" \
|
||||
-o $old_hostname-osu-client \
|
||||
-O $old_hostname-oscp -p lanforge -S $old_hostname \
|
||||
-V $old_hostname-osu-revoked \
|
||||
-m local -u http://$old_hostname:8888/
|
||||
|
||||
# Configure subscription policies
|
||||
mkdir -p /home/user/hs20-server/spp/policy
|
||||
cat > /home/user/hs20-server/spp/policy/default.xml <<EOF
|
||||
<Policy>
|
||||
<PolicyUpdate>
|
||||
<UpdateInterval>30</UpdateInterval>
|
||||
<UpdateMethod>ClientInitiated</UpdateMethod>
|
||||
<Restriction>Unrestricted</Restriction>
|
||||
<URI>https://policy-server.osu.example.com/hs20/spp.php</URI>
|
||||
</PolicyUpdate>
|
||||
</Policy>
|
||||
EOF
|
||||
|
||||
|
||||
# Install Hotspot 2.0 SPP and OMA DM XML schema/DTD files
|
||||
|
||||
# XML schema for SPP
|
||||
# Copy the latest XML schema into /home/user/hs20-server/spp/spp.xsd
|
||||
|
||||
# OMA DM Device Description Framework DTD
|
||||
# Copy into /home/user/hs20-server/spp/dm_ddf-v1_2.dtd
|
||||
# http://www.openmobilealliance.org/tech/DTD/dm_ddf-v1_2.dtd
|
||||
|
||||
|
||||
# Configure RADIUS authentication service
|
||||
# Note: Change the URL to match the setup
|
||||
# Note: Install AAA server key/certificate and root CA in Key directory
|
||||
|
||||
cat > /home/user/hs20-server/AS/as-sql.conf <<EOF
|
||||
driver=none
|
||||
radius_server_clients=as.radius_clients
|
||||
eap_server=1
|
||||
eap_user_file=sqlite:DB/eap_user.db
|
||||
ca_cert=Key/ca.pem
|
||||
server_cert=Key/server.pem
|
||||
private_key=Key/server.key
|
||||
private_key_passwd=passphrase
|
||||
eap_sim_db=unix:/tmp/hlr_auc_gw.sock db=eap_sim.db
|
||||
subscr_remediation_url=https://subscription-server.osu.example.com/hs20/spp.php
|
||||
EOF
|
||||
|
||||
# Set RADIUS passphrase for the APs
|
||||
# Note: Modify to match the setup
|
||||
cat > /home/user/hs20-server/AS/as.radius_clients <<EOF
|
||||
0.0.0.0/0 radius
|
||||
EOF
|
||||
|
||||
|
||||
Start RADIUS authentication server
|
||||
----------------------------------
|
||||
|
||||
cd /home/user/hs20-server/AS
|
||||
./hostapd -B as-sql.conf
|
||||
|
||||
|
||||
OSEN RADIUS server configuration notes
|
||||
|
||||
The OSEN RADIUS server config file should have the 'ocsp_stapling_response'
|
||||
configuration in it. For example:
|
||||
|
||||
# hostapd-radius config for the radius used by the OSEN AP
|
||||
interface=eth0#0
|
||||
driver=none
|
||||
logger_syslog=-1
|
||||
logger_syslog_level=2
|
||||
logger_stdout=-1
|
||||
logger_stdout_level=2
|
||||
ctrl_interface=/var/run/hostapd
|
||||
ctrl_interface_group=0
|
||||
eap_server=1
|
||||
eap_user_file=/home/user/hs20-server/AS/hostapd-osen.eap_user
|
||||
server_id=ben-ota-2-osen
|
||||
radius_server_auth_port=1811
|
||||
radius_server_clients=/home/user/hs20-server/AS/hostap.radius_clients
|
||||
|
||||
ca_cert=/home/user/hs20-server/ca/ca.pem
|
||||
server_cert=/home/user/hs20-server/ca/server.pem
|
||||
private_key=/home/user/hs20-server/ca/server.key
|
||||
private_key_passwd=whatever
|
||||
|
||||
ocsp_stapling_response=/home/user/hs20-server/ca/ocsp-server-cache.der
|
||||
|
||||
The /home/user/hs20-server/AS/hostapd-osen.eap_user file should look
|
||||
similar to this, and should coorelate with the osu_nai entry in
|
||||
the non-OSEN VAP config file. For instance:
|
||||
|
||||
# cat hostapd-osen.eap_user
|
||||
# For OSEN authentication (Hotspot 2.0 Release 2)
|
||||
"osen@w1.fi" WFA-UNAUTH-TLS
|
||||
|
||||
|
||||
# Run OCSP server:
|
||||
cd /home/user/hs20-server/ca
|
||||
./ocsp-responder.sh&
|
||||
|
||||
# Update cache (This should be run periodically)
|
||||
./ocsp-update-cache.sh
|
||||
|
||||
|
||||
Configure web server
|
||||
--------------------
|
||||
|
||||
Edit /etc/apache2/sites-available/default-ssl
|
||||
|
||||
Add following block just before "SSL Engine Switch" line":
|
||||
|
||||
Alias /hs20/ "/home/user/hs20-server/www/"
|
||||
<Directory "/home/user/hs20-server/www/">
|
||||
Options Indexes MultiViews FollowSymLinks
|
||||
AllowOverride None
|
||||
Require all granted
|
||||
SSLOptions +StdEnvVars
|
||||
</Directory>
|
||||
|
||||
Update SSL configuration to use the OSU server certificate/key.
|
||||
They keys and certs are called 'server.key' and 'server.pem' from
|
||||
ca/setup.sh.
|
||||
|
||||
To support subscription remediation using client certificates, set
|
||||
"SSLVerifyClient optional" and configure the trust root CA(s) for the
|
||||
client certificates with SSLCACertificateFile.
|
||||
|
||||
Enable default-ssl site and restart Apache2:
|
||||
sudo a2ensite default-ssl
|
||||
sudo a2enmod ssl
|
||||
sudo service apache2 restart
|
||||
|
||||
|
||||
Management UI
|
||||
-------------
|
||||
|
||||
The sample PHP scripts include a management UI for testing
|
||||
purposes. That is available at https://<server>/hs20/users.php
|
||||
|
||||
|
||||
AP configuration
|
||||
----------------
|
||||
|
||||
APs can now be configured to use the OSU server as the RADIUS
|
||||
authentication server. In addition, the OSU Provider List ANQP element
|
||||
should be configured to use the SPP (SOAP+XML) option and with the
|
||||
following Server URL:
|
||||
https://<server>/hs20/spp.php/signup?realm=example.com
|
|
@ -0,0 +1,207 @@
|
|||
/*
|
||||
* Hotspot 2.0 SPP server - standalone version
|
||||
* Copyright (c) 2012-2013, Qualcomm Atheros, Inc.
|
||||
*
|
||||
* This software may be distributed under the terms of the BSD license.
|
||||
* See README for more details.
|
||||
*/
|
||||
|
||||
#include "includes.h"
|
||||
#include <time.h>
|
||||
#include <sqlite3.h>
|
||||
|
||||
#include "common.h"
|
||||
#include "common/version.h"
|
||||
#include "xml-utils.h"
|
||||
#include "spp_server.h"
|
||||
|
||||
|
||||
static void write_timestamp(FILE *f)
|
||||
{
|
||||
time_t t;
|
||||
struct tm *tm;
|
||||
|
||||
time(&t);
|
||||
tm = localtime(&t);
|
||||
|
||||
fprintf(f, "%04u-%02u-%02u %02u:%02u:%02u ",
|
||||
tm->tm_year + 1900, tm->tm_mon + 1, tm->tm_mday,
|
||||
tm->tm_hour, tm->tm_min, tm->tm_sec);
|
||||
}
|
||||
|
||||
|
||||
void debug_print(struct hs20_svc *ctx, int print, const char *fmt, ...)
|
||||
{
|
||||
va_list ap;
|
||||
|
||||
if (ctx->debug_log == NULL)
|
||||
return;
|
||||
|
||||
write_timestamp(ctx->debug_log);
|
||||
va_start(ap, fmt);
|
||||
vfprintf(ctx->debug_log, fmt, ap);
|
||||
va_end(ap);
|
||||
|
||||
fprintf(ctx->debug_log, "\n");
|
||||
}
|
||||
|
||||
|
||||
void debug_dump_node(struct hs20_svc *ctx, const char *title, xml_node_t *node)
|
||||
{
|
||||
char *str;
|
||||
|
||||
if (ctx->debug_log == NULL)
|
||||
return;
|
||||
str = xml_node_to_str(ctx->xml, node);
|
||||
if (str == NULL)
|
||||
return;
|
||||
|
||||
write_timestamp(ctx->debug_log);
|
||||
fprintf(ctx->debug_log, "%s: '%s'\n", title, str);
|
||||
os_free(str);
|
||||
}
|
||||
|
||||
|
||||
static int process(struct hs20_svc *ctx)
|
||||
{
|
||||
int dmacc = 0;
|
||||
xml_node_t *soap, *spp, *resp;
|
||||
char *user, *realm, *post, *str;
|
||||
|
||||
ctx->addr = getenv("HS20ADDR");
|
||||
if (ctx->addr)
|
||||
debug_print(ctx, 1, "Connection from %s", ctx->addr);
|
||||
ctx->test = getenv("HS20TEST");
|
||||
if (ctx->test)
|
||||
debug_print(ctx, 1, "Requested test functionality: %s",
|
||||
ctx->test);
|
||||
|
||||
user = getenv("HS20USER");
|
||||
if (user && strlen(user) == 0)
|
||||
user = NULL;
|
||||
realm = getenv("HS20REALM");
|
||||
if (realm == NULL) {
|
||||
debug_print(ctx, 1, "HS20REALM not set");
|
||||
return -1;
|
||||
}
|
||||
post = getenv("HS20POST");
|
||||
if (post == NULL) {
|
||||
debug_print(ctx, 1, "HS20POST not set");
|
||||
return -1;
|
||||
}
|
||||
|
||||
ctx->imsi = getenv("HS20IMSI");
|
||||
if (ctx->imsi)
|
||||
debug_print(ctx, 1, "IMSI %s", ctx->imsi);
|
||||
|
||||
ctx->eap_method = getenv("HS20EAPMETHOD");
|
||||
if (ctx->eap_method)
|
||||
debug_print(ctx, 1, "EAP method %s", ctx->eap_method);
|
||||
|
||||
ctx->id_hash = getenv("HS20IDHASH");
|
||||
if (ctx->id_hash)
|
||||
debug_print(ctx, 1, "ID-HASH %s", ctx->id_hash);
|
||||
|
||||
soap = xml_node_from_buf(ctx->xml, post);
|
||||
if (soap == NULL) {
|
||||
debug_print(ctx, 1, "Could not parse SOAP data");
|
||||
return -1;
|
||||
}
|
||||
debug_dump_node(ctx, "Received SOAP message", soap);
|
||||
spp = soap_get_body(ctx->xml, soap);
|
||||
if (spp == NULL) {
|
||||
debug_print(ctx, 1, "Could not get SPP message");
|
||||
xml_node_free(ctx->xml, soap);
|
||||
return -1;
|
||||
}
|
||||
debug_dump_node(ctx, "Received SPP message", spp);
|
||||
|
||||
resp = hs20_spp_server_process(ctx, spp, user, realm, dmacc);
|
||||
xml_node_free(ctx->xml, soap);
|
||||
if (resp == NULL && user == NULL) {
|
||||
debug_print(ctx, 1, "Request HTTP authentication");
|
||||
return 2; /* Request authentication */
|
||||
}
|
||||
if (resp == NULL) {
|
||||
debug_print(ctx, 1, "No response");
|
||||
return -1;
|
||||
}
|
||||
|
||||
soap = soap_build_envelope(ctx->xml, resp);
|
||||
if (soap == NULL) {
|
||||
debug_print(ctx, 1, "SOAP envelope building failed");
|
||||
return -1;
|
||||
}
|
||||
str = xml_node_to_str(ctx->xml, soap);
|
||||
xml_node_free(ctx->xml, soap);
|
||||
if (str == NULL) {
|
||||
debug_print(ctx, 1, "Could not get node string");
|
||||
return -1;
|
||||
}
|
||||
printf("%s", str);
|
||||
free(str);
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
|
||||
static void usage(void)
|
||||
{
|
||||
printf("usage:\n"
|
||||
"hs20_spp_server -r<root directory> [-f<debug log>]\n");
|
||||
}
|
||||
|
||||
|
||||
int main(int argc, char *argv[])
|
||||
{
|
||||
struct hs20_svc ctx;
|
||||
int ret;
|
||||
|
||||
os_memset(&ctx, 0, sizeof(ctx));
|
||||
for (;;) {
|
||||
int c = getopt(argc, argv, "f:r:v");
|
||||
if (c < 0)
|
||||
break;
|
||||
switch (c) {
|
||||
case 'f':
|
||||
if (ctx.debug_log)
|
||||
break;
|
||||
ctx.debug_log = fopen(optarg, "a");
|
||||
if (ctx.debug_log == NULL) {
|
||||
printf("Could not write to %s\n", optarg);
|
||||
return -1;
|
||||
}
|
||||
break;
|
||||
case 'r':
|
||||
ctx.root_dir = optarg;
|
||||
break;
|
||||
case 'v':
|
||||
printf("hs20_spp_server v" VERSION_STR "\n");
|
||||
return 0;
|
||||
default:
|
||||
usage();
|
||||
return -1;
|
||||
}
|
||||
}
|
||||
if (ctx.root_dir == NULL) {
|
||||
usage();
|
||||
return -1;
|
||||
}
|
||||
ctx.xml = xml_node_init_ctx(&ctx, NULL);
|
||||
if (ctx.xml == NULL)
|
||||
return -1;
|
||||
if (hs20_spp_server_init(&ctx) < 0) {
|
||||
xml_node_deinit_ctx(ctx.xml);
|
||||
return -1;
|
||||
}
|
||||
|
||||
ret = process(&ctx);
|
||||
debug_print(&ctx, 1, "process() --> %d", ret);
|
||||
|
||||
xml_node_deinit_ctx(ctx.xml);
|
||||
hs20_spp_server_deinit(&ctx);
|
||||
if (ctx.debug_log)
|
||||
fclose(ctx.debug_log);
|
||||
|
||||
return ret;
|
||||
}
|
File diff suppressed because it is too large
Load Diff
|
@ -0,0 +1,36 @@
|
|||
/*
|
||||
* Hotspot 2.0 SPP server
|
||||
* Copyright (c) 2012-2013, Qualcomm Atheros, Inc.
|
||||
*
|
||||
* This software may be distributed under the terms of the BSD license.
|
||||
* See README for more details.
|
||||
*/
|
||||
|
||||
#ifndef SPP_SERVER_H
|
||||
#define SPP_SERVER_H
|
||||
|
||||
struct hs20_svc {
|
||||
const void *ctx;
|
||||
struct xml_node_ctx *xml;
|
||||
char *root_dir;
|
||||
FILE *debug_log;
|
||||
sqlite3 *db;
|
||||
const char *addr;
|
||||
const char *test;
|
||||
const char *imsi;
|
||||
const char *eap_method;
|
||||
const char *id_hash;
|
||||
};
|
||||
|
||||
|
||||
void debug_print(struct hs20_svc *ctx, int print, const char *fmt, ...)
|
||||
__attribute__ ((format (printf, 3, 4)));
|
||||
void debug_dump_node(struct hs20_svc *ctx, const char *title, xml_node_t *node);
|
||||
|
||||
xml_node_t * hs20_spp_server_process(struct hs20_svc *ctx, xml_node_t *node,
|
||||
const char *auth_user,
|
||||
const char *auth_realm, int dmacc);
|
||||
int hs20_spp_server_init(struct hs20_svc *ctx);
|
||||
void hs20_spp_server_deinit(struct hs20_svc *ctx);
|
||||
|
||||
#endif /* SPP_SERVER_H */
|
|
@ -0,0 +1,17 @@
|
|||
INSERT INTO osu_config(realm,field,value) VALUES('example.com','fqdn','example.com');
|
||||
INSERT INTO osu_config(realm,field,value) VALUES('example.com','friendly_name','Example Operator');
|
||||
INSERT INTO osu_config(realm,field,value) VALUES('example.com','spp_http_auth_url','https://subscription-server.osu.example.com/hs20/spp.php?realm=example.com');
|
||||
INSERT INTO osu_config(realm,field,value) VALUES('example.com','trust_root_cert_url','https://osu-server.osu.example.com/hs20/files/spp-root-ca.der');
|
||||
INSERT INTO osu_config(realm,field,value) VALUES('example.com','trust_root_cert_fingerprint','5b393a9246865569485c2605c3304e48212b449367858299beba9384c4cf4647');
|
||||
INSERT INTO osu_config(realm,field,value) VALUES('example.com','aaa_trust_root_cert_url','https://osu-server.osu.example.com/hs20/files/aaa-root-ca.der');
|
||||
INSERT INTO osu_config(realm,field,value) VALUES('example.com','aaa_trust_root_cert_fingerprint','5b393a9246865569485c2605c3304e48212b449367858299beba9384c4cf4647');
|
||||
INSERT INTO osu_config(realm,field,value) VALUES('example.com','free_account','free');
|
||||
INSERT INTO osu_config(realm,field,value) VALUES('example.com','policy_url','https://subscription-server.osu.example.com/hs20/spp.php?realm=example.com');
|
||||
INSERT INTO osu_config(realm,field,value) VALUES('example.com','remediation_url','https://subscription-server.osu.example.com/hs20/remediation.php?session_id=');
|
||||
INSERT INTO osu_config(realm,field,value) VALUES('example.com','free_remediation_url','https://subscription-server.osu.example.com/hs20/free-remediation.php?session_id=');
|
||||
INSERT INTO osu_config(realm,field,value) VALUES('example.com','signup_url','https://subscription-server.osu.example.com/hs20/signup.php?session_id=');
|
||||
|
||||
|
||||
INSERT INTO users(identity,realm,methods,password,phase2,shared) VALUES('free','example.com','TTLS-MSCHAPV2','free',1,1);
|
||||
|
||||
INSERT INTO wildcards(identity,methods) VALUES('','TTLS,TLS');
|
|
@ -0,0 +1,108 @@
|
|||
CREATE TABLE eventlog(
|
||||
user TEXT,
|
||||
realm TEXT,
|
||||
sessionid TEXT COLLATE NOCASE,
|
||||
timestamp TEXT,
|
||||
notes TEXT,
|
||||
dump TEXT,
|
||||
addr TEXT
|
||||
);
|
||||
|
||||
CREATE TABLE sessions(
|
||||
timestamp TEXT,
|
||||
id TEXT COLLATE NOCASE,
|
||||
user TEXT,
|
||||
realm TEXT,
|
||||
password TEXT,
|
||||
machine_managed BOOLEAN,
|
||||
operation INTEGER,
|
||||
type TEXT,
|
||||
pps TEXT,
|
||||
redirect_uri TEXT,
|
||||
devinfo TEXT,
|
||||
devdetail TEXT,
|
||||
cert TEXT,
|
||||
cert_pem TEXT,
|
||||
mac_addr TEXT,
|
||||
osu_user TEXT,
|
||||
osu_password TEXT,
|
||||
eap_method TEXT,
|
||||
mobile_identifier_hash TEXT,
|
||||
test TEXT
|
||||
);
|
||||
|
||||
CREATE index sessions_id_index ON sessions(id);
|
||||
|
||||
CREATE TABLE osu_config(
|
||||
realm TEXT,
|
||||
field TEXT,
|
||||
value TEXT
|
||||
);
|
||||
|
||||
CREATE TABLE users(
|
||||
identity TEXT PRIMARY KEY,
|
||||
methods TEXT,
|
||||
password TEXT,
|
||||
machine_managed BOOLEAN,
|
||||
remediation TEXT,
|
||||
phase2 INTEGER,
|
||||
realm TEXT,
|
||||
policy TEXT,
|
||||
devinfo TEXT,
|
||||
devdetail TEXT,
|
||||
pps TEXT,
|
||||
fetch_pps INTEGER,
|
||||
osu_user TEXT,
|
||||
osu_password TEXT,
|
||||
shared INTEGER,
|
||||
cert TEXT,
|
||||
cert_pem TEXT,
|
||||
t_c_timestamp INTEGER,
|
||||
mac_addr TEXT,
|
||||
last_msk TEXT,
|
||||
polupd_done TEXT,
|
||||
subrem TEXT
|
||||
);
|
||||
|
||||
CREATE TABLE wildcards(
|
||||
identity TEXT PRIMARY KEY,
|
||||
methods TEXT
|
||||
);
|
||||
|
||||
CREATE TABLE authlog(
|
||||
timestamp TEXT,
|
||||
session TEXT,
|
||||
nas_ip TEXT,
|
||||
username TEXT,
|
||||
note TEXT
|
||||
);
|
||||
|
||||
CREATE TABLE pending_tc(
|
||||
mac_addr TEXT PRIMARY KEY,
|
||||
identity TEXT
|
||||
);
|
||||
|
||||
CREATE TABLE current_sessions(
|
||||
mac_addr TEXT PRIMARY KEY,
|
||||
identity TEXT,
|
||||
start_time TEXT,
|
||||
nas TEXT,
|
||||
hs20_t_c_filtering BOOLEAN,
|
||||
waiting_coa_ack BOOLEAN,
|
||||
coa_ack_received BOOLEAN
|
||||
);
|
||||
|
||||
CREATE TABLE cert_enroll(
|
||||
mac_addr TEXT PRIMARY KEY,
|
||||
user TEXT,
|
||||
realm TEXT,
|
||||
serialnum TEXT
|
||||
);
|
||||
|
||||
CREATE TABLE sim_provisioning(
|
||||
mobile_identifier_hash TEXT PRIMARY KEY,
|
||||
imsi TEXT,
|
||||
mac_addr TEXT,
|
||||
eap_method TEXT,
|
||||
timestamp TEXT
|
||||
);
|
|
@ -0,0 +1,50 @@
|
|||
<?php
|
||||
|
||||
require('config.php');
|
||||
|
||||
$db = new PDO($osu_db);
|
||||
if (!$db) {
|
||||
die($sqliteerror);
|
||||
}
|
||||
|
||||
if (isset($_POST["id"]))
|
||||
$id = preg_replace("/[^a-fA-F0-9]/", "", $_POST["id"]);
|
||||
else
|
||||
die("Missing session id");
|
||||
if (strlen($id) < 32)
|
||||
die("Invalid session id");
|
||||
|
||||
$row = $db->query("SELECT rowid,* FROM sessions WHERE id='$id'")->fetch();
|
||||
if ($row == false) {
|
||||
die("Session not found");
|
||||
}
|
||||
|
||||
$uri = $row['redirect_uri'];
|
||||
$rowid = $row['rowid'];
|
||||
$realm = $row['realm'];
|
||||
|
||||
$row = $db->query("SELECT value FROM osu_config WHERE realm='$realm' AND field='free_account'")->fetch();
|
||||
if (!$row || strlen($row['value']) == 0) {
|
||||
die("Free account disabled");
|
||||
}
|
||||
|
||||
$user = $row['value'];
|
||||
|
||||
$row = $db->query("SELECT password FROM users WHERE identity='$user' AND realm='$realm'")->fetch();
|
||||
if (!$row)
|
||||
die("Free account not found");
|
||||
|
||||
$pw = $row['password'];
|
||||
|
||||
if (!$db->exec("UPDATE sessions SET user='$user', password='$pw', realm='$realm', machine_managed='1' WHERE rowid=$rowid")) {
|
||||
die("Failed to update session database");
|
||||
}
|
||||
|
||||
$db->exec("INSERT INTO eventlog(user,realm,sessionid,timestamp,notes) " .
|
||||
"VALUES ('$user', '$realm', '$id', " .
|
||||
"strftime('%Y-%m-%d %H:%M:%f','now'), " .
|
||||
"'completed user input response for a new PPS MO')");
|
||||
|
||||
header("Location: $uri", true, 302);
|
||||
|
||||
?>
|
|
@ -0,0 +1,56 @@
|
|||
<?php
|
||||
|
||||
require('config.php');
|
||||
|
||||
$db = new PDO($osu_db);
|
||||
if (!$db) {
|
||||
die($sqliteerror);
|
||||
}
|
||||
|
||||
if (isset($_POST["id"]))
|
||||
$id = preg_replace("/[^a-fA-F0-9]/", "", $_POST["id"]);
|
||||
else
|
||||
die("Missing session id");
|
||||
|
||||
$user = $_POST["user"];
|
||||
$pw = $_POST["password"];
|
||||
if (strlen($id) < 32 || !isset($user) || !isset($pw)) {
|
||||
die("Invalid POST data");
|
||||
}
|
||||
|
||||
if (strlen($user) < 1 || strncasecmp($user, "cert-", 5) == 0) {
|
||||
echo "<html><body><p><red>Invalid username</red></p>\n";
|
||||
echo "<a href=\"signup.php?session_id=$id\">Try again</a>\n";
|
||||
echo "</body></html>\n";
|
||||
exit;
|
||||
}
|
||||
|
||||
$row = $db->query("SELECT rowid,* FROM sessions WHERE id='$id'")->fetch();
|
||||
if ($row == false) {
|
||||
die("Session not found");
|
||||
}
|
||||
$realm = $row['realm'];
|
||||
|
||||
$userrow = $db->query("SELECT identity FROM users WHERE identity='$user' AND realm='$realm'")->fetch();
|
||||
if ($userrow) {
|
||||
echo "<html><body><p><red>Selected username is not available</red></p>\n";
|
||||
echo "<a href=\"signup.php?session_id=$id\">Try again</a>\n";
|
||||
echo "</body></html>\n";
|
||||
exit;
|
||||
}
|
||||
|
||||
$uri = $row['redirect_uri'];
|
||||
$rowid = $row['rowid'];
|
||||
|
||||
if (!$db->exec("UPDATE sessions SET user='$user', password='$pw', realm='$realm', type='password' WHERE rowid=$rowid")) {
|
||||
die("Failed to update session database");
|
||||
}
|
||||
|
||||
$db->exec("INSERT INTO eventlog(user,realm,sessionid,timestamp,notes) " .
|
||||
"VALUES ('$user', '$realm', '$id', " .
|
||||
"strftime('%Y-%m-%d %H:%M:%f','now'), " .
|
||||
"'completed user input response for a new PPS MO')");
|
||||
|
||||
header("Location: $uri", true, 302);
|
||||
|
||||
?>
|
|
@ -0,0 +1,39 @@
|
|||
<?php
|
||||
|
||||
require('config.php');
|
||||
|
||||
$db = new PDO($osu_db);
|
||||
if (!$db) {
|
||||
die($sqliteerror);
|
||||
}
|
||||
|
||||
if (isset($_GET["id"]))
|
||||
$id = preg_replace("/[^a-fA-F0-9]/", "", $_GET["id"]);
|
||||
else
|
||||
die("Missing session id");
|
||||
if (strlen($id) < 32)
|
||||
die("Invalid session id");
|
||||
|
||||
$row = $db->query("SELECT rowid,* FROM sessions WHERE id='$id'")->fetch();
|
||||
if ($row == false) {
|
||||
die("Session not found");
|
||||
}
|
||||
|
||||
$uri = $row['redirect_uri'];
|
||||
$rowid = $row['rowid'];
|
||||
$realm = $row['realm'];
|
||||
|
||||
$user = sha1(mt_rand());
|
||||
|
||||
if (!$db->exec("UPDATE sessions SET user='$user', type='cert' WHERE rowid=$rowid")) {
|
||||
die("Failed to update session database");
|
||||
}
|
||||
|
||||
$db->exec("INSERT INTO eventlog(user,realm,sessionid,timestamp,notes) " .
|
||||
"VALUES ('', '$realm', '$id', " .
|
||||
"strftime('%Y-%m-%d %H:%M:%f','now'), " .
|
||||
"'completed user input response for client certificate enrollment')");
|
||||
|
||||
header("Location: $uri", true, 302);
|
||||
|
||||
?>
|
|
@ -0,0 +1,7 @@
|
|||
<?php
|
||||
$osu_root = "/home/user/hs20-server";
|
||||
$osu_db = "sqlite:$osu_root/AS/DB/eap_user.db";
|
||||
$t_c_file = "$osu_root/terms-and-conditions";
|
||||
$t_c_timestamp = 123456789;
|
||||
$hostapd_ctrl = "udg:///home/user/hs20-server/AS/ctrl/as"
|
||||
?>
|
|
@ -0,0 +1,232 @@
|
|||
<?php
|
||||
|
||||
require('config.php');
|
||||
|
||||
$params = explode("/", $_SERVER["PATH_INFO"], 3);
|
||||
$realm = $params[1];
|
||||
$cmd = $params[2];
|
||||
$method = $_SERVER["REQUEST_METHOD"];
|
||||
|
||||
unset($user);
|
||||
unset($rowid);
|
||||
|
||||
$db = new PDO($osu_db);
|
||||
if (!$db) {
|
||||
error_log("EST: Could not access database");
|
||||
die("Could not access database");
|
||||
}
|
||||
|
||||
if (!empty($_SERVER['PHP_AUTH_DIGEST'])) {
|
||||
$needed = array('nonce'=>1, 'nc'=>1, 'cnonce'=>1, 'qop'=>1, 'username'=>1,
|
||||
'uri'=>1, 'response'=>1);
|
||||
$data = array();
|
||||
$keys = implode('|', array_keys($needed));
|
||||
preg_match_all('@(' . $keys . ')=(?:([\'"])([^\2]+?)\2|([^\s,]+))@',
|
||||
$_SERVER['PHP_AUTH_DIGEST'], $matches, PREG_SET_ORDER);
|
||||
foreach ($matches as $m) {
|
||||
$data[$m[1]] = $m[3] ? $m[3] : $m[4];
|
||||
unset($needed[$m[1]]);
|
||||
}
|
||||
if ($needed) {
|
||||
error_log("EST: Missing auth parameter");
|
||||
die('Authentication failed');
|
||||
}
|
||||
$user = $data['username'];
|
||||
if (strlen($user) < 1) {
|
||||
error_log("EST: Empty username");
|
||||
die('Authentication failed');
|
||||
}
|
||||
|
||||
$sql = "SELECT rowid,password,operation FROM sessions " .
|
||||
"WHERE user='$user' AND realm='$realm'";
|
||||
$q = $db->query($sql);
|
||||
if (!$q) {
|
||||
error_log("EST: Session not found for user=$user realm=$realm");
|
||||
die("Session not found");
|
||||
}
|
||||
$row = $q->fetch();
|
||||
if (!$row) {
|
||||
error_log("EST: Session fetch failed for user=$user realm=$realm");
|
||||
die('Session not found');
|
||||
}
|
||||
$rowid = $row['rowid'];
|
||||
|
||||
$oper = $row['operation'];
|
||||
if ($oper != '5') {
|
||||
error_log("EST: Unexpected operation $oper for user=$user realm=$realm");
|
||||
die("Session not found");
|
||||
}
|
||||
$pw = $row['password'];
|
||||
if (strlen($pw) < 1) {
|
||||
error_log("EST: Empty password for user=$user realm=$realm");
|
||||
die('Authentication failed');
|
||||
}
|
||||
|
||||
$A1 = md5($user . ':' . $realm . ':' . $pw);
|
||||
$A2 = md5($method . ':' . $data['uri']);
|
||||
$resp = md5($A1 . ':' . $data['nonce'] . ':' . $data['nc'] . ':' .
|
||||
$data['cnonce'] . ':' . $data['qop'] . ':' . $A2);
|
||||
if ($data['response'] != $resp) {
|
||||
error_log("EST: Incorrect authentication response for user=$user realm=$realm");
|
||||
die('Authentication failed');
|
||||
}
|
||||
} else if (isset($_SERVER["SSL_CLIENT_VERIFY"]) &&
|
||||
$_SERVER["SSL_CLIENT_VERIFY"] == "SUCCESS" &&
|
||||
isset($_SERVER["SSL_CLIENT_M_SERIAL"])) {
|
||||
$user = "cert-" . $_SERVER["SSL_CLIENT_M_SERIAL"];
|
||||
$sql = "SELECT rowid,password,operation FROM sessions " .
|
||||
"WHERE user='$user' AND realm='$realm'";
|
||||
$q = $db->query($sql);
|
||||
if (!$q) {
|
||||
error_log("EST: Session not found for user=$user realm=$realm");
|
||||
die("Session not found");
|
||||
}
|
||||
$row = $q->fetch();
|
||||
if (!$row) {
|
||||
error_log("EST: Session fetch failed for user=$user realm=$realm");
|
||||
die('Session not found');
|
||||
}
|
||||
$rowid = $row['rowid'];
|
||||
|
||||
$oper = $row['operation'];
|
||||
if ($oper != '10') {
|
||||
error_log("EST: Unexpected operation $oper for user=$user realm=$realm");
|
||||
die("Session not found");
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
if ($method == "GET" && $cmd == "cacerts") {
|
||||
$fname = "$osu_root/est/$realm-cacerts.pkcs7";
|
||||
if (!file_exists($fname)) {
|
||||
error_log("EST: cacerts - unknown realm $realm");
|
||||
die("Unknown realm");
|
||||
}
|
||||
|
||||
header("Content-Transfer-Encoding: base64");
|
||||
header("Content-Type: application/pkcs7-mime");
|
||||
|
||||
$data = file_get_contents($fname);
|
||||
echo wordwrap(base64_encode($data), 72, "\n", true);
|
||||
echo "\n";
|
||||
error_log("EST: cacerts");
|
||||
} else if ($method == "GET" && $cmd == "csrattrs") {
|
||||
header("Content-Transfer-Encoding: base64");
|
||||
header("Content-Type: application/csrattrs");
|
||||
readfile("$osu_root/est/est-attrs.b64");
|
||||
error_log("EST: csrattrs");
|
||||
} else if ($method == "POST" &&
|
||||
($cmd == "simpleenroll" || $cmd == "simplereenroll")) {
|
||||
$reenroll = $cmd == "simplereenroll";
|
||||
if (!$reenroll && (!isset($user) || strlen($user) == 0)) {
|
||||
header('HTTP/1.1 401 Unauthorized');
|
||||
header('WWW-Authenticate: Digest realm="'.$realm.
|
||||
'",qop="auth",nonce="'.uniqid().'",opaque="'.md5($realm).'"');
|
||||
error_log("EST: simpleenroll - require authentication");
|
||||
die('Authentication required');
|
||||
}
|
||||
if ($reenroll &&
|
||||
(!isset($user) ||
|
||||
!isset($_SERVER["SSL_CLIENT_VERIFY"]) ||
|
||||
$_SERVER["SSL_CLIENT_VERIFY"] != "SUCCESS")) {
|
||||
header('HTTP/1.1 403 Forbidden');
|
||||
error_log("EST: simplereenroll - require certificate authentication");
|
||||
die('Authentication required');
|
||||
}
|
||||
if (!isset($_SERVER["CONTENT_TYPE"])) {
|
||||
error_log("EST: simpleenroll without Content-Type");
|
||||
die("Missing Content-Type");
|
||||
}
|
||||
if (!stristr($_SERVER["CONTENT_TYPE"], "application/pkcs10")) {
|
||||
error_log("EST: simpleenroll - unexpected Content-Type: " .
|
||||
$_SERVER["CONTENT_TYPE"]);
|
||||
die("Unexpected Content-Type");
|
||||
}
|
||||
|
||||
$data = file_get_contents("php://input");
|
||||
error_log("EST: simpleenroll - POST data from php://input: " . $data);
|
||||
$req = base64_decode($data);
|
||||
if ($req == FALSE) {
|
||||
error_log("EST: simpleenroll - Invalid base64-encoded PKCS#10 data");
|
||||
die("Invalid base64-encoded PKCS#10 data");
|
||||
}
|
||||
$cadir = "$osu_root/est";
|
||||
$reqfile = "$cadir/tmp/cert-req.pkcs10";
|
||||
$f = fopen($reqfile, "wb");
|
||||
fwrite($f, $req);
|
||||
fclose($f);
|
||||
|
||||
$req_pem = "$reqfile.pem";
|
||||
if (file_exists($req_pem))
|
||||
unlink($req_pem);
|
||||
exec("openssl req -in $reqfile -inform DER -out $req_pem -outform PEM");
|
||||
if (!file_exists($req_pem)) {
|
||||
error_log("EST: simpleenroll - Failed to parse certificate request");
|
||||
die("Failed to parse certificate request");
|
||||
}
|
||||
|
||||
/* FIX: validate request and add HS 2.0 extensions to cert */
|
||||
$cert_pem = "$cadir/tmp/req-signed.pem";
|
||||
if (file_exists($cert_pem))
|
||||
unlink($cert_pem);
|
||||
exec("openssl x509 -req -in $req_pem -CAkey $cadir/cakey.pem -out $cert_pem -CA $cadir/cacert.pem -CAserial $cadir/serial -days 365 -text");
|
||||
if (!file_exists($cert_pem)) {
|
||||
error_log("EST: simpleenroll - Failed to sign certificate");
|
||||
die("Failed to sign certificate");
|
||||
}
|
||||
|
||||
$cert = file_get_contents($cert_pem);
|
||||
$handle = popen("openssl x509 -in $cert_pem -serial -noout", "r");
|
||||
$serial = fread($handle, 200);
|
||||
pclose($handle);
|
||||
$pattern = "/serial=(?P<snhex>[0-9a-fA-F:]*)/m";
|
||||
preg_match($pattern, $serial, $matches);
|
||||
if (!isset($matches['snhex']) || strlen($matches['snhex']) < 1) {
|
||||
error_log("EST: simpleenroll - Could not get serial number");
|
||||
die("Could not get serial number");
|
||||
}
|
||||
$sn = str_replace(":", "", strtoupper($matches['snhex']));
|
||||
|
||||
$user = "cert-$sn";
|
||||
error_log("EST: user = $user");
|
||||
|
||||
$cert_der = "$cadir/tmp/req-signed.der";
|
||||
if (file_exists($cert_der))
|
||||
unlink($cert_der);
|
||||
exec("openssl x509 -in $cert_pem -inform PEM -out $cert_der -outform DER");
|
||||
if (!file_exists($cert_der)) {
|
||||
error_log("EST: simpleenroll - Failed to convert certificate");
|
||||
die("Failed to convert certificate");
|
||||
}
|
||||
$der = file_get_contents($cert_der);
|
||||
$fingerprint = hash("sha256", $der);
|
||||
error_log("EST: sha256(DER cert): $fingerprint");
|
||||
|
||||
$pkcs7 = "$cadir/tmp/est-client.pkcs7";
|
||||
if (file_exists($pkcs7))
|
||||
unlink($pkcs7);
|
||||
exec("openssl crl2pkcs7 -nocrl -certfile $cert_pem -out $pkcs7 -outform DER");
|
||||
if (!file_exists($pkcs7)) {
|
||||
error_log("EST: simpleenroll - Failed to prepare PKCS#7 file");
|
||||
die("Failed to prepare PKCS#7 file");
|
||||
}
|
||||
|
||||
if (!$db->exec("UPDATE sessions SET user='$user', cert='$fingerprint', cert_pem='$cert' WHERE rowid=$rowid")) {
|
||||
error_log("EST: simpleenroll - Failed to update session database");
|
||||
die("Failed to update session database");
|
||||
}
|
||||
|
||||
header("Content-Transfer-Encoding: base64");
|
||||
header("Content-Type: application/pkcs7-mime");
|
||||
|
||||
$data = file_get_contents($pkcs7);
|
||||
$resp = wordwrap(base64_encode($data), 72, "\n", true);
|
||||
echo $resp . "\n";
|
||||
error_log("EST: simpleenroll - PKCS#7 response: " . $resp);
|
||||
} else {
|
||||
header("HTTP/1.0 404 Not Found");
|
||||
error_log("EST: Unexpected method or path");
|
||||
die("Unexpected method or path");
|
||||
}
|
||||
|
||||
?>
|
|
@ -0,0 +1,19 @@
|
|||
<html>
|
||||
<head>
|
||||
<title>Hotspot 2.0 - public and free hotspot - remediation</title>
|
||||
</head>
|
||||
<body>
|
||||
|
||||
<h3>Hotspot 2.0 - public and free hotspot</h3>
|
||||
|
||||
<p>Terms and conditions have changed. You need to accept the new terms
|
||||
to continue using this network.</p>
|
||||
|
||||
<p>Terms and conditions..</p>
|
||||
|
||||
<?php
|
||||
echo "<a href=\"redirect.php?id=" . $_GET["session_id"] . "\">Accept</a><br>\n";
|
||||
?>
|
||||
|
||||
</body>
|
||||
</html>
|
|
@ -0,0 +1,23 @@
|
|||
<html>
|
||||
<head>
|
||||
<title>Hotspot 2.0 - public and free hotspot</title>
|
||||
</head>
|
||||
<body>
|
||||
|
||||
<?php
|
||||
|
||||
$id = $_GET["session_id"];
|
||||
|
||||
echo "<h3>Hotspot 2.0 - public and free hotspot</h3>\n";
|
||||
|
||||
echo "<form action=\"add-free.php\" method=\"POST\">\n";
|
||||
echo "<input type=\"hidden\" name=\"id\" value=\"$id\">\n";
|
||||
|
||||
?>
|
||||
|
||||
<p>Terms and conditions..</p>
|
||||
<input type="submit" value="Accept">
|
||||
</form>
|
||||
|
||||
</body>
|
||||
</html>
|
|
@ -0,0 +1,32 @@
|
|||
<?php
|
||||
|
||||
require('config.php');
|
||||
|
||||
$db = new PDO($osu_db);
|
||||
if (!$db) {
|
||||
die($sqliteerror);
|
||||
}
|
||||
|
||||
if (isset($_GET["id"]))
|
||||
$id = preg_replace("/[^a-fA-F0-9]/", "", $_GET["id"]);
|
||||
else
|
||||
$id = 0;
|
||||
|
||||
$row = $db->query("SELECT rowid,* FROM sessions WHERE id='$id'")->fetch();
|
||||
if ($row == false) {
|
||||
die("Session not found");
|
||||
}
|
||||
|
||||
$uri = $row['redirect_uri'];
|
||||
|
||||
header("Location: $uri", true, 302);
|
||||
|
||||
$user = $row['user'];
|
||||
$realm = $row['realm'];
|
||||
|
||||
$db->exec("INSERT INTO eventlog(user,realm,sessionid,timestamp,notes) " .
|
||||
"VALUES ('$user', '$realm', '$id', " .
|
||||
"strftime('%Y-%m-%d %H:%M:%f','now'), " .
|
||||
"'redirected after user input')");
|
||||
|
||||
?>
|
|
@ -0,0 +1,41 @@
|
|||
<?php
|
||||
|
||||
require('config.php');
|
||||
|
||||
$db = new PDO($osu_db);
|
||||
if (!$db) {
|
||||
die($sqliteerror);
|
||||
}
|
||||
|
||||
if (isset($_POST["id"]))
|
||||
$id = preg_replace("/[^a-fA-F0-9]/", "", $_POST["id"]);
|
||||
else
|
||||
die("Missing session id");
|
||||
|
||||
$pw = $_POST["password"];
|
||||
if (strlen($id) < 32 || !isset($pw)) {
|
||||
die("Invalid POST data");
|
||||
}
|
||||
|
||||
$row = $db->query("SELECT rowid,* FROM sessions WHERE id='$id'")->fetch();
|
||||
if ($row == false) {
|
||||
die("Session not found");
|
||||
}
|
||||
$user = $row['user'];
|
||||
$realm = $row['realm'];
|
||||
|
||||
$uri = $row['redirect_uri'];
|
||||
$rowid = $row['rowid'];
|
||||
|
||||
if (!$db->exec("UPDATE sessions SET password='$pw' WHERE rowid=$rowid")) {
|
||||
die("Failed to update session database");
|
||||
}
|
||||
|
||||
$db->exec("INSERT INTO eventlog(user,realm,sessionid,timestamp,notes) " .
|
||||
"VALUES ('$user', '$realm', '$id', " .
|
||||
"strftime('%Y-%m-%d %H:%M:%f','now'), " .
|
||||
"'completed user input response for subscription remediation')");
|
||||
|
||||
header("Location: $uri", true, 302);
|
||||
|
||||
?>
|
|
@ -0,0 +1,55 @@
|
|||
<html>
|
||||
<head>
|
||||
<title>Hotspot 2.0 subscription remediation</title>
|
||||
</head>
|
||||
<body>
|
||||
|
||||
<?php
|
||||
|
||||
require('config.php');
|
||||
|
||||
$db = new PDO($osu_db);
|
||||
if (!$db) {
|
||||
die($sqliteerror);
|
||||
}
|
||||
|
||||
if (isset($_GET["session_id"]))
|
||||
$id = preg_replace("/[^a-fA-F0-9]/", "", $_GET["session_id"]);
|
||||
else
|
||||
$id = 0;
|
||||
echo "SessionID: " . $id . "<br>\n";
|
||||
|
||||
$row = $db->query("SELECT * FROM sessions WHERE id='$id'")->fetch();
|
||||
if ($row == false) {
|
||||
die("Session not found");
|
||||
}
|
||||
|
||||
$username = $row['user'];
|
||||
echo "User: " . $username . "@" . $row['realm'] . "<br>\n";
|
||||
|
||||
$user = $db->query("SELECT machine_managed,methods FROM users WHERE identity='$username'")->fetch();
|
||||
if ($user == false) {
|
||||
die("User not found");
|
||||
}
|
||||
|
||||
echo "<hr><br>\n";
|
||||
|
||||
$cert = $user['methods'] == "TLS" || strncmp($username, "cert-", 5) == 0;
|
||||
|
||||
if ($cert) {
|
||||
echo "<a href=\"redirect.php?id=" . $_GET["session_id"] . "\">Complete user subscription remediation</a><br>\n";
|
||||
} else if ($user['machine_managed'] == "1") {
|
||||
echo "<a href=\"redirect.php?id=" . $_GET["session_id"] . "\">Complete user subscription remediation</a><br>\n";
|
||||
echo "This will provide a new machine-generated password.<br>\n";
|
||||
} else {
|
||||
echo "<form action=\"remediation-pw.php\" method=\"POST\">\n";
|
||||
echo "<input type=\"hidden\" name=\"id\" value=\"$id\">\n";
|
||||
echo "New password: <input type=\"password\" name=\"password\"><br>\n";
|
||||
echo "<input type=\"submit\" value=\"Change password\">\n";
|
||||
echo "</form>\n";
|
||||
}
|
||||
|
||||
?>
|
||||
|
||||
</body>
|
||||
</html>
|
|
@ -0,0 +1,59 @@
|
|||
<html>
|
||||
<head>
|
||||
<title>Hotspot 2.0 signup</title>
|
||||
</head>
|
||||
<body>
|
||||
|
||||
<?php
|
||||
|
||||
$id = $_GET["session_id"];
|
||||
|
||||
require('config.php');
|
||||
|
||||
$db = new PDO($osu_db);
|
||||
if (!$db) {
|
||||
die($sqliteerror);
|
||||
}
|
||||
|
||||
$row = $db->query("SELECT realm,test FROM sessions WHERE id='$id'")->fetch();
|
||||
if ($row == false) {
|
||||
die("Session not found for id: $id");
|
||||
}
|
||||
$realm = $row['realm'];
|
||||
$test = $row['test'];
|
||||
|
||||
if (strlen($test) > 0) {
|
||||
echo "<p style=\"color:#FF0000\">Special test functionality: $test</red></big></p>\n";
|
||||
}
|
||||
|
||||
echo "<h3>Sign up for a subscription - $realm</h3>\n";
|
||||
|
||||
echo "<p>This page can be used to select between three different types of subscriptions for testing purposes.</p>\n";
|
||||
|
||||
echo "<h4>Option 1 - shared free access credential</h4>\n";
|
||||
|
||||
$row = $db->query("SELECT value FROM osu_config WHERE realm='$realm' AND field='free_account'")->fetch();
|
||||
if ($row && strlen($row['value']) > 0) {
|
||||
echo "<p><a href=\"free.php?session_id=$id\">Sign up for free access</a></p>\n";
|
||||
}
|
||||
|
||||
echo "<h4>Option 2 - username/password credential</h4>\n";
|
||||
|
||||
echo "<form action=\"add-mo.php\" method=\"POST\">\n";
|
||||
echo "<input type=\"hidden\" name=\"id\" value=\"$id\">\n";
|
||||
?>
|
||||
Select a username and password. Leave password empty to get automatically
|
||||
generated and machine managed password.<br>
|
||||
Username: <input type="text" name="user"><br>
|
||||
Password: <input type="password" name="password"><br>
|
||||
<input type="submit" value="Complete subscription registration">
|
||||
</form>
|
||||
|
||||
<?php
|
||||
echo "<h4>Option 3 - client certificate credential</h4>\n";
|
||||
|
||||
echo "<p><a href=\"cert-enroll.php?id=$id\">Enroll a client certificate</a></p>\n"
|
||||
?>
|
||||
|
||||
</body>
|
||||
</html>
|
|
@ -0,0 +1,168 @@
|
|||
<?php
|
||||
|
||||
require('config.php');
|
||||
|
||||
if (!stristr($_SERVER["CONTENT_TYPE"], "application/soap+xml")) {
|
||||
error_log("spp.php - Unexpected Content-Type " . $_SERVER["CONTENT_TYPE"]);
|
||||
die("Unexpected Content-Type");
|
||||
}
|
||||
|
||||
if ($_SERVER["REQUEST_METHOD"] != "POST") {
|
||||
error_log("spp.php - Unexpected method " . $_SERVER["REQUEST_METHOD"]);
|
||||
die("Unexpected method");
|
||||
}
|
||||
|
||||
if (isset($_GET["realm"])) {
|
||||
$realm = $_GET["realm"];
|
||||
$realm = PREG_REPLACE("/[^0-9a-zA-Z\.\-]/i", '', $realm);
|
||||
} else {
|
||||
error_log("spp.php - Realm not specified");
|
||||
die("Realm not specified");
|
||||
}
|
||||
|
||||
if (isset($_GET["test"]))
|
||||
$test = PREG_REPLACE("/[^0-9a-zA-Z\_\-]/i", '', $_GET["test"]);
|
||||
else
|
||||
$test = "";
|
||||
|
||||
unset($user);
|
||||
putenv("HS20CERT");
|
||||
|
||||
if (!empty($_SERVER['PHP_AUTH_DIGEST'])) {
|
||||
$needed = array('nonce'=>1, 'nc'=>1, 'cnonce'=>1, 'qop'=>1, 'username'=>1,
|
||||
'uri'=>1, 'response'=>1);
|
||||
$data = array();
|
||||
$keys = implode('|', array_keys($needed));
|
||||
preg_match_all('@(' . $keys . ')=(?:([\'"])([^\2]+?)\2|([^\s,]+))@',
|
||||
$_SERVER['PHP_AUTH_DIGEST'], $matches, PREG_SET_ORDER);
|
||||
foreach ($matches as $m) {
|
||||
$data[$m[1]] = $m[3] ? $m[3] : $m[4];
|
||||
unset($needed[$m[1]]);
|
||||
}
|
||||
if ($needed) {
|
||||
error_log("spp.php - Authentication failed - missing: " . print_r($needed));
|
||||
die('Authentication failed');
|
||||
}
|
||||
$user = $data['username'];
|
||||
if (strlen($user) < 1) {
|
||||
error_log("spp.php - Authentication failed - empty username");
|
||||
die('Authentication failed');
|
||||
}
|
||||
|
||||
|
||||
$db = new PDO($osu_db);
|
||||
if (!$db) {
|
||||
error_log("spp.php - Could not access database");
|
||||
die("Could not access database");
|
||||
}
|
||||
$row = $db->query("SELECT password FROM users " .
|
||||
"WHERE identity='$user' AND realm='$realm'")->fetch();
|
||||
if (!$row) {
|
||||
$row = $db->query("SELECT osu_password FROM users " .
|
||||
"WHERE osu_user='$user' AND realm='$realm'")->fetch();
|
||||
$pw = $row['osu_password'];
|
||||
} else
|
||||
$pw = $row['password'];
|
||||
if (!$row) {
|
||||
error_log("spp.php - Authentication failed - user '$user' not found");
|
||||
die('Authentication failed');
|
||||
}
|
||||
if (strlen($pw) < 1) {
|
||||
error_log("spp.php - Authentication failed - empty password");
|
||||
die('Authentication failed');
|
||||
}
|
||||
|
||||
$A1 = md5($user . ':' . $realm . ':' . $pw);
|
||||
$A2 = md5($_SERVER['REQUEST_METHOD'] . ':' . $data['uri']);
|
||||
$resp = md5($A1 . ':' . $data['nonce'] . ':' . $data['nc'] . ':' .
|
||||
$data['cnonce'] . ':' . $data['qop'] . ':' . $A2);
|
||||
if ($data['response'] != $resp) {
|
||||
error_log("Authentication failure - response mismatch");
|
||||
die('Authentication failed');
|
||||
}
|
||||
} else if (isset($_SERVER["SSL_CLIENT_VERIFY"]) &&
|
||||
$_SERVER["SSL_CLIENT_VERIFY"] == "SUCCESS" &&
|
||||
isset($_SERVER["SSL_CLIENT_M_SERIAL"])) {
|
||||
$user = "cert-" . $_SERVER["SSL_CLIENT_M_SERIAL"];
|
||||
putenv("HS20CERT=yes");
|
||||
} else if (isset($_GET["hotspot2dot0-mobile-identifier-hash"])) {
|
||||
$id_hash = $_GET["hotspot2dot0-mobile-identifier-hash"];
|
||||
$id_hash = PREG_REPLACE("/[^0-9a-h]/i", '', $id_hash);
|
||||
|
||||
$db = new PDO($osu_db);
|
||||
if (!$db) {
|
||||
error_log("spp.php - Could not access database");
|
||||
die("Could not access database");
|
||||
}
|
||||
|
||||
$row = $db->query("SELECT * FROM sim_provisioning " .
|
||||
"WHERE mobile_identifier_hash='$id_hash'")->fetch();
|
||||
if (!$row) {
|
||||
error_log("spp.php - SIM provisioning failed - mobile_identifier_hash not found");
|
||||
die('SIM provisioning failed - mobile_identifier_hash not found');
|
||||
}
|
||||
|
||||
$imsi = $row['imsi'];
|
||||
$mac_addr = $row['mac_addr'];
|
||||
$eap_method = $row['eap_method'];
|
||||
|
||||
$row = $db->query("SELECT COUNT(*) FROM osu_config " .
|
||||
"WHERE realm='$realm'")->fetch();
|
||||
if (!$row || intval($row[0]) < 1) {
|
||||
error_log("spp.php - SIM provisioning failed - realm $realm not found");
|
||||
die('SIM provisioning failed');
|
||||
}
|
||||
|
||||
error_log("spp.php - SIM provisioning for IMSI $imsi");
|
||||
putenv("HS20SIMPROV=yes");
|
||||
putenv("HS20IMSI=$imsi");
|
||||
putenv("HS20MACADDR=$mac_addr");
|
||||
putenv("HS20EAPMETHOD=$eap_method");
|
||||
putenv("HS20IDHASH=$id_hash");
|
||||
} else if (!isset($_SERVER["PATH_INFO"]) ||
|
||||
$_SERVER["PATH_INFO"] != "/signup") {
|
||||
header('HTTP/1.1 401 Unauthorized');
|
||||
header('WWW-Authenticate: Digest realm="'.$realm.
|
||||
'",qop="auth",nonce="'.uniqid().'",opaque="'.md5($realm).'"');
|
||||
error_log("spp.php - Authentication required (not signup)");
|
||||
die('Authentication required (not signup)');
|
||||
}
|
||||
|
||||
|
||||
if (isset($user) && strlen($user) > 0)
|
||||
putenv("HS20USER=$user");
|
||||
else
|
||||
putenv("HS20USER");
|
||||
|
||||
putenv("HS20REALM=$realm");
|
||||
$postdata = file_get_contents("php://input");
|
||||
putenv("HS20POST=$postdata");
|
||||
$addr = $_SERVER["REMOTE_ADDR"];
|
||||
putenv("HS20ADDR=$addr");
|
||||
putenv("HS20TEST=$test");
|
||||
|
||||
$last = exec("$osu_root/spp/hs20_spp_server -r$osu_root -f/tmp/hs20_spp_server.log", $output, $ret);
|
||||
|
||||
if ($ret == 2) {
|
||||
if (empty($_SERVER['PHP_AUTH_DIGEST'])) {
|
||||
header('HTTP/1.1 401 Unauthorized');
|
||||
header('WWW-Authenticate: Digest realm="'.$realm.
|
||||
'",qop="auth",nonce="'.uniqid().'",opaque="'.md5($realm).'"');
|
||||
error_log("spp.php - Authentication required (ret 2)");
|
||||
die('Authentication required');
|
||||
} else {
|
||||
error_log("spp.php - Unexpected authentication error");
|
||||
die("Unexpected authentication error");
|
||||
}
|
||||
}
|
||||
if ($ret != 0) {
|
||||
error_log("spp.php - Failed to process SPP request");
|
||||
die("Failed to process SPP request");
|
||||
}
|
||||
//error_log("spp.php: Response: " . implode($output));
|
||||
|
||||
header("Content-Type: application/soap+xml");
|
||||
|
||||
echo implode($output);
|
||||
|
||||
?>
|
|
@ -0,0 +1,87 @@
|
|||
<?php
|
||||
|
||||
require('config.php');
|
||||
|
||||
function print_header()
|
||||
{
|
||||
echo "<html>\n";
|
||||
echo "<head><title>HS 2.0 Terms and Conditions</title></head>\n";
|
||||
echo "<body>\n";
|
||||
}
|
||||
|
||||
$db = new PDO($osu_db);
|
||||
if (!$db) {
|
||||
die($sqliteerror);
|
||||
}
|
||||
|
||||
if (!isset($_GET["addr"])) {
|
||||
die("Missing addr parameter");
|
||||
}
|
||||
$addr = $_GET["addr"];
|
||||
|
||||
$accept = isset($_GET["accept"]) && $_GET["accept"] == "yes";
|
||||
|
||||
$res = $db->prepare("SELECT identity FROM pending_tc WHERE mac_addr=?");
|
||||
$res->execute(array($addr));
|
||||
$row = $res->fetch();
|
||||
if (!$row) {
|
||||
die("No pending session for the specified MAC address");
|
||||
}
|
||||
$identity = $row[0];
|
||||
|
||||
if (!$accept) {
|
||||
print_header();
|
||||
|
||||
echo "<p>Accept the following terms and conditions by clicking here: <a href=\"terms.php?addr=$addr&accept=yes\">Accept</a></p>\n<hr>\n";
|
||||
readfile($t_c_file);
|
||||
} else {
|
||||
$res = $db->prepare("UPDATE users SET t_c_timestamp=? WHERE identity=?");
|
||||
if (!$res->execute(array($t_c_timestamp, $identity))) {
|
||||
die("Failed to update user account.");
|
||||
}
|
||||
|
||||
$res = $db->prepare("DELETE FROM pending_tc WHERE mac_addr=?");
|
||||
$res->execute(array($addr));
|
||||
|
||||
$fp = fsockopen($hostapd_ctrl);
|
||||
if (!$fp) {
|
||||
die("Could not connect to hostapd(AS)");
|
||||
}
|
||||
|
||||
fwrite($fp, "DAC_REQUEST coa $addr t_c_clear");
|
||||
fclose($fp);
|
||||
|
||||
$waiting = true;
|
||||
$ack = false;
|
||||
for ($i = 1; $i <= 10; $i++) {
|
||||
$res = $db->prepare("SELECT waiting_coa_ack,coa_ack_received FROM current_sessions WHERE mac_addr=?");
|
||||
$res->execute(array($addr));
|
||||
$row = $res->fetch();
|
||||
if (!$row) {
|
||||
die("No current session for the specified MAC address");
|
||||
}
|
||||
if (strlen($row[0]) > 0)
|
||||
$waiting = $row[0] == 1;
|
||||
if (strlen($row[1]) > 0)
|
||||
$ack = $row[1] == 1;
|
||||
$res->closeCursor();
|
||||
if (!$waiting)
|
||||
break;
|
||||
sleep(1);
|
||||
}
|
||||
if ($ack) {
|
||||
header('X-WFA-Hotspot20-Filtering: removed');
|
||||
print_header();
|
||||
echo "<p>Terms and conditions were accepted.</p>\n";
|
||||
|
||||
echo "<P>Filtering disabled.</P>\n";
|
||||
} else {
|
||||
print_header();
|
||||
echo "<P>Failed to disable filtering.</P>\n";
|
||||
}
|
||||
}
|
||||
|
||||
?>
|
||||
|
||||
</body>
|
||||
</html>
|
|
@ -0,0 +1,377 @@
|
|||
<?php
|
||||
|
||||
require('config.php');
|
||||
|
||||
$db = new PDO($osu_db);
|
||||
if (!$db) {
|
||||
die($sqliteerror);
|
||||
}
|
||||
|
||||
if (isset($_GET["id"])) {
|
||||
$id = $_GET["id"];
|
||||
if (!is_numeric($id))
|
||||
$id = 0;
|
||||
} else
|
||||
$id = 0;
|
||||
if (isset($_GET["cmd"]))
|
||||
$cmd = $_GET["cmd"];
|
||||
else
|
||||
$cmd = '';
|
||||
|
||||
if ($cmd == 'eventlog' && $id > 0) {
|
||||
$row = $db->query("SELECT dump FROM eventlog WHERE rowid=$id")->fetch();
|
||||
$dump = $row['dump'];
|
||||
if ($dump[0] == '<') {
|
||||
header("Content-type: text/xml");
|
||||
echo "<?xml version=\"1.0\"?>\n";
|
||||
echo $dump;
|
||||
} else {
|
||||
header("Content-type: text/plain");
|
||||
echo $dump;
|
||||
}
|
||||
exit;
|
||||
}
|
||||
|
||||
if ($cmd == 'mo' && $id > 0) {
|
||||
$mo = $_GET["mo"];
|
||||
if (!isset($mo))
|
||||
exit;
|
||||
if ($mo != "devinfo" && $mo != "devdetail" && $mo != "pps")
|
||||
exit;
|
||||
$row = $db->query("SELECT $mo FROM users WHERE rowid=$id")->fetch();
|
||||
header("Content-type: text/xml");
|
||||
echo "<?xml version=\"1.0\"?>\n";
|
||||
echo $row[$mo];
|
||||
exit;
|
||||
}
|
||||
|
||||
if ($cmd == 'cert' && $id > 0) {
|
||||
$row = $db->query("SELECT cert_pem FROM users WHERE rowid=$id")->fetch();
|
||||
header("Content-type: text/plain");
|
||||
echo $row['cert_pem'];
|
||||
exit;
|
||||
}
|
||||
|
||||
?>
|
||||
|
||||
<html>
|
||||
<head><title>HS 2.0 users</title></head>
|
||||
<body>
|
||||
|
||||
<?php
|
||||
|
||||
if ($cmd == 'subrem-clear' && $id > 0) {
|
||||
$db->exec("UPDATE users SET remediation='' WHERE rowid=$id");
|
||||
}
|
||||
if ($cmd == 'subrem-add-user' && $id > 0) {
|
||||
$db->exec("UPDATE users SET remediation='user' WHERE rowid=$id");
|
||||
}
|
||||
if ($cmd == 'subrem-add-machine' && $id > 0) {
|
||||
$db->exec("UPDATE users SET remediation='machine' WHERE rowid=$id");
|
||||
}
|
||||
if ($cmd == 'subrem-add-reenroll' && $id > 0) {
|
||||
$db->exec("UPDATE users SET remediation='reenroll' WHERE rowid=$id");
|
||||
}
|
||||
if ($cmd == 'subrem-add-policy' && $id > 0) {
|
||||
$db->exec("UPDATE users SET remediation='policy' WHERE rowid=$id");
|
||||
}
|
||||
if ($cmd == 'subrem-add-free' && $id > 0) {
|
||||
$db->exec("UPDATE users SET remediation='free' WHERE rowid=$id");
|
||||
}
|
||||
if ($cmd == 'fetch-pps-on' && $id > 0) {
|
||||
$db->exec("UPDATE users SET fetch_pps=1 WHERE rowid=$id");
|
||||
}
|
||||
if ($cmd == 'fetch-pps-off' && $id > 0) {
|
||||
$db->exec("UPDATE users SET fetch_pps=0 WHERE rowid=$id");
|
||||
}
|
||||
if ($cmd == 'reset-pw' && $id > 0) {
|
||||
$db->exec("UPDATE users SET password='ChangeMe' WHERE rowid=$id");
|
||||
}
|
||||
if ($cmd == "policy" && $id > 0 && isset($_GET["policy"])) {
|
||||
$policy = $_GET["policy"];
|
||||
if ($policy == "no-policy" ||
|
||||
is_readable("$osu_root/spp/policy/$policy.xml")) {
|
||||
$db->exec("UPDATE users SET policy='$policy' WHERE rowid=$id");
|
||||
}
|
||||
}
|
||||
if ($cmd == "account-type" && $id > 0 && isset($_GET["type"])) {
|
||||
$type = $_GET["type"];
|
||||
if ($type == "shared")
|
||||
$db->exec("UPDATE users SET shared=1 WHERE rowid=$id");
|
||||
if ($type == "default")
|
||||
$db->exec("UPDATE users SET shared=0 WHERE rowid=$id");
|
||||
}
|
||||
|
||||
if ($cmd == "set-osu-cred" && $id > 0) {
|
||||
$osu_user = $_POST["osu_user"];
|
||||
$osu_password = $_POST["osu_password"];
|
||||
if (strlen($osu_user) == 0)
|
||||
$osu_password = "";
|
||||
$db->exec("UPDATE users SET osu_user='$osu_user', osu_password='$osu_password' WHERE rowid=$id");
|
||||
}
|
||||
|
||||
if ($cmd == 'clear-t-c' && $id > 0) {
|
||||
$db->exec("UPDATE users SET t_c_timestamp=NULL WHERE rowid=$id");
|
||||
}
|
||||
|
||||
$dump = 0;
|
||||
|
||||
if ($id > 0) {
|
||||
|
||||
if (isset($_GET["dump"])) {
|
||||
$dump = $_GET["dump"];
|
||||
if (!is_numeric($dump))
|
||||
$dump = 0;
|
||||
} else
|
||||
$dump = 0;
|
||||
|
||||
echo "[<a href=\"users.php\">All users</a>] ";
|
||||
if ($dump == 0)
|
||||
echo "[<a href=\"users.php?id=$id&dump=1\">Include debug dump</a>] ";
|
||||
else
|
||||
echo "[<a href=\"users.php?id=$id\">Without debug dump</a>] ";
|
||||
echo "<br>\n";
|
||||
|
||||
$row = $db->query("SELECT rowid,* FROM users WHERE rowid=$id")->fetch();
|
||||
|
||||
echo "<H3>" . $row['identity'] . "@" . $row['realm'] . "</H3>\n";
|
||||
|
||||
echo "MO: ";
|
||||
if (strlen($row['devinfo']) > 0) {
|
||||
echo "[<a href=\"users.php?cmd=mo&id=$id&mo=devinfo\">DevInfo</a>]\n";
|
||||
}
|
||||
if (strlen($row['devdetail']) > 0) {
|
||||
echo "[<a href=\"users.php?cmd=mo&id=$id&mo=devdetail\">DevDetail</a>]\n";
|
||||
}
|
||||
if (strlen($row['pps']) > 0) {
|
||||
echo "[<a href=\"users.php?cmd=mo&id=$id&mo=pps\">PPS</a>]\n";
|
||||
}
|
||||
if (strlen($row['cert_pem']) > 0) {
|
||||
echo "[<a href=\"users.php?cmd=cert&id=$id\">Certificate</a>]\n";
|
||||
}
|
||||
echo "<BR>\n";
|
||||
|
||||
echo "Fetch PPS MO: ";
|
||||
if ($row['fetch_pps'] == "1") {
|
||||
echo "On next connection " .
|
||||
"[<a href=\"users.php?cmd=fetch-pps-off&id=$id\">" .
|
||||
"do not fetch</a>]<br>\n";
|
||||
} else {
|
||||
echo "Do not fetch " .
|
||||
"[<a href=\"users.php?cmd=fetch-pps-on&id=$id\">" .
|
||||
"request fetch</a>]<br>\n";
|
||||
}
|
||||
|
||||
$cert = $row['cert'];
|
||||
if (strlen($cert) > 0) {
|
||||
echo "Certificate fingerprint: $cert<br>\n";
|
||||
}
|
||||
|
||||
echo "Remediation: ";
|
||||
$rem = $row['remediation'];
|
||||
if ($rem == "") {
|
||||
echo "Not required";
|
||||
echo " [<a href=\"users.php?cmd=subrem-add-user&id=" .
|
||||
$row['rowid'] . "\">add:user</a>]";
|
||||
echo " [<a href=\"users.php?cmd=subrem-add-machine&id=" .
|
||||
$row['rowid'] . "\">add:machine</a>]";
|
||||
if ($row['methods'] == 'TLS') {
|
||||
echo " [<a href=\"users.php?cmd=subrem-add-reenroll&id=" .
|
||||
$row['rowid'] . "\">add:reenroll</a>]";
|
||||
}
|
||||
echo " [<a href=\"users.php?cmd=subrem-add-policy&id=" .
|
||||
$row['rowid'] . "\">add:policy</a>]";
|
||||
echo " [<a href=\"users.php?cmd=subrem-add-free&id=" .
|
||||
$row['rowid'] . "\">add:free</a>]";
|
||||
} else if ($rem == "user") {
|
||||
echo "User [<a href=\"users.php?cmd=subrem-clear&id=" .
|
||||
$row['rowid'] . "\">clear</a>]";
|
||||
} else if ($rem == "policy") {
|
||||
echo "Policy [<a href=\"users.php?cmd=subrem-clear&id=" .
|
||||
$row['rowid'] . "\">clear</a>]";
|
||||
} else if ($rem == "free") {
|
||||
echo "Free [<a href=\"users.php?cmd=subrem-clear&id=" .
|
||||
$row['rowid'] . "\">clear</a>]";
|
||||
} else if ($rem == "reenroll") {
|
||||
echo "Reenroll [<a href=\"users.php?cmd=subrem-clear&id=" .
|
||||
$row['rowid'] . "\">clear</a>]";
|
||||
} else {
|
||||
echo "Machine [<a href=\"users.php?cmd=subrem-clear&id=" .
|
||||
$row['rowid'] . "\">clear</a>]";
|
||||
}
|
||||
echo "<br>\n";
|
||||
|
||||
if (strncmp($row['identity'], "cert-", 5) != 0)
|
||||
echo "Machine managed: " . ($row['machine_managed'] == "1" ? "TRUE" : "FALSE") . "<br>\n";
|
||||
|
||||
echo "<form>Policy: <select name=\"policy\" " .
|
||||
"onChange=\"window.location='users.php?cmd=policy&id=" .
|
||||
$row['rowid'] . "&policy=' + this.value;\">\n";
|
||||
echo "<option value=\"" . $row['policy'] . "\" selected>" . $row['policy'] .
|
||||
"</option>\n";
|
||||
$files = scandir("$osu_root/spp/policy");
|
||||
foreach ($files as $file) {
|
||||
if (!preg_match("/.xml$/", $file))
|
||||
continue;
|
||||
if ($file == $row['policy'] . ".xml")
|
||||
continue;
|
||||
$p = substr($file, 0, -4);
|
||||
echo "<option value=\"$p\">$p</option>\n";
|
||||
}
|
||||
echo "<option value=\"no-policy\">no policy</option>\n";
|
||||
echo "</select></form>\n";
|
||||
|
||||
echo "<form>Account type: <select name=\"type\" " .
|
||||
"onChange=\"window.location='users.php?cmd=account-type&id=" .
|
||||
$row['rowid'] . "&type=' + this.value;\">\n";
|
||||
if ($row['shared'] > 0) {
|
||||
$default_sel = "";
|
||||
$shared_sel = " selected";
|
||||
} else {
|
||||
$default_sel = " selected";
|
||||
$shared_sel = "";
|
||||
}
|
||||
echo "<option value=\"default\"$default_sel>default</option>\n";
|
||||
echo "<option value=\"shared\"$shared_sel>shared</option>\n";
|
||||
echo "</select></form>\n";
|
||||
|
||||
echo "Phase 2 method(s): " . $row['methods'] . "<br>\n";
|
||||
|
||||
echo "<br>\n";
|
||||
echo "<a href=\"users.php?cmd=reset-pw&id=" .
|
||||
$row['rowid'] . "\">Reset AAA password</a><br>\n";
|
||||
|
||||
echo "<br>\n";
|
||||
echo "<form action=\"users.php?cmd=set-osu-cred&id=" . $row['rowid'] .
|
||||
"\" method=\"POST\">\n";
|
||||
echo "OSU credentials (if username empty, AAA credentials are used):<br>\n";
|
||||
echo "username: <input type=\"text\" name=\"osu_user\" value=\"" .
|
||||
$row['osu_user'] . "\">\n";
|
||||
echo "password: <input type=\"password\" name=\"osu_password\">\n";
|
||||
echo "<input type=\"submit\" value=\"Set OSU credentials\">\n";
|
||||
echo "</form>\n";
|
||||
|
||||
if (strlen($row['t_c_timestamp']) > 0) {
|
||||
echo "<br>\n";
|
||||
echo "<a href=\"users.php?cmd=clear-t-c&id=" .
|
||||
$row['rowid'] .
|
||||
"\">Clear Terms and Conditions acceptance</a><br>\n";
|
||||
}
|
||||
|
||||
echo "<hr>\n";
|
||||
|
||||
$user = $row['identity'];
|
||||
$osu_user = $row['osu_user'];
|
||||
$realm = $row['realm'];
|
||||
}
|
||||
|
||||
if ($id > 0 || ($id == 0 && $cmd == 'eventlog')) {
|
||||
|
||||
if ($id == 0) {
|
||||
echo "[<a href=\"users.php\">All users</a>] ";
|
||||
echo "<br>\n";
|
||||
}
|
||||
|
||||
echo "<table border=1>\n";
|
||||
echo "<tr>";
|
||||
if ($id == 0) {
|
||||
echo "<th>user<th>realm";
|
||||
}
|
||||
echo "<th>time<th>address<th>sessionID<th>notes";
|
||||
if ($dump > 0)
|
||||
echo "<th>dump";
|
||||
echo "\n";
|
||||
if (isset($_GET["limit"])) {
|
||||
$limit = $_GET["limit"];
|
||||
if (!is_numeric($limit))
|
||||
$limit = 20;
|
||||
} else
|
||||
$limit = 20;
|
||||
if ($id == 0)
|
||||
$res = $db->query("SELECT rowid,* FROM eventlog ORDER BY timestamp DESC LIMIT $limit");
|
||||
else if (strlen($osu_user) > 0)
|
||||
$res = $db->query("SELECT rowid,* FROM eventlog WHERE (user='$user' OR user='$osu_user') AND realm='$realm' ORDER BY timestamp DESC LIMIT $limit");
|
||||
else
|
||||
$res = $db->query("SELECT rowid,* FROM eventlog WHERE user='$user' AND realm='$realm' ORDER BY timestamp DESC LIMIT $limit");
|
||||
foreach ($res as $row) {
|
||||
echo "<tr>";
|
||||
if ($id == 0) {
|
||||
echo "<td>" . $row['user'] . "\n";
|
||||
echo "<td>" . $row['realm'] . "\n";
|
||||
}
|
||||
echo "<td>" . $row['timestamp'] . "\n";
|
||||
echo "<td>" . $row['addr'] . "\n";
|
||||
echo "<td>" . $row['sessionid'] . "\n";
|
||||
echo "<td>" . $row['notes'] . "\n";
|
||||
$d = $row['dump'];
|
||||
if (strlen($d) > 0) {
|
||||
echo "[<a href=\"users.php?cmd=eventlog&id=" . $row['rowid'] .
|
||||
"\">";
|
||||
if ($d[0] == '<')
|
||||
echo "XML";
|
||||
else
|
||||
echo "txt";
|
||||
echo "</a>]\n";
|
||||
if ($dump > 0)
|
||||
echo "<td>" . htmlspecialchars($d) . "\n";
|
||||
}
|
||||
}
|
||||
echo "</table>\n";
|
||||
|
||||
}
|
||||
|
||||
|
||||
if ($id == 0 && $cmd != 'eventlog') {
|
||||
|
||||
echo "[<a href=\"users.php?cmd=eventlog&limit=50\">Eventlog</a>] ";
|
||||
echo "<br>\n";
|
||||
|
||||
echo "<table border=1 cellspacing=0 cellpadding=0>\n";
|
||||
echo "<tr><th>User<th>Realm<th><small>Remediation</small><th>Policy<th><small>Account type</small><th><small>Phase 2 method(s)</small><th>DevId<th>MAC Address<th>T&C\n";
|
||||
|
||||
$res = $db->query('SELECT rowid,* FROM users WHERE (phase2=1 OR methods=\'TLS\') ORDER BY identity');
|
||||
foreach ($res as $row) {
|
||||
echo "<tr><td><a href=\"users.php?id=" . $row['rowid'] . "\"> " .
|
||||
$row['identity'] . " </a>";
|
||||
echo "<td>" . $row['realm'];
|
||||
$rem = $row['remediation'];
|
||||
echo "<td>";
|
||||
if ($rem == "") {
|
||||
echo "-";
|
||||
} else if ($rem == "user") {
|
||||
echo "User";
|
||||
} else if ($rem == "policy") {
|
||||
echo "Policy";
|
||||
} else if ($rem == "free") {
|
||||
echo "Free";
|
||||
} else if ($rem == "reenroll") {
|
||||
echo "Reenroll";
|
||||
} else {
|
||||
echo "Machine";
|
||||
}
|
||||
echo "<td>" . $row['policy'];
|
||||
if ($row['shared'] > 0)
|
||||
echo "<td>shared";
|
||||
else
|
||||
echo "<td>default";
|
||||
echo "<td><small>" . $row['methods'] . "</small>";
|
||||
echo "<td>";
|
||||
$xml = xml_parser_create();
|
||||
xml_parse_into_struct($xml, $row['devinfo'], $devinfo);
|
||||
foreach($devinfo as $k) {
|
||||
if ($k['tag'] == 'DEVID') {
|
||||
echo "<small>" . $k['value'] . "</small>";
|
||||
break;
|
||||
}
|
||||
}
|
||||
echo "<td><small>" . $row['mac_addr'] . "</small>";
|
||||
echo "<td><small>" . $row['t_c_timestamp'] . "</small>";
|
||||
echo "\n";
|
||||
}
|
||||
echo "</table>\n";
|
||||
|
||||
}
|
||||
|
||||
?>
|
||||
|
||||
</html>
|
|
@ -0,0 +1,12 @@
|
|||
SUBDIRS=ap common crypto drivers eapol_auth eapol_supp eap_common eap_peer eap_server l2_packet p2p pae radius rsn_supp tls utils wps
|
||||
SUBDIRS += fst
|
||||
|
||||
all:
|
||||
for d in $(SUBDIRS); do [ -d $$d ] && $(MAKE) -C $$d; done
|
||||
|
||||
clean:
|
||||
for d in $(SUBDIRS); do [ -d $$d ] && $(MAKE) -C $$d clean; done
|
||||
rm -f *~
|
||||
|
||||
install:
|
||||
for d in $(SUBDIRS); do [ -d $$d ] && $(MAKE) -C $$d install; done
|
|
@ -0,0 +1,76 @@
|
|||
all: libap.a
|
||||
|
||||
clean:
|
||||
rm -f *~ *.o *.d *.gcno *.gcda *.gcov libap.a
|
||||
|
||||
install:
|
||||
@echo Nothing to be made.
|
||||
|
||||
include ../lib.rules
|
||||
|
||||
CFLAGS += -DHOSTAPD
|
||||
CFLAGS += -DNEED_AP_MLME
|
||||
CFLAGS += -DCONFIG_ETH_P_OUI
|
||||
CFLAGS += -DCONFIG_HS20
|
||||
CFLAGS += -DCONFIG_INTERWORKING
|
||||
CFLAGS += -DCONFIG_IEEE80211R
|
||||
CFLAGS += -DCONFIG_IEEE80211R_AP
|
||||
CFLAGS += -DCONFIG_IEEE80211W
|
||||
CFLAGS += -DCONFIG_WPS
|
||||
CFLAGS += -DCONFIG_PROXYARP
|
||||
CFLAGS += -DCONFIG_IPV6
|
||||
CFLAGS += -DCONFIG_IAPP
|
||||
CFLAGS += -DCONFIG_AIRTIME_POLICY
|
||||
|
||||
LIB_OBJS= \
|
||||
accounting.o \
|
||||
ap_config.o \
|
||||
ap_drv_ops.o \
|
||||
ap_list.o \
|
||||
ap_mlme.o \
|
||||
airtime_policy.o \
|
||||
authsrv.o \
|
||||
beacon.o \
|
||||
bss_load.o \
|
||||
ctrl_iface_ap.o \
|
||||
dfs.o \
|
||||
dhcp_snoop.o \
|
||||
drv_callbacks.o \
|
||||
eap_user_db.o \
|
||||
eth_p_oui.o \
|
||||
gas_serv.o \
|
||||
hostapd.o \
|
||||
hs20.o \
|
||||
hw_features.o \
|
||||
iapp.o \
|
||||
ieee802_11_auth.o \
|
||||
ieee802_11.o \
|
||||
ieee802_11_ht.o \
|
||||
ieee802_11_shared.o \
|
||||
ieee802_11_vht.o \
|
||||
ieee802_1x.o \
|
||||
neighbor_db.o \
|
||||
ndisc_snoop.o \
|
||||
p2p_hostapd.o \
|
||||
pmksa_cache_auth.o \
|
||||
preauth_auth.o \
|
||||
rrm.o \
|
||||
sta_info.o \
|
||||
tkip_countermeasures.o \
|
||||
utils.o \
|
||||
vlan.o \
|
||||
vlan_ifconfig.o \
|
||||
vlan_init.o \
|
||||
wmm.o \
|
||||
wnm_ap.o \
|
||||
wpa_auth.o \
|
||||
wpa_auth_ft.o \
|
||||
wpa_auth_glue.o \
|
||||
wpa_auth_ie.o \
|
||||
wps_hostapd.o \
|
||||
x_snoop.o
|
||||
|
||||
libap.a: $(LIB_OBJS)
|
||||
$(AR) crT $@ $?
|
||||
|
||||
-include $(OBJS:%.o=%.d)
|
|
@ -0,0 +1,547 @@
|
|||
/*
|
||||
* hostapd / RADIUS Accounting
|
||||
* Copyright (c) 2002-2009, 2012-2015, Jouni Malinen <j@w1.fi>
|
||||
*
|
||||
* This software may be distributed under the terms of the BSD license.
|
||||
* See README for more details.
|
||||
*/
|
||||
|
||||
#include "utils/includes.h"
|
||||
|
||||
#include "utils/common.h"
|
||||
#include "utils/eloop.h"
|
||||
#include "eapol_auth/eapol_auth_sm.h"
|
||||
#include "eapol_auth/eapol_auth_sm_i.h"
|
||||
#include "radius/radius.h"
|
||||
#include "radius/radius_client.h"
|
||||
#include "hostapd.h"
|
||||
#include "ieee802_1x.h"
|
||||
#include "ap_config.h"
|
||||
#include "sta_info.h"
|
||||
#include "ap_drv_ops.h"
|
||||
#include "accounting.h"
|
||||
|
||||
|
||||
/* Default interval in seconds for polling TX/RX octets from the driver if
|
||||
* STA is not using interim accounting. This detects wrap arounds for
|
||||
* input/output octets and updates Acct-{Input,Output}-Gigawords. */
|
||||
#define ACCT_DEFAULT_UPDATE_INTERVAL 300
|
||||
|
||||
static void accounting_sta_interim(struct hostapd_data *hapd,
|
||||
struct sta_info *sta);
|
||||
|
||||
|
||||
static struct radius_msg * accounting_msg(struct hostapd_data *hapd,
|
||||
struct sta_info *sta,
|
||||
int status_type)
|
||||
{
|
||||
struct radius_msg *msg;
|
||||
char buf[128];
|
||||
u8 *val;
|
||||
size_t len;
|
||||
int i;
|
||||
struct wpabuf *b;
|
||||
struct os_time now;
|
||||
|
||||
msg = radius_msg_new(RADIUS_CODE_ACCOUNTING_REQUEST,
|
||||
radius_client_get_id(hapd->radius));
|
||||
if (msg == NULL) {
|
||||
wpa_printf(MSG_INFO, "Could not create new RADIUS packet");
|
||||
return NULL;
|
||||
}
|
||||
|
||||
if (!radius_msg_add_attr_int32(msg, RADIUS_ATTR_ACCT_STATUS_TYPE,
|
||||
status_type)) {
|
||||
wpa_printf(MSG_INFO, "Could not add Acct-Status-Type");
|
||||
goto fail;
|
||||
}
|
||||
|
||||
if (sta) {
|
||||
if (!hostapd_config_get_radius_attr(
|
||||
hapd->conf->radius_acct_req_attr,
|
||||
RADIUS_ATTR_ACCT_AUTHENTIC) &&
|
||||
!radius_msg_add_attr_int32(msg, RADIUS_ATTR_ACCT_AUTHENTIC,
|
||||
hapd->conf->ieee802_1x ?
|
||||
RADIUS_ACCT_AUTHENTIC_RADIUS :
|
||||
RADIUS_ACCT_AUTHENTIC_LOCAL)) {
|
||||
wpa_printf(MSG_INFO, "Could not add Acct-Authentic");
|
||||
goto fail;
|
||||
}
|
||||
|
||||
/* Use 802.1X identity if available */
|
||||
val = ieee802_1x_get_identity(sta->eapol_sm, &len);
|
||||
|
||||
/* Use RADIUS ACL identity if 802.1X provides no identity */
|
||||
if (!val && sta->identity) {
|
||||
val = (u8 *) sta->identity;
|
||||
len = os_strlen(sta->identity);
|
||||
}
|
||||
|
||||
/* Use STA MAC if neither 802.1X nor RADIUS ACL provided
|
||||
* identity */
|
||||
if (!val) {
|
||||
os_snprintf(buf, sizeof(buf), RADIUS_ADDR_FORMAT,
|
||||
MAC2STR(sta->addr));
|
||||
val = (u8 *) buf;
|
||||
len = os_strlen(buf);
|
||||
}
|
||||
|
||||
if (!radius_msg_add_attr(msg, RADIUS_ATTR_USER_NAME, val,
|
||||
len)) {
|
||||
wpa_printf(MSG_INFO, "Could not add User-Name");
|
||||
goto fail;
|
||||
}
|
||||
}
|
||||
|
||||
if (add_common_radius_attr(hapd, hapd->conf->radius_acct_req_attr, sta,
|
||||
msg) < 0)
|
||||
goto fail;
|
||||
|
||||
if (sta && add_sqlite_radius_attr(hapd, sta, msg, 1) < 0)
|
||||
goto fail;
|
||||
|
||||
if (sta) {
|
||||
for (i = 0; ; i++) {
|
||||
val = ieee802_1x_get_radius_class(sta->eapol_sm, &len,
|
||||
i);
|
||||
if (val == NULL)
|
||||
break;
|
||||
|
||||
if (!radius_msg_add_attr(msg, RADIUS_ATTR_CLASS,
|
||||
val, len)) {
|
||||
wpa_printf(MSG_INFO, "Could not add Class");
|
||||
goto fail;
|
||||
}
|
||||
}
|
||||
|
||||
b = ieee802_1x_get_radius_cui(sta->eapol_sm);
|
||||
if (b &&
|
||||
!radius_msg_add_attr(msg,
|
||||
RADIUS_ATTR_CHARGEABLE_USER_IDENTITY,
|
||||
wpabuf_head(b), wpabuf_len(b))) {
|
||||
wpa_printf(MSG_ERROR, "Could not add CUI");
|
||||
goto fail;
|
||||
}
|
||||
|
||||
if (!b && sta->radius_cui &&
|
||||
!radius_msg_add_attr(msg,
|
||||
RADIUS_ATTR_CHARGEABLE_USER_IDENTITY,
|
||||
(u8 *) sta->radius_cui,
|
||||
os_strlen(sta->radius_cui))) {
|
||||
wpa_printf(MSG_ERROR, "Could not add CUI from ACL");
|
||||
goto fail;
|
||||
}
|
||||
|
||||
if (sta->ipaddr &&
|
||||
!radius_msg_add_attr_int32(msg,
|
||||
RADIUS_ATTR_FRAMED_IP_ADDRESS,
|
||||
be_to_host32(sta->ipaddr))) {
|
||||
wpa_printf(MSG_ERROR,
|
||||
"Could not add Framed-IP-Address");
|
||||
goto fail;
|
||||
}
|
||||
}
|
||||
|
||||
os_get_time(&now);
|
||||
if (now.sec > 1000000000 &&
|
||||
!radius_msg_add_attr_int32(msg, RADIUS_ATTR_EVENT_TIMESTAMP,
|
||||
now.sec)) {
|
||||
wpa_printf(MSG_INFO, "Could not add Event-Timestamp");
|
||||
goto fail;
|
||||
}
|
||||
|
||||
/*
|
||||
* Add Acct-Delay-Time with zero value for the first transmission. This
|
||||
* will be updated within radius_client.c when retransmitting the frame.
|
||||
*/
|
||||
if (!radius_msg_add_attr_int32(msg, RADIUS_ATTR_ACCT_DELAY_TIME, 0)) {
|
||||
wpa_printf(MSG_INFO, "Could not add Acct-Delay-Time");
|
||||
goto fail;
|
||||
}
|
||||
|
||||
return msg;
|
||||
|
||||
fail:
|
||||
radius_msg_free(msg);
|
||||
return NULL;
|
||||
}
|
||||
|
||||
|
||||
static int accounting_sta_update_stats(struct hostapd_data *hapd,
|
||||
struct sta_info *sta,
|
||||
struct hostap_sta_driver_data *data)
|
||||
{
|
||||
if (hostapd_drv_read_sta_data(hapd, data, sta->addr))
|
||||
return -1;
|
||||
|
||||
if (!data->bytes_64bit) {
|
||||
/* Extend 32-bit counters from the driver to 64-bit counters */
|
||||
if (sta->last_rx_bytes_lo > data->rx_bytes)
|
||||
sta->last_rx_bytes_hi++;
|
||||
sta->last_rx_bytes_lo = data->rx_bytes;
|
||||
|
||||
if (sta->last_tx_bytes_lo > data->tx_bytes)
|
||||
sta->last_tx_bytes_hi++;
|
||||
sta->last_tx_bytes_lo = data->tx_bytes;
|
||||
}
|
||||
|
||||
hostapd_logger(hapd, sta->addr, HOSTAPD_MODULE_RADIUS,
|
||||
HOSTAPD_LEVEL_DEBUG,
|
||||
"updated TX/RX stats: rx_bytes=%llu [%u:%u] tx_bytes=%llu [%u:%u] bytes_64bit=%d",
|
||||
data->rx_bytes, sta->last_rx_bytes_hi,
|
||||
sta->last_rx_bytes_lo,
|
||||
data->tx_bytes, sta->last_tx_bytes_hi,
|
||||
sta->last_tx_bytes_lo,
|
||||
data->bytes_64bit);
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
|
||||
static void accounting_interim_update(void *eloop_ctx, void *timeout_ctx)
|
||||
{
|
||||
struct hostapd_data *hapd = eloop_ctx;
|
||||
struct sta_info *sta = timeout_ctx;
|
||||
int interval;
|
||||
|
||||
if (sta->acct_interim_interval) {
|
||||
accounting_sta_interim(hapd, sta);
|
||||
interval = sta->acct_interim_interval;
|
||||
} else {
|
||||
struct hostap_sta_driver_data data;
|
||||
accounting_sta_update_stats(hapd, sta, &data);
|
||||
interval = ACCT_DEFAULT_UPDATE_INTERVAL;
|
||||
}
|
||||
|
||||
eloop_register_timeout(interval, 0, accounting_interim_update,
|
||||
hapd, sta);
|
||||
}
|
||||
|
||||
|
||||
/**
|
||||
* accounting_sta_start - Start STA accounting
|
||||
* @hapd: hostapd BSS data
|
||||
* @sta: The station
|
||||
*/
|
||||
void accounting_sta_start(struct hostapd_data *hapd, struct sta_info *sta)
|
||||
{
|
||||
struct radius_msg *msg;
|
||||
int interval;
|
||||
|
||||
if (sta->acct_session_started)
|
||||
return;
|
||||
|
||||
hostapd_logger(hapd, sta->addr, HOSTAPD_MODULE_RADIUS,
|
||||
HOSTAPD_LEVEL_INFO,
|
||||
"starting accounting session %016llX",
|
||||
(unsigned long long) sta->acct_session_id);
|
||||
|
||||
os_get_reltime(&sta->acct_session_start);
|
||||
sta->last_rx_bytes_hi = 0;
|
||||
sta->last_rx_bytes_lo = 0;
|
||||
sta->last_tx_bytes_hi = 0;
|
||||
sta->last_tx_bytes_lo = 0;
|
||||
hostapd_drv_sta_clear_stats(hapd, sta->addr);
|
||||
|
||||
if (!hapd->conf->radius->acct_server)
|
||||
return;
|
||||
|
||||
if (sta->acct_interim_interval)
|
||||
interval = sta->acct_interim_interval;
|
||||
else
|
||||
interval = ACCT_DEFAULT_UPDATE_INTERVAL;
|
||||
eloop_register_timeout(interval, 0, accounting_interim_update,
|
||||
hapd, sta);
|
||||
|
||||
msg = accounting_msg(hapd, sta, RADIUS_ACCT_STATUS_TYPE_START);
|
||||
if (msg &&
|
||||
radius_client_send(hapd->radius, msg, RADIUS_ACCT, sta->addr) < 0)
|
||||
radius_msg_free(msg);
|
||||
|
||||
sta->acct_session_started = 1;
|
||||
}
|
||||
|
||||
|
||||
static void accounting_sta_report(struct hostapd_data *hapd,
|
||||
struct sta_info *sta, int stop)
|
||||
{
|
||||
struct radius_msg *msg;
|
||||
int cause = sta->acct_terminate_cause;
|
||||
struct hostap_sta_driver_data data;
|
||||
struct os_reltime now_r, diff;
|
||||
u64 bytes;
|
||||
|
||||
if (!hapd->conf->radius->acct_server)
|
||||
return;
|
||||
|
||||
msg = accounting_msg(hapd, sta,
|
||||
stop ? RADIUS_ACCT_STATUS_TYPE_STOP :
|
||||
RADIUS_ACCT_STATUS_TYPE_INTERIM_UPDATE);
|
||||
if (!msg) {
|
||||
wpa_printf(MSG_INFO, "Could not create RADIUS Accounting message");
|
||||
return;
|
||||
}
|
||||
|
||||
os_get_reltime(&now_r);
|
||||
os_reltime_sub(&now_r, &sta->acct_session_start, &diff);
|
||||
if (!radius_msg_add_attr_int32(msg, RADIUS_ATTR_ACCT_SESSION_TIME,
|
||||
diff.sec)) {
|
||||
wpa_printf(MSG_INFO, "Could not add Acct-Session-Time");
|
||||
goto fail;
|
||||
}
|
||||
|
||||
if (accounting_sta_update_stats(hapd, sta, &data) == 0) {
|
||||
if (!radius_msg_add_attr_int32(msg,
|
||||
RADIUS_ATTR_ACCT_INPUT_PACKETS,
|
||||
data.rx_packets)) {
|
||||
wpa_printf(MSG_INFO, "Could not add Acct-Input-Packets");
|
||||
goto fail;
|
||||
}
|
||||
if (!radius_msg_add_attr_int32(msg,
|
||||
RADIUS_ATTR_ACCT_OUTPUT_PACKETS,
|
||||
data.tx_packets)) {
|
||||
wpa_printf(MSG_INFO, "Could not add Acct-Output-Packets");
|
||||
goto fail;
|
||||
}
|
||||
if (data.bytes_64bit)
|
||||
bytes = data.rx_bytes;
|
||||
else
|
||||
bytes = ((u64) sta->last_rx_bytes_hi << 32) |
|
||||
sta->last_rx_bytes_lo;
|
||||
if (!radius_msg_add_attr_int32(msg,
|
||||
RADIUS_ATTR_ACCT_INPUT_OCTETS,
|
||||
(u32) bytes)) {
|
||||
wpa_printf(MSG_INFO, "Could not add Acct-Input-Octets");
|
||||
goto fail;
|
||||
}
|
||||
if (!radius_msg_add_attr_int32(msg,
|
||||
RADIUS_ATTR_ACCT_INPUT_GIGAWORDS,
|
||||
(u32) (bytes >> 32))) {
|
||||
wpa_printf(MSG_INFO, "Could not add Acct-Input-Gigawords");
|
||||
goto fail;
|
||||
}
|
||||
if (data.bytes_64bit)
|
||||
bytes = data.tx_bytes;
|
||||
else
|
||||
bytes = ((u64) sta->last_tx_bytes_hi << 32) |
|
||||
sta->last_tx_bytes_lo;
|
||||
if (!radius_msg_add_attr_int32(msg,
|
||||
RADIUS_ATTR_ACCT_OUTPUT_OCTETS,
|
||||
(u32) bytes)) {
|
||||
wpa_printf(MSG_INFO, "Could not add Acct-Output-Octets");
|
||||
goto fail;
|
||||
}
|
||||
if (!radius_msg_add_attr_int32(msg,
|
||||
RADIUS_ATTR_ACCT_OUTPUT_GIGAWORDS,
|
||||
(u32) (bytes >> 32))) {
|
||||
wpa_printf(MSG_INFO, "Could not add Acct-Output-Gigawords");
|
||||
goto fail;
|
||||
}
|
||||
}
|
||||
|
||||
if (eloop_terminated())
|
||||
cause = RADIUS_ACCT_TERMINATE_CAUSE_ADMIN_REBOOT;
|
||||
|
||||
if (stop && cause &&
|
||||
!radius_msg_add_attr_int32(msg, RADIUS_ATTR_ACCT_TERMINATE_CAUSE,
|
||||
cause)) {
|
||||
wpa_printf(MSG_INFO, "Could not add Acct-Terminate-Cause");
|
||||
goto fail;
|
||||
}
|
||||
|
||||
if (radius_client_send(hapd->radius, msg,
|
||||
stop ? RADIUS_ACCT : RADIUS_ACCT_INTERIM,
|
||||
sta->addr) < 0)
|
||||
goto fail;
|
||||
return;
|
||||
|
||||
fail:
|
||||
radius_msg_free(msg);
|
||||
}
|
||||
|
||||
|
||||
/**
|
||||
* accounting_sta_interim - Send a interim STA accounting report
|
||||
* @hapd: hostapd BSS data
|
||||
* @sta: The station
|
||||
*/
|
||||
static void accounting_sta_interim(struct hostapd_data *hapd,
|
||||
struct sta_info *sta)
|
||||
{
|
||||
if (sta->acct_session_started)
|
||||
accounting_sta_report(hapd, sta, 0);
|
||||
}
|
||||
|
||||
|
||||
/**
|
||||
* accounting_sta_stop - Stop STA accounting
|
||||
* @hapd: hostapd BSS data
|
||||
* @sta: The station
|
||||
*/
|
||||
void accounting_sta_stop(struct hostapd_data *hapd, struct sta_info *sta)
|
||||
{
|
||||
if (sta->acct_session_started) {
|
||||
accounting_sta_report(hapd, sta, 1);
|
||||
eloop_cancel_timeout(accounting_interim_update, hapd, sta);
|
||||
hostapd_logger(hapd, sta->addr, HOSTAPD_MODULE_RADIUS,
|
||||
HOSTAPD_LEVEL_INFO,
|
||||
"stopped accounting session %016llX",
|
||||
(unsigned long long) sta->acct_session_id);
|
||||
sta->acct_session_started = 0;
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
int accounting_sta_get_id(struct hostapd_data *hapd, struct sta_info *sta)
|
||||
{
|
||||
return radius_gen_session_id((u8 *) &sta->acct_session_id,
|
||||
sizeof(sta->acct_session_id));
|
||||
}
|
||||
|
||||
|
||||
/**
|
||||
* accounting_receive - Process the RADIUS frames from Accounting Server
|
||||
* @msg: RADIUS response message
|
||||
* @req: RADIUS request message
|
||||
* @shared_secret: RADIUS shared secret
|
||||
* @shared_secret_len: Length of shared_secret in octets
|
||||
* @data: Context data (struct hostapd_data *)
|
||||
* Returns: Processing status
|
||||
*/
|
||||
static RadiusRxResult
|
||||
accounting_receive(struct radius_msg *msg, struct radius_msg *req,
|
||||
const u8 *shared_secret, size_t shared_secret_len,
|
||||
void *data)
|
||||
{
|
||||
if (radius_msg_get_hdr(msg)->code != RADIUS_CODE_ACCOUNTING_RESPONSE) {
|
||||
wpa_printf(MSG_INFO, "Unknown RADIUS message code");
|
||||
return RADIUS_RX_UNKNOWN;
|
||||
}
|
||||
|
||||
if (radius_msg_verify(msg, shared_secret, shared_secret_len, req, 0)) {
|
||||
wpa_printf(MSG_INFO, "Incoming RADIUS packet did not have correct Authenticator - dropped");
|
||||
return RADIUS_RX_INVALID_AUTHENTICATOR;
|
||||
}
|
||||
|
||||
return RADIUS_RX_PROCESSED;
|
||||
}
|
||||
|
||||
|
||||
static void accounting_report_state(struct hostapd_data *hapd, int on)
|
||||
{
|
||||
struct radius_msg *msg;
|
||||
|
||||
if (!hapd->conf->radius->acct_server || hapd->radius == NULL)
|
||||
return;
|
||||
|
||||
/* Inform RADIUS server that accounting will start/stop so that the
|
||||
* server can close old accounting sessions. */
|
||||
msg = accounting_msg(hapd, NULL,
|
||||
on ? RADIUS_ACCT_STATUS_TYPE_ACCOUNTING_ON :
|
||||
RADIUS_ACCT_STATUS_TYPE_ACCOUNTING_OFF);
|
||||
if (!msg)
|
||||
return;
|
||||
|
||||
if (hapd->acct_session_id) {
|
||||
char buf[20];
|
||||
|
||||
os_snprintf(buf, sizeof(buf), "%016llX",
|
||||
(unsigned long long) hapd->acct_session_id);
|
||||
if (!radius_msg_add_attr(msg, RADIUS_ATTR_ACCT_SESSION_ID,
|
||||
(u8 *) buf, os_strlen(buf)))
|
||||
wpa_printf(MSG_ERROR, "Could not add Acct-Session-Id");
|
||||
}
|
||||
|
||||
if (radius_client_send(hapd->radius, msg, RADIUS_ACCT, NULL) < 0)
|
||||
radius_msg_free(msg);
|
||||
}
|
||||
|
||||
|
||||
static void accounting_interim_error_cb(const u8 *addr, void *ctx)
|
||||
{
|
||||
struct hostapd_data *hapd = ctx;
|
||||
struct sta_info *sta;
|
||||
unsigned int i, wait_time;
|
||||
int res;
|
||||
|
||||
sta = ap_get_sta(hapd, addr);
|
||||
if (!sta)
|
||||
return;
|
||||
sta->acct_interim_errors++;
|
||||
if (sta->acct_interim_errors > 10 /* RADIUS_CLIENT_MAX_RETRIES */) {
|
||||
wpa_printf(MSG_DEBUG,
|
||||
"Interim RADIUS accounting update failed for " MACSTR
|
||||
" - too many errors, abandon this interim accounting update",
|
||||
MAC2STR(addr));
|
||||
sta->acct_interim_errors = 0;
|
||||
/* Next update will be tried after normal update interval */
|
||||
return;
|
||||
}
|
||||
|
||||
/*
|
||||
* Use a shorter update interval as an improved retransmission mechanism
|
||||
* for failed interim accounting updates. This allows the statistics to
|
||||
* be updated for each retransmission.
|
||||
*
|
||||
* RADIUS client code has already waited RADIUS_CLIENT_FIRST_WAIT.
|
||||
* Schedule the first retry attempt immediately and every following one
|
||||
* with exponential backoff.
|
||||
*/
|
||||
if (sta->acct_interim_errors == 1) {
|
||||
wait_time = 0;
|
||||
} else {
|
||||
wait_time = 3; /* RADIUS_CLIENT_FIRST_WAIT */
|
||||
for (i = 1; i < sta->acct_interim_errors; i++)
|
||||
wait_time *= 2;
|
||||
}
|
||||
res = eloop_deplete_timeout(wait_time, 0, accounting_interim_update,
|
||||
hapd, sta);
|
||||
if (res == 1)
|
||||
wpa_printf(MSG_DEBUG,
|
||||
"Interim RADIUS accounting update failed for " MACSTR
|
||||
" (error count: %u) - schedule next update in %u seconds",
|
||||
MAC2STR(addr), sta->acct_interim_errors, wait_time);
|
||||
else if (res == 0)
|
||||
wpa_printf(MSG_DEBUG,
|
||||
"Interim RADIUS accounting update failed for " MACSTR
|
||||
" (error count: %u)", MAC2STR(addr),
|
||||
sta->acct_interim_errors);
|
||||
else
|
||||
wpa_printf(MSG_DEBUG,
|
||||
"Interim RADIUS accounting update failed for " MACSTR
|
||||
" (error count: %u) - no timer found", MAC2STR(addr),
|
||||
sta->acct_interim_errors);
|
||||
}
|
||||
|
||||
|
||||
/**
|
||||
* accounting_init: Initialize accounting
|
||||
* @hapd: hostapd BSS data
|
||||
* Returns: 0 on success, -1 on failure
|
||||
*/
|
||||
int accounting_init(struct hostapd_data *hapd)
|
||||
{
|
||||
if (radius_gen_session_id((u8 *) &hapd->acct_session_id,
|
||||
sizeof(hapd->acct_session_id)) < 0)
|
||||
return -1;
|
||||
|
||||
if (radius_client_register(hapd->radius, RADIUS_ACCT,
|
||||
accounting_receive, hapd))
|
||||
return -1;
|
||||
radius_client_set_interim_error_cb(hapd->radius,
|
||||
accounting_interim_error_cb, hapd);
|
||||
|
||||
accounting_report_state(hapd, 1);
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
|
||||
/**
|
||||
* accounting_deinit: Deinitialize accounting
|
||||
* @hapd: hostapd BSS data
|
||||
*/
|
||||
void accounting_deinit(struct hostapd_data *hapd)
|
||||
{
|
||||
accounting_report_state(hapd, 0);
|
||||
}
|
|
@ -0,0 +1,45 @@
|
|||
/*
|
||||
* hostapd / RADIUS Accounting
|
||||
* Copyright (c) 2002-2005, Jouni Malinen <j@w1.fi>
|
||||
*
|
||||
* This software may be distributed under the terms of the BSD license.
|
||||
* See README for more details.
|
||||
*/
|
||||
|
||||
#ifndef ACCOUNTING_H
|
||||
#define ACCOUNTING_H
|
||||
|
||||
#ifdef CONFIG_NO_ACCOUNTING
|
||||
static inline int accounting_sta_get_id(struct hostapd_data *hapd,
|
||||
struct sta_info *sta)
|
||||
{
|
||||
return 0;
|
||||
}
|
||||
|
||||
static inline void accounting_sta_start(struct hostapd_data *hapd,
|
||||
struct sta_info *sta)
|
||||
{
|
||||
}
|
||||
|
||||
static inline void accounting_sta_stop(struct hostapd_data *hapd,
|
||||
struct sta_info *sta)
|
||||
{
|
||||
}
|
||||
|
||||
static inline int accounting_init(struct hostapd_data *hapd)
|
||||
{
|
||||
return 0;
|
||||
}
|
||||
|
||||
static inline void accounting_deinit(struct hostapd_data *hapd)
|
||||
{
|
||||
}
|
||||
#else /* CONFIG_NO_ACCOUNTING */
|
||||
int accounting_sta_get_id(struct hostapd_data *hapd, struct sta_info *sta);
|
||||
void accounting_sta_start(struct hostapd_data *hapd, struct sta_info *sta);
|
||||
void accounting_sta_stop(struct hostapd_data *hapd, struct sta_info *sta);
|
||||
int accounting_init(struct hostapd_data *hapd);
|
||||
void accounting_deinit(struct hostapd_data *hapd);
|
||||
#endif /* CONFIG_NO_ACCOUNTING */
|
||||
|
||||
#endif /* ACCOUNTING_H */
|
|
@ -0,0 +1,986 @@
|
|||
/*
|
||||
* ACS - Automatic Channel Selection module
|
||||
* Copyright (c) 2011, Atheros Communications
|
||||
* Copyright (c) 2013, Qualcomm Atheros, Inc.
|
||||
*
|
||||
* This software may be distributed under the terms of the BSD license.
|
||||
* See README for more details.
|
||||
*/
|
||||
|
||||
#include "utils/includes.h"
|
||||
#include <math.h>
|
||||
|
||||
#include "utils/common.h"
|
||||
#include "utils/list.h"
|
||||
#include "common/ieee802_11_defs.h"
|
||||
#include "common/hw_features_common.h"
|
||||
#include "common/wpa_ctrl.h"
|
||||
#include "drivers/driver.h"
|
||||
#include "hostapd.h"
|
||||
#include "ap_drv_ops.h"
|
||||
#include "ap_config.h"
|
||||
#include "hw_features.h"
|
||||
#include "acs.h"
|
||||
|
||||
/*
|
||||
* Automatic Channel Selection
|
||||
* ===========================
|
||||
*
|
||||
* More info at
|
||||
* ------------
|
||||
* http://wireless.kernel.org/en/users/Documentation/acs
|
||||
*
|
||||
* How to use
|
||||
* ----------
|
||||
* - make sure you have CONFIG_ACS=y in hostapd's .config
|
||||
* - use channel=0 or channel=acs to enable ACS
|
||||
*
|
||||
* How does it work
|
||||
* ----------------
|
||||
* 1. passive scans are used to collect survey data
|
||||
* (it is assumed that scan trigger collection of survey data in driver)
|
||||
* 2. interference factor is calculated for each channel
|
||||
* 3. ideal channel is picked depending on channel width by using adjacent
|
||||
* channel interference factors
|
||||
*
|
||||
* Known limitations
|
||||
* -----------------
|
||||
* - Current implementation depends heavily on the amount of time willing to
|
||||
* spend gathering survey data during hostapd startup. Short traffic bursts
|
||||
* may be missed and a suboptimal channel may be picked.
|
||||
* - Ideal channel may end up overlapping a channel with 40 MHz intolerant BSS
|
||||
*
|
||||
* Todo / Ideas
|
||||
* ------------
|
||||
* - implement other interference computation methods
|
||||
* - BSS/RSSI based
|
||||
* - spectral scan based
|
||||
* (should be possibly to hook this up with current ACS scans)
|
||||
* - add wpa_supplicant support (for P2P)
|
||||
* - collect a histogram of interference over time allowing more educated
|
||||
* guess about an ideal channel (perhaps CSA could be used to migrate AP to a
|
||||
* new "better" channel while running)
|
||||
* - include neighboring BSS scan to avoid conflicts with 40 MHz intolerant BSSs
|
||||
* when choosing the ideal channel
|
||||
*
|
||||
* Survey interference factor implementation details
|
||||
* -------------------------------------------------
|
||||
* Generic interference_factor in struct hostapd_channel_data is used.
|
||||
*
|
||||
* The survey interference factor is defined as the ratio of the
|
||||
* observed busy time over the time we spent on the channel,
|
||||
* this value is then amplified by the observed noise floor on
|
||||
* the channel in comparison to the lowest noise floor observed
|
||||
* on the entire band.
|
||||
*
|
||||
* This corresponds to:
|
||||
* ---
|
||||
* (busy time - tx time) / (active time - tx time) * 2^(chan_nf + band_min_nf)
|
||||
* ---
|
||||
*
|
||||
* The coefficient of 2 reflects the way power in "far-field"
|
||||
* radiation decreases as the square of distance from the antenna [1].
|
||||
* What this does is it decreases the observed busy time ratio if the
|
||||
* noise observed was low but increases it if the noise was high,
|
||||
* proportionally to the way "far field" radiation changes over
|
||||
* distance.
|
||||
*
|
||||
* If channel busy time is not available the fallback is to use channel RX time.
|
||||
*
|
||||
* Since noise floor is in dBm it is necessary to convert it into Watts so that
|
||||
* combined channel interference (e.g., HT40, which uses two channels) can be
|
||||
* calculated easily.
|
||||
* ---
|
||||
* (busy time - tx time) / (active time - tx time) *
|
||||
* 2^(10^(chan_nf/10) + 10^(band_min_nf/10))
|
||||
* ---
|
||||
*
|
||||
* However to account for cases where busy/rx time is 0 (channel load is then
|
||||
* 0%) channel noise floor signal power is combined into the equation so a
|
||||
* channel with lower noise floor is preferred. The equation becomes:
|
||||
* ---
|
||||
* 10^(chan_nf/5) + (busy time - tx time) / (active time - tx time) *
|
||||
* 2^(10^(chan_nf/10) + 10^(band_min_nf/10))
|
||||
* ---
|
||||
*
|
||||
* All this "interference factor" is purely subjective and only time
|
||||
* will tell how usable this is. By using the minimum noise floor we
|
||||
* remove any possible issues due to card calibration. The computation
|
||||
* of the interference factor then is dependent on what the card itself
|
||||
* picks up as the minimum noise, not an actual real possible card
|
||||
* noise value.
|
||||
*
|
||||
* Total interference computation details
|
||||
* --------------------------------------
|
||||
* The above channel interference factor is calculated with no respect to
|
||||
* target operational bandwidth.
|
||||
*
|
||||
* To find an ideal channel the above data is combined by taking into account
|
||||
* the target operational bandwidth and selected band. E.g., on 2.4 GHz channels
|
||||
* overlap with 20 MHz bandwidth, but there is no overlap for 20 MHz bandwidth
|
||||
* on 5 GHz.
|
||||
*
|
||||
* Each valid and possible channel spec (i.e., channel + width) is taken and its
|
||||
* interference factor is computed by summing up interferences of each channel
|
||||
* it overlaps. The one with least total interference is picked up.
|
||||
*
|
||||
* Note: This implies base channel interference factor must be non-negative
|
||||
* allowing easy summing up.
|
||||
*
|
||||
* Example ACS analysis printout
|
||||
* -----------------------------
|
||||
*
|
||||
* ACS: Trying survey-based ACS
|
||||
* ACS: Survey analysis for channel 1 (2412 MHz)
|
||||
* ACS: 1: min_nf=-113 interference_factor=0.0802469 nf=-113 time=162 busy=0 rx=13
|
||||
* ACS: 2: min_nf=-113 interference_factor=0.0745342 nf=-113 time=161 busy=0 rx=12
|
||||
* ACS: 3: min_nf=-113 interference_factor=0.0679012 nf=-113 time=162 busy=0 rx=11
|
||||
* ACS: 4: min_nf=-113 interference_factor=0.0310559 nf=-113 time=161 busy=0 rx=5
|
||||
* ACS: 5: min_nf=-113 interference_factor=0.0248447 nf=-113 time=161 busy=0 rx=4
|
||||
* ACS: * interference factor average: 0.0557166
|
||||
* ACS: Survey analysis for channel 2 (2417 MHz)
|
||||
* ACS: 1: min_nf=-113 interference_factor=0.0185185 nf=-113 time=162 busy=0 rx=3
|
||||
* ACS: 2: min_nf=-113 interference_factor=0.0246914 nf=-113 time=162 busy=0 rx=4
|
||||
* ACS: 3: min_nf=-113 interference_factor=0.037037 nf=-113 time=162 busy=0 rx=6
|
||||
* ACS: 4: min_nf=-113 interference_factor=0.149068 nf=-113 time=161 busy=0 rx=24
|
||||
* ACS: 5: min_nf=-113 interference_factor=0.0248447 nf=-113 time=161 busy=0 rx=4
|
||||
* ACS: * interference factor average: 0.050832
|
||||
* ACS: Survey analysis for channel 3 (2422 MHz)
|
||||
* ACS: 1: min_nf=-113 interference_factor=2.51189e-23 nf=-113 time=162 busy=0 rx=0
|
||||
* ACS: 2: min_nf=-113 interference_factor=0.0185185 nf=-113 time=162 busy=0 rx=3
|
||||
* ACS: 3: min_nf=-113 interference_factor=0.0186335 nf=-113 time=161 busy=0 rx=3
|
||||
* ACS: 4: min_nf=-113 interference_factor=0.0186335 nf=-113 time=161 busy=0 rx=3
|
||||
* ACS: 5: min_nf=-113 interference_factor=0.0186335 nf=-113 time=161 busy=0 rx=3
|
||||
* ACS: * interference factor average: 0.0148838
|
||||
* ACS: Survey analysis for channel 4 (2427 MHz)
|
||||
* ACS: 1: min_nf=-114 interference_factor=1.58489e-23 nf=-114 time=162 busy=0 rx=0
|
||||
* ACS: 2: min_nf=-114 interference_factor=0.0555556 nf=-114 time=162 busy=0 rx=9
|
||||
* ACS: 3: min_nf=-114 interference_factor=1.58489e-23 nf=-114 time=161 busy=0 rx=0
|
||||
* ACS: 4: min_nf=-114 interference_factor=0.0186335 nf=-114 time=161 busy=0 rx=3
|
||||
* ACS: 5: min_nf=-114 interference_factor=0.00621118 nf=-114 time=161 busy=0 rx=1
|
||||
* ACS: * interference factor average: 0.0160801
|
||||
* ACS: Survey analysis for channel 5 (2432 MHz)
|
||||
* ACS: 1: min_nf=-114 interference_factor=0.409938 nf=-113 time=161 busy=0 rx=66
|
||||
* ACS: 2: min_nf=-114 interference_factor=0.0432099 nf=-113 time=162 busy=0 rx=7
|
||||
* ACS: 3: min_nf=-114 interference_factor=0.0124224 nf=-113 time=161 busy=0 rx=2
|
||||
* ACS: 4: min_nf=-114 interference_factor=0.677019 nf=-113 time=161 busy=0 rx=109
|
||||
* ACS: 5: min_nf=-114 interference_factor=0.0186335 nf=-114 time=161 busy=0 rx=3
|
||||
* ACS: * interference factor average: 0.232244
|
||||
* ACS: Survey analysis for channel 6 (2437 MHz)
|
||||
* ACS: 1: min_nf=-113 interference_factor=0.552795 nf=-113 time=161 busy=0 rx=89
|
||||
* ACS: 2: min_nf=-113 interference_factor=0.0807453 nf=-112 time=161 busy=0 rx=13
|
||||
* ACS: 3: min_nf=-113 interference_factor=0.0310559 nf=-113 time=161 busy=0 rx=5
|
||||
* ACS: 4: min_nf=-113 interference_factor=0.434783 nf=-112 time=161 busy=0 rx=70
|
||||
* ACS: 5: min_nf=-113 interference_factor=0.0621118 nf=-113 time=161 busy=0 rx=10
|
||||
* ACS: * interference factor average: 0.232298
|
||||
* ACS: Survey analysis for channel 7 (2442 MHz)
|
||||
* ACS: 1: min_nf=-113 interference_factor=0.440994 nf=-112 time=161 busy=0 rx=71
|
||||
* ACS: 2: min_nf=-113 interference_factor=0.385093 nf=-113 time=161 busy=0 rx=62
|
||||
* ACS: 3: min_nf=-113 interference_factor=0.0372671 nf=-113 time=161 busy=0 rx=6
|
||||
* ACS: 4: min_nf=-113 interference_factor=0.0372671 nf=-113 time=161 busy=0 rx=6
|
||||
* ACS: 5: min_nf=-113 interference_factor=0.0745342 nf=-113 time=161 busy=0 rx=12
|
||||
* ACS: * interference factor average: 0.195031
|
||||
* ACS: Survey analysis for channel 8 (2447 MHz)
|
||||
* ACS: 1: min_nf=-114 interference_factor=0.0496894 nf=-112 time=161 busy=0 rx=8
|
||||
* ACS: 2: min_nf=-114 interference_factor=0.0496894 nf=-114 time=161 busy=0 rx=8
|
||||
* ACS: 3: min_nf=-114 interference_factor=0.0372671 nf=-113 time=161 busy=0 rx=6
|
||||
* ACS: 4: min_nf=-114 interference_factor=0.12963 nf=-113 time=162 busy=0 rx=21
|
||||
* ACS: 5: min_nf=-114 interference_factor=0.166667 nf=-114 time=162 busy=0 rx=27
|
||||
* ACS: * interference factor average: 0.0865885
|
||||
* ACS: Survey analysis for channel 9 (2452 MHz)
|
||||
* ACS: 1: min_nf=-114 interference_factor=0.0124224 nf=-114 time=161 busy=0 rx=2
|
||||
* ACS: 2: min_nf=-114 interference_factor=0.0310559 nf=-114 time=161 busy=0 rx=5
|
||||
* ACS: 3: min_nf=-114 interference_factor=1.58489e-23 nf=-114 time=161 busy=0 rx=0
|
||||
* ACS: 4: min_nf=-114 interference_factor=0.00617284 nf=-114 time=162 busy=0 rx=1
|
||||
* ACS: 5: min_nf=-114 interference_factor=1.58489e-23 nf=-114 time=162 busy=0 rx=0
|
||||
* ACS: * interference factor average: 0.00993022
|
||||
* ACS: Survey analysis for channel 10 (2457 MHz)
|
||||
* ACS: 1: min_nf=-114 interference_factor=0.00621118 nf=-114 time=161 busy=0 rx=1
|
||||
* ACS: 2: min_nf=-114 interference_factor=0.00621118 nf=-114 time=161 busy=0 rx=1
|
||||
* ACS: 3: min_nf=-114 interference_factor=0.00621118 nf=-114 time=161 busy=0 rx=1
|
||||
* ACS: 4: min_nf=-114 interference_factor=0.0493827 nf=-114 time=162 busy=0 rx=8
|
||||
* ACS: 5: min_nf=-114 interference_factor=1.58489e-23 nf=-114 time=162 busy=0 rx=0
|
||||
* ACS: * interference factor average: 0.0136033
|
||||
* ACS: Survey analysis for channel 11 (2462 MHz)
|
||||
* ACS: 1: min_nf=-114 interference_factor=1.58489e-23 nf=-114 time=161 busy=0 rx=0
|
||||
* ACS: 2: min_nf=-114 interference_factor=2.51189e-23 nf=-113 time=161 busy=0 rx=0
|
||||
* ACS: 3: min_nf=-114 interference_factor=2.51189e-23 nf=-113 time=161 busy=0 rx=0
|
||||
* ACS: 4: min_nf=-114 interference_factor=0.0432099 nf=-114 time=162 busy=0 rx=7
|
||||
* ACS: 5: min_nf=-114 interference_factor=0.0925926 nf=-114 time=162 busy=0 rx=15
|
||||
* ACS: * interference factor average: 0.0271605
|
||||
* ACS: Survey analysis for channel 12 (2467 MHz)
|
||||
* ACS: 1: min_nf=-114 interference_factor=0.0621118 nf=-113 time=161 busy=0 rx=10
|
||||
* ACS: 2: min_nf=-114 interference_factor=0.00621118 nf=-114 time=161 busy=0 rx=1
|
||||
* ACS: 3: min_nf=-114 interference_factor=2.51189e-23 nf=-113 time=162 busy=0 rx=0
|
||||
* ACS: 4: min_nf=-114 interference_factor=2.51189e-23 nf=-113 time=162 busy=0 rx=0
|
||||
* ACS: 5: min_nf=-114 interference_factor=0.00617284 nf=-113 time=162 busy=0 rx=1
|
||||
* ACS: * interference factor average: 0.0148992
|
||||
* ACS: Survey analysis for channel 13 (2472 MHz)
|
||||
* ACS: 1: min_nf=-114 interference_factor=0.0745342 nf=-114 time=161 busy=0 rx=12
|
||||
* ACS: 2: min_nf=-114 interference_factor=0.0555556 nf=-114 time=162 busy=0 rx=9
|
||||
* ACS: 3: min_nf=-114 interference_factor=1.58489e-23 nf=-114 time=162 busy=0 rx=0
|
||||
* ACS: 4: min_nf=-114 interference_factor=1.58489e-23 nf=-114 time=162 busy=0 rx=0
|
||||
* ACS: 5: min_nf=-114 interference_factor=1.58489e-23 nf=-114 time=162 busy=0 rx=0
|
||||
* ACS: * interference factor average: 0.0260179
|
||||
* ACS: Survey analysis for selected bandwidth 20MHz
|
||||
* ACS: * channel 1: total interference = 0.121432
|
||||
* ACS: * channel 2: total interference = 0.137512
|
||||
* ACS: * channel 3: total interference = 0.369757
|
||||
* ACS: * channel 4: total interference = 0.546338
|
||||
* ACS: * channel 5: total interference = 0.690538
|
||||
* ACS: * channel 6: total interference = 0.762242
|
||||
* ACS: * channel 7: total interference = 0.756092
|
||||
* ACS: * channel 8: total interference = 0.537451
|
||||
* ACS: * channel 9: total interference = 0.332313
|
||||
* ACS: * channel 10: total interference = 0.152182
|
||||
* ACS: * channel 11: total interference = 0.0916111
|
||||
* ACS: * channel 12: total interference = 0.0816809
|
||||
* ACS: * channel 13: total interference = 0.0680776
|
||||
* ACS: Ideal channel is 13 (2472 MHz) with total interference factor of 0.0680776
|
||||
*
|
||||
* [1] http://en.wikipedia.org/wiki/Near_and_far_field
|
||||
*/
|
||||
|
||||
|
||||
static int acs_request_scan(struct hostapd_iface *iface);
|
||||
static int acs_survey_is_sufficient(struct freq_survey *survey);
|
||||
|
||||
|
||||
static void acs_clean_chan_surveys(struct hostapd_channel_data *chan)
|
||||
{
|
||||
struct freq_survey *survey, *tmp;
|
||||
|
||||
if (dl_list_empty(&chan->survey_list))
|
||||
return;
|
||||
|
||||
dl_list_for_each_safe(survey, tmp, &chan->survey_list,
|
||||
struct freq_survey, list) {
|
||||
dl_list_del(&survey->list);
|
||||
os_free(survey);
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
void acs_cleanup(struct hostapd_iface *iface)
|
||||
{
|
||||
int i;
|
||||
struct hostapd_channel_data *chan;
|
||||
|
||||
for (i = 0; i < iface->current_mode->num_channels; i++) {
|
||||
chan = &iface->current_mode->channels[i];
|
||||
|
||||
if (chan->flag & HOSTAPD_CHAN_SURVEY_LIST_INITIALIZED)
|
||||
acs_clean_chan_surveys(chan);
|
||||
|
||||
dl_list_init(&chan->survey_list);
|
||||
chan->flag |= HOSTAPD_CHAN_SURVEY_LIST_INITIALIZED;
|
||||
chan->min_nf = 0;
|
||||
}
|
||||
|
||||
iface->chans_surveyed = 0;
|
||||
iface->acs_num_completed_scans = 0;
|
||||
}
|
||||
|
||||
|
||||
static void acs_fail(struct hostapd_iface *iface)
|
||||
{
|
||||
wpa_printf(MSG_ERROR, "ACS: Failed to start");
|
||||
acs_cleanup(iface);
|
||||
hostapd_disable_iface(iface);
|
||||
}
|
||||
|
||||
|
||||
static long double
|
||||
acs_survey_interference_factor(struct freq_survey *survey, s8 min_nf)
|
||||
{
|
||||
long double factor, busy, total;
|
||||
|
||||
if (survey->filled & SURVEY_HAS_CHAN_TIME_BUSY)
|
||||
busy = survey->channel_time_busy;
|
||||
else if (survey->filled & SURVEY_HAS_CHAN_TIME_RX)
|
||||
busy = survey->channel_time_rx;
|
||||
else {
|
||||
/* This shouldn't really happen as survey data is checked in
|
||||
* acs_sanity_check() */
|
||||
wpa_printf(MSG_ERROR, "ACS: Survey data missing");
|
||||
return 0;
|
||||
}
|
||||
|
||||
total = survey->channel_time;
|
||||
|
||||
if (survey->filled & SURVEY_HAS_CHAN_TIME_TX) {
|
||||
busy -= survey->channel_time_tx;
|
||||
total -= survey->channel_time_tx;
|
||||
}
|
||||
|
||||
/* TODO: figure out the best multiplier for noise floor base */
|
||||
factor = pow(10, survey->nf / 5.0L) +
|
||||
(total ? (busy / total) : 0) *
|
||||
pow(2, pow(10, (long double) survey->nf / 10.0L) -
|
||||
pow(10, (long double) min_nf / 10.0L));
|
||||
|
||||
return factor;
|
||||
}
|
||||
|
||||
|
||||
static void
|
||||
acs_survey_chan_interference_factor(struct hostapd_iface *iface,
|
||||
struct hostapd_channel_data *chan)
|
||||
{
|
||||
struct freq_survey *survey;
|
||||
unsigned int i = 0;
|
||||
long double int_factor = 0;
|
||||
unsigned count = 0;
|
||||
|
||||
if (dl_list_empty(&chan->survey_list) ||
|
||||
(chan->flag & HOSTAPD_CHAN_DISABLED))
|
||||
return;
|
||||
|
||||
chan->interference_factor = 0;
|
||||
|
||||
dl_list_for_each(survey, &chan->survey_list, struct freq_survey, list)
|
||||
{
|
||||
i++;
|
||||
|
||||
if (!acs_survey_is_sufficient(survey)) {
|
||||
wpa_printf(MSG_DEBUG, "ACS: %d: insufficient data", i);
|
||||
continue;
|
||||
}
|
||||
|
||||
count++;
|
||||
int_factor = acs_survey_interference_factor(survey,
|
||||
iface->lowest_nf);
|
||||
chan->interference_factor += int_factor;
|
||||
wpa_printf(MSG_DEBUG, "ACS: %d: min_nf=%d interference_factor=%Lg nf=%d time=%lu busy=%lu rx=%lu",
|
||||
i, chan->min_nf, int_factor,
|
||||
survey->nf, (unsigned long) survey->channel_time,
|
||||
(unsigned long) survey->channel_time_busy,
|
||||
(unsigned long) survey->channel_time_rx);
|
||||
}
|
||||
|
||||
if (count)
|
||||
chan->interference_factor /= count;
|
||||
}
|
||||
|
||||
|
||||
static int acs_usable_ht40_chan(const struct hostapd_channel_data *chan)
|
||||
{
|
||||
const int allowed[] = { 36, 44, 52, 60, 100, 108, 116, 124, 132, 149,
|
||||
157, 184, 192 };
|
||||
unsigned int i;
|
||||
|
||||
for (i = 0; i < ARRAY_SIZE(allowed); i++)
|
||||
if (chan->chan == allowed[i])
|
||||
return 1;
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
|
||||
static int acs_usable_vht80_chan(const struct hostapd_channel_data *chan)
|
||||
{
|
||||
const int allowed[] = { 36, 52, 100, 116, 132, 149 };
|
||||
unsigned int i;
|
||||
|
||||
for (i = 0; i < ARRAY_SIZE(allowed); i++)
|
||||
if (chan->chan == allowed[i])
|
||||
return 1;
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
|
||||
static int acs_usable_vht160_chan(const struct hostapd_channel_data *chan)
|
||||
{
|
||||
const int allowed[] = { 36, 100 };
|
||||
unsigned int i;
|
||||
|
||||
for (i = 0; i < ARRAY_SIZE(allowed); i++)
|
||||
if (chan->chan == allowed[i])
|
||||
return 1;
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
|
||||
static int acs_survey_is_sufficient(struct freq_survey *survey)
|
||||
{
|
||||
if (!(survey->filled & SURVEY_HAS_NF)) {
|
||||
wpa_printf(MSG_INFO, "ACS: Survey is missing noise floor");
|
||||
return 0;
|
||||
}
|
||||
|
||||
if (!(survey->filled & SURVEY_HAS_CHAN_TIME)) {
|
||||
wpa_printf(MSG_INFO, "ACS: Survey is missing channel time");
|
||||
return 0;
|
||||
}
|
||||
|
||||
if (!(survey->filled & SURVEY_HAS_CHAN_TIME_BUSY) &&
|
||||
!(survey->filled & SURVEY_HAS_CHAN_TIME_RX)) {
|
||||
wpa_printf(MSG_INFO,
|
||||
"ACS: Survey is missing RX and busy time (at least one is required)");
|
||||
return 0;
|
||||
}
|
||||
|
||||
return 1;
|
||||
}
|
||||
|
||||
|
||||
static int acs_survey_list_is_sufficient(struct hostapd_channel_data *chan)
|
||||
{
|
||||
struct freq_survey *survey;
|
||||
int ret = -1;
|
||||
|
||||
dl_list_for_each(survey, &chan->survey_list, struct freq_survey, list)
|
||||
{
|
||||
if (acs_survey_is_sufficient(survey)) {
|
||||
ret = 1;
|
||||
break;
|
||||
}
|
||||
ret = 0;
|
||||
}
|
||||
|
||||
if (ret == -1)
|
||||
ret = 1; /* no survey list entries */
|
||||
|
||||
if (!ret) {
|
||||
wpa_printf(MSG_INFO,
|
||||
"ACS: Channel %d has insufficient survey data",
|
||||
chan->chan);
|
||||
}
|
||||
|
||||
return ret;
|
||||
}
|
||||
|
||||
|
||||
static int acs_surveys_are_sufficient(struct hostapd_iface *iface)
|
||||
{
|
||||
int i;
|
||||
struct hostapd_channel_data *chan;
|
||||
int valid = 0;
|
||||
|
||||
for (i = 0; i < iface->current_mode->num_channels; i++) {
|
||||
chan = &iface->current_mode->channels[i];
|
||||
if (!(chan->flag & HOSTAPD_CHAN_DISABLED) &&
|
||||
acs_survey_list_is_sufficient(chan))
|
||||
valid++;
|
||||
}
|
||||
|
||||
/* We need at least survey data for one channel */
|
||||
return !!valid;
|
||||
}
|
||||
|
||||
|
||||
static int acs_usable_chan(struct hostapd_channel_data *chan)
|
||||
{
|
||||
return !dl_list_empty(&chan->survey_list) &&
|
||||
!(chan->flag & HOSTAPD_CHAN_DISABLED) &&
|
||||
acs_survey_list_is_sufficient(chan);
|
||||
}
|
||||
|
||||
|
||||
static int is_in_chanlist(struct hostapd_iface *iface,
|
||||
struct hostapd_channel_data *chan)
|
||||
{
|
||||
if (!iface->conf->acs_ch_list.num)
|
||||
return 1;
|
||||
|
||||
return freq_range_list_includes(&iface->conf->acs_ch_list, chan->chan);
|
||||
}
|
||||
|
||||
|
||||
static void acs_survey_all_chans_intereference_factor(
|
||||
struct hostapd_iface *iface)
|
||||
{
|
||||
int i;
|
||||
struct hostapd_channel_data *chan;
|
||||
|
||||
for (i = 0; i < iface->current_mode->num_channels; i++) {
|
||||
chan = &iface->current_mode->channels[i];
|
||||
|
||||
if (!acs_usable_chan(chan))
|
||||
continue;
|
||||
|
||||
if (!is_in_chanlist(iface, chan))
|
||||
continue;
|
||||
|
||||
wpa_printf(MSG_DEBUG, "ACS: Survey analysis for channel %d (%d MHz)",
|
||||
chan->chan, chan->freq);
|
||||
|
||||
acs_survey_chan_interference_factor(iface, chan);
|
||||
|
||||
wpa_printf(MSG_DEBUG, "ACS: * interference factor average: %Lg",
|
||||
chan->interference_factor);
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
static struct hostapd_channel_data *acs_find_chan(struct hostapd_iface *iface,
|
||||
int freq)
|
||||
{
|
||||
struct hostapd_channel_data *chan;
|
||||
int i;
|
||||
|
||||
for (i = 0; i < iface->current_mode->num_channels; i++) {
|
||||
chan = &iface->current_mode->channels[i];
|
||||
|
||||
if (chan->flag & HOSTAPD_CHAN_DISABLED)
|
||||
continue;
|
||||
|
||||
if (chan->freq == freq)
|
||||
return chan;
|
||||
}
|
||||
|
||||
return NULL;
|
||||
}
|
||||
|
||||
|
||||
static int is_24ghz_mode(enum hostapd_hw_mode mode)
|
||||
{
|
||||
return mode == HOSTAPD_MODE_IEEE80211B ||
|
||||
mode == HOSTAPD_MODE_IEEE80211G;
|
||||
}
|
||||
|
||||
|
||||
static int is_common_24ghz_chan(int chan)
|
||||
{
|
||||
return chan == 1 || chan == 6 || chan == 11;
|
||||
}
|
||||
|
||||
|
||||
#ifndef ACS_ADJ_WEIGHT
|
||||
#define ACS_ADJ_WEIGHT 0.85
|
||||
#endif /* ACS_ADJ_WEIGHT */
|
||||
|
||||
#ifndef ACS_NEXT_ADJ_WEIGHT
|
||||
#define ACS_NEXT_ADJ_WEIGHT 0.55
|
||||
#endif /* ACS_NEXT_ADJ_WEIGHT */
|
||||
|
||||
#ifndef ACS_24GHZ_PREFER_1_6_11
|
||||
/*
|
||||
* Select commonly used channels 1, 6, 11 by default even if a neighboring
|
||||
* channel has a smaller interference factor as long as it is not better by more
|
||||
* than this multiplier.
|
||||
*/
|
||||
#define ACS_24GHZ_PREFER_1_6_11 0.8
|
||||
#endif /* ACS_24GHZ_PREFER_1_6_11 */
|
||||
|
||||
/*
|
||||
* At this point it's assumed chan->interface_factor has been computed.
|
||||
* This function should be reusable regardless of interference computation
|
||||
* option (survey, BSS, spectral, ...). chan->interference factor must be
|
||||
* summable (i.e., must be always greater than zero).
|
||||
*/
|
||||
static struct hostapd_channel_data *
|
||||
acs_find_ideal_chan(struct hostapd_iface *iface)
|
||||
{
|
||||
struct hostapd_channel_data *chan, *adj_chan, *ideal_chan = NULL,
|
||||
*rand_chan = NULL;
|
||||
long double factor, ideal_factor = 0;
|
||||
int i, j;
|
||||
int n_chans = 1;
|
||||
u32 bw;
|
||||
unsigned int k;
|
||||
|
||||
/* TODO: HT40- support */
|
||||
|
||||
if (iface->conf->ieee80211n &&
|
||||
iface->conf->secondary_channel == -1) {
|
||||
wpa_printf(MSG_ERROR, "ACS: HT40- is not supported yet. Please try HT40+");
|
||||
return NULL;
|
||||
}
|
||||
|
||||
if (iface->conf->ieee80211n &&
|
||||
iface->conf->secondary_channel)
|
||||
n_chans = 2;
|
||||
|
||||
if (iface->conf->ieee80211ac || iface->conf->ieee80211ax) {
|
||||
switch (hostapd_get_oper_chwidth(iface->conf)) {
|
||||
case CHANWIDTH_80MHZ:
|
||||
n_chans = 4;
|
||||
break;
|
||||
case CHANWIDTH_160MHZ:
|
||||
n_chans = 8;
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
bw = num_chan_to_bw(n_chans);
|
||||
|
||||
/* TODO: VHT/HE80+80. Update acs_adjust_center_freq() too. */
|
||||
|
||||
wpa_printf(MSG_DEBUG,
|
||||
"ACS: Survey analysis for selected bandwidth %d MHz", bw);
|
||||
|
||||
for (i = 0; i < iface->current_mode->num_channels; i++) {
|
||||
double total_weight;
|
||||
struct acs_bias *bias, tmp_bias;
|
||||
|
||||
chan = &iface->current_mode->channels[i];
|
||||
|
||||
/* Since in the current ACS implementation the first channel is
|
||||
* always a primary channel, skip channels not available as
|
||||
* primary until more sophisticated channel selection is
|
||||
* implemented. */
|
||||
if (!chan_pri_allowed(chan))
|
||||
continue;
|
||||
|
||||
if (!is_in_chanlist(iface, chan))
|
||||
continue;
|
||||
|
||||
if (!chan_bw_allowed(chan, bw, 1, 1)) {
|
||||
wpa_printf(MSG_DEBUG,
|
||||
"ACS: Channel %d: BW %u is not supported",
|
||||
chan->chan, bw);
|
||||
continue;
|
||||
}
|
||||
|
||||
/* HT40 on 5 GHz has a limited set of primary channels as per
|
||||
* 11n Annex J */
|
||||
if (iface->current_mode->mode == HOSTAPD_MODE_IEEE80211A &&
|
||||
iface->conf->ieee80211n &&
|
||||
iface->conf->secondary_channel &&
|
||||
!acs_usable_ht40_chan(chan)) {
|
||||
wpa_printf(MSG_DEBUG, "ACS: Channel %d: not allowed as primary channel for HT40",
|
||||
chan->chan);
|
||||
continue;
|
||||
}
|
||||
|
||||
if (iface->current_mode->mode == HOSTAPD_MODE_IEEE80211A &&
|
||||
(iface->conf->ieee80211ac || iface->conf->ieee80211ax)) {
|
||||
if (hostapd_get_oper_chwidth(iface->conf) ==
|
||||
CHANWIDTH_80MHZ &&
|
||||
!acs_usable_vht80_chan(chan)) {
|
||||
wpa_printf(MSG_DEBUG,
|
||||
"ACS: Channel %d: not allowed as primary channel for VHT80",
|
||||
chan->chan);
|
||||
continue;
|
||||
}
|
||||
|
||||
if (hostapd_get_oper_chwidth(iface->conf) ==
|
||||
CHANWIDTH_160MHZ &&
|
||||
!acs_usable_vht160_chan(chan)) {
|
||||
wpa_printf(MSG_DEBUG,
|
||||
"ACS: Channel %d: not allowed as primary channel for VHT160",
|
||||
chan->chan);
|
||||
continue;
|
||||
}
|
||||
}
|
||||
|
||||
factor = 0;
|
||||
if (acs_usable_chan(chan))
|
||||
factor = chan->interference_factor;
|
||||
total_weight = 1;
|
||||
|
||||
for (j = 1; j < n_chans; j++) {
|
||||
adj_chan = acs_find_chan(iface, chan->freq + (j * 20));
|
||||
if (!adj_chan)
|
||||
break;
|
||||
|
||||
if (!chan_bw_allowed(adj_chan, bw, 1, 0)) {
|
||||
wpa_printf(MSG_DEBUG,
|
||||
"ACS: PRI Channel %d: secondary channel %d BW %u is not supported",
|
||||
chan->chan, adj_chan->chan, bw);
|
||||
break;
|
||||
}
|
||||
|
||||
if (acs_usable_chan(adj_chan)) {
|
||||
factor += adj_chan->interference_factor;
|
||||
total_weight += 1;
|
||||
}
|
||||
}
|
||||
|
||||
if (j != n_chans) {
|
||||
wpa_printf(MSG_DEBUG, "ACS: Channel %d: not enough bandwidth",
|
||||
chan->chan);
|
||||
continue;
|
||||
}
|
||||
|
||||
/* 2.4 GHz has overlapping 20 MHz channels. Include adjacent
|
||||
* channel interference factor. */
|
||||
if (is_24ghz_mode(iface->current_mode->mode)) {
|
||||
for (j = 0; j < n_chans; j++) {
|
||||
adj_chan = acs_find_chan(iface, chan->freq +
|
||||
(j * 20) - 5);
|
||||
if (adj_chan && acs_usable_chan(adj_chan)) {
|
||||
factor += ACS_ADJ_WEIGHT *
|
||||
adj_chan->interference_factor;
|
||||
total_weight += ACS_ADJ_WEIGHT;
|
||||
}
|
||||
|
||||
adj_chan = acs_find_chan(iface, chan->freq +
|
||||
(j * 20) - 10);
|
||||
if (adj_chan && acs_usable_chan(adj_chan)) {
|
||||
factor += ACS_NEXT_ADJ_WEIGHT *
|
||||
adj_chan->interference_factor;
|
||||
total_weight += ACS_NEXT_ADJ_WEIGHT;
|
||||
}
|
||||
|
||||
adj_chan = acs_find_chan(iface, chan->freq +
|
||||
(j * 20) + 5);
|
||||
if (adj_chan && acs_usable_chan(adj_chan)) {
|
||||
factor += ACS_ADJ_WEIGHT *
|
||||
adj_chan->interference_factor;
|
||||
total_weight += ACS_ADJ_WEIGHT;
|
||||
}
|
||||
|
||||
adj_chan = acs_find_chan(iface, chan->freq +
|
||||
(j * 20) + 10);
|
||||
if (adj_chan && acs_usable_chan(adj_chan)) {
|
||||
factor += ACS_NEXT_ADJ_WEIGHT *
|
||||
adj_chan->interference_factor;
|
||||
total_weight += ACS_NEXT_ADJ_WEIGHT;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
factor /= total_weight;
|
||||
|
||||
bias = NULL;
|
||||
if (iface->conf->acs_chan_bias) {
|
||||
for (k = 0; k < iface->conf->num_acs_chan_bias; k++) {
|
||||
bias = &iface->conf->acs_chan_bias[k];
|
||||
if (bias->channel == chan->chan)
|
||||
break;
|
||||
bias = NULL;
|
||||
}
|
||||
} else if (is_24ghz_mode(iface->current_mode->mode) &&
|
||||
is_common_24ghz_chan(chan->chan)) {
|
||||
tmp_bias.channel = chan->chan;
|
||||
tmp_bias.bias = ACS_24GHZ_PREFER_1_6_11;
|
||||
bias = &tmp_bias;
|
||||
}
|
||||
|
||||
if (bias) {
|
||||
factor *= bias->bias;
|
||||
wpa_printf(MSG_DEBUG,
|
||||
"ACS: * channel %d: total interference = %Lg (%f bias)",
|
||||
chan->chan, factor, bias->bias);
|
||||
} else {
|
||||
wpa_printf(MSG_DEBUG,
|
||||
"ACS: * channel %d: total interference = %Lg",
|
||||
chan->chan, factor);
|
||||
}
|
||||
|
||||
if (acs_usable_chan(chan) &&
|
||||
(!ideal_chan || factor < ideal_factor)) {
|
||||
ideal_factor = factor;
|
||||
ideal_chan = chan;
|
||||
}
|
||||
|
||||
/* This channel would at least be usable */
|
||||
if (!rand_chan)
|
||||
rand_chan = chan;
|
||||
}
|
||||
|
||||
if (ideal_chan) {
|
||||
wpa_printf(MSG_DEBUG, "ACS: Ideal channel is %d (%d MHz) with total interference factor of %Lg",
|
||||
ideal_chan->chan, ideal_chan->freq, ideal_factor);
|
||||
return ideal_chan;
|
||||
}
|
||||
|
||||
return rand_chan;
|
||||
}
|
||||
|
||||
|
||||
static void acs_adjust_center_freq(struct hostapd_iface *iface)
|
||||
{
|
||||
int offset;
|
||||
|
||||
wpa_printf(MSG_DEBUG, "ACS: Adjusting VHT center frequency");
|
||||
|
||||
switch (hostapd_get_oper_chwidth(iface->conf)) {
|
||||
case CHANWIDTH_USE_HT:
|
||||
offset = 2 * iface->conf->secondary_channel;
|
||||
break;
|
||||
case CHANWIDTH_80MHZ:
|
||||
offset = 6;
|
||||
break;
|
||||
case CHANWIDTH_160MHZ:
|
||||
offset = 14;
|
||||
break;
|
||||
default:
|
||||
/* TODO: How can this be calculated? Adjust
|
||||
* acs_find_ideal_chan() */
|
||||
wpa_printf(MSG_INFO,
|
||||
"ACS: Only VHT20/40/80/160 is supported now");
|
||||
return;
|
||||
}
|
||||
|
||||
hostapd_set_oper_centr_freq_seg0_idx(iface->conf,
|
||||
iface->conf->channel + offset);
|
||||
}
|
||||
|
||||
|
||||
static int acs_study_survey_based(struct hostapd_iface *iface)
|
||||
{
|
||||
wpa_printf(MSG_DEBUG, "ACS: Trying survey-based ACS");
|
||||
|
||||
if (!iface->chans_surveyed) {
|
||||
wpa_printf(MSG_ERROR, "ACS: Unable to collect survey data");
|
||||
return -1;
|
||||
}
|
||||
|
||||
if (!acs_surveys_are_sufficient(iface)) {
|
||||
wpa_printf(MSG_ERROR, "ACS: Surveys have insufficient data");
|
||||
return -1;
|
||||
}
|
||||
|
||||
acs_survey_all_chans_intereference_factor(iface);
|
||||
return 0;
|
||||
}
|
||||
|
||||
|
||||
static int acs_study_options(struct hostapd_iface *iface)
|
||||
{
|
||||
if (acs_study_survey_based(iface) == 0)
|
||||
return 0;
|
||||
|
||||
/* TODO: If no surveys are available/sufficient this is a good
|
||||
* place to fallback to BSS-based ACS */
|
||||
|
||||
return -1;
|
||||
}
|
||||
|
||||
|
||||
static void acs_study(struct hostapd_iface *iface)
|
||||
{
|
||||
struct hostapd_channel_data *ideal_chan;
|
||||
int err;
|
||||
|
||||
err = acs_study_options(iface);
|
||||
if (err < 0) {
|
||||
wpa_printf(MSG_ERROR, "ACS: All study options have failed");
|
||||
goto fail;
|
||||
}
|
||||
|
||||
ideal_chan = acs_find_ideal_chan(iface);
|
||||
if (!ideal_chan) {
|
||||
wpa_printf(MSG_ERROR, "ACS: Failed to compute ideal channel");
|
||||
err = -1;
|
||||
goto fail;
|
||||
}
|
||||
|
||||
iface->conf->channel = ideal_chan->chan;
|
||||
|
||||
if (iface->conf->ieee80211ac || iface->conf->ieee80211ax)
|
||||
acs_adjust_center_freq(iface);
|
||||
|
||||
err = 0;
|
||||
fail:
|
||||
/*
|
||||
* hostapd_setup_interface_complete() will return -1 on failure,
|
||||
* 0 on success and 0 is HOSTAPD_CHAN_VALID :)
|
||||
*/
|
||||
if (hostapd_acs_completed(iface, err) == HOSTAPD_CHAN_VALID) {
|
||||
acs_cleanup(iface);
|
||||
return;
|
||||
}
|
||||
|
||||
/* This can possibly happen if channel parameters (secondary
|
||||
* channel, center frequencies) are misconfigured */
|
||||
wpa_printf(MSG_ERROR, "ACS: Possibly channel configuration is invalid, please report this along with your config file.");
|
||||
acs_fail(iface);
|
||||
}
|
||||
|
||||
|
||||
static void acs_scan_complete(struct hostapd_iface *iface)
|
||||
{
|
||||
int err;
|
||||
|
||||
iface->scan_cb = NULL;
|
||||
|
||||
wpa_printf(MSG_DEBUG, "ACS: Using survey based algorithm (acs_num_scans=%d)",
|
||||
iface->conf->acs_num_scans);
|
||||
|
||||
err = hostapd_drv_get_survey(iface->bss[0], 0);
|
||||
if (err) {
|
||||
wpa_printf(MSG_ERROR, "ACS: Failed to get survey data");
|
||||
goto fail;
|
||||
}
|
||||
|
||||
if (++iface->acs_num_completed_scans < iface->conf->acs_num_scans) {
|
||||
err = acs_request_scan(iface);
|
||||
if (err) {
|
||||
wpa_printf(MSG_ERROR, "ACS: Failed to request scan");
|
||||
goto fail;
|
||||
}
|
||||
|
||||
return;
|
||||
}
|
||||
|
||||
acs_study(iface);
|
||||
return;
|
||||
fail:
|
||||
hostapd_acs_completed(iface, 1);
|
||||
acs_fail(iface);
|
||||
}
|
||||
|
||||
|
||||
static int acs_request_scan(struct hostapd_iface *iface)
|
||||
{
|
||||
struct wpa_driver_scan_params params;
|
||||
struct hostapd_channel_data *chan;
|
||||
int i, *freq;
|
||||
|
||||
os_memset(¶ms, 0, sizeof(params));
|
||||
params.freqs = os_calloc(iface->current_mode->num_channels + 1,
|
||||
sizeof(params.freqs[0]));
|
||||
if (params.freqs == NULL)
|
||||
return -1;
|
||||
|
||||
freq = params.freqs;
|
||||
for (i = 0; i < iface->current_mode->num_channels; i++) {
|
||||
chan = &iface->current_mode->channels[i];
|
||||
if (chan->flag & HOSTAPD_CHAN_DISABLED)
|
||||
continue;
|
||||
|
||||
if (!is_in_chanlist(iface, chan))
|
||||
continue;
|
||||
|
||||
*freq++ = chan->freq;
|
||||
}
|
||||
*freq = 0;
|
||||
|
||||
iface->scan_cb = acs_scan_complete;
|
||||
|
||||
wpa_printf(MSG_DEBUG, "ACS: Scanning %d / %d",
|
||||
iface->acs_num_completed_scans + 1,
|
||||
iface->conf->acs_num_scans);
|
||||
|
||||
if (hostapd_driver_scan(iface->bss[0], ¶ms) < 0) {
|
||||
wpa_printf(MSG_ERROR, "ACS: Failed to request initial scan");
|
||||
acs_cleanup(iface);
|
||||
os_free(params.freqs);
|
||||
return -1;
|
||||
}
|
||||
|
||||
os_free(params.freqs);
|
||||
return 0;
|
||||
}
|
||||
|
||||
|
||||
enum hostapd_chan_status acs_init(struct hostapd_iface *iface)
|
||||
{
|
||||
wpa_printf(MSG_INFO, "ACS: Automatic channel selection started, this may take a bit");
|
||||
|
||||
if (iface->drv_flags & WPA_DRIVER_FLAGS_ACS_OFFLOAD) {
|
||||
wpa_printf(MSG_INFO, "ACS: Offloading to driver");
|
||||
if (hostapd_drv_do_acs(iface->bss[0]))
|
||||
return HOSTAPD_CHAN_INVALID;
|
||||
return HOSTAPD_CHAN_ACS;
|
||||
}
|
||||
|
||||
if (!iface->current_mode)
|
||||
return HOSTAPD_CHAN_INVALID;
|
||||
|
||||
acs_cleanup(iface);
|
||||
|
||||
if (acs_request_scan(iface) < 0)
|
||||
return HOSTAPD_CHAN_INVALID;
|
||||
|
||||
hostapd_set_state(iface, HAPD_IFACE_ACS);
|
||||
wpa_msg(iface->bss[0]->msg_ctx, MSG_INFO, ACS_EVENT_STARTED);
|
||||
|
||||
return HOSTAPD_CHAN_ACS;
|
||||
}
|
|
@ -0,0 +1,32 @@
|
|||
/*
|
||||
* ACS - Automatic Channel Selection module
|
||||
* Copyright (c) 2011, Atheros Communications
|
||||
* Copyright (c) 2013, Qualcomm Atheros, Inc.
|
||||
*
|
||||
* This software may be distributed under the terms of the BSD license.
|
||||
* See README for more details.
|
||||
*/
|
||||
|
||||
#ifndef ACS_H
|
||||
#define ACS_H
|
||||
|
||||
#ifdef CONFIG_ACS
|
||||
|
||||
enum hostapd_chan_status acs_init(struct hostapd_iface *iface);
|
||||
void acs_cleanup(struct hostapd_iface *iface);
|
||||
|
||||
#else /* CONFIG_ACS */
|
||||
|
||||
static inline enum hostapd_chan_status acs_init(struct hostapd_iface *iface)
|
||||
{
|
||||
wpa_printf(MSG_ERROR, "ACS was disabled on your build, rebuild hostapd with CONFIG_ACS=y or set channel");
|
||||
return HOSTAPD_CHAN_INVALID;
|
||||
}
|
||||
|
||||
static inline void acs_cleanup(struct hostapd_iface *iface)
|
||||
{
|
||||
}
|
||||
|
||||
#endif /* CONFIG_ACS */
|
||||
|
||||
#endif /* ACS_H */
|
|
@ -0,0 +1,269 @@
|
|||
/*
|
||||
* Airtime policy configuration
|
||||
* Copyright (c) 2018-2019, Toke Høiland-Jørgensen <toke@toke.dk>
|
||||
*
|
||||
* This software may be distributed under the terms of the BSD license.
|
||||
* See README for more details.
|
||||
*/
|
||||
|
||||
#include "utils/includes.h"
|
||||
|
||||
#include "utils/common.h"
|
||||
#include "utils/eloop.h"
|
||||
#include "hostapd.h"
|
||||
#include "ap_drv_ops.h"
|
||||
#include "sta_info.h"
|
||||
#include "airtime_policy.h"
|
||||
|
||||
/* Idea:
|
||||
* Two modes of airtime enforcement:
|
||||
* 1. Static weights: specify weights per MAC address with a per-BSS default
|
||||
* 2. Per-BSS limits: Dynamically calculate weights of backlogged stations to
|
||||
* enforce relative total shares between BSSes.
|
||||
*
|
||||
* - Periodic per-station callback to update queue status.
|
||||
*
|
||||
* Copy accounting_sta_update_stats() to get TXQ info and airtime weights and
|
||||
* keep them updated in sta_info.
|
||||
*
|
||||
* - Separate periodic per-bss (or per-iface?) callback to update weights.
|
||||
*
|
||||
* Just need to loop through all interfaces, count sum the active stations (or
|
||||
* should the per-STA callback just adjust that for the BSS?) and calculate new
|
||||
* weights.
|
||||
*/
|
||||
|
||||
static int get_airtime_policy_update_timeout(struct hostapd_iface *iface,
|
||||
unsigned int *sec,
|
||||
unsigned int *usec)
|
||||
{
|
||||
unsigned int update_int = iface->conf->airtime_update_interval;
|
||||
|
||||
if (!update_int) {
|
||||
wpa_printf(MSG_ERROR,
|
||||
"Airtime policy: Invalid airtime policy update interval %u",
|
||||
update_int);
|
||||
return -1;
|
||||
}
|
||||
|
||||
*sec = update_int / 1000;
|
||||
*usec = (update_int % 1000) * 1000;
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
|
||||
static void set_new_backlog_time(struct hostapd_data *hapd,
|
||||
struct sta_info *sta,
|
||||
struct os_reltime *now)
|
||||
{
|
||||
sta->backlogged_until = *now;
|
||||
sta->backlogged_until.usec += hapd->iconf->airtime_update_interval *
|
||||
AIRTIME_BACKLOG_EXPIRY_FACTOR;
|
||||
while (sta->backlogged_until.usec >= 1000000) {
|
||||
sta->backlogged_until.sec++;
|
||||
sta->backlogged_until.usec -= 1000000;
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
static void count_backlogged_sta(struct hostapd_data *hapd)
|
||||
{
|
||||
struct sta_info *sta;
|
||||
struct hostap_sta_driver_data data = {};
|
||||
unsigned int num_backlogged = 0;
|
||||
struct os_reltime now;
|
||||
|
||||
os_get_reltime(&now);
|
||||
|
||||
for (sta = hapd->sta_list; sta; sta = sta->next) {
|
||||
if (hostapd_drv_read_sta_data(hapd, &data, sta->addr))
|
||||
continue;
|
||||
|
||||
if (data.backlog_bytes > 0)
|
||||
set_new_backlog_time(hapd, sta, &now);
|
||||
if (os_reltime_before(&now, &sta->backlogged_until))
|
||||
num_backlogged++;
|
||||
}
|
||||
hapd->num_backlogged_sta = num_backlogged;
|
||||
}
|
||||
|
||||
|
||||
static int sta_set_airtime_weight(struct hostapd_data *hapd,
|
||||
struct sta_info *sta,
|
||||
unsigned int weight)
|
||||
{
|
||||
int ret = 0;
|
||||
|
||||
if (weight != sta->airtime_weight &&
|
||||
(ret = hostapd_sta_set_airtime_weight(hapd, sta->addr, weight)))
|
||||
return ret;
|
||||
|
||||
sta->airtime_weight = weight;
|
||||
return ret;
|
||||
}
|
||||
|
||||
|
||||
static void set_sta_weights(struct hostapd_data *hapd, unsigned int weight)
|
||||
{
|
||||
struct sta_info *sta;
|
||||
|
||||
for (sta = hapd->sta_list; sta; sta = sta->next)
|
||||
sta_set_airtime_weight(hapd, sta, weight);
|
||||
}
|
||||
|
||||
|
||||
static unsigned int get_airtime_quantum(unsigned int max_wt)
|
||||
{
|
||||
unsigned int quantum = AIRTIME_QUANTUM_TARGET / max_wt;
|
||||
|
||||
if (quantum < AIRTIME_QUANTUM_MIN)
|
||||
quantum = AIRTIME_QUANTUM_MIN;
|
||||
else if (quantum > AIRTIME_QUANTUM_MAX)
|
||||
quantum = AIRTIME_QUANTUM_MAX;
|
||||
|
||||
return quantum;
|
||||
}
|
||||
|
||||
|
||||
static void update_airtime_weights(void *eloop_data, void *user_data)
|
||||
{
|
||||
struct hostapd_iface *iface = eloop_data;
|
||||
struct hostapd_data *bss;
|
||||
unsigned int sec, usec;
|
||||
unsigned int num_sta_min = 0, num_sta_prod = 1, num_sta_sum = 0,
|
||||
wt_sum = 0;
|
||||
unsigned int quantum;
|
||||
Boolean all_div_min = TRUE;
|
||||
Boolean apply_limit = iface->conf->airtime_mode == AIRTIME_MODE_DYNAMIC;
|
||||
int wt, num_bss = 0, max_wt = 0;
|
||||
size_t i;
|
||||
|
||||
for (i = 0; i < iface->num_bss; i++) {
|
||||
bss = iface->bss[i];
|
||||
if (!bss->started || !bss->conf->airtime_weight)
|
||||
continue;
|
||||
|
||||
count_backlogged_sta(bss);
|
||||
if (!bss->num_backlogged_sta)
|
||||
continue;
|
||||
|
||||
if (!num_sta_min || bss->num_backlogged_sta < num_sta_min)
|
||||
num_sta_min = bss->num_backlogged_sta;
|
||||
|
||||
num_sta_prod *= bss->num_backlogged_sta;
|
||||
num_sta_sum += bss->num_backlogged_sta;
|
||||
wt_sum += bss->conf->airtime_weight;
|
||||
num_bss++;
|
||||
}
|
||||
|
||||
if (num_sta_min) {
|
||||
for (i = 0; i < iface->num_bss; i++) {
|
||||
bss = iface->bss[i];
|
||||
if (!bss->started || !bss->conf->airtime_weight)
|
||||
continue;
|
||||
|
||||
/* Check if we can divide all sta numbers by the
|
||||
* smallest number to keep weights as small as possible.
|
||||
* This is a lazy way to avoid having to factor
|
||||
* integers. */
|
||||
if (bss->num_backlogged_sta &&
|
||||
bss->num_backlogged_sta % num_sta_min > 0)
|
||||
all_div_min = FALSE;
|
||||
|
||||
/* If we're in LIMIT mode, we only apply the weight
|
||||
* scaling when the BSS(es) marked as limited would a
|
||||
* larger share than the relative BSS weights indicates
|
||||
* it should. */
|
||||
if (!apply_limit && bss->conf->airtime_limit) {
|
||||
if (bss->num_backlogged_sta * wt_sum >
|
||||
bss->conf->airtime_weight * num_sta_sum)
|
||||
apply_limit = TRUE;
|
||||
}
|
||||
}
|
||||
if (all_div_min)
|
||||
num_sta_prod /= num_sta_min;
|
||||
}
|
||||
|
||||
for (i = 0; i < iface->num_bss; i++) {
|
||||
bss = iface->bss[i];
|
||||
if (!bss->started || !bss->conf->airtime_weight)
|
||||
continue;
|
||||
|
||||
/* We only set the calculated weight if the BSS has active
|
||||
* stations and there are other active interfaces as well -
|
||||
* otherwise we just set a unit weight. This ensures that
|
||||
* the weights are set reasonably when stations transition from
|
||||
* inactive to active. */
|
||||
if (apply_limit && bss->num_backlogged_sta && num_bss > 1)
|
||||
wt = bss->conf->airtime_weight * num_sta_prod /
|
||||
bss->num_backlogged_sta;
|
||||
else
|
||||
wt = 1;
|
||||
|
||||
bss->airtime_weight = wt;
|
||||
if (wt > max_wt)
|
||||
max_wt = wt;
|
||||
}
|
||||
|
||||
quantum = get_airtime_quantum(max_wt);
|
||||
|
||||
for (i = 0; i < iface->num_bss; i++) {
|
||||
bss = iface->bss[i];
|
||||
if (!bss->started || !bss->conf->airtime_weight)
|
||||
continue;
|
||||
set_sta_weights(bss, bss->airtime_weight * quantum);
|
||||
}
|
||||
|
||||
if (get_airtime_policy_update_timeout(iface, &sec, &usec) < 0)
|
||||
return;
|
||||
|
||||
eloop_register_timeout(sec, usec, update_airtime_weights, iface,
|
||||
NULL);
|
||||
}
|
||||
|
||||
|
||||
static int get_weight_for_sta(struct hostapd_data *hapd, const u8 *sta)
|
||||
{
|
||||
struct airtime_sta_weight *wt;
|
||||
|
||||
wt = hapd->conf->airtime_weight_list;
|
||||
while (wt && os_memcmp(wt->addr, sta, ETH_ALEN) != 0)
|
||||
wt = wt->next;
|
||||
|
||||
return wt ? wt->weight : hapd->conf->airtime_weight;
|
||||
}
|
||||
|
||||
|
||||
int airtime_policy_new_sta(struct hostapd_data *hapd, struct sta_info *sta)
|
||||
{
|
||||
unsigned int weight;
|
||||
|
||||
if (hapd->iconf->airtime_mode == AIRTIME_MODE_STATIC) {
|
||||
weight = get_weight_for_sta(hapd, sta->addr);
|
||||
if (weight)
|
||||
return sta_set_airtime_weight(hapd, sta, weight);
|
||||
}
|
||||
return 0;
|
||||
}
|
||||
|
||||
|
||||
int airtime_policy_update_init(struct hostapd_iface *iface)
|
||||
{
|
||||
unsigned int sec, usec;
|
||||
|
||||
if (iface->conf->airtime_mode < AIRTIME_MODE_DYNAMIC)
|
||||
return 0;
|
||||
|
||||
if (get_airtime_policy_update_timeout(iface, &sec, &usec) < 0)
|
||||
return -1;
|
||||
|
||||
eloop_register_timeout(sec, usec, update_airtime_weights, iface, NULL);
|
||||
return 0;
|
||||
}
|
||||
|
||||
|
||||
void airtime_policy_update_deinit(struct hostapd_iface *iface)
|
||||
{
|
||||
eloop_cancel_timeout(update_airtime_weights, iface, NULL);
|
||||
}
|
|
@ -0,0 +1,48 @@
|
|||
/*
|
||||
* Airtime policy configuration
|
||||
* Copyright (c) 2018-2019, Toke Høiland-Jørgensen <toke@toke.dk>
|
||||
*
|
||||
* This software may be distributed under the terms of the BSD license.
|
||||
* See README for more details.
|
||||
*/
|
||||
|
||||
#ifndef AIRTIME_POLICY_H
|
||||
#define AIRTIME_POLICY_H
|
||||
|
||||
struct hostapd_iface;
|
||||
|
||||
#ifdef CONFIG_AIRTIME_POLICY
|
||||
|
||||
#define AIRTIME_DEFAULT_UPDATE_INTERVAL 200 /* ms */
|
||||
#define AIRTIME_BACKLOG_EXPIRY_FACTOR 2500 /* 2.5 intervals + convert to usec */
|
||||
|
||||
/* scale quantum so this becomes the effective quantum after applying the max
|
||||
* weight, but never go below min or above max */
|
||||
#define AIRTIME_QUANTUM_MIN 8 /* usec */
|
||||
#define AIRTIME_QUANTUM_MAX 256 /* usec */
|
||||
#define AIRTIME_QUANTUM_TARGET 1024 /* usec */
|
||||
|
||||
int airtime_policy_new_sta(struct hostapd_data *hapd, struct sta_info *sta);
|
||||
int airtime_policy_update_init(struct hostapd_iface *iface);
|
||||
void airtime_policy_update_deinit(struct hostapd_iface *iface);
|
||||
|
||||
#else /* CONFIG_AIRTIME_POLICY */
|
||||
|
||||
static inline int airtime_policy_new_sta(struct hostapd_data *hapd,
|
||||
struct sta_info *sta)
|
||||
{
|
||||
return -1;
|
||||
}
|
||||
|
||||
static inline int airtime_policy_update_init(struct hostapd_iface *iface)
|
||||
{
|
||||
return -1;
|
||||
}
|
||||
|
||||
static inline void airtime_policy_update_deinit(struct hostapd_iface *iface)
|
||||
{
|
||||
}
|
||||
|
||||
#endif /* CONFIG_AIRTIME_POLICY */
|
||||
|
||||
#endif /* AIRTIME_POLICY_H */
|
File diff suppressed because it is too large
Load Diff
File diff suppressed because it is too large
Load Diff
|
@ -0,0 +1,970 @@
|
|||
/*
|
||||
* hostapd - Driver operations
|
||||
* Copyright (c) 2009-2010, Jouni Malinen <j@w1.fi>
|
||||
*
|
||||
* This software may be distributed under the terms of the BSD license.
|
||||
* See README for more details.
|
||||
*/
|
||||
|
||||
#include "utils/includes.h"
|
||||
|
||||
#include "utils/common.h"
|
||||
#include "common/ieee802_11_defs.h"
|
||||
#include "common/hw_features_common.h"
|
||||
#include "wps/wps.h"
|
||||
#include "p2p/p2p.h"
|
||||
#include "hostapd.h"
|
||||
#include "ieee802_11.h"
|
||||
#include "sta_info.h"
|
||||
#include "ap_config.h"
|
||||
#include "p2p_hostapd.h"
|
||||
#include "hs20.h"
|
||||
#include "wpa_auth.h"
|
||||
#include "ap_drv_ops.h"
|
||||
|
||||
|
||||
u32 hostapd_sta_flags_to_drv(u32 flags)
|
||||
{
|
||||
int res = 0;
|
||||
if (flags & WLAN_STA_AUTHORIZED)
|
||||
res |= WPA_STA_AUTHORIZED;
|
||||
if (flags & WLAN_STA_WMM)
|
||||
res |= WPA_STA_WMM;
|
||||
if (flags & WLAN_STA_SHORT_PREAMBLE)
|
||||
res |= WPA_STA_SHORT_PREAMBLE;
|
||||
if (flags & WLAN_STA_MFP)
|
||||
res |= WPA_STA_MFP;
|
||||
if (flags & WLAN_STA_AUTH)
|
||||
res |= WPA_STA_AUTHENTICATED;
|
||||
if (flags & WLAN_STA_ASSOC)
|
||||
res |= WPA_STA_ASSOCIATED;
|
||||
return res;
|
||||
}
|
||||
|
||||
|
||||
static int add_buf(struct wpabuf **dst, const struct wpabuf *src)
|
||||
{
|
||||
if (!src)
|
||||
return 0;
|
||||
if (wpabuf_resize(dst, wpabuf_len(src)) != 0)
|
||||
return -1;
|
||||
wpabuf_put_buf(*dst, src);
|
||||
return 0;
|
||||
}
|
||||
|
||||
|
||||
static int add_buf_data(struct wpabuf **dst, const u8 *data, size_t len)
|
||||
{
|
||||
if (!data || !len)
|
||||
return 0;
|
||||
if (wpabuf_resize(dst, len) != 0)
|
||||
return -1;
|
||||
wpabuf_put_data(*dst, data, len);
|
||||
return 0;
|
||||
}
|
||||
|
||||
|
||||
int hostapd_build_ap_extra_ies(struct hostapd_data *hapd,
|
||||
struct wpabuf **beacon_ret,
|
||||
struct wpabuf **proberesp_ret,
|
||||
struct wpabuf **assocresp_ret)
|
||||
{
|
||||
struct wpabuf *beacon = NULL, *proberesp = NULL, *assocresp = NULL;
|
||||
u8 buf[200], *pos;
|
||||
|
||||
*beacon_ret = *proberesp_ret = *assocresp_ret = NULL;
|
||||
|
||||
pos = buf;
|
||||
pos = hostapd_eid_time_adv(hapd, pos);
|
||||
if (add_buf_data(&beacon, buf, pos - buf) < 0)
|
||||
goto fail;
|
||||
pos = hostapd_eid_time_zone(hapd, pos);
|
||||
if (add_buf_data(&proberesp, buf, pos - buf) < 0)
|
||||
goto fail;
|
||||
|
||||
pos = buf;
|
||||
pos = hostapd_eid_ext_capab(hapd, pos);
|
||||
if (add_buf_data(&assocresp, buf, pos - buf) < 0)
|
||||
goto fail;
|
||||
pos = hostapd_eid_interworking(hapd, pos);
|
||||
pos = hostapd_eid_adv_proto(hapd, pos);
|
||||
pos = hostapd_eid_roaming_consortium(hapd, pos);
|
||||
if (add_buf_data(&beacon, buf, pos - buf) < 0 ||
|
||||
add_buf_data(&proberesp, buf, pos - buf) < 0)
|
||||
goto fail;
|
||||
|
||||
#ifdef CONFIG_FST
|
||||
if (add_buf(&beacon, hapd->iface->fst_ies) < 0 ||
|
||||
add_buf(&proberesp, hapd->iface->fst_ies) < 0 ||
|
||||
add_buf(&assocresp, hapd->iface->fst_ies) < 0)
|
||||
goto fail;
|
||||
#endif /* CONFIG_FST */
|
||||
|
||||
#ifdef CONFIG_FILS
|
||||
pos = hostapd_eid_fils_indic(hapd, buf, 0);
|
||||
if (add_buf_data(&beacon, buf, pos - buf) < 0 ||
|
||||
add_buf_data(&proberesp, buf, pos - buf) < 0)
|
||||
goto fail;
|
||||
#endif /* CONFIG_FILS */
|
||||
|
||||
if (add_buf(&beacon, hapd->wps_beacon_ie) < 0 ||
|
||||
add_buf(&proberesp, hapd->wps_probe_resp_ie) < 0)
|
||||
goto fail;
|
||||
|
||||
#ifdef CONFIG_P2P
|
||||
if (add_buf(&beacon, hapd->p2p_beacon_ie) < 0 ||
|
||||
add_buf(&proberesp, hapd->p2p_probe_resp_ie) < 0)
|
||||
goto fail;
|
||||
#endif /* CONFIG_P2P */
|
||||
|
||||
#ifdef CONFIG_P2P_MANAGER
|
||||
if (hapd->conf->p2p & P2P_MANAGE) {
|
||||
if (wpabuf_resize(&beacon, 100) == 0) {
|
||||
u8 *start, *p;
|
||||
start = wpabuf_put(beacon, 0);
|
||||
p = hostapd_eid_p2p_manage(hapd, start);
|
||||
wpabuf_put(beacon, p - start);
|
||||
}
|
||||
|
||||
if (wpabuf_resize(&proberesp, 100) == 0) {
|
||||
u8 *start, *p;
|
||||
start = wpabuf_put(proberesp, 0);
|
||||
p = hostapd_eid_p2p_manage(hapd, start);
|
||||
wpabuf_put(proberesp, p - start);
|
||||
}
|
||||
}
|
||||
#endif /* CONFIG_P2P_MANAGER */
|
||||
|
||||
#ifdef CONFIG_WPS
|
||||
if (hapd->conf->wps_state) {
|
||||
struct wpabuf *a = wps_build_assoc_resp_ie();
|
||||
add_buf(&assocresp, a);
|
||||
wpabuf_free(a);
|
||||
}
|
||||
#endif /* CONFIG_WPS */
|
||||
|
||||
#ifdef CONFIG_P2P_MANAGER
|
||||
if (hapd->conf->p2p & P2P_MANAGE) {
|
||||
if (wpabuf_resize(&assocresp, 100) == 0) {
|
||||
u8 *start, *p;
|
||||
start = wpabuf_put(assocresp, 0);
|
||||
p = hostapd_eid_p2p_manage(hapd, start);
|
||||
wpabuf_put(assocresp, p - start);
|
||||
}
|
||||
}
|
||||
#endif /* CONFIG_P2P_MANAGER */
|
||||
|
||||
#ifdef CONFIG_WIFI_DISPLAY
|
||||
if (hapd->p2p_group) {
|
||||
struct wpabuf *a;
|
||||
a = p2p_group_assoc_resp_ie(hapd->p2p_group, P2P_SC_SUCCESS);
|
||||
add_buf(&assocresp, a);
|
||||
wpabuf_free(a);
|
||||
}
|
||||
#endif /* CONFIG_WIFI_DISPLAY */
|
||||
|
||||
#ifdef CONFIG_HS20
|
||||
pos = hostapd_eid_hs20_indication(hapd, buf);
|
||||
if (add_buf_data(&beacon, buf, pos - buf) < 0 ||
|
||||
add_buf_data(&proberesp, buf, pos - buf) < 0)
|
||||
goto fail;
|
||||
|
||||
pos = hostapd_eid_osen(hapd, buf);
|
||||
if (add_buf_data(&beacon, buf, pos - buf) < 0 ||
|
||||
add_buf_data(&proberesp, buf, pos - buf) < 0)
|
||||
goto fail;
|
||||
#endif /* CONFIG_HS20 */
|
||||
|
||||
#ifdef CONFIG_MBO
|
||||
if (hapd->conf->mbo_enabled ||
|
||||
OCE_STA_CFON_ENABLED(hapd) || OCE_AP_ENABLED(hapd)) {
|
||||
pos = hostapd_eid_mbo(hapd, buf, sizeof(buf));
|
||||
if (add_buf_data(&beacon, buf, pos - buf) < 0 ||
|
||||
add_buf_data(&proberesp, buf, pos - buf) < 0 ||
|
||||
add_buf_data(&assocresp, buf, pos - buf) < 0)
|
||||
goto fail;
|
||||
}
|
||||
#endif /* CONFIG_MBO */
|
||||
|
||||
#ifdef CONFIG_OWE
|
||||
pos = hostapd_eid_owe_trans(hapd, buf, sizeof(buf));
|
||||
if (add_buf_data(&beacon, buf, pos - buf) < 0 ||
|
||||
add_buf_data(&proberesp, buf, pos - buf) < 0)
|
||||
goto fail;
|
||||
#endif /* CONFIG_OWE */
|
||||
|
||||
add_buf(&beacon, hapd->conf->vendor_elements);
|
||||
add_buf(&proberesp, hapd->conf->vendor_elements);
|
||||
add_buf(&assocresp, hapd->conf->assocresp_elements);
|
||||
|
||||
*beacon_ret = beacon;
|
||||
*proberesp_ret = proberesp;
|
||||
*assocresp_ret = assocresp;
|
||||
|
||||
return 0;
|
||||
|
||||
fail:
|
||||
wpabuf_free(beacon);
|
||||
wpabuf_free(proberesp);
|
||||
wpabuf_free(assocresp);
|
||||
return -1;
|
||||
}
|
||||
|
||||
|
||||
void hostapd_free_ap_extra_ies(struct hostapd_data *hapd,
|
||||
struct wpabuf *beacon,
|
||||
struct wpabuf *proberesp,
|
||||
struct wpabuf *assocresp)
|
||||
{
|
||||
wpabuf_free(beacon);
|
||||
wpabuf_free(proberesp);
|
||||
wpabuf_free(assocresp);
|
||||
}
|
||||
|
||||
|
||||
int hostapd_reset_ap_wps_ie(struct hostapd_data *hapd)
|
||||
{
|
||||
if (hapd->driver == NULL || hapd->driver->set_ap_wps_ie == NULL)
|
||||
return 0;
|
||||
|
||||
return hapd->driver->set_ap_wps_ie(hapd->drv_priv, NULL, NULL, NULL);
|
||||
}
|
||||
|
||||
|
||||
int hostapd_set_ap_wps_ie(struct hostapd_data *hapd)
|
||||
{
|
||||
struct wpabuf *beacon, *proberesp, *assocresp;
|
||||
int ret;
|
||||
|
||||
if (hapd->driver == NULL || hapd->driver->set_ap_wps_ie == NULL)
|
||||
return 0;
|
||||
|
||||
if (hostapd_build_ap_extra_ies(hapd, &beacon, &proberesp, &assocresp) <
|
||||
0)
|
||||
return -1;
|
||||
|
||||
ret = hapd->driver->set_ap_wps_ie(hapd->drv_priv, beacon, proberesp,
|
||||
assocresp);
|
||||
|
||||
hostapd_free_ap_extra_ies(hapd, beacon, proberesp, assocresp);
|
||||
|
||||
return ret;
|
||||
}
|
||||
|
||||
|
||||
int hostapd_set_authorized(struct hostapd_data *hapd,
|
||||
struct sta_info *sta, int authorized)
|
||||
{
|
||||
if (authorized) {
|
||||
return hostapd_sta_set_flags(hapd, sta->addr,
|
||||
hostapd_sta_flags_to_drv(
|
||||
sta->flags),
|
||||
WPA_STA_AUTHORIZED, ~0);
|
||||
}
|
||||
|
||||
return hostapd_sta_set_flags(hapd, sta->addr,
|
||||
hostapd_sta_flags_to_drv(sta->flags),
|
||||
0, ~WPA_STA_AUTHORIZED);
|
||||
}
|
||||
|
||||
|
||||
int hostapd_set_sta_flags(struct hostapd_data *hapd, struct sta_info *sta)
|
||||
{
|
||||
int set_flags, total_flags, flags_and, flags_or;
|
||||
total_flags = hostapd_sta_flags_to_drv(sta->flags);
|
||||
set_flags = WPA_STA_SHORT_PREAMBLE | WPA_STA_WMM | WPA_STA_MFP;
|
||||
if (((!hapd->conf->ieee802_1x && !hapd->conf->wpa) ||
|
||||
sta->auth_alg == WLAN_AUTH_FT) &&
|
||||
sta->flags & WLAN_STA_AUTHORIZED)
|
||||
set_flags |= WPA_STA_AUTHORIZED;
|
||||
flags_or = total_flags & set_flags;
|
||||
flags_and = total_flags | ~set_flags;
|
||||
return hostapd_sta_set_flags(hapd, sta->addr, total_flags,
|
||||
flags_or, flags_and);
|
||||
}
|
||||
|
||||
|
||||
int hostapd_set_drv_ieee8021x(struct hostapd_data *hapd, const char *ifname,
|
||||
int enabled)
|
||||
{
|
||||
struct wpa_bss_params params;
|
||||
os_memset(¶ms, 0, sizeof(params));
|
||||
params.ifname = ifname;
|
||||
params.enabled = enabled;
|
||||
if (enabled) {
|
||||
params.wpa = hapd->conf->wpa;
|
||||
params.ieee802_1x = hapd->conf->ieee802_1x;
|
||||
params.wpa_group = hapd->conf->wpa_group;
|
||||
if ((hapd->conf->wpa & (WPA_PROTO_WPA | WPA_PROTO_RSN)) ==
|
||||
(WPA_PROTO_WPA | WPA_PROTO_RSN))
|
||||
params.wpa_pairwise = hapd->conf->wpa_pairwise |
|
||||
hapd->conf->rsn_pairwise;
|
||||
else if (hapd->conf->wpa & WPA_PROTO_RSN)
|
||||
params.wpa_pairwise = hapd->conf->rsn_pairwise;
|
||||
else if (hapd->conf->wpa & WPA_PROTO_WPA)
|
||||
params.wpa_pairwise = hapd->conf->wpa_pairwise;
|
||||
params.wpa_key_mgmt = hapd->conf->wpa_key_mgmt;
|
||||
params.rsn_preauth = hapd->conf->rsn_preauth;
|
||||
#ifdef CONFIG_IEEE80211W
|
||||
params.ieee80211w = hapd->conf->ieee80211w;
|
||||
#endif /* CONFIG_IEEE80211W */
|
||||
}
|
||||
return hostapd_set_ieee8021x(hapd, ¶ms);
|
||||
}
|
||||
|
||||
|
||||
int hostapd_vlan_if_add(struct hostapd_data *hapd, const char *ifname)
|
||||
{
|
||||
char force_ifname[IFNAMSIZ];
|
||||
u8 if_addr[ETH_ALEN];
|
||||
return hostapd_if_add(hapd, WPA_IF_AP_VLAN, ifname, hapd->own_addr,
|
||||
NULL, NULL, force_ifname, if_addr, NULL, 0);
|
||||
}
|
||||
|
||||
|
||||
int hostapd_vlan_if_remove(struct hostapd_data *hapd, const char *ifname)
|
||||
{
|
||||
return hostapd_if_remove(hapd, WPA_IF_AP_VLAN, ifname);
|
||||
}
|
||||
|
||||
|
||||
int hostapd_set_wds_sta(struct hostapd_data *hapd, char *ifname_wds,
|
||||
const u8 *addr, int aid, int val)
|
||||
{
|
||||
const char *bridge = NULL;
|
||||
|
||||
if (hapd->driver == NULL || hapd->driver->set_wds_sta == NULL)
|
||||
return -1;
|
||||
if (hapd->conf->wds_bridge[0])
|
||||
bridge = hapd->conf->wds_bridge;
|
||||
else if (hapd->conf->bridge[0])
|
||||
bridge = hapd->conf->bridge;
|
||||
return hapd->driver->set_wds_sta(hapd->drv_priv, addr, aid, val,
|
||||
bridge, ifname_wds);
|
||||
}
|
||||
|
||||
|
||||
int hostapd_add_sta_node(struct hostapd_data *hapd, const u8 *addr,
|
||||
u16 auth_alg)
|
||||
{
|
||||
if (hapd->driver == NULL || hapd->driver->add_sta_node == NULL)
|
||||
return 0;
|
||||
return hapd->driver->add_sta_node(hapd->drv_priv, addr, auth_alg);
|
||||
}
|
||||
|
||||
|
||||
int hostapd_sta_auth(struct hostapd_data *hapd, const u8 *addr,
|
||||
u16 seq, u16 status, const u8 *ie, size_t len)
|
||||
{
|
||||
struct wpa_driver_sta_auth_params params;
|
||||
#ifdef CONFIG_FILS
|
||||
struct sta_info *sta;
|
||||
#endif /* CONFIG_FILS */
|
||||
|
||||
if (hapd->driver == NULL || hapd->driver->sta_auth == NULL)
|
||||
return 0;
|
||||
|
||||
os_memset(¶ms, 0, sizeof(params));
|
||||
|
||||
#ifdef CONFIG_FILS
|
||||
sta = ap_get_sta(hapd, addr);
|
||||
if (!sta) {
|
||||
wpa_printf(MSG_DEBUG, "Station " MACSTR
|
||||
" not found for sta_auth processing",
|
||||
MAC2STR(addr));
|
||||
return 0;
|
||||
}
|
||||
|
||||
if (sta->auth_alg == WLAN_AUTH_FILS_SK ||
|
||||
sta->auth_alg == WLAN_AUTH_FILS_SK_PFS ||
|
||||
sta->auth_alg == WLAN_AUTH_FILS_PK) {
|
||||
params.fils_auth = 1;
|
||||
wpa_auth_get_fils_aead_params(sta->wpa_sm, params.fils_anonce,
|
||||
params.fils_snonce,
|
||||
params.fils_kek,
|
||||
¶ms.fils_kek_len);
|
||||
}
|
||||
#endif /* CONFIG_FILS */
|
||||
|
||||
params.own_addr = hapd->own_addr;
|
||||
params.addr = addr;
|
||||
params.seq = seq;
|
||||
params.status = status;
|
||||
params.ie = ie;
|
||||
params.len = len;
|
||||
|
||||
return hapd->driver->sta_auth(hapd->drv_priv, ¶ms);
|
||||
}
|
||||
|
||||
|
||||
int hostapd_sta_assoc(struct hostapd_data *hapd, const u8 *addr,
|
||||
int reassoc, u16 status, const u8 *ie, size_t len)
|
||||
{
|
||||
if (hapd->driver == NULL || hapd->driver->sta_assoc == NULL)
|
||||
return 0;
|
||||
return hapd->driver->sta_assoc(hapd->drv_priv, hapd->own_addr, addr,
|
||||
reassoc, status, ie, len);
|
||||
}
|
||||
|
||||
|
||||
int hostapd_sta_add(struct hostapd_data *hapd,
|
||||
const u8 *addr, u16 aid, u16 capability,
|
||||
const u8 *supp_rates, size_t supp_rates_len,
|
||||
u16 listen_interval,
|
||||
const struct ieee80211_ht_capabilities *ht_capab,
|
||||
const struct ieee80211_vht_capabilities *vht_capab,
|
||||
const struct ieee80211_he_capabilities *he_capab,
|
||||
size_t he_capab_len,
|
||||
u32 flags, u8 qosinfo, u8 vht_opmode, int supp_p2p_ps,
|
||||
int set)
|
||||
{
|
||||
struct hostapd_sta_add_params params;
|
||||
|
||||
if (hapd->driver == NULL)
|
||||
return 0;
|
||||
if (hapd->driver->sta_add == NULL)
|
||||
return 0;
|
||||
|
||||
os_memset(¶ms, 0, sizeof(params));
|
||||
params.addr = addr;
|
||||
params.aid = aid;
|
||||
params.capability = capability;
|
||||
params.supp_rates = supp_rates;
|
||||
params.supp_rates_len = supp_rates_len;
|
||||
params.listen_interval = listen_interval;
|
||||
params.ht_capabilities = ht_capab;
|
||||
params.vht_capabilities = vht_capab;
|
||||
params.he_capab = he_capab;
|
||||
params.he_capab_len = he_capab_len;
|
||||
params.vht_opmode_enabled = !!(flags & WLAN_STA_VHT_OPMODE_ENABLED);
|
||||
params.vht_opmode = vht_opmode;
|
||||
params.flags = hostapd_sta_flags_to_drv(flags);
|
||||
params.qosinfo = qosinfo;
|
||||
params.support_p2p_ps = supp_p2p_ps;
|
||||
params.set = set;
|
||||
return hapd->driver->sta_add(hapd->drv_priv, ¶ms);
|
||||
}
|
||||
|
||||
|
||||
int hostapd_add_tspec(struct hostapd_data *hapd, const u8 *addr,
|
||||
u8 *tspec_ie, size_t tspec_ielen)
|
||||
{
|
||||
if (hapd->driver == NULL || hapd->driver->add_tspec == NULL)
|
||||
return 0;
|
||||
return hapd->driver->add_tspec(hapd->drv_priv, addr, tspec_ie,
|
||||
tspec_ielen);
|
||||
}
|
||||
|
||||
|
||||
int hostapd_set_privacy(struct hostapd_data *hapd, int enabled)
|
||||
{
|
||||
if (hapd->driver == NULL || hapd->driver->set_privacy == NULL)
|
||||
return 0;
|
||||
return hapd->driver->set_privacy(hapd->drv_priv, enabled);
|
||||
}
|
||||
|
||||
|
||||
int hostapd_set_generic_elem(struct hostapd_data *hapd, const u8 *elem,
|
||||
size_t elem_len)
|
||||
{
|
||||
if (hapd->driver == NULL || hapd->driver->set_generic_elem == NULL)
|
||||
return 0;
|
||||
return hapd->driver->set_generic_elem(hapd->drv_priv, elem, elem_len);
|
||||
}
|
||||
|
||||
|
||||
int hostapd_get_ssid(struct hostapd_data *hapd, u8 *buf, size_t len)
|
||||
{
|
||||
if (hapd->driver == NULL || hapd->driver->hapd_get_ssid == NULL)
|
||||
return 0;
|
||||
return hapd->driver->hapd_get_ssid(hapd->drv_priv, buf, len);
|
||||
}
|
||||
|
||||
|
||||
int hostapd_set_ssid(struct hostapd_data *hapd, const u8 *buf, size_t len)
|
||||
{
|
||||
if (hapd->driver == NULL || hapd->driver->hapd_set_ssid == NULL)
|
||||
return 0;
|
||||
return hapd->driver->hapd_set_ssid(hapd->drv_priv, buf, len);
|
||||
}
|
||||
|
||||
|
||||
int hostapd_if_add(struct hostapd_data *hapd, enum wpa_driver_if_type type,
|
||||
const char *ifname, const u8 *addr, void *bss_ctx,
|
||||
void **drv_priv, char *force_ifname, u8 *if_addr,
|
||||
const char *bridge, int use_existing)
|
||||
{
|
||||
if (hapd->driver == NULL || hapd->driver->if_add == NULL)
|
||||
return -1;
|
||||
return hapd->driver->if_add(hapd->drv_priv, type, ifname, addr,
|
||||
bss_ctx, drv_priv, force_ifname, if_addr,
|
||||
bridge, use_existing, 1);
|
||||
}
|
||||
|
||||
|
||||
int hostapd_if_remove(struct hostapd_data *hapd, enum wpa_driver_if_type type,
|
||||
const char *ifname)
|
||||
{
|
||||
if (hapd->driver == NULL || hapd->drv_priv == NULL ||
|
||||
hapd->driver->if_remove == NULL)
|
||||
return -1;
|
||||
return hapd->driver->if_remove(hapd->drv_priv, type, ifname);
|
||||
}
|
||||
|
||||
|
||||
int hostapd_set_ieee8021x(struct hostapd_data *hapd,
|
||||
struct wpa_bss_params *params)
|
||||
{
|
||||
if (hapd->driver == NULL || hapd->driver->set_ieee8021x == NULL)
|
||||
return 0;
|
||||
return hapd->driver->set_ieee8021x(hapd->drv_priv, params);
|
||||
}
|
||||
|
||||
|
||||
int hostapd_get_seqnum(const char *ifname, struct hostapd_data *hapd,
|
||||
const u8 *addr, int idx, u8 *seq)
|
||||
{
|
||||
if (hapd->driver == NULL || hapd->driver->get_seqnum == NULL)
|
||||
return 0;
|
||||
return hapd->driver->get_seqnum(ifname, hapd->drv_priv, addr, idx,
|
||||
seq);
|
||||
}
|
||||
|
||||
|
||||
int hostapd_flush(struct hostapd_data *hapd)
|
||||
{
|
||||
if (hapd->driver == NULL || hapd->driver->flush == NULL)
|
||||
return 0;
|
||||
return hapd->driver->flush(hapd->drv_priv);
|
||||
}
|
||||
|
||||
|
||||
int hostapd_set_freq(struct hostapd_data *hapd, enum hostapd_hw_mode mode,
|
||||
int freq, int channel, int ht_enabled, int vht_enabled,
|
||||
int he_enabled,
|
||||
int sec_channel_offset, int oper_chwidth,
|
||||
int center_segment0, int center_segment1)
|
||||
{
|
||||
struct hostapd_freq_params data;
|
||||
struct hostapd_hw_modes *cmode = hapd->iface->current_mode;
|
||||
|
||||
if (hostapd_set_freq_params(&data, mode, freq, channel, ht_enabled,
|
||||
vht_enabled, he_enabled, sec_channel_offset,
|
||||
oper_chwidth,
|
||||
center_segment0, center_segment1,
|
||||
cmode ? cmode->vht_capab : 0,
|
||||
cmode ?
|
||||
&cmode->he_capab[IEEE80211_MODE_AP] : NULL))
|
||||
return -1;
|
||||
|
||||
if (hapd->driver == NULL)
|
||||
return 0;
|
||||
if (hapd->driver->set_freq == NULL)
|
||||
return 0;
|
||||
return hapd->driver->set_freq(hapd->drv_priv, &data);
|
||||
}
|
||||
|
||||
int hostapd_set_rts(struct hostapd_data *hapd, int rts)
|
||||
{
|
||||
if (hapd->driver == NULL || hapd->driver->set_rts == NULL)
|
||||
return 0;
|
||||
return hapd->driver->set_rts(hapd->drv_priv, rts);
|
||||
}
|
||||
|
||||
|
||||
int hostapd_set_frag(struct hostapd_data *hapd, int frag)
|
||||
{
|
||||
if (hapd->driver == NULL || hapd->driver->set_frag == NULL)
|
||||
return 0;
|
||||
return hapd->driver->set_frag(hapd->drv_priv, frag);
|
||||
}
|
||||
|
||||
|
||||
int hostapd_sta_set_flags(struct hostapd_data *hapd, u8 *addr,
|
||||
int total_flags, int flags_or, int flags_and)
|
||||
{
|
||||
if (hapd->driver == NULL || hapd->driver->sta_set_flags == NULL)
|
||||
return 0;
|
||||
return hapd->driver->sta_set_flags(hapd->drv_priv, addr, total_flags,
|
||||
flags_or, flags_and);
|
||||
}
|
||||
|
||||
|
||||
int hostapd_sta_set_airtime_weight(struct hostapd_data *hapd, const u8 *addr,
|
||||
unsigned int weight)
|
||||
{
|
||||
if (!hapd->driver || !hapd->driver->sta_set_airtime_weight)
|
||||
return 0;
|
||||
return hapd->driver->sta_set_airtime_weight(hapd->drv_priv, addr,
|
||||
weight);
|
||||
}
|
||||
|
||||
|
||||
int hostapd_set_country(struct hostapd_data *hapd, const char *country)
|
||||
{
|
||||
if (hapd->driver == NULL ||
|
||||
hapd->driver->set_country == NULL)
|
||||
return 0;
|
||||
return hapd->driver->set_country(hapd->drv_priv, country);
|
||||
}
|
||||
|
||||
|
||||
int hostapd_set_tx_queue_params(struct hostapd_data *hapd, int queue, int aifs,
|
||||
int cw_min, int cw_max, int burst_time)
|
||||
{
|
||||
if (hapd->driver == NULL || hapd->driver->set_tx_queue_params == NULL)
|
||||
return 0;
|
||||
return hapd->driver->set_tx_queue_params(hapd->drv_priv, queue, aifs,
|
||||
cw_min, cw_max, burst_time);
|
||||
}
|
||||
|
||||
|
||||
struct hostapd_hw_modes *
|
||||
hostapd_get_hw_feature_data(struct hostapd_data *hapd, u16 *num_modes,
|
||||
u16 *flags, u8 *dfs_domain)
|
||||
{
|
||||
if (hapd->driver == NULL ||
|
||||
hapd->driver->get_hw_feature_data == NULL)
|
||||
return NULL;
|
||||
return hapd->driver->get_hw_feature_data(hapd->drv_priv, num_modes,
|
||||
flags, dfs_domain);
|
||||
}
|
||||
|
||||
|
||||
int hostapd_driver_commit(struct hostapd_data *hapd)
|
||||
{
|
||||
if (hapd->driver == NULL || hapd->driver->commit == NULL)
|
||||
return 0;
|
||||
return hapd->driver->commit(hapd->drv_priv);
|
||||
}
|
||||
|
||||
|
||||
int hostapd_drv_none(struct hostapd_data *hapd)
|
||||
{
|
||||
return hapd->driver && os_strcmp(hapd->driver->name, "none") == 0;
|
||||
}
|
||||
|
||||
|
||||
int hostapd_driver_scan(struct hostapd_data *hapd,
|
||||
struct wpa_driver_scan_params *params)
|
||||
{
|
||||
if (hapd->driver && hapd->driver->scan2)
|
||||
return hapd->driver->scan2(hapd->drv_priv, params);
|
||||
return -1;
|
||||
}
|
||||
|
||||
|
||||
struct wpa_scan_results * hostapd_driver_get_scan_results(
|
||||
struct hostapd_data *hapd)
|
||||
{
|
||||
if (hapd->driver && hapd->driver->get_scan_results2)
|
||||
return hapd->driver->get_scan_results2(hapd->drv_priv);
|
||||
return NULL;
|
||||
}
|
||||
|
||||
|
||||
int hostapd_driver_set_noa(struct hostapd_data *hapd, u8 count, int start,
|
||||
int duration)
|
||||
{
|
||||
if (hapd->driver && hapd->driver->set_noa)
|
||||
return hapd->driver->set_noa(hapd->drv_priv, count, start,
|
||||
duration);
|
||||
return -1;
|
||||
}
|
||||
|
||||
|
||||
int hostapd_drv_set_key(const char *ifname, struct hostapd_data *hapd,
|
||||
enum wpa_alg alg, const u8 *addr,
|
||||
int key_idx, int set_tx,
|
||||
const u8 *seq, size_t seq_len,
|
||||
const u8 *key, size_t key_len)
|
||||
{
|
||||
if (hapd->driver == NULL || hapd->driver->set_key == NULL)
|
||||
return 0;
|
||||
return hapd->driver->set_key(ifname, hapd->drv_priv, alg, addr,
|
||||
key_idx, set_tx, seq, seq_len, key,
|
||||
key_len);
|
||||
}
|
||||
|
||||
|
||||
int hostapd_drv_send_mlme(struct hostapd_data *hapd,
|
||||
const void *msg, size_t len, int noack)
|
||||
{
|
||||
if (!hapd->driver || !hapd->driver->send_mlme || !hapd->drv_priv)
|
||||
return 0;
|
||||
return hapd->driver->send_mlme(hapd->drv_priv, msg, len, noack, 0,
|
||||
NULL, 0);
|
||||
}
|
||||
|
||||
|
||||
int hostapd_drv_send_mlme_csa(struct hostapd_data *hapd,
|
||||
const void *msg, size_t len, int noack,
|
||||
const u16 *csa_offs, size_t csa_offs_len)
|
||||
{
|
||||
if (hapd->driver == NULL || hapd->driver->send_mlme == NULL)
|
||||
return 0;
|
||||
return hapd->driver->send_mlme(hapd->drv_priv, msg, len, noack, 0,
|
||||
csa_offs, csa_offs_len);
|
||||
}
|
||||
|
||||
|
||||
int hostapd_drv_sta_deauth(struct hostapd_data *hapd,
|
||||
const u8 *addr, int reason)
|
||||
{
|
||||
if (!hapd->driver || !hapd->driver->sta_deauth || !hapd->drv_priv)
|
||||
return 0;
|
||||
return hapd->driver->sta_deauth(hapd->drv_priv, hapd->own_addr, addr,
|
||||
reason);
|
||||
}
|
||||
|
||||
|
||||
int hostapd_drv_sta_disassoc(struct hostapd_data *hapd,
|
||||
const u8 *addr, int reason)
|
||||
{
|
||||
if (!hapd->driver || !hapd->driver->sta_disassoc || !hapd->drv_priv)
|
||||
return 0;
|
||||
return hapd->driver->sta_disassoc(hapd->drv_priv, hapd->own_addr, addr,
|
||||
reason);
|
||||
}
|
||||
|
||||
|
||||
int hostapd_drv_wnm_oper(struct hostapd_data *hapd, enum wnm_oper oper,
|
||||
const u8 *peer, u8 *buf, u16 *buf_len)
|
||||
{
|
||||
if (hapd->driver == NULL || hapd->driver->wnm_oper == NULL)
|
||||
return -1;
|
||||
return hapd->driver->wnm_oper(hapd->drv_priv, oper, peer, buf,
|
||||
buf_len);
|
||||
}
|
||||
|
||||
|
||||
int hostapd_drv_send_action(struct hostapd_data *hapd, unsigned int freq,
|
||||
unsigned int wait, const u8 *dst, const u8 *data,
|
||||
size_t len)
|
||||
{
|
||||
const u8 *bssid;
|
||||
const u8 wildcard_bssid[ETH_ALEN] = {
|
||||
0xff, 0xff, 0xff, 0xff, 0xff, 0xff
|
||||
};
|
||||
|
||||
if (!hapd->driver || !hapd->driver->send_action || !hapd->drv_priv)
|
||||
return 0;
|
||||
bssid = hapd->own_addr;
|
||||
if (!is_multicast_ether_addr(dst) &&
|
||||
len > 0 && data[0] == WLAN_ACTION_PUBLIC) {
|
||||
struct sta_info *sta;
|
||||
|
||||
/*
|
||||
* Public Action frames to a STA that is not a member of the BSS
|
||||
* shall use wildcard BSSID value.
|
||||
*/
|
||||
sta = ap_get_sta(hapd, dst);
|
||||
if (!sta || !(sta->flags & WLAN_STA_ASSOC))
|
||||
bssid = wildcard_bssid;
|
||||
} else if (is_broadcast_ether_addr(dst) &&
|
||||
len > 0 && data[0] == WLAN_ACTION_PUBLIC) {
|
||||
/*
|
||||
* The only current use case of Public Action frames with
|
||||
* broadcast destination address is DPP PKEX. That case is
|
||||
* directing all devices and not just the STAs within the BSS,
|
||||
* so have to use the wildcard BSSID value.
|
||||
*/
|
||||
bssid = wildcard_bssid;
|
||||
}
|
||||
return hapd->driver->send_action(hapd->drv_priv, freq, wait, dst,
|
||||
hapd->own_addr, bssid, data, len, 0);
|
||||
}
|
||||
|
||||
|
||||
int hostapd_drv_send_action_addr3_ap(struct hostapd_data *hapd,
|
||||
unsigned int freq,
|
||||
unsigned int wait, const u8 *dst,
|
||||
const u8 *data, size_t len)
|
||||
{
|
||||
if (hapd->driver == NULL || hapd->driver->send_action == NULL)
|
||||
return 0;
|
||||
return hapd->driver->send_action(hapd->drv_priv, freq, wait, dst,
|
||||
hapd->own_addr, hapd->own_addr, data,
|
||||
len, 0);
|
||||
}
|
||||
|
||||
|
||||
int hostapd_start_dfs_cac(struct hostapd_iface *iface,
|
||||
enum hostapd_hw_mode mode, int freq,
|
||||
int channel, int ht_enabled, int vht_enabled,
|
||||
int he_enabled,
|
||||
int sec_channel_offset, int oper_chwidth,
|
||||
int center_segment0, int center_segment1)
|
||||
{
|
||||
struct hostapd_data *hapd = iface->bss[0];
|
||||
struct hostapd_freq_params data;
|
||||
int res;
|
||||
struct hostapd_hw_modes *cmode = iface->current_mode;
|
||||
|
||||
if (!hapd->driver || !hapd->driver->start_dfs_cac || !cmode)
|
||||
return 0;
|
||||
|
||||
if (!iface->conf->ieee80211h) {
|
||||
wpa_printf(MSG_ERROR, "Can't start DFS CAC, DFS functionality "
|
||||
"is not enabled");
|
||||
return -1;
|
||||
}
|
||||
|
||||
if (hostapd_set_freq_params(&data, mode, freq, channel, ht_enabled,
|
||||
vht_enabled, he_enabled, sec_channel_offset,
|
||||
oper_chwidth, center_segment0,
|
||||
center_segment1,
|
||||
cmode->vht_capab,
|
||||
&cmode->he_capab[IEEE80211_MODE_AP])) {
|
||||
wpa_printf(MSG_ERROR, "Can't set freq params");
|
||||
return -1;
|
||||
}
|
||||
|
||||
res = hapd->driver->start_dfs_cac(hapd->drv_priv, &data);
|
||||
if (!res) {
|
||||
iface->cac_started = 1;
|
||||
os_get_reltime(&iface->dfs_cac_start);
|
||||
}
|
||||
|
||||
return res;
|
||||
}
|
||||
|
||||
|
||||
int hostapd_drv_set_qos_map(struct hostapd_data *hapd,
|
||||
const u8 *qos_map_set, u8 qos_map_set_len)
|
||||
{
|
||||
if (!hapd->driver || !hapd->driver->set_qos_map || !hapd->drv_priv)
|
||||
return 0;
|
||||
return hapd->driver->set_qos_map(hapd->drv_priv, qos_map_set,
|
||||
qos_map_set_len);
|
||||
}
|
||||
|
||||
|
||||
static void hostapd_get_hw_mode_any_channels(struct hostapd_data *hapd,
|
||||
struct hostapd_hw_modes *mode,
|
||||
int acs_ch_list_all,
|
||||
int **freq_list)
|
||||
{
|
||||
int i;
|
||||
|
||||
for (i = 0; i < mode->num_channels; i++) {
|
||||
struct hostapd_channel_data *chan = &mode->channels[i];
|
||||
|
||||
if ((acs_ch_list_all ||
|
||||
freq_range_list_includes(&hapd->iface->conf->acs_ch_list,
|
||||
chan->chan)) &&
|
||||
!(chan->flag & HOSTAPD_CHAN_DISABLED) &&
|
||||
!(hapd->iface->conf->acs_exclude_dfs &&
|
||||
(chan->flag & HOSTAPD_CHAN_RADAR)))
|
||||
int_array_add_unique(freq_list, chan->freq);
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
void hostapd_get_ext_capa(struct hostapd_iface *iface)
|
||||
{
|
||||
struct hostapd_data *hapd = iface->bss[0];
|
||||
|
||||
if (!hapd->driver || !hapd->driver->get_ext_capab)
|
||||
return;
|
||||
|
||||
hapd->driver->get_ext_capab(hapd->drv_priv, WPA_IF_AP_BSS,
|
||||
&iface->extended_capa,
|
||||
&iface->extended_capa_mask,
|
||||
&iface->extended_capa_len);
|
||||
}
|
||||
|
||||
|
||||
int hostapd_drv_do_acs(struct hostapd_data *hapd)
|
||||
{
|
||||
struct drv_acs_params params;
|
||||
int ret, i, acs_ch_list_all = 0;
|
||||
u8 *channels = NULL;
|
||||
unsigned int num_channels = 0;
|
||||
struct hostapd_hw_modes *mode;
|
||||
int *freq_list = NULL;
|
||||
|
||||
if (hapd->driver == NULL || hapd->driver->do_acs == NULL)
|
||||
return 0;
|
||||
|
||||
os_memset(¶ms, 0, sizeof(params));
|
||||
params.hw_mode = hapd->iface->conf->hw_mode;
|
||||
|
||||
/*
|
||||
* If no chanlist config parameter is provided, include all enabled
|
||||
* channels of the selected hw_mode.
|
||||
*/
|
||||
if (!hapd->iface->conf->acs_ch_list.num)
|
||||
acs_ch_list_all = 1;
|
||||
|
||||
mode = hapd->iface->current_mode;
|
||||
if (mode) {
|
||||
channels = os_malloc(mode->num_channels);
|
||||
if (channels == NULL)
|
||||
return -1;
|
||||
|
||||
for (i = 0; i < mode->num_channels; i++) {
|
||||
struct hostapd_channel_data *chan = &mode->channels[i];
|
||||
if (!acs_ch_list_all &&
|
||||
!freq_range_list_includes(
|
||||
&hapd->iface->conf->acs_ch_list,
|
||||
chan->chan))
|
||||
continue;
|
||||
if (hapd->iface->conf->acs_exclude_dfs &&
|
||||
(chan->flag & HOSTAPD_CHAN_RADAR))
|
||||
continue;
|
||||
if (!(chan->flag & HOSTAPD_CHAN_DISABLED)) {
|
||||
channels[num_channels++] = chan->chan;
|
||||
int_array_add_unique(&freq_list, chan->freq);
|
||||
}
|
||||
}
|
||||
} else {
|
||||
for (i = 0; i < hapd->iface->num_hw_features; i++) {
|
||||
mode = &hapd->iface->hw_features[i];
|
||||
hostapd_get_hw_mode_any_channels(hapd, mode,
|
||||
acs_ch_list_all,
|
||||
&freq_list);
|
||||
}
|
||||
}
|
||||
|
||||
params.ch_list = channels;
|
||||
params.ch_list_len = num_channels;
|
||||
params.freq_list = freq_list;
|
||||
|
||||
params.ht_enabled = !!(hapd->iface->conf->ieee80211n);
|
||||
params.ht40_enabled = !!(hapd->iface->conf->ht_capab &
|
||||
HT_CAP_INFO_SUPP_CHANNEL_WIDTH_SET);
|
||||
params.vht_enabled = !!(hapd->iface->conf->ieee80211ac);
|
||||
params.ch_width = 20;
|
||||
if (hapd->iface->conf->ieee80211n && params.ht40_enabled)
|
||||
params.ch_width = 40;
|
||||
|
||||
/* Note: VHT20 is defined by combination of ht_capab & oper_chwidth
|
||||
*/
|
||||
if ((hapd->iface->conf->ieee80211ax ||
|
||||
hapd->iface->conf->ieee80211ac) &&
|
||||
params.ht40_enabled) {
|
||||
u8 oper_chwidth = hostapd_get_oper_chwidth(hapd->iface->conf);
|
||||
|
||||
if (oper_chwidth == CHANWIDTH_80MHZ)
|
||||
params.ch_width = 80;
|
||||
else if (oper_chwidth == CHANWIDTH_160MHZ ||
|
||||
oper_chwidth == CHANWIDTH_80P80MHZ)
|
||||
params.ch_width = 160;
|
||||
}
|
||||
|
||||
ret = hapd->driver->do_acs(hapd->drv_priv, ¶ms);
|
||||
os_free(channels);
|
||||
|
||||
return ret;
|
||||
}
|
||||
|
||||
|
||||
int hostapd_drv_update_dh_ie(struct hostapd_data *hapd, const u8 *peer,
|
||||
u16 reason_code, const u8 *ie, size_t ielen)
|
||||
{
|
||||
if (!hapd->driver || !hapd->driver->update_dh_ie || !hapd->drv_priv)
|
||||
return 0;
|
||||
return hapd->driver->update_dh_ie(hapd->drv_priv, peer, reason_code,
|
||||
ie, ielen);
|
||||
}
|
|
@ -0,0 +1,384 @@
|
|||
/*
|
||||
* hostapd - Driver operations
|
||||
* Copyright (c) 2009-2014, Jouni Malinen <j@w1.fi>
|
||||
*
|
||||
* This software may be distributed under the terms of the BSD license.
|
||||
* See README for more details.
|
||||
*/
|
||||
|
||||
#ifndef AP_DRV_OPS
|
||||
#define AP_DRV_OPS
|
||||
|
||||
enum wpa_driver_if_type;
|
||||
struct wpa_bss_params;
|
||||
struct wpa_driver_scan_params;
|
||||
struct ieee80211_ht_capabilities;
|
||||
struct ieee80211_vht_capabilities;
|
||||
struct hostapd_freq_params;
|
||||
|
||||
u32 hostapd_sta_flags_to_drv(u32 flags);
|
||||
int hostapd_build_ap_extra_ies(struct hostapd_data *hapd,
|
||||
struct wpabuf **beacon,
|
||||
struct wpabuf **proberesp,
|
||||
struct wpabuf **assocresp);
|
||||
void hostapd_free_ap_extra_ies(struct hostapd_data *hapd, struct wpabuf *beacon,
|
||||
struct wpabuf *proberesp,
|
||||
struct wpabuf *assocresp);
|
||||
int hostapd_reset_ap_wps_ie(struct hostapd_data *hapd);
|
||||
int hostapd_set_ap_wps_ie(struct hostapd_data *hapd);
|
||||
int hostapd_set_authorized(struct hostapd_data *hapd,
|
||||
struct sta_info *sta, int authorized);
|
||||
int hostapd_set_sta_flags(struct hostapd_data *hapd, struct sta_info *sta);
|
||||
int hostapd_set_drv_ieee8021x(struct hostapd_data *hapd, const char *ifname,
|
||||
int enabled);
|
||||
int hostapd_vlan_if_add(struct hostapd_data *hapd, const char *ifname);
|
||||
int hostapd_vlan_if_remove(struct hostapd_data *hapd, const char *ifname);
|
||||
int hostapd_set_wds_sta(struct hostapd_data *hapd, char *ifname_wds,
|
||||
const u8 *addr, int aid, int val);
|
||||
int hostapd_sta_add(struct hostapd_data *hapd,
|
||||
const u8 *addr, u16 aid, u16 capability,
|
||||
const u8 *supp_rates, size_t supp_rates_len,
|
||||
u16 listen_interval,
|
||||
const struct ieee80211_ht_capabilities *ht_capab,
|
||||
const struct ieee80211_vht_capabilities *vht_capab,
|
||||
const struct ieee80211_he_capabilities *he_capab,
|
||||
size_t he_capab_len,
|
||||
u32 flags, u8 qosinfo, u8 vht_opmode, int supp_p2p_ps,
|
||||
int set);
|
||||
int hostapd_set_privacy(struct hostapd_data *hapd, int enabled);
|
||||
int hostapd_set_generic_elem(struct hostapd_data *hapd, const u8 *elem,
|
||||
size_t elem_len);
|
||||
int hostapd_get_ssid(struct hostapd_data *hapd, u8 *buf, size_t len);
|
||||
int hostapd_set_ssid(struct hostapd_data *hapd, const u8 *buf, size_t len);
|
||||
int hostapd_if_add(struct hostapd_data *hapd, enum wpa_driver_if_type type,
|
||||
const char *ifname, const u8 *addr, void *bss_ctx,
|
||||
void **drv_priv, char *force_ifname, u8 *if_addr,
|
||||
const char *bridge, int use_existing);
|
||||
int hostapd_if_remove(struct hostapd_data *hapd, enum wpa_driver_if_type type,
|
||||
const char *ifname);
|
||||
int hostapd_set_ieee8021x(struct hostapd_data *hapd,
|
||||
struct wpa_bss_params *params);
|
||||
int hostapd_get_seqnum(const char *ifname, struct hostapd_data *hapd,
|
||||
const u8 *addr, int idx, u8 *seq);
|
||||
int hostapd_flush(struct hostapd_data *hapd);
|
||||
int hostapd_set_freq(struct hostapd_data *hapd, enum hostapd_hw_mode mode,
|
||||
int freq, int channel, int ht_enabled, int vht_enabled,
|
||||
int he_enabled, int sec_channel_offset, int oper_chwidth,
|
||||
int center_segment0, int center_segment1);
|
||||
int hostapd_set_rts(struct hostapd_data *hapd, int rts);
|
||||
int hostapd_set_frag(struct hostapd_data *hapd, int frag);
|
||||
int hostapd_sta_set_flags(struct hostapd_data *hapd, u8 *addr,
|
||||
int total_flags, int flags_or, int flags_and);
|
||||
int hostapd_sta_set_airtime_weight(struct hostapd_data *hapd, const u8 *addr,
|
||||
unsigned int weight);
|
||||
int hostapd_set_country(struct hostapd_data *hapd, const char *country);
|
||||
int hostapd_set_tx_queue_params(struct hostapd_data *hapd, int queue, int aifs,
|
||||
int cw_min, int cw_max, int burst_time);
|
||||
struct hostapd_hw_modes *
|
||||
hostapd_get_hw_feature_data(struct hostapd_data *hapd, u16 *num_modes,
|
||||
u16 *flags, u8 *dfs_domain);
|
||||
int hostapd_driver_commit(struct hostapd_data *hapd);
|
||||
int hostapd_drv_none(struct hostapd_data *hapd);
|
||||
int hostapd_driver_scan(struct hostapd_data *hapd,
|
||||
struct wpa_driver_scan_params *params);
|
||||
struct wpa_scan_results * hostapd_driver_get_scan_results(
|
||||
struct hostapd_data *hapd);
|
||||
int hostapd_driver_set_noa(struct hostapd_data *hapd, u8 count, int start,
|
||||
int duration);
|
||||
int hostapd_drv_set_key(const char *ifname,
|
||||
struct hostapd_data *hapd,
|
||||
enum wpa_alg alg, const u8 *addr,
|
||||
int key_idx, int set_tx,
|
||||
const u8 *seq, size_t seq_len,
|
||||
const u8 *key, size_t key_len);
|
||||
int hostapd_drv_send_mlme(struct hostapd_data *hapd,
|
||||
const void *msg, size_t len, int noack);
|
||||
int hostapd_drv_send_mlme_csa(struct hostapd_data *hapd,
|
||||
const void *msg, size_t len, int noack,
|
||||
const u16 *csa_offs, size_t csa_offs_len);
|
||||
int hostapd_drv_sta_deauth(struct hostapd_data *hapd,
|
||||
const u8 *addr, int reason);
|
||||
int hostapd_drv_sta_disassoc(struct hostapd_data *hapd,
|
||||
const u8 *addr, int reason);
|
||||
int hostapd_drv_send_action(struct hostapd_data *hapd, unsigned int freq,
|
||||
unsigned int wait, const u8 *dst, const u8 *data,
|
||||
size_t len);
|
||||
int hostapd_drv_send_action_addr3_ap(struct hostapd_data *hapd,
|
||||
unsigned int freq,
|
||||
unsigned int wait, const u8 *dst,
|
||||
const u8 *data, size_t len);
|
||||
static inline void
|
||||
hostapd_drv_send_action_cancel_wait(struct hostapd_data *hapd)
|
||||
{
|
||||
if (!hapd->driver || !hapd->driver->send_action_cancel_wait ||
|
||||
!hapd->drv_priv)
|
||||
return;
|
||||
hapd->driver->send_action_cancel_wait(hapd->drv_priv);
|
||||
}
|
||||
int hostapd_add_sta_node(struct hostapd_data *hapd, const u8 *addr,
|
||||
u16 auth_alg);
|
||||
int hostapd_sta_auth(struct hostapd_data *hapd, const u8 *addr,
|
||||
u16 seq, u16 status, const u8 *ie, size_t len);
|
||||
int hostapd_sta_assoc(struct hostapd_data *hapd, const u8 *addr,
|
||||
int reassoc, u16 status, const u8 *ie, size_t len);
|
||||
int hostapd_add_tspec(struct hostapd_data *hapd, const u8 *addr,
|
||||
u8 *tspec_ie, size_t tspec_ielen);
|
||||
int hostapd_start_dfs_cac(struct hostapd_iface *iface,
|
||||
enum hostapd_hw_mode mode, int freq,
|
||||
int channel, int ht_enabled, int vht_enabled,
|
||||
int he_enabled,
|
||||
int sec_channel_offset, int oper_chwidth,
|
||||
int center_segment0, int center_segment1);
|
||||
int hostapd_drv_do_acs(struct hostapd_data *hapd);
|
||||
int hostapd_drv_update_dh_ie(struct hostapd_data *hapd, const u8 *peer,
|
||||
u16 reason_code, const u8 *ie, size_t ielen);
|
||||
|
||||
|
||||
#include "drivers/driver.h"
|
||||
|
||||
int hostapd_drv_wnm_oper(struct hostapd_data *hapd,
|
||||
enum wnm_oper oper, const u8 *peer,
|
||||
u8 *buf, u16 *buf_len);
|
||||
|
||||
int hostapd_drv_set_qos_map(struct hostapd_data *hapd, const u8 *qos_map_set,
|
||||
u8 qos_map_set_len);
|
||||
|
||||
void hostapd_get_ext_capa(struct hostapd_iface *iface);
|
||||
|
||||
static inline int hostapd_drv_set_countermeasures(struct hostapd_data *hapd,
|
||||
int enabled)
|
||||
{
|
||||
if (hapd->driver == NULL ||
|
||||
hapd->driver->hapd_set_countermeasures == NULL)
|
||||
return 0;
|
||||
return hapd->driver->hapd_set_countermeasures(hapd->drv_priv, enabled);
|
||||
}
|
||||
|
||||
static inline int hostapd_drv_set_sta_vlan(const char *ifname,
|
||||
struct hostapd_data *hapd,
|
||||
const u8 *addr, int vlan_id)
|
||||
{
|
||||
if (hapd->driver == NULL || hapd->driver->set_sta_vlan == NULL)
|
||||
return 0;
|
||||
return hapd->driver->set_sta_vlan(hapd->drv_priv, addr, ifname,
|
||||
vlan_id);
|
||||
}
|
||||
|
||||
static inline int hostapd_drv_get_inact_sec(struct hostapd_data *hapd,
|
||||
const u8 *addr)
|
||||
{
|
||||
if (hapd->driver == NULL || hapd->driver->get_inact_sec == NULL)
|
||||
return 0;
|
||||
return hapd->driver->get_inact_sec(hapd->drv_priv, addr);
|
||||
}
|
||||
|
||||
static inline int hostapd_drv_sta_remove(struct hostapd_data *hapd,
|
||||
const u8 *addr)
|
||||
{
|
||||
if (!hapd->driver || !hapd->driver->sta_remove || !hapd->drv_priv)
|
||||
return 0;
|
||||
return hapd->driver->sta_remove(hapd->drv_priv, addr);
|
||||
}
|
||||
|
||||
static inline int hostapd_drv_hapd_send_eapol(struct hostapd_data *hapd,
|
||||
const u8 *addr, const u8 *data,
|
||||
size_t data_len, int encrypt,
|
||||
u32 flags)
|
||||
{
|
||||
if (hapd->driver == NULL || hapd->driver->hapd_send_eapol == NULL)
|
||||
return 0;
|
||||
return hapd->driver->hapd_send_eapol(hapd->drv_priv, addr, data,
|
||||
data_len, encrypt,
|
||||
hapd->own_addr, flags);
|
||||
}
|
||||
|
||||
static inline int hostapd_drv_read_sta_data(
|
||||
struct hostapd_data *hapd, struct hostap_sta_driver_data *data,
|
||||
const u8 *addr)
|
||||
{
|
||||
if (hapd->driver == NULL || hapd->driver->read_sta_data == NULL)
|
||||
return -1;
|
||||
return hapd->driver->read_sta_data(hapd->drv_priv, data, addr);
|
||||
}
|
||||
|
||||
static inline int hostapd_drv_sta_clear_stats(struct hostapd_data *hapd,
|
||||
const u8 *addr)
|
||||
{
|
||||
if (hapd->driver == NULL || hapd->driver->sta_clear_stats == NULL)
|
||||
return 0;
|
||||
return hapd->driver->sta_clear_stats(hapd->drv_priv, addr);
|
||||
}
|
||||
|
||||
static inline int hostapd_drv_set_acl(struct hostapd_data *hapd,
|
||||
struct hostapd_acl_params *params)
|
||||
{
|
||||
if (hapd->driver == NULL || hapd->driver->set_acl == NULL)
|
||||
return 0;
|
||||
return hapd->driver->set_acl(hapd->drv_priv, params);
|
||||
}
|
||||
|
||||
static inline int hostapd_drv_set_ap(struct hostapd_data *hapd,
|
||||
struct wpa_driver_ap_params *params)
|
||||
{
|
||||
if (hapd->driver == NULL || hapd->driver->set_ap == NULL)
|
||||
return 0;
|
||||
return hapd->driver->set_ap(hapd->drv_priv, params);
|
||||
}
|
||||
|
||||
static inline int hostapd_drv_set_radius_acl_auth(struct hostapd_data *hapd,
|
||||
const u8 *mac, int accepted,
|
||||
u32 session_timeout)
|
||||
{
|
||||
if (hapd->driver == NULL || hapd->driver->set_radius_acl_auth == NULL)
|
||||
return 0;
|
||||
return hapd->driver->set_radius_acl_auth(hapd->drv_priv, mac, accepted,
|
||||
session_timeout);
|
||||
}
|
||||
|
||||
static inline int hostapd_drv_set_radius_acl_expire(struct hostapd_data *hapd,
|
||||
const u8 *mac)
|
||||
{
|
||||
if (hapd->driver == NULL ||
|
||||
hapd->driver->set_radius_acl_expire == NULL)
|
||||
return 0;
|
||||
return hapd->driver->set_radius_acl_expire(hapd->drv_priv, mac);
|
||||
}
|
||||
|
||||
static inline int hostapd_drv_set_authmode(struct hostapd_data *hapd,
|
||||
int auth_algs)
|
||||
{
|
||||
if (hapd->driver == NULL || hapd->driver->set_authmode == NULL)
|
||||
return 0;
|
||||
return hapd->driver->set_authmode(hapd->drv_priv, auth_algs);
|
||||
}
|
||||
|
||||
static inline void hostapd_drv_poll_client(struct hostapd_data *hapd,
|
||||
const u8 *own_addr, const u8 *addr,
|
||||
int qos)
|
||||
{
|
||||
if (hapd->driver == NULL || hapd->driver->poll_client == NULL)
|
||||
return;
|
||||
hapd->driver->poll_client(hapd->drv_priv, own_addr, addr, qos);
|
||||
}
|
||||
|
||||
static inline int hostapd_drv_get_survey(struct hostapd_data *hapd,
|
||||
unsigned int freq)
|
||||
{
|
||||
if (hapd->driver == NULL)
|
||||
return -1;
|
||||
if (!hapd->driver->get_survey)
|
||||
return -1;
|
||||
return hapd->driver->get_survey(hapd->drv_priv, freq);
|
||||
}
|
||||
|
||||
static inline int hostapd_get_country(struct hostapd_data *hapd, char *alpha2)
|
||||
{
|
||||
if (hapd->driver == NULL || hapd->driver->get_country == NULL)
|
||||
return -1;
|
||||
return hapd->driver->get_country(hapd->drv_priv, alpha2);
|
||||
}
|
||||
|
||||
static inline const char * hostapd_drv_get_radio_name(struct hostapd_data *hapd)
|
||||
{
|
||||
if (hapd->driver == NULL || hapd->drv_priv == NULL ||
|
||||
hapd->driver->get_radio_name == NULL)
|
||||
return NULL;
|
||||
return hapd->driver->get_radio_name(hapd->drv_priv);
|
||||
}
|
||||
|
||||
static inline int hostapd_drv_switch_channel(struct hostapd_data *hapd,
|
||||
struct csa_settings *settings)
|
||||
{
|
||||
if (hapd->driver == NULL || hapd->driver->switch_channel == NULL ||
|
||||
hapd->drv_priv == NULL)
|
||||
return -1;
|
||||
|
||||
return hapd->driver->switch_channel(hapd->drv_priv, settings);
|
||||
}
|
||||
|
||||
static inline int hostapd_drv_status(struct hostapd_data *hapd, char *buf,
|
||||
size_t buflen)
|
||||
{
|
||||
if (!hapd->driver || !hapd->driver->status || !hapd->drv_priv)
|
||||
return -1;
|
||||
return hapd->driver->status(hapd->drv_priv, buf, buflen);
|
||||
}
|
||||
|
||||
static inline int hostapd_drv_br_add_ip_neigh(struct hostapd_data *hapd,
|
||||
int version, const u8 *ipaddr,
|
||||
int prefixlen, const u8 *addr)
|
||||
{
|
||||
if (hapd->driver == NULL || hapd->drv_priv == NULL ||
|
||||
hapd->driver->br_add_ip_neigh == NULL)
|
||||
return -1;
|
||||
return hapd->driver->br_add_ip_neigh(hapd->drv_priv, version, ipaddr,
|
||||
prefixlen, addr);
|
||||
}
|
||||
|
||||
static inline int hostapd_drv_br_delete_ip_neigh(struct hostapd_data *hapd,
|
||||
u8 version, const u8 *ipaddr)
|
||||
{
|
||||
if (hapd->driver == NULL || hapd->drv_priv == NULL ||
|
||||
hapd->driver->br_delete_ip_neigh == NULL)
|
||||
return -1;
|
||||
return hapd->driver->br_delete_ip_neigh(hapd->drv_priv, version,
|
||||
ipaddr);
|
||||
}
|
||||
|
||||
static inline int hostapd_drv_br_port_set_attr(struct hostapd_data *hapd,
|
||||
enum drv_br_port_attr attr,
|
||||
unsigned int val)
|
||||
{
|
||||
if (hapd->driver == NULL || hapd->drv_priv == NULL ||
|
||||
hapd->driver->br_port_set_attr == NULL)
|
||||
return -1;
|
||||
return hapd->driver->br_port_set_attr(hapd->drv_priv, attr, val);
|
||||
}
|
||||
|
||||
static inline int hostapd_drv_br_set_net_param(struct hostapd_data *hapd,
|
||||
enum drv_br_net_param param,
|
||||
unsigned int val)
|
||||
{
|
||||
if (hapd->driver == NULL || hapd->drv_priv == NULL ||
|
||||
hapd->driver->br_set_net_param == NULL)
|
||||
return -1;
|
||||
return hapd->driver->br_set_net_param(hapd->drv_priv, param, val);
|
||||
}
|
||||
|
||||
static inline int hostapd_drv_vendor_cmd(struct hostapd_data *hapd,
|
||||
int vendor_id, int subcmd,
|
||||
const u8 *data, size_t data_len,
|
||||
struct wpabuf *buf)
|
||||
{
|
||||
if (hapd->driver == NULL || hapd->driver->vendor_cmd == NULL)
|
||||
return -1;
|
||||
return hapd->driver->vendor_cmd(hapd->drv_priv, vendor_id, subcmd, data,
|
||||
data_len, buf);
|
||||
}
|
||||
|
||||
static inline int hostapd_drv_stop_ap(struct hostapd_data *hapd)
|
||||
{
|
||||
if (!hapd->driver || !hapd->driver->stop_ap || !hapd->drv_priv)
|
||||
return 0;
|
||||
return hapd->driver->stop_ap(hapd->drv_priv);
|
||||
}
|
||||
|
||||
static inline int hostapd_drv_channel_info(struct hostapd_data *hapd,
|
||||
struct wpa_channel_info *ci)
|
||||
{
|
||||
if (!hapd->driver || !hapd->driver->channel_info)
|
||||
return -1;
|
||||
return hapd->driver->channel_info(hapd->drv_priv, ci);
|
||||
}
|
||||
|
||||
static inline int
|
||||
hostapd_drv_send_external_auth_status(struct hostapd_data *hapd,
|
||||
struct external_auth *params)
|
||||
{
|
||||
if (!hapd->driver || !hapd->drv_priv ||
|
||||
!hapd->driver->send_external_auth_status)
|
||||
return -1;
|
||||
return hapd->driver->send_external_auth_status(hapd->drv_priv, params);
|
||||
}
|
||||
|
||||
#endif /* AP_DRV_OPS */
|
|
@ -0,0 +1,312 @@
|
|||
/*
|
||||
* hostapd / AP table
|
||||
* Copyright (c) 2002-2009, Jouni Malinen <j@w1.fi>
|
||||
* Copyright (c) 2003-2004, Instant802 Networks, Inc.
|
||||
* Copyright (c) 2006, Devicescape Software, Inc.
|
||||
*
|
||||
* This software may be distributed under the terms of the BSD license.
|
||||
* See README for more details.
|
||||
*/
|
||||
|
||||
#include "utils/includes.h"
|
||||
|
||||
#include "utils/common.h"
|
||||
#include "utils/eloop.h"
|
||||
#include "common/ieee802_11_defs.h"
|
||||
#include "common/ieee802_11_common.h"
|
||||
#include "hostapd.h"
|
||||
#include "ap_config.h"
|
||||
#include "ieee802_11.h"
|
||||
#include "sta_info.h"
|
||||
#include "beacon.h"
|
||||
#include "ap_list.h"
|
||||
|
||||
|
||||
/* AP list is a double linked list with head->prev pointing to the end of the
|
||||
* list and tail->next = NULL. Entries are moved to the head of the list
|
||||
* whenever a beacon has been received from the AP in question. The tail entry
|
||||
* in this link will thus be the least recently used entry. */
|
||||
|
||||
|
||||
static int ap_list_beacon_olbc(struct hostapd_iface *iface, struct ap_info *ap)
|
||||
{
|
||||
int i;
|
||||
|
||||
if (iface->current_mode == NULL ||
|
||||
iface->current_mode->mode != HOSTAPD_MODE_IEEE80211G ||
|
||||
iface->conf->channel != ap->channel)
|
||||
return 0;
|
||||
|
||||
if (ap->erp != -1 && (ap->erp & ERP_INFO_NON_ERP_PRESENT))
|
||||
return 1;
|
||||
|
||||
for (i = 0; i < WLAN_SUPP_RATES_MAX; i++) {
|
||||
int rate = (ap->supported_rates[i] & 0x7f) * 5;
|
||||
if (rate == 60 || rate == 90 || rate > 110)
|
||||
return 0;
|
||||
}
|
||||
|
||||
return 1;
|
||||
}
|
||||
|
||||
|
||||
static struct ap_info * ap_get_ap(struct hostapd_iface *iface, const u8 *ap)
|
||||
{
|
||||
struct ap_info *s;
|
||||
|
||||
s = iface->ap_hash[STA_HASH(ap)];
|
||||
while (s != NULL && os_memcmp(s->addr, ap, ETH_ALEN) != 0)
|
||||
s = s->hnext;
|
||||
return s;
|
||||
}
|
||||
|
||||
|
||||
static void ap_ap_list_add(struct hostapd_iface *iface, struct ap_info *ap)
|
||||
{
|
||||
if (iface->ap_list) {
|
||||
ap->prev = iface->ap_list->prev;
|
||||
iface->ap_list->prev = ap;
|
||||
} else
|
||||
ap->prev = ap;
|
||||
ap->next = iface->ap_list;
|
||||
iface->ap_list = ap;
|
||||
}
|
||||
|
||||
|
||||
static void ap_ap_list_del(struct hostapd_iface *iface, struct ap_info *ap)
|
||||
{
|
||||
if (iface->ap_list == ap)
|
||||
iface->ap_list = ap->next;
|
||||
else
|
||||
ap->prev->next = ap->next;
|
||||
|
||||
if (ap->next)
|
||||
ap->next->prev = ap->prev;
|
||||
else if (iface->ap_list)
|
||||
iface->ap_list->prev = ap->prev;
|
||||
}
|
||||
|
||||
|
||||
static void ap_ap_hash_add(struct hostapd_iface *iface, struct ap_info *ap)
|
||||
{
|
||||
ap->hnext = iface->ap_hash[STA_HASH(ap->addr)];
|
||||
iface->ap_hash[STA_HASH(ap->addr)] = ap;
|
||||
}
|
||||
|
||||
|
||||
static void ap_ap_hash_del(struct hostapd_iface *iface, struct ap_info *ap)
|
||||
{
|
||||
struct ap_info *s;
|
||||
|
||||
s = iface->ap_hash[STA_HASH(ap->addr)];
|
||||
if (s == NULL) return;
|
||||
if (os_memcmp(s->addr, ap->addr, ETH_ALEN) == 0) {
|
||||
iface->ap_hash[STA_HASH(ap->addr)] = s->hnext;
|
||||
return;
|
||||
}
|
||||
|
||||
while (s->hnext != NULL &&
|
||||
os_memcmp(s->hnext->addr, ap->addr, ETH_ALEN) != 0)
|
||||
s = s->hnext;
|
||||
if (s->hnext != NULL)
|
||||
s->hnext = s->hnext->hnext;
|
||||
else
|
||||
wpa_printf(MSG_INFO, "AP: could not remove AP " MACSTR
|
||||
" from hash table", MAC2STR(ap->addr));
|
||||
}
|
||||
|
||||
|
||||
static void ap_free_ap(struct hostapd_iface *iface, struct ap_info *ap)
|
||||
{
|
||||
ap_ap_hash_del(iface, ap);
|
||||
ap_ap_list_del(iface, ap);
|
||||
|
||||
iface->num_ap--;
|
||||
os_free(ap);
|
||||
}
|
||||
|
||||
|
||||
static void hostapd_free_aps(struct hostapd_iface *iface)
|
||||
{
|
||||
struct ap_info *ap, *prev;
|
||||
|
||||
ap = iface->ap_list;
|
||||
|
||||
while (ap) {
|
||||
prev = ap;
|
||||
ap = ap->next;
|
||||
ap_free_ap(iface, prev);
|
||||
}
|
||||
|
||||
iface->ap_list = NULL;
|
||||
}
|
||||
|
||||
|
||||
static struct ap_info * ap_ap_add(struct hostapd_iface *iface, const u8 *addr)
|
||||
{
|
||||
struct ap_info *ap;
|
||||
|
||||
ap = os_zalloc(sizeof(struct ap_info));
|
||||
if (ap == NULL)
|
||||
return NULL;
|
||||
|
||||
/* initialize AP info data */
|
||||
os_memcpy(ap->addr, addr, ETH_ALEN);
|
||||
ap_ap_list_add(iface, ap);
|
||||
iface->num_ap++;
|
||||
ap_ap_hash_add(iface, ap);
|
||||
|
||||
if (iface->num_ap > iface->conf->ap_table_max_size && ap != ap->prev) {
|
||||
wpa_printf(MSG_DEBUG, "Removing the least recently used AP "
|
||||
MACSTR " from AP table", MAC2STR(ap->prev->addr));
|
||||
ap_free_ap(iface, ap->prev);
|
||||
}
|
||||
|
||||
return ap;
|
||||
}
|
||||
|
||||
|
||||
void ap_list_process_beacon(struct hostapd_iface *iface,
|
||||
const struct ieee80211_mgmt *mgmt,
|
||||
struct ieee802_11_elems *elems,
|
||||
struct hostapd_frame_info *fi)
|
||||
{
|
||||
struct ap_info *ap;
|
||||
int new_ap = 0;
|
||||
int set_beacon = 0;
|
||||
|
||||
if (iface->conf->ap_table_max_size < 1)
|
||||
return;
|
||||
|
||||
ap = ap_get_ap(iface, mgmt->bssid);
|
||||
if (!ap) {
|
||||
ap = ap_ap_add(iface, mgmt->bssid);
|
||||
if (!ap) {
|
||||
wpa_printf(MSG_INFO,
|
||||
"Failed to allocate AP information entry");
|
||||
return;
|
||||
}
|
||||
new_ap = 1;
|
||||
}
|
||||
|
||||
merge_byte_arrays(ap->supported_rates, WLAN_SUPP_RATES_MAX,
|
||||
elems->supp_rates, elems->supp_rates_len,
|
||||
elems->ext_supp_rates, elems->ext_supp_rates_len);
|
||||
|
||||
if (elems->erp_info)
|
||||
ap->erp = elems->erp_info[0];
|
||||
else
|
||||
ap->erp = -1;
|
||||
|
||||
if (elems->ds_params)
|
||||
ap->channel = elems->ds_params[0];
|
||||
else if (elems->ht_operation)
|
||||
ap->channel = elems->ht_operation[0];
|
||||
else if (fi)
|
||||
ap->channel = fi->channel;
|
||||
|
||||
if (elems->ht_capabilities)
|
||||
ap->ht_support = 1;
|
||||
else
|
||||
ap->ht_support = 0;
|
||||
|
||||
os_get_reltime(&ap->last_beacon);
|
||||
|
||||
if (!new_ap && ap != iface->ap_list) {
|
||||
/* move AP entry into the beginning of the list so that the
|
||||
* oldest entry is always in the end of the list */
|
||||
ap_ap_list_del(iface, ap);
|
||||
ap_ap_list_add(iface, ap);
|
||||
}
|
||||
|
||||
if (!iface->olbc &&
|
||||
ap_list_beacon_olbc(iface, ap)) {
|
||||
iface->olbc = 1;
|
||||
wpa_printf(MSG_DEBUG, "OLBC AP detected: " MACSTR
|
||||
" (channel %d) - enable protection",
|
||||
MAC2STR(ap->addr), ap->channel);
|
||||
set_beacon++;
|
||||
}
|
||||
|
||||
#ifdef CONFIG_IEEE80211N
|
||||
if (!iface->olbc_ht && !ap->ht_support &&
|
||||
(ap->channel == 0 ||
|
||||
ap->channel == iface->conf->channel ||
|
||||
ap->channel == iface->conf->channel +
|
||||
iface->conf->secondary_channel * 4)) {
|
||||
iface->olbc_ht = 1;
|
||||
hostapd_ht_operation_update(iface);
|
||||
wpa_printf(MSG_DEBUG, "OLBC HT AP detected: " MACSTR
|
||||
" (channel %d) - enable protection",
|
||||
MAC2STR(ap->addr), ap->channel);
|
||||
set_beacon++;
|
||||
}
|
||||
#endif /* CONFIG_IEEE80211N */
|
||||
|
||||
if (set_beacon)
|
||||
ieee802_11_update_beacons(iface);
|
||||
}
|
||||
|
||||
|
||||
void ap_list_timer(struct hostapd_iface *iface)
|
||||
{
|
||||
struct os_reltime now;
|
||||
struct ap_info *ap;
|
||||
int set_beacon = 0;
|
||||
|
||||
if (!iface->ap_list)
|
||||
return;
|
||||
|
||||
os_get_reltime(&now);
|
||||
|
||||
while (iface->ap_list) {
|
||||
ap = iface->ap_list->prev;
|
||||
if (!os_reltime_expired(&now, &ap->last_beacon,
|
||||
iface->conf->ap_table_expiration_time))
|
||||
break;
|
||||
|
||||
ap_free_ap(iface, ap);
|
||||
}
|
||||
|
||||
if (iface->olbc || iface->olbc_ht) {
|
||||
int olbc = 0;
|
||||
int olbc_ht = 0;
|
||||
|
||||
ap = iface->ap_list;
|
||||
while (ap && (olbc == 0 || olbc_ht == 0)) {
|
||||
if (ap_list_beacon_olbc(iface, ap))
|
||||
olbc = 1;
|
||||
if (!ap->ht_support)
|
||||
olbc_ht = 1;
|
||||
ap = ap->next;
|
||||
}
|
||||
if (!olbc && iface->olbc) {
|
||||
wpa_printf(MSG_DEBUG, "OLBC not detected anymore");
|
||||
iface->olbc = 0;
|
||||
set_beacon++;
|
||||
}
|
||||
#ifdef CONFIG_IEEE80211N
|
||||
if (!olbc_ht && iface->olbc_ht) {
|
||||
wpa_printf(MSG_DEBUG, "OLBC HT not detected anymore");
|
||||
iface->olbc_ht = 0;
|
||||
hostapd_ht_operation_update(iface);
|
||||
set_beacon++;
|
||||
}
|
||||
#endif /* CONFIG_IEEE80211N */
|
||||
}
|
||||
|
||||
if (set_beacon)
|
||||
ieee802_11_update_beacons(iface);
|
||||
}
|
||||
|
||||
|
||||
int ap_list_init(struct hostapd_iface *iface)
|
||||
{
|
||||
return 0;
|
||||
}
|
||||
|
||||
|
||||
void ap_list_deinit(struct hostapd_iface *iface)
|
||||
{
|
||||
hostapd_free_aps(iface);
|
||||
}
|
|
@ -0,0 +1,58 @@
|
|||
/*
|
||||
* hostapd / AP table
|
||||
* Copyright (c) 2002-2003, Jouni Malinen <j@w1.fi>
|
||||
* Copyright (c) 2003-2004, Instant802 Networks, Inc.
|
||||
* Copyright (c) 2006, Devicescape Software, Inc.
|
||||
*
|
||||
* This software may be distributed under the terms of the BSD license.
|
||||
* See README for more details.
|
||||
*/
|
||||
|
||||
#ifndef AP_LIST_H
|
||||
#define AP_LIST_H
|
||||
|
||||
struct ap_info {
|
||||
/* Note: next/prev pointers are updated whenever a new beacon is
|
||||
* received because these are used to find the least recently used
|
||||
* entries. */
|
||||
struct ap_info *next; /* next entry in AP list */
|
||||
struct ap_info *prev; /* previous entry in AP list */
|
||||
struct ap_info *hnext; /* next entry in hash table list */
|
||||
u8 addr[6];
|
||||
u8 supported_rates[WLAN_SUPP_RATES_MAX];
|
||||
int erp; /* ERP Info or -1 if ERP info element not present */
|
||||
|
||||
int channel;
|
||||
|
||||
int ht_support;
|
||||
|
||||
struct os_reltime last_beacon;
|
||||
};
|
||||
|
||||
struct ieee802_11_elems;
|
||||
struct hostapd_frame_info;
|
||||
|
||||
void ap_list_process_beacon(struct hostapd_iface *iface,
|
||||
const struct ieee80211_mgmt *mgmt,
|
||||
struct ieee802_11_elems *elems,
|
||||
struct hostapd_frame_info *fi);
|
||||
#ifdef NEED_AP_MLME
|
||||
int ap_list_init(struct hostapd_iface *iface);
|
||||
void ap_list_deinit(struct hostapd_iface *iface);
|
||||
void ap_list_timer(struct hostapd_iface *iface);
|
||||
#else /* NEED_AP_MLME */
|
||||
static inline int ap_list_init(struct hostapd_iface *iface)
|
||||
{
|
||||
return 0;
|
||||
}
|
||||
|
||||
static inline void ap_list_deinit(struct hostapd_iface *iface)
|
||||
{
|
||||
}
|
||||
|
||||
static inline void ap_list_timer(struct hostapd_iface *iface)
|
||||
{
|
||||
}
|
||||
#endif /* NEED_AP_MLME */
|
||||
|
||||
#endif /* AP_LIST_H */
|
Some files were not shown because too many files have changed in this diff Show More
Loading…
Reference in New Issue