qcacld-3.0: Add HDD FIPS infrastructure
As part of the support for the FIPS certification feature add the
HDD infrastructure.
Change-Id: I76a545c42b10a662db04b5994de100c713a46c59
CRs-Fixed: 2065002
diff --git a/Kbuild b/Kbuild
index 6bf4ffa..07bc362 100644
--- a/Kbuild
+++ b/Kbuild
@@ -431,6 +431,10 @@
HDD_OBJS+= $(HDD_SRC_DIR)/wlan_hdd_ocb.o
endif
+ifeq ($(CONFIG_WLAN_FEATURE_FIPS), y)
+HDD_OBJS+= $(HDD_SRC_DIR)/wlan_hdd_fips.o
+endif
+
ifeq ($(CONFIG_WLAN_FEATURE_LPSS),y)
HDD_OBJS += $(HDD_SRC_DIR)/wlan_hdd_lpass.o
endif
diff --git a/core/hdd/src/wlan_hdd_fips.c b/core/hdd/src/wlan_hdd_fips.c
new file mode 100644
index 0000000..a472dba
--- /dev/null
+++ b/core/hdd/src/wlan_hdd_fips.c
@@ -0,0 +1,291 @@
+/*
+ * Copyright (c) 2017 The Linux Foundation. All rights reserved.
+ *
+ * Permission to use, copy, modify, and/or distribute this software for
+ * any purpose with or without fee is hereby granted, provided that the
+ * above copyright notice and this permission notice appear in all
+ * copies.
+ *
+ * THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL
+ * WARRANTIES WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED
+ * WARRANTIES OF MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL THE
+ * AUTHOR BE LIABLE FOR ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL
+ * DAMAGES OR ANY DAMAGES WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR
+ * PROFITS, WHETHER IN AN ACTION OF CONTRACT, NEGLIGENCE OR OTHER
+ * TORTIOUS ACTION, ARISING OUT OF OR IN CONNECTION WITH THE USE OR
+ * PERFORMANCE OF THIS SOFTWARE.
+ */
+
+/**
+ * DOC: wlan_hdd_fips.c
+ *
+ * WLAN Host Device Driver FIPS Certification Feature
+ */
+
+#include "wlan_hdd_main.h"
+#include "wlan_hdd_fips.h"
+#include "wlan_hdd_request_manager.h"
+#include "qdf_mem.h"
+#include "sme_api.h"
+
+
+#define WLAN_WAIT_TIME_FIPS 5000
+
+
+/**
+ * hdd_fips_context - hdd fips context
+ * @status: status of response. 0: no error, -ENOMEM: unable to allocate
+ * memory for the response payload
+ * @request: fips request
+ * @response: fips response
+ */
+struct hdd_fips_context {
+ int status;
+ struct fips_params request;
+ struct wmi_host_fips_event_param response;
+};
+
+/**
+ * hdd_fips_event_dup () - duplicate a fips event
+ * @dest: destination event
+ * @src: source event
+ *
+ * Make a "deep" duplicate of a FIPS event
+ *
+ * Return: 0 if the event was duplicated, otherwise an error
+ */
+static int hdd_fips_event_dup(struct wmi_host_fips_event_param *dest,
+ const struct wmi_host_fips_event_param *src)
+{
+ *dest = *src;
+ if (dest->data_len) {
+ dest->data = qdf_mem_malloc(dest->data_len);
+ if (!dest->data) {
+ hdd_err("memory allocation failed");
+ return -ENOMEM;
+ }
+ qdf_mem_copy(dest->data, src->data, src->data_len);
+ } else {
+ /* make sure we don't have a rogue pointer */
+ dest->data = NULL;
+ }
+
+ return 0;
+}
+
+/**
+ * hdd_fips_cb () - fips response message handler
+ * @cookie: hdd request cookie
+ * @response: fips response parameters
+ *
+ * Return: none
+ */
+static void hdd_fips_cb(void *cookie,
+ struct wmi_host_fips_event_param *response)
+{
+ struct hdd_request *request;
+ struct hdd_fips_context *context;
+
+ ENTER();
+
+ if (!response) {
+ hdd_err("response is NULL");
+ return;
+ }
+
+ request = hdd_request_get(cookie);
+ if (!request) {
+ hdd_debug("Obsolete request");
+ return;
+ }
+
+ hdd_debug("pdev_id %u, status %u, data_len %u",
+ response->pdev_id,
+ response->error_status,
+ response->data_len);
+ qdf_trace_hex_dump(QDF_MODULE_ID_HDD, QDF_TRACE_LEVEL_DEBUG,
+ response->data, response->data_len);
+
+ context = hdd_request_priv(request);
+ if (response->error_status) {
+ context->status = -ETIMEDOUT;
+ } else {
+ context->status = hdd_fips_event_dup(&context->response,
+ response);
+ }
+
+ hdd_request_complete(request);
+ hdd_request_put(request);
+ EXIT();
+}
+
+static void hdd_fips_context_dealloc(void *priv)
+{
+ struct hdd_fips_context *context = priv;
+
+ qdf_mem_free(context->response.data);
+}
+
+
+static int hdd_fips_validate_request(struct iw_fips_test_request *user_request,
+ uint32_t request_len)
+{
+ uint32_t expected_data_len;
+
+ if (request_len < sizeof(*user_request)) {
+ hdd_debug("Request len %u is too small", request_len);
+ return -EINVAL;
+ }
+
+ if ((user_request->key_len != FIPS_KEY_LENGTH_128) &&
+ (user_request->key_len != FIPS_KEY_LENGTH_256)) {
+ hdd_debug("Invalid key len %u", user_request->key_len);
+ return -EINVAL;
+ }
+
+ expected_data_len = request_len - sizeof(*user_request);
+ if (expected_data_len != user_request->data_len) {
+ hdd_debug("Unexpected data_len %u for request_len %u",
+ user_request->data_len, request_len);
+ return -EINVAL;
+ }
+
+ if ((user_request->mode != FIPS_ENGINE_AES_CTR) &&
+ (user_request->mode != FIPS_ENGINE_AES_MIC)) {
+ hdd_debug("Invalid mode %u", user_request->mode);
+ return -EINVAL;
+ }
+
+ if ((user_request->operation != FIPS_ENCRYPT_CMD) &&
+ (user_request->operation != FIPS_DECRYPT_CMD)) {
+ hdd_debug("Invalid operation %u", user_request->operation);
+ return -EINVAL;
+ }
+
+ return 0;
+}
+
+static int __hdd_fips_test(struct net_device *dev,
+ struct iw_request_info *info,
+ union iwreq_data *wrqu, char *extra)
+{
+ hdd_adapter_t *adapter;
+ hdd_context_t *hdd_ctx;
+ struct iw_fips_test_request *user_request;
+ struct iw_fips_test_response *user_response;
+ uint32_t request_len;
+ int ret;
+ QDF_STATUS qdf_status;
+ void *cookie;
+ struct hdd_request *request;
+ struct hdd_fips_context *context;
+ struct fips_params *fips_request;
+ struct wmi_host_fips_event_param *fips_response;
+ static const struct hdd_request_params params = {
+ .priv_size = sizeof(*context),
+ .timeout_ms = WLAN_WAIT_TIME_FIPS,
+ .dealloc = hdd_fips_context_dealloc,
+ };
+
+ ENTER_DEV(dev);
+
+ adapter = WLAN_HDD_GET_PRIV_PTR(dev);
+ hdd_ctx = WLAN_HDD_GET_CTX(adapter);
+ ret = wlan_hdd_validate_context(hdd_ctx);
+ if (ret)
+ return ret;
+
+ user_request = (struct iw_fips_test_request *)extra;
+ request_len = wrqu->data.length;
+ ret = hdd_fips_validate_request(user_request, request_len);
+ if (ret)
+ return ret;
+
+ request = hdd_request_alloc(¶ms);
+ if (!request) {
+ hdd_err("Request allocation failure");
+ return -ENOMEM;
+ }
+ context = hdd_request_priv(request);
+ fips_request = &context->request;
+ fips_request->key = &user_request->key[0];
+ fips_request->key_len = user_request->key_len;
+ fips_request->data = &user_request->data[0];
+ fips_request->data_len = user_request->data_len;
+ fips_request->mode = user_request->mode;
+ fips_request->op = user_request->operation;
+ fips_request->pdev_id = WMI_PDEV_ID_1ST;
+
+ cookie = hdd_request_cookie(request);
+ qdf_status = sme_fips_request(hdd_ctx->hHal, &context->request,
+ hdd_fips_cb, cookie);
+
+ if (!QDF_IS_STATUS_SUCCESS(qdf_status)) {
+ hdd_err("Unable to post fips message");
+ ret = -EINVAL;
+ goto cleanup;
+ }
+
+ ret = hdd_request_wait_for_response(request);
+ if (ret) {
+ hdd_err("Target response timed out");
+ goto cleanup;
+ }
+
+ ret = context->status;
+ if (ret) {
+ hdd_err("Target response processing failed");
+ goto cleanup;
+ }
+
+ fips_response = &context->response;
+ if (fips_response->data_len != fips_request->data_len) {
+ hdd_err("Data length mismatch, got %u, expected %u",
+ fips_response->data_len, fips_request->data_len);
+ ret = -EINVAL;
+ goto cleanup;
+ }
+ user_response = (struct iw_fips_test_response *)extra;
+ user_response->status = context->status;
+ if (user_response->status) {
+ user_response->data_len = 0;
+ } else {
+ user_response->data_len = fips_response->data_len;
+ qdf_mem_copy(user_response->data, fips_response->data,
+ fips_response->data_len);
+ }
+
+ /*
+ * By default wireless extensions private ioctls have either
+ * SET semantics (even numbered ioctls) or GET semantics (odd
+ * numbered ioctls). This is an even numbered ioctl so the SET
+ * semantics apply. This means the core kernel ioctl code took
+ * care of copying the request parameters from userspace to
+ * kernel space. However this ioctl also needs to return the
+ * response. Since the core kernel ioctl code doesn't support
+ * SET ioctls returning anything other than status, we have to
+ * explicitly copy the result to userspace.
+ */
+ wrqu->data.length = sizeof(*user_response) + user_response->data_len;
+ if (copy_to_user(wrqu->data.pointer, user_response, wrqu->data.length))
+ ret = -EFAULT;
+
+cleanup:
+ hdd_request_put(request);
+
+ EXIT();
+ return ret;
+}
+
+int hdd_fips_test(struct net_device *dev,
+ struct iw_request_info *info,
+ union iwreq_data *wrqu, char *extra)
+{
+ int ret;
+
+ cds_ssr_protect(__func__);
+ ret = __hdd_fips_test(dev, info, wrqu, extra);
+ cds_ssr_unprotect(__func__);
+
+ return ret;
+}
diff --git a/core/hdd/src/wlan_hdd_fips.h b/core/hdd/src/wlan_hdd_fips.h
new file mode 100644
index 0000000..03e7c98
--- /dev/null
+++ b/core/hdd/src/wlan_hdd_fips.h
@@ -0,0 +1,73 @@
+/*
+ * Copyright (c) 2017 The Linux Foundation. All rights reserved.
+ *
+ * Permission to use, copy, modify, and/or distribute this software for
+ * any purpose with or without fee is hereby granted, provided that the
+ * above copyright notice and this permission notice appear in all
+ * copies.
+ *
+ * THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL
+ * WARRANTIES WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED
+ * WARRANTIES OF MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL THE
+ * AUTHOR BE LIABLE FOR ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL
+ * DAMAGES OR ANY DAMAGES WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR
+ * PROFITS, WHETHER IN AN ACTION OF CONTRACT, NEGLIGENCE OR OTHER
+ * TORTIOUS ACTION, ARISING OUT OF OR IN CONNECTION WITH THE USE OR
+ * PERFORMANCE OF THIS SOFTWARE.
+ */
+
+/**
+ * DOC: wlan_hdd_fips.h
+ *
+ * WLAN Host Device Driver FIPS Certification Feature
+ */
+
+#ifndef __WLAN_HDD_FIPS_H__
+#define __WLAN_HDD_FIPS_H__
+
+struct net_device;
+struct iw_request_info;
+union iwreq_data;
+struct hdd_adapter_s;
+void fips_test(struct hdd_adapter_s *adapter);
+
+#define FIPS_KEY_LEN 32
+struct iw_fips_test_request {
+ uint32_t operation;
+ uint32_t mode;
+ uint32_t key_len;
+ uint8_t key[FIPS_KEY_LEN];
+ uint32_t data_len;
+ uint8_t data[0];
+};
+
+struct iw_fips_test_response {
+ uint32_t status;
+ uint32_t data_len;
+ uint8_t data[0];
+};
+
+
+/**
+ * hdd_fips_test() - Perform FIPS test
+ * @dev: netdev upon which the FIPS test ioctl was received
+ * @info: ioctl request information
+ * @wrqu: ioctl request data structure
+ * @extra: extra payload for wireless extensions ioctl
+ *
+ * This API implements the FIPS test interface. Upon entry the @extra
+ * buffer will contain a FIPS test vector formated as a &struct
+ * iw_fips_test_request. This vector will be sent to firmware where it
+ * will be run through the appropriate hardware. The result of the
+ * operation will be sent back to userspace via @extra encoded as a
+ * &struct iw_fips_test_response.
+ *
+ * Return: 0 if the test vector was processed, otherwise a negative
+ * errno.
+ */
+
+int hdd_fips_test(struct net_device *dev,
+ struct iw_request_info *info,
+ union iwreq_data *wrqu, char *extra);
+
+#endif /* __WLAN_HDD_FIPS_H__ */
diff --git a/core/hdd/src/wlan_hdd_wext.c b/core/hdd/src/wlan_hdd_wext.c
index dd06cf3..ec8b0ed 100644
--- a/core/hdd/src/wlan_hdd_wext.c
+++ b/core/hdd/src/wlan_hdd_wext.c
@@ -82,6 +82,7 @@
#include "sme_power_save_api.h"
#include "wlan_policy_mgr_api.h"
#include "wlan_hdd_conc_ut.h"
+#include "wlan_hdd_fips.h"
#include "wlan_hdd_tsf.h"
#include "wlan_hdd_ocb.h"
#include "wlan_hdd_napi.h"
@@ -2534,6 +2535,28 @@
#define MAX_VAR_ARGS 9
#endif
+/*
+ * <ioctl>
+ * fips_test - Perform a FIPS test
+ *
+ * @INPUT: Binary representation of the following packed structure
+ *
+ * @OUTPUT: Binary representation of the following packed structure
+ *
+ * This IOCTL is used to perform FIPS certification testing
+ *
+ * @E.g: iwpriv wlan0 fips_test <test vector>
+ *
+ * iwpriv wlan0 fips_test <tbd>
+ *
+ * Supported Feature: FIPS
+ *
+ * Usage: Internal
+ *
+ * </ioctl>
+ */
+#define WLAN_PRIV_FIPS_TEST (SIOCIWFIRSTPRIV + 8)
+
/* Private ioctls (with no sub-ioctls) */
/* note that they must be odd so that they have "get" semantics */
/*
@@ -2627,7 +2650,6 @@
*/
#define WLAN_PRIV_GET_TSPEC (SIOCIWFIRSTPRIV + 13)
-/* (SIOCIWFIRSTPRIV + 8) is currently unused */
/* (SIOCIWFIRSTPRIV + 10) is currently unused */
/* (SIOCIWFIRSTPRIV + 12) is currently unused */
/* (SIOCIWFIRSTPRIV + 14) is currently unused */
@@ -12818,6 +12840,7 @@
iw_hdd_set_var_ints_getnone,
[WLAN_PRIV_SET_NONE_GET_THREE_INT - SIOCIWFIRSTPRIV] =
iw_setnone_get_threeint,
+ [WLAN_PRIV_FIPS_TEST - SIOCIWFIRSTPRIV] = hdd_fips_test,
[WLAN_PRIV_ADD_TSPEC - SIOCIWFIRSTPRIV] = iw_add_tspec,
[WLAN_PRIV_DEL_TSPEC - SIOCIWFIRSTPRIV] = iw_del_tspec,
[WLAN_PRIV_GET_TSPEC - SIOCIWFIRSTPRIV] = iw_get_tspec,
@@ -13842,6 +13865,12 @@
"gpio_control"},
#endif
/* handlers for main ioctl */
+ {WLAN_PRIV_FIPS_TEST,
+ IW_PRIV_TYPE_BYTE | WE_MAX_STR_LEN,
+ IW_PRIV_TYPE_BYTE | WE_MAX_STR_LEN,
+ "fips_test"},
+
+ /* handlers for main ioctl */
{WLAN_PRIV_ADD_TSPEC,
IW_PRIV_TYPE_INT | IW_PRIV_SIZE_FIXED | HDD_WLAN_WMM_PARAM_COUNT,
IW_PRIV_TYPE_INT | IW_PRIV_SIZE_FIXED | 1,