[meta-xilinx] [PATCH] minized-zynq7: Add support * Add the device tree for MiniZed * Add machine config for MiniZed * Add u-boot patch to support MiniZed * Add kernel config to suport MiniZed and wireless module * Add driver for Murata wireless module

Clement Laigle clement.laigle8 at gmail.com
Thu May 3 22:34:30 PDT 2018


From: Clément Laigle <clement.laigle8 at gmail.com>

Signed-off-by: Clément Laigle <clement.laigle8 at gmail.com>
---
 .../conf/machine/minized-zynq7.conf                |   30 +
 .../recipes-bsp/device-tree/device-tree.bbappend   |    6 +
 .../device-tree/files/minized-zynq7.dts            |  241 ++
 .../minized-wireless/minized-wireless_2017.4.bb    |   49 +
 .../u-boot-xlnx/v2017.3/u-boot-minized.patch       |  203 +
 .../recipes-bsp/u-boot/u-boot-xlnx_2017.3.bbappend |    8 +
 .../hostapd/hostapd/0001-murata-hostapd-conf.patch |   11 +
 .../hostapd/hostapd_%.bbappend                     |    5 +
 ...urata-wpa_supplication-Add-server-in-hs20.patch | 4575 ++++++++++++++++++++
 .../wpa-supplicant/wpa-supplicant_%.bbappend       |    4 +
 .../cypress-orga-backport_4.12.bb                  |   73 +
 .../linux/linux-xlnx/v2017.3/wifi-bluetooth.cfg    |   33 +
 .../linux/linux-xlnx_2017.3.bbappend               |    1 +
 13 files changed, 5239 insertions(+)
 create mode 100644 meta-xilinx-contrib/conf/machine/minized-zynq7.conf
 create mode 100644 meta-xilinx-contrib/recipes-bsp/device-tree/device-tree.bbappend
 create mode 100644 meta-xilinx-contrib/recipes-bsp/device-tree/files/minized-zynq7.dts
 create mode 100644 meta-xilinx-contrib/recipes-bsp/minized-wireless/minized-wireless_2017.4.bb
 create mode 100644 meta-xilinx-contrib/recipes-bsp/u-boot/u-boot-xlnx/v2017.3/u-boot-minized.patch
 create mode 100644 meta-xilinx-contrib/recipes-bsp/u-boot/u-boot-xlnx_2017.3.bbappend
 create mode 100644 meta-xilinx-contrib/recipes-connectivity/hostapd/hostapd/0001-murata-hostapd-conf.patch
 create mode 100644 meta-xilinx-contrib/recipes-connectivity/hostapd/hostapd_%.bbappend
 create mode 100644 meta-xilinx-contrib/recipes-connectivity/wpa-supplicant/wpa-supplicant/0001-murata-wpa_supplication-Add-server-in-hs20.patch
 create mode 100644 meta-xilinx-contrib/recipes-connectivity/wpa-supplicant/wpa-supplicant_%.bbappend
 create mode 100644 meta-xilinx-contrib/recipes-kernel/cypress-orga-backport/cypress-orga-backport_4.12.bb
 create mode 100644 meta-xilinx-contrib/recipes-kernel/linux/linux-xlnx/v2017.3/wifi-bluetooth.cfg

diff --git a/meta-xilinx-contrib/conf/machine/minized-zynq7.conf b/meta-xilinx-contrib/conf/machine/minized-zynq7.conf
new file mode 100644
index 0000000..92d3876
--- /dev/null
+++ b/meta-xilinx-contrib/conf/machine/minized-zynq7.conf
@@ -0,0 +1,30 @@
+#@TYPE: Machine
+#@NAME: minized-zynq7
+#@DESCRIPTION: Machine support for MiniZed. (http://www.minized.org/)
+
+require conf/machine/include/tune-zynq.inc
+require conf/machine/include/machine-xilinx-default.inc
+require conf/machine/include/machine-xilinx-board.inc
+
+MACHINE_FEATURES = "ext2 vfat usbhost wifi bluetooth"
+
+MACHINE_ESSENTIAL_EXTRA_RRECOMMENDS += "cypress-orga-backport minized-wireless"
+
+# u-boot configuration
+UBOOT_MACHINE = "zynq_minized_config"
+SPL_BINARY = "spl/boot.bin"
+
+EXTRA_IMAGEDEPENDS += " \
+		u-boot-zynq-uenv \
+		virtual/boot-bin \
+		"
+
+SERIAL_CONSOLE = "115200 ttyPS0"
+
+MACHINE_ESSENTIAL_EXTRA_RDEPENDS += "device-tree"
+
+IMAGE_BOOT_FILES += " \
+		boot.bin \
+		${MACHINE}.dtb \
+		uEnv.txt \
+		"
diff --git a/meta-xilinx-contrib/recipes-bsp/device-tree/device-tree.bbappend b/meta-xilinx-contrib/recipes-bsp/device-tree/device-tree.bbappend
new file mode 100644
index 0000000..79f63fe
--- /dev/null
+++ b/meta-xilinx-contrib/recipes-bsp/device-tree/device-tree.bbappend
@@ -0,0 +1,6 @@
+FILESEXTRAPATHS_prepend := "${THISDIR}/files:"
+
+# device tree sources for MiniZed
+COMPATIBLE_MACHINE_minized-zynq7 = ".*"
+SRC_URI_append_minized-zynq7 = " file://minized-zynq7.dts"
+
diff --git a/meta-xilinx-contrib/recipes-bsp/device-tree/files/minized-zynq7.dts b/meta-xilinx-contrib/recipes-bsp/device-tree/files/minized-zynq7.dts
new file mode 100644
index 0000000..36ac168
--- /dev/null
+++ b/meta-xilinx-contrib/recipes-bsp/device-tree/files/minized-zynq7.dts
@@ -0,0 +1,241 @@
+/dts-v1/;
+/include/ "zynq-7000.dtsi"
+
+
+/ {
+	model = "Zynq Minized Board";
+	compatible = "xlnx,zynq-Minized", "xlnx,zynq-7000";
+
+	chosen {
+		stdout-path = "serial0:115200n8";
+	};
+
+	cpus {
+		cpu at 0 {
+			operating-points = <666666 1000000 333333 1000000>;
+		};
+	};
+
+	aliases {
+		serial0 = &uart1;
+                serial1 = &bluetooth_uart;
+		spi0 = &qspi;
+		mmc0 = &sdhci1;
+	};
+
+	memory {
+		device_type = "memory";
+		reg = <0 0x20000000>;
+	};
+
+	usb_phy0: phy0 {
+		compatible = "ulpi-phy";
+		#phy-cells = <0x0>;
+		reg = <0xe0002000 0x1000>;
+		view-port = <0x170>;
+		drv-vbus;
+		linux,phandle = <0x7>;
+		phandle = <0x7>;
+	};
+
+	leds {
+		compatible = "gpio-leds";
+		led-psg {
+			label = "led-psg";
+			gpios = <&gpio0 53 0>;
+			default-state = "on";
+			linux,default-trigger = "heartbeat";
+		};
+		led-psr {
+			label = "led-psr";
+			gpios = <&gpio0 52 0>;
+			default-state = "on";
+			linux,default-trigger = "heartbeat";
+		};
+	};
+
+	gpio-keys {
+		compatible = "gpio-keys";
+		#address-cells = <1>;
+		#size-cells = <0>;
+		autorepeat;
+		sw3 {
+			label = "ps-bp";
+			gpios = <&gpio0 0 0>;
+			linux,code = <108>; /* down */
+			gpio-key,wakeup;
+			autorepeat;
+		};
+	};
+
+	amba_pl: amba_pl {
+		#address-cells = <1>;
+		#size-cells = <1>;
+		compatible = "simple-bus";
+		ranges ;
+
+		axi_gpio_0: gpio at 41200000 {
+			#gpio-cells = <2>;
+			compatible = "xlnx,xps-gpio-1.00.a";
+			gpio-controller ;
+			reg = <0x41200000 0x10000>;
+			xlnx,all-inputs = <0x0>;
+			xlnx,all-inputs-2 = <0x0>;
+			xlnx,all-outputs = <0x1>;
+			xlnx,all-outputs-2 = <0x1>;
+			xlnx,dout-default = <0x00000000>;
+			xlnx,dout-default-2 = <0x00000000>;
+			xlnx,gpio-width = <0x1>;
+			xlnx,gpio2-width = <0x1>;
+			xlnx,interrupt-present = <0x0>;
+			xlnx,is-dual = <0x1>;
+			xlnx,tri-default = <0xFFFFFFFF>;
+			xlnx,tri-default-2 = <0xFFFFFFFF>;
+		};
+		axi_gpio_1: gpio at 41210000 {
+			#gpio-cells = <2>;
+			compatible = "xlnx,xps-gpio-1.00.a";
+			gpio-controller ;
+			reg = <0x41210000 0x10000>;
+			xlnx,all-inputs = <0x1>;
+			xlnx,all-inputs-2 = <0x0>;
+			xlnx,all-outputs = <0x0>;
+			xlnx,all-outputs-2 = <0x0>;
+			xlnx,dout-default = <0x00000000>;
+			xlnx,dout-default-2 = <0x00000000>;
+			xlnx,gpio-width = <0x1>;
+			xlnx,gpio2-width = <0x20>;
+			xlnx,interrupt-present = <0x0>;
+			xlnx,is-dual = <0x0>;
+			xlnx,tri-default = <0xFFFFFFFF>;
+			xlnx,tri-default-2 = <0xFFFFFFFF>;
+		};
+		axi_iic_0: i2c at 41600000 {
+			#address-cells = <1>;
+			#size-cells = <0>;
+			clock-names = "ref_clk";
+			clocks = <&clkc 15>;
+			compatible = "xlnx,xps-iic-2.00.a";
+			interrupt-parent = <&intc>;
+			interrupts = <0 30 4>;
+			reg = <0x41600000 0x10000>;
+		};
+		bluetooth_uart: serial at 43c00000 {
+			clock-frequency = <0x2dc6c00>;
+			clock-names = "ref_clk";
+			clocks = <&clkc 0>;
+			compatible = "xlnx,xps-uart16550-2.00.a", "ns16550a";
+			current-speed = <115200>;
+			device_type = "serial";
+			interrupt-parent = <&intc>;
+			interrupts = <0 29 4>;
+			port-number = <1>;
+			reg = <0x43c00000 0x10000>;
+			reg-offset = <0x1000>;
+			reg-shift = <2>;
+			xlnx,external-xin-clk-hz = <0x2dc6c00>;
+			xlnx,external-xin-clk-hz-d = <0x30>;
+			xlnx,has-external-rclk = <0x0>;
+			xlnx,has-external-xin = <0x1>;
+			xlnx,is-a-16550 = <0x1>;
+			xlnx,s-axi-aclk-freq-hz-d = "100.0";
+			xlnx,use-modem-ports = <0x1>;
+			xlnx,use-user-ports = <0x1>;
+		};
+	};
+
+	wlreg_on: wlreg-on {
+		compatible = "regulator-fixed";
+		regulator-name = "wlreg_on";
+		enable-active-high;
+		gpio = <&gpio0 56 0>;
+		regulator-min-microvolt = <3300000>;
+		regulator-max-microvolt = <3300000>;
+		startup-delay-us = <100>;
+	};
+};
+
+&gpio0 {
+	emio-gpio-width = <4>;
+	gpio-mask-high = <0x0>;
+	gpio-mask-low = <0x5600>;
+};
+
+&intc {
+	num_cpus = <1>;
+	num_interrupts = <96>;
+};
+
+&uart1 {
+	status = "okay";
+};
+
+&sdhci0 {
+	status = "okay";
+	bus-width= <4>;
+	xlnx,has-cd = <0x0>;
+	xlnx,has-power = <0x0>;
+	xlnx,has-wp = <0x0>;
+	non-removeable;
+	broken-cd;
+	vmmc-supply = <&wlreg_on>;
+
+        brcmf: brcmf at 1 {
+		status = "okay";
+                reg = <1>;
+                compatible = "brcm,bcm43430-fmac";
+	};
+};
+
+&sdhci1 {
+	status = "okay";
+	broken-adma2;
+	xlnx,has-cd = <0x1>;
+	xlnx,has-power = <0x0>;
+	xlnx,has-wp = <0x1>;
+};
+
+&usb0 {
+	status = "okay";
+	dr_mode = "host";
+	phy_type = "ulpi";
+	usb-reset = <&gpio0 7 0>;
+	usb-phy = <&usb_phy0>;
+};
+
+&qspi {
+	status = "okay";
+	#address-cells = <1>;
+	#size-cells = <0>;
+	interrupt-parent = <0x3>;
+	is-dual = <0x0>;
+	num-cs = <0x1>;
+
+
+	flash0: flash at 0 {
+		compatible = "micron,m25p80";
+		reg = <0x0>;
+		#address-cells = <1>;
+		#size-cells = <1>;
+		spi-max-frequency = <50000000>;
+
+		partition at 0x00000000 {
+			label = "boot";
+			reg = <0x00000000 0x00500000>;
+		};
+		partition at 0x00500000 {
+			label = "bootenv";
+			reg = <0x00500000 0x00020000>;
+		};
+		partition at 0x00520000 {
+			label = "kernel";
+			reg = <0x00520000 0x00a80000>;
+		};
+		partition at 0x00fa0000 {
+			label = "spare";
+			reg = <0x00fa0000 0x00000000>;
+		};
+
+	};
+};
+
diff --git a/meta-xilinx-contrib/recipes-bsp/minized-wireless/minized-wireless_2017.4.bb b/meta-xilinx-contrib/recipes-bsp/minized-wireless/minized-wireless_2017.4.bb
new file mode 100644
index 0000000..a2e9c10
--- /dev/null
+++ b/meta-xilinx-contrib/recipes-bsp/minized-wireless/minized-wireless_2017.4.bb
@@ -0,0 +1,49 @@
+SUMMARY = "minized-wireless:  Wi-Fi/BT drivers and firmware for the Murata 1DX module"
+#SECTION = "PETALINUX/modules"
+LICENSE = "MIT"
+LIC_FILES_CHKSUM = "file://${COMMON_LICENSE_DIR}/MIT;md5=0835ade698e0bcf8506ecda2f7b4f302"
+
+SRC_URI = " \
+	git://github.com/murata-wireless/cyw-fmac-fw;protocol=http;branch=orga;destsuffix=cyw-fmac-fw \
+	git://github.com/murata-wireless/cyw-fmac-nvram;protocol=http;branch=orga;destsuffix=cyw-fmac-nvram \
+	git://github.com/murata-wireless/cyw-bt-patch;protocol=http;branch=morty-orga;destsuffix=cyw-bt-patch \
+	git://github.com/murata-wireless/cyw-fmac-utils-imx32;protocol=http;branch=orga;destsuffix=cyw-fmac-utils-imx32 \
+"
+
+SRCREV_cyw-fmac-fw = "2242fd3f67a913fbfff8678cc8f7761629dca8ca"
+SRCREV_cyw-fmac-nvram = "d12c2ac1b93941eaa03063bb7cb3c1ee1aa1b7d0"
+SRCREV_cyw-bt-patch = "9216e0d9f778009b5667d032886dfd49174c4b3a"
+SRCREV_cyw-fmac-utils-imx32 = "060688dfe76df98751207c8146268ce7fd80b6ab"
+
+SRCREV_default = "${AUTOREV}"
+
+DEPENDS = "libnl virtual/kernel"
+
+S = "${WORKDIR}"
+
+KERNEL_VERSION = "${@base_read_file('${STAGING_KERNEL_BUILDDIR}/kernel-abiversion')}"
+
+
+do_install() {
+	install -d ${D}/lib/firmware/brcm
+	install -d ${D}/etc/firmware
+	install -d ${D}/usr/bin
+
+	install -m 644 ${S}/cyw-fmac-fw/brcmfmac43430-sdio.bin ${D}/lib/firmware/brcm/brcmfmac43430-sdio.bin
+	install -m 644 ${S}/cyw-fmac-nvram/brcmfmac43430-sdio.txt ${D}/lib/firmware/brcm/brcmfmac43430-sdio.txt
+	install -m 644 ${S}/cyw-bt-patch/CYW43430A1.1DX.hcd ${D}/etc/firmware/BCM43430A1.1DX.hcd
+	install -m 755 ${S}/cyw-fmac-utils-imx32/wl ${D}/usr/bin/wl
+}
+
+PACKAGES =+ "${PN}-mfgtest"
+
+FILES_${PN} = " \
+	/lib/firmware/brcm/brcmfmac43430-sdio.bin \
+	/lib/firmware/brcm/brcmfmac43430-sdio.txt \
+	/etc/firmware/BCM43430A1.1DX.hcd \
+"
+
+FILES_${PN}-mfgtest = " \
+	/usr/bin/wl \
+"
+
diff --git a/meta-xilinx-contrib/recipes-bsp/u-boot/u-boot-xlnx/v2017.3/u-boot-minized.patch b/meta-xilinx-contrib/recipes-bsp/u-boot/u-boot-xlnx/v2017.3/u-boot-minized.patch
new file mode 100644
index 0000000..793656a
--- /dev/null
+++ b/meta-xilinx-contrib/recipes-bsp/u-boot/u-boot-xlnx/v2017.3/u-boot-minized.patch
@@ -0,0 +1,203 @@
+From 4401d707451ea115d944e7ba34c6fd5777236e2d Mon Sep 17 00:00:00 2001
+From: Clément Laigle <clement.laigle8 at gmail.com>
+Date: Wed, 2 May 2018 20:29:22 +0200
+Subject: [PATCH] Add MiniZed support
+MIME-Version: 1.0
+Content-Type: text/plain; charset=UTF-8
+Content-Transfer-Encoding: 8bit
+
+Signed-off-by: Clément Laigle <clement.laigle8 at gmail.com>
+---
+ arch/arm/dts/Makefile          |   3 +-
+ arch/arm/dts/zynq-minized.dts  | 101 +++++++++++++++++++++++++++++++++++++++++
+ configs/zynq_minized_defconfig |  56 +++++++++++++++++++++++
+ 3 files changed, 159 insertions(+), 1 deletion(-)
+ create mode 100644 arch/arm/dts/zynq-minized.dts
+ create mode 100644 configs/zynq_minized_defconfig
+
+diff --git a/arch/arm/dts/Makefile b/arch/arm/dts/Makefile
+index 27330e6..dde2c8e 100644
+--- a/arch/arm/dts/Makefile
++++ b/arch/arm/dts/Makefile
+@@ -115,7 +115,8 @@ dtb-$(CONFIG_ARCH_ZYNQ) += zynq-zc702.dtb \
+	zynq-zc770-xm010.dtb \
+	zynq-zc770-xm011.dtb \
+	zynq-zc770-xm012.dtb \
+-	zynq-zc770-xm013.dtb
++	zynq-zc770-xm013.dtb \
++	zynq-minized.dtb
+ dtb-$(CONFIG_ARCH_ZYNQMP) += \
+	zynqmp-ep108.dtb			\
+	zynqmp-zcu100-revC.dtb			\
+diff --git a/arch/arm/dts/zynq-minized.dts b/arch/arm/dts/zynq-minized.dts
+new file mode 100644
+index 0000000..383d184
+--- /dev/null
++++ b/arch/arm/dts/zynq-minized.dts
+@@ -0,0 +1,101 @@
++/*
++ * Avnet Minized board DTS
++ *
++ * Copyright (C) 2015 Xilinx, Inc.
++ *
++ * SPDX-License-Identifier:	GPL-2.0+
++ */
++/dts-v1/;
++#include "zynq-7000.dtsi"
++
++/ {
++	model = "Zynq Minized Board";
++	compatible = "xlnx,zynq-Minized", "xlnx,zynq-7000";
++
++	chosen {
++		stdout-path = "serial0:115200n8";
++	};
++
++	cpus {
++		cpu at 0 {
++			operating-points = <666666 1000000 333333 1000000>;
++		};
++	};
++
++	aliases {
++		serial0 = &uart1;
++		spi0 = &qspi;
++		mmc0 = &sdhci1;
++	};
++
++	memory at 0 {
++		device_type = "memory";
++		reg = <0 0x20000000>;
++	};
++
++};
++
++&gpio0 {
++	emio-gpio-width = <4>;
++	gpio-mask-high = <0x0>;
++	gpio-mask-low = <0x5600>;
++};
++
++&intc {
++	num_cpus = <2>;
++	num_interrupts = <96>;
++};
++
++&uart1 {
++	u-boot,dm-pre-reloc;
++	status = "okay";
++};
++
++&sdhci0 {
++	status = "disabled";
++};
++
++&sdhci1 {
++	status = "okay";
++	broken-adma2;
++	xlnx,has-cd = <0x1>;
++	xlnx,has-power = <0x0>;
++	xlnx,has-wp = <0x1>;
++};
++
++&usb0 {
++	dr_mode = "host";
++	phy_type = "ulpi";
++	status = "okay";
++	usb-reset = <&gpio0 7 0>; /* USB_RST_N-MIO7 */
++};
++
++&qspi {
++	u-boot,dm-pre-reloc;
++	status = "okay";
++	#address-cells = <1>;
++	#size-cells = <0>;
++	flash0: flash at 0 {
++		compatible = "micron,n25q128";
++		reg = <0x0>;
++		#address-cells = <1>;
++		#size-cells = <1>;
++		spi-max-frequency = <50000000>;
++		partition at 0x00000000 {
++			label = "boot";
++			reg = <0x00000000 0x00620000>;
++		};
++		partition at 0x00620000 {
++			label = "bootenv";
++			reg = <0x00620000 0x00020000>;
++		};
++		partition at 0x00640000 {
++			label = "kernel";
++			reg = <0x00640000 0x00960000>;
++		};
++		partition at 0x00fa0000 {
++			label = "spare";
++			reg = <0x00fa0000 0x00060000>;
++		};
++	};
++};
+diff --git a/configs/zynq_minized_defconfig b/configs/zynq_minized_defconfig
+new file mode 100644
+index 0000000..5da9640
+--- /dev/null
++++ b/configs/zynq_minized_defconfig
+@@ -0,0 +1,56 @@
++CONFIG_ARM=y
++CONFIG_ARCH_ZYNQ=y
++CONFIG_SYS_TEXT_BASE=0x4000000
++CONFIG_DEFAULT_DEVICE_TREE="zynq-minized"
++CONFIG_SYS_NO_FLASH=y
++# CONFIG_DISPLAY_CPUINFO is not set
++CONFIG_SPL=y
++CONFIG_SPL_MTD_SUPPORT=y
++CONFIG_SPL_OS_BOOT=y
++CONFIG_HUSH_PARSER=y
++CONFIG_SYS_PROMPT="Zynq> "
++CONFIG_CMD_BOOTZ=y
++# CONFIG_CMD_IMLS is not set
++# CONFIG_CMD_FLASH is not set
++CONFIG_CMD_MMC=y
++CONFIG_CMD_SF=y
++CONFIG_CMD_SPI=y
++CONFIG_CMD_USB=y
++CONFIG_CMD_DFU=y
++CONFIG_CMD_GPIO=y
++# CONFIG_CMD_SETEXPR is not set
++# CONFIG_CMD_NET is not set
++# CONFIG_CMD_NFS is not set
++CONFIG_CMD_CACHE=y
++CONFIG_CMD_EXT2=y
++CONFIG_CMD_EXT4=y
++CONFIG_CMD_EXT4_WRITE=y
++CONFIG_CMD_FAT=y
++CONFIG_CMD_FS_GENERIC=y
++CONFIG_OF_EMBED=y
++CONFIG_SPL_DM_SEQ_ALIAS=y
++CONFIG_DFU_MMC=y
++CONFIG_DFU_RAM=y
++CONFIG_DM_GPIO=y
++CONFIG_DM_MMC=y
++CONFIG_ZYNQ_SDHCI=y
++CONFIG_MMC_SDHCI=y
++CONFIG_MTD=y
++CONFIG_CFI_FLASH=y
++CONFIG_SPI_FLASH=y
++CONFIG_SPI_FLASH_BAR=y
++CONFIG_SPI_FLASH_STMICRO=y
++CONFIG_ZYNQ_QSPI=y
++CONFIG_USB=y
++CONFIG_USB_EHCI_HCD=y
++CONFIG_USB_ULPI_VIEWPORT=y
++CONFIG_USB_ULPI=y
++CONFIG_USB_STORAGE=y
++CONFIG_USB_GADGET=y
++CONFIG_CI_UDC=y
++CONFIG_USB_GADGET_DOWNLOAD=y
++CONFIG_G_DNL_MANUFACTURER="Xilinx"
++CONFIG_G_DNL_VENDOR_NUM=0x03fd
++CONFIG_G_DNL_PRODUCT_NUM=0x0300
++CONFIG_REGEX=y
++CONFIG_LIB_RAND=y
+--
+2.7.4
+
diff --git a/meta-xilinx-contrib/recipes-bsp/u-boot/u-boot-xlnx_2017.3.bbappend b/meta-xilinx-contrib/recipes-bsp/u-boot/u-boot-xlnx_2017.3.bbappend
new file mode 100644
index 0000000..c5e3e95
--- /dev/null
+++ b/meta-xilinx-contrib/recipes-bsp/u-boot/u-boot-xlnx_2017.3.bbappend
@@ -0,0 +1,8 @@
+FILESEXTRAPATHS_prepend := "${THISDIR}/${PN}/v2017.3:"
+
+SRC_URI_append = "file://u-boot-minized.patch \
+		 "
+
+# u-boot-xlnx compatible with MiniZed
+HAS_PLATFORM_INIT += "zynq_minized_config"
+
diff --git a/meta-xilinx-contrib/recipes-connectivity/hostapd/hostapd/0001-murata-hostapd-conf.patch b/meta-xilinx-contrib/recipes-connectivity/hostapd/hostapd/0001-murata-hostapd-conf.patch
new file mode 100644
index 0000000..148fec2
--- /dev/null
+++ b/meta-xilinx-contrib/recipes-connectivity/hostapd/hostapd/0001-murata-hostapd-conf.patch
@@ -0,0 +1,11 @@
+diff -ur hostapd-2.6-original/hostapd/hostapd.conf hostapd-2.6-modified/hostapd/hostapd.conf
+--- hostapd-2.6-original/hostapd/hostapd.conf	2016-10-02 13:51:11.000000000 -0500
++++ hostapd-2.6-modified/hostapd/hostapd.conf	2017-12-05 15:02:29.842056615 -0600
+@@ -25,6 +25,7 @@
+ # Use driver=none if building hostapd as a standalone RADIUS server that does
+ # not control any wireless/wired driver.
+ # driver=hostap
++driver=nl80211
+
+ # Driver interface parameters (mainly for development testing use)
+ # driver_params=<params>
diff --git a/meta-xilinx-contrib/recipes-connectivity/hostapd/hostapd_%.bbappend b/meta-xilinx-contrib/recipes-connectivity/hostapd/hostapd_%.bbappend
new file mode 100644
index 0000000..c4964cf
--- /dev/null
+++ b/meta-xilinx-contrib/recipes-connectivity/hostapd/hostapd_%.bbappend
@@ -0,0 +1,5 @@
+FILESEXTRAPATHS_prepend := "${THISDIR}/${PN}:"
+
+SRC_URI_append_minized-zynq7 = " file://0001-murata-hostapd-conf.patch"
+
+
diff --git a/meta-xilinx-contrib/recipes-connectivity/wpa-supplicant/wpa-supplicant/0001-murata-wpa_supplication-Add-server-in-hs20.patch b/meta-xilinx-contrib/recipes-connectivity/wpa-supplicant/wpa-supplicant/0001-murata-wpa_supplication-Add-server-in-hs20.patch
new file mode 100644
index 0000000..8ff6e17
--- /dev/null
+++ b/meta-xilinx-contrib/recipes-connectivity/wpa-supplicant/wpa-supplicant/0001-murata-wpa_supplication-Add-server-in-hs20.patch
@@ -0,0 +1,4575 @@
+diff -urN awpa_supplicant-2.6-original/hs20/server/ca/clean.sh bwpa_supplicant-2.6-patched-final/hs20/server/ca/clean.sh
+--- awpa_supplicant-2.6-original/hs20/server/ca/clean.sh	1969-12-31 18:00:00.000000000 -0600
++++ bwpa_supplicant-2.6-patched-final/hs20/server/ca/clean.sh	2016-10-02 13:51:11.000000000 -0500
+@@ -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 -urN awpa_supplicant-2.6-original/hs20/server/ca/est-csrattrs.cnf bwpa_supplicant-2.6-patched-final/hs20/server/ca/est-csrattrs.cnf
+--- awpa_supplicant-2.6-original/hs20/server/ca/est-csrattrs.cnf	1969-12-31 18:00:00.000000000 -0600
++++ bwpa_supplicant-2.6-patched-final/hs20/server/ca/est-csrattrs.cnf	2016-10-02 13:51:11.000000000 -0500
+@@ -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 -urN awpa_supplicant-2.6-original/hs20/server/ca/est-csrattrs.sh bwpa_supplicant-2.6-patched-final/hs20/server/ca/est-csrattrs.sh
+--- awpa_supplicant-2.6-original/hs20/server/ca/est-csrattrs.sh	1969-12-31 18:00:00.000000000 -0600
++++ bwpa_supplicant-2.6-patched-final/hs20/server/ca/est-csrattrs.sh	2016-10-02 13:51:11.000000000 -0500
+@@ -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 -urN awpa_supplicant-2.6-original/hs20/server/ca/hs20.oid bwpa_supplicant-2.6-patched-final/hs20/server/ca/hs20.oid
+--- awpa_supplicant-2.6-original/hs20/server/ca/hs20.oid	1969-12-31 18:00:00.000000000 -0600
++++ bwpa_supplicant-2.6-patched-final/hs20/server/ca/hs20.oid	2016-10-02 13:51:11.000000000 -0500
+@@ -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 -urN awpa_supplicant-2.6-original/hs20/server/ca/ocsp-req.sh bwpa_supplicant-2.6-patched-final/hs20/server/ca/ocsp-req.sh
+--- awpa_supplicant-2.6-original/hs20/server/ca/ocsp-req.sh	1969-12-31 18:00:00.000000000 -0600
++++ bwpa_supplicant-2.6-patched-final/hs20/server/ca/ocsp-req.sh	2016-10-02 13:51:11.000000000 -0500
+@@ -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 -urN awpa_supplicant-2.6-original/hs20/server/ca/ocsp-responder-ica.sh bwpa_supplicant-2.6-patched-final/hs20/server/ca/ocsp-responder-ica.sh
+--- awpa_supplicant-2.6-original/hs20/server/ca/ocsp-responder-ica.sh	1969-12-31 18:00:00.000000000 -0600
++++ bwpa_supplicant-2.6-patched-final/hs20/server/ca/ocsp-responder-ica.sh	2016-10-02 13:51:11.000000000 -0500
+@@ -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 -urN awpa_supplicant-2.6-original/hs20/server/ca/ocsp-responder.sh bwpa_supplicant-2.6-patched-final/hs20/server/ca/ocsp-responder.sh
+--- awpa_supplicant-2.6-original/hs20/server/ca/ocsp-responder.sh	1969-12-31 18:00:00.000000000 -0600
++++ bwpa_supplicant-2.6-patched-final/hs20/server/ca/ocsp-responder.sh	2016-10-02 13:51:11.000000000 -0500
+@@ -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
+diff -urN awpa_supplicant-2.6-original/hs20/server/ca/ocsp-update-cache.sh bwpa_supplicant-2.6-patched-final/hs20/server/ca/ocsp-update-cache.sh
+--- awpa_supplicant-2.6-original/hs20/server/ca/ocsp-update-cache.sh	1969-12-31 18:00:00.000000000 -0600
++++ bwpa_supplicant-2.6-patched-final/hs20/server/ca/ocsp-update-cache.sh	2016-10-02 13:51:11.000000000 -0500
+@@ -0,0 +1,10 @@
++#!/bin/sh
++
++openssl ocsp \
++	-no_nonce \
++	-CAfile ca.pem \
++	-verify_other demoCA/cacert.pem \
++	-issuer demoCA/cacert.pem \
++	-cert server.pem \
++	-url http://localhost:8888/ \
++	-respout ocsp-server-cache.der
+diff -urN awpa_supplicant-2.6-original/hs20/server/ca/openssl.cnf bwpa_supplicant-2.6-patched-final/hs20/server/ca/openssl.cnf
+--- awpa_supplicant-2.6-original/hs20/server/ca/openssl.cnf	1969-12-31 18:00:00.000000000 -0600
++++ bwpa_supplicant-2.6-patched-final/hs20/server/ca/openssl.cnf	2016-10-02 13:51:11.000000000 -0500
+@@ -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:. at 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 -urN awpa_supplicant-2.6-original/hs20/server/ca/openssl-root.cnf bwpa_supplicant-2.6-patched-final/hs20/server/ca/openssl-root.cnf
+--- awpa_supplicant-2.6-original/hs20/server/ca/openssl-root.cnf	1969-12-31 18:00:00.000000000 -0600
++++ bwpa_supplicant-2.6-patched-final/hs20/server/ca/openssl-root.cnf	2016-10-02 13:51:11.000000000 -0500
+@@ -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 -urN awpa_supplicant-2.6-original/hs20/server/ca/setup.sh bwpa_supplicant-2.6-patched-final/hs20/server/ca/setup.sh
+--- awpa_supplicant-2.6-original/hs20/server/ca/setup.sh	1969-12-31 18:00:00.000000000 -0600
++++ bwpa_supplicant-2.6-patched-final/hs20/server/ca/setup.sh	2016-10-02 13:51:11.000000000 -0500
+@@ -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, at OCSP_URI@,$OCSP_URI," |
++sed "s, at LOGO_URI@,$LOGO_URI," |
++sed "s, at LOGO_HASH1@,$LOGO_HASH1," |
++sed "s, at LOGO_HASH256@,$LOGO_HASH256," |
++sed "s/@DOMAIN@/$DOMAIN/" \
++ > my-openssl.cnf
++
++
++cat my-openssl-root.cnf | sed "s/#@CN@/commonName_default = $CNR/" > openssl.cnf.tmp
++mkdir -p rootCA/certs rootCA/crl rootCA/newcerts rootCA/private
++touch rootCA/index.txt
++if [ -e rootCA/private/cakey.pem ]; then
++    echo " * Use existing Root CA"
++else
++    echo " * Generate Root CA private key"
++    $OPENSSL req -config openssl.cnf.tmp -batch -new -newkey rsa:4096 -keyout rootCA/private/cakey.pem -out rootCA/careq.pem || fail "Failed to generate Root CA private key"
++    echo " * Sign Root CA certificate"
++    $OPENSSL ca -config openssl.cnf.tmp -md sha256 -create_serial -out rootCA/cacert.pem -days 10957 -batch -keyfile rootCA/private/cakey.pem -passin pass:$PASS -selfsign -extensions v3_ca -outdir rootCA/newcerts -infiles rootCA/careq.pem || fail "Failed to sign Root CA certificate"
++    $OPENSSL x509 -in rootCA/cacert.pem -out rootCA/cacert.der -outform DER || fail "Failed to create rootCA DER"
++    sha256sum rootCA/cacert.der > rootCA/cacert.fingerprint || fail "Failed to create rootCA fingerprint"
++fi
++if [ ! -e rootCA/crlnumber ]; then
++    echo 00 > rootCA/crlnumber
++fi
++
++echo
++echo "---[ Intermediate CA ]--------------------------------------------------"
++echo
++
++cat my-openssl.cnf | sed "s/#@CN@/commonName_default = $COMPANY Hotspot 2.0 Intermediate CA/" > openssl.cnf.tmp
++mkdir -p demoCA/certs demoCA/crl demoCA/newcerts demoCA/private
++touch demoCA/index.txt
++if [ -e demoCA/private/cakey.pem ]; then
++    echo " * Use existing Intermediate CA"
++else
++    echo " * Generate Intermediate CA private key"
++    $OPENSSL req -config openssl.cnf.tmp -batch -new -newkey rsa:2048 -keyout demoCA/private/cakey.pem -out demoCA/careq.pem || fail "Failed to generate Intermediate CA private key"
++    echo " * Sign Intermediate CA certificate"
++    $OPENSSL ca -config openssl.cnf.tmp -md sha256 -create_serial -out demoCA/cacert.pem -days 3652 -batch -keyfile rootCA/private/cakey.pem -cert rootCA/cacert.pem -passin pass:$PASS -extensions v3_ca -infiles demoCA/careq.pem || fail "Failed to sign Intermediate CA certificate"
++    # horrible from security view point, but for testing purposes since OCSP responder does not seem to support -passin
++    openssl rsa -in demoCA/private/cakey.pem -out demoCA/private/cakey-plain.pem -passin pass:$PASS
++    $OPENSSL x509 -in demoCA/cacert.pem -out demoCA/cacert.der -outform DER || fail "Failed to create demoCA DER."
++    sha256sum demoCA/cacert.der > demoCA/cacert.fingerprint || fail "Failed to create demoCA fingerprint"
++fi
++if [ ! -e demoCA/crlnumber ]; then
++    echo 00 > demoCA/crlnumber
++fi
++
++echo
++echo "OCSP responder"
++echo
++
++cat my-openssl.cnf | sed "s/#@CN@/commonName_default = $CNO/" > openssl.cnf.tmp
++$OPENSSL req -config $PWD/openssl.cnf.tmp -batch -new -newkey rsa:2048 -nodes -out ocsp.csr -keyout ocsp.key -extensions v3_OCSP
++$OPENSSL ca -config $PWD/openssl.cnf.tmp -batch -md sha256 -keyfile demoCA/private/cakey.pem -passin pass:$PASS -in ocsp.csr -out ocsp.pem -days 730 -extensions v3_OCSP || fail "Could not generate ocsp.pem"
++
++echo
++echo "---[ Server - to be revoked ] ------------------------------------------"
++echo
++
++cat my-openssl.cnf | sed "s/#@CN@/commonName_default = $CNV/" > openssl.cnf.tmp
++$OPENSSL req -config $PWD/openssl.cnf.tmp -batch -new -newkey rsa:2048 -nodes -out server-revoked.csr -keyout server-revoked.key
++$OPENSSL ca -config $PWD/openssl.cnf.tmp -batch -md sha256 -in server-revoked.csr -out server-revoked.pem -key $PASS -days 730 -extensions ext_server
++$OPENSSL ca -revoke server-revoked.pem -key $PASS
++
++echo
++echo "---[ Server - with client ext key use ] ---------------------------------"
++echo "---[ Only used for negative-testing for OSU-client implementation ] -----"
++echo
++
++cat my-openssl.cnf | sed "s/#@CN@/commonName_default = $CNOC/" > openssl.cnf.tmp
++$OPENSSL req -config $PWD/openssl.cnf.tmp -batch -new -newkey rsa:2048 -nodes -out server-client.csr -keyout server-client.key || fail "Could not create server-client.key"
++$OPENSSL ca -config $PWD/openssl.cnf.tmp -batch -md sha256 -in server-client.csr -out server-client.pem -key $PASS -days 730 -extensions ext_client || fail "Could not create server-client.pem"
++
++echo
++echo "---[ User ]-------------------------------------------------------------"
++echo
++
++cat my-openssl.cnf | sed "s/#@CN@/commonName_default = User/" > openssl.cnf.tmp
++$OPENSSL req -config $PWD/openssl.cnf.tmp -batch -new -newkey rsa:2048 -nodes -out user.csr -keyout user.key || fail "Could not create user.key"
++$OPENSSL ca -config $PWD/openssl.cnf.tmp -batch -md sha256 -in user.csr -out user.pem -key $PASS -days 730 -extensions ext_client || fail "Could not create user.pem"
++
++echo
++echo "---[ Server ]-----------------------------------------------------------"
++echo
++
++ALT="DNS:$OSU_SERVER_HOSTNAME"
++ALT="$ALT,otherName:1.3.6.1.4.1.40808.1.1.1;UTF8String:$OPER_ENG"
++ALT="$ALT,otherName:1.3.6.1.4.1.40808.1.1.1;UTF8String:$OPER_FI"
++
++cat my-openssl.cnf |
++	sed "s/#@CN@/commonName_default = $OSU_SERVER_HOSTNAME/" |
++	sed "s/^##organizationalUnitName/organizationalUnitName/" |
++	sed "s/#@OU@/organizationalUnitName_default = Hotspot 2.0 Online Sign Up Server/" |
++	sed "s/#@ALTNAME@/subjectAltName=critical,$ALT/" \
++	> openssl.cnf.tmp
++echo $OPENSSL req -config $PWD/openssl.cnf.tmp -batch -sha256 -new -newkey rsa:2048 -nodes -out server.csr -keyout server.key -reqexts v3_osu_server
++$OPENSSL req -config $PWD/openssl.cnf.tmp -batch -sha256 -new -newkey rsa:2048 -nodes -out server.csr -keyout server.key -reqexts v3_osu_server || fail "Failed to generate server request"
++$OPENSSL ca -config $PWD/openssl.cnf.tmp -batch -md sha256 -in server.csr -out server.pem -key $PASS -days 730 -extensions ext_server -policy policy_osu_server || fail "Failed to sign server certificate"
++
++#dump logotype details for debugging
++$OPENSSL x509 -in server.pem -out server.der -outform DER
++openssl asn1parse -in server.der -inform DER | grep HEX | tail -1 | sed 's/.*://' | xxd -r -p > logo.der
++openssl asn1parse -in logo.der -inform DER > logo.asn1
++
++
++echo
++echo "---[ CRL ]---------------------------------------------------------------"
++echo
++
++$OPENSSL ca -config $PWD/my-openssl.cnf -gencrl -md sha256 -out demoCA/crl/crl.pem -passin pass:$PASS
++
++echo
++echo "---[ Verify ]------------------------------------------------------------"
++echo
++
++$OPENSSL verify -CAfile rootCA/cacert.pem demoCA/cacert.pem
++$OPENSSL verify -CAfile rootCA/cacert.pem -untrusted demoCA/cacert.pem *.pem
++
++cat rootCA/cacert.pem demoCA/cacert.pem > ca.pem
+Binary files awpa_supplicant-2.6-original/hs20/server/ca/w1fi_logo.png and bwpa_supplicant-2.6-patched-final/hs20/server/ca/w1fi_logo.png differ
+diff -urN awpa_supplicant-2.6-original/hs20/server/hs20-osu-server.txt bwpa_supplicant-2.6-patched-final/hs20/server/hs20-osu-server.txt
+--- awpa_supplicant-2.6-original/hs20/server/hs20-osu-server.txt	1969-12-31 18:00:00.000000000 -0600
++++ bwpa_supplicant-2.6-patched-final/hs20/server/hs20-osu-server.txt	2016-10-02 13:51:11.000000000 -0500
+@@ -0,0 +1,257 @@
++Hotspot 2.0 OSU server
++======================
++
++The information in this document is based on the assumption that Ubuntu
++12.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.
++
++NOTE: While this describes use on Ubuntu 12.04, the version of Apache2
++included in that distribution is not new enough to support all OSU
++server validation steps. In other words, it may be most adapt the steps
++described here to Ubuntu 13.10.
++
++
++Build dependencies
++------------------
++
++Ubuntu 12.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 php5-sqlite libapache2-mod-php5
++
++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
++
++# 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" -d $old_hostname \
++   -I "Hotspot 2.0 Intermediate 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 at 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
++                Order allow,deny
++                Allow from all
++        </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.
++
++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 -urN awpa_supplicant-2.6-original/hs20/server/hs20_spp_server.c bwpa_supplicant-2.6-patched-final/hs20/server/hs20_spp_server.c
+--- awpa_supplicant-2.6-original/hs20/server/hs20_spp_server.c	1969-12-31 18:00:00.000000000 -0600
++++ bwpa_supplicant-2.6-patched-final/hs20/server/hs20_spp_server.c	2016-10-02 13:51:11.000000000 -0500
+@@ -0,0 +1,187 @@
++/*
++ * 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 "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);
++
++	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;
++	}
++
++	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:");
++		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;
++		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 -urN awpa_supplicant-2.6-original/hs20/server/Makefile bwpa_supplicant-2.6-patched-final/hs20/server/Makefile
+--- awpa_supplicant-2.6-original/hs20/server/Makefile	1969-12-31 18:00:00.000000000 -0600
++++ bwpa_supplicant-2.6-patched-final/hs20/server/Makefile	2016-10-02 13:51:11.000000000 -0500
+@@ -0,0 +1,46 @@
++all: hs20_spp_server
++
++ifndef CC
++CC=gcc
++endif
++
++ifndef LDO
++LDO=$(CC)
++endif
++
++ifndef CFLAGS
++CFLAGS = -MMD -O2 -Wall -g
++endif
++
++CFLAGS += -I../../src
++CFLAGS += -I../../src/utils
++CFLAGS += -I../../src/crypto
++
++LIBS += -lsqlite3
++
++# Using glibc < 2.17 requires -lrt for clock_gettime()
++LIBS += -lrt
++
++OBJS=spp_server.o
++OBJS += hs20_spp_server.o
++OBJS += ../../src/utils/xml-utils.o
++OBJS += ../../src/utils/base64.o
++OBJS += ../../src/utils/common.o
++OBJS += ../../src/utils/os_unix.o
++OBJS += ../../src/utils/wpa_debug.o
++OBJS += ../../src/crypto/md5-internal.o
++CFLAGS += $(shell xml2-config --cflags)
++LIBS += $(shell xml2-config --libs)
++OBJS += ../../src/utils/xml_libxml2.o
++
++hs20_spp_server: $(OBJS)
++	$(LDO) $(LDFLAGS) -o hs20_spp_server $(OBJS) $(LIBS)
++
++clean:
++	rm -f core *~ *.o *.d hs20_spp_server
++	rm -f ../../src/utils/*.o
++	rm -f ../../src/utils/*.d
++	rm -f ../../src/crypto/*.o
++	rm -f ../../src/crypto/*.d
++
++-include $(OBJS:%.o=%.d)
+diff -urN awpa_supplicant-2.6-original/hs20/server/spp_server.c bwpa_supplicant-2.6-patched-final/hs20/server/spp_server.c
+--- awpa_supplicant-2.6-original/hs20/server/spp_server.c	1969-12-31 18:00:00.000000000 -0600
++++ bwpa_supplicant-2.6-patched-final/hs20/server/spp_server.c	2016-10-02 13:51:11.000000000 -0500
+@@ -0,0 +1,2292 @@
++/*
++ * 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,
++};
++
++
++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 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)
++{
++	char *sql;
++	int ret = 0;
++
++	sql = sqlite3_mprintf("INSERT INTO sessions(timestamp,id,user,realm,"
++			      "operation,password,redirect_uri) "
++			      "VALUES "
++			      "(strftime('%%Y-%%m-%%d %%H:%%M:%%f','now'),"
++			      "%Q,%Q,%Q,%d,%Q,%Q)",
++			      sessionid, user ? user : "", realm ? realm : "",
++			      operation, pw ? pw : "",
++			      redirect_uri ? redirect_uri : "");
++	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_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",
++			      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 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",
++			      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",
++			      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 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;
++
++	node = xml_node_create(ctx->xml, parent, NULL, "UsernamePassword");
++	if (node == NULL)
++		return NULL;
++
++	add_text_node(ctx, node, "Username", user);
++
++	b64 = (char *) base64_encode((unsigned char *) pw, strlen(pw), NULL);
++	if (b64 == NULL)
++		return NULL;
++	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)
++{
++	xml_node_t *node;
++
++	node = build_username_password(ctx, cred, user, pw);
++	if (node == NULL)
++		return -1;
++
++	add_text_node(ctx, node, "MachineManaged", "TRUE");
++	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)
++{
++	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) < 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);
++}
++
++
++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 * 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 *real_user = NULL;
++	char *status;
++	char *cert;
++
++	if (dmacc) {
++		real_user = db_get_val(ctx, user, realm, "identity", dmacc);
++		if (real_user == NULL) {
++			debug_print(ctx, 1, "Could not find user identity for "
++				    "dmacc user '%s'", user);
++			return NULL;
++		}
++	}
++
++	cert = db_get_val(ctx, user, realm, "cert", dmacc);
++	if (cert && cert[0] == '\0')
++		cert = NULL;
++	if (cert) {
++		cred = build_credential_cert(ctx, real_user ? real_user : user,
++					     realm, cert);
++	} 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");
++		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");
++		return NULL;
++	}
++
++	snprintf(buf, sizeof(buf),
++		 "./Wi-Fi/%s/PerProviderSubscription/Credential1/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);
++		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, "Certificate credential - no need for DB "
++			    "password update on success notification");
++	} 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);
++	}
++
++	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 * 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);
++
++	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/Credential1/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);
++
++	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);
++
++	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
++		ret = machine_remediation(ctx, user, realm, session_id, dmacc);
++	free(type);
++
++	return ret;
++}
++
++
++static xml_node_t * build_policy(struct hs20_svc *ctx, const char *user,
++				 const char *realm, int use_dmacc)
++{
++	char *policy_id;
++	char fname[200];
++	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;
++	}
++
++	snprintf(fname, sizeof(fname), "%s/spp/policy/%s.xml",
++		 ctx->root_dir, policy_id);
++	free(policy_id);
++	debug_print(ctx, 1, "Use policy file %s", fname);
++
++	policy = node_from_file(ctx->xml, fname);
++	if (policy == NULL)
++		return NULL;
++
++	node = get_node_uri(ctx->xml, policy, "Policy/PolicyUpdate/URI");
++	if (node) {
++		char *url;
++		url = db_get_osu_config_val(ctx, realm, "policy_url");
++		if (url == NULL) {
++			xml_node_free(ctx->xml, policy);
++			return NULL;
++		}
++		xml_node_set_text(ctx->xml, node, url);
++		free(url);
++	}
++
++	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);
++
++	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/Credential1/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)
++{
++	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) < 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)
++{
++	xml_node_t *pps, *c, *trust, *aaa, *aaa1, *upd, *homesp;
++	xml_node_t *cred, *eap, *userpw;
++
++	pps = xml_node_create_root(ctx->xml, NULL, NULL, NULL,
++				   "PerProviderSubscription");
++	if (pps == NULL)
++		return NULL;
++
++	add_text_node(ctx, pps, "UpdateIdentifier", "1");
++
++	c = xml_node_create(ctx->xml, pps, NULL, "Credential1");
++
++	add_text_node(ctx, c, "CredentialPriority", "1");
++
++	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");
++	add_text_node_conf(ctx, realm, aaa1, "CertSHA256Fingerprint",
++			   "aaa_trust_root_cert_fingerprint");
++
++	upd = xml_node_create(ctx->xml, c, NULL, "SubscriptionUpdate");
++	add_text_node(ctx, upd, "UpdateInterval", "4294967295");
++	add_text_node(ctx, upd, "UpdateMethod", "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");
++	add_text_node_conf(ctx, realm, trust, "CertSHA256Fingerprint",
++			   "trust_root_cert_fingerprint");
++
++	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 (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)
++{
++	xml_namespace_t *ns;
++	xml_node_t *spp_node, *enroll, *exec_node;
++	char *val;
++	char password[11];
++	char *b64;
++
++	if (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);
++	xml_node_create_text(ctx->xml, enroll, ns, "estUserID", user);
++
++	b64 = (char *) base64_encode((unsigned char *) 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;
++	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);
++		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");
++	pps = build_pps(ctx, user, realm, pw,
++			fingerprint ? fingerprint : NULL, machine_managed);
++	free(fingerprint);
++	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);
++	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/Credential1/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_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, 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)
++		return hs20_user_input_registration(ctx, session_id, 1);
++
++	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_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;
++	char *version;
++	int valid;
++	char *supp, *pos;
++	char *err;
++
++	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);
++	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) < 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);
++		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) {
++		if (devinfo)
++			db_add_session_devinfo(ctx, session_id, devinfo);
++		if (devdetail)
++			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;
++	}
++
++	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 *sql;
++	int ret = -1;
++	char *free_account;
++	int free_acc;
++	char *type;
++	int cert = 0;
++	char *cert_pem, *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");
++	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);
++
++	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);
++
++	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;
++	}
++
++	sql = sqlite3_mprintf("INSERT INTO users(identity,realm,phase2,"
++			      "methods,cert,cert_pem,machine_managed) VALUES "
++			      "(%Q,%Q,1,%Q,%Q,%Q,%d)",
++			      user, realm, cert ? "TLS" : "TTLS-MSCHAPV2",
++			      fingerprint ? fingerprint : "",
++			      cert_pem ? cert_pem : "",
++			      pw_mm && atoi(pw_mm) ? 1 : 0);
++	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",
++				      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 (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);
++	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, user, realm, session_id, "operation");
++	if (!val) {
++		debug_print(ctx, 1,
++			    "No session active for user: %s  sessionID: %s",
++			    user, 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 == 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);
++		}
++		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, user, realm, 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 -urN awpa_supplicant-2.6-original/hs20/server/spp_server.h bwpa_supplicant-2.6-patched-final/hs20/server/spp_server.h
+--- awpa_supplicant-2.6-original/hs20/server/spp_server.h	1969-12-31 18:00:00.000000000 -0600
++++ bwpa_supplicant-2.6-patched-final/hs20/server/spp_server.h	2016-10-02 13:51:11.000000000 -0500
+@@ -0,0 +1,32 @@
++/*
++ * 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;
++};
++
++
++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 -urN awpa_supplicant-2.6-original/hs20/server/sql-example.txt bwpa_supplicant-2.6-patched-final/hs20/server/sql-example.txt
+--- awpa_supplicant-2.6-original/hs20/server/sql-example.txt	1969-12-31 18:00:00.000000000 -0600
++++ bwpa_supplicant-2.6-patched-final/hs20/server/sql-example.txt	2016-10-02 13:51:11.000000000 -0500
+@@ -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 -urN awpa_supplicant-2.6-original/hs20/server/sql.txt bwpa_supplicant-2.6-patched-final/hs20/server/sql.txt
+--- awpa_supplicant-2.6-original/hs20/server/sql.txt	1969-12-31 18:00:00.000000000 -0600
++++ bwpa_supplicant-2.6-patched-final/hs20/server/sql.txt	2016-10-02 13:51:11.000000000 -0500
+@@ -0,0 +1,59 @@
++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
++);
++
++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
++);
++
++CREATE TABLE wildcards(
++	identity TEXT PRIMARY KEY,
++	methods TEXT
++);
+diff -urN awpa_supplicant-2.6-original/hs20/server/www/add-free.php bwpa_supplicant-2.6-patched-final/hs20/server/www/add-free.php
+--- awpa_supplicant-2.6-original/hs20/server/www/add-free.php	1969-12-31 18:00:00.000000000 -0600
++++ bwpa_supplicant-2.6-patched-final/hs20/server/www/add-free.php	2016-10-02 13:51:11.000000000 -0500
+@@ -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 -urN awpa_supplicant-2.6-original/hs20/server/www/add-mo.php bwpa_supplicant-2.6-patched-final/hs20/server/www/add-mo.php
+--- awpa_supplicant-2.6-original/hs20/server/www/add-mo.php	1969-12-31 18:00:00.000000000 -0600
++++ bwpa_supplicant-2.6-patched-final/hs20/server/www/add-mo.php	2016-10-02 13:51:11.000000000 -0500
+@@ -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 -urN awpa_supplicant-2.6-original/hs20/server/www/cert-enroll.php bwpa_supplicant-2.6-patched-final/hs20/server/www/cert-enroll.php
+--- awpa_supplicant-2.6-original/hs20/server/www/cert-enroll.php	1969-12-31 18:00:00.000000000 -0600
++++ bwpa_supplicant-2.6-patched-final/hs20/server/www/cert-enroll.php	2016-10-02 13:51:11.000000000 -0500
+@@ -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 -urN awpa_supplicant-2.6-original/hs20/server/www/config.php bwpa_supplicant-2.6-patched-final/hs20/server/www/config.php
+--- awpa_supplicant-2.6-original/hs20/server/www/config.php	1969-12-31 18:00:00.000000000 -0600
++++ bwpa_supplicant-2.6-patched-final/hs20/server/www/config.php	2016-10-02 13:51:11.000000000 -0500
+@@ -0,0 +1,4 @@
++<?php
++$osu_root = "/home/user/hs20-server";
++$osu_db = "sqlite:$osu_root/AS/DB/eap_user.db";
++?>
+diff -urN awpa_supplicant-2.6-original/hs20/server/www/est.php bwpa_supplicant-2.6-patched-final/hs20/server/www/est.php
+--- awpa_supplicant-2.6-original/hs20/server/www/est.php	1969-12-31 18:00:00.000000000 -0600
++++ bwpa_supplicant-2.6-patched-final/hs20/server/www/est.php	2016-10-02 13:51:11.000000000 -0500
+@@ -0,0 +1,198 @@
++<?php
++
++require('config.php');
++
++$params = split("/", $_SERVER["PATH_INFO"], 3);
++$realm = $params[1];
++$cmd = $params[2];
++$method = $_SERVER["REQUEST_METHOD"];
++
++unset($user);
++unset($rowid);
++
++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');
++  }
++
++  $db = new PDO($osu_db);
++  if (!$db) {
++    error_log("EST: Could not access database");
++    die("Could not access database");
++  }
++
++  $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');
++  }
++}
++
++
++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") {
++  if (!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 (!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);
++
++  $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 -urN awpa_supplicant-2.6-original/hs20/server/www/free.php bwpa_supplicant-2.6-patched-final/hs20/server/www/free.php
+--- awpa_supplicant-2.6-original/hs20/server/www/free.php	1969-12-31 18:00:00.000000000 -0600
++++ bwpa_supplicant-2.6-patched-final/hs20/server/www/free.php	2016-10-02 13:51:11.000000000 -0500
+@@ -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 -urN awpa_supplicant-2.6-original/hs20/server/www/free-remediation.php bwpa_supplicant-2.6-patched-final/hs20/server/www/free-remediation.php
+--- awpa_supplicant-2.6-original/hs20/server/www/free-remediation.php	1969-12-31 18:00:00.000000000 -0600
++++ bwpa_supplicant-2.6-patched-final/hs20/server/www/free-remediation.php	2016-10-02 13:51:11.000000000 -0500
+@@ -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 -urN awpa_supplicant-2.6-original/hs20/server/www/redirect.php bwpa_supplicant-2.6-patched-final/hs20/server/www/redirect.php
+--- awpa_supplicant-2.6-original/hs20/server/www/redirect.php	1969-12-31 18:00:00.000000000 -0600
++++ bwpa_supplicant-2.6-patched-final/hs20/server/www/redirect.php	2016-10-02 13:51:11.000000000 -0500
+@@ -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 -urN awpa_supplicant-2.6-original/hs20/server/www/remediation.php bwpa_supplicant-2.6-patched-final/hs20/server/www/remediation.php
+--- awpa_supplicant-2.6-original/hs20/server/www/remediation.php	1969-12-31 18:00:00.000000000 -0600
++++ bwpa_supplicant-2.6-patched-final/hs20/server/www/remediation.php	2016-10-02 13:51:11.000000000 -0500
+@@ -0,0 +1,18 @@
++<html>
++<head>
++<title>Hotspot 2.0 subscription remediation</title>
++</head>
++<body>
++
++<?php
++
++echo "SessionID: " . $_GET["session_id"] . "<br>\n";
++
++echo "<a href=\"redirect.php?id=" . $_GET["session_id"] . "\">Complete user subscription remediation</a><br>\n";
++
++?>
++
++This will provide a new machine-generated password.
++
++</body>
++</html>
+diff -urN awpa_supplicant-2.6-original/hs20/server/www/signup.php bwpa_supplicant-2.6-patched-final/hs20/server/www/signup.php
+--- awpa_supplicant-2.6-original/hs20/server/www/signup.php	1969-12-31 18:00:00.000000000 -0600
++++ bwpa_supplicant-2.6-patched-final/hs20/server/www/signup.php	2016-10-02 13:51:11.000000000 -0500
+@@ -0,0 +1,46 @@
++<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 FROM sessions WHERE id='$id'")->fetch();
++if ($row == false) {
++   die("Session not found for id: $id");
++}
++$realm = $row['realm'];
++
++echo "<h3>Sign up for a subscription - $realm</h3>\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 "<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 "<p><a href=\"cert-enroll.php?id=$id\">Enroll a client certificate</a></p>\n"
++?>
++
++</body>
++</html>
+diff -urN awpa_supplicant-2.6-original/hs20/server/www/spp.php bwpa_supplicant-2.6-patched-final/hs20/server/www/spp.php
+--- awpa_supplicant-2.6-original/hs20/server/www/spp.php	1969-12-31 18:00:00.000000000 -0600
++++ bwpa_supplicant-2.6-patched-final/hs20/server/www/spp.php	2016-10-02 13:51:11.000000000 -0500
+@@ -0,0 +1,128 @@
++<?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");
++}
++
++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($_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");
++
++$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 -urN awpa_supplicant-2.6-original/hs20/server/www/users.php bwpa_supplicant-2.6-patched-final/hs20/server/www/users.php
+--- awpa_supplicant-2.6-original/hs20/server/www/users.php	1969-12-31 18:00:00.000000000 -0600
++++ bwpa_supplicant-2.6-patched-final/hs20/server/www/users.php	2016-10-02 13:51:11.000000000 -0500
+@@ -0,0 +1,349 @@
++<?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-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");
++}
++
++$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>]";
++	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  {
++	echo "Machine [<a href=\"users.php?cmd=subrem-clear&id=" .
++			  $row['rowid'] . "\">clear</a>]";
++}
++echo "<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";
++
++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>\n";
++echo "<tr><th>User<th>Realm<th>Remediation<th>Policy<th>Account type<th>Phase 2 method(s)<th>DevId\n";
++
++$res = $db->query('SELECT rowid,* FROM users WHERE phase2=1');
++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 "Not required";
++	} else if ($rem == "user") {
++		echo "User";
++	} else if ($rem == "policy") {
++		echo "Policy";
++	} else if ($rem == "free") {
++		echo "Free";
++	} else  {
++		echo "Machine";
++	}
++	echo "<td>" . $row['policy'];
++	if ($row['shared'] > 0)
++	  echo "<td>shared";
++	else
++	  echo "<td>default";
++	echo "<td>" . $row['methods'];
++	echo "<td>";
++	$xml = xml_parser_create();
++	xml_parse_into_struct($xml, $row['devinfo'], $devinfo);
++	foreach($devinfo as $k) {
++	  if ($k['tag'] == 'DEVID') {
++	    echo $k['value'];
++	    break;
++	  }
++	}
++	echo "\n";
++}
++echo "</table>\n";
++
++}
++
++?>
++
++</html>
diff --git a/meta-xilinx-contrib/recipes-connectivity/wpa-supplicant/wpa-supplicant_%.bbappend b/meta-xilinx-contrib/recipes-connectivity/wpa-supplicant/wpa-supplicant_%.bbappend
new file mode 100644
index 0000000..4225667
--- /dev/null
+++ b/meta-xilinx-contrib/recipes-connectivity/wpa-supplicant/wpa-supplicant_%.bbappend
@@ -0,0 +1,4 @@
+FILESEXTRAPATHS_prepend := "${THISDIR}/${PN}:"
+
+SRC_URI_append_minized-zynq7 = " file://0001-murata-wpa_supplication-Add-server-in-hs20.patch"
+
diff --git a/meta-xilinx-contrib/recipes-kernel/cypress-orga-backport/cypress-orga-backport_4.12.bb b/meta-xilinx-contrib/recipes-kernel/cypress-orga-backport/cypress-orga-backport_4.12.bb
new file mode 100644
index 0000000..a9b1ad4
--- /dev/null
+++ b/meta-xilinx-contrib/recipes-kernel/cypress-orga-backport/cypress-orga-backport_4.12.bb
@@ -0,0 +1,73 @@
+# Copyright (C) 2017 Khem Raj <raj.khem at gmail.com>
+# Released under the MIT license (see COPYING.MIT for the terms)
+
+
+DESCRIPTION = "Cypress Orga Wi-Fi driver backport recipe"
+HOMEPAGE = "https://github.com/murata-wireless"
+SECTION = "kernel/modules"
+LICENSE = "GPLv2"
+LIC_FILES_CHKSUM = "file://COPYING;md5=d7810fab7487fb0aad327b76f1be7cd7"
+
+SRC_URI =  "git://github.com/murata-wireless/cyw-fmac-v4.12-orga;protocol=http;branch=imx-morty-orga"
+SRCREV = "dc0592f5b81d427d91ec706c92e8337710a894a8"
+
+S = "${WORKDIR}/git"
+
+EXTRA_OEMAKE = "KLIB_BUILD=${STAGING_KERNEL_DIR} KLIB=${D} DESTDIR=${D}"
+
+DEPENDS += "virtual/kernel"
+inherit module-base
+addtask make_scripts after do_patch before do_configure
+do_make_scripts[lockfiles] = "${TMPDIR}/kernel-scripts.lock"
+do_make_scripts[deptask] = "do_populate_sysroot"
+
+do_configure_prepend() {
+	cp ${STAGING_KERNEL_BUILDDIR}/.config ${STAGING_KERNEL_DIR}/.config
+	CC=${BUILD_CC} oe_runmake defconfig-brcmfmac
+}
+
+do_configure_append() {
+	unset LDFLAGS
+	oe_runmake
+}
+
+
+
+
+do_compile() {
+	echo "KERNEL VERSION:  ${KERNEL_VERSION}"
+	unset CFLAGS CPPFLAGS CXXFLAGS LDFLAGS
+	oe_runmake KERNEL_PATH=${STAGING_KERNEL_DIR}   \
+		   KERNEL_SRC=${STAGING_KERNEL_DIR}    \
+		   KERNEL_VERSION=${KERNEL_VERSION}    \
+		   CC="${KERNEL_CC}" LD="${KERNEL_LD}" \
+		   AR="${KERNEL_AR}" \
+		   ${MAKE_TARGETS}
+}
+
+do_install() {
+
+	install -d ${D}/lib/modules/${KERNEL_VERSION}/updates/compat
+	install -d ${D}/lib/modules/${KERNEL_VERSION}/updates/net/wireless
+	install -d ${D}/lib/modules/${KERNEL_VERSION}/updates/drivers/net/wireless/broadcom/brcm80211/brcmutil
+	install -d ${D}/lib/modules/${KERNEL_VERSION}/updates/drivers/net/wireless/broadcom/brcm80211/brcmfmac
+
+	install -m 644 ${S}/compat/compat.ko \
+		${D}/lib/modules/${KERNEL_VERSION}/updates/compat/compat.ko
+	install -m 644 ${S}/net/wireless/cfg80211.ko \
+		${D}/lib/modules/${KERNEL_VERSION}/updates/net/wireless/cfg80211.ko
+	install -m 644 ${S}/drivers/net/wireless/broadcom/brcm80211/brcmutil/brcmutil.ko \
+		${D}/lib/modules/${KERNEL_VERSION}/updates/drivers/net/wireless/broadcom/brcm80211/brcmutil/brcmutil.ko
+	install -m 644 ${S}/drivers/net/wireless/broadcom/brcm80211/brcmfmac/brcmfmac.ko \
+		${D}/lib/modules/${KERNEL_VERSION}/updates/drivers/net/wireless/broadcom/brcm80211/brcmfmac/brcmfmac.ko
+
+	rm ${STAGING_KERNEL_DIR}/.config
+}
+
+
+FILES_${PN} = " \
+	/lib/modules/${KERNEL_VERSION}/updates/compat/compat.ko \
+	/lib/modules/${KERNEL_VERSION}/updates/net/wireless/cfg80211.ko \
+	/lib/modules/${KERNEL_VERSION}/updates/drivers/net/wireless/broadcom/brcm80211/brcmutil/brcmutil.ko \
+	/lib/modules/${KERNEL_VERSION}/updates/drivers/net/wireless/broadcom/brcm80211/brcmfmac/brcmfmac.ko \
+"
diff --git a/meta-xilinx-contrib/recipes-kernel/linux/linux-xlnx/v2017.3/wifi-bluetooth.cfg b/meta-xilinx-contrib/recipes-kernel/linux/linux-xlnx/v2017.3/wifi-bluetooth.cfg
new file mode 100644
index 0000000..f71e53a
--- /dev/null
+++ b/meta-xilinx-contrib/recipes-kernel/linux/linux-xlnx/v2017.3/wifi-bluetooth.cfg
@@ -0,0 +1,33 @@
+#
+# Bluetooth config
+#
+CONFIG_BT=y
+CONFIG_BT_BREDR=y
+CONFIG_BT_HS=y
+CONFIG_BT_LE=y
+CONFIG_BT_BCM=y
+CONFIG_BT_HCIUART=y
+CONFIG_BT_HCIUART_H4=y
+CONFIG_BT_HCIUART_BCM=y
+CONFIG_BT_HIDP=y
+CONFIG_CFG80211=y
+CONFIG_CFG80211_DEFAULT_PS=y
+CONFIG_CFG80211_CRDA_SUPPORT=y
+CONFIG_BRCMUTIL=y
+CONFIG_BRCMFMAC=y
+CONFIG_BRCMFMAC_PROTO_BCDC=y
+CONFIG_BRCMFMAC_SDIO=y
+CONFIG_CRYPTO_BLKCIPHER=y
+CONFIG_CRYPTO_MANAGER=y
+CONFIG_CRYPTO_ECB=y
+CONFIG_CRYPTO_CMAC=y
+CONFIG_CRYPTO_SHA256=y
+
+#
+# Regulator config
+#
+CONFIG_REGMAP_IRQ=y
+CONFIG_I2C_XILINX=y
+CONFIG_MFD_DA9062=y
+CONFIG_REGULATOR_DA9062=y
+
diff --git a/meta-xilinx-contrib/recipes-kernel/linux/linux-xlnx_2017.3.bbappend b/meta-xilinx-contrib/recipes-kernel/linux/linux-xlnx_2017.3.bbappend
index 83b08f1..48ff77a 100644
--- a/meta-xilinx-contrib/recipes-kernel/linux/linux-xlnx_2017.3.bbappend
+++ b/meta-xilinx-contrib/recipes-kernel/linux/linux-xlnx_2017.3.bbappend
@@ -6,3 +6,4 @@ SRC_URI_append_zybo-linux-bd-zynq7 = " \
 	file://0003-drm-xilinx-Fix-DPMS-transition-to-on.patch \
 	"
 
+SRC_URI_append_minized-zynq7 = " file://wifi-bluetooth.cfg"
-- 
2.7.4



More information about the meta-xilinx mailing list