aboutsummaryrefslogtreecommitdiff
diff options
context:
space:
mode:
authorCy Schubert <cy@FreeBSD.org>2022-01-18 16:08:03 +0000
committerCy Schubert <cy@FreeBSD.org>2022-01-18 16:10:33 +0000
commit64e33c5cb1e2ec64e0b1ce576eb53bbb27e93582 (patch)
tree79e533763565a7490abb7570f429d871e7b3d099
parent679ff6112361d2660f4e0c3cda71198a5e773a25 (diff)
downloadsrc-64e33c5cb1e2ec64e0b1ce576eb53bbb27e93582.tar.gz
src-64e33c5cb1e2ec64e0b1ce576eb53bbb27e93582.zip
Revert "wpa: Import wpa 2.10."
This reverts commit 5eb81a4b4028113e3c319f21a1db6b67613ec7ab, reversing changes made to c6806434e79079f4f9419c3ba4fec37efcaa1635 and this reverts commit 679ff6112361d2660f4e0c3cda71198a5e773a25. What happend is git rebase --rebase-merges doesn't do what is expected.
-rw-r--r--contrib/byacc/test/btyacc/err_syntax22.error1
-rw-r--r--contrib/byacc/test/yacc/err_syntax22.error1
-rw-r--r--contrib/unbound/doc/unbound.doxygen1656
-rw-r--r--contrib/wpa/CONTRIBUTIONS2
-rw-r--r--contrib/wpa/COPYING2
-rw-r--r--contrib/wpa/README2
-rw-r--r--contrib/wpa/hostapd/ChangeLog43
-rw-r--r--contrib/wpa/hostapd/README2
-rw-r--r--contrib/wpa/hostapd/hostapd_cli.c4
-rw-r--r--contrib/wpa/hostapd/main.c4
-rw-r--r--contrib/wpa/hs20/server/Makefile42
-rwxr-xr-xcontrib/wpa/hs20/server/ca/clean.sh13
-rw-r--r--contrib/wpa/hs20/server/ca/est-csrattrs.cnf17
-rwxr-xr-xcontrib/wpa/hs20/server/ca/est-csrattrs.sh4
-rw-r--r--contrib/wpa/hs20/server/ca/hs20.oid7
-rwxr-xr-xcontrib/wpa/hs20/server/ca/ocsp-req.sh11
-rwxr-xr-xcontrib/wpa/hs20/server/ca/ocsp-responder-ica.sh3
-rwxr-xr-xcontrib/wpa/hs20/server/ca/ocsp-responder.sh3
-rwxr-xr-xcontrib/wpa/hs20/server/ca/ocsp-update-cache.sh11
-rw-r--r--contrib/wpa/hs20/server/ca/openssl-root.cnf125
-rw-r--r--contrib/wpa/hs20/server/ca/openssl.cnf200
-rwxr-xr-xcontrib/wpa/hs20/server/ca/setup.sh209
-rw-r--r--contrib/wpa/hs20/server/ca/w1fi_logo.pngbin0 -> 7549 bytes
-rw-r--r--contrib/wpa/hs20/server/hs20-osu-server.txt262
-rw-r--r--contrib/wpa/hs20/server/hs20_spp_server.c207
-rw-r--r--contrib/wpa/hs20/server/spp_server.c2933
-rw-r--r--contrib/wpa/hs20/server/spp_server.h36
-rw-r--r--contrib/wpa/hs20/server/sql-example.txt17
-rw-r--r--contrib/wpa/hs20/server/sql.txt108
-rw-r--r--contrib/wpa/hs20/server/www/add-free.php50
-rw-r--r--contrib/wpa/hs20/server/www/add-mo.php56
-rw-r--r--contrib/wpa/hs20/server/www/cert-enroll.php39
-rw-r--r--contrib/wpa/hs20/server/www/config.php7
-rw-r--r--contrib/wpa/hs20/server/www/est.php232
-rw-r--r--contrib/wpa/hs20/server/www/free-remediation.php19
-rw-r--r--contrib/wpa/hs20/server/www/free.php23
-rw-r--r--contrib/wpa/hs20/server/www/redirect.php32
-rw-r--r--contrib/wpa/hs20/server/www/remediation-pw.php41
-rw-r--r--contrib/wpa/hs20/server/www/remediation.php55
-rw-r--r--contrib/wpa/hs20/server/www/signup.php59
-rw-r--r--contrib/wpa/hs20/server/www/spp.php168
-rw-r--r--contrib/wpa/hs20/server/www/terms.php87
-rw-r--r--contrib/wpa/hs20/server/www/users.php377
-rw-r--r--contrib/wpa/src/common/dragonfly.c34
-rw-r--r--contrib/wpa/src/common/dragonfly.h2
-rw-r--r--contrib/wpa/src/common/qca-vendor.h43
-rw-r--r--contrib/wpa/src/common/sae.c61
-rw-r--r--contrib/wpa/src/common/version.h2
-rw-r--r--contrib/wpa/src/crypto/crypto.h12
-rw-r--r--contrib/wpa/src/crypto/crypto_openssl.c99
-rw-r--r--contrib/wpa/src/crypto/crypto_wolfssl.c24
-rw-r--r--contrib/wpa/src/crypto/tls_openssl.c28
-rw-r--r--contrib/wpa/src/eap_common/eap_pwd_common.c46
-rw-r--r--contrib/wpa/wpa_supplicant/ChangeLog53
-rw-r--r--contrib/wpa/wpa_supplicant/README2
-rw-r--r--contrib/wpa/wpa_supplicant/binder/fi/w1/wpa_supplicant/IIface.aidl16
-rw-r--r--contrib/wpa/wpa_supplicant/binder/fi/w1/wpa_supplicant/ISupplicant.aidl59
-rw-r--r--contrib/wpa/wpa_supplicant/binder/fi/w1/wpa_supplicant/ISupplicantCallbacks.aidl20
-rw-r--r--contrib/wpa/wpa_supplicant/dbus/dbus_new_handlers.c2
-rw-r--r--contrib/wpa/wpa_supplicant/doc/docbook/eapol_test.sgml2
-rw-r--r--contrib/wpa/wpa_supplicant/doc/docbook/wpa_background.sgml2
-rw-r--r--contrib/wpa/wpa_supplicant/doc/docbook/wpa_cli.sgml2
-rw-r--r--contrib/wpa/wpa_supplicant/doc/docbook/wpa_gui.sgml2
-rw-r--r--contrib/wpa/wpa_supplicant/doc/docbook/wpa_passphrase.sgml2
-rw-r--r--contrib/wpa/wpa_supplicant/doc/docbook/wpa_priv.sgml2
-rw-r--r--contrib/wpa/wpa_supplicant/doc/docbook/wpa_supplicant.sgml2
-rw-r--r--contrib/wpa/wpa_supplicant/sme.c10
-rwxr-xr-xcontrib/wpa/wpa_supplicant/vs2005/eapol_test/eapol_test.vcproj477
-rwxr-xr-xcontrib/wpa/wpa_supplicant/vs2005/wpa_cli/wpa_cli.vcproj215
-rwxr-xr-xcontrib/wpa/wpa_supplicant/vs2005/wpa_passphrase/wpa_passphrase.vcproj236
-rwxr-xr-xcontrib/wpa/wpa_supplicant/vs2005/wpa_supplicant/wpa_supplicant.vcproj465
-rw-r--r--contrib/wpa/wpa_supplicant/wnm_sta.c11
-rw-r--r--contrib/wpa/wpa_supplicant/wpa_cli.c4
-rw-r--r--contrib/wpa/wpa_supplicant/wpa_supplicant.c13
-rw-r--r--contrib/wpa/wpa_supplicant/wpa_supplicant_i.h1
-rw-r--r--sys/contrib/openzfs/tests/zfs-tests/cmd/send_doall/.gitignore1
-rw-r--r--tests/Makefile22
-rw-r--r--tests/README62
-rw-r--r--usr.bin/bmake/tests/archives/fmt_44bsd/expected.status.11
79 files changed, 8828 insertions, 377 deletions
diff --git a/contrib/byacc/test/btyacc/err_syntax22.error b/contrib/byacc/test/btyacc/err_syntax22.error
new file mode 100644
index 000000000000..8622aa73f598
--- /dev/null
+++ b/contrib/byacc/test/btyacc/err_syntax22.error
@@ -0,0 +1 @@
+YACC: e - line 17 of "./err_syntax22.y", $2 (recur) is untyped
diff --git a/contrib/byacc/test/yacc/err_syntax22.error b/contrib/byacc/test/yacc/err_syntax22.error
new file mode 100644
index 000000000000..8622aa73f598
--- /dev/null
+++ b/contrib/byacc/test/yacc/err_syntax22.error
@@ -0,0 +1 @@
+YACC: e - line 17 of "./err_syntax22.y", $2 (recur) is untyped
diff --git a/contrib/unbound/doc/unbound.doxygen b/contrib/unbound/doc/unbound.doxygen
new file mode 100644
index 000000000000..7222dbc274e9
--- /dev/null
+++ b/contrib/unbound/doc/unbound.doxygen
@@ -0,0 +1,1656 @@
+# Doxyfile 1.7.1
+
+# This file describes the settings to be used by the documentation system
+# doxygen (www.doxygen.org) for a project
+#
+# All text after a hash (#) is considered a comment and will be ignored
+# The format is:
+# TAG = value [value, ...]
+# For lists items can also be appended using:
+# TAG += value [value, ...]
+# Values that contain spaces should be placed between quotes (" ")
+
+#---------------------------------------------------------------------------
+# Project related configuration options
+#---------------------------------------------------------------------------
+
+# This tag specifies the encoding used for all characters in the config file
+# that follow. The default is UTF-8 which is also the encoding used for all
+# text before the first occurrence of this tag. Doxygen uses libiconv (or the
+# iconv built into libc) for the transcoding. See
+# http://www.gnu.org/software/libiconv for the list of possible encodings.
+
+DOXYFILE_ENCODING = UTF-8
+
+# The PROJECT_NAME tag is a single word (or a sequence of words surrounded
+# by quotes) that should identify the project.
+
+PROJECT_NAME = unbound
+
+# The PROJECT_NUMBER tag can be used to enter a project or revision number.
+# This could be handy for archiving the generated documentation or
+# if some version control system is used.
+
+PROJECT_NUMBER = 0.1
+
+# The OUTPUT_DIRECTORY tag is used to specify the (relative or absolute)
+# base path where the generated documentation will be put.
+# If a relative path is entered, it will be relative to the location
+# where doxygen was started. If left blank the current directory will be used.
+
+OUTPUT_DIRECTORY = doc
+
+# If the CREATE_SUBDIRS tag is set to YES, then doxygen will create
+# 4096 sub-directories (in 2 levels) under the output directory of each output
+# format and will distribute the generated files over these directories.
+# Enabling this option can be useful when feeding doxygen a huge amount of
+# source files, where putting all generated files in the same directory would
+# otherwise cause performance problems for the file system.
+
+CREATE_SUBDIRS = NO
+
+# The OUTPUT_LANGUAGE tag is used to specify the language in which all
+# documentation generated by doxygen is written. Doxygen will use this
+# information to generate all constant output in the proper language.
+# The default language is English, other supported languages are:
+# Afrikaans, Arabic, Brazilian, Catalan, Chinese, Chinese-Traditional,
+# Croatian, Czech, Danish, Dutch, Esperanto, Farsi, Finnish, French, German,
+# Greek, Hungarian, Italian, Japanese, Japanese-en (Japanese with English
+# messages), Korean, Korean-en, Lithuanian, Norwegian, Macedonian, Persian,
+# Polish, Portuguese, Romanian, Russian, Serbian, Serbian-Cyrilic, Slovak,
+# Slovene, Spanish, Swedish, Ukrainian, and Vietnamese.
+
+OUTPUT_LANGUAGE = English
+
+# If the BRIEF_MEMBER_DESC tag is set to YES (the default) Doxygen will
+# include brief member descriptions after the members that are listed in
+# the file and class documentation (similar to JavaDoc).
+# Set to NO to disable this.
+
+BRIEF_MEMBER_DESC = YES
+
+# If the REPEAT_BRIEF tag is set to YES (the default) Doxygen will prepend
+# the brief description of a member or function before the detailed description.
+# Note: if both HIDE_UNDOC_MEMBERS and BRIEF_MEMBER_DESC are set to NO, the
+# brief descriptions will be completely suppressed.
+
+REPEAT_BRIEF = YES
+
+# This tag implements a quasi-intelligent brief description abbreviator
+# that is used to form the text in various listings. Each string
+# in this list, if found as the leading text of the brief description, will be
+# stripped from the text and the result after processing the whole list, is
+# used as the annotated text. Otherwise, the brief description is used as-is.
+# If left blank, the following values are used ("$name" is automatically
+# replaced with the name of the entity): "The $name class" "The $name widget"
+# "The $name file" "is" "provides" "specifies" "contains"
+# "represents" "a" "an" "the"
+
+ABBREVIATE_BRIEF =
+
+# If the ALWAYS_DETAILED_SEC and REPEAT_BRIEF tags are both set to YES then
+# Doxygen will generate a detailed section even if there is only a brief
+# description.
+
+ALWAYS_DETAILED_SEC = NO
+
+# If the INLINE_INHERITED_MEMB tag is set to YES, doxygen will show all
+# inherited members of a class in the documentation of that class as if those
+# members were ordinary class members. Constructors, destructors and assignment
+# operators of the base classes will not be shown.
+
+INLINE_INHERITED_MEMB = NO
+
+# If the FULL_PATH_NAMES tag is set to YES then Doxygen will prepend the full
+# path before files name in the file list and in the header files. If set
+# to NO the shortest path that makes the file name unique will be used.
+
+FULL_PATH_NAMES = YES
+
+# If the FULL_PATH_NAMES tag is set to YES then the STRIP_FROM_PATH tag
+# can be used to strip a user-defined part of the path. Stripping is
+# only done if one of the specified strings matches the left-hand part of
+# the path. The tag can be used to show relative paths in the file list.
+# If left blank the directory from which doxygen is run is used as the
+# path to strip.
+
+STRIP_FROM_PATH =
+
+# The STRIP_FROM_INC_PATH tag can be used to strip a user-defined part of
+# the path mentioned in the documentation of a class, which tells
+# the reader which header file to include in order to use a class.
+# If left blank only the name of the header file containing the class
+# definition is used. Otherwise one should specify the include paths that
+# are normally passed to the compiler using the -I flag.
+
+STRIP_FROM_INC_PATH =
+
+# If the SHORT_NAMES tag is set to YES, doxygen will generate much shorter
+# (but less readable) file names. This can be useful is your file systems
+# doesn't support long names like on DOS, Mac, or CD-ROM.
+
+SHORT_NAMES = NO
+
+# If the JAVADOC_AUTOBRIEF tag is set to YES then Doxygen
+# will interpret the first line (until the first dot) of a JavaDoc-style
+# comment as the brief description. If set to NO, the JavaDoc
+# comments will behave just like regular Qt-style comments
+# (thus requiring an explicit @brief command for a brief description.)
+
+JAVADOC_AUTOBRIEF = YES
+
+# If the QT_AUTOBRIEF tag is set to YES then Doxygen will
+# interpret the first line (until the first dot) of a Qt-style
+# comment as the brief description. If set to NO, the comments
+# will behave just like regular Qt-style comments (thus requiring
+# an explicit \brief command for a brief description.)
+
+QT_AUTOBRIEF = NO
+
+# The MULTILINE_CPP_IS_BRIEF tag can be set to YES to make Doxygen
+# treat a multi-line C++ special comment block (i.e. a block of //! or ///
+# comments) as a brief description. This used to be the default behaviour.
+# The new default is to treat a multi-line C++ comment block as a detailed
+# description. Set this tag to YES if you prefer the old behaviour instead.
+
+MULTILINE_CPP_IS_BRIEF = NO
+
+# If the INHERIT_DOCS tag is set to YES (the default) then an undocumented
+# member inherits the documentation from any documented member that it
+# re-implements.
+
+INHERIT_DOCS = YES
+
+# If the SEPARATE_MEMBER_PAGES tag is set to YES, then doxygen will produce
+# a new page for each member. If set to NO, the documentation of a member will
+# be part of the file/class/namespace that contains it.
+
+SEPARATE_MEMBER_PAGES = NO
+
+# The TAB_SIZE tag can be used to set the number of spaces in a tab.
+# Doxygen uses this value to replace tabs by spaces in code fragments.
+
+TAB_SIZE = 8
+
+# This tag can be used to specify a number of aliases that acts
+# as commands in the documentation. An alias has the form "name=value".
+# For example adding "sideeffect=\par Side Effects:\n" will allow you to
+# put the command \sideeffect (or @sideeffect) in the documentation, which
+# will result in a user-defined paragraph with heading "Side Effects:".
+# You can put \n's in the value part of an alias to insert newlines.
+
+ALIASES =
+
+# Set the OPTIMIZE_OUTPUT_FOR_C tag to YES if your project consists of C
+# sources only. Doxygen will then generate output that is more tailored for C.
+# For instance, some of the names that are used will be different. The list
+# of all members will be omitted, etc.
+
+OPTIMIZE_OUTPUT_FOR_C = YES
+
+# Set the OPTIMIZE_OUTPUT_JAVA tag to YES if your project consists of Java
+# sources only. Doxygen will then generate output that is more tailored for
+# Java. For instance, namespaces will be presented as packages, qualified
+# scopes will look different, etc.
+
+OPTIMIZE_OUTPUT_JAVA = NO
+
+# Set the OPTIMIZE_FOR_FORTRAN tag to YES if your project consists of Fortran
+# sources only. Doxygen will then generate output that is more tailored for
+# Fortran.
+
+OPTIMIZE_FOR_FORTRAN = NO
+
+# Set the OPTIMIZE_OUTPUT_VHDL tag to YES if your project consists of VHDL
+# sources. Doxygen will then generate output that is tailored for
+# VHDL.
+
+OPTIMIZE_OUTPUT_VHDL = NO
+
+# Doxygen selects the parser to use depending on the extension of the files it
+# parses. With this tag you can assign which parser to use for a given extension.
+# Doxygen has a built-in mapping, but you can override or extend it using this
+# tag. The format is ext=language, where ext is a file extension, and language
+# is one of the parsers supported by doxygen: IDL, Java, Javascript, CSharp, C,
+# C++, D, PHP, Objective-C, Python, Fortran, VHDL, C, C++. For instance to make
+# doxygen treat .inc files as Fortran files (default is PHP), and .f files as C
+# (default is Fortran), use: inc=Fortran f=C. Note that for custom extensions
+# you also need to set FILE_PATTERNS otherwise the files are not read by doxygen.
+
+EXTENSION_MAPPING =
+
+# If you use STL classes (i.e. std::string, std::vector, etc.) but do not want
+# to include (a tag file for) the STL sources as input, then you should
+# set this tag to YES in order to let doxygen match functions declarations and
+# definitions whose arguments contain STL classes (e.g. func(std::string); v.s.
+# func(std::string) {}). This also make the inheritance and collaboration
+# diagrams that involve STL classes more complete and accurate.
+
+BUILTIN_STL_SUPPORT = NO
+
+# If you use Microsoft's C++/CLI language, you should set this option to YES to
+# enable parsing support.
+
+CPP_CLI_SUPPORT = NO
+
+# Set the SIP_SUPPORT tag to YES if your project consists of sip sources only.
+# Doxygen will parse them like normal C++ but will assume all classes use public
+# instead of private inheritance when no explicit protection keyword is present.
+
+SIP_SUPPORT = NO
+
+# For Microsoft's IDL there are propget and propput attributes to indicate getter
+# and setter methods for a property. Setting this option to YES (the default)
+# will make doxygen to replace the get and set methods by a property in the
+# documentation. This will only work if the methods are indeed getting or
+# setting a simple type. If this is not the case, or you want to show the
+# methods anyway, you should set this option to NO.
+
+IDL_PROPERTY_SUPPORT = YES
+
+# If member grouping is used in the documentation and the DISTRIBUTE_GROUP_DOC
+# tag is set to YES, then doxygen will reuse the documentation of the first
+# member in the group (if any) for the other members of the group. By default
+# all members of a group must be documented explicitly.
+
+DISTRIBUTE_GROUP_DOC = NO
+
+# Set the SUBGROUPING tag to YES (the default) to allow class member groups of
+# the same type (for instance a group of public functions) to be put as a
+# subgroup of that type (e.g. under the Public Functions section). Set it to
+# NO to prevent subgrouping. Alternatively, this can be done per class using
+# the \nosubgrouping command.
+
+SUBGROUPING = YES
+
+# When TYPEDEF_HIDES_STRUCT is enabled, a typedef of a struct, union, or enum
+# is documented as struct, union, or enum with the name of the typedef. So
+# typedef struct TypeS {} TypeT, will appear in the documentation as a struct
+# with name TypeT. When disabled the typedef will appear as a member of a file,
+# namespace, or class. And the struct will be named TypeS. This can typically
+# be useful for C code in case the coding convention dictates that all compound
+# types are typedef'ed and only the typedef is referenced, never the tag name.
+
+TYPEDEF_HIDES_STRUCT = NO
+
+# The SYMBOL_CACHE_SIZE determines the size of the internal cache use to
+# determine which symbols to keep in memory and which to flush to disk.
+# When the cache is full, less often used symbols will be written to disk.
+# For small to medium size projects (<1000 input files) the default value is
+# probably good enough. For larger projects a too small cache size can cause
+# doxygen to be busy swapping symbols to and from disk most of the time
+# causing a significant performance penalty.
+# If the system has enough physical memory increasing the cache will improve the
+# performance by keeping more symbols in memory. Note that the value works on
+# a logarithmic scale so increasing the size by one will roughly double the
+# memory usage. The cache size is given by this formula:
+# 2^(16+SYMBOL_CACHE_SIZE). The valid range is 0..9, the default is 0,
+# corresponding to a cache size of 2^16 = 65536 symbols
+
+#SYMBOL_CACHE_SIZE = 0
+
+#---------------------------------------------------------------------------
+# Build related configuration options
+#---------------------------------------------------------------------------
+
+# If the EXTRACT_ALL tag is set to YES doxygen will assume all entities in
+# documentation are documented, even if no documentation was available.
+# Private class members and static file members will be hidden unless
+# the EXTRACT_PRIVATE and EXTRACT_STATIC tags are set to YES
+
+EXTRACT_ALL = NO
+
+# If the EXTRACT_PRIVATE tag is set to YES all private members of a class
+# will be included in the documentation.
+
+EXTRACT_PRIVATE = YES
+
+# If the EXTRACT_STATIC tag is set to YES all static members of a file
+# will be included in the documentation.
+
+EXTRACT_STATIC = YES
+
+# If the EXTRACT_LOCAL_CLASSES tag is set to YES classes (and structs)
+# defined locally in source files will be included in the documentation.
+# If set to NO only classes defined in header files are included.
+
+EXTRACT_LOCAL_CLASSES = YES
+
+# This flag is only useful for Objective-C code. When set to YES local
+# methods, which are defined in the implementation section but not in
+# the interface are included in the documentation.
+# If set to NO (the default) only methods in the interface are included.
+
+EXTRACT_LOCAL_METHODS = YES
+
+# If this flag is set to YES, the members of anonymous namespaces will be
+# extracted and appear in the documentation as a namespace called
+# 'anonymous_namespace{file}', where file will be replaced with the base
+# name of the file that contains the anonymous namespace. By default
+# anonymous namespace are hidden.
+
+EXTRACT_ANON_NSPACES = NO
+
+# If the HIDE_UNDOC_MEMBERS tag is set to YES, Doxygen will hide all
+# undocumented members of documented classes, files or namespaces.
+# If set to NO (the default) these members will be included in the
+# various overviews, but no documentation section is generated.
+# This option has no effect if EXTRACT_ALL is enabled.
+
+HIDE_UNDOC_MEMBERS = NO
+
+# If the HIDE_UNDOC_CLASSES tag is set to YES, Doxygen will hide all
+# undocumented classes that are normally visible in the class hierarchy.
+# If set to NO (the default) these classes will be included in the various
+# overviews. This option has no effect if EXTRACT_ALL is enabled.
+
+HIDE_UNDOC_CLASSES = NO
+
+# If the HIDE_FRIEND_COMPOUNDS tag is set to YES, Doxygen will hide all
+# friend (class|struct|union) declarations.
+# If set to NO (the default) these declarations will be included in the
+# documentation.
+
+HIDE_FRIEND_COMPOUNDS = NO
+
+# If the HIDE_IN_BODY_DOCS tag is set to YES, Doxygen will hide any
+# documentation blocks found inside the body of a function.
+# If set to NO (the default) these blocks will be appended to the
+# function's detailed documentation block.
+
+HIDE_IN_BODY_DOCS = NO
+
+# The INTERNAL_DOCS tag determines if documentation
+# that is typed after a \internal command is included. If the tag is set
+# to NO (the default) then the documentation will be excluded.
+# Set it to YES to include the internal documentation.
+
+INTERNAL_DOCS = NO
+
+# If the CASE_SENSE_NAMES tag is set to NO then Doxygen will only generate
+# file names in lower-case letters. If set to YES upper-case letters are also
+# allowed. This is useful if you have classes or files whose names only differ
+# in case and if your file system supports case sensitive file names. Windows
+# and Mac users are advised to set this option to NO.
+
+CASE_SENSE_NAMES = YES
+
+# If the HIDE_SCOPE_NAMES tag is set to NO (the default) then Doxygen
+# will show members with their full class and namespace scopes in the
+# documentation. If set to YES the scope will be hidden.
+
+HIDE_SCOPE_NAMES = NO
+
+# If the SHOW_INCLUDE_FILES tag is set to YES (the default) then Doxygen
+# will put a list of the files that are included by a file in the documentation
+# of that file.
+
+SHOW_INCLUDE_FILES = YES
+
+# If the FORCE_LOCAL_INCLUDES tag is set to YES then Doxygen
+# will list include files with double quotes in the documentation
+# rather than with sharp brackets.
+
+FORCE_LOCAL_INCLUDES = NO
+
+# If the INLINE_INFO tag is set to YES (the default) then a tag [inline]
+# is inserted in the documentation for inline members.
+
+INLINE_INFO = YES
+
+# If the SORT_MEMBER_DOCS tag is set to YES (the default) then doxygen
+# will sort the (detailed) documentation of file and class members
+# alphabetically by member name. If set to NO the members will appear in
+# declaration order.
+
+SORT_MEMBER_DOCS = NO
+
+# If the SORT_BRIEF_DOCS tag is set to YES then doxygen will sort the
+# brief documentation of file, namespace and class members alphabetically
+# by member name. If set to NO (the default) the members will appear in
+# declaration order.
+
+SORT_BRIEF_DOCS = NO
+
+# If the SORT_MEMBERS_CTORS_1ST tag is set to YES then doxygen
+# will sort the (brief and detailed) documentation of class members so that
+# constructors and destructors are listed first. If set to NO (the default)
+# the constructors will appear in the respective orders defined by
+# SORT_MEMBER_DOCS and SORT_BRIEF_DOCS.
+# This tag will be ignored for brief docs if SORT_BRIEF_DOCS is set to NO
+# and ignored for detailed docs if SORT_MEMBER_DOCS is set to NO.
+
+SORT_MEMBERS_CTORS_1ST = NO
+
+# If the SORT_GROUP_NAMES tag is set to YES then doxygen will sort the
+# hierarchy of group names into alphabetical order. If set to NO (the default)
+# the group names will appear in their defined order.
+
+SORT_GROUP_NAMES = NO
+
+# If the SORT_BY_SCOPE_NAME tag is set to YES, the class list will be
+# sorted by fully-qualified names, including namespaces. If set to
+# NO (the default), the class list will be sorted only by class name,
+# not including the namespace part.
+# Note: This option is not very useful if HIDE_SCOPE_NAMES is set to YES.
+# Note: This option applies only to the class list, not to the
+# alphabetical list.
+
+SORT_BY_SCOPE_NAME = NO
+
+# The GENERATE_TODOLIST tag can be used to enable (YES) or
+# disable (NO) the todo list. This list is created by putting \todo
+# commands in the documentation.
+
+GENERATE_TODOLIST = YES
+
+# The GENERATE_TESTLIST tag can be used to enable (YES) or
+# disable (NO) the test list. This list is created by putting \test
+# commands in the documentation.
+
+GENERATE_TESTLIST = YES
+
+# The GENERATE_BUGLIST tag can be used to enable (YES) or
+# disable (NO) the bug list. This list is created by putting \bug
+# commands in the documentation.
+
+GENERATE_BUGLIST = YES
+
+# The GENERATE_DEPRECATEDLIST tag can be used to enable (YES) or
+# disable (NO) the deprecated list. This list is created by putting
+# \deprecated commands in the documentation.
+
+GENERATE_DEPRECATEDLIST= YES
+
+# The ENABLED_SECTIONS tag can be used to enable conditional
+# documentation sections, marked by \if sectionname ... \endif.
+
+ENABLED_SECTIONS =
+
+# The MAX_INITIALIZER_LINES tag determines the maximum number of lines
+# the initial value of a variable or define consists of for it to appear in
+# the documentation. If the initializer consists of more lines than specified
+# here it will be hidden. Use a value of 0 to hide initializers completely.
+# The appearance of the initializer of individual variables and defines in the
+# documentation can be controlled using \showinitializer or \hideinitializer
+# command in the documentation regardless of this setting.
+
+MAX_INITIALIZER_LINES = 30
+
+# Set the SHOW_USED_FILES tag to NO to disable the list of files generated
+# at the bottom of the documentation of classes and structs. If set to YES the
+# list will mention the files that were used to generate the documentation.
+
+SHOW_USED_FILES = YES
+
+# If the sources in your project are distributed over multiple directories
+# then setting the SHOW_DIRECTORIES tag to YES will show the directory hierarchy
+# in the documentation. The default is NO.
+
+#SHOW_DIRECTORIES = YES
+
+# Set the SHOW_FILES tag to NO to disable the generation of the Files page.
+# This will remove the Files entry from the Quick Index and from the
+# Folder Tree View (if specified). The default is YES.
+
+SHOW_FILES = YES
+
+# Set the SHOW_NAMESPACES tag to NO to disable the generation of the
+# Namespaces page.
+# This will remove the Namespaces entry from the Quick Index
+# and from the Folder Tree View (if specified). The default is YES.
+
+SHOW_NAMESPACES = YES
+
+# The FILE_VERSION_FILTER tag can be used to specify a program or script that
+# doxygen should invoke to get the current version for each file (typically from
+# the version control system). Doxygen will invoke the program by executing (via
+# popen()) the command <command> <input-file>, where <command> is the value of
+# the FILE_VERSION_FILTER tag, and <input-file> is the name of an input file
+# provided by doxygen. Whatever the program writes to standard output
+# is used as the file version. See the manual for examples.
+
+FILE_VERSION_FILTER =
+
+# The LAYOUT_FILE tag can be used to specify a layout file which will be parsed
+# by doxygen. The layout file controls the global structure of the generated
+# output files in an output format independent way. The create the layout file
+# that represents doxygen's defaults, run doxygen with the -l option.
+# You can optionally specify a file name after the option, if omitted
+# DoxygenLayout.xml will be used as the name of the layout file.
+
+LAYOUT_FILE =
+
+#---------------------------------------------------------------------------
+# configuration options related to warning and progress messages
+#---------------------------------------------------------------------------
+
+# The QUIET tag can be used to turn on/off the messages that are generated
+# by doxygen. Possible values are YES and NO. If left blank NO is used.
+
+QUIET = YES
+
+# The WARNINGS tag can be used to turn on/off the warning messages that are
+# generated by doxygen. Possible values are YES and NO. If left blank
+# NO is used.
+
+WARNINGS = YES
+
+# If WARN_IF_UNDOCUMENTED is set to YES, then doxygen will generate warnings
+# for undocumented members. If EXTRACT_ALL is set to YES then this flag will
+# automatically be disabled.
+
+WARN_IF_UNDOCUMENTED = NO
+
+# If WARN_IF_DOC_ERROR is set to YES, doxygen will generate warnings for
+# potential errors in the documentation, such as not documenting some
+# parameters in a documented function, or documenting parameters that
+# don't exist or using markup commands wrongly.
+
+WARN_IF_DOC_ERROR = YES
+
+# This WARN_NO_PARAMDOC option can be abled to get warnings for
+# functions that are documented, but have no documentation for their parameters
+# or return value. If set to NO (the default) doxygen will only warn about
+# wrong or incomplete parameter documentation, but not about the absence of
+# documentation.
+
+WARN_NO_PARAMDOC = YES
+
+# The WARN_FORMAT tag determines the format of the warning messages that
+# doxygen can produce. The string should contain the $file, $line, and $text
+# tags, which will be replaced by the file and line number from which the
+# warning originated and the warning text. Optionally the format may contain
+# $version, which will be replaced by the version of the file (if it could
+# be obtained via FILE_VERSION_FILTER)
+
+WARN_FORMAT = "$file:$line: $text"
+
+# The WARN_LOGFILE tag can be used to specify a file to which warning
+# and error messages should be written. If left blank the output is written
+# to stderr.
+
+WARN_LOGFILE =
+
+#---------------------------------------------------------------------------
+# configuration options related to the input files
+#---------------------------------------------------------------------------
+
+# The INPUT tag can be used to specify the files and/or directories that contain
+# documented source files. You may enter file names like "myfile.cpp" or
+# directories like "/usr/src/myproject". Separate the files or directories
+# with spaces.
+
+INPUT = .
+
+# This tag can be used to specify the character encoding of the source files
+# that doxygen parses. Internally doxygen uses the UTF-8 encoding, which is
+# also the default input encoding. Doxygen uses libiconv (or the iconv built
+# into libc) for the transcoding. See http://www.gnu.org/software/libiconv for
+# the list of possible encodings.
+
+INPUT_ENCODING = UTF-8
+
+# If the value of the INPUT tag contains directories, you can use the
+# FILE_PATTERNS tag to specify one or more wildcard pattern (like *.cpp
+# and *.h) to filter out the source-files in the directories. If left
+# blank the following patterns are tested:
+# *.c *.cc *.cxx *.cpp *.c++ *.java *.ii *.ixx *.ipp *.i++ *.inl *.h *.hh *.hxx
+# *.hpp *.h++ *.idl *.odl *.cs *.php *.php3 *.inc *.m *.mm *.py *.f90
+
+FILE_PATTERNS =
+
+# The RECURSIVE tag can be used to turn specify whether or not subdirectories
+# should be searched for input files as well. Possible values are YES and NO.
+# If left blank NO is used.
+
+RECURSIVE = YES
+
+# The EXCLUDE tag can be used to specify files and/or directories that should
+# excluded from the INPUT source files. This way you can easily exclude a
+# subdirectory from a directory tree whose root is specified with the INPUT tag.
+
+EXCLUDE = ./build \
+ ./compat \
+ ./contrib \
+ util/configparser.c \
+ util/configparser.h \
+ util/configlexer.c \
+ util/locks.h \
+ pythonmod/doc \
+ pythonmod/examples \
+ pythonmod/unboundmodule.py \
+ pythonmod/interface.h \
+ pythonmod/ubmodule-msg.py \
+ pythonmod/ubmodule-tst.py \
+ unboundmodule.py \
+ libunbound/python/unbound.py \
+ libunbound/python/libunbound_wrap.c \
+ libunbound/python/doc \
+ libunbound/python/examples \
+ ./ldns-src \
+ README.md \
+ doc/control_proto_spec.txt \
+ doc/requirements.txt
+
+# The EXCLUDE_SYMLINKS tag can be used select whether or not files or
+# directories that are symbolic links (a Unix filesystem feature) are excluded
+# from the input.
+
+EXCLUDE_SYMLINKS = NO
+
+# If the value of the INPUT tag contains directories, you can use the
+# EXCLUDE_PATTERNS tag to specify one or more wildcard patterns to exclude
+# certain files from those directories. Note that the wildcards are matched
+# against the file with absolute path, so to exclude all test directories
+# for example use the pattern */test/*
+
+EXCLUDE_PATTERNS =
+
+# The EXCLUDE_SYMBOLS tag can be used to specify one or more symbol names
+# (namespaces, classes, functions, etc.) that should be excluded from the
+# output. The symbol name can be a fully qualified name, a word, or if the
+# wildcard * is used, a substring. Examples: ANamespace, AClass,
+# AClass::ANamespace, ANamespace::*Test
+
+EXCLUDE_SYMBOLS =
+
+# The EXAMPLE_PATH tag can be used to specify one or more files or
+# directories that contain example code fragments that are included (see
+# the \include command).
+
+EXAMPLE_PATH =
+
+# If the value of the EXAMPLE_PATH tag contains directories, you can use the
+# EXAMPLE_PATTERNS tag to specify one or more wildcard pattern (like *.cpp
+# and *.h) to filter out the source-files in the directories. If left
+# blank all files are included.
+
+EXAMPLE_PATTERNS =
+
+# If the EXAMPLE_RECURSIVE tag is set to YES then subdirectories will be
+# searched for input files to be used with the \include or \dontinclude
+# commands irrespective of the value of the RECURSIVE tag.
+# Possible values are YES and NO. If left blank NO is used.
+
+EXAMPLE_RECURSIVE = NO
+
+# The IMAGE_PATH tag can be used to specify one or more files or
+# directories that contain image that are included in the documentation (see
+# the \image command).
+
+IMAGE_PATH =
+
+# The INPUT_FILTER tag can be used to specify a program that doxygen should
+# invoke to filter for each input file. Doxygen will invoke the filter program
+# by executing (via popen()) the command <filter> <input-file>, where <filter>
+# is the value of the INPUT_FILTER tag, and <input-file> is the name of an
+# input file. Doxygen will then use the output that the filter program writes
+# to standard output.
+# If FILTER_PATTERNS is specified, this tag will be
+# ignored.
+
+INPUT_FILTER =
+
+# The FILTER_PATTERNS tag can be used to specify filters on a per file pattern
+# basis.
+# Doxygen will compare the file name with each pattern and apply the
+# filter if there is a match.
+# The filters are a list of the form:
+# pattern=filter (like *.cpp=my_cpp_filter). See INPUT_FILTER for further
+# info on how filters are used. If FILTER_PATTERNS is empty, INPUT_FILTER
+# is applied to all files.
+
+FILTER_PATTERNS =
+
+# If the FILTER_SOURCE_FILES tag is set to YES, the input filter (if set using
+# INPUT_FILTER) will be used to filter the input files when producing source
+# files to browse (i.e. when SOURCE_BROWSER is set to YES).
+
+FILTER_SOURCE_FILES = NO
+
+#---------------------------------------------------------------------------
+# configuration options related to source browsing
+#---------------------------------------------------------------------------
+
+# If the SOURCE_BROWSER tag is set to YES then a list of source files will
+# be generated. Documented entities will be cross-referenced with these sources.
+# Note: To get rid of all source code in the generated output, make sure also
+# VERBATIM_HEADERS is set to NO.
+
+SOURCE_BROWSER = NO
+
+# Setting the INLINE_SOURCES tag to YES will include the body
+# of functions and classes directly in the documentation.
+
+INLINE_SOURCES = NO
+
+# Setting the STRIP_CODE_COMMENTS tag to YES (the default) will instruct
+# doxygen to hide any special comment blocks from generated source code
+# fragments. Normal C and C++ comments will always remain visible.
+
+STRIP_CODE_COMMENTS = YES
+
+# If the REFERENCED_BY_RELATION tag is set to YES
+# then for each documented function all documented
+# functions referencing it will be listed.
+
+REFERENCED_BY_RELATION = YES
+
+# If the REFERENCES_RELATION tag is set to YES
+# then for each documented function all documented entities
+# called/used by that function will be listed.
+
+REFERENCES_RELATION = YES
+
+# If the REFERENCES_LINK_SOURCE tag is set to YES (the default)
+# and SOURCE_BROWSER tag is set to YES, then the hyperlinks from
+# functions in REFERENCES_RELATION and REFERENCED_BY_RELATION lists will
+# link to the source code.
+# Otherwise they will link to the documentation.
+
+REFERENCES_LINK_SOURCE = YES
+
+# If the USE_HTAGS tag is set to YES then the references to source code
+# will point to the HTML generated by the htags(1) tool instead of doxygen
+# built-in source browser. The htags tool is part of GNU's global source
+# tagging system (see http://www.gnu.org/software/global/global.html). You
+# will need version 4.8.6 or higher.
+
+USE_HTAGS = NO
+
+# If the VERBATIM_HEADERS tag is set to YES (the default) then Doxygen
+# will generate a verbatim copy of the header file for each class for
+# which an include is specified. Set to NO to disable this.
+
+VERBATIM_HEADERS = NO
+
+#---------------------------------------------------------------------------
+# configuration options related to the alphabetical class index
+#---------------------------------------------------------------------------
+
+# If the ALPHABETICAL_INDEX tag is set to YES, an alphabetical index
+# of all compounds will be generated. Enable this if the project
+# contains a lot of classes, structs, unions or interfaces.
+
+ALPHABETICAL_INDEX = YES
+
+# If the alphabetical index is enabled (see ALPHABETICAL_INDEX) then
+# the COLS_IN_ALPHA_INDEX tag can be used to specify the number of columns
+# in which this list will be split (can be a number in the range [1..20])
+
+#COLS_IN_ALPHA_INDEX = 5
+
+# In case all classes in a project start with a common prefix, all
+# classes will be put under the same header in the alphabetical index.
+# The IGNORE_PREFIX tag can be used to specify one or more prefixes that
+# should be ignored while generating the index headers.
+
+IGNORE_PREFIX =
+
+#---------------------------------------------------------------------------
+# configuration options related to the HTML output
+#---------------------------------------------------------------------------
+
+# If the GENERATE_HTML tag is set to YES (the default) Doxygen will
+# generate HTML output.
+
+GENERATE_HTML = YES
+
+# The HTML_OUTPUT tag is used to specify where the HTML docs will be put.
+# If a relative path is entered the value of OUTPUT_DIRECTORY will be
+# put in front of it. If left blank `html' will be used as the default path.
+
+HTML_OUTPUT = html
+
+# The HTML_FILE_EXTENSION tag can be used to specify the file extension for
+# each generated HTML page (for example: .htm,.php,.asp). If it is left blank
+# doxygen will generate files with .html extension.
+
+HTML_FILE_EXTENSION = .html
+
+# The HTML_HEADER tag can be used to specify a personal HTML header for
+# each generated HTML page. If it is left blank doxygen will generate a
+# standard header.
+
+HTML_HEADER =
+
+# The HTML_FOOTER tag can be used to specify a personal HTML footer for
+# each generated HTML page. If it is left blank doxygen will generate a
+# standard footer.
+
+HTML_FOOTER =
+
+# If the HTML_TIMESTAMP tag is set to YES then the generated HTML
+# documentation will contain the timesstamp.
+
+HTML_TIMESTAMP = NO
+
+# The HTML_STYLESHEET tag can be used to specify a user-defined cascading
+# style sheet that is used by each HTML page. It can be used to
+# fine-tune the look of the HTML output. If the tag is left blank doxygen
+# will generate a default style sheet. Note that doxygen will try to copy
+# the style sheet file to the HTML output directory, so don't put your own
+# stylesheet in the HTML output directory as well, or it will be erased!
+
+HTML_STYLESHEET =
+
+# The HTML_COLORSTYLE_HUE tag controls the color of the HTML output.
+# Doxygen will adjust the colors in the stylesheet and background images
+# according to this color. Hue is specified as an angle on a colorwheel,
+# see http://en.wikipedia.org/wiki/Hue for more information.
+# For instance the value 0 represents red, 60 is yellow, 120 is green,
+# 180 is cyan, 240 is blue, 300 purple, and 360 is red again.
+# The allowed range is 0 to 359.
+
+#HTML_COLORSTYLE_HUE = 220
+
+# The HTML_COLORSTYLE_SAT tag controls the purity (or saturation) of
+# the colors in the HTML output. For a value of 0 the output will use
+# grayscales only. A value of 255 will produce the most vivid colors.
+
+#HTML_COLORSTYLE_SAT = 100
+
+# The HTML_COLORSTYLE_GAMMA tag controls the gamma correction applied to
+# the luminance component of the colors in the HTML output. Values below
+# 100 gradually make the output lighter, whereas values above 100 make
+# the output darker. The value divided by 100 is the actual gamma applied,
+# so 80 represents a gamma of 0.8, The value 220 represents a gamma of 2.2,
+# and 100 does not change the gamma.
+
+#HTML_COLORSTYLE_GAMMA = 80
+
+# If the HTML_TIMESTAMP tag is set to YES then the footer of each generated HTML
+# page will contain the date and time when the page was generated. Setting
+# this to NO can help when comparing the output of multiple runs.
+
+HTML_TIMESTAMP = YES
+
+# If the HTML_ALIGN_MEMBERS tag is set to YES, the members of classes,
+# files or namespaces will be aligned in HTML using tables. If set to
+# NO a bullet list will be used.
+
+#HTML_ALIGN_MEMBERS = YES
+
+# If the HTML_DYNAMIC_SECTIONS tag is set to YES then the generated HTML
+# documentation will contain sections that can be hidden and shown after the
+# page has loaded. For this to work a browser that supports
+# JavaScript and DHTML is required (for instance Mozilla 1.0+, Firefox
+# Netscape 6.0+, Internet explorer 5.0+, Konqueror, or Safari).
+
+HTML_DYNAMIC_SECTIONS = NO
+
+# If the GENERATE_DOCSET tag is set to YES, additional index files
+# will be generated that can be used as input for Apple's Xcode 3
+# integrated development environment, introduced with OSX 10.5 (Leopard).
+# To create a documentation set, doxygen will generate a Makefile in the
+# HTML output directory. Running make will produce the docset in that
+# directory and running "make install" will install the docset in
+# ~/Library/Developer/Shared/Documentation/DocSets so that Xcode will find
+# it at startup.
+# See http://developer.apple.com/tools/creatingdocsetswithdoxygen.html
+# for more information.
+
+GENERATE_DOCSET = NO
+
+# When GENERATE_DOCSET tag is set to YES, this tag determines the name of the
+# feed. A documentation feed provides an umbrella under which multiple
+# documentation sets from a single provider (such as a company or product suite)
+# can be grouped.
+
+DOCSET_FEEDNAME = "Doxygen generated docs"
+
+# When GENERATE_DOCSET tag is set to YES, this tag specifies a string that
+# should uniquely identify the documentation set bundle. This should be a
+# reverse domain-name style string, e.g. com.mycompany.MyDocSet. Doxygen
+# will append .docset to the name.
+
+DOCSET_BUNDLE_ID = org.doxygen.Project
+
+# When GENERATE_PUBLISHER_ID tag specifies a string that should uniquely identify
+# the documentation publisher. This should be a reverse domain-name style
+# string, e.g. com.mycompany.MyDocSet.documentation.
+
+#DOCSET_PUBLISHER_ID = org.doxygen.Publisher
+
+# The GENERATE_PUBLISHER_NAME tag identifies the documentation publisher.
+
+#DOCSET_PUBLISHER_NAME = Publisher
+
+# If the GENERATE_HTMLHELP tag is set to YES, additional index files
+# will be generated that can be used as input for tools like the
+# Microsoft HTML help workshop to generate a compiled HTML help file (.chm)
+# of the generated HTML documentation.
+
+GENERATE_HTMLHELP = NO
+
+# If the GENERATE_HTMLHELP tag is set to YES, the CHM_FILE tag can
+# be used to specify the file name of the resulting .chm file. You
+# can add a path in front of the file if the result should not be
+# written to the html output directory.
+
+CHM_FILE =
+
+# If the GENERATE_HTMLHELP tag is set to YES, the HHC_LOCATION tag can
+# be used to specify the location (absolute path including file name) of
+# the HTML help compiler (hhc.exe). If non-empty doxygen will try to run
+# the HTML help compiler on the generated index.hhp.
+
+HHC_LOCATION =
+
+# If the GENERATE_HTMLHELP tag is set to YES, the GENERATE_CHI flag
+# controls if a separate .chi index file is generated (YES) or that
+# it should be included in the master .chm file (NO).
+
+GENERATE_CHI = NO
+
+# If the GENERATE_HTMLHELP tag is set to YES, the CHM_INDEX_ENCODING
+# is used to encode HtmlHelp index (hhk), content (hhc) and project file
+# content.
+
+CHM_INDEX_ENCODING =
+
+# If the GENERATE_HTMLHELP tag is set to YES, the BINARY_TOC flag
+# controls whether a binary table of contents is generated (YES) or a
+# normal table of contents (NO) in the .chm file.
+
+BINARY_TOC = NO
+
+# The TOC_EXPAND flag can be set to YES to add extra items for group members
+# to the contents of the HTML help documentation and to the tree view.
+
+TOC_EXPAND = NO
+
+# If the GENERATE_QHP tag is set to YES and both QHP_NAMESPACE and
+# QHP_VIRTUAL_FOLDER are set, an additional index file will be generated
+# that can be used as input for Qt's qhelpgenerator to generate a
+# Qt Compressed Help (.qch) of the generated HTML documentation.
+
+GENERATE_QHP = NO
+
+# If the QHG_LOCATION tag is specified, the QCH_FILE tag can
+# be used to specify the file name of the resulting .qch file.
+# The path specified is relative to the HTML output folder.
+
+QCH_FILE =
+
+# The QHP_NAMESPACE tag specifies the namespace to use when generating
+# Qt Help Project output. For more information please see
+# http://doc.trolltech.com/qthelpproject.html#namespace
+
+QHP_NAMESPACE = org.doxygen.Project
+
+# The QHP_VIRTUAL_FOLDER tag specifies the namespace to use when generating
+# Qt Help Project output. For more information please see
+# http://doc.trolltech.com/qthelpproject.html#virtual-folders
+
+QHP_VIRTUAL_FOLDER = doc
+
+# If QHP_CUST_FILTER_NAME is set, it specifies the name of a custom filter to
+# add. For more information please see
+# http://doc.trolltech.com/qthelpproject.html#custom-filters
+
+QHP_CUST_FILTER_NAME =
+
+# The QHP_CUST_FILT_ATTRS tag specifies the list of the attributes of the
+# custom filter to add. For more information please see
+# <a href="http://doc.trolltech.com/qthelpproject.html#custom-filters">
+# Qt Help Project / Custom Filters</a>.
+
+QHP_CUST_FILTER_ATTRS =
+
+# The QHP_SECT_FILTER_ATTRS tag specifies the list of the attributes this
+# project's
+# filter section matches.
+# <a href="http://doc.trolltech.com/qthelpproject.html#filter-attributes">
+# Qt Help Project / Filter Attributes</a>.
+
+QHP_SECT_FILTER_ATTRS =
+
+# If the GENERATE_QHP tag is set to YES, the QHG_LOCATION tag can
+# be used to specify the location of Qt's qhelpgenerator.
+# If non-empty doxygen will try to run qhelpgenerator on the generated
+# .qhp file.
+
+QHG_LOCATION =
+
+# If the GENERATE_ECLIPSEHELP tag is set to YES, additional index files
+# will be generated, which together with the HTML files, form an Eclipse help
+# plugin. To install this plugin and make it available under the help contents
+# menu in Eclipse, the contents of the directory containing the HTML and XML
+# files needs to be copied into the plugins directory of eclipse. The name of
+# the directory within the plugins directory should be the same as
+# the ECLIPSE_DOC_ID value. After copying Eclipse needs to be restarted before
+# the help appears.
+
+GENERATE_ECLIPSEHELP = NO
+
+# A unique identifier for the eclipse help plugin. When installing the plugin
+# the directory name containing the HTML and XML files should also have
+# this name.
+
+ECLIPSE_DOC_ID = org.doxygen.Project
+
+# The DISABLE_INDEX tag can be used to turn on/off the condensed index at
+# top of each HTML page. The value NO (the default) enables the index and
+# the value YES disables it.
+
+DISABLE_INDEX = NO
+
+# This tag can be used to set the number of enum values (range [1..20])
+# that doxygen will group on one line in the generated HTML documentation.
+
+ENUM_VALUES_PER_LINE = 4
+
+# The GENERATE_TREEVIEW tag is used to specify whether a tree-like index
+# structure should be generated to display hierarchical information.
+# If the tag value is set to YES, a side panel will be generated
+# containing a tree-like index structure (just like the one that
+# is generated for HTML Help). For this to work a browser that supports
+# JavaScript, DHTML, CSS and frames is required (i.e. any modern browser).
+# Windows users are probably better off using the HTML help feature.
+
+GENERATE_TREEVIEW = NO
+
+# By enabling USE_INLINE_TREES, doxygen will generate the Groups, Directories,
+# and Class Hierarchy pages using a tree view instead of an ordered list.
+
+#USE_INLINE_TREES = NO
+
+# If the treeview is enabled (see GENERATE_TREEVIEW) then this tag can be
+# used to set the initial width (in pixels) of the frame in which the tree
+# is shown.
+
+TREEVIEW_WIDTH = 250
+
+# When the EXT_LINKS_IN_WINDOW option is set to YES doxygen will open
+# links to external symbols imported via tag files in a separate window.
+
+#EXT_LINKS_IN_WINDOW = NO
+
+# Use this tag to change the font size of Latex formulas included
+# as images in the HTML documentation. The default is 10. Note that
+# when you change the font size after a successful doxygen run you need
+# to manually remove any form_*.png images from the HTML output directory
+# to force them to be regenerated.
+
+FORMULA_FONTSIZE = 10
+
+# Use the FORMULA_TRANSPARENT tag to determine whether or not the images
+# generated for formulas are transparent PNGs. Transparent PNGs are
+# not supported properly for IE 6.0, but are supported on all modern browsers.
+# Note that when changing this option you need to delete any form_*.png files
+# in the HTML output before the changes have effect.
+
+#FORMULA_TRANSPARENT = YES
+
+# When the SEARCHENGINE tag is enabled doxygen will generate a search box
+# for the HTML output. The underlying search engine uses javascript
+# and DHTML and should work on any modern browser. Note that when using
+# HTML help (GENERATE_HTMLHELP), Qt help (GENERATE_QHP), or docsets
+# (GENERATE_DOCSET) there is already a search function so this one should
+# typically be disabled. For large projects the javascript based search engine
+# can be slow, then enabling SERVER_BASED_SEARCH may provide a better solution.
+
+SEARCHENGINE = NO
+
+# When the SERVER_BASED_SEARCH tag is enabled the search engine will be
+# implemented using a PHP enabled web server instead of at the web client
+# using Javascript. Doxygen will generate the search PHP script and index
+# file to put on the web server. The advantage of the server
+# based approach is that it scales better to large projects and allows
+# full text search. The disadvances is that it is more difficult to setup
+# and does not have live searching capabilities.
+
+SERVER_BASED_SEARCH = NO
+
+#---------------------------------------------------------------------------
+# configuration options related to the LaTeX output
+#---------------------------------------------------------------------------
+
+# If the GENERATE_LATEX tag is set to YES (the default) Doxygen will
+# generate Latex output.
+
+GENERATE_LATEX = NO
+
+# The LATEX_OUTPUT tag is used to specify where the LaTeX docs will be put.
+# If a relative path is entered the value of OUTPUT_DIRECTORY will be
+# put in front of it. If left blank `latex' will be used as the default path.
+
+LATEX_OUTPUT = latex
+
+# The LATEX_CMD_NAME tag can be used to specify the LaTeX command name to be
+# invoked. If left blank `latex' will be used as the default command name.
+# Note that when enabling USE_PDFLATEX this option is only used for
+# generating bitmaps for formulas in the HTML output, but not in the
+# Makefile that is written to the output directory.
+
+LATEX_CMD_NAME = latex
+
+# The MAKEINDEX_CMD_NAME tag can be used to specify the command name to
+# generate index for LaTeX. If left blank `makeindex' will be used as the
+# default command name.
+
+MAKEINDEX_CMD_NAME = makeindex
+
+# If the COMPACT_LATEX tag is set to YES Doxygen generates more compact
+# LaTeX documents. This may be useful for small projects and may help to
+# save some trees in general.
+
+COMPACT_LATEX = NO
+
+# The PAPER_TYPE tag can be used to set the paper type that is used
+# by the printer. Possible values are: a4, a4wide, letter, legal and
+# executive. If left blank a4wide will be used.
+
+#PAPER_TYPE = a4wide
+
+# The EXTRA_PACKAGES tag can be to specify one or more names of LaTeX
+# packages that should be included in the LaTeX output.
+
+EXTRA_PACKAGES =
+
+# The LATEX_HEADER tag can be used to specify a personal LaTeX header for
+# the generated latex document. The header should contain everything until
+# the first chapter. If it is left blank doxygen will generate a
+# standard header. Notice: only use this tag if you know what you are doing!
+
+LATEX_HEADER =
+
+# If the PDF_HYPERLINKS tag is set to YES, the LaTeX that is generated
+# is prepared for conversion to pdf (using ps2pdf). The pdf file will
+# contain links (just like the HTML output) instead of page references
+# This makes the output suitable for online browsing using a pdf viewer.
+
+PDF_HYPERLINKS = NO
+
+# If the USE_PDFLATEX tag is set to YES, pdflatex will be used instead of
+# plain latex in the generated Makefile. Set this option to YES to get a
+# higher quality PDF documentation.
+
+USE_PDFLATEX = NO
+
+# If the LATEX_BATCHMODE tag is set to YES, doxygen will add the \\batchmode.
+# command to the generated LaTeX files. This will instruct LaTeX to keep
+# running if errors occur, instead of asking the user for help.
+# This option is also used when generating formulas in HTML.
+
+LATEX_BATCHMODE = NO
+
+# If LATEX_HIDE_INDICES is set to YES then doxygen will not
+# include the index chapters (such as File Index, Compound Index, etc.)
+# in the output.
+
+LATEX_HIDE_INDICES = NO
+
+# If LATEX_SOURCE_CODE is set to YES then doxygen will include
+# source code with syntax highlighting in the LaTeX output.
+# Note that which sources are shown also depends on other settings
+# such as SOURCE_BROWSER.
+
+LATEX_SOURCE_CODE = NO
+
+#---------------------------------------------------------------------------
+# configuration options related to the RTF output
+#---------------------------------------------------------------------------
+
+# If the GENERATE_RTF tag is set to YES Doxygen will generate RTF output
+# The RTF output is optimized for Word 97 and may not look very pretty with
+# other RTF readers or editors.
+
+GENERATE_RTF = NO
+
+# The RTF_OUTPUT tag is used to specify where the RTF docs will be put.
+# If a relative path is entered the value of OUTPUT_DIRECTORY will be
+# put in front of it. If left blank `rtf' will be used as the default path.
+
+RTF_OUTPUT = rtf
+
+# If the COMPACT_RTF tag is set to YES Doxygen generates more compact
+# RTF documents. This may be useful for small projects and may help to
+# save some trees in general.
+
+COMPACT_RTF = NO
+
+# If the RTF_HYPERLINKS tag is set to YES, the RTF that is generated
+# will contain hyperlink fields. The RTF file will
+# contain links (just like the HTML output) instead of page references.
+# This makes the output suitable for online browsing using WORD or other
+# programs which support those fields.
+# Note: wordpad (write) and others do not support links.
+
+RTF_HYPERLINKS = NO
+
+# Load stylesheet definitions from file. Syntax is similar to doxygen's
+# config file, i.e. a series of assignments. You only have to provide
+# replacements, missing definitions are set to their default value.
+
+RTF_STYLESHEET_FILE =
+
+# Set optional variables used in the generation of an rtf document.
+# Syntax is similar to doxygen's config file.
+
+RTF_EXTENSIONS_FILE =
+
+#---------------------------------------------------------------------------
+# configuration options related to the man page output
+#---------------------------------------------------------------------------
+
+# If the GENERATE_MAN tag is set to YES (the default) Doxygen will
+# generate man pages
+
+GENERATE_MAN = NO
+
+# The MAN_OUTPUT tag is used to specify where the man pages will be put.
+# If a relative path is entered the value of OUTPUT_DIRECTORY will be
+# put in front of it. If left blank `man' will be used as the default path.
+
+MAN_OUTPUT = man
+
+# The MAN_EXTENSION tag determines the extension that is added to
+# the generated man pages (default is the subroutine's section .3)
+
+MAN_EXTENSION = .3
+
+# If the MAN_LINKS tag is set to YES and Doxygen generates man output,
+# then it will generate one additional man file for each entity
+# documented in the real man page(s). These additional files
+# only source the real man page, but without them the man command
+# would be unable to find the correct page. The default is NO.
+
+MAN_LINKS = NO
+
+#---------------------------------------------------------------------------
+# configuration options related to the XML output
+#---------------------------------------------------------------------------
+
+# If the GENERATE_XML tag is set to YES Doxygen will
+# generate an XML file that captures the structure of
+# the code including all documentation.
+
+GENERATE_XML = YES
+
+# The XML_OUTPUT tag is used to specify where the XML pages will be put.
+# If a relative path is entered the value of OUTPUT_DIRECTORY will be
+# put in front of it. If left blank `xml' will be used as the default path.
+
+XML_OUTPUT = xml
+
+# The XML_SCHEMA tag can be used to specify an XML schema,
+# which can be used by a validating XML parser to check the
+# syntax of the XML files.
+
+#XML_SCHEMA =
+
+# The XML_DTD tag can be used to specify an XML DTD,
+# which can be used by a validating XML parser to check the
+# syntax of the XML files.
+
+#XML_DTD =
+
+# If the XML_PROGRAMLISTING tag is set to YES Doxygen will
+# dump the program listings (including syntax highlighting
+# and cross-referencing information) to the XML output. Note that
+# enabling this will significantly increase the size of the XML output.
+
+XML_PROGRAMLISTING = YES
+
+#---------------------------------------------------------------------------
+# configuration options for the AutoGen Definitions output
+#---------------------------------------------------------------------------
+
+# If the GENERATE_AUTOGEN_DEF tag is set to YES Doxygen will
+# generate an AutoGen Definitions (see autogen.sf.net) file
+# that captures the structure of the code including all
+# documentation. Note that this feature is still experimental
+# and incomplete at the moment.
+
+GENERATE_AUTOGEN_DEF = NO
+
+#---------------------------------------------------------------------------
+# configuration options related to the Perl module output
+#---------------------------------------------------------------------------
+
+# If the GENERATE_PERLMOD tag is set to YES Doxygen will
+# generate a Perl module file that captures the structure of
+# the code including all documentation. Note that this
+# feature is still experimental and incomplete at the
+# moment.
+
+GENERATE_PERLMOD = NO
+
+# If the PERLMOD_LATEX tag is set to YES Doxygen will generate
+# the necessary Makefile rules, Perl scripts and LaTeX code to be able
+# to generate PDF and DVI output from the Perl module output.
+
+PERLMOD_LATEX = NO
+
+# If the PERLMOD_PRETTY tag is set to YES the Perl module output will be
+# nicely formatted so it can be parsed by a human reader.
+# This is useful
+# if you want to understand what is going on.
+# On the other hand, if this
+# tag is set to NO the size of the Perl module output will be much smaller
+# and Perl will parse it just the same.
+
+PERLMOD_PRETTY = YES
+
+# The names of the make variables in the generated doxyrules.make file
+# are prefixed with the string contained in PERLMOD_MAKEVAR_PREFIX.
+# This is useful so different doxyrules.make files included by the same
+# Makefile don't overwrite each other's variables.
+
+PERLMOD_MAKEVAR_PREFIX =
+
+#---------------------------------------------------------------------------
+# Configuration options related to the preprocessor
+#---------------------------------------------------------------------------
+
+# If the ENABLE_PREPROCESSING tag is set to YES (the default) Doxygen will
+# evaluate all C-preprocessor directives found in the sources and include
+# files.
+
+ENABLE_PREPROCESSING = YES
+
+# If the MACRO_EXPANSION tag is set to YES Doxygen will expand all macro
+# names in the source code. If set to NO (the default) only conditional
+# compilation will be performed. Macro expansion can be done in a controlled
+# way by setting EXPAND_ONLY_PREDEF to YES.
+
+MACRO_EXPANSION = YES
+
+# If the EXPAND_ONLY_PREDEF and MACRO_EXPANSION tags are both set to YES
+# then the macro expansion is limited to the macros specified with the
+# PREDEFINED and EXPAND_AS_DEFINED tags.
+
+EXPAND_ONLY_PREDEF = YES
+
+# If the SEARCH_INCLUDES tag is set to YES (the default) the includes files
+# in the INCLUDE_PATH (see below) will be search if a #include is found.
+
+SEARCH_INCLUDES = YES
+
+# The INCLUDE_PATH tag can be used to specify one or more directories that
+# contain include files that are not input files but should be processed by
+# the preprocessor.
+
+INCLUDE_PATH =
+
+# You can use the INCLUDE_FILE_PATTERNS tag to specify one or more wildcard
+# patterns (like *.h and *.hpp) to filter out the header-files in the
+# directories. If left blank, the patterns specified with FILE_PATTERNS will
+# be used.
+
+INCLUDE_FILE_PATTERNS = *.h
+
+# The PREDEFINED tag can be used to specify one or more macro names that
+# are defined before the preprocessor is started (similar to the -D option of
+# gcc). The argument of the tag is a list of macros of the form: name
+# or name=definition (no spaces). If the definition and the = are
+# omitted =1 is assumed. To prevent a macro definition from being
+# undefined via #undef or recursively expanded use the := operator
+# instead of the = operator.
+
+PREDEFINED = DOXYGEN
+
+# If the MACRO_EXPANSION and EXPAND_ONLY_PREDEF tags are set to YES then
+# this tag can be used to specify a list of macro names that should be expanded.
+# The macro definition that is found in the sources will be used.
+# Use the PREDEFINED tag if you want to use a different macro definition.
+
+EXPAND_AS_DEFINED = ATTR_UNUSED
+
+# If the SKIP_FUNCTION_MACROS tag is set to YES (the default) then
+# doxygen's preprocessor will remove all function-like macros that are alone
+# on a line, have an all uppercase name, and do not end with a semicolon. Such
+# function macros are typically used for boiler-plate code, and will confuse
+# the parser if not removed.
+
+SKIP_FUNCTION_MACROS = YES
+
+#---------------------------------------------------------------------------
+# Configuration::additions related to external references
+#---------------------------------------------------------------------------
+
+# The TAGFILES option can be used to specify one or more tagfiles.
+# Optionally an initial location of the external documentation
+# can be added for each tagfile. The format of a tag file without
+# this location is as follows:
+#
+# TAGFILES = file1 file2 ...
+# Adding location for the tag files is done as follows:
+#
+# TAGFILES = file1=loc1 "file2 = loc2" ...
+# where "loc1" and "loc2" can be relative or absolute paths or
+# URLs. If a location is present for each tag, the installdox tool
+# does not have to be run to correct the links.
+# Note that each tag file must have a unique name
+# (where the name does NOT include the path)
+# If a tag file is not located in the directory in which doxygen
+# is run, you must also specify the path to the tagfile here.
+
+TAGFILES =
+
+# When a file name is specified after GENERATE_TAGFILE, doxygen will create
+# a tag file that is based on the input files it reads.
+
+GENERATE_TAGFILE =
+
+# If the ALLEXTERNALS tag is set to YES all external classes will be listed
+# in the class index. If set to NO only the inherited external classes
+# will be listed.
+
+ALLEXTERNALS = NO
+
+# If the EXTERNAL_GROUPS tag is set to YES all external groups will be listed
+# in the modules index. If set to NO, only the current project's groups will
+# be listed.
+
+EXTERNAL_GROUPS = YES
+
+# The PERL_PATH should be the absolute path and name of the perl script
+# interpreter (i.e. the result of `which perl').
+
+#PERL_PATH = /usr/bin/perl
+
+#---------------------------------------------------------------------------
+# Configuration options related to the dot tool
+#---------------------------------------------------------------------------
+
+# If the CLASS_DIAGRAMS tag is set to YES (the default) Doxygen will
+# generate a inheritance diagram (in HTML, RTF and LaTeX) for classes with base
+# or super classes. Setting the tag to NO turns the diagrams off. Note that
+# this option is superseded by the HAVE_DOT option below. This is only a
+# fallback. It is recommended to install and use dot, since it yields more
+# powerful graphs.
+
+CLASS_DIAGRAMS = YES
+
+# You can define message sequence charts within doxygen comments using the \msc
+# command. Doxygen will then run the mscgen tool (see
+# http://www.mcternan.me.uk/mscgen/) to produce the chart and insert it in the
+# documentation. The MSCGEN_PATH tag allows you to specify the directory where
+# the mscgen tool resides. If left empty the tool is assumed to be found in the
+# default search path.
+
+#MSCGEN_PATH =
+
+# If set to YES, the inheritance and collaboration graphs will hide
+# inheritance and usage relations if the target is undocumented
+# or is not a class.
+
+HIDE_UNDOC_RELATIONS = YES
+
+# If you set the HAVE_DOT tag to YES then doxygen will assume the dot tool is
+# available from the path. This tool is part of Graphviz, a graph visualization
+# toolkit from AT&T and Lucent Bell Labs. The other options in this section
+# have no effect if this option is set to NO (the default)
+
+HAVE_DOT = NO
+
+# The DOT_NUM_THREADS specifies the number of dot invocations doxygen is
+# allowed to run in parallel. When set to 0 (the default) doxygen will
+# base this on the number of processors available in the system. You can set it
+# explicitly to a value larger than 0 to get control over the balance
+# between CPU load and processing speed.
+
+#DOT_NUM_THREADS = 0
+
+# By default doxygen will write a font called FreeSans.ttf to the output
+# directory and reference it in all dot files that doxygen generates. This
+# font does not include all possible unicode characters however, so when you need
+# these (or just want a differently looking font) you can specify the font name
+# using DOT_FONTNAME. You need need to make sure dot is able to find the font,
+# which can be done by putting it in a standard location or by setting the
+# DOTFONTPATH environment variable or by setting DOT_FONTPATH to the directory
+# containing the font.
+
+#DOT_FONTNAME = FreeSans.ttf
+
+# The DOT_FONTSIZE tag can be used to set the size of the font of dot graphs.
+# The default size is 10pt.
+
+DOT_FONTSIZE = 10
+
+# By default doxygen will tell dot to use the output directory to look for the
+# FreeSans.ttf font (which doxygen will put there itself). If you specify a
+# different font using DOT_FONTNAME you can set the path where dot
+# can find it using this tag.
+
+DOT_FONTPATH =
+
+# If the CLASS_GRAPH and HAVE_DOT tags are set to YES then doxygen
+# will generate a graph for each documented class showing the direct and
+# indirect inheritance relations. Setting this tag to YES will force the
+# the CLASS_DIAGRAMS tag to NO.
+
+CLASS_GRAPH = YES
+
+# If the COLLABORATION_GRAPH and HAVE_DOT tags are set to YES then doxygen
+# will generate a graph for each documented class showing the direct and
+# indirect implementation dependencies (inheritance, containment, and
+# class references variables) of the class with other documented classes.
+
+COLLABORATION_GRAPH = YES
+
+# If the GROUP_GRAPHS and HAVE_DOT tags are set to YES then doxygen
+# will generate a graph for groups, showing the direct groups dependencies
+
+GROUP_GRAPHS = YES
+
+# If the UML_LOOK tag is set to YES doxygen will generate inheritance and
+# collaboration diagrams in a style similar to the OMG's Unified Modeling
+# Language.
+
+UML_LOOK = NO
+
+# If set to YES, the inheritance and collaboration graphs will show the
+# relations between templates and their instances.
+
+TEMPLATE_RELATIONS = NO
+
+# If the ENABLE_PREPROCESSING, SEARCH_INCLUDES, INCLUDE_GRAPH, and HAVE_DOT
+# tags are set to YES then doxygen will generate a graph for each documented
+# file showing the direct and indirect include dependencies of the file with
+# other documented files.
+
+INCLUDE_GRAPH = YES
+
+# If the ENABLE_PREPROCESSING, SEARCH_INCLUDES, INCLUDED_BY_GRAPH, and
+# HAVE_DOT tags are set to YES then doxygen will generate a graph for each
+# documented header file showing the documented files that directly or
+# indirectly include this file.
+
+INCLUDED_BY_GRAPH = YES
+
+# If the CALL_GRAPH and HAVE_DOT options are set to YES then
+# doxygen will generate a call dependency graph for every global function
+# or class method. Note that enabling this option will significantly increase
+# the time of a run. So in most cases it will be better to enable call graphs
+# for selected functions only using the \callgraph command.
+
+CALL_GRAPH = NO
+
+# If the CALLER_GRAPH and HAVE_DOT tags are set to YES then
+# doxygen will generate a caller dependency graph for every global function
+# or class method. Note that enabling this option will significantly increase
+# the time of a run. So in most cases it will be better to enable caller
+# graphs for selected functions only using the \callergraph command.
+
+CALLER_GRAPH = NO
+
+# If the GRAPHICAL_HIERARCHY and HAVE_DOT tags are set to YES then doxygen
+# will graphical hierarchy of all classes instead of a textual one.
+
+GRAPHICAL_HIERARCHY = YES
+
+# If the DIRECTORY_GRAPH, SHOW_DIRECTORIES and HAVE_DOT tags are set to YES
+# then doxygen will show the dependencies a directory has on other directories
+# in a graphical way. The dependency relations are determined by the #include
+# relations between the files in the directories.
+
+DIRECTORY_GRAPH = YES
+
+# The DOT_IMAGE_FORMAT tag can be used to set the image format of the images
+# generated by dot. Possible values are png, jpg, or gif
+# If left blank png will be used.
+
+DOT_IMAGE_FORMAT = png
+
+# The tag DOT_PATH can be used to specify the path where the dot tool can be
+# found. If left blank, it is assumed the dot tool can be found in the path.
+
+DOT_PATH =
+
+# The DOTFILE_DIRS tag can be used to specify one or more directories that
+# contain dot files that are included in the documentation (see the
+# \dotfile command).
+
+DOTFILE_DIRS =
+
+# The DOT_GRAPH_MAX_NODES tag can be used to set the maximum number of
+# nodes that will be shown in the graph. If the number of nodes in a graph
+# becomes larger than this value, doxygen will truncate the graph, which is
+# visualized by representing a node as a red box. Note that doxygen if the
+# number of direct children of the root node in a graph is already larger than
+# DOT_GRAPH_MAX_NODES then the graph will not be shown at all. Also note
+# that the size of a graph can be further restricted by MAX_DOT_GRAPH_DEPTH.
+
+DOT_GRAPH_MAX_NODES = 50
+
+# The MAX_DOT_GRAPH_DEPTH tag can be used to set the maximum depth of the
+# graphs generated by dot. A depth value of 3 means that only nodes reachable
+# from the root by following a path via at most 3 edges will be shown. Nodes
+# that lay further from the root node will be omitted. Note that setting this
+# option to 1 or 2 may greatly reduce the computation time needed for large
+# code bases. Also note that the size of a graph can be further restricted by
+# DOT_GRAPH_MAX_NODES. Using a depth of 0 means no depth restriction.
+
+MAX_DOT_GRAPH_DEPTH = 0
+
+# Set the DOT_TRANSPARENT tag to YES to generate images with a transparent
+# background. This is disabled by default, because dot on Windows does not
+# seem to support this out of the box. Warning: Depending on the platform used,
+# enabling this option may lead to badly anti-aliased labels on the edges of
+# a graph (i.e. they become hard to read).
+
+DOT_TRANSPARENT = NO
+
+# Set the DOT_MULTI_TARGETS tag to YES allow dot to generate multiple output
+# files in one run (i.e. multiple -o and -T options on the command line). This
+# makes dot run faster, but since only newer versions of dot (>1.8.10)
+# support this, this feature is disabled by default.
+
+DOT_MULTI_TARGETS = NO
+
+# If the GENERATE_LEGEND tag is set to YES (the default) Doxygen will
+# generate a legend page explaining the meaning of the various boxes and
+# arrows in the dot generated graphs.
+
+GENERATE_LEGEND = YES
+
+# If the DOT_CLEANUP tag is set to YES (the default) Doxygen will
+# remove the intermediate dot files that are used to generate
+# the various graphs.
+
+DOT_CLEANUP = YES
diff --git a/contrib/wpa/CONTRIBUTIONS b/contrib/wpa/CONTRIBUTIONS
index b2064dc83443..1b4caf7ac811 100644
--- a/contrib/wpa/CONTRIBUTIONS
+++ b/contrib/wpa/CONTRIBUTIONS
@@ -143,7 +143,7 @@ The license terms used for hostap.git files
Modified BSD license (no advertisement clause):
-Copyright (c) 2002-2022, Jouni Malinen <j@w1.fi> and contributors
+Copyright (c) 2002-2021, Jouni Malinen <j@w1.fi> and contributors
All Rights Reserved.
Redistribution and use in source and binary forms, with or without
diff --git a/contrib/wpa/COPYING b/contrib/wpa/COPYING
index 7ca30301e28b..5d0115c9ca6f 100644
--- a/contrib/wpa/COPYING
+++ b/contrib/wpa/COPYING
@@ -1,7 +1,7 @@
wpa_supplicant and hostapd
--------------------------
-Copyright (c) 2002-2022, Jouni Malinen <j@w1.fi> and contributors
+Copyright (c) 2002-2019, Jouni Malinen <j@w1.fi> and contributors
All Rights Reserved.
diff --git a/contrib/wpa/README b/contrib/wpa/README
index 1470c4f23582..a9f806967bf9 100644
--- a/contrib/wpa/README
+++ b/contrib/wpa/README
@@ -1,7 +1,7 @@
wpa_supplicant and hostapd
--------------------------
-Copyright (c) 2002-2022, Jouni Malinen <j@w1.fi> and contributors
+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
diff --git a/contrib/wpa/hostapd/ChangeLog b/contrib/wpa/hostapd/ChangeLog
index 279298e4d4d4..34a8a081879d 100644
--- a/contrib/wpa/hostapd/ChangeLog
+++ b/contrib/wpa/hostapd/ChangeLog
@@ -1,48 +1,5 @@
ChangeLog for hostapd
-2022-01-16 - v2.10
- * SAE changes
- - improved protection against side channel attacks
- [https://w1.fi/security/2022-1/]
- - added option send SAE Confirm immediately (sae_config_immediate=1)
- after SAE Commit
- - added support for the hash-to-element mechanism (sae_pwe=1 or
- sae_pwe=2)
- - fixed PMKSA caching with OKC
- - added support for SAE-PK
- * EAP-pwd changes
- - improved protection against side channel attacks
- [https://w1.fi/security/2022-1/]
- * fixed WPS UPnP SUBSCRIBE handling of invalid operations
- [https://w1.fi/security/2020-1/]
- * fixed PMF disconnection protection bypass
- [https://w1.fi/security/2019-7/]
- * added support for using OpenSSL 3.0
- * fixed various issues in experimental support for EAP-TEAP server
- * added configuration (max_auth_rounds, max_auth_rounds_short) to
- increase the maximum number of EAP message exchanges (mainly to
- support cases with very large certificates) for the EAP server
- * added support for DPP release 2 (Wi-Fi Device Provisioning Protocol)
- * extended HE (IEEE 802.11ax) support, including 6 GHz support
- * removed obsolete IAPP functionality
- * fixed EAP-FAST server with TLS GCM/CCM ciphers
- * dropped support for libnl 1.1
- * added support for nl80211 control port for EAPOL frame TX/RX
- * fixed OWE key derivation with groups 20 and 21; this breaks backwards
- compatibility for these groups while the default group 19 remains
- backwards compatible; owe_ptk_workaround=1 can be used to enabled a
- a workaround for the group 20/21 backwards compatibility
- * added support for Beacon protection
- * added support for Extended Key ID for pairwise keys
- * removed WEP support from the default build (CONFIG_WEP=y can be used
- to enable it, if really needed)
- * added a build option to remove TKIP support (CONFIG_NO_TKIP=y)
- * added support for Transition Disable mechanism to allow the AP to
- automatically disable transition mode to improve security
- * added support for PASN
- * added EAP-TLS server support for TLS 1.3 (disabled by default for now)
- * a large number of other fixes, cleanup, and extensions
-
2019-08-07 - v2.9
* SAE changes
- disable use of groups using Brainpool curves
diff --git a/contrib/wpa/hostapd/README b/contrib/wpa/hostapd/README
index 739c964d44d8..1f30d7ea39fa 100644
--- a/contrib/wpa/hostapd/README
+++ b/contrib/wpa/hostapd/README
@@ -2,7 +2,7 @@ hostapd - user space IEEE 802.11 AP and IEEE 802.1X/WPA/WPA2/EAP
Authenticator and RADIUS authentication server
================================================================
-Copyright (c) 2002-2022, Jouni Malinen <j@w1.fi> and contributors
+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
diff --git a/contrib/wpa/hostapd/hostapd_cli.c b/contrib/wpa/hostapd/hostapd_cli.c
index 2609121116b5..0e7fdd6bccfb 100644
--- a/contrib/wpa/hostapd/hostapd_cli.c
+++ b/contrib/wpa/hostapd/hostapd_cli.c
@@ -1,6 +1,6 @@
/*
* hostapd - command line interface for hostapd daemon
- * Copyright (c) 2004-2022, Jouni Malinen <j@w1.fi>
+ * Copyright (c) 2004-2019, Jouni Malinen <j@w1.fi>
*
* This software may be distributed under the terms of the BSD license.
* See README for more details.
@@ -21,7 +21,7 @@
static const char *const hostapd_cli_version =
"hostapd_cli v" VERSION_STR "\n"
-"Copyright (c) 2004-2022, Jouni Malinen <j@w1.fi> and contributors";
+"Copyright (c) 2004-2019, Jouni Malinen <j@w1.fi> and contributors";
static struct wpa_ctrl *ctrl_conn;
static int hostapd_cli_quit = 0;
diff --git a/contrib/wpa/hostapd/main.c b/contrib/wpa/hostapd/main.c
index c9ec38d19f88..4f2d1f21659e 100644
--- a/contrib/wpa/hostapd/main.c
+++ b/contrib/wpa/hostapd/main.c
@@ -1,6 +1,6 @@
/*
* hostapd / main()
- * Copyright (c) 2002-2022, Jouni Malinen <j@w1.fi>
+ * 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.
@@ -454,7 +454,7 @@ static void show_version(void)
"hostapd v%s\n"
"User space daemon for IEEE 802.11 AP management,\n"
"IEEE 802.1X/WPA/WPA2/EAP/RADIUS Authenticator\n"
- "Copyright (c) 2002-2022, Jouni Malinen <j@w1.fi> "
+ "Copyright (c) 2002-2019, Jouni Malinen <j@w1.fi> "
"and contributors\n",
VERSION_STR);
}
diff --git a/contrib/wpa/hs20/server/Makefile b/contrib/wpa/hs20/server/Makefile
new file mode 100644
index 000000000000..0cab6d6b010a
--- /dev/null
+++ b/contrib/wpa/hs20/server/Makefile
@@ -0,0 +1,42 @@
+ALL=hs20_spp_server
+
+include ../../src/build.rules
+
+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
+
+_OBJS_VAR := OBJS
+include ../../src/objs.mk
+hs20_spp_server: $(OBJS)
+ $(LDO) $(LDFLAGS) -o hs20_spp_server $(OBJS) $(LIBS)
+
+clean: common-clean
+ rm -f core *~
diff --git a/contrib/wpa/hs20/server/ca/clean.sh b/contrib/wpa/hs20/server/ca/clean.sh
new file mode 100755
index 000000000000..c72dcbda45e9
--- /dev/null
+++ b/contrib/wpa/hs20/server/ca/clean.sh
@@ -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
diff --git a/contrib/wpa/hs20/server/ca/est-csrattrs.cnf b/contrib/wpa/hs20/server/ca/est-csrattrs.cnf
new file mode 100644
index 000000000000..b50ea00d0b77
--- /dev/null
+++ b/contrib/wpa/hs20/server/ca/est-csrattrs.cnf
@@ -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
diff --git a/contrib/wpa/hs20/server/ca/est-csrattrs.sh b/contrib/wpa/hs20/server/ca/est-csrattrs.sh
new file mode 100755
index 000000000000..0b73a0408284
--- /dev/null
+++ b/contrib/wpa/hs20/server/ca/est-csrattrs.sh
@@ -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
diff --git a/contrib/wpa/hs20/server/ca/hs20.oid b/contrib/wpa/hs20/server/ca/hs20.oid
new file mode 100644
index 000000000000..a829ff29bf44
--- /dev/null
+++ b/contrib/wpa/hs20/server/ca/hs20.oid
@@ -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
diff --git a/contrib/wpa/hs20/server/ca/ocsp-req.sh b/contrib/wpa/hs20/server/ca/ocsp-req.sh
new file mode 100755
index 000000000000..931a20696d02
--- /dev/null
+++ b/contrib/wpa/hs20/server/ca/ocsp-req.sh
@@ -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
diff --git a/contrib/wpa/hs20/server/ca/ocsp-responder-ica.sh b/contrib/wpa/hs20/server/ca/ocsp-responder-ica.sh
new file mode 100755
index 000000000000..116c6e1c3d01
--- /dev/null
+++ b/contrib/wpa/hs20/server/ca/ocsp-responder-ica.sh
@@ -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
diff --git a/contrib/wpa/hs20/server/ca/ocsp-responder.sh b/contrib/wpa/hs20/server/ca/ocsp-responder.sh
new file mode 100755
index 000000000000..620947d01af0
--- /dev/null
+++ b/contrib/wpa/hs20/server/ca/ocsp-responder.sh
@@ -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 -ignore_err
diff --git a/contrib/wpa/hs20/server/ca/ocsp-update-cache.sh b/contrib/wpa/hs20/server/ca/ocsp-update-cache.sh
new file mode 100755
index 000000000000..f2b23250cadd
--- /dev/null
+++ b/contrib/wpa/hs20/server/ca/ocsp-update-cache.sh
@@ -0,0 +1,11 @@
+#!/bin/sh
+
+# NOTE: You may need to replace 'localhost' with your OCSP server hostname.
+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
diff --git a/contrib/wpa/hs20/server/ca/openssl-root.cnf b/contrib/wpa/hs20/server/ca/openssl-root.cnf
new file mode 100644
index 000000000000..5bc50be1dbc9
--- /dev/null
+++ b/contrib/wpa/hs20/server/ca/openssl-root.cnf
@@ -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
diff --git a/contrib/wpa/hs20/server/ca/openssl.cnf b/contrib/wpa/hs20/server/ca/openssl.cnf
new file mode 100644
index 000000000000..61410138340f
--- /dev/null
+++ b/contrib/wpa/hs20/server/ca/openssl.cnf
@@ -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
diff --git a/contrib/wpa/hs20/server/ca/setup.sh b/contrib/wpa/hs20/server/ca/setup.sh
new file mode 100755
index 000000000000..78abcccff455
--- /dev/null
+++ b/contrib/wpa/hs20/server/ca/setup.sh
@@ -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
diff --git a/contrib/wpa/hs20/server/ca/w1fi_logo.png b/contrib/wpa/hs20/server/ca/w1fi_logo.png
new file mode 100644
index 000000000000..ac7c259fff2e
--- /dev/null
+++ b/contrib/wpa/hs20/server/ca/w1fi_logo.png
Binary files differ
diff --git a/contrib/wpa/hs20/server/hs20-osu-server.txt b/contrib/wpa/hs20/server/hs20-osu-server.txt
new file mode 100644
index 000000000000..22478ad9d2cb
--- /dev/null
+++ b/contrib/wpa/hs20/server/hs20-osu-server.txt
@@ -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
diff --git a/contrib/wpa/hs20/server/hs20_spp_server.c b/contrib/wpa/hs20/server/hs20_spp_server.c
new file mode 100644
index 000000000000..347c40a73d6a
--- /dev/null
+++ b/contrib/wpa/hs20/server/hs20_spp_server.c
@@ -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%s\n", VERSION_STR);
+ 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;
+}
diff --git a/contrib/wpa/hs20/server/spp_server.c b/contrib/wpa/hs20/server/spp_server.c
new file mode 100644
index 000000000000..a50e9074f7b4
--- /dev/null
+++ b/contrib/wpa/hs20/server/spp_server.c
@@ -0,0 +1,2933 @@
+/*
+ * 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.
+ */
+
+#include <stdlib.h>
+#include <stdio.h>
+#include <string.h>
+#include <ctype.h>
+#include <time.h>
+#include <errno.h>
+#include <sqlite3.h>
+
+#include "common.h"
+#include "base64.h"
+#include "md5_i.h"
+#include "xml-utils.h"
+#include "spp_server.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_OMA_DM_DMACC "urn:oma:mo:oma-dm-dmacc:1.0"
+#define URN_HS20_PPS "urn:wfa:mo:hotspot2dot0-perprovidersubscription:1.0"
+
+
+/* TODO: timeout to expire sessions */
+
+enum hs20_session_operation {
+ NO_OPERATION,
+ UPDATE_PASSWORD,
+ CONTINUE_SUBSCRIPTION_REMEDIATION,
+ CONTINUE_POLICY_UPDATE,
+ USER_REMEDIATION,
+ SUBSCRIPTION_REGISTRATION,
+ POLICY_REMEDIATION,
+ POLICY_UPDATE,
+ FREE_REMEDIATION,
+ CLEAR_REMEDIATION,
+ CERT_REENROLL,
+};
+
+
+static char * db_get_session_val(struct hs20_svc *ctx, const char *user,
+ const char *realm, const char *session_id,
+ const char *field);
+static char * db_get_osu_config_val(struct hs20_svc *ctx, const char *realm,
+ const char *field);
+static xml_node_t * build_policy(struct hs20_svc *ctx, const char *user,
+ const char *realm, int use_dmacc);
+static xml_node_t * spp_exec_get_certificate(struct hs20_svc *ctx,
+ const char *session_id,
+ const char *user,
+ const char *realm,
+ int add_est_user);
+
+
+static int db_add_session(struct hs20_svc *ctx,
+ const char *user, const char *realm,
+ const char *sessionid, const char *pw,
+ const char *redirect_uri,
+ enum hs20_session_operation operation,
+ const u8 *mac_addr)
+{
+ char *sql;
+ int ret = 0;
+ char addr[20];
+
+ if (mac_addr)
+ snprintf(addr, sizeof(addr), MACSTR, MAC2STR(mac_addr));
+ else
+ addr[0] = '\0';
+ sql = sqlite3_mprintf("INSERT INTO sessions(timestamp,id,user,realm,"
+ "operation,password,redirect_uri,mac_addr,test) "
+ "VALUES "
+ "(strftime('%%Y-%%m-%%d %%H:%%M:%%f','now'),"
+ "%Q,%Q,%Q,%d,%Q,%Q,%Q,%Q)",
+ sessionid, user ? user : "", realm ? realm : "",
+ operation, pw ? pw : "",
+ redirect_uri ? redirect_uri : "",
+ addr, ctx->test);
+ if (sql == NULL)
+ return -1;
+ debug_print(ctx, 1, "DB: %s", sql);
+ if (sqlite3_exec(ctx->db, sql, NULL, NULL, NULL) != SQLITE_OK) {
+ debug_print(ctx, 1, "Failed to add session entry into sqlite "
+ "database: %s", sqlite3_errmsg(ctx->db));
+ ret = -1;
+ }
+ sqlite3_free(sql);
+ return ret;
+}
+
+
+static void db_update_session_password(struct hs20_svc *ctx, const char *user,
+ const char *realm, const char *sessionid,
+ const char *pw)
+{
+ char *sql;
+
+ sql = sqlite3_mprintf("UPDATE sessions SET password=%Q WHERE id=%Q AND "
+ "user=%Q AND realm=%Q",
+ pw, sessionid, user, realm);
+ if (sql == NULL)
+ return;
+ debug_print(ctx, 1, "DB: %s", sql);
+ if (sqlite3_exec(ctx->db, sql, NULL, NULL, NULL) != SQLITE_OK) {
+ debug_print(ctx, 1, "Failed to update session password: %s",
+ sqlite3_errmsg(ctx->db));
+ }
+ sqlite3_free(sql);
+}
+
+
+static void db_update_session_machine_managed(struct hs20_svc *ctx,
+ const char *user,
+ const char *realm,
+ const char *sessionid,
+ const int pw_mm)
+{
+ char *sql;
+
+ sql = sqlite3_mprintf("UPDATE sessions SET machine_managed=%Q WHERE id=%Q AND user=%Q AND realm=%Q",
+ pw_mm ? "1" : "0", sessionid, user, realm);
+ if (sql == NULL)
+ return;
+ debug_print(ctx, 1, "DB: %s", sql);
+ if (sqlite3_exec(ctx->db, sql, NULL, NULL, NULL) != SQLITE_OK) {
+ debug_print(ctx, 1,
+ "Failed to update session machine_managed: %s",
+ sqlite3_errmsg(ctx->db));
+ }
+ sqlite3_free(sql);
+}
+
+
+static void db_add_session_pps(struct hs20_svc *ctx, const char *user,
+ const char *realm, const char *sessionid,
+ xml_node_t *node)
+{
+ char *str;
+ char *sql;
+
+ str = xml_node_to_str(ctx->xml, node);
+ if (str == NULL)
+ return;
+ sql = sqlite3_mprintf("UPDATE sessions SET pps=%Q WHERE id=%Q AND "
+ "user=%Q AND realm=%Q",
+ str, sessionid, user, realm);
+ free(str);
+ if (sql == NULL)
+ return;
+ debug_print(ctx, 1, "DB: %s", sql);
+ if (sqlite3_exec(ctx->db, sql, NULL, NULL, NULL) != SQLITE_OK) {
+ debug_print(ctx, 1, "Failed to add session pps: %s",
+ sqlite3_errmsg(ctx->db));
+ }
+ sqlite3_free(sql);
+}
+
+
+static void db_add_session_devinfo(struct hs20_svc *ctx, const char *sessionid,
+ xml_node_t *node)
+{
+ char *str;
+ char *sql;
+
+ str = xml_node_to_str(ctx->xml, node);
+ if (str == NULL)
+ return;
+ sql = sqlite3_mprintf("UPDATE sessions SET devinfo=%Q WHERE id=%Q",
+ str, sessionid);
+ free(str);
+ if (sql == NULL)
+ return;
+ debug_print(ctx, 1, "DB: %s", sql);
+ if (sqlite3_exec(ctx->db, sql, NULL, NULL, NULL) != SQLITE_OK) {
+ debug_print(ctx, 1, "Failed to add session devinfo: %s",
+ sqlite3_errmsg(ctx->db));
+ }
+ sqlite3_free(sql);
+}
+
+
+static void db_add_session_devdetail(struct hs20_svc *ctx,
+ const char *sessionid,
+ xml_node_t *node)
+{
+ char *str;
+ char *sql;
+
+ str = xml_node_to_str(ctx->xml, node);
+ if (str == NULL)
+ return;
+ sql = sqlite3_mprintf("UPDATE sessions SET devdetail=%Q WHERE id=%Q",
+ str, sessionid);
+ free(str);
+ if (sql == NULL)
+ return;
+ debug_print(ctx, 1, "DB: %s", sql);
+ if (sqlite3_exec(ctx->db, sql, NULL, NULL, NULL) != SQLITE_OK) {
+ debug_print(ctx, 1, "Failed to add session devdetail: %s",
+ sqlite3_errmsg(ctx->db));
+ }
+ sqlite3_free(sql);
+}
+
+
+static void db_add_session_dmacc(struct hs20_svc *ctx, const char *sessionid,
+ const char *username, const char *password)
+{
+ char *sql;
+
+ sql = sqlite3_mprintf("UPDATE sessions SET osu_user=%Q, osu_password=%Q WHERE id=%Q",
+ username, password, sessionid);
+ if (!sql)
+ return;
+ debug_print(ctx, 1, "DB: %s", sql);
+ if (sqlite3_exec(ctx->db, sql, NULL, NULL, NULL) != SQLITE_OK) {
+ debug_print(ctx, 1, "Failed to add session DMAcc: %s",
+ sqlite3_errmsg(ctx->db));
+ }
+ sqlite3_free(sql);
+}
+
+
+static void db_add_session_eap_method(struct hs20_svc *ctx,
+ const char *sessionid,
+ const char *method)
+{
+ char *sql;
+
+ sql = sqlite3_mprintf("UPDATE sessions SET eap_method=%Q WHERE id=%Q",
+ method, sessionid);
+ if (!sql)
+ return;
+ debug_print(ctx, 1, "DB: %s", sql);
+ if (sqlite3_exec(ctx->db, sql, NULL, NULL, NULL) != SQLITE_OK) {
+ debug_print(ctx, 1, "Failed to add session EAP method: %s",
+ sqlite3_errmsg(ctx->db));
+ }
+ sqlite3_free(sql);
+}
+
+
+static void db_add_session_id_hash(struct hs20_svc *ctx, const char *sessionid,
+ const char *id_hash)
+{
+ char *sql;
+
+ sql = sqlite3_mprintf("UPDATE sessions SET mobile_identifier_hash=%Q WHERE id=%Q",
+ id_hash, sessionid);
+ if (!sql)
+ return;
+ debug_print(ctx, 1, "DB: %s", sql);
+ if (sqlite3_exec(ctx->db, sql, NULL, NULL, NULL) != SQLITE_OK) {
+ debug_print(ctx, 1, "Failed to add session ID hash: %s",
+ sqlite3_errmsg(ctx->db));
+ }
+ sqlite3_free(sql);
+}
+
+
+static void db_remove_session(struct hs20_svc *ctx,
+ const char *user, const char *realm,
+ const char *sessionid)
+{
+ char *sql;
+
+ if (user == NULL || realm == NULL) {
+ sql = sqlite3_mprintf("DELETE FROM sessions WHERE "
+ "id=%Q", sessionid);
+ } else {
+ sql = sqlite3_mprintf("DELETE FROM sessions WHERE "
+ "user=%Q AND realm=%Q AND id=%Q",
+ user, realm, sessionid);
+ }
+ if (sql == NULL)
+ return;
+ debug_print(ctx, 1, "DB: %s", sql);
+ if (sqlite3_exec(ctx->db, sql, NULL, NULL, NULL) != SQLITE_OK) {
+ debug_print(ctx, 1, "Failed to delete session entry from "
+ "sqlite database: %s", sqlite3_errmsg(ctx->db));
+ }
+ sqlite3_free(sql);
+}
+
+
+static void hs20_eventlog(struct hs20_svc *ctx,
+ const char *user, const char *realm,
+ const char *sessionid, const char *notes,
+ const char *dump)
+{
+ char *sql;
+ char *user_buf = NULL, *realm_buf = NULL;
+
+ debug_print(ctx, 1, "eventlog: %s", notes);
+
+ if (user == NULL) {
+ user_buf = db_get_session_val(ctx, NULL, NULL, sessionid,
+ "user");
+ user = user_buf;
+ realm_buf = db_get_session_val(ctx, NULL, NULL, sessionid,
+ "realm");
+ realm = realm_buf;
+ }
+
+ sql = sqlite3_mprintf("INSERT INTO eventlog"
+ "(user,realm,sessionid,timestamp,notes,dump,addr)"
+ " VALUES (%Q,%Q,%Q,"
+ "strftime('%%Y-%%m-%%d %%H:%%M:%%f','now'),"
+ "%Q,%Q,%Q)",
+ user, realm, sessionid, notes,
+ dump ? dump : "", ctx->addr ? ctx->addr : "");
+ free(user_buf);
+ free(realm_buf);
+ if (sql == NULL)
+ return;
+ if (sqlite3_exec(ctx->db, sql, NULL, NULL, NULL) != SQLITE_OK) {
+ debug_print(ctx, 1, "Failed to add eventlog entry into sqlite "
+ "database: %s", sqlite3_errmsg(ctx->db));
+ }
+ sqlite3_free(sql);
+}
+
+
+static void hs20_eventlog_node(struct hs20_svc *ctx,
+ const char *user, const char *realm,
+ const char *sessionid, const char *notes,
+ xml_node_t *node)
+{
+ char *str;
+
+ if (node)
+ str = xml_node_to_str(ctx->xml, node);
+ else
+ str = NULL;
+ hs20_eventlog(ctx, user, realm, sessionid, notes, str);
+ free(str);
+}
+
+
+static void db_update_mo_str(struct hs20_svc *ctx, const char *user,
+ const char *realm, const char *name,
+ const char *str)
+{
+ char *sql;
+ if (user == NULL || realm == NULL || name == NULL)
+ return;
+ sql = sqlite3_mprintf("UPDATE users SET %s=%Q WHERE identity=%Q AND realm=%Q AND (phase2=1 OR methods='TLS')",
+ name, str, user, realm);
+ if (sql == NULL)
+ return;
+ debug_print(ctx, 1, "DB: %s", sql);
+ if (sqlite3_exec(ctx->db, sql, NULL, NULL, NULL) != SQLITE_OK) {
+ debug_print(ctx, 1, "Failed to update user MO entry in sqlite "
+ "database: %s", sqlite3_errmsg(ctx->db));
+ }
+ sqlite3_free(sql);
+}
+
+
+static void db_update_mo(struct hs20_svc *ctx, const char *user,
+ const char *realm, const char *name, xml_node_t *mo)
+{
+ char *str;
+
+ str = xml_node_to_str(ctx->xml, mo);
+ if (str == NULL)
+ return;
+
+ db_update_mo_str(ctx, user, realm, name, str);
+ free(str);
+}
+
+
+static void add_text_node(struct hs20_svc *ctx, xml_node_t *parent,
+ const char *name, const char *value)
+{
+ xml_node_create_text(ctx->xml, parent, NULL, name, value ? value : "");
+}
+
+
+static void add_text_node_conf(struct hs20_svc *ctx, const char *realm,
+ xml_node_t *parent, const char *name,
+ const char *field)
+{
+ char *val;
+ val = db_get_osu_config_val(ctx, realm, field);
+ xml_node_create_text(ctx->xml, parent, NULL, name, val ? val : "");
+ os_free(val);
+}
+
+
+static void add_text_node_conf_corrupt(struct hs20_svc *ctx, const char *realm,
+ xml_node_t *parent, const char *name,
+ const char *field)
+{
+ char *val;
+
+ val = db_get_osu_config_val(ctx, realm, field);
+ if (val) {
+ size_t len;
+
+ len = os_strlen(val);
+ if (len > 0) {
+ if (val[len - 1] == '0')
+ val[len - 1] = '1';
+ else
+ val[len - 1] = '0';
+ }
+ }
+ xml_node_create_text(ctx->xml, parent, NULL, name, val ? val : "");
+ os_free(val);
+}
+
+
+static int new_password(char *buf, int buflen)
+{
+ int i;
+
+ if (buflen < 1)
+ return -1;
+ buf[buflen - 1] = '\0';
+ if (os_get_random((unsigned char *) buf, buflen - 1) < 0)
+ return -1;
+
+ for (i = 0; i < buflen - 1; i++) {
+ unsigned char val = buf[i];
+ val %= 2 * 26 + 10;
+ if (val < 26)
+ buf[i] = 'a' + val;
+ else if (val < 2 * 26)
+ buf[i] = 'A' + val - 26;
+ else
+ buf[i] = '0' + val - 2 * 26;
+ }
+
+ return 0;
+}
+
+
+struct get_db_field_data {
+ const char *field;
+ char *value;
+};
+
+
+static int get_db_field(void *ctx, int argc, char *argv[], char *col[])
+{
+ struct get_db_field_data *data = ctx;
+ int i;
+
+ for (i = 0; i < argc; i++) {
+ if (os_strcmp(col[i], data->field) == 0 && argv[i]) {
+ os_free(data->value);
+ data->value = os_strdup(argv[i]);
+ break;
+ }
+ }
+
+ return 0;
+}
+
+
+static char * db_get_val(struct hs20_svc *ctx, const char *user,
+ const char *realm, const char *field, int dmacc)
+{
+ char *cmd;
+ struct get_db_field_data data;
+
+ cmd = sqlite3_mprintf("SELECT %s FROM users WHERE %s=%Q AND realm=%Q AND (phase2=1 OR methods='TLS')",
+ field, dmacc ? "osu_user" : "identity",
+ user, realm);
+ if (cmd == NULL)
+ return NULL;
+ memset(&data, 0, sizeof(data));
+ data.field = field;
+ if (sqlite3_exec(ctx->db, cmd, get_db_field, &data, NULL) != SQLITE_OK)
+ {
+ debug_print(ctx, 1, "Could not find user '%s'", user);
+ sqlite3_free(cmd);
+ return NULL;
+ }
+ sqlite3_free(cmd);
+
+ debug_print(ctx, 1, "DB: user='%s' realm='%s' field='%s' dmacc=%d --> "
+ "value='%s'", user, realm, field, dmacc, data.value);
+
+ return data.value;
+}
+
+
+static int db_update_val(struct hs20_svc *ctx, const char *user,
+ const char *realm, const char *field,
+ const char *val, int dmacc)
+{
+ char *cmd;
+ int ret;
+
+ cmd = sqlite3_mprintf("UPDATE users SET %s=%Q WHERE %s=%Q AND realm=%Q AND (phase2=1 OR methods='TLS')",
+ field, val, dmacc ? "osu_user" : "identity", user,
+ realm);
+ if (cmd == NULL)
+ return -1;
+ debug_print(ctx, 1, "DB: %s", cmd);
+ if (sqlite3_exec(ctx->db, cmd, NULL, NULL, NULL) != SQLITE_OK) {
+ debug_print(ctx, 1,
+ "Failed to update user in sqlite database: %s",
+ sqlite3_errmsg(ctx->db));
+ ret = -1;
+ } else {
+ debug_print(ctx, 1,
+ "DB: user='%s' realm='%s' field='%s' set to '%s'",
+ user, realm, field, val);
+ ret = 0;
+ }
+ sqlite3_free(cmd);
+
+ return ret;
+}
+
+
+static char * db_get_session_val(struct hs20_svc *ctx, const char *user,
+ const char *realm, const char *session_id,
+ const char *field)
+{
+ char *cmd;
+ struct get_db_field_data data;
+
+ if (user == NULL || realm == NULL) {
+ cmd = sqlite3_mprintf("SELECT %s FROM sessions WHERE "
+ "id=%Q", field, session_id);
+ } else {
+ cmd = sqlite3_mprintf("SELECT %s FROM sessions WHERE "
+ "user=%Q AND realm=%Q AND id=%Q",
+ field, user, realm, session_id);
+ }
+ if (cmd == NULL)
+ return NULL;
+ debug_print(ctx, 1, "DB: %s", cmd);
+ memset(&data, 0, sizeof(data));
+ data.field = field;
+ if (sqlite3_exec(ctx->db, cmd, get_db_field, &data, NULL) != SQLITE_OK)
+ {
+ debug_print(ctx, 1, "DB: Could not find session %s: %s",
+ session_id, sqlite3_errmsg(ctx->db));
+ sqlite3_free(cmd);
+ return NULL;
+ }
+ sqlite3_free(cmd);
+
+ debug_print(ctx, 1, "DB: return '%s'", data.value);
+ return data.value;
+}
+
+
+static int update_password(struct hs20_svc *ctx, const char *user,
+ const char *realm, const char *pw, int dmacc)
+{
+ char *cmd;
+
+ cmd = sqlite3_mprintf("UPDATE users SET password=%Q, "
+ "remediation='' "
+ "WHERE %s=%Q AND phase2=1",
+ pw, dmacc ? "osu_user" : "identity",
+ user);
+ if (cmd == NULL)
+ return -1;
+ debug_print(ctx, 1, "DB: %s", cmd);
+ if (sqlite3_exec(ctx->db, cmd, NULL, NULL, NULL) != SQLITE_OK) {
+ debug_print(ctx, 1, "Failed to update database for user '%s'",
+ user);
+ }
+ sqlite3_free(cmd);
+
+ return 0;
+}
+
+
+static int clear_remediation(struct hs20_svc *ctx, const char *user,
+ const char *realm, int dmacc)
+{
+ char *cmd;
+
+ cmd = sqlite3_mprintf("UPDATE users SET remediation='' WHERE %s=%Q",
+ dmacc ? "osu_user" : "identity",
+ user);
+ if (cmd == NULL)
+ return -1;
+ debug_print(ctx, 1, "DB: %s", cmd);
+ if (sqlite3_exec(ctx->db, cmd, NULL, NULL, NULL) != SQLITE_OK) {
+ debug_print(ctx, 1, "Failed to update database for user '%s'",
+ user);
+ }
+ sqlite3_free(cmd);
+
+ return 0;
+}
+
+
+static int add_eap_ttls(struct hs20_svc *ctx, xml_node_t *parent)
+{
+ xml_node_t *node;
+
+ node = xml_node_create(ctx->xml, parent, NULL, "EAPMethod");
+ if (node == NULL)
+ return -1;
+
+ add_text_node(ctx, node, "EAPType", "21");
+ add_text_node(ctx, node, "InnerMethod", "MS-CHAP-V2");
+
+ return 0;
+}
+
+
+static xml_node_t * build_username_password(struct hs20_svc *ctx,
+ xml_node_t *parent,
+ const char *user, const char *pw)
+{
+ xml_node_t *node;
+ char *b64;
+ size_t len;
+
+ node = xml_node_create(ctx->xml, parent, NULL, "UsernamePassword");
+ if (node == NULL)
+ return NULL;
+
+ add_text_node(ctx, node, "Username", user);
+
+ b64 = base64_encode(pw, strlen(pw), NULL);
+ if (b64 == NULL)
+ return NULL;
+ len = os_strlen(b64);
+ if (len > 0 && b64[len - 1] == '\n')
+ b64[len - 1] = '\0';
+ add_text_node(ctx, node, "Password", b64);
+ free(b64);
+
+ return node;
+}
+
+
+static int add_username_password(struct hs20_svc *ctx, xml_node_t *cred,
+ const char *user, const char *pw,
+ int machine_managed)
+{
+ xml_node_t *node;
+
+ node = build_username_password(ctx, cred, user, pw);
+ if (node == NULL)
+ return -1;
+
+ add_text_node(ctx, node, "MachineManaged",
+ machine_managed ? "TRUE" : "FALSE");
+ add_text_node(ctx, node, "SoftTokenApp", "");
+ add_eap_ttls(ctx, node);
+
+ return 0;
+}
+
+
+static void add_creation_date(struct hs20_svc *ctx, xml_node_t *cred)
+{
+ char str[30];
+ time_t now;
+ struct tm tm;
+
+ time(&now);
+ gmtime_r(&now, &tm);
+ snprintf(str, sizeof(str), "%04u-%02u-%02uT%02u:%02u:%02uZ",
+ tm.tm_year + 1900, tm.tm_mon + 1, tm.tm_mday,
+ tm.tm_hour, tm.tm_min, tm.tm_sec);
+ xml_node_create_text(ctx->xml, cred, NULL, "CreationDate", str);
+}
+
+
+static xml_node_t * build_credential_pw(struct hs20_svc *ctx,
+ const char *user, const char *realm,
+ const char *pw, int machine_managed)
+{
+ xml_node_t *cred;
+
+ cred = xml_node_create_root(ctx->xml, NULL, NULL, NULL, "Credential");
+ if (cred == NULL) {
+ debug_print(ctx, 1, "Failed to create Credential node");
+ return NULL;
+ }
+ add_creation_date(ctx, cred);
+ if (add_username_password(ctx, cred, user, pw, machine_managed) < 0) {
+ xml_node_free(ctx->xml, cred);
+ return NULL;
+ }
+ add_text_node(ctx, cred, "Realm", realm);
+
+ return cred;
+}
+
+
+static xml_node_t * build_credential(struct hs20_svc *ctx,
+ const char *user, const char *realm,
+ char *new_pw, size_t new_pw_len)
+{
+ if (new_password(new_pw, new_pw_len) < 0)
+ return NULL;
+ debug_print(ctx, 1, "Update password to '%s'", new_pw);
+ return build_credential_pw(ctx, user, realm, new_pw, 1);
+}
+
+
+static xml_node_t * build_credential_cert(struct hs20_svc *ctx,
+ const char *user, const char *realm,
+ const char *cert_fingerprint)
+{
+ xml_node_t *cred, *cert;
+
+ cred = xml_node_create_root(ctx->xml, NULL, NULL, NULL, "Credential");
+ if (cred == NULL) {
+ debug_print(ctx, 1, "Failed to create Credential node");
+ return NULL;
+ }
+ add_creation_date(ctx, cred);
+ cert = xml_node_create(ctx->xml, cred, NULL, "DigitalCertificate");
+ add_text_node(ctx, cert, "CertificateType", "x509v3");
+ add_text_node(ctx, cert, "CertSHA256Fingerprint", cert_fingerprint);
+ add_text_node(ctx, cred, "Realm", realm);
+
+ return cred;
+}
+
+
+static xml_node_t * build_post_dev_data_response(struct hs20_svc *ctx,
+ xml_namespace_t **ret_ns,
+ const char *session_id,
+ const char *status,
+ const char *error_code)
+{
+ xml_node_t *spp_node = NULL;
+ xml_namespace_t *ns;
+
+ spp_node = xml_node_create_root(ctx->xml, SPP_NS_URI, "spp", &ns,
+ "sppPostDevDataResponse");
+ if (spp_node == NULL)
+ return NULL;
+ if (ret_ns)
+ *ret_ns = ns;
+
+ xml_node_add_attr(ctx->xml, spp_node, ns, "sppVersion", "1.0");
+ xml_node_add_attr(ctx->xml, spp_node, ns, "sessionID", session_id);
+ xml_node_add_attr(ctx->xml, spp_node, ns, "sppStatus", status);
+
+ if (error_code) {
+ xml_node_t *node;
+ node = xml_node_create(ctx->xml, spp_node, ns, "sppError");
+ if (node)
+ xml_node_add_attr(ctx->xml, node, NULL, "errorCode",
+ error_code);
+ }
+
+ return spp_node;
+}
+
+
+static int add_update_node(struct hs20_svc *ctx, xml_node_t *spp_node,
+ xml_namespace_t *ns, const char *uri,
+ xml_node_t *upd_node)
+{
+ xml_node_t *node, *tnds;
+ char *str;
+
+ tnds = mo_to_tnds(ctx->xml, upd_node, 0, NULL, NULL);
+ if (!tnds)
+ return -1;
+
+ str = xml_node_to_str(ctx->xml, tnds);
+ xml_node_free(ctx->xml, tnds);
+ if (str == NULL)
+ return -1;
+ node = xml_node_create_text(ctx->xml, spp_node, ns, "updateNode", str);
+ free(str);
+
+ xml_node_add_attr(ctx->xml, node, ns, "managementTreeURI", uri);
+
+ return 0;
+}
+
+
+static xml_node_t * read_subrem_file(struct hs20_svc *ctx,
+ const char *subrem_id,
+ char *uri, size_t uri_size)
+{
+ char fname[200];
+ char *buf, *buf2, *pos;
+ size_t len;
+ xml_node_t *node;
+
+ os_snprintf(fname, sizeof(fname), "%s/spp/subrem/%s",
+ ctx->root_dir, subrem_id);
+ debug_print(ctx, 1, "Use subrem file %s", fname);
+
+ buf = os_readfile(fname, &len);
+ if (!buf)
+ return NULL;
+ buf2 = os_realloc(buf, len + 1);
+ if (!buf2) {
+ os_free(buf);
+ return NULL;
+ }
+ buf = buf2;
+ buf[len] = '\0';
+
+ pos = os_strchr(buf, '\n');
+ if (!pos) {
+ os_free(buf);
+ return NULL;
+ }
+ *pos++ = '\0';
+ os_strlcpy(uri, buf, uri_size);
+
+ node = xml_node_from_buf(ctx->xml, pos);
+ os_free(buf);
+
+ return node;
+}
+
+
+static xml_node_t * build_sub_rem_resp(struct hs20_svc *ctx,
+ const char *user, const char *realm,
+ const char *session_id,
+ int machine_rem, int dmacc)
+{
+ xml_namespace_t *ns;
+ xml_node_t *spp_node, *cred;
+ char buf[400];
+ char new_pw[33];
+ char *status;
+ char *cert;
+
+ cert = db_get_val(ctx, user, realm, "cert", dmacc);
+ if (cert && cert[0] == '\0') {
+ os_free(cert);
+ cert = NULL;
+ }
+ if (cert) {
+ char *subrem;
+
+ /* No change needed in PPS MO unless specifically asked to */
+ cred = NULL;
+ buf[0] = '\0';
+
+ subrem = db_get_val(ctx, user, realm, "subrem", dmacc);
+ if (subrem && subrem[0]) {
+ cred = read_subrem_file(ctx, subrem, buf, sizeof(buf));
+ if (!cred) {
+ debug_print(ctx, 1,
+ "Could not create updateNode from subrem file");
+ os_free(subrem);
+ os_free(cert);
+ return NULL;
+ }
+ }
+ os_free(subrem);
+ } else {
+ char *real_user = NULL;
+ char *pw;
+
+ if (dmacc) {
+ real_user = db_get_val(ctx, user, realm, "identity",
+ dmacc);
+ if (!real_user) {
+ debug_print(ctx, 1,
+ "Could not find user identity for dmacc user '%s'",
+ user);
+ return NULL;
+ }
+ }
+
+ pw = db_get_session_val(ctx, user, realm, session_id,
+ "password");
+ if (pw && pw[0]) {
+ debug_print(ctx, 1, "New password from the user: '%s'",
+ pw);
+ snprintf(new_pw, sizeof(new_pw), "%s", pw);
+ free(pw);
+ cred = build_credential_pw(ctx,
+ real_user ? real_user : user,
+ realm, new_pw, 0);
+ } else {
+ cred = build_credential(ctx,
+ real_user ? real_user : user,
+ realm, new_pw, sizeof(new_pw));
+ }
+
+ free(real_user);
+ if (!cred) {
+ debug_print(ctx, 1, "Could not build credential");
+ os_free(cert);
+ return NULL;
+ }
+
+ snprintf(buf, sizeof(buf),
+ "./Wi-Fi/%s/PerProviderSubscription/Cred01/Credential",
+ realm);
+ }
+
+ status = "Remediation complete, request sppUpdateResponse";
+ spp_node = build_post_dev_data_response(ctx, &ns, session_id, status,
+ NULL);
+ if (spp_node == NULL) {
+ debug_print(ctx, 1, "Could not build sppPostDevDataResponse");
+ os_free(cert);
+ return NULL;
+ }
+
+ if ((cred && add_update_node(ctx, spp_node, ns, buf, cred) < 0) ||
+ (!cred && !xml_node_create(ctx->xml, spp_node, ns, "noMOUpdate"))) {
+ debug_print(ctx, 1, "Could not add update node");
+ xml_node_free(ctx->xml, spp_node);
+ os_free(cert);
+ return NULL;
+ }
+
+ hs20_eventlog_node(ctx, user, realm, session_id,
+ machine_rem ? "machine remediation" :
+ "user remediation", cred);
+ xml_node_free(ctx->xml, cred);
+
+ if (cert) {
+ debug_print(ctx, 1, "Request DB remediation clearing on success notification (certificate credential)");
+ db_add_session(ctx, user, realm, session_id, NULL, NULL,
+ CLEAR_REMEDIATION, NULL);
+ } else {
+ debug_print(ctx, 1, "Request DB password update on success "
+ "notification");
+ db_add_session(ctx, user, realm, session_id, new_pw, NULL,
+ UPDATE_PASSWORD, NULL);
+ }
+ os_free(cert);
+
+ return spp_node;
+}
+
+
+static xml_node_t * machine_remediation(struct hs20_svc *ctx,
+ const char *user,
+ const char *realm,
+ const char *session_id, int dmacc)
+{
+ return build_sub_rem_resp(ctx, user, realm, session_id, 1, dmacc);
+}
+
+
+static xml_node_t * cert_reenroll(struct hs20_svc *ctx,
+ const char *user,
+ const char *realm,
+ const char *session_id)
+{
+ db_add_session(ctx, user, realm, session_id, NULL, NULL,
+ CERT_REENROLL, NULL);
+ return spp_exec_get_certificate(ctx, session_id, user, realm, 0);
+}
+
+
+static xml_node_t * policy_remediation(struct hs20_svc *ctx,
+ const char *user, const char *realm,
+ const char *session_id, int dmacc)
+{
+ xml_namespace_t *ns;
+ xml_node_t *spp_node, *policy;
+ char buf[400];
+ const char *status;
+
+ hs20_eventlog(ctx, user, realm, session_id,
+ "requires policy remediation", NULL);
+
+ db_add_session(ctx, user, realm, session_id, NULL, NULL,
+ POLICY_REMEDIATION, NULL);
+
+ policy = build_policy(ctx, user, realm, dmacc);
+ if (!policy) {
+ return build_post_dev_data_response(
+ ctx, NULL, session_id,
+ "No update available at this time", NULL);
+ }
+
+ status = "Remediation complete, request sppUpdateResponse";
+ spp_node = build_post_dev_data_response(ctx, &ns, session_id, status,
+ NULL);
+ if (spp_node == NULL)
+ return NULL;
+
+ snprintf(buf, sizeof(buf),
+ "./Wi-Fi/%s/PerProviderSubscription/Cred01/Policy",
+ realm);
+
+ if (add_update_node(ctx, spp_node, ns, buf, policy) < 0) {
+ xml_node_free(ctx->xml, spp_node);
+ xml_node_free(ctx->xml, policy);
+ return NULL;
+ }
+
+ hs20_eventlog_node(ctx, user, realm, session_id,
+ "policy update (sub rem)", policy);
+ xml_node_free(ctx->xml, policy);
+
+ return spp_node;
+}
+
+
+static xml_node_t * browser_remediation(struct hs20_svc *ctx,
+ const char *session_id,
+ const char *redirect_uri,
+ const char *uri)
+{
+ xml_namespace_t *ns;
+ xml_node_t *spp_node, *exec_node;
+
+ if (redirect_uri == NULL) {
+ debug_print(ctx, 1, "Missing redirectURI attribute for user "
+ "remediation");
+ return NULL;
+ }
+ debug_print(ctx, 1, "redirectURI %s", redirect_uri);
+
+ spp_node = build_post_dev_data_response(ctx, &ns, session_id, "OK",
+ NULL);
+ if (spp_node == NULL)
+ return NULL;
+
+ exec_node = xml_node_create(ctx->xml, spp_node, ns, "exec");
+ xml_node_create_text(ctx->xml, exec_node, ns, "launchBrowserToURI",
+ uri);
+ return spp_node;
+}
+
+
+static xml_node_t * user_remediation(struct hs20_svc *ctx, const char *user,
+ const char *realm, const char *session_id,
+ const char *redirect_uri)
+{
+ char uri[300], *val;
+
+ hs20_eventlog(ctx, user, realm, session_id,
+ "requires user remediation", NULL);
+ val = db_get_osu_config_val(ctx, realm, "remediation_url");
+ if (val == NULL)
+ return NULL;
+
+ db_add_session(ctx, user, realm, session_id, NULL, redirect_uri,
+ USER_REMEDIATION, NULL);
+
+ snprintf(uri, sizeof(uri), "%s%s", val, session_id);
+ os_free(val);
+ return browser_remediation(ctx, session_id, redirect_uri, uri);
+}
+
+
+static xml_node_t * free_remediation(struct hs20_svc *ctx,
+ const char *user, const char *realm,
+ const char *session_id,
+ const char *redirect_uri)
+{
+ char uri[300], *val;
+
+ hs20_eventlog(ctx, user, realm, session_id,
+ "requires free/public account remediation", NULL);
+ val = db_get_osu_config_val(ctx, realm, "free_remediation_url");
+ if (val == NULL)
+ return NULL;
+
+ db_add_session(ctx, user, realm, session_id, NULL, redirect_uri,
+ FREE_REMEDIATION, NULL);
+
+ snprintf(uri, sizeof(uri), "%s%s", val, session_id);
+ os_free(val);
+ return browser_remediation(ctx, session_id, redirect_uri, uri);
+}
+
+
+static xml_node_t * no_sub_rem(struct hs20_svc *ctx,
+ const char *user, const char *realm,
+ const char *session_id)
+{
+ const char *status;
+
+ hs20_eventlog(ctx, user, realm, session_id,
+ "no subscription mediation available", NULL);
+
+ status = "No update available at this time";
+ return build_post_dev_data_response(ctx, NULL, session_id, status,
+ NULL);
+}
+
+
+static xml_node_t * hs20_subscription_remediation(struct hs20_svc *ctx,
+ const char *user,
+ const char *realm,
+ const char *session_id,
+ int dmacc,
+ const char *redirect_uri)
+{
+ char *type, *identity;
+ xml_node_t *ret;
+ char *free_account;
+
+ identity = db_get_val(ctx, user, realm, "identity", dmacc);
+ if (identity == NULL || strlen(identity) == 0) {
+ hs20_eventlog(ctx, user, realm, session_id,
+ "user not found in database for remediation",
+ NULL);
+ os_free(identity);
+ return build_post_dev_data_response(ctx, NULL, session_id,
+ "Error occurred",
+ "Not found");
+ }
+ os_free(identity);
+
+ free_account = db_get_osu_config_val(ctx, realm, "free_account");
+ if (free_account && strcmp(free_account, user) == 0) {
+ free(free_account);
+ return no_sub_rem(ctx, user, realm, session_id);
+ }
+ free(free_account);
+
+ type = db_get_val(ctx, user, realm, "remediation", dmacc);
+ if (type && strcmp(type, "free") != 0) {
+ char *val;
+ int shared = 0;
+ val = db_get_val(ctx, user, realm, "shared", dmacc);
+ if (val)
+ shared = atoi(val);
+ free(val);
+ if (shared) {
+ free(type);
+ return no_sub_rem(ctx, user, realm, session_id);
+ }
+ }
+ if (type && strcmp(type, "user") == 0)
+ ret = user_remediation(ctx, user, realm, session_id,
+ redirect_uri);
+ else if (type && strcmp(type, "free") == 0)
+ ret = free_remediation(ctx, user, realm, session_id,
+ redirect_uri);
+ else if (type && strcmp(type, "policy") == 0)
+ ret = policy_remediation(ctx, user, realm, session_id, dmacc);
+ else if (type && strcmp(type, "machine") == 0)
+ ret = machine_remediation(ctx, user, realm, session_id, dmacc);
+ else if (type && strcmp(type, "reenroll") == 0)
+ ret = cert_reenroll(ctx, user, realm, session_id);
+ else
+ ret = no_sub_rem(ctx, user, realm, session_id);
+ free(type);
+
+ return ret;
+}
+
+
+static xml_node_t * read_policy_file(struct hs20_svc *ctx,
+ const char *policy_id)
+{
+ char fname[200];
+
+ snprintf(fname, sizeof(fname), "%s/spp/policy/%s.xml",
+ ctx->root_dir, policy_id);
+ debug_print(ctx, 1, "Use policy file %s", fname);
+
+ return node_from_file(ctx->xml, fname);
+}
+
+
+static void update_policy_update_uri(struct hs20_svc *ctx, const char *realm,
+ xml_node_t *policy)
+{
+ xml_node_t *node;
+ char *url;
+
+ node = get_node_uri(ctx->xml, policy, "Policy/PolicyUpdate/URI");
+ if (!node)
+ return;
+
+ url = db_get_osu_config_val(ctx, realm, "policy_url");
+ if (!url)
+ return;
+ xml_node_set_text(ctx->xml, node, url);
+ free(url);
+}
+
+
+static xml_node_t * build_policy(struct hs20_svc *ctx, const char *user,
+ const char *realm, int use_dmacc)
+{
+ char *policy_id;
+ xml_node_t *policy, *node;
+
+ policy_id = db_get_val(ctx, user, realm, "policy", use_dmacc);
+ if (policy_id == NULL || strlen(policy_id) == 0) {
+ free(policy_id);
+ policy_id = strdup("default");
+ if (policy_id == NULL)
+ return NULL;
+ }
+ policy = read_policy_file(ctx, policy_id);
+ free(policy_id);
+ if (policy == NULL)
+ return NULL;
+
+ update_policy_update_uri(ctx, realm, policy);
+
+ node = get_node_uri(ctx->xml, policy, "Policy/PolicyUpdate");
+ if (node && use_dmacc) {
+ char *pw;
+ pw = db_get_val(ctx, user, realm, "osu_password", use_dmacc);
+ if (pw == NULL ||
+ build_username_password(ctx, node, user, pw) == NULL) {
+ debug_print(ctx, 1, "Failed to add Policy/PolicyUpdate/"
+ "UsernamePassword");
+ free(pw);
+ xml_node_free(ctx->xml, policy);
+ return NULL;
+ }
+ free(pw);
+ }
+
+ return policy;
+}
+
+
+static xml_node_t * hs20_policy_update(struct hs20_svc *ctx,
+ const char *user, const char *realm,
+ const char *session_id, int dmacc)
+{
+ xml_namespace_t *ns;
+ xml_node_t *spp_node;
+ xml_node_t *policy;
+ char buf[400];
+ const char *status;
+ char *identity;
+
+ identity = db_get_val(ctx, user, realm, "identity", dmacc);
+ if (identity == NULL || strlen(identity) == 0) {
+ hs20_eventlog(ctx, user, realm, session_id,
+ "user not found in database for policy update",
+ NULL);
+ os_free(identity);
+ return build_post_dev_data_response(ctx, NULL, session_id,
+ "Error occurred",
+ "Not found");
+ }
+ os_free(identity);
+
+ policy = build_policy(ctx, user, realm, dmacc);
+ if (!policy) {
+ return build_post_dev_data_response(
+ ctx, NULL, session_id,
+ "No update available at this time", NULL);
+ }
+
+ db_add_session(ctx, user, realm, session_id, NULL, NULL, POLICY_UPDATE,
+ NULL);
+
+ status = "Update complete, request sppUpdateResponse";
+ spp_node = build_post_dev_data_response(ctx, &ns, session_id, status,
+ NULL);
+ if (spp_node == NULL)
+ return NULL;
+
+ snprintf(buf, sizeof(buf),
+ "./Wi-Fi/%s/PerProviderSubscription/Cred01/Policy",
+ realm);
+
+ if (add_update_node(ctx, spp_node, ns, buf, policy) < 0) {
+ xml_node_free(ctx->xml, spp_node);
+ xml_node_free(ctx->xml, policy);
+ return NULL;
+ }
+
+ hs20_eventlog_node(ctx, user, realm, session_id, "policy update",
+ policy);
+ xml_node_free(ctx->xml, policy);
+
+ return spp_node;
+}
+
+
+static xml_node_t * spp_get_mo(struct hs20_svc *ctx, xml_node_t *node,
+ const char *urn, int *valid, char **ret_err)
+{
+ xml_node_t *child, *tnds, *mo;
+ const char *name;
+ char *mo_urn;
+ char *str;
+ char fname[200];
+
+ *valid = -1;
+ if (ret_err)
+ *ret_err = NULL;
+
+ xml_node_for_each_child(ctx->xml, child, node) {
+ xml_node_for_each_check(ctx->xml, child);
+ name = xml_node_get_localname(ctx->xml, child);
+ if (strcmp(name, "moContainer") != 0)
+ continue;
+ mo_urn = xml_node_get_attr_value_ns(ctx->xml, child, SPP_NS_URI,
+ "moURN");
+ if (strcasecmp(urn, mo_urn) == 0) {
+ xml_node_get_attr_value_free(ctx->xml, mo_urn);
+ break;
+ }
+ xml_node_get_attr_value_free(ctx->xml, mo_urn);
+ }
+
+ if (child == NULL)
+ return NULL;
+
+ debug_print(ctx, 1, "moContainer text for %s", urn);
+ debug_dump_node(ctx, "moContainer", child);
+
+ str = xml_node_get_text(ctx->xml, child);
+ debug_print(ctx, 1, "moContainer payload: '%s'", str);
+ tnds = xml_node_from_buf(ctx->xml, str);
+ xml_node_get_text_free(ctx->xml, str);
+ if (tnds == NULL) {
+ debug_print(ctx, 1, "could not parse moContainer text");
+ return NULL;
+ }
+
+ snprintf(fname, sizeof(fname), "%s/spp/dm_ddf-v1_2.dtd", ctx->root_dir);
+ if (xml_validate_dtd(ctx->xml, tnds, fname, ret_err) == 0)
+ *valid = 1;
+ else if (ret_err && *ret_err &&
+ os_strcmp(*ret_err, "No declaration for attribute xmlns of element MgmtTree\n") == 0) {
+ free(*ret_err);
+ debug_print(ctx, 1, "Ignore OMA-DM DDF DTD validation error for MgmtTree namespace declaration with xmlns attribute");
+ *ret_err = NULL;
+ *valid = 1;
+ } else
+ *valid = 0;
+
+ mo = tnds_to_mo(ctx->xml, tnds);
+ xml_node_free(ctx->xml, tnds);
+ if (mo == NULL) {
+ debug_print(ctx, 1, "invalid moContainer for %s", urn);
+ }
+
+ return mo;
+}
+
+
+static xml_node_t * spp_exec_upload_mo(struct hs20_svc *ctx,
+ const char *session_id, const char *urn)
+{
+ xml_namespace_t *ns;
+ xml_node_t *spp_node, *node, *exec_node;
+
+ spp_node = build_post_dev_data_response(ctx, &ns, session_id, "OK",
+ NULL);
+ if (spp_node == NULL)
+ return NULL;
+
+ exec_node = xml_node_create(ctx->xml, spp_node, ns, "exec");
+
+ node = xml_node_create(ctx->xml, exec_node, ns, "uploadMO");
+ xml_node_add_attr(ctx->xml, node, ns, "moURN", urn);
+
+ return spp_node;
+}
+
+
+static xml_node_t * hs20_subscription_registration(struct hs20_svc *ctx,
+ const char *realm,
+ const char *session_id,
+ const char *redirect_uri,
+ const u8 *mac_addr)
+{
+ xml_namespace_t *ns;
+ xml_node_t *spp_node, *exec_node;
+ char uri[300], *val;
+
+ if (db_add_session(ctx, NULL, realm, session_id, NULL, redirect_uri,
+ SUBSCRIPTION_REGISTRATION, mac_addr) < 0)
+ return NULL;
+ val = db_get_osu_config_val(ctx, realm, "signup_url");
+ if (val == NULL)
+ return NULL;
+
+ spp_node = build_post_dev_data_response(ctx, &ns, session_id, "OK",
+ NULL);
+ if (spp_node == NULL)
+ return NULL;
+
+ exec_node = xml_node_create(ctx->xml, spp_node, ns, "exec");
+
+ snprintf(uri, sizeof(uri), "%s%s", val, session_id);
+ os_free(val);
+ xml_node_create_text(ctx->xml, exec_node, ns, "launchBrowserToURI",
+ uri);
+ return spp_node;
+}
+
+
+static xml_node_t * hs20_user_input_remediation(struct hs20_svc *ctx,
+ const char *user,
+ const char *realm, int dmacc,
+ const char *session_id)
+{
+ return build_sub_rem_resp(ctx, user, realm, session_id, 0, dmacc);
+}
+
+
+static char * db_get_osu_config_val(struct hs20_svc *ctx, const char *realm,
+ const char *field)
+{
+ char *cmd;
+ struct get_db_field_data data;
+
+ cmd = sqlite3_mprintf("SELECT value FROM osu_config WHERE realm=%Q AND "
+ "field=%Q", realm, field);
+ if (cmd == NULL)
+ return NULL;
+ debug_print(ctx, 1, "DB: %s", cmd);
+ memset(&data, 0, sizeof(data));
+ data.field = "value";
+ if (sqlite3_exec(ctx->db, cmd, get_db_field, &data, NULL) != SQLITE_OK)
+ {
+ debug_print(ctx, 1, "DB: Could not find osu_config %s: %s",
+ realm, sqlite3_errmsg(ctx->db));
+ sqlite3_free(cmd);
+ return NULL;
+ }
+ sqlite3_free(cmd);
+
+ debug_print(ctx, 1, "DB: return '%s'", data.value);
+ return data.value;
+}
+
+
+static xml_node_t * build_pps(struct hs20_svc *ctx,
+ const char *user, const char *realm,
+ const char *pw, const char *cert,
+ int machine_managed, const char *test,
+ const char *imsi, const char *dmacc_username,
+ const char *dmacc_password,
+ xml_node_t *policy_node)
+{
+ xml_node_t *pps, *c, *trust, *aaa, *aaa1, *upd, *homesp, *p;
+ xml_node_t *cred, *eap, *userpw;
+
+ pps = xml_node_create_root(ctx->xml, NULL, NULL, NULL,
+ "PerProviderSubscription");
+ if (!pps) {
+ xml_node_free(ctx->xml, policy_node);
+ return NULL;
+ }
+
+ add_text_node(ctx, pps, "UpdateIdentifier", "1");
+
+ c = xml_node_create(ctx->xml, pps, NULL, "Cred01");
+
+ add_text_node(ctx, c, "CredentialPriority", "1");
+
+ if (imsi)
+ goto skip_aaa_trust_root;
+ aaa = xml_node_create(ctx->xml, c, NULL, "AAAServerTrustRoot");
+ aaa1 = xml_node_create(ctx->xml, aaa, NULL, "AAA1");
+ add_text_node_conf(ctx, realm, aaa1, "CertURL",
+ "aaa_trust_root_cert_url");
+ if (test && os_strcmp(test, "corrupt_aaa_hash") == 0) {
+ debug_print(ctx, 1,
+ "TEST: Corrupt PPS/Cred*/AAAServerTrustRoot/Root*/CertSHA256FingerPrint");
+ add_text_node_conf_corrupt(ctx, realm, aaa1,
+ "CertSHA256Fingerprint",
+ "aaa_trust_root_cert_fingerprint");
+ } else {
+ add_text_node_conf(ctx, realm, aaa1, "CertSHA256Fingerprint",
+ "aaa_trust_root_cert_fingerprint");
+ }
+
+ if (test && os_strcmp(test, "corrupt_polupd_hash") == 0) {
+ debug_print(ctx, 1,
+ "TEST: Corrupt PPS/Cred*/Policy/PolicyUpdate/Trustroot/CertSHA256FingerPrint");
+ p = xml_node_create(ctx->xml, c, NULL, "Policy");
+ upd = xml_node_create(ctx->xml, p, NULL, "PolicyUpdate");
+ add_text_node(ctx, upd, "UpdateInterval", "30");
+ add_text_node(ctx, upd, "UpdateMethod", "SPP-ClientInitiated");
+ add_text_node(ctx, upd, "Restriction", "Unrestricted");
+ add_text_node_conf(ctx, realm, upd, "URI", "policy_url");
+ trust = xml_node_create(ctx->xml, upd, NULL, "TrustRoot");
+ add_text_node_conf(ctx, realm, trust, "CertURL",
+ "policy_trust_root_cert_url");
+ add_text_node_conf_corrupt(ctx, realm, trust,
+ "CertSHA256Fingerprint",
+ "policy_trust_root_cert_fingerprint");
+ }
+skip_aaa_trust_root:
+
+ upd = xml_node_create(ctx->xml, c, NULL, "SubscriptionUpdate");
+ add_text_node(ctx, upd, "UpdateInterval", "4294967295");
+ add_text_node(ctx, upd, "UpdateMethod", "SPP-ClientInitiated");
+ add_text_node(ctx, upd, "Restriction", "HomeSP");
+ add_text_node_conf(ctx, realm, upd, "URI", "spp_http_auth_url");
+ trust = xml_node_create(ctx->xml, upd, NULL, "TrustRoot");
+ add_text_node_conf(ctx, realm, trust, "CertURL", "trust_root_cert_url");
+ if (test && os_strcmp(test, "corrupt_subrem_hash") == 0) {
+ debug_print(ctx, 1,
+ "TEST: Corrupt PPS/Cred*/SubscriptionUpdate/Trustroot/CertSHA256FingerPrint");
+ add_text_node_conf_corrupt(ctx, realm, trust,
+ "CertSHA256Fingerprint",
+ "trust_root_cert_fingerprint");
+ } else {
+ add_text_node_conf(ctx, realm, trust, "CertSHA256Fingerprint",
+ "trust_root_cert_fingerprint");
+ }
+
+ if (dmacc_username &&
+ !build_username_password(ctx, upd, dmacc_username,
+ dmacc_password)) {
+ xml_node_free(ctx->xml, pps);
+ xml_node_free(ctx->xml, policy_node);
+ return NULL;
+ }
+
+ if (policy_node)
+ xml_node_add_child(ctx->xml, c, policy_node);
+
+ homesp = xml_node_create(ctx->xml, c, NULL, "HomeSP");
+ add_text_node_conf(ctx, realm, homesp, "FriendlyName", "friendly_name");
+ add_text_node_conf(ctx, realm, homesp, "FQDN", "fqdn");
+
+ xml_node_create(ctx->xml, c, NULL, "SubscriptionParameters");
+
+ cred = xml_node_create(ctx->xml, c, NULL, "Credential");
+ add_creation_date(ctx, cred);
+ if (imsi) {
+ xml_node_t *sim;
+ const char *type = "18"; /* default to EAP-SIM */
+
+ sim = xml_node_create(ctx->xml, cred, NULL, "SIM");
+ add_text_node(ctx, sim, "IMSI", imsi);
+ if (ctx->eap_method && os_strcmp(ctx->eap_method, "AKA") == 0)
+ type = "23";
+ else if (ctx->eap_method &&
+ os_strcmp(ctx->eap_method, "AKA'") == 0)
+ type = "50";
+ add_text_node(ctx, sim, "EAPType", type);
+ } else if (cert) {
+ xml_node_t *dc;
+ dc = xml_node_create(ctx->xml, cred, NULL,
+ "DigitalCertificate");
+ add_text_node(ctx, dc, "CertificateType", "x509v3");
+ add_text_node(ctx, dc, "CertSHA256Fingerprint", cert);
+ } else {
+ userpw = build_username_password(ctx, cred, user, pw);
+ add_text_node(ctx, userpw, "MachineManaged",
+ machine_managed ? "TRUE" : "FALSE");
+ eap = xml_node_create(ctx->xml, userpw, NULL, "EAPMethod");
+ add_text_node(ctx, eap, "EAPType", "21");
+ add_text_node(ctx, eap, "InnerMethod", "MS-CHAP-V2");
+ }
+ add_text_node(ctx, cred, "Realm", realm);
+
+ return pps;
+}
+
+
+static xml_node_t * spp_exec_get_certificate(struct hs20_svc *ctx,
+ const char *session_id,
+ const char *user,
+ const char *realm,
+ int add_est_user)
+{
+ xml_namespace_t *ns;
+ xml_node_t *spp_node, *enroll, *exec_node;
+ char *val;
+ char password[11];
+ char *b64;
+
+ if (add_est_user && new_password(password, sizeof(password)) < 0)
+ return NULL;
+
+ spp_node = build_post_dev_data_response(ctx, &ns, session_id, "OK",
+ NULL);
+ if (spp_node == NULL)
+ return NULL;
+
+ exec_node = xml_node_create(ctx->xml, spp_node, ns, "exec");
+
+ enroll = xml_node_create(ctx->xml, exec_node, ns, "getCertificate");
+ xml_node_add_attr(ctx->xml, enroll, NULL, "enrollmentProtocol", "EST");
+
+ val = db_get_osu_config_val(ctx, realm, "est_url");
+ xml_node_create_text(ctx->xml, enroll, ns, "enrollmentServerURI",
+ val ? val : "");
+ os_free(val);
+
+ if (!add_est_user)
+ return spp_node;
+
+ xml_node_create_text(ctx->xml, enroll, ns, "estUserID", user);
+
+ b64 = base64_encode(password, strlen(password), NULL);
+ if (b64 == NULL) {
+ xml_node_free(ctx->xml, spp_node);
+ return NULL;
+ }
+ xml_node_create_text(ctx->xml, enroll, ns, "estPassword", b64);
+ free(b64);
+
+ db_update_session_password(ctx, user, realm, session_id, password);
+
+ return spp_node;
+}
+
+
+static xml_node_t * hs20_user_input_registration(struct hs20_svc *ctx,
+ const char *session_id,
+ int enrollment_done)
+{
+ xml_namespace_t *ns;
+ xml_node_t *spp_node, *node = NULL;
+ xml_node_t *pps, *tnds;
+ char buf[400];
+ char *str;
+ char *user, *realm, *pw, *type, *mm, *test;
+ const char *status;
+ int cert = 0;
+ int machine_managed = 0;
+ char *fingerprint;
+
+ user = db_get_session_val(ctx, NULL, NULL, session_id, "user");
+ realm = db_get_session_val(ctx, NULL, NULL, session_id, "realm");
+ pw = db_get_session_val(ctx, NULL, NULL, session_id, "password");
+
+ if (!user || !realm || !pw) {
+ debug_print(ctx, 1, "Could not find session info from DB for "
+ "the new subscription");
+ free(user);
+ free(realm);
+ free(pw);
+ return NULL;
+ }
+
+ mm = db_get_session_val(ctx, NULL, NULL, session_id, "machine_managed");
+ if (mm && atoi(mm))
+ machine_managed = 1;
+ free(mm);
+
+ type = db_get_session_val(ctx, NULL, NULL, session_id, "type");
+ if (type && strcmp(type, "cert") == 0)
+ cert = 1;
+ free(type);
+
+ if (cert && !enrollment_done) {
+ xml_node_t *ret;
+ hs20_eventlog(ctx, user, realm, session_id,
+ "request client certificate enrollment", NULL);
+ ret = spp_exec_get_certificate(ctx, session_id, user, realm, 1);
+ free(user);
+ free(realm);
+ free(pw);
+ return ret;
+ }
+
+ if (!cert && strlen(pw) == 0) {
+ machine_managed = 1;
+ free(pw);
+ pw = malloc(11);
+ if (pw == NULL || new_password(pw, 11) < 0) {
+ free(user);
+ free(realm);
+ free(pw);
+ return NULL;
+ }
+ }
+
+ status = "Provisioning complete, request sppUpdateResponse";
+ spp_node = build_post_dev_data_response(ctx, &ns, session_id, status,
+ NULL);
+ if (spp_node == NULL)
+ return NULL;
+
+ fingerprint = db_get_session_val(ctx, NULL, NULL, session_id, "cert");
+ test = db_get_session_val(ctx, NULL, NULL, session_id, "test");
+ if (test)
+ debug_print(ctx, 1, "TEST: Requested special behavior: %s",
+ test);
+ pps = build_pps(ctx, user, realm, pw,
+ fingerprint ? fingerprint : NULL, machine_managed,
+ test, NULL, NULL, NULL, NULL);
+ free(fingerprint);
+ free(test);
+ if (!pps) {
+ xml_node_free(ctx->xml, spp_node);
+ free(user);
+ free(realm);
+ free(pw);
+ return NULL;
+ }
+
+ debug_print(ctx, 1, "Request DB subscription registration on success "
+ "notification");
+ if (machine_managed) {
+ db_update_session_password(ctx, user, realm, session_id, pw);
+ db_update_session_machine_managed(ctx, user, realm, session_id,
+ machine_managed);
+ }
+ db_add_session_pps(ctx, user, realm, session_id, pps);
+
+ hs20_eventlog_node(ctx, user, realm, session_id,
+ "new subscription", pps);
+ free(user);
+ free(pw);
+
+ tnds = mo_to_tnds(ctx->xml, pps, 0, URN_HS20_PPS, NULL);
+ xml_node_free(ctx->xml, pps);
+ if (!tnds) {
+ xml_node_free(ctx->xml, spp_node);
+ free(realm);
+ return NULL;
+ }
+
+ str = xml_node_to_str(ctx->xml, tnds);
+ xml_node_free(ctx->xml, tnds);
+ if (str == NULL) {
+ xml_node_free(ctx->xml, spp_node);
+ free(realm);
+ return NULL;
+ }
+
+ node = xml_node_create_text(ctx->xml, spp_node, ns, "addMO", str);
+ free(str);
+ snprintf(buf, sizeof(buf), "./Wi-Fi/%s/PerProviderSubscription", realm);
+ free(realm);
+ xml_node_add_attr(ctx->xml, node, ns, "managementTreeURI", buf);
+ xml_node_add_attr(ctx->xml, node, ns, "moURN", URN_HS20_PPS);
+
+ return spp_node;
+}
+
+
+static xml_node_t * hs20_user_input_free_remediation(struct hs20_svc *ctx,
+ const char *user,
+ const char *realm,
+ const char *session_id)
+{
+ xml_namespace_t *ns;
+ xml_node_t *spp_node;
+ xml_node_t *cred;
+ char buf[400];
+ char *status;
+ char *free_account, *pw;
+
+ free_account = db_get_osu_config_val(ctx, realm, "free_account");
+ if (free_account == NULL)
+ return NULL;
+ pw = db_get_val(ctx, free_account, realm, "password", 0);
+ if (pw == NULL) {
+ free(free_account);
+ return NULL;
+ }
+
+ cred = build_credential_pw(ctx, free_account, realm, pw, 1);
+ free(free_account);
+ free(pw);
+ if (!cred) {
+ xml_node_free(ctx->xml, cred);
+ return NULL;
+ }
+
+ status = "Remediation complete, request sppUpdateResponse";
+ spp_node = build_post_dev_data_response(ctx, &ns, session_id, status,
+ NULL);
+ if (spp_node == NULL)
+ return NULL;
+
+ snprintf(buf, sizeof(buf),
+ "./Wi-Fi/%s/PerProviderSubscription/Cred01/Credential",
+ realm);
+
+ if (add_update_node(ctx, spp_node, ns, buf, cred) < 0) {
+ xml_node_free(ctx->xml, spp_node);
+ return NULL;
+ }
+
+ hs20_eventlog_node(ctx, user, realm, session_id,
+ "free/public remediation", cred);
+ xml_node_free(ctx->xml, cred);
+
+ return spp_node;
+}
+
+
+static xml_node_t * hs20_user_input_complete(struct hs20_svc *ctx,
+ const char *user,
+ const char *realm, int dmacc,
+ const char *session_id)
+{
+ char *val;
+ enum hs20_session_operation oper;
+
+ val = db_get_session_val(ctx, user, realm, session_id, "operation");
+ if (val == NULL) {
+ debug_print(ctx, 1, "No session %s found to continue",
+ session_id);
+ return NULL;
+ }
+ oper = atoi(val);
+ free(val);
+
+ if (oper == USER_REMEDIATION) {
+ return hs20_user_input_remediation(ctx, user, realm, dmacc,
+ session_id);
+ }
+
+ if (oper == FREE_REMEDIATION) {
+ return hs20_user_input_free_remediation(ctx, user, realm,
+ session_id);
+ }
+
+ if (oper == SUBSCRIPTION_REGISTRATION) {
+ return hs20_user_input_registration(ctx, session_id, 0);
+ }
+
+ debug_print(ctx, 1, "User session %s not in state for user input "
+ "completion", session_id);
+ return NULL;
+}
+
+
+static xml_node_t * hs20_cert_reenroll_complete(struct hs20_svc *ctx,
+ const char *session_id)
+{
+ char *user, *realm, *cert;
+ char *status;
+ xml_namespace_t *ns;
+ xml_node_t *spp_node, *cred;
+ char buf[400];
+
+ user = db_get_session_val(ctx, NULL, NULL, session_id, "user");
+ realm = db_get_session_val(ctx, NULL, NULL, session_id, "realm");
+ cert = db_get_session_val(ctx, NULL, NULL, session_id, "cert");
+ if (!user || !realm || !cert) {
+ debug_print(ctx, 1,
+ "Could not find session info from DB for certificate reenrollment");
+ free(user);
+ free(realm);
+ free(cert);
+ return NULL;
+ }
+
+ cred = build_credential_cert(ctx, user, realm, cert);
+ if (!cred) {
+ debug_print(ctx, 1, "Could not build credential");
+ free(user);
+ free(realm);
+ free(cert);
+ return NULL;
+ }
+
+ status = "Remediation complete, request sppUpdateResponse";
+ spp_node = build_post_dev_data_response(ctx, &ns, session_id, status,
+ NULL);
+ if (spp_node == NULL) {
+ debug_print(ctx, 1, "Could not build sppPostDevDataResponse");
+ free(user);
+ free(realm);
+ free(cert);
+ xml_node_free(ctx->xml, cred);
+ return NULL;
+ }
+
+ snprintf(buf, sizeof(buf),
+ "./Wi-Fi/%s/PerProviderSubscription/Cred01/Credential",
+ realm);
+
+ if (add_update_node(ctx, spp_node, ns, buf, cred) < 0) {
+ debug_print(ctx, 1, "Could not add update node");
+ xml_node_free(ctx->xml, spp_node);
+ free(user);
+ free(realm);
+ free(cert);
+ return NULL;
+ }
+
+ hs20_eventlog_node(ctx, user, realm, session_id,
+ "certificate reenrollment", cred);
+ xml_node_free(ctx->xml, cred);
+
+ free(user);
+ free(realm);
+ free(cert);
+ return spp_node;
+}
+
+
+static xml_node_t * hs20_cert_enroll_completed(struct hs20_svc *ctx,
+ const char *user,
+ const char *realm, int dmacc,
+ const char *session_id)
+{
+ char *val;
+ enum hs20_session_operation oper;
+
+ val = db_get_session_val(ctx, NULL, NULL, session_id, "operation");
+ if (val == NULL) {
+ debug_print(ctx, 1, "No session %s found to continue",
+ session_id);
+ return NULL;
+ }
+ oper = atoi(val);
+ free(val);
+
+ if (oper == SUBSCRIPTION_REGISTRATION)
+ return hs20_user_input_registration(ctx, session_id, 1);
+ if (oper == CERT_REENROLL)
+ return hs20_cert_reenroll_complete(ctx, session_id);
+
+ debug_print(ctx, 1, "User session %s not in state for certificate "
+ "enrollment completion", session_id);
+ return NULL;
+}
+
+
+static xml_node_t * hs20_cert_enroll_failed(struct hs20_svc *ctx,
+ const char *user,
+ const char *realm, int dmacc,
+ const char *session_id)
+{
+ char *val;
+ enum hs20_session_operation oper;
+ xml_node_t *spp_node, *node;
+ char *status;
+ xml_namespace_t *ns;
+
+ val = db_get_session_val(ctx, user, realm, session_id, "operation");
+ if (val == NULL) {
+ debug_print(ctx, 1, "No session %s found to continue",
+ session_id);
+ return NULL;
+ }
+ oper = atoi(val);
+ free(val);
+
+ if (oper != SUBSCRIPTION_REGISTRATION) {
+ debug_print(ctx, 1, "User session %s not in state for "
+ "enrollment failure", session_id);
+ return NULL;
+ }
+
+ status = "Error occurred";
+ spp_node = build_post_dev_data_response(ctx, &ns, session_id, status,
+ NULL);
+ if (spp_node == NULL)
+ return NULL;
+ node = xml_node_create(ctx->xml, spp_node, ns, "sppError");
+ xml_node_add_attr(ctx->xml, node, NULL, "errorCode",
+ "Credentials cannot be provisioned at this time");
+ db_remove_session(ctx, user, realm, session_id);
+
+ return spp_node;
+}
+
+
+static xml_node_t * hs20_sim_provisioning(struct hs20_svc *ctx,
+ const char *user,
+ const char *realm, int dmacc,
+ const char *session_id)
+{
+ xml_namespace_t *ns;
+ xml_node_t *spp_node, *node = NULL;
+ xml_node_t *pps, *tnds;
+ char buf[400];
+ char *str;
+ const char *status;
+ char dmacc_username[32];
+ char dmacc_password[32];
+ char *policy;
+ xml_node_t *policy_node = NULL;
+
+ if (!ctx->imsi) {
+ debug_print(ctx, 1, "IMSI not available for SIM provisioning");
+ return NULL;
+ }
+
+ if (new_password(dmacc_username, sizeof(dmacc_username)) < 0 ||
+ new_password(dmacc_password, sizeof(dmacc_password)) < 0) {
+ debug_print(ctx, 1,
+ "Failed to generate DMAcc username/password");
+ return NULL;
+ }
+
+ status = "Provisioning complete, request sppUpdateResponse";
+ spp_node = build_post_dev_data_response(ctx, &ns, session_id, status,
+ NULL);
+ if (!spp_node)
+ return NULL;
+
+ policy = db_get_osu_config_val(ctx, realm, "sim_policy");
+ if (policy) {
+ policy_node = read_policy_file(ctx, policy);
+ os_free(policy);
+ if (!policy_node) {
+ xml_node_free(ctx->xml, spp_node);
+ return NULL;
+ }
+ update_policy_update_uri(ctx, realm, policy_node);
+ node = get_node_uri(ctx->xml, policy_node,
+ "Policy/PolicyUpdate");
+ if (node)
+ build_username_password(ctx, node, dmacc_username,
+ dmacc_password);
+ }
+
+ pps = build_pps(ctx, NULL, realm, NULL, NULL, 0, NULL, ctx->imsi,
+ dmacc_username, dmacc_password, policy_node);
+ if (!pps) {
+ xml_node_free(ctx->xml, spp_node);
+ return NULL;
+ }
+
+ debug_print(ctx, 1,
+ "Request DB subscription registration on success notification");
+ if (!user || !user[0])
+ user = ctx->imsi;
+ db_add_session(ctx, user, realm, session_id, NULL, NULL,
+ SUBSCRIPTION_REGISTRATION, NULL);
+ db_add_session_dmacc(ctx, session_id, dmacc_username, dmacc_password);
+ if (ctx->eap_method)
+ db_add_session_eap_method(ctx, session_id, ctx->eap_method);
+ if (ctx->id_hash)
+ db_add_session_id_hash(ctx, session_id, ctx->id_hash);
+ db_add_session_pps(ctx, user, realm, session_id, pps);
+
+ hs20_eventlog_node(ctx, user, realm, session_id,
+ "new subscription", pps);
+
+ tnds = mo_to_tnds(ctx->xml, pps, 0, URN_HS20_PPS, NULL);
+ xml_node_free(ctx->xml, pps);
+ if (!tnds) {
+ xml_node_free(ctx->xml, spp_node);
+ return NULL;
+ }
+
+ str = xml_node_to_str(ctx->xml, tnds);
+ xml_node_free(ctx->xml, tnds);
+ if (!str) {
+ xml_node_free(ctx->xml, spp_node);
+ return NULL;
+ }
+
+ node = xml_node_create_text(ctx->xml, spp_node, ns, "addMO", str);
+ free(str);
+ snprintf(buf, sizeof(buf), "./Wi-Fi/%s/PerProviderSubscription", realm);
+ xml_node_add_attr(ctx->xml, node, ns, "managementTreeURI", buf);
+ xml_node_add_attr(ctx->xml, node, ns, "moURN", URN_HS20_PPS);
+
+ return spp_node;
+}
+
+
+static xml_node_t * hs20_spp_post_dev_data(struct hs20_svc *ctx,
+ xml_node_t *node,
+ const char *user,
+ const char *realm,
+ const char *session_id,
+ int dmacc)
+{
+ const char *req_reason;
+ char *redirect_uri = NULL;
+ char *req_reason_buf = NULL;
+ char str[200];
+ xml_node_t *ret = NULL, *devinfo = NULL, *devdetail = NULL;
+ xml_node_t *mo, *macaddr;
+ char *version;
+ int valid;
+ char *supp, *pos;
+ char *err;
+ u8 wifi_mac_addr[ETH_ALEN];
+
+ version = xml_node_get_attr_value_ns(ctx->xml, node, SPP_NS_URI,
+ "sppVersion");
+ if (version == NULL || strstr(version, "1.0") == NULL) {
+ ret = build_post_dev_data_response(
+ ctx, NULL, session_id, "Error occurred",
+ "SPP version not supported");
+ hs20_eventlog_node(ctx, user, realm, session_id,
+ "Unsupported sppVersion", ret);
+ xml_node_get_attr_value_free(ctx->xml, version);
+ return ret;
+ }
+ xml_node_get_attr_value_free(ctx->xml, version);
+
+ mo = get_node(ctx->xml, node, "supportedMOList");
+ if (mo == NULL) {
+ ret = build_post_dev_data_response(
+ ctx, NULL, session_id, "Error occurred",
+ "Other");
+ hs20_eventlog_node(ctx, user, realm, session_id,
+ "No supportedMOList element", ret);
+ return ret;
+ }
+ supp = xml_node_get_text(ctx->xml, mo);
+ for (pos = supp; pos && *pos; pos++)
+ *pos = tolower(*pos);
+ if (supp == NULL ||
+ strstr(supp, URN_OMA_DM_DEVINFO) == NULL ||
+ strstr(supp, URN_OMA_DM_DEVDETAIL) == NULL ||
+ strstr(supp, URN_HS20_PPS) == NULL) {
+ xml_node_get_text_free(ctx->xml, supp);
+ ret = build_post_dev_data_response(
+ ctx, NULL, session_id, "Error occurred",
+ "One or more mandatory MOs not supported");
+ hs20_eventlog_node(ctx, user, realm, session_id,
+ "Unsupported MOs", ret);
+ return ret;
+ }
+ xml_node_get_text_free(ctx->xml, supp);
+
+ req_reason_buf = xml_node_get_attr_value(ctx->xml, node,
+ "requestReason");
+ if (req_reason_buf == NULL) {
+ debug_print(ctx, 1, "No requestReason attribute");
+ return NULL;
+ }
+ req_reason = req_reason_buf;
+
+ redirect_uri = xml_node_get_attr_value(ctx->xml, node, "redirectURI");
+
+ debug_print(ctx, 1, "requestReason: %s sessionID: %s redirectURI: %s",
+ req_reason, session_id, redirect_uri);
+ snprintf(str, sizeof(str), "sppPostDevData: requestReason=%s",
+ req_reason);
+ hs20_eventlog(ctx, user, realm, session_id, str, NULL);
+
+ devinfo = spp_get_mo(ctx, node, URN_OMA_DM_DEVINFO, &valid, &err);
+ if (devinfo == NULL) {
+ ret = build_post_dev_data_response(ctx, NULL, session_id,
+ "Error occurred", "Other");
+ hs20_eventlog_node(ctx, user, realm, session_id,
+ "No DevInfo moContainer in sppPostDevData",
+ ret);
+ os_free(err);
+ goto out;
+ }
+
+ hs20_eventlog_node(ctx, user, realm, session_id,
+ "Received DevInfo MO", devinfo);
+ if (valid == 0) {
+ hs20_eventlog(ctx, user, realm, session_id,
+ "OMA-DM DDF DTD validation errors in DevInfo MO",
+ err);
+ ret = build_post_dev_data_response(ctx, NULL, session_id,
+ "Error occurred", "Other");
+ os_free(err);
+ goto out;
+ }
+ os_free(err);
+ if (user)
+ db_update_mo(ctx, user, realm, "devinfo", devinfo);
+
+ devdetail = spp_get_mo(ctx, node, URN_OMA_DM_DEVDETAIL, &valid, &err);
+ if (devdetail == NULL) {
+ ret = build_post_dev_data_response(ctx, NULL, session_id,
+ "Error occurred", "Other");
+ hs20_eventlog_node(ctx, user, realm, session_id,
+ "No DevDetail moContainer in sppPostDevData",
+ ret);
+ os_free(err);
+ goto out;
+ }
+
+ hs20_eventlog_node(ctx, user, realm, session_id,
+ "Received DevDetail MO", devdetail);
+ if (valid == 0) {
+ hs20_eventlog(ctx, user, realm, session_id,
+ "OMA-DM DDF DTD validation errors "
+ "in DevDetail MO", err);
+ ret = build_post_dev_data_response(ctx, NULL, session_id,
+ "Error occurred", "Other");
+ os_free(err);
+ goto out;
+ }
+ os_free(err);
+
+ os_memset(wifi_mac_addr, 0, ETH_ALEN);
+ macaddr = get_node(ctx->xml, devdetail,
+ "Ext/org.wi-fi/Wi-Fi/Wi-FiMACAddress");
+ if (macaddr) {
+ char *addr, buf[50];
+
+ addr = xml_node_get_text(ctx->xml, macaddr);
+ if (addr && hwaddr_compact_aton(addr, wifi_mac_addr) == 0) {
+ snprintf(buf, sizeof(buf), "DevDetail MAC address: "
+ MACSTR, MAC2STR(wifi_mac_addr));
+ hs20_eventlog(ctx, user, realm, session_id, buf, NULL);
+ xml_node_get_text_free(ctx->xml, addr);
+ } else {
+ hs20_eventlog(ctx, user, realm, session_id,
+ "Could not extract MAC address from DevDetail",
+ NULL);
+ }
+ } else {
+ hs20_eventlog(ctx, user, realm, session_id,
+ "No MAC address in DevDetail", NULL);
+ }
+
+ if (user)
+ db_update_mo(ctx, user, realm, "devdetail", devdetail);
+
+ if (user)
+ mo = spp_get_mo(ctx, node, URN_HS20_PPS, &valid, &err);
+ else {
+ mo = NULL;
+ err = NULL;
+ }
+ if (user && mo) {
+ hs20_eventlog_node(ctx, user, realm, session_id,
+ "Received PPS MO", mo);
+ if (valid == 0) {
+ hs20_eventlog(ctx, user, realm, session_id,
+ "OMA-DM DDF DTD validation errors "
+ "in PPS MO", err);
+ xml_node_get_attr_value_free(ctx->xml, redirect_uri);
+ os_free(err);
+ return build_post_dev_data_response(
+ ctx, NULL, session_id,
+ "Error occurred", "Other");
+ }
+ db_update_mo(ctx, user, realm, "pps", mo);
+ db_update_val(ctx, user, realm, "fetch_pps", "0", dmacc);
+ xml_node_free(ctx->xml, mo);
+ }
+ os_free(err);
+
+ if (user && !mo) {
+ char *fetch;
+ int fetch_pps;
+
+ fetch = db_get_val(ctx, user, realm, "fetch_pps", dmacc);
+ fetch_pps = fetch ? atoi(fetch) : 0;
+ free(fetch);
+
+ if (fetch_pps) {
+ enum hs20_session_operation oper;
+ if (strcasecmp(req_reason, "Subscription remediation")
+ == 0)
+ oper = CONTINUE_SUBSCRIPTION_REMEDIATION;
+ else if (strcasecmp(req_reason, "Policy update") == 0)
+ oper = CONTINUE_POLICY_UPDATE;
+ else
+ oper = NO_OPERATION;
+ if (db_add_session(ctx, user, realm, session_id, NULL,
+ NULL, oper, NULL) < 0)
+ goto out;
+
+ ret = spp_exec_upload_mo(ctx, session_id,
+ URN_HS20_PPS);
+ hs20_eventlog_node(ctx, user, realm, session_id,
+ "request PPS MO upload",
+ ret);
+ goto out;
+ }
+ }
+
+ if (user && strcasecmp(req_reason, "MO upload") == 0) {
+ char *val = db_get_session_val(ctx, user, realm, session_id,
+ "operation");
+ enum hs20_session_operation oper;
+ if (!val) {
+ debug_print(ctx, 1, "No session %s found to continue",
+ session_id);
+ goto out;
+ }
+ oper = atoi(val);
+ free(val);
+ if (oper == CONTINUE_SUBSCRIPTION_REMEDIATION)
+ req_reason = "Subscription remediation";
+ else if (oper == CONTINUE_POLICY_UPDATE)
+ req_reason = "Policy update";
+ else {
+ debug_print(ctx, 1,
+ "No pending operation in session %s",
+ session_id);
+ goto out;
+ }
+ }
+
+ if (strcasecmp(req_reason, "Subscription registration") == 0) {
+ ret = hs20_subscription_registration(ctx, realm, session_id,
+ redirect_uri,
+ wifi_mac_addr);
+ hs20_eventlog_node(ctx, user, realm, session_id,
+ "subscription registration response",
+ ret);
+ goto out;
+ }
+ if (user && strcasecmp(req_reason, "Subscription remediation") == 0) {
+ ret = hs20_subscription_remediation(ctx, user, realm,
+ session_id, dmacc,
+ redirect_uri);
+ hs20_eventlog_node(ctx, user, realm, session_id,
+ "subscription remediation response",
+ ret);
+ goto out;
+ }
+ if (user && strcasecmp(req_reason, "Policy update") == 0) {
+ ret = hs20_policy_update(ctx, user, realm, session_id, dmacc);
+ hs20_eventlog_node(ctx, user, realm, session_id,
+ "policy update response",
+ ret);
+ goto out;
+ }
+
+ if (strcasecmp(req_reason, "User input completed") == 0) {
+ db_add_session_devinfo(ctx, session_id, devinfo);
+ db_add_session_devdetail(ctx, session_id, devdetail);
+ ret = hs20_user_input_complete(ctx, user, realm, dmacc,
+ session_id);
+ hs20_eventlog_node(ctx, user, realm, session_id,
+ "user input completed response", ret);
+ goto out;
+ }
+
+ if (strcasecmp(req_reason, "Certificate enrollment completed") == 0) {
+ ret = hs20_cert_enroll_completed(ctx, user, realm, dmacc,
+ session_id);
+ hs20_eventlog_node(ctx, user, realm, session_id,
+ "certificate enrollment response", ret);
+ goto out;
+ }
+
+ if (strcasecmp(req_reason, "Certificate enrollment failed") == 0) {
+ ret = hs20_cert_enroll_failed(ctx, user, realm, dmacc,
+ session_id);
+ hs20_eventlog_node(ctx, user, realm, session_id,
+ "certificate enrollment failed response",
+ ret);
+ goto out;
+ }
+
+ if (strcasecmp(req_reason, "Subscription provisioning") == 0) {
+ ret = hs20_sim_provisioning(ctx, user, realm, dmacc,
+ session_id);
+ hs20_eventlog_node(ctx, user, realm, session_id,
+ "subscription provisioning response",
+ ret);
+ goto out;
+ }
+
+ debug_print(ctx, 1, "Unsupported requestReason '%s' user '%s'",
+ req_reason, user);
+out:
+ xml_node_get_attr_value_free(ctx->xml, req_reason_buf);
+ xml_node_get_attr_value_free(ctx->xml, redirect_uri);
+ if (devinfo)
+ xml_node_free(ctx->xml, devinfo);
+ if (devdetail)
+ xml_node_free(ctx->xml, devdetail);
+ return ret;
+}
+
+
+static xml_node_t * build_spp_exchange_complete(struct hs20_svc *ctx,
+ const char *session_id,
+ const char *status,
+ const char *error_code)
+{
+ xml_namespace_t *ns;
+ xml_node_t *spp_node, *node;
+
+ spp_node = xml_node_create_root(ctx->xml, SPP_NS_URI, "spp", &ns,
+ "sppExchangeComplete");
+
+
+ xml_node_add_attr(ctx->xml, spp_node, ns, "sppVersion", "1.0");
+ xml_node_add_attr(ctx->xml, spp_node, ns, "sessionID", session_id);
+ xml_node_add_attr(ctx->xml, spp_node, ns, "sppStatus", status);
+
+ if (error_code) {
+ node = xml_node_create(ctx->xml, spp_node, ns, "sppError");
+ xml_node_add_attr(ctx->xml, node, NULL, "errorCode",
+ error_code);
+ }
+
+ return spp_node;
+}
+
+
+static int add_subscription(struct hs20_svc *ctx, const char *session_id)
+{
+ char *user, *realm, *pw, *pw_mm, *pps, *str;
+ char *osu_user, *osu_password, *eap_method;
+ char *policy = NULL;
+ char *sql;
+ int ret = -1;
+ char *free_account;
+ int free_acc;
+ char *type;
+ int cert = 0;
+ char *cert_pem, *fingerprint;
+ const char *method;
+
+ user = db_get_session_val(ctx, NULL, NULL, session_id, "user");
+ realm = db_get_session_val(ctx, NULL, NULL, session_id, "realm");
+ pw = db_get_session_val(ctx, NULL, NULL, session_id, "password");
+ pw_mm = db_get_session_val(ctx, NULL, NULL, session_id,
+ "machine_managed");
+ pps = db_get_session_val(ctx, NULL, NULL, session_id, "pps");
+ cert_pem = db_get_session_val(ctx, NULL, NULL, session_id, "cert_pem");
+ fingerprint = db_get_session_val(ctx, NULL, NULL, session_id, "cert");
+ type = db_get_session_val(ctx, NULL, NULL, session_id, "type");
+ if (type && strcmp(type, "cert") == 0)
+ cert = 1;
+ free(type);
+ osu_user = db_get_session_val(ctx, NULL, NULL, session_id, "osu_user");
+ osu_password = db_get_session_val(ctx, NULL, NULL, session_id,
+ "osu_password");
+ eap_method = db_get_session_val(ctx, NULL, NULL, session_id,
+ "eap_method");
+
+ if (!user || !realm || !pw) {
+ debug_print(ctx, 1, "Could not find session info from DB for "
+ "the new subscription");
+ goto out;
+ }
+
+ free_account = db_get_osu_config_val(ctx, realm, "free_account");
+ free_acc = free_account && strcmp(free_account, user) == 0;
+ free(free_account);
+
+ policy = db_get_osu_config_val(ctx, realm, "sim_policy");
+
+ debug_print(ctx, 1,
+ "New subscription: user='%s' realm='%s' free_acc=%d",
+ user, realm, free_acc);
+ debug_print(ctx, 1, "New subscription: pps='%s'", pps);
+
+ sql = sqlite3_mprintf("UPDATE eventlog SET user=%Q, realm=%Q WHERE "
+ "sessionid=%Q AND (user='' OR user IS NULL)",
+ user, realm, session_id);
+ if (sql) {
+ debug_print(ctx, 1, "DB: %s", sql);
+ if (sqlite3_exec(ctx->db, sql, NULL, NULL, NULL) != SQLITE_OK) {
+ debug_print(ctx, 1, "Failed to update eventlog in "
+ "sqlite database: %s",
+ sqlite3_errmsg(ctx->db));
+ }
+ sqlite3_free(sql);
+ }
+
+ if (free_acc) {
+ hs20_eventlog(ctx, user, realm, session_id,
+ "completed shared free account registration",
+ NULL);
+ ret = 0;
+ goto out;
+ }
+
+ str = db_get_session_val(ctx, NULL, NULL, session_id, "mac_addr");
+
+ if (eap_method && eap_method[0])
+ method = eap_method;
+ else
+ method = cert ? "TLS" : "TTLS-MSCHAPV2";
+ sql = sqlite3_mprintf("INSERT INTO users(identity,realm,phase2,methods,cert,cert_pem,machine_managed,mac_addr,osu_user,osu_password,policy) VALUES (%Q,%Q,%d,%Q,%Q,%Q,%d,%Q,%Q,%Q,%Q)",
+ user, realm, cert ? 0 : 1,
+ method,
+ fingerprint ? fingerprint : "",
+ cert_pem ? cert_pem : "",
+ pw_mm && atoi(pw_mm) ? 1 : 0,
+ str ? str : "",
+ osu_user ? osu_user : "",
+ osu_password ? osu_password : "",
+ policy ? policy : "");
+ free(str);
+ if (sql == NULL)
+ goto out;
+ debug_print(ctx, 1, "DB: %s", sql);
+ if (sqlite3_exec(ctx->db, sql, NULL, NULL, NULL) != SQLITE_OK) {
+ debug_print(ctx, 1, "Failed to add user in sqlite database: %s",
+ sqlite3_errmsg(ctx->db));
+ sqlite3_free(sql);
+ goto out;
+ }
+ sqlite3_free(sql);
+
+ if (cert)
+ ret = 0;
+ else
+ ret = update_password(ctx, user, realm, pw, 0);
+ if (ret < 0) {
+ sql = sqlite3_mprintf("DELETE FROM users WHERE identity=%Q AND realm=%Q AND (phase2=1 OR methods='TLS')",
+ user, realm);
+ if (sql) {
+ debug_print(ctx, 1, "DB: %s", sql);
+ sqlite3_exec(ctx->db, sql, NULL, NULL, NULL);
+ sqlite3_free(sql);
+ }
+ }
+
+ if (pps)
+ db_update_mo_str(ctx, user, realm, "pps", pps);
+
+ str = db_get_session_val(ctx, NULL, NULL, session_id, "devinfo");
+ if (str) {
+ db_update_mo_str(ctx, user, realm, "devinfo", str);
+ free(str);
+ }
+
+ str = db_get_session_val(ctx, NULL, NULL, session_id, "devdetail");
+ if (str) {
+ db_update_mo_str(ctx, user, realm, "devdetail", str);
+ free(str);
+ }
+
+ if (cert && user) {
+ const char *serialnum;
+
+ str = db_get_session_val(ctx, NULL, NULL, session_id,
+ "mac_addr");
+
+ if (os_strncmp(user, "cert-", 5) == 0)
+ serialnum = user + 5;
+ else
+ serialnum = "";
+ sql = sqlite3_mprintf("INSERT OR REPLACE INTO cert_enroll (mac_addr,user,realm,serialnum) VALUES(%Q,%Q,%Q,%Q)",
+ str ? str : "", user, realm ? realm : "",
+ serialnum);
+ free(str);
+ if (sql) {
+ debug_print(ctx, 1, "DB: %s", sql);
+ if (sqlite3_exec(ctx->db, sql, NULL, NULL, NULL) !=
+ SQLITE_OK) {
+ debug_print(ctx, 1,
+ "Failed to add cert_enroll entry into sqlite database: %s",
+ sqlite3_errmsg(ctx->db));
+ }
+ sqlite3_free(sql);
+ }
+ }
+
+ str = db_get_session_val(ctx, NULL, NULL, session_id,
+ "mobile_identifier_hash");
+ if (str) {
+ sql = sqlite3_mprintf("DELETE FROM sim_provisioning WHERE mobile_identifier_hash=%Q",
+ str);
+ if (sql) {
+ debug_print(ctx, 1, "DB: %s", sql);
+ if (sqlite3_exec(ctx->db, sql, NULL, NULL, NULL) !=
+ SQLITE_OK) {
+ debug_print(ctx, 1,
+ "Failed to delete pending sim_provisioning entry: %s",
+ sqlite3_errmsg(ctx->db));
+ }
+ sqlite3_free(sql);
+ }
+ os_free(str);
+ }
+
+ if (ret == 0) {
+ hs20_eventlog(ctx, user, realm, session_id,
+ "completed subscription registration", NULL);
+ }
+
+out:
+ free(user);
+ free(realm);
+ free(pw);
+ free(pw_mm);
+ free(pps);
+ free(cert_pem);
+ free(fingerprint);
+ free(osu_user);
+ free(osu_password);
+ free(eap_method);
+ os_free(policy);
+ return ret;
+}
+
+
+static xml_node_t * hs20_spp_update_response(struct hs20_svc *ctx,
+ xml_node_t *node,
+ const char *user,
+ const char *realm,
+ const char *session_id,
+ int dmacc)
+{
+ char *status;
+ xml_node_t *ret;
+ char *val;
+ enum hs20_session_operation oper;
+
+ status = xml_node_get_attr_value_ns(ctx->xml, node, SPP_NS_URI,
+ "sppStatus");
+ if (status == NULL) {
+ debug_print(ctx, 1, "No sppStatus attribute");
+ return NULL;
+ }
+
+ debug_print(ctx, 1, "sppUpdateResponse: sppStatus: %s sessionID: %s",
+ status, session_id);
+
+ val = db_get_session_val(ctx, NULL, NULL, session_id, "operation");
+ if (!val) {
+ debug_print(ctx, 1,
+ "No session active for sessionID: %s",
+ session_id);
+ oper = NO_OPERATION;
+ } else
+ oper = atoi(val);
+
+ if (strcasecmp(status, "OK") == 0) {
+ char *new_pw = NULL;
+
+ xml_node_get_attr_value_free(ctx->xml, status);
+
+ if (oper == USER_REMEDIATION) {
+ new_pw = db_get_session_val(ctx, user, realm,
+ session_id, "password");
+ if (new_pw == NULL || strlen(new_pw) == 0) {
+ free(new_pw);
+ ret = build_spp_exchange_complete(
+ ctx, session_id, "Error occurred",
+ "Other");
+ hs20_eventlog_node(ctx, user, realm,
+ session_id, "No password "
+ "had been assigned for "
+ "session", ret);
+ db_remove_session(ctx, user, realm, session_id);
+ return ret;
+ }
+ oper = UPDATE_PASSWORD;
+ }
+ if (oper == UPDATE_PASSWORD) {
+ if (!new_pw) {
+ new_pw = db_get_session_val(ctx, user, realm,
+ session_id,
+ "password");
+ if (!new_pw) {
+ db_remove_session(ctx, user, realm,
+ session_id);
+ return NULL;
+ }
+ }
+ debug_print(ctx, 1, "Update user '%s' password in DB",
+ user);
+ if (update_password(ctx, user, realm, new_pw, dmacc) <
+ 0) {
+ debug_print(ctx, 1, "Failed to update user "
+ "'%s' password in DB", user);
+ ret = build_spp_exchange_complete(
+ ctx, session_id, "Error occurred",
+ "Other");
+ hs20_eventlog_node(ctx, user, realm,
+ session_id, "Failed to "
+ "update database", ret);
+ db_remove_session(ctx, user, realm, session_id);
+ return ret;
+ }
+ hs20_eventlog(ctx, user, realm,
+ session_id, "Updated user password "
+ "in database", NULL);
+ }
+ if (oper == CLEAR_REMEDIATION) {
+ debug_print(ctx, 1,
+ "Clear remediation requirement for user '%s' in DB",
+ user);
+ if (clear_remediation(ctx, user, realm, dmacc) < 0) {
+ debug_print(ctx, 1,
+ "Failed to clear remediation requirement for user '%s' in DB",
+ user);
+ ret = build_spp_exchange_complete(
+ ctx, session_id, "Error occurred",
+ "Other");
+ hs20_eventlog_node(ctx, user, realm,
+ session_id,
+ "Failed to update database",
+ ret);
+ db_remove_session(ctx, user, realm, session_id);
+ return ret;
+ }
+ hs20_eventlog(ctx, user, realm,
+ session_id,
+ "Cleared remediation requirement in database",
+ NULL);
+ }
+ if (oper == SUBSCRIPTION_REGISTRATION) {
+ if (add_subscription(ctx, session_id) < 0) {
+ debug_print(ctx, 1, "Failed to add "
+ "subscription into DB");
+ ret = build_spp_exchange_complete(
+ ctx, session_id, "Error occurred",
+ "Other");
+ hs20_eventlog_node(ctx, user, realm,
+ session_id, "Failed to "
+ "update database", ret);
+ db_remove_session(ctx, user, realm, session_id);
+ return ret;
+ }
+ }
+ if (oper == POLICY_REMEDIATION || oper == POLICY_UPDATE) {
+ char *val;
+ val = db_get_val(ctx, user, realm, "remediation",
+ dmacc);
+ if (val && strcmp(val, "policy") == 0)
+ db_update_val(ctx, user, realm, "remediation",
+ "", dmacc);
+ free(val);
+ }
+ if (oper == POLICY_UPDATE)
+ db_update_val(ctx, user, realm, "polupd_done", "1",
+ dmacc);
+ if (oper == CERT_REENROLL) {
+ char *new_user;
+ char event[200];
+
+ new_user = db_get_session_val(ctx, NULL, NULL,
+ session_id, "user");
+ if (!new_user) {
+ debug_print(ctx, 1,
+ "Failed to find new user name (cert-serialnum)");
+ ret = build_spp_exchange_complete(
+ ctx, session_id, "Error occurred",
+ "Other");
+ hs20_eventlog_node(ctx, user, realm,
+ session_id,
+ "Failed to find new user name (cert reenroll)",
+ ret);
+ db_remove_session(ctx, NULL, NULL, session_id);
+ return ret;
+ }
+
+ debug_print(ctx, 1,
+ "Update certificate user entry to use the new serial number (old=%s new=%s)",
+ user, new_user);
+ os_snprintf(event, sizeof(event), "renamed user to: %s",
+ new_user);
+ hs20_eventlog(ctx, user, realm, session_id, event,
+ NULL);
+
+ if (db_update_val(ctx, user, realm, "identity",
+ new_user, 0) < 0 ||
+ db_update_val(ctx, new_user, realm, "remediation",
+ "", 0) < 0) {
+ debug_print(ctx, 1,
+ "Failed to update user name (cert-serialnum)");
+ ret = build_spp_exchange_complete(
+ ctx, session_id, "Error occurred",
+ "Other");
+ hs20_eventlog_node(ctx, user, realm,
+ session_id,
+ "Failed to update user name (cert reenroll)",
+ ret);
+ db_remove_session(ctx, NULL, NULL, session_id);
+ os_free(new_user);
+ return ret;
+ }
+
+ os_free(new_user);
+ }
+ ret = build_spp_exchange_complete(
+ ctx, session_id,
+ "Exchange complete, release TLS connection", NULL);
+ hs20_eventlog_node(ctx, user, realm, session_id,
+ "Exchange completed", ret);
+ db_remove_session(ctx, NULL, NULL, session_id);
+ return ret;
+ }
+
+ ret = build_spp_exchange_complete(ctx, session_id, "Error occurred",
+ "Other");
+ hs20_eventlog_node(ctx, user, realm, session_id, "Error occurred", ret);
+ db_remove_session(ctx, user, realm, session_id);
+ xml_node_get_attr_value_free(ctx->xml, status);
+ return ret;
+}
+
+
+#define SPP_SESSION_ID_LEN 16
+
+static char * gen_spp_session_id(void)
+{
+ FILE *f;
+ int i;
+ char *session;
+
+ session = os_malloc(SPP_SESSION_ID_LEN * 2 + 1);
+ if (session == NULL)
+ return NULL;
+
+ f = fopen("/dev/urandom", "r");
+ if (f == NULL) {
+ os_free(session);
+ return NULL;
+ }
+ for (i = 0; i < SPP_SESSION_ID_LEN; i++)
+ os_snprintf(session + i * 2, 3, "%02x", fgetc(f));
+
+ fclose(f);
+ return session;
+}
+
+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)
+{
+ xml_node_t *ret = NULL;
+ char *session_id;
+ const char *op_name;
+ char *xml_err;
+ char fname[200];
+
+ debug_dump_node(ctx, "received request", node);
+
+ if (!dmacc && auth_user && auth_realm) {
+ char *real;
+ real = db_get_val(ctx, auth_user, auth_realm, "identity", 0);
+ if (!real) {
+ real = db_get_val(ctx, auth_user, auth_realm,
+ "identity", 1);
+ if (real)
+ dmacc = 1;
+ }
+ os_free(real);
+ }
+
+ snprintf(fname, sizeof(fname), "%s/spp/spp.xsd", ctx->root_dir);
+ if (xml_validate(ctx->xml, node, fname, &xml_err) < 0) {
+ /*
+ * We may not be able to extract the sessionID from invalid
+ * input, but well, we can try.
+ */
+ session_id = xml_node_get_attr_value_ns(ctx->xml, node,
+ SPP_NS_URI,
+ "sessionID");
+ debug_print(ctx, 1,
+ "SPP message failed validation, xsd file: %s xml-error: %s",
+ fname, xml_err);
+ hs20_eventlog_node(ctx, auth_user, auth_realm, session_id,
+ "SPP message failed validation", node);
+ hs20_eventlog(ctx, auth_user, auth_realm, session_id,
+ "Validation errors", xml_err);
+ os_free(xml_err);
+ xml_node_get_attr_value_free(ctx->xml, session_id);
+ /* TODO: what to return here? */
+ ret = xml_node_create_root(ctx->xml, NULL, NULL, NULL,
+ "SppValidationError");
+ return ret;
+ }
+
+ session_id = xml_node_get_attr_value_ns(ctx->xml, node, SPP_NS_URI,
+ "sessionID");
+ if (session_id) {
+ char *tmp;
+ debug_print(ctx, 1, "Received sessionID %s", session_id);
+ tmp = os_strdup(session_id);
+ xml_node_get_attr_value_free(ctx->xml, session_id);
+ if (tmp == NULL)
+ return NULL;
+ session_id = tmp;
+ } else {
+ session_id = gen_spp_session_id();
+ if (session_id == NULL) {
+ debug_print(ctx, 1, "Failed to generate sessionID");
+ return NULL;
+ }
+ debug_print(ctx, 1, "Generated sessionID %s", session_id);
+ }
+
+ op_name = xml_node_get_localname(ctx->xml, node);
+ if (op_name == NULL) {
+ debug_print(ctx, 1, "Could not get op_name");
+ return NULL;
+ }
+
+ if (strcmp(op_name, "sppPostDevData") == 0) {
+ hs20_eventlog_node(ctx, auth_user, auth_realm, session_id,
+ "sppPostDevData received and validated",
+ node);
+ ret = hs20_spp_post_dev_data(ctx, node, auth_user, auth_realm,
+ session_id, dmacc);
+ } else if (strcmp(op_name, "sppUpdateResponse") == 0) {
+ hs20_eventlog_node(ctx, auth_user, auth_realm, session_id,
+ "sppUpdateResponse received and validated",
+ node);
+ ret = hs20_spp_update_response(ctx, node, auth_user,
+ auth_realm, session_id, dmacc);
+ } else {
+ hs20_eventlog_node(ctx, auth_user, auth_realm, session_id,
+ "Unsupported SPP message received and "
+ "validated", node);
+ debug_print(ctx, 1, "Unsupported operation '%s'", op_name);
+ /* TODO: what to return here? */
+ ret = xml_node_create_root(ctx->xml, NULL, NULL, NULL,
+ "SppUnknownCommandError");
+ }
+ os_free(session_id);
+
+ if (ret == NULL) {
+ /* TODO: what to return here? */
+ ret = xml_node_create_root(ctx->xml, NULL, NULL, NULL,
+ "SppInternalError");
+ }
+
+ return ret;
+}
+
+
+int hs20_spp_server_init(struct hs20_svc *ctx)
+{
+ char fname[200];
+ ctx->db = NULL;
+ snprintf(fname, sizeof(fname), "%s/AS/DB/eap_user.db", ctx->root_dir);
+ if (sqlite3_open(fname, &ctx->db)) {
+ printf("Failed to open sqlite database: %s\n",
+ sqlite3_errmsg(ctx->db));
+ sqlite3_close(ctx->db);
+ return -1;
+ }
+
+ return 0;
+}
+
+
+void hs20_spp_server_deinit(struct hs20_svc *ctx)
+{
+ sqlite3_close(ctx->db);
+ ctx->db = NULL;
+}
diff --git a/contrib/wpa/hs20/server/spp_server.h b/contrib/wpa/hs20/server/spp_server.h
new file mode 100644
index 000000000000..421974c607b8
--- /dev/null
+++ b/contrib/wpa/hs20/server/spp_server.h
@@ -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 */
diff --git a/contrib/wpa/hs20/server/sql-example.txt b/contrib/wpa/hs20/server/sql-example.txt
new file mode 100644
index 000000000000..20dcf2f5c688
--- /dev/null
+++ b/contrib/wpa/hs20/server/sql-example.txt
@@ -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');
diff --git a/contrib/wpa/hs20/server/sql.txt b/contrib/wpa/hs20/server/sql.txt
new file mode 100644
index 000000000000..2cc6edea4063
--- /dev/null
+++ b/contrib/wpa/hs20/server/sql.txt
@@ -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
+);
diff --git a/contrib/wpa/hs20/server/www/add-free.php b/contrib/wpa/hs20/server/www/add-free.php
new file mode 100644
index 000000000000..1efc65563274
--- /dev/null
+++ b/contrib/wpa/hs20/server/www/add-free.php
@@ -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);
+
+?>
diff --git a/contrib/wpa/hs20/server/www/add-mo.php b/contrib/wpa/hs20/server/www/add-mo.php
new file mode 100644
index 000000000000..a3b4513531f8
--- /dev/null
+++ b/contrib/wpa/hs20/server/www/add-mo.php
@@ -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);
+
+?>
diff --git a/contrib/wpa/hs20/server/www/cert-enroll.php b/contrib/wpa/hs20/server/www/cert-enroll.php
new file mode 100644
index 000000000000..f023ca5a5b03
--- /dev/null
+++ b/contrib/wpa/hs20/server/www/cert-enroll.php
@@ -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);
+
+?>
diff --git a/contrib/wpa/hs20/server/www/config.php b/contrib/wpa/hs20/server/www/config.php
new file mode 100644
index 000000000000..4272b102a88c
--- /dev/null
+++ b/contrib/wpa/hs20/server/www/config.php
@@ -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"
+?>
diff --git a/contrib/wpa/hs20/server/www/est.php b/contrib/wpa/hs20/server/www/est.php
new file mode 100644
index 000000000000..b7fb260d56c4
--- /dev/null
+++ b/contrib/wpa/hs20/server/www/est.php
@@ -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");
+}
+
+?>
diff --git a/contrib/wpa/hs20/server/www/free-remediation.php b/contrib/wpa/hs20/server/www/free-remediation.php
new file mode 100644
index 000000000000..5648b30e8d6b
--- /dev/null
+++ b/contrib/wpa/hs20/server/www/free-remediation.php
@@ -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>
diff --git a/contrib/wpa/hs20/server/www/free.php b/contrib/wpa/hs20/server/www/free.php
new file mode 100644
index 000000000000..8195069ed8ff
--- /dev/null
+++ b/contrib/wpa/hs20/server/www/free.php
@@ -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>
diff --git a/contrib/wpa/hs20/server/www/redirect.php b/contrib/wpa/hs20/server/www/redirect.php
new file mode 100644
index 000000000000..8fc9cd644273
--- /dev/null
+++ b/contrib/wpa/hs20/server/www/redirect.php
@@ -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')");
+
+?>
diff --git a/contrib/wpa/hs20/server/www/remediation-pw.php b/contrib/wpa/hs20/server/www/remediation-pw.php
new file mode 100644
index 000000000000..76fdccbdf9f7
--- /dev/null
+++ b/contrib/wpa/hs20/server/www/remediation-pw.php
@@ -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);
+
+?>
diff --git a/contrib/wpa/hs20/server/www/remediation.php b/contrib/wpa/hs20/server/www/remediation.php
new file mode 100644
index 000000000000..3628065ac225
--- /dev/null
+++ b/contrib/wpa/hs20/server/www/remediation.php
@@ -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>
diff --git a/contrib/wpa/hs20/server/www/signup.php b/contrib/wpa/hs20/server/www/signup.php
new file mode 100644
index 000000000000..80a9d403e8fc
--- /dev/null
+++ b/contrib/wpa/hs20/server/www/signup.php
@@ -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>
diff --git a/contrib/wpa/hs20/server/www/spp.php b/contrib/wpa/hs20/server/www/spp.php
new file mode 100644
index 000000000000..c56d3d69e0ed
--- /dev/null
+++ b/contrib/wpa/hs20/server/www/spp.php
@@ -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);
+
+?>
diff --git a/contrib/wpa/hs20/server/www/terms.php b/contrib/wpa/hs20/server/www/terms.php
new file mode 100644
index 000000000000..acba23ef1ad7
--- /dev/null
+++ b/contrib/wpa/hs20/server/www/terms.php
@@ -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>
diff --git a/contrib/wpa/hs20/server/www/users.php b/contrib/wpa/hs20/server/www/users.php
new file mode 100644
index 000000000000..2bd555275dda
--- /dev/null
+++ b/contrib/wpa/hs20/server/www/users.php
@@ -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>
diff --git a/contrib/wpa/src/common/dragonfly.c b/contrib/wpa/src/common/dragonfly.c
index 1e842716668e..547be66f1561 100644
--- a/contrib/wpa/src/common/dragonfly.c
+++ b/contrib/wpa/src/common/dragonfly.c
@@ -213,37 +213,3 @@ int dragonfly_generate_scalar(const struct crypto_bignum *order,
"dragonfly: Unable to get randomness for own scalar");
return -1;
}
-
-
-/* res = sqrt(val) */
-int dragonfly_sqrt(struct crypto_ec *ec, const struct crypto_bignum *val,
- struct crypto_bignum *res)
-{
- const struct crypto_bignum *prime;
- struct crypto_bignum *tmp, *one;
- int ret = 0;
- u8 prime_bin[DRAGONFLY_MAX_ECC_PRIME_LEN];
- size_t prime_len;
-
- /* For prime p such that p = 3 mod 4, sqrt(w) = w^((p+1)/4) mod p */
-
- prime = crypto_ec_get_prime(ec);
- prime_len = crypto_ec_prime_len(ec);
- tmp = crypto_bignum_init();
- one = crypto_bignum_init_uint(1);
-
- if (crypto_bignum_to_bin(prime, prime_bin, sizeof(prime_bin),
- prime_len) < 0 ||
- (prime_bin[prime_len - 1] & 0x03) != 3 ||
- !tmp || !one ||
- /* tmp = (p+1)/4 */
- crypto_bignum_add(prime, one, tmp) < 0 ||
- crypto_bignum_rshift(tmp, 2, tmp) < 0 ||
- /* res = sqrt(val) */
- crypto_bignum_exptmod(val, tmp, prime, res) < 0)
- ret = -1;
-
- crypto_bignum_deinit(tmp, 0);
- crypto_bignum_deinit(one, 0);
- return ret;
-}
diff --git a/contrib/wpa/src/common/dragonfly.h b/contrib/wpa/src/common/dragonfly.h
index 84d67f575c54..ec3dd593eda4 100644
--- a/contrib/wpa/src/common/dragonfly.h
+++ b/contrib/wpa/src/common/dragonfly.h
@@ -27,7 +27,5 @@ int dragonfly_generate_scalar(const struct crypto_bignum *order,
struct crypto_bignum *_rand,
struct crypto_bignum *_mask,
struct crypto_bignum *scalar);
-int dragonfly_sqrt(struct crypto_ec *ec, const struct crypto_bignum *val,
- struct crypto_bignum *res);
#endif /* DRAGONFLY_H */
diff --git a/contrib/wpa/src/common/qca-vendor.h b/contrib/wpa/src/common/qca-vendor.h
index b77e29939195..d9eab0212e73 100644
--- a/contrib/wpa/src/common/qca-vendor.h
+++ b/contrib/wpa/src/common/qca-vendor.h
@@ -1462,11 +1462,6 @@ enum qca_wlan_vendor_attr_p2p_listen_offload {
* Used with event to notify the puncture pattern selected in ACS operation.
* Encoding for this attribute will follow the convention used in the Disabled
* Subchannel Bitmap field of the EHT Operation IE.
- *
- * @QCA_WLAN_VENDOR_ATTR_ACS_EHT_ENABLED: Flag attribute.
- * Used with command to configure ACS operation for EHT mode.
- * Disable (flag attribute not present) - EHT disabled and
- * Enable (flag attribute present) - EHT enabled.
*/
enum qca_wlan_vendor_attr_acs_offload {
QCA_WLAN_VENDOR_ATTR_ACS_CHANNEL_INVALID = 0,
@@ -1488,7 +1483,6 @@ enum qca_wlan_vendor_attr_acs_offload {
QCA_WLAN_VENDOR_ATTR_ACS_EDMG_ENABLED = 16,
QCA_WLAN_VENDOR_ATTR_ACS_EDMG_CHANNEL = 17,
QCA_WLAN_VENDOR_ATTR_ACS_PUNCTURE_BITMAP = 18,
- QCA_WLAN_VENDOR_ATTR_ACS_EHT_ENABLED = 19,
/* keep last */
QCA_WLAN_VENDOR_ATTR_ACS_AFTER_LAST,
@@ -1794,53 +1788,36 @@ enum qca_access_policy {
};
/**
- * enum qca_vendor_attr_tsf_cmd: Vendor attributes for TSF capture
- * @QCA_WLAN_VENDOR_ATTR_TSF_CMD: Required (u32)
- * Specify the TSF command. Possible values are defined in
- * &enum qca_tsf_cmd.
- * @QCA_WLAN_VENDOR_ATTR_TSF_TIMER_VALUE: Optional (u64)
- * This attribute contains TSF timer value. This attribute is only available
- * in %QCA_TSF_GET or %QCA_TSF_SYNC_GET response.
- * @QCA_WLAN_VENDOR_ATTR_TSF_SOC_TIMER_VALUE: Optional (u64)
- * This attribute contains SOC timer value at TSF capture. This attribute is
- * only available in %QCA_TSF_GET or %QCA_TSF_SYNC_GET response.
- * @QCA_WLAN_VENDOR_ATTR_TSF_SYNC_INTERVAL: Optional (u32)
- * This attribute is used to provide TSF sync interval and only applicable when
- * TSF command is %QCA_TSF_SYNC_START. If this attribute is not provided, the
- * driver will use the default value. Time unit is in milliseconds.
+ * enum qca_vendor_attr_get_tsf: Vendor attributes for TSF capture
+ * @QCA_WLAN_VENDOR_ATTR_TSF_CMD: enum qca_tsf_operation (u32)
+ * @QCA_WLAN_VENDOR_ATTR_TSF_TIMER_VALUE: Unsigned 64 bit TSF timer value
+ * @QCA_WLAN_VENDOR_ATTR_TSF_SOC_TIMER_VALUE: Unsigned 64 bit Synchronized
+ * SOC timer value at TSF capture
*/
enum qca_vendor_attr_tsf_cmd {
QCA_WLAN_VENDOR_ATTR_TSF_INVALID = 0,
QCA_WLAN_VENDOR_ATTR_TSF_CMD,
QCA_WLAN_VENDOR_ATTR_TSF_TIMER_VALUE,
QCA_WLAN_VENDOR_ATTR_TSF_SOC_TIMER_VALUE,
- QCA_WLAN_VENDOR_ATTR_TSF_SYNC_INTERVAL,
QCA_WLAN_VENDOR_ATTR_TSF_AFTER_LAST,
QCA_WLAN_VENDOR_ATTR_TSF_MAX =
QCA_WLAN_VENDOR_ATTR_TSF_AFTER_LAST - 1
};
/**
- * enum qca_tsf_cmd: TSF driver commands
+ * enum qca_tsf_operation: TSF driver commands
* @QCA_TSF_CAPTURE: Initiate TSF Capture
* @QCA_TSF_GET: Get TSF capture value
* @QCA_TSF_SYNC_GET: Initiate TSF capture and return with captured value
* @QCA_TSF_AUTO_REPORT_ENABLE: Used in STA mode only. Once set, the target
* will automatically send TSF report to the host. To query
- * %QCA_WLAN_VENDOR_ATTR_GET_STA_INFO_UPLINK_DELAY, this operation needs to be
+ * QCA_WLAN_VENDOR_ATTR_GET_STA_INFO_UPLINK_DELAY, this operation needs to be
* initiated first.
* @QCA_TSF_AUTO_REPORT_DISABLE: Used in STA mode only. Once set, the target
* will not automatically send TSF report to the host. If
- * %QCA_TSF_AUTO_REPORT_ENABLE is initiated and
- * %QCA_WLAN_VENDOR_ATTR_GET_STA_INFO_UPLINK_DELAY is not queried anymore, this
+ * QCA_TSF_AUTO_REPORT_ENABLE is initiated and
+ * QCA_WLAN_VENDOR_ATTR_GET_STA_INFO_UPLINK_DELAY is not queried anymore, this
* operation needs to be initiated.
- * @QCA_TSF_SYNC_START: Start periodic TSF sync feature. The driver periodically
- * fetches TSF and host time mapping from the firmware with interval configured
- * through the %QCA_WLAN_VENDOR_ATTR_TSF_SYNC_INTERVAL attribute. If the
- * interval value is not provided the driver will use the default value. The
- * userspace can query the TSF and host time mapping via the %QCA_TSF_GET
- * command.
- * @QCA_TSF_SYNC_STOP: Stop periodic TSF sync feature.
*/
enum qca_tsf_cmd {
QCA_TSF_CAPTURE,
@@ -1848,8 +1825,6 @@ enum qca_tsf_cmd {
QCA_TSF_SYNC_GET,
QCA_TSF_AUTO_REPORT_ENABLE,
QCA_TSF_AUTO_REPORT_DISABLE,
- QCA_TSF_SYNC_START,
- QCA_TSF_SYNC_STOP,
};
/**
diff --git a/contrib/wpa/src/common/sae.c b/contrib/wpa/src/common/sae.c
index c0f154e9134d..b768c22faa9d 100644
--- a/contrib/wpa/src/common/sae.c
+++ b/contrib/wpa/src/common/sae.c
@@ -290,16 +290,14 @@ static int sae_derive_pwe_ecc(struct sae_data *sae, const u8 *addr1,
int pwd_seed_odd = 0;
u8 prime[SAE_MAX_ECC_PRIME_LEN];
size_t prime_len;
- struct crypto_bignum *x = NULL, *y = NULL, *qr = NULL, *qnr = NULL;
+ struct crypto_bignum *x = NULL, *qr = NULL, *qnr = NULL;
u8 x_bin[SAE_MAX_ECC_PRIME_LEN];
u8 x_cand_bin[SAE_MAX_ECC_PRIME_LEN];
u8 qr_bin[SAE_MAX_ECC_PRIME_LEN];
u8 qnr_bin[SAE_MAX_ECC_PRIME_LEN];
- u8 x_y[2 * SAE_MAX_ECC_PRIME_LEN];
int res = -1;
u8 found = 0; /* 0 (false) or 0xff (true) to be used as const_time_*
* mask */
- unsigned int is_eq;
os_memset(x_bin, 0, sizeof(x_bin));
@@ -398,42 +396,25 @@ static int sae_derive_pwe_ecc(struct sae_data *sae, const u8 *addr1,
goto fail;
}
- /* y = sqrt(x^3 + ax + b) mod p
- * if LSB(save) == LSB(y): PWE = (x, y)
- * else: PWE = (x, p - y)
- *
- * Calculate y and the two possible values for PWE and after that,
- * use constant time selection to copy the correct alternative.
- */
- y = crypto_ec_point_compute_y_sqr(sae->tmp->ec, x);
- if (!y ||
- dragonfly_sqrt(sae->tmp->ec, y, y) < 0 ||
- crypto_bignum_to_bin(y, x_y, SAE_MAX_ECC_PRIME_LEN,
- prime_len) < 0 ||
- crypto_bignum_sub(sae->tmp->prime, y, y) < 0 ||
- crypto_bignum_to_bin(y, x_y + SAE_MAX_ECC_PRIME_LEN,
- SAE_MAX_ECC_PRIME_LEN, prime_len) < 0) {
- wpa_printf(MSG_DEBUG, "SAE: Could not solve y");
- goto fail;
- }
-
- is_eq = const_time_eq(pwd_seed_odd, x_y[prime_len - 1] & 0x01);
- const_time_select_bin(is_eq, x_y, x_y + SAE_MAX_ECC_PRIME_LEN,
- prime_len, x_y + prime_len);
- os_memcpy(x_y, x_bin, prime_len);
- wpa_hexdump_key(MSG_DEBUG, "SAE: PWE", x_y, 2 * prime_len);
- crypto_ec_point_deinit(sae->tmp->pwe_ecc, 1);
- sae->tmp->pwe_ecc = crypto_ec_point_from_bin(sae->tmp->ec, x_y);
- if (!sae->tmp->pwe_ecc) {
- wpa_printf(MSG_DEBUG, "SAE: Could not generate PWE");
+ if (!sae->tmp->pwe_ecc)
+ sae->tmp->pwe_ecc = crypto_ec_point_init(sae->tmp->ec);
+ if (!sae->tmp->pwe_ecc)
res = -1;
+ else
+ res = crypto_ec_point_solve_y_coord(sae->tmp->ec,
+ sae->tmp->pwe_ecc, x,
+ pwd_seed_odd);
+ if (res < 0) {
+ /*
+ * This should not happen since we already checked that there
+ * is a result.
+ */
+ wpa_printf(MSG_DEBUG, "SAE: Could not solve y");
}
fail:
- forced_memzero(x_y, sizeof(x_y));
crypto_bignum_deinit(qr, 0);
crypto_bignum_deinit(qnr, 0);
- crypto_bignum_deinit(y, 1);
os_free(stub_password);
bin_clear_free(tmp_password, password_len);
crypto_bignum_deinit(x, 1);
@@ -766,9 +747,19 @@ static struct crypto_ec_point * sswu(struct crypto_ec *ec, int group,
const_time_select_bin(is_qr, bin1, bin2, prime_len, x_y);
wpa_hexdump_key(MSG_DEBUG, "SSWU: x = CSEL(l, x1, x2)", x_y, prime_len);
- /* y = sqrt(v) */
+ /* y = sqrt(v)
+ * For prime p such that p = 3 mod 4 --> v^((p+1)/4) */
+ if (crypto_bignum_to_bin(prime, bin1, sizeof(bin1), prime_len) < 0)
+ goto fail;
+ if ((bin1[prime_len - 1] & 0x03) != 3) {
+ wpa_printf(MSG_DEBUG, "SSWU: prime does not have p = 3 mod 4");
+ goto fail;
+ }
y = crypto_bignum_init();
- if (!y || dragonfly_sqrt(ec, v, y) < 0)
+ if (!y ||
+ crypto_bignum_add(prime, one, t1) < 0 ||
+ crypto_bignum_rshift(t1, 2, t1) < 0 ||
+ crypto_bignum_exptmod(v, t1, prime, y) < 0)
goto fail;
debug_print_bignum("SSWU: y = sqrt(v)", y, prime_len);
diff --git a/contrib/wpa/src/common/version.h b/contrib/wpa/src/common/version.h
index 7502f58e0b87..0235c9bf6776 100644
--- a/contrib/wpa/src/common/version.h
+++ b/contrib/wpa/src/common/version.h
@@ -9,6 +9,6 @@
#define GIT_VERSION_STR_POSTFIX ""
#endif /* GIT_VERSION_STR_POSTFIX */
-#define VERSION_STR "2.10" VERSION_STR_POSTFIX GIT_VERSION_STR_POSTFIX
+#define VERSION_STR "2.10-devel" VERSION_STR_POSTFIX GIT_VERSION_STR_POSTFIX
#endif /* VERSION_H */
diff --git a/contrib/wpa/src/crypto/crypto.h b/contrib/wpa/src/crypto/crypto.h
index e6150b0cf16e..eb600699d3d0 100644
--- a/contrib/wpa/src/crypto/crypto.h
+++ b/contrib/wpa/src/crypto/crypto.h
@@ -883,6 +883,18 @@ int crypto_ec_point_mul(struct crypto_ec *e, const struct crypto_ec_point *p,
int crypto_ec_point_invert(struct crypto_ec *e, struct crypto_ec_point *p);
/**
+ * crypto_ec_point_solve_y_coord - Solve y coordinate for an x coordinate
+ * @e: EC context from crypto_ec_init()
+ * @p: EC point to use for the returning the result
+ * @x: x coordinate
+ * @y_bit: y-bit (0 or 1) for selecting the y value to use
+ * Returns: 0 on success, -1 on failure
+ */
+int crypto_ec_point_solve_y_coord(struct crypto_ec *e,
+ struct crypto_ec_point *p,
+ const struct crypto_bignum *x, int y_bit);
+
+/**
* crypto_ec_point_compute_y_sqr - Compute y^2 = x^3 + ax + b
* @e: EC context from crypto_ec_init()
* @x: x coordinate
diff --git a/contrib/wpa/src/crypto/crypto_openssl.c b/contrib/wpa/src/crypto/crypto_openssl.c
index 82c85762d84f..ef669c408474 100644
--- a/contrib/wpa/src/crypto/crypto_openssl.c
+++ b/contrib/wpa/src/crypto/crypto_openssl.c
@@ -24,9 +24,6 @@
#include <openssl/x509.h>
#include <openssl/pem.h>
#endif /* CONFIG_ECC */
-#if OPENSSL_VERSION_NUMBER >= 0x30000000L
-#include <openssl/provider.h>
-#endif /* OpenSSL version >= 3.0 */
#include "common.h"
#include "utils/const_time.h"
@@ -120,26 +117,6 @@ static const unsigned char * ASN1_STRING_get0_data(const ASN1_STRING *x)
}
#endif /* OpenSSL version < 1.1.0 */
-
-void openssl_load_legacy_provider(void)
-{
-#if OPENSSL_VERSION_NUMBER >= 0x30000000L
- static bool loaded = false;
- OSSL_PROVIDER *legacy;
-
- if (loaded)
- return;
-
- legacy = OSSL_PROVIDER_load(NULL, "legacy");
-
- if (legacy) {
- OSSL_PROVIDER_load(NULL, "default");
- loaded = true;
- }
-#endif /* OpenSSL version >= 3.0 */
-}
-
-
static BIGNUM * get_group5_prime(void)
{
#if OPENSSL_VERSION_NUMBER >= 0x10100000L && \
@@ -246,7 +223,6 @@ static int openssl_digest_vector(const EVP_MD *type, size_t num_elem,
#ifndef CONFIG_FIPS
int md4_vector(size_t num_elem, const u8 *addr[], const size_t *len, u8 *mac)
{
- openssl_load_legacy_provider();
return openssl_digest_vector(EVP_md4(), num_elem, addr, len, mac);
}
#endif /* CONFIG_FIPS */
@@ -258,8 +234,6 @@ int des_encrypt(const u8 *clear, const u8 *key, u8 *cypher)
int i, plen, ret = -1;
EVP_CIPHER_CTX *ctx;
- openssl_load_legacy_provider();
-
/* Add parity bits to the key */
next = 0;
for (i = 0; i < 7; i++) {
@@ -297,8 +271,6 @@ int rc4_skip(const u8 *key, size_t keylen, size_t skip,
int res = -1;
unsigned char skip_buf[16];
- openssl_load_legacy_provider();
-
ctx = EVP_CIPHER_CTX_new();
if (!ctx ||
!EVP_CipherInit_ex(ctx, EVP_rc4(), NULL, NULL, NULL, 1) ||
@@ -1951,27 +1923,48 @@ int crypto_ec_point_invert(struct crypto_ec *e, struct crypto_ec_point *p)
}
+int crypto_ec_point_solve_y_coord(struct crypto_ec *e,
+ struct crypto_ec_point *p,
+ const struct crypto_bignum *x, int y_bit)
+{
+ if (TEST_FAIL())
+ return -1;
+ if (!EC_POINT_set_compressed_coordinates_GFp(e->group, (EC_POINT *) p,
+ (const BIGNUM *) x, y_bit,
+ e->bnctx) ||
+ !EC_POINT_is_on_curve(e->group, (EC_POINT *) p, e->bnctx))
+ return -1;
+ return 0;
+}
+
+
struct crypto_bignum *
crypto_ec_point_compute_y_sqr(struct crypto_ec *e,
const struct crypto_bignum *x)
{
- BIGNUM *tmp;
+ BIGNUM *tmp, *tmp2, *y_sqr = NULL;
if (TEST_FAIL())
return NULL;
tmp = BN_new();
+ tmp2 = BN_new();
- /* y^2 = x^3 + ax + b = (x^2 + a)x + b */
- if (tmp &&
+ /* y^2 = x^3 + ax + b */
+ if (tmp && tmp2 &&
BN_mod_sqr(tmp, (const BIGNUM *) x, e->prime, e->bnctx) &&
- BN_mod_add_quick(tmp, e->a, tmp, e->prime) &&
BN_mod_mul(tmp, tmp, (const BIGNUM *) x, e->prime, e->bnctx) &&
- BN_mod_add_quick(tmp, tmp, e->b, e->prime))
- return (struct crypto_bignum *) tmp;
+ BN_mod_mul(tmp2, e->a, (const BIGNUM *) x, e->prime, e->bnctx) &&
+ BN_mod_add_quick(tmp2, tmp2, tmp, e->prime) &&
+ BN_mod_add_quick(tmp2, tmp2, e->b, e->prime)) {
+ y_sqr = tmp2;
+ tmp2 = NULL;
+ }
BN_clear_free(tmp);
- return NULL;
+ BN_clear_free(tmp2);
+
+ return (struct crypto_bignum *) y_sqr;
}
@@ -2487,13 +2480,12 @@ struct crypto_ec_key * crypto_ec_key_gen(int group)
goto fail;
}
- eckey = EVP_PKEY_get1_EC_KEY(key);
+ eckey = EVP_PKEY_get0_EC_KEY(key);
if (!eckey) {
key = NULL;
goto fail;
}
EC_KEY_set_conv_form(eckey, POINT_CONVERSION_COMPRESSED);
- EC_KEY_free(eckey);
fail:
EC_KEY_free(ec_params);
@@ -2603,34 +2595,12 @@ fail:
unsigned char *der = NULL;
int der_len;
struct wpabuf *buf;
- EC_KEY *eckey;
-#if OPENSSL_VERSION_NUMBER >= 0x30000000L
- EVP_PKEY *tmp;
-#endif /* OpenSSL version >= 3.0 */
-
- eckey = EVP_PKEY_get1_EC_KEY((EVP_PKEY *) key);
- if (!eckey)
- return NULL;
/* For now, all users expect COMPRESSED form */
- EC_KEY_set_conv_form(eckey, POINT_CONVERSION_COMPRESSED);
-
-#if OPENSSL_VERSION_NUMBER >= 0x30000000L
- tmp = EVP_PKEY_new();
- if (!tmp)
- return NULL;
- if (EVP_PKEY_set1_EC_KEY(tmp, eckey) != 1) {
- EVP_PKEY_free(tmp);
- return NULL;
- }
- key = (struct crypto_ec_key *) tmp;
-#endif /* OpenSSL version >= 3.0 */
+ EC_KEY_set_conv_form(EVP_PKEY_get0_EC_KEY((EVP_PKEY *) key),
+ POINT_CONVERSION_COMPRESSED);
der_len = i2d_PUBKEY((EVP_PKEY *) key, &der);
- EC_KEY_free(eckey);
-#if OPENSSL_VERSION_NUMBER >= 0x30000000L
- EVP_PKEY_free(tmp);
-#endif /* OpenSSL version >= 3.0 */
if (der_len <= 0) {
wpa_printf(MSG_INFO, "OpenSSL: i2d_PUBKEY() failed: %s",
ERR_error_string(ERR_get_error(), NULL));
@@ -2653,7 +2623,7 @@ struct wpabuf * crypto_ec_key_get_ecprivate_key(struct crypto_ec_key *key,
struct wpabuf *buf;
unsigned int key_flags;
- eckey = EVP_PKEY_get1_EC_KEY((EVP_PKEY *) key);
+ eckey = EVP_PKEY_get0_EC_KEY((EVP_PKEY *) key);
if (!eckey)
return NULL;
@@ -2667,7 +2637,6 @@ struct wpabuf * crypto_ec_key_get_ecprivate_key(struct crypto_ec_key *key,
EC_KEY_set_conv_form(eckey, POINT_CONVERSION_UNCOMPRESSED);
der_len = i2d_ECPrivateKey(eckey, &der);
- EC_KEY_free(eckey);
if (der_len <= 0)
return NULL;
buf = wpabuf_alloc_copy(der, der_len);
@@ -2728,7 +2697,7 @@ struct wpabuf * crypto_ec_key_get_pubkey_point(struct crypto_ec_key *key,
const struct crypto_ec_point *
crypto_ec_key_get_public_key(struct crypto_ec_key *key)
{
- const EC_KEY *eckey;
+ EC_KEY *eckey;
eckey = EVP_PKEY_get0_EC_KEY((EVP_PKEY *) key);
if (!eckey)
@@ -2740,7 +2709,7 @@ crypto_ec_key_get_public_key(struct crypto_ec_key *key)
const struct crypto_bignum *
crypto_ec_key_get_private_key(struct crypto_ec_key *key)
{
- const EC_KEY *eckey;
+ EC_KEY *eckey;
eckey = EVP_PKEY_get0_EC_KEY((EVP_PKEY *) key);
if (!eckey)
diff --git a/contrib/wpa/src/crypto/crypto_wolfssl.c b/contrib/wpa/src/crypto/crypto_wolfssl.c
index 00ecf61352a1..6f116eb62f53 100644
--- a/contrib/wpa/src/crypto/crypto_wolfssl.c
+++ b/contrib/wpa/src/crypto/crypto_wolfssl.c
@@ -1630,6 +1630,30 @@ int crypto_ec_point_invert(struct crypto_ec *e, struct crypto_ec_point *p)
}
+int crypto_ec_point_solve_y_coord(struct crypto_ec *e,
+ struct crypto_ec_point *p,
+ const struct crypto_bignum *x, int y_bit)
+{
+ byte buf[1 + 2 * MAX_ECC_BYTES];
+ int ret;
+ int prime_len = crypto_ec_prime_len(e);
+
+ if (TEST_FAIL())
+ return -1;
+
+ buf[0] = y_bit ? ECC_POINT_COMP_ODD : ECC_POINT_COMP_EVEN;
+ ret = crypto_bignum_to_bin(x, buf + 1, prime_len, prime_len);
+ if (ret <= 0)
+ return -1;
+ ret = wc_ecc_import_point_der(buf, 1 + 2 * ret, e->key.idx,
+ (ecc_point *) p);
+ if (ret != 0)
+ return -1;
+
+ return 0;
+}
+
+
struct crypto_bignum *
crypto_ec_point_compute_y_sqr(struct crypto_ec *e,
const struct crypto_bignum *x)
diff --git a/contrib/wpa/src/crypto/tls_openssl.c b/contrib/wpa/src/crypto/tls_openssl.c
index c9e00b3af855..203b0f781ff5 100644
--- a/contrib/wpa/src/crypto/tls_openssl.c
+++ b/contrib/wpa/src/crypto/tls_openssl.c
@@ -957,10 +957,6 @@ void * tls_init(const struct tls_config *conf)
const char *ciphers;
if (tls_openssl_ref_count == 0) {
- void openssl_load_legacy_provider(void);
-
- openssl_load_legacy_provider();
-
tls_global = context = tls_context_new(conf);
if (context == NULL)
return NULL;
@@ -3023,23 +3019,13 @@ static int tls_set_conn_flags(struct tls_connection *conn, unsigned int flags,
#if OPENSSL_VERSION_NUMBER >= 0x10100000L && \
!defined(LIBRESSL_VERSION_NUMBER) && \
!defined(OPENSSL_IS_BORINGSSL)
- {
-#if OPENSSL_VERSION_NUMBER >= 0x30000000L
- int need_level = 0;
-#else
- int need_level = 1;
-#endif
-
- if ((flags &
- (TLS_CONN_ENABLE_TLSv1_0 | TLS_CONN_ENABLE_TLSv1_1)) &&
- SSL_get_security_level(ssl) > need_level) {
- /*
- * Need to drop to security level 1 (or 0 with OpenSSL
- * 3.0) to allow TLS versions older than 1.2 to be used
- * when explicitly enabled in configuration.
- */
- SSL_set_security_level(conn->ssl, need_level);
- }
+ if ((flags & (TLS_CONN_ENABLE_TLSv1_0 | TLS_CONN_ENABLE_TLSv1_1)) &&
+ SSL_get_security_level(ssl) >= 2) {
+ /*
+ * Need to drop to security level 1 to allow TLS versions older
+ * than 1.2 to be used when explicitly enabled in configuration.
+ */
+ SSL_set_security_level(conn->ssl, 1);
}
#endif
diff --git a/contrib/wpa/src/eap_common/eap_pwd_common.c b/contrib/wpa/src/eap_common/eap_pwd_common.c
index ff22b29b087a..2b2b8efdbd01 100644
--- a/contrib/wpa/src/eap_common/eap_pwd_common.c
+++ b/contrib/wpa/src/eap_common/eap_pwd_common.c
@@ -127,8 +127,7 @@ int compute_password_element(EAP_PWD_group *grp, u16 num,
u8 qr_or_qnr_bin[MAX_ECC_PRIME_LEN];
u8 x_bin[MAX_ECC_PRIME_LEN];
u8 prime_bin[MAX_ECC_PRIME_LEN];
- u8 x_y[2 * MAX_ECC_PRIME_LEN];
- struct crypto_bignum *tmp2 = NULL, *y = NULL;
+ struct crypto_bignum *tmp2 = NULL;
struct crypto_hash *hash;
unsigned char pwe_digest[SHA256_MAC_LEN], *prfbuf = NULL, ctr;
int ret = 0, res;
@@ -140,7 +139,6 @@ int compute_password_element(EAP_PWD_group *grp, u16 num,
u8 found_ctr = 0, is_odd = 0;
int cmp_prime;
unsigned int in_range;
- unsigned int is_eq;
if (grp->pwe)
return -1;
@@ -153,6 +151,11 @@ int compute_password_element(EAP_PWD_group *grp, u16 num,
if (crypto_bignum_to_bin(prime, prime_bin, sizeof(prime_bin),
primebytelen) < 0)
return -1;
+ grp->pwe = crypto_ec_point_init(grp->group);
+ if (!grp->pwe) {
+ wpa_printf(MSG_INFO, "EAP-pwd: unable to create bignums");
+ goto fail;
+ }
if ((prfbuf = os_malloc(primebytelen)) == NULL) {
wpa_printf(MSG_INFO, "EAP-pwd: unable to malloc space for prf "
@@ -258,37 +261,10 @@ int compute_password_element(EAP_PWD_group *grp, u16 num,
*/
crypto_bignum_deinit(x_candidate, 1);
x_candidate = crypto_bignum_init_set(x_bin, primebytelen);
- if (!x_candidate)
- goto fail;
-
- /* y = sqrt(x^3 + ax + b) mod p
- * if LSB(y) == LSB(pwd-seed): PWE = (x, y)
- * else: PWE = (x, p - y)
- *
- * Calculate y and the two possible values for PWE and after that,
- * use constant time selection to copy the correct alternative.
- */
- y = crypto_ec_point_compute_y_sqr(grp->group, x_candidate);
- if (!y ||
- dragonfly_sqrt(grp->group, y, y) < 0 ||
- crypto_bignum_to_bin(y, x_y, MAX_ECC_PRIME_LEN, primebytelen) < 0 ||
- crypto_bignum_sub(prime, y, y) < 0 ||
- crypto_bignum_to_bin(y, x_y + MAX_ECC_PRIME_LEN,
- MAX_ECC_PRIME_LEN, primebytelen) < 0) {
- wpa_printf(MSG_DEBUG, "SAE: Could not solve y");
- goto fail;
- }
-
- /* Constant time selection of the y coordinate from the two
- * options */
- is_eq = const_time_eq(is_odd, x_y[primebytelen - 1] & 0x01);
- const_time_select_bin(is_eq, x_y, x_y + MAX_ECC_PRIME_LEN,
- primebytelen, x_y + primebytelen);
- os_memcpy(x_y, x_bin, primebytelen);
- wpa_hexdump_key(MSG_DEBUG, "EAP-pwd: PWE", x_y, 2 * primebytelen);
- grp->pwe = crypto_ec_point_from_bin(grp->group, x_y);
- if (!grp->pwe) {
- wpa_printf(MSG_DEBUG, "EAP-pwd: Could not generate PWE");
+ if (!x_candidate ||
+ crypto_ec_point_solve_y_coord(grp->group, grp->pwe, x_candidate,
+ is_odd) != 0) {
+ wpa_printf(MSG_INFO, "EAP-pwd: Could not solve for y");
goto fail;
}
@@ -313,7 +289,6 @@ int compute_password_element(EAP_PWD_group *grp, u16 num,
/* cleanliness and order.... */
crypto_bignum_deinit(x_candidate, 1);
crypto_bignum_deinit(tmp2, 1);
- crypto_bignum_deinit(y, 1);
crypto_bignum_deinit(qr, 1);
crypto_bignum_deinit(qnr, 1);
bin_clear_free(prfbuf, primebytelen);
@@ -321,7 +296,6 @@ int compute_password_element(EAP_PWD_group *grp, u16 num,
os_memset(qnr_bin, 0, sizeof(qnr_bin));
os_memset(qr_or_qnr_bin, 0, sizeof(qr_or_qnr_bin));
os_memset(pwe_digest, 0, sizeof(pwe_digest));
- forced_memzero(x_y, sizeof(x_y));
return ret;
}
diff --git a/contrib/wpa/wpa_supplicant/ChangeLog b/contrib/wpa/wpa_supplicant/ChangeLog
index efcc6cd9c9ba..5ca82457ad1b 100644
--- a/contrib/wpa/wpa_supplicant/ChangeLog
+++ b/contrib/wpa/wpa_supplicant/ChangeLog
@@ -1,58 +1,5 @@
ChangeLog for wpa_supplicant
-2022-01-16 - v2.10
- * SAE changes
- - improved protection against side channel attacks
- [https://w1.fi/security/2022-1/]
- - added support for the hash-to-element mechanism (sae_pwe=1 or
- sae_pwe=2); this is currently disabled by default, but will likely
- get enabled by default in the future
- - fixed PMKSA caching with OKC
- - added support for SAE-PK
- * EAP-pwd changes
- - improved protection against side channel attacks
- [https://w1.fi/security/2022-1/]
- * fixed P2P provision discovery processing of a specially constructed
- invalid frame
- [https://w1.fi/security/2021-1/]
- * fixed P2P group information processing of a specially constructed
- invalid frame
- [https://w1.fi/security/2020-2/]
- * fixed PMF disconnection protection bypass in AP mode
- [https://w1.fi/security/2019-7/]
- * added support for using OpenSSL 3.0
- * increased the maximum number of EAP message exchanges (mainly to
- support cases with very large certificates)
- * fixed various issues in experimental support for EAP-TEAP peer
- * added support for DPP release 2 (Wi-Fi Device Provisioning Protocol)
- * a number of MKA/MACsec fixes and extensions
- * added support for SAE (WPA3-Personal) AP mode configuration
- * added P2P support for EDMG (IEEE 802.11ay) channels
- * fixed EAP-FAST peer with TLS GCM/CCM ciphers
- * improved throughput estimation and BSS selection
- * dropped support for libnl 1.1
- * added support for nl80211 control port for EAPOL frame TX/RX
- * fixed OWE key derivation with groups 20 and 21; this breaks backwards
- compatibility for these groups while the default group 19 remains
- backwards compatible
- * added support for Beacon protection
- * added support for Extended Key ID for pairwise keys
- * removed WEP support from the default build (CONFIG_WEP=y can be used
- to enable it, if really needed)
- * added a build option to remove TKIP support (CONFIG_NO_TKIP=y)
- * added support for Transition Disable mechanism to allow the AP to
- automatically disable transition mode to improve security
- * extended D-Bus interface
- * added support for PASN
- * added a file-based backend for external password storage to allow
- secret information to be moved away from the main configuration file
- without requiring external tools
- * added EAP-TLS peer support for TLS 1.3 (disabled by default for now)
- * added support for SCS, MSCS, DSCP policy
- * changed driver interface selection to default to automatic fallback
- to other compiled in options
- * a large number of other fixes, cleanup, and extensions
-
2019-08-07 - v2.9
* SAE changes
- disable use of groups using Brainpool curves
diff --git a/contrib/wpa/wpa_supplicant/README b/contrib/wpa/wpa_supplicant/README
index c643b2684700..05f15ff46bda 100644
--- a/contrib/wpa/wpa_supplicant/README
+++ b/contrib/wpa/wpa_supplicant/README
@@ -1,7 +1,7 @@
wpa_supplicant
==============
-Copyright (c) 2003-2022, Jouni Malinen <j@w1.fi> and contributors
+Copyright (c) 2003-2019, Jouni Malinen <j@w1.fi> and contributors
All Rights Reserved.
This program is licensed under the BSD license (the one with
diff --git a/contrib/wpa/wpa_supplicant/binder/fi/w1/wpa_supplicant/IIface.aidl b/contrib/wpa/wpa_supplicant/binder/fi/w1/wpa_supplicant/IIface.aidl
new file mode 100644
index 000000000000..ea11d426df1f
--- /dev/null
+++ b/contrib/wpa/wpa_supplicant/binder/fi/w1/wpa_supplicant/IIface.aidl
@@ -0,0 +1,16 @@
+/*
+ * binder interface for wpa_supplicant daemon
+ * Copyright (c) 2004-2016, Jouni Malinen <j@w1.fi>
+ * Copyright (c) 2004-2016, Roshan Pius <rpius@google.com>
+ *
+ * This software may be distributed under the terms of the BSD license.
+ * See README for more details.
+ */
+
+package fi.w1.wpa_supplicant;
+
+/**
+ * Interface exposed by wpa_supplicant for each network interface it controls.
+ */
+interface IIface {
+}
diff --git a/contrib/wpa/wpa_supplicant/binder/fi/w1/wpa_supplicant/ISupplicant.aidl b/contrib/wpa/wpa_supplicant/binder/fi/w1/wpa_supplicant/ISupplicant.aidl
new file mode 100644
index 000000000000..1cbee20a620f
--- /dev/null
+++ b/contrib/wpa/wpa_supplicant/binder/fi/w1/wpa_supplicant/ISupplicant.aidl
@@ -0,0 +1,59 @@
+/*
+ * WPA Supplicant - binder interface for wpa_supplicant daemon
+ * Copyright (c) 2004-2016, Jouni Malinen <j@w1.fi>
+ * Copyright (c) 2004-2016, Roshan Pius <rpius@google.com>
+ *
+ * This software may be distributed under the terms of the BSD license.
+ * See README for more details.
+ */
+
+package fi.w1.wpa_supplicant;
+
+import android.os.PersistableBundle;
+import fi.w1.wpa_supplicant.IIface;
+
+/**
+ * Interface exposed by the wpa_supplicant binder service registered
+ * with the service manager with name: fi.w1.wpa_supplicant.
+ */
+interface ISupplicant {
+ /* Error values returned by the service to RPC method calls. */
+ const int ERROR_INVALID_ARGS = 1;
+ const int ERROR_UNKNOWN = 2;
+ const int ERROR_IFACE_EXISTS = 3;
+ const int ERROR_IFACE_UNKNOWN = 4;
+
+ /**
+ * Registers a wireless interface in wpa_supplicant.
+ *
+ * @param args A dictionary with arguments used to add the interface to
+ * wpa_supplicant.
+ * The dictionary may contain the following entries:
+ * Ifname(String) Name of the network interface to control, e.g.,
+ * wlan0.
+ * BridgeIfname(String) Name of the bridge interface to control, e.g.,
+ * br0.
+ * Driver(String) Driver name which the interface uses, e.g., nl80211.
+ * ConfigFile(String) Configuration file path.
+ *
+ * @return Binder object representing the interface.
+ */
+ IIface CreateInterface(in PersistableBundle args);
+
+ /**
+ * Deregisters a wireless interface from wpa_supplicant.
+ *
+ * @param ifname Name of the network interface, e.g., wlan0
+ */
+ void RemoveInterface(in @utf8InCpp String ifname);
+
+ /**
+ * Gets a binder object for the interface corresponding to ifname
+ * which wpa_supplicant already controls.
+ *
+ * @param ifname Name of the network interface, e.g., wlan0
+ *
+ * @return Binder object representing the interface.
+ */
+ IIface GetInterface(in @utf8InCpp String ifname);
+}
diff --git a/contrib/wpa/wpa_supplicant/binder/fi/w1/wpa_supplicant/ISupplicantCallbacks.aidl b/contrib/wpa/wpa_supplicant/binder/fi/w1/wpa_supplicant/ISupplicantCallbacks.aidl
new file mode 100644
index 000000000000..d624d9133603
--- /dev/null
+++ b/contrib/wpa/wpa_supplicant/binder/fi/w1/wpa_supplicant/ISupplicantCallbacks.aidl
@@ -0,0 +1,20 @@
+/*
+ * binder interface for wpa_supplicant daemon
+ * Copyright (c) 2004-2016, Jouni Malinen <j@w1.fi>
+ * Copyright (c) 2004-2016, Roshan Pius <rpius@google.com>
+ *
+ * This software may be distributed under the terms of the BSD license.
+ * See README for more details.
+ */
+
+package fi.w1.wpa_supplicant;
+
+import android.os.PersistableBundle;
+
+/**
+ * Callback Interface exposed by the wpa_supplicant service. Clients need
+ * to host an instance of this binder object and pass a reference of the object
+ * to wpa_supplicant via the registerCallbacksObject method.
+ */
+interface ISupplicantCallbacks {
+}
diff --git a/contrib/wpa/wpa_supplicant/dbus/dbus_new_handlers.c b/contrib/wpa/wpa_supplicant/dbus/dbus_new_handlers.c
index 959a68b4cdf5..545e9f64295a 100644
--- a/contrib/wpa/wpa_supplicant/dbus/dbus_new_handlers.c
+++ b/contrib/wpa/wpa_supplicant/dbus/dbus_new_handlers.c
@@ -1780,7 +1780,6 @@ DBusMessage * wpas_dbus_handler_remove_all_creds(DBusMessage *message,
}
-#ifdef CONFIG_INTERWORKING
DBusMessage *
wpas_dbus_handler_interworking_select(DBusMessage *message,
struct wpa_supplicant *wpa_s)
@@ -1801,7 +1800,6 @@ wpas_dbus_handler_interworking_select(DBusMessage *message,
return reply;
}
-#endif /* CONFIG_INTERWORKING */
/**
diff --git a/contrib/wpa/wpa_supplicant/doc/docbook/eapol_test.sgml b/contrib/wpa/wpa_supplicant/doc/docbook/eapol_test.sgml
index b9b0a95c2691..4cfa3c1db384 100644
--- a/contrib/wpa/wpa_supplicant/doc/docbook/eapol_test.sgml
+++ b/contrib/wpa/wpa_supplicant/doc/docbook/eapol_test.sgml
@@ -198,7 +198,7 @@ eapol_test -ctest.conf -a127.0.0.1 -p1812 -ssecret -r1
</refsect1>
<refsect1>
<title>Legal</title>
- <para>wpa_supplicant is copyright (c) 2003-2022,
+ <para>wpa_supplicant is copyright (c) 2003-2019,
Jouni Malinen <email>j@w1.fi</email> and
contributors.
All Rights Reserved.</para>
diff --git a/contrib/wpa/wpa_supplicant/doc/docbook/wpa_background.sgml b/contrib/wpa/wpa_supplicant/doc/docbook/wpa_background.sgml
index b0592e233b88..22241ccf9006 100644
--- a/contrib/wpa/wpa_supplicant/doc/docbook/wpa_background.sgml
+++ b/contrib/wpa/wpa_supplicant/doc/docbook/wpa_background.sgml
@@ -94,7 +94,7 @@
<refsect1>
<title>Legal</title>
- <para>wpa_supplicant is copyright (c) 2003-2022,
+ <para>wpa_supplicant is copyright (c) 2003-2019,
Jouni Malinen <email>j@w1.fi</email> and
contributors.
All Rights Reserved.</para>
diff --git a/contrib/wpa/wpa_supplicant/doc/docbook/wpa_cli.sgml b/contrib/wpa/wpa_supplicant/doc/docbook/wpa_cli.sgml
index b3d95ee20abb..2ba1fe42236a 100644
--- a/contrib/wpa/wpa_supplicant/doc/docbook/wpa_cli.sgml
+++ b/contrib/wpa/wpa_supplicant/doc/docbook/wpa_cli.sgml
@@ -349,7 +349,7 @@ CTRL-REQ-OTP-2:Challenge 1235663 needed for SSID foobar
</refsect1>
<refsect1>
<title>Legal</title>
- <para>wpa_supplicant is copyright (c) 2003-2022,
+ <para>wpa_supplicant is copyright (c) 2003-2019,
Jouni Malinen <email>j@w1.fi</email> and
contributors.
All Rights Reserved.</para>
diff --git a/contrib/wpa/wpa_supplicant/doc/docbook/wpa_gui.sgml b/contrib/wpa/wpa_supplicant/doc/docbook/wpa_gui.sgml
index c391645eef15..cb0c735e4ce7 100644
--- a/contrib/wpa/wpa_supplicant/doc/docbook/wpa_gui.sgml
+++ b/contrib/wpa/wpa_supplicant/doc/docbook/wpa_gui.sgml
@@ -95,7 +95,7 @@
</refsect1>
<refsect1>
<title>Legal</title>
- <para>wpa_supplicant is copyright (c) 2003-2022,
+ <para>wpa_supplicant is copyright (c) 2003-2019,
Jouni Malinen <email>j@w1.fi</email> and
contributors.
All Rights Reserved.</para>
diff --git a/contrib/wpa/wpa_supplicant/doc/docbook/wpa_passphrase.sgml b/contrib/wpa/wpa_supplicant/doc/docbook/wpa_passphrase.sgml
index 5934e79ae108..077296904f89 100644
--- a/contrib/wpa/wpa_supplicant/doc/docbook/wpa_passphrase.sgml
+++ b/contrib/wpa/wpa_supplicant/doc/docbook/wpa_passphrase.sgml
@@ -66,7 +66,7 @@
</refsect1>
<refsect1>
<title>Legal</title>
- <para>wpa_supplicant is copyright (c) 2003-2022,
+ <para>wpa_supplicant is copyright (c) 2003-2019,
Jouni Malinen <email>j@w1.fi</email> and
contributors.
All Rights Reserved.</para>
diff --git a/contrib/wpa/wpa_supplicant/doc/docbook/wpa_priv.sgml b/contrib/wpa/wpa_supplicant/doc/docbook/wpa_priv.sgml
index 4053eda3b3ba..0d5c94a9f776 100644
--- a/contrib/wpa/wpa_supplicant/doc/docbook/wpa_priv.sgml
+++ b/contrib/wpa/wpa_supplicant/doc/docbook/wpa_priv.sgml
@@ -141,7 +141,7 @@ wpa_supplicant -i ath0 -c wpa_supplicant.conf
</refsect1>
<refsect1>
<title>Legal</title>
- <para>wpa_supplicant is copyright (c) 2003-2022,
+ <para>wpa_supplicant is copyright (c) 2003-2019,
Jouni Malinen <email>j@w1.fi</email> and
contributors.
All Rights Reserved.</para>
diff --git a/contrib/wpa/wpa_supplicant/doc/docbook/wpa_supplicant.sgml b/contrib/wpa/wpa_supplicant/doc/docbook/wpa_supplicant.sgml
index 02012d1b08de..e4a83698393a 100644
--- a/contrib/wpa/wpa_supplicant/doc/docbook/wpa_supplicant.sgml
+++ b/contrib/wpa/wpa_supplicant/doc/docbook/wpa_supplicant.sgml
@@ -753,7 +753,7 @@ fi
</refsect1>
<refsect1>
<title>Legal</title>
- <para>wpa_supplicant is copyright (c) 2003-2022,
+ <para>wpa_supplicant is copyright (c) 2003-2019,
Jouni Malinen <email>j@w1.fi</email> and
contributors.
All Rights Reserved.</para>
diff --git a/contrib/wpa/wpa_supplicant/sme.c b/contrib/wpa/wpa_supplicant/sme.c
index 7f43216c6582..1dc7001a7305 100644
--- a/contrib/wpa/wpa_supplicant/sme.c
+++ b/contrib/wpa/wpa_supplicant/sme.c
@@ -946,9 +946,6 @@ static void sme_auth_start_cb(struct wpa_radio_work *work, int deinit)
struct wpa_supplicant *wpa_s = work->wpa_s;
wpa_s->roam_in_progress = false;
-#ifdef CONFIG_WNM
- wpa_s->bss_trans_mgmt_in_progress = false;
-#endif /* CONFIG_WNM */
if (deinit) {
if (work->started)
@@ -995,13 +992,6 @@ void sme_authenticate(struct wpa_supplicant *wpa_s,
"SME: Reject sme_authenticate() in favor of explicit roam request");
return;
}
-#ifdef CONFIG_WNM
- if (wpa_s->bss_trans_mgmt_in_progress) {
- wpa_dbg(wpa_s, MSG_DEBUG,
- "SME: Reject sme_authenticate() in favor of BSS transition management request");
- return;
- }
-#endif /* CONFIG_WNM */
if (radio_work_pending(wpa_s, "sme-connect")) {
/*
* The previous sme-connect work might no longer be valid due to
diff --git a/contrib/wpa/wpa_supplicant/vs2005/eapol_test/eapol_test.vcproj b/contrib/wpa/wpa_supplicant/vs2005/eapol_test/eapol_test.vcproj
new file mode 100755
index 000000000000..c92b8fd89d6c
--- /dev/null
+++ b/contrib/wpa/wpa_supplicant/vs2005/eapol_test/eapol_test.vcproj
@@ -0,0 +1,477 @@
+<?xml version="1.0" encoding="Windows-1252"?>
+<VisualStudioProject
+ ProjectType="Visual C++"
+ Version="8.00"
+ Name="eapol_test"
+ ProjectGUID="{0E3F2C6D-1372-48D6-BCAB-E584917C4DE3}"
+ RootNamespace="eapol_test"
+ Keyword="Win32Proj"
+ >
+ <Platforms>
+ <Platform
+ Name="Win32"
+ />
+ </Platforms>
+ <ToolFiles>
+ </ToolFiles>
+ <Configurations>
+ <Configuration
+ Name="Debug|Win32"
+ OutputDirectory="$(SolutionDir)$(ConfigurationName)"
+ IntermediateDirectory="$(ConfigurationName)"
+ ConfigurationType="1"
+ CharacterSet="1"
+ >
+ <Tool
+ Name="VCPreBuildEventTool"
+ />
+ <Tool
+ Name="VCCustomBuildTool"
+ />
+ <Tool
+ Name="VCXMLDataGeneratorTool"
+ />
+ <Tool
+ Name="VCWebServiceProxyGeneratorTool"
+ />
+ <Tool
+ Name="VCMIDLTool"
+ />
+ <Tool
+ Name="VCCLCompilerTool"
+ Optimization="0"
+ AdditionalIncludeDirectories="..\..;..\..\..\src;..\..\..\src\utils;C:\dev\WpdPack\include;C:\dev\openssl\include"
+ PreprocessorDefinitions="WIN32;_DEBUG;_CONSOLE;CONFIG_WIN32_DEFAULTS"
+ MinimalRebuild="true"
+ BasicRuntimeChecks="3"
+ RuntimeLibrary="3"
+ UsePrecompiledHeader="0"
+ WarningLevel="3"
+ Detect64BitPortabilityProblems="true"
+ DebugInformationFormat="4"
+ DisableSpecificWarnings="4244;4267;4311"
+ />
+ <Tool
+ Name="VCManagedResourceCompilerTool"
+ />
+ <Tool
+ Name="VCResourceCompilerTool"
+ />
+ <Tool
+ Name="VCPreLinkEventTool"
+ />
+ <Tool
+ Name="VCLinkerTool"
+ AdditionalDependencies="ws2_32.lib Crypt32.lib Winscard.lib Packet.lib wpcap.lib libeay32MT.lib ssleay32Mt.lib"
+ LinkIncremental="2"
+ AdditionalLibraryDirectories="C:\dev\WpdPack\lib;C:\dev\openssl\lib"
+ GenerateDebugInformation="true"
+ SubSystem="1"
+ TargetMachine="1"
+ />
+ <Tool
+ Name="VCALinkTool"
+ />
+ <Tool
+ Name="VCManifestTool"
+ />
+ <Tool
+ Name="VCXDCMakeTool"
+ />
+ <Tool
+ Name="VCBscMakeTool"
+ />
+ <Tool
+ Name="VCFxCopTool"
+ />
+ <Tool
+ Name="VCAppVerifierTool"
+ />
+ <Tool
+ Name="VCWebDeploymentTool"
+ />
+ <Tool
+ Name="VCPostBuildEventTool"
+ />
+ </Configuration>
+ <Configuration
+ Name="Release|Win32"
+ OutputDirectory="$(SolutionDir)$(ConfigurationName)"
+ IntermediateDirectory="$(ConfigurationName)"
+ ConfigurationType="1"
+ CharacterSet="1"
+ WholeProgramOptimization="1"
+ >
+ <Tool
+ Name="VCPreBuildEventTool"
+ />
+ <Tool
+ Name="VCCustomBuildTool"
+ />
+ <Tool
+ Name="VCXMLDataGeneratorTool"
+ />
+ <Tool
+ Name="VCWebServiceProxyGeneratorTool"
+ />
+ <Tool
+ Name="VCMIDLTool"
+ />
+ <Tool
+ Name="VCCLCompilerTool"
+ AdditionalIncludeDirectories="..\..;..\..\..\src;..\..\..\src\utils;C:\dev\WpdPack\include;C:\dev\openssl\include"
+ PreprocessorDefinitions="WIN32;NDEBUG;_CONSOLE;CONFIG_WIN32_DEFAULTS"
+ RuntimeLibrary="2"
+ UsePrecompiledHeader="0"
+ WarningLevel="3"
+ Detect64BitPortabilityProblems="true"
+ DebugInformationFormat="3"
+ DisableSpecificWarnings="4244;4267;4311"
+ />
+ <Tool
+ Name="VCManagedResourceCompilerTool"
+ />
+ <Tool
+ Name="VCResourceCompilerTool"
+ />
+ <Tool
+ Name="VCPreLinkEventTool"
+ />
+ <Tool
+ Name="VCLinkerTool"
+ AdditionalDependencies="ws2_32.lib Crypt32.lib Winscard.lib Packet.lib wpcap.lib libeay32MT.lib ssleay32Mt.lib"
+ LinkIncremental="1"
+ AdditionalLibraryDirectories="C:\dev\WpdPack\lib;C:\dev\openssl\lib"
+ GenerateDebugInformation="true"
+ SubSystem="1"
+ OptimizeReferences="2"
+ EnableCOMDATFolding="2"
+ TargetMachine="1"
+ />
+ <Tool
+ Name="VCALinkTool"
+ />
+ <Tool
+ Name="VCManifestTool"
+ />
+ <Tool
+ Name="VCXDCMakeTool"
+ />
+ <Tool
+ Name="VCBscMakeTool"
+ />
+ <Tool
+ Name="VCFxCopTool"
+ />
+ <Tool
+ Name="VCAppVerifierTool"
+ />
+ <Tool
+ Name="VCWebDeploymentTool"
+ />
+ <Tool
+ Name="VCPostBuildEventTool"
+ />
+ </Configuration>
+ </Configurations>
+ <References>
+ </References>
+ <Files>
+ <Filter
+ Name="Source Files"
+ Filter="cpp;c;cc;cxx;def;odl;idl;hpj;bat;asm;asmx"
+ UniqueIdentifier="{4FC737F1-C7A5-4376-A066-2A32D752A2FF}"
+ >
+ <File
+ RelativePath="..\..\..\src\crypto\aes-cbc.c"
+ >
+ </File>
+ <File
+ RelativePath="..\..\..\src\crypto\aes-ctr.c"
+ >
+ </File>
+ <File
+ RelativePath="..\..\..\src\crypto\aes-eax.c"
+ >
+ </File>
+ <File
+ RelativePath="..\..\..\src\crypto\aes-encblock.c"
+ >
+ </File>
+ <File
+ RelativePath="..\..\..\src\crypto\aes-omac1.c"
+ >
+ </File>
+ <File
+ RelativePath="..\..\..\src\crypto\aes-unwrap.c"
+ >
+ </File>
+ <File
+ RelativePath="..\..\..\src\crypto\aes-wrap.c"
+ >
+ </File>
+ <File
+ RelativePath="..\..\..\src\utils\base64.c"
+ >
+ </File>
+ <File
+ RelativePath="..\..\bssid_ignore.c"
+ >
+ </File>
+ <File
+ RelativePath="..\..\bss.c"
+ >
+ </File>
+ <File
+ RelativePath="..\..\..\src\eap_common\chap.c"
+ >
+ </File>
+ <File
+ RelativePath="..\..\..\src\utils\common.c"
+ >
+ </File>
+ <File
+ RelativePath="..\..\..\src\utils\config.c"
+ >
+ </File>
+ <File
+ RelativePath="..\..\config.c"
+ >
+ </File>
+ <File
+ RelativePath="..\..\config_file.c"
+ >
+ </File>
+ <File
+ RelativePath="..\..\..\src\crypto\crypto_openssl.c"
+ >
+ </File>
+ <File
+ RelativePath="..\..\ctrl_iface.c"
+ >
+ </File>
+ <File
+ RelativePath="..\..\ctrl_iface_named_pipe.c"
+ >
+ </File>
+ <File
+ RelativePath="..\..\..\src\drivers\driver_common.c"
+ >
+ </File>
+ <File
+ RelativePath="..\..\..\src\eap_peer\eap.c"
+ >
+ </File>
+ <File
+ RelativePath="..\..\..\src\eap_peer\eap_aka.c"
+ >
+ </File>
+ <File
+ RelativePath="..\..\..\src\eap_common\eap_common.c"
+ >
+ </File>
+ <File
+ RelativePath="..\..\..\src\eap_peer\eap_gtc.c"
+ >
+ </File>
+ <File
+ RelativePath="..\..\..\src\eap_peer\eap_leap.c"
+ >
+ </File>
+ <File
+ RelativePath="..\..\..\src\eap_peer\eap_md5.c"
+ >
+ </File>
+ <File
+ RelativePath="..\..\..\src\eap_peer\eap_methods.c"
+ >
+ </File>
+ <File
+ RelativePath="..\..\..\src\eap_peer\eap_mschapv2.c"
+ >
+ </File>
+ <File
+ RelativePath="..\..\..\src\eap_peer\eap_otp.c"
+ >
+ </File>
+ <File
+ RelativePath="..\..\..\src\eap_peer\eap_peap.c"
+ >
+ </File>
+ <File
+ RelativePath="..\..\..\src\eap_common\eap_peap_common.c"
+ >
+ </File>
+ <File
+ RelativePath="..\..\eap_register.c"
+ >
+ </File>
+ <File
+ RelativePath="..\..\..\src\eap_peer\eap_sim.c"
+ >
+ </File>
+ <File
+ RelativePath="..\..\..\src\eap_common\eap_sim_common.c"
+ >
+ </File>
+ <File
+ RelativePath="..\..\..\src\eap_peer\eap_tls.c"
+ >
+ </File>
+ <File
+ RelativePath="..\..\..\src\eap_peer\eap_tls_common.c"
+ >
+ </File>
+ <File
+ RelativePath="..\..\..\src\eap_peer\eap_tnc.c"
+ >
+ </File>
+ <File
+ RelativePath="..\..\..\src\eap_peer\eap_ttls.c"
+ >
+ </File>
+ <File
+ RelativePath="..\..\..\src\eapol_supp\eapol_supp_sm.c"
+ >
+ </File>
+ <File
+ RelativePath="..\..\eapol_test.c"
+ >
+ </File>
+ <File
+ RelativePath="..\..\..\src\utils\eloop_win.c"
+ >
+ </File>
+ <File
+ RelativePath="..\..\events.c"
+ >
+ </File>
+ <File
+ RelativePath="..\..\..\src\crypto\fips_prf_openssl.c"
+ >
+ </File>
+ <File
+ RelativePath="..\..\..\src\utils\ip_addr.c"
+ >
+ </File>
+ <File
+ RelativePath="..\..\..\src\l2_packet\l2_packet_winpcap.c"
+ >
+ </File>
+ <File
+ RelativePath="..\..\..\src\crypto\md5.c"
+ >
+ </File>
+ <File
+ RelativePath="..\..\..\src\crypto\ms_funcs.c"
+ >
+ </File>
+ <File
+ RelativePath="..\..\..\src\eap_peer\mschapv2.c"
+ >
+ </File>
+ <File
+ RelativePath="..\..\notify.c"
+ >
+ </File>
+ <File
+ RelativePath="..\..\..\src\utils\os_win32.c"
+ >
+ </File>
+ <File
+ RelativePath="..\..\..\src\utils\pcsc_funcs.c"
+ >
+ </File>
+ <File
+ RelativePath="..\..\..\src\rsn_supp\peerkey.c"
+ >
+ </File>
+ <File
+ RelativePath="..\..\..\src\rsn_supp\pmksa_cache.c"
+ >
+ </File>
+ <File
+ RelativePath="..\..\..\src\rsn_supp\preauth.c"
+ >
+ </File>
+ <File
+ RelativePath="..\..\..\src\radius\radius.c"
+ >
+ </File>
+ <File
+ RelativePath="..\..\..\src\radius\radius_client.c"
+ >
+ </File>
+ <File
+ RelativePath="..\..\scan.c"
+ >
+ </File>
+ <File
+ RelativePath="..\..\..\src\crypto\sha1.c"
+ >
+ </File>
+ <File
+ RelativePath="..\..\..\src\crypto\sha1-pbkdf2.c"
+ >
+ </File>
+ <File
+ RelativePath="..\..\..\src\crypto\sha1-prf.c"
+ >
+ </File>
+ <File
+ RelativePath="..\..\..\src\crypto\sha1-tlsprf.c"
+ >
+ </File>
+ <File
+ RelativePath="..\..\..\src\crypto\tls_openssl.c"
+ >
+ </File>
+ <File
+ RelativePath="..\..\..\src\eap_peer\tncc.c"
+ >
+ </File>
+ <File
+ RelativePath="..\..\..\src\rsn_supp\wpa.c"
+ >
+ </File>
+ <File
+ RelativePath="..\..\..\src\common\wpa_common.c"
+ >
+ </File>
+ <File
+ RelativePath="..\..\..\src\utils\wpa_debug.c"
+ >
+ </File>
+ <File
+ RelativePath="..\..\..\src\rsn_supp\wpa_ie.c"
+ >
+ </File>
+ <File
+ RelativePath="..\..\wpa_supplicant.c"
+ >
+ </File>
+ <File
+ RelativePath="..\..\..\src\utils\wpabuf.c"
+ >
+ </File>
+ <File
+ RelativePath="..\..\wpas_glue.c"
+ >
+ </File>
+ </Filter>
+ <Filter
+ Name="Header Files"
+ Filter="h;hpp;hxx;hm;inl;inc;xsd"
+ UniqueIdentifier="{93995380-89BD-4b04-88EB-625FBE52EBFB}"
+ >
+ </Filter>
+ <Filter
+ Name="Resource Files"
+ Filter="rc;ico;cur;bmp;dlg;rc2;rct;bin;rgs;gif;jpg;jpeg;jpe;resx;tiff;tif;png;wav"
+ UniqueIdentifier="{67DA6AB6-F800-4c08-8B7A-83BB121AAD01}"
+ >
+ </Filter>
+ </Files>
+ <Globals>
+ </Globals>
+</VisualStudioProject>
diff --git a/contrib/wpa/wpa_supplicant/vs2005/wpa_cli/wpa_cli.vcproj b/contrib/wpa/wpa_supplicant/vs2005/wpa_cli/wpa_cli.vcproj
new file mode 100755
index 000000000000..d2de768e7cdc
--- /dev/null
+++ b/contrib/wpa/wpa_supplicant/vs2005/wpa_cli/wpa_cli.vcproj
@@ -0,0 +1,215 @@
+<?xml version="1.0" encoding="Windows-1252"?>
+<VisualStudioProject
+ ProjectType="Visual C++"
+ Version="8.00"
+ Name="wpa_cli"
+ ProjectGUID="{E3A7B181-22CC-4DA3-8410-6AD69879A9EC}"
+ RootNamespace="wpa_cli"
+ Keyword="Win32Proj"
+ >
+ <Platforms>
+ <Platform
+ Name="Win32"
+ />
+ </Platforms>
+ <ToolFiles>
+ </ToolFiles>
+ <Configurations>
+ <Configuration
+ Name="Debug|Win32"
+ OutputDirectory="$(SolutionDir)$(ConfigurationName)"
+ IntermediateDirectory="$(ConfigurationName)"
+ ConfigurationType="1"
+ CharacterSet="0"
+ >
+ <Tool
+ Name="VCPreBuildEventTool"
+ />
+ <Tool
+ Name="VCCustomBuildTool"
+ />
+ <Tool
+ Name="VCXMLDataGeneratorTool"
+ />
+ <Tool
+ Name="VCWebServiceProxyGeneratorTool"
+ />
+ <Tool
+ Name="VCMIDLTool"
+ />
+ <Tool
+ Name="VCCLCompilerTool"
+ Optimization="0"
+ AdditionalIncludeDirectories="..\..\..\src;..\..\..\src\utils"
+ PreprocessorDefinitions="WIN32;_DEBUG;_CONSOLE;CONFIG_WIN32_DEFAULTS"
+ MinimalRebuild="true"
+ BasicRuntimeChecks="3"
+ RuntimeLibrary="3"
+ UsePrecompiledHeader="0"
+ WarningLevel="3"
+ Detect64BitPortabilityProblems="true"
+ DebugInformationFormat="4"
+ DisableSpecificWarnings="4244;4267"
+ />
+ <Tool
+ Name="VCManagedResourceCompilerTool"
+ />
+ <Tool
+ Name="VCResourceCompilerTool"
+ />
+ <Tool
+ Name="VCPreLinkEventTool"
+ />
+ <Tool
+ Name="VCLinkerTool"
+ AdditionalDependencies="ws2_32.lib"
+ LinkIncremental="2"
+ GenerateDebugInformation="true"
+ SubSystem="1"
+ TargetMachine="1"
+ />
+ <Tool
+ Name="VCALinkTool"
+ />
+ <Tool
+ Name="VCManifestTool"
+ />
+ <Tool
+ Name="VCXDCMakeTool"
+ />
+ <Tool
+ Name="VCBscMakeTool"
+ />
+ <Tool
+ Name="VCFxCopTool"
+ />
+ <Tool
+ Name="VCAppVerifierTool"
+ />
+ <Tool
+ Name="VCWebDeploymentTool"
+ />
+ <Tool
+ Name="VCPostBuildEventTool"
+ />
+ </Configuration>
+ <Configuration
+ Name="Release|Win32"
+ OutputDirectory="$(SolutionDir)$(ConfigurationName)"
+ IntermediateDirectory="$(ConfigurationName)"
+ ConfigurationType="1"
+ CharacterSet="0"
+ WholeProgramOptimization="1"
+ >
+ <Tool
+ Name="VCPreBuildEventTool"
+ />
+ <Tool
+ Name="VCCustomBuildTool"
+ />
+ <Tool
+ Name="VCXMLDataGeneratorTool"
+ />
+ <Tool
+ Name="VCWebServiceProxyGeneratorTool"
+ />
+ <Tool
+ Name="VCMIDLTool"
+ />
+ <Tool
+ Name="VCCLCompilerTool"
+ AdditionalIncludeDirectories="..\..\..\src;..\..\..\src\utils"
+ PreprocessorDefinitions="WIN32;NDEBUG;_CONSOLE;CONFIG_WIN32_DEFAULTS"
+ RuntimeLibrary="2"
+ UsePrecompiledHeader="0"
+ WarningLevel="3"
+ Detect64BitPortabilityProblems="true"
+ DebugInformationFormat="3"
+ DisableSpecificWarnings="4244;4267"
+ />
+ <Tool
+ Name="VCManagedResourceCompilerTool"
+ />
+ <Tool
+ Name="VCResourceCompilerTool"
+ />
+ <Tool
+ Name="VCPreLinkEventTool"
+ />
+ <Tool
+ Name="VCLinkerTool"
+ AdditionalDependencies="ws2_32.lib"
+ LinkIncremental="1"
+ GenerateDebugInformation="true"
+ SubSystem="1"
+ OptimizeReferences="2"
+ EnableCOMDATFolding="2"
+ TargetMachine="1"
+ />
+ <Tool
+ Name="VCALinkTool"
+ />
+ <Tool
+ Name="VCManifestTool"
+ />
+ <Tool
+ Name="VCXDCMakeTool"
+ />
+ <Tool
+ Name="VCBscMakeTool"
+ />
+ <Tool
+ Name="VCFxCopTool"
+ />
+ <Tool
+ Name="VCAppVerifierTool"
+ />
+ <Tool
+ Name="VCWebDeploymentTool"
+ />
+ <Tool
+ Name="VCPostBuildEventTool"
+ />
+ </Configuration>
+ </Configurations>
+ <References>
+ </References>
+ <Files>
+ <Filter
+ Name="Source Files"
+ Filter="cpp;c;cc;cxx;def;odl;idl;hpj;bat;asm;asmx"
+ UniqueIdentifier="{4FC737F1-C7A5-4376-A066-2A32D752A2FF}"
+ >
+ <File
+ RelativePath="..\..\..\src\utils\common.c"
+ >
+ </File>
+ <File
+ RelativePath="..\..\..\src\utils\os_win32.c"
+ >
+ </File>
+ <File
+ RelativePath="..\..\wpa_cli.c"
+ >
+ </File>
+ <File
+ RelativePath="..\..\..\src\common\wpa_ctrl.c"
+ >
+ </File>
+ </Filter>
+ <Filter
+ Name="Header Files"
+ Filter="h;hpp;hxx;hm;inl;inc;xsd"
+ UniqueIdentifier="{93995380-89BD-4b04-88EB-625FBE52EBFB}"
+ >
+ </Filter>
+ <Filter
+ Name="Resource Files"
+ Filter="rc;ico;cur;bmp;dlg;rc2;rct;bin;rgs;gif;jpg;jpeg;jpe;resx;tiff;tif;png;wav"
+ UniqueIdentifier="{67DA6AB6-F800-4c08-8B7A-83BB121AAD01}"
+ >
+ </Filter>
+ </Files>
+ <Globals>
+ </Globals>
+</VisualStudioProject>
diff --git a/contrib/wpa/wpa_supplicant/vs2005/wpa_passphrase/wpa_passphrase.vcproj b/contrib/wpa/wpa_supplicant/vs2005/wpa_passphrase/wpa_passphrase.vcproj
new file mode 100755
index 000000000000..97aa2c5aecb5
--- /dev/null
+++ b/contrib/wpa/wpa_supplicant/vs2005/wpa_passphrase/wpa_passphrase.vcproj
@@ -0,0 +1,236 @@
+<?xml version="1.0" encoding="Windows-1252"?>
+<VisualStudioProject
+ ProjectType="Visual C++"
+ Version="8.00"
+ Name="wpa_passphrase"
+ ProjectGUID="{ADBE4EA8-F0C5-40C2-AE89-C56D0F2EC1DF}"
+ RootNamespace="wpa_passphrase"
+ Keyword="Win32Proj"
+ >
+ <Platforms>
+ <Platform
+ Name="Win32"
+ />
+ </Platforms>
+ <ToolFiles>
+ </ToolFiles>
+ <Configurations>
+ <Configuration
+ Name="Debug|Win32"
+ OutputDirectory="$(SolutionDir)$(ConfigurationName)"
+ IntermediateDirectory="$(ConfigurationName)"
+ ConfigurationType="1"
+ CharacterSet="0"
+ >
+ <Tool
+ Name="VCPreBuildEventTool"
+ />
+ <Tool
+ Name="VCCustomBuildTool"
+ />
+ <Tool
+ Name="VCXMLDataGeneratorTool"
+ />
+ <Tool
+ Name="VCWebServiceProxyGeneratorTool"
+ />
+ <Tool
+ Name="VCMIDLTool"
+ />
+ <Tool
+ Name="VCCLCompilerTool"
+ Optimization="0"
+ AdditionalIncludeDirectories="..\..\..\src;..\..\..\src\utils;C:\dev\openssl\include"
+ PreprocessorDefinitions="WIN32;_DEBUG;_CONSOLE;CONFIG_WIN32_DEFAULTS"
+ MinimalRebuild="true"
+ BasicRuntimeChecks="3"
+ RuntimeLibrary="3"
+ UsePrecompiledHeader="0"
+ WarningLevel="3"
+ Detect64BitPortabilityProblems="true"
+ DebugInformationFormat="4"
+ DisableSpecificWarnings="4244;4267"
+ />
+ <Tool
+ Name="VCManagedResourceCompilerTool"
+ />
+ <Tool
+ Name="VCResourceCompilerTool"
+ />
+ <Tool
+ Name="VCPreLinkEventTool"
+ />
+ <Tool
+ Name="VCLinkerTool"
+ AdditionalDependencies="ws2_32.lib"
+ LinkIncremental="2"
+ AdditionalLibraryDirectories=""
+ GenerateDebugInformation="true"
+ SubSystem="1"
+ TargetMachine="1"
+ />
+ <Tool
+ Name="VCALinkTool"
+ />
+ <Tool
+ Name="VCManifestTool"
+ />
+ <Tool
+ Name="VCXDCMakeTool"
+ />
+ <Tool
+ Name="VCBscMakeTool"
+ />
+ <Tool
+ Name="VCFxCopTool"
+ />
+ <Tool
+ Name="VCAppVerifierTool"
+ />
+ <Tool
+ Name="VCWebDeploymentTool"
+ />
+ <Tool
+ Name="VCPostBuildEventTool"
+ />
+ </Configuration>
+ <Configuration
+ Name="Release|Win32"
+ OutputDirectory="$(SolutionDir)$(ConfigurationName)"
+ IntermediateDirectory="$(ConfigurationName)"
+ ConfigurationType="1"
+ CharacterSet="0"
+ WholeProgramOptimization="1"
+ >
+ <Tool
+ Name="VCPreBuildEventTool"
+ />
+ <Tool
+ Name="VCCustomBuildTool"
+ />
+ <Tool
+ Name="VCXMLDataGeneratorTool"
+ />
+ <Tool
+ Name="VCWebServiceProxyGeneratorTool"
+ />
+ <Tool
+ Name="VCMIDLTool"
+ />
+ <Tool
+ Name="VCCLCompilerTool"
+ AdditionalIncludeDirectories="..\..\..\src;..\..\..\src\utils;C:\dev\openssl\include"
+ PreprocessorDefinitions="WIN32;NDEBUG;_CONSOLE;CONFIG_WIN32_DEFAULTS"
+ RuntimeLibrary="2"
+ UsePrecompiledHeader="0"
+ WarningLevel="3"
+ Detect64BitPortabilityProblems="true"
+ DebugInformationFormat="3"
+ DisableSpecificWarnings="4244;4267"
+ />
+ <Tool
+ Name="VCManagedResourceCompilerTool"
+ />
+ <Tool
+ Name="VCResourceCompilerTool"
+ />
+ <Tool
+ Name="VCPreLinkEventTool"
+ />
+ <Tool
+ Name="VCLinkerTool"
+ AdditionalDependencies="ws2_32.lib"
+ LinkIncremental="1"
+ GenerateDebugInformation="true"
+ SubSystem="1"
+ OptimizeReferences="2"
+ EnableCOMDATFolding="2"
+ TargetMachine="1"
+ />
+ <Tool
+ Name="VCALinkTool"
+ />
+ <Tool
+ Name="VCManifestTool"
+ />
+ <Tool
+ Name="VCXDCMakeTool"
+ />
+ <Tool
+ Name="VCBscMakeTool"
+ />
+ <Tool
+ Name="VCFxCopTool"
+ />
+ <Tool
+ Name="VCAppVerifierTool"
+ />
+ <Tool
+ Name="VCWebDeploymentTool"
+ />
+ <Tool
+ Name="VCPostBuildEventTool"
+ />
+ </Configuration>
+ </Configurations>
+ <References>
+ </References>
+ <Files>
+ <Filter
+ Name="Source Files"
+ Filter="cpp;c;cc;cxx;def;odl;idl;hpj;bat;asm;asmx"
+ UniqueIdentifier="{4FC737F1-C7A5-4376-A066-2A32D752A2FF}"
+ >
+ <File
+ RelativePath="..\..\..\src\utils\common.c"
+ >
+ </File>
+ <File
+ RelativePath="..\..\..\src\crypto\md5.c"
+ >
+ </File>
+ <File
+ RelativePath="..\..\..\src\crypto\md5-internal.c"
+ >
+ </File>
+ <File
+ RelativePath="..\..\..\src\utils\os_win32.c"
+ >
+ </File>
+ <File
+ RelativePath="..\..\..\src\crypto\sha1.c"
+ >
+ </File>
+ <File
+ RelativePath="..\..\..\src\crypto\sha1-internal.c"
+ >
+ </File>
+ <File
+ RelativePath="..\..\..\src\crypto\sha1-prf.c"
+ >
+ </File>
+ <File
+ RelativePath="..\..\..\src\crypto\sha1-pbkdf2.c"
+ >
+ </File>
+ <File
+ RelativePath="..\..\wpa_passphrase.c"
+ >
+ </File>
+ </Filter>
+ <Filter
+ Name="Header Files"
+ Filter="h;hpp;hxx;hm;inl;inc;xsd"
+ UniqueIdentifier="{93995380-89BD-4b04-88EB-625FBE52EBFB}"
+ >
+ </Filter>
+ <Filter
+ Name="Resource Files"
+ Filter="rc;ico;cur;bmp;dlg;rc2;rct;bin;rgs;gif;jpg;jpeg;jpe;resx;tiff;tif;png;wav"
+ UniqueIdentifier="{67DA6AB6-F800-4c08-8B7A-83BB121AAD01}"
+ >
+ </Filter>
+ </Files>
+ <Globals>
+ </Globals>
+</VisualStudioProject>
diff --git a/contrib/wpa/wpa_supplicant/vs2005/wpa_supplicant/wpa_supplicant.vcproj b/contrib/wpa/wpa_supplicant/vs2005/wpa_supplicant/wpa_supplicant.vcproj
new file mode 100755
index 000000000000..10c05b565597
--- /dev/null
+++ b/contrib/wpa/wpa_supplicant/vs2005/wpa_supplicant/wpa_supplicant.vcproj
@@ -0,0 +1,465 @@
+<?xml version="1.0" encoding="Windows-1252"?>
+<VisualStudioProject
+ ProjectType="Visual C++"
+ Version="8.00"
+ Name="wpa_supplicant"
+ ProjectGUID="{8BCFDA77-AEDC-4168-8897-5B73105BBB87}"
+ RootNamespace="wpa_supplicant"
+ Keyword="Win32Proj"
+ >
+ <Platforms>
+ <Platform
+ Name="Win32"
+ />
+ </Platforms>
+ <ToolFiles>
+ </ToolFiles>
+ <Configurations>
+ <Configuration
+ Name="Debug|Win32"
+ OutputDirectory="$(SolutionDir)$(ConfigurationName)"
+ IntermediateDirectory="$(ConfigurationName)"
+ ConfigurationType="1"
+ CharacterSet="0"
+ >
+ <Tool
+ Name="VCPreBuildEventTool"
+ />
+ <Tool
+ Name="VCCustomBuildTool"
+ />
+ <Tool
+ Name="VCXMLDataGeneratorTool"
+ />
+ <Tool
+ Name="VCWebServiceProxyGeneratorTool"
+ />
+ <Tool
+ Name="VCMIDLTool"
+ />
+ <Tool
+ Name="VCCLCompilerTool"
+ Optimization="0"
+ AdditionalIncludeDirectories="..\..;..\..\..\src;..\..\..\src\utils;C:\dev\WpdPack\include;C:\dev\openssl\include"
+ PreprocessorDefinitions="WIN32;_DEBUG;_CONSOLE;CONFIG_WIN32_DEFAULTS"
+ MinimalRebuild="true"
+ BasicRuntimeChecks="3"
+ RuntimeLibrary="3"
+ UsePrecompiledHeader="0"
+ WarningLevel="3"
+ Detect64BitPortabilityProblems="true"
+ DebugInformationFormat="4"
+ DisableSpecificWarnings="4244;4267;4311"
+ />
+ <Tool
+ Name="VCManagedResourceCompilerTool"
+ />
+ <Tool
+ Name="VCResourceCompilerTool"
+ />
+ <Tool
+ Name="VCPreLinkEventTool"
+ />
+ <Tool
+ Name="VCLinkerTool"
+ AdditionalDependencies="wbemuuid.lib ws2_32.lib Crypt32.lib Winscard.lib Packet.lib wpcap.lib libeay32MT.lib ssleay32Mt.lib"
+ LinkIncremental="2"
+ AdditionalLibraryDirectories="C:\dev\WpdPack\lib;C:\dev\openssl\lib"
+ GenerateDebugInformation="true"
+ SubSystem="1"
+ TargetMachine="1"
+ />
+ <Tool
+ Name="VCALinkTool"
+ />
+ <Tool
+ Name="VCManifestTool"
+ />
+ <Tool
+ Name="VCXDCMakeTool"
+ />
+ <Tool
+ Name="VCBscMakeTool"
+ />
+ <Tool
+ Name="VCFxCopTool"
+ />
+ <Tool
+ Name="VCAppVerifierTool"
+ />
+ <Tool
+ Name="VCWebDeploymentTool"
+ />
+ <Tool
+ Name="VCPostBuildEventTool"
+ />
+ </Configuration>
+ <Configuration
+ Name="Release|Win32"
+ OutputDirectory="$(SolutionDir)$(ConfigurationName)"
+ IntermediateDirectory="$(ConfigurationName)"
+ ConfigurationType="1"
+ CharacterSet="0"
+ WholeProgramOptimization="1"
+ >
+ <Tool
+ Name="VCPreBuildEventTool"
+ />
+ <Tool
+ Name="VCCustomBuildTool"
+ />
+ <Tool
+ Name="VCXMLDataGeneratorTool"
+ />
+ <Tool
+ Name="VCWebServiceProxyGeneratorTool"
+ />
+ <Tool
+ Name="VCMIDLTool"
+ />
+ <Tool
+ Name="VCCLCompilerTool"
+ AdditionalIncludeDirectories="..\..;..\..\..\src;..\..\..\src\utils;C:\dev\WpdPack\include;C:\dev\openssl\include"
+ PreprocessorDefinitions="WIN32;NDEBUG;_CONSOLE;CONFIG_WIN32_DEFAULTS"
+ RuntimeLibrary="2"
+ UsePrecompiledHeader="0"
+ WarningLevel="3"
+ Detect64BitPortabilityProblems="true"
+ DebugInformationFormat="3"
+ DisableSpecificWarnings="4244;4267;4311"
+ />
+ <Tool
+ Name="VCManagedResourceCompilerTool"
+ />
+ <Tool
+ Name="VCResourceCompilerTool"
+ />
+ <Tool
+ Name="VCPreLinkEventTool"
+ />
+ <Tool
+ Name="VCLinkerTool"
+ AdditionalDependencies="wbemuuid.lib ws2_32.lib Crypt32.lib Winscard.lib Packet.lib wpcap.lib libeay32MT.lib ssleay32Mt.lib"
+ LinkIncremental="1"
+ AdditionalLibraryDirectories="C:\dev\WpdPack\lib;C:\dev\openssl\lib"
+ GenerateDebugInformation="true"
+ SubSystem="1"
+ OptimizeReferences="2"
+ EnableCOMDATFolding="2"
+ TargetMachine="1"
+ />
+ <Tool
+ Name="VCALinkTool"
+ />
+ <Tool
+ Name="VCManifestTool"
+ />
+ <Tool
+ Name="VCXDCMakeTool"
+ />
+ <Tool
+ Name="VCBscMakeTool"
+ />
+ <Tool
+ Name="VCFxCopTool"
+ />
+ <Tool
+ Name="VCAppVerifierTool"
+ />
+ <Tool
+ Name="VCWebDeploymentTool"
+ />
+ <Tool
+ Name="VCPostBuildEventTool"
+ />
+ </Configuration>
+ </Configurations>
+ <References>
+ </References>
+ <Files>
+ <Filter
+ Name="Source Files"
+ Filter="cpp;c;cc;cxx;def;odl;idl;hpj;bat;asm;asmx"
+ UniqueIdentifier="{4FC737F1-C7A5-4376-A066-2A32D752A2FF}"
+ >
+ <File
+ RelativePath="..\..\..\src\crypto\aes-cbc.c"
+ >
+ </File>
+ <File
+ RelativePath="..\..\..\src\crypto\aes-ctr.c"
+ >
+ </File>
+ <File
+ RelativePath="..\..\..\src\crypto\aes-eax.c"
+ >
+ </File>
+ <File
+ RelativePath="..\..\..\src\crypto\aes-encblock.c"
+ >
+ </File>
+ <File
+ RelativePath="..\..\..\src\crypto\aes-omac1.c"
+ >
+ </File>
+ <File
+ RelativePath="..\..\..\src\crypto\aes-unwrap.c"
+ >
+ </File>
+ <File
+ RelativePath="..\..\..\src\crypto\aes-wrap.c"
+ >
+ </File>
+ <File
+ RelativePath="..\..\..\src\utils\base64.c"
+ >
+ </File>
+ <File
+ RelativePath="..\..\bssid_ignore.c"
+ >
+ </File>
+ <File
+ RelativePath="..\..\bss.c"
+ >
+ </File>
+ <File
+ RelativePath="..\..\..\src\eap_common\chap.c"
+ >
+ </File>
+ <File
+ RelativePath="..\..\..\src\utils\common.c"
+ >
+ </File>
+ <File
+ RelativePath="..\..\..\src\utils\config.c"
+ >
+ </File>
+ <File
+ RelativePath="..\..\config.c"
+ >
+ </File>
+ <File
+ RelativePath="..\..\config_file.c"
+ >
+ </File>
+ <File
+ RelativePath="..\..\..\src\crypto\crypto_openssl.c"
+ >
+ </File>
+ <File
+ RelativePath="..\..\ctrl_iface.c"
+ >
+ </File>
+ <File
+ RelativePath="..\..\ctrl_iface_named_pipe.c"
+ >
+ </File>
+ <File
+ RelativePath="..\..\..\src\drivers\driver_common.c"
+ >
+ </File>
+ <File
+ RelativePath="..\..\..\src\drivers\driver_ndis.c"
+ >
+ </File>
+ <File
+ RelativePath="..\..\..\src\drivers\driver_ndis_.c"
+ >
+ </File>
+ <File
+ RelativePath="..\..\..\src\drivers\drivers.c"
+ >
+ </File>
+ <File
+ RelativePath="..\..\..\src\eap_peer\eap.c"
+ >
+ </File>
+ <File
+ RelativePath="..\..\..\src\eap_common\eap_common.c"
+ >
+ </File>
+ <File
+ RelativePath="..\..\..\src\eap_peer\eap_gtc.c"
+ >
+ </File>
+ <File
+ RelativePath="..\..\..\src\eap_peer\eap_leap.c"
+ >
+ </File>
+ <File
+ RelativePath="..\..\..\src\eap_peer\eap_md5.c"
+ >
+ </File>
+ <File
+ RelativePath="..\..\..\src\eap_peer\eap_methods.c"
+ >
+ </File>
+ <File
+ RelativePath="..\..\..\src\eap_peer\eap_mschapv2.c"
+ >
+ </File>
+ <File
+ RelativePath="..\..\..\src\eap_peer\eap_otp.c"
+ >
+ </File>
+ <File
+ RelativePath="..\..\..\src\eap_peer\eap_peap.c"
+ >
+ </File>
+ <File
+ RelativePath="..\..\..\src\eap_common\eap_peap_common.c"
+ >
+ </File>
+ <File
+ RelativePath="..\..\eap_register.c"
+ >
+ </File>
+ <File
+ RelativePath="..\..\..\src\eap_peer\eap_tls.c"
+ >
+ </File>
+ <File
+ RelativePath="..\..\..\src\eap_peer\eap_tls_common.c"
+ >
+ </File>
+ <File
+ RelativePath="..\..\..\src\eap_peer\eap_tnc.c"
+ >
+ </File>
+ <File
+ RelativePath="..\..\..\src\eap_peer\eap_ttls.c"
+ >
+ </File>
+ <File
+ RelativePath="..\..\..\src\eapol_supp\eapol_supp_sm.c"
+ >
+ </File>
+ <File
+ RelativePath="..\..\..\src\utils\eloop_win.c"
+ >
+ </File>
+ <File
+ RelativePath="..\..\events.c"
+ >
+ </File>
+ <File
+ RelativePath="..\..\..\src\l2_packet\l2_packet_winpcap.c"
+ >
+ </File>
+ <File
+ RelativePath="..\..\main.c"
+ >
+ </File>
+ <File
+ RelativePath="..\..\..\src\crypto\md5.c"
+ >
+ </File>
+ <File
+ RelativePath="..\..\..\src\crypto\ms_funcs.c"
+ >
+ </File>
+ <File
+ RelativePath="..\..\..\src\eap_peer\mschapv2.c"
+ >
+ </File>
+ <File
+ RelativePath="..\..\..\src\drivers\ndis_events.c"
+ >
+ </File>
+ <File
+ RelativePath="..\..\notify.c"
+ >
+ </File>
+ <File
+ RelativePath="..\..\..\src\utils\os_win32.c"
+ >
+ </File>
+ <File
+ RelativePath="..\..\..\src\utils\pcsc_funcs.c"
+ >
+ </File>
+ <File
+ RelativePath="..\..\..\src\rsn_supp\peerkey.c"
+ >
+ </File>
+ <File
+ RelativePath="..\..\..\src\rsn_supp\pmksa_cache.c"
+ >
+ </File>
+ <File
+ RelativePath="..\..\..\src\rsn_supp\preauth.c"
+ >
+ </File>
+ <File
+ RelativePath="..\..\scan.c"
+ >
+ </File>
+ <File
+ RelativePath="..\..\..\src\crypto\sha1.c"
+ >
+ </File>
+ <File
+ RelativePath="..\..\..\src\crypto\sha1-pbkdf2.c"
+ >
+ </File>
+ <File
+ RelativePath="..\..\..\src\crypto\sha1-prf.c"
+ >
+ </File>
+ <File
+ RelativePath="..\..\..\src\crypto\sha1-tlsprf.c"
+ >
+ </File>
+ <File
+ RelativePath="..\..\..\src\crypto\tls_openssl.c"
+ >
+ </File>
+ <File
+ RelativePath="..\..\..\src\eap_peer\tncc.c"
+ >
+ </File>
+ <File
+ RelativePath="..\..\..\src\rsn_supp\wpa.c"
+ >
+ </File>
+ <File
+ RelativePath="..\..\..\src\common\wpa_common.c"
+ >
+ </File>
+ <File
+ RelativePath="..\..\..\src\utils\wpa_debug.c"
+ >
+ </File>
+ <File
+ RelativePath="..\..\..\src\rsn_supp\wpa_ie.c"
+ >
+ </File>
+ <File
+ RelativePath="..\..\wpa_supplicant.c"
+ >
+ </File>
+ <File
+ RelativePath="..\..\..\src\utils\wpabuf.c"
+ >
+ </File>
+ <File
+ RelativePath="..\..\wpas_glue.c"
+ >
+ </File>
+ </Filter>
+ <Filter
+ Name="Header Files"
+ Filter="h;hpp;hxx;hm;inl;inc;xsd"
+ UniqueIdentifier="{93995380-89BD-4b04-88EB-625FBE52EBFB}"
+ >
+ </Filter>
+ <Filter
+ Name="Resource Files"
+ Filter="rc;ico;cur;bmp;dlg;rc2;rct;bin;rgs;gif;jpg;jpeg;jpe;resx;tiff;tif;png;wav"
+ UniqueIdentifier="{67DA6AB6-F800-4c08-8B7A-83BB121AAD01}"
+ >
+ </Filter>
+ </Files>
+ <Globals>
+ </Globals>
+</VisualStudioProject>
diff --git a/contrib/wpa/wpa_supplicant/wnm_sta.c b/contrib/wpa/wpa_supplicant/wnm_sta.c
index 96160dccbf5b..8a1a44690ba5 100644
--- a/contrib/wpa/wpa_supplicant/wnm_sta.c
+++ b/contrib/wpa/wpa_supplicant/wnm_sta.c
@@ -1097,8 +1097,6 @@ static void wnm_bss_tm_connect(struct wpa_supplicant *wpa_s,
struct wpa_bss *bss, struct wpa_ssid *ssid,
int after_new_scan)
{
- struct wpa_radio_work *already_connecting;
-
wpa_dbg(wpa_s, MSG_DEBUG,
"WNM: Transition to BSS " MACSTR
" based on BSS Transition Management Request (old BSSID "
@@ -1123,18 +1121,9 @@ static void wnm_bss_tm_connect(struct wpa_supplicant *wpa_s,
return;
}
- already_connecting = radio_work_pending(wpa_s, "sme-connect");
wpa_s->reassociate = 1;
wpa_printf(MSG_DEBUG, "WNM: Issuing connect");
wpa_supplicant_connect(wpa_s, bss, ssid);
-
- /*
- * Indicate that a BSS transition is in progress so scan results that
- * come in before the 'sme-connect' radio work gets executed do not
- * override the original connection attempt.
- */
- if (!already_connecting && radio_work_pending(wpa_s, "sme-connect"))
- wpa_s->bss_trans_mgmt_in_progress = true;
wnm_deallocate_memory(wpa_s);
}
diff --git a/contrib/wpa/wpa_supplicant/wpa_cli.c b/contrib/wpa/wpa_supplicant/wpa_cli.c
index 465d63862ee7..5bbc4535e364 100644
--- a/contrib/wpa/wpa_supplicant/wpa_cli.c
+++ b/contrib/wpa/wpa_supplicant/wpa_cli.c
@@ -1,6 +1,6 @@
/*
* WPA Supplicant - command line interface for wpa_supplicant daemon
- * Copyright (c) 2004-2022, Jouni Malinen <j@w1.fi>
+ * Copyright (c) 2004-2019, Jouni Malinen <j@w1.fi>
*
* This software may be distributed under the terms of the BSD license.
* See README for more details.
@@ -29,7 +29,7 @@
static const char *const wpa_cli_version =
"wpa_cli v" VERSION_STR "\n"
-"Copyright (c) 2004-2022, Jouni Malinen <j@w1.fi> and contributors";
+"Copyright (c) 2004-2019, Jouni Malinen <j@w1.fi> and contributors";
#define VENDOR_ELEM_FRAME_ID \
" 0: Probe Req (P2P), 1: Probe Resp (P2P) , 2: Probe Resp (GO), " \
diff --git a/contrib/wpa/wpa_supplicant/wpa_supplicant.c b/contrib/wpa/wpa_supplicant/wpa_supplicant.c
index cf990f5a8f85..53b44035f16e 100644
--- a/contrib/wpa/wpa_supplicant/wpa_supplicant.c
+++ b/contrib/wpa/wpa_supplicant/wpa_supplicant.c
@@ -1,6 +1,6 @@
/*
* WPA Supplicant
- * Copyright (c) 2003-2022, Jouni Malinen <j@w1.fi>
+ * Copyright (c) 2003-2019, Jouni Malinen <j@w1.fi>
*
* This software may be distributed under the terms of the BSD license.
* See README for more details.
@@ -71,7 +71,7 @@
const char *const wpa_supplicant_version =
"wpa_supplicant v" VERSION_STR "\n"
-"Copyright (c) 2003-2022, Jouni Malinen <j@w1.fi> and contributors";
+"Copyright (c) 2003-2019, Jouni Malinen <j@w1.fi> and contributors";
const char *const wpa_supplicant_license =
"This software may be distributed under the terms of the BSD license.\n"
@@ -3621,11 +3621,6 @@ static void wpas_start_assoc_cb(struct wpa_radio_work *work, int deinit)
struct ieee80211_vht_capabilities vhtcaps_mask;
#endif /* CONFIG_VHT_OVERRIDES */
- wpa_s->roam_in_progress = false;
-#ifdef CONFIG_WNM
- wpa_s->bss_trans_mgmt_in_progress = false;
-#endif /* CONFIG_WNM */
-
if (deinit) {
if (work->started) {
wpa_s->connect_work = NULL;
@@ -8178,10 +8173,6 @@ void wpas_request_disconnection(struct wpa_supplicant *wpa_s)
eloop_cancel_timeout(wpas_network_reenabled, wpa_s, NULL);
radio_remove_works(wpa_s, "connect", 0);
radio_remove_works(wpa_s, "sme-connect", 0);
- wpa_s->roam_in_progress = false;
-#ifdef CONFIG_WNM
- wpa_s->bss_trans_mgmt_in_progress = false;
-#endif /* CONFIG_WNM */
}
diff --git a/contrib/wpa/wpa_supplicant/wpa_supplicant_i.h b/contrib/wpa/wpa_supplicant/wpa_supplicant_i.h
index 8bb8672fbf7f..5fa765fda25c 100644
--- a/contrib/wpa/wpa_supplicant/wpa_supplicant_i.h
+++ b/contrib/wpa/wpa_supplicant/wpa_supplicant_i.h
@@ -1286,7 +1286,6 @@ struct wpa_supplicant {
struct os_reltime wnm_cand_valid_until;
u8 wnm_cand_from_bss[ETH_ALEN];
enum bss_trans_mgmt_status_code bss_tm_status;
- bool bss_trans_mgmt_in_progress;
struct wpabuf *coloc_intf_elems;
u8 coloc_intf_dialog_token;
u8 coloc_intf_auto_report;
diff --git a/sys/contrib/openzfs/tests/zfs-tests/cmd/send_doall/.gitignore b/sys/contrib/openzfs/tests/zfs-tests/cmd/send_doall/.gitignore
new file mode 100644
index 000000000000..6ba2e603f744
--- /dev/null
+++ b/sys/contrib/openzfs/tests/zfs-tests/cmd/send_doall/.gitignore
@@ -0,0 +1 @@
+/send_doall
diff --git a/tests/Makefile b/tests/Makefile
new file mode 100644
index 000000000000..561a0ec5fcab
--- /dev/null
+++ b/tests/Makefile
@@ -0,0 +1,22 @@
+# $FreeBSD$
+
+PACKAGE= tests
+
+TESTSDIR= ${TESTSBASE}
+
+${PACKAGE}FILES+= README
+
+KYUAFILE= yes
+
+SUBDIR+= etc
+SUBDIR+= sys
+
+SUBDIR_PARALLEL=
+
+afterinstall: install-tests-local
+install-tests-local: .PHONY
+ ${INSTALL_SYMLINK} -T 'package=tests' \
+ ../local/tests ${DESTDIR}${TESTSDIR}/local
+
+.include "Makefile.inc0"
+.include <bsd.test.mk>
diff --git a/tests/README b/tests/README
new file mode 100644
index 000000000000..c014db2cb9ba
--- /dev/null
+++ b/tests/README
@@ -0,0 +1,62 @@
+src/tests: The FreeBSD test suite
+=================================
+
+Usage of the FreeBSD test suite:
+(1) Run the tests:
+ kyua test -k /usr/tests/Kyuafile
+(2) See the test results:
+ kyua report
+
+For further information on using the test suite, read tests(7):
+ man tests
+
+Description of FreeBSD test suite
+=================================
+The build of the test suite is organized in the following manner:
+
+* The build of all test artifacts is protected by the MK_TESTS knob.
+ The user can disable these with the WITHOUT_TESTS setting in
+ src.conf(5).
+
+* The goal for /usr/tests/ (the installed test programs) is to follow
+ the same hierarchy as /usr/src/ wherever possible, which in turn drives
+ several of the design decisions described below. This simplifies the
+ discoverability of tests. We want a mapping such as:
+
+ /usr/src/bin/cp/ -> /usr/tests/bin/cp/
+ /usr/src/lib/libc/ -> /usr/tests/lib/libc/
+ /usr/src/usr.bin/cut/ -> /usr/tests/usr.bin/cut/
+ ... and many more ...
+
+* Test programs for specific utilities and libraries are located next
+ to the source code of such programs. For example, the tests for the
+ src/lib/libcrypt/ library live in src/lib/libcrypt/tests/. The tests/
+ subdirectory is optional and should, in general, be avoided.
+
+* The src/tests/ hierarchy (this directory) provides generic test
+ infrastructure and glue code to join all test programs together into
+ a single test suite definition.
+
+* The src/tests/ hierarchy also includes cross-functional test programs:
+ i.e. test programs that cover more than a single utility or library
+ and thus don't fit anywhere else in the tree. Consider this to follow
+ the same rationale as src/share/man/: this directory contains generic
+ manual pages while the manual pages that are specific to individual
+ tools or libraries live next to the source code.
+
+In order to keep the src/tests/ hierarchy decoupled from the actual test
+programs being installed --which is a worthy goal because it simplifies
+the addition of new test programs and simplifies the maintenance of the
+tree-- the top-level Kyuafile does not know which subdirectories may
+exist upfront. Instead, such Kyuafile automatically detects, at
+run-time, which */Kyuafile files exist and uses those directly.
+
+Similarly, every directory in src/ that wants to install a Kyuafile to
+just recurse into other subdirectories reuses this Kyuafile with
+auto-discovery features. As an example, take a look at src/lib/tests/
+whose sole purpose is to install a Kyuafile into /usr/tests/lib/.
+The goal in this specific case is for /usr/tests/lib/ to be generated
+entirely from src/lib/.
+
+--
+$FreeBSD$
diff --git a/usr.bin/bmake/tests/archives/fmt_44bsd/expected.status.1 b/usr.bin/bmake/tests/archives/fmt_44bsd/expected.status.1
new file mode 100644
index 000000000000..573541ac9702
--- /dev/null
+++ b/usr.bin/bmake/tests/archives/fmt_44bsd/expected.status.1
@@ -0,0 +1 @@
+0