From 6fa1e0113beb8519f63de81f72970b83270fb8bc Mon Sep 17 00:00:00 2001 From: Jeff Johnson Date: Wed, 5 Apr 2017 06:40:53 -0700 Subject: [PATCH] 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 --- Kbuild | 4 + core/hdd/src/wlan_hdd_fips.c | 291 +++++++++++++++++++++++++++++++++++ core/hdd/src/wlan_hdd_fips.h | 73 +++++++++ core/hdd/src/wlan_hdd_wext.c | 31 +++- 4 files changed, 398 insertions(+), 1 deletion(-) create mode 100644 core/hdd/src/wlan_hdd_fips.c create mode 100644 core/hdd/src/wlan_hdd_fips.h diff --git a/Kbuild b/Kbuild index 6bf4ffa947c6..07bc36237b09 100644 --- a/Kbuild +++ b/Kbuild @@ -431,6 +431,10 @@ ifeq ($(CONFIG_WLAN_FEATURE_DSRC), y) 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 000000000000..a472dba76453 --- /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 000000000000..03e7c981a7eb --- /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 dd06cf356479..ec8b0ed427b0 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 @@ static const struct ccp_freq_chan_map freq_chan_map[] = { #define MAX_VAR_ARGS 9 #endif +/* + * + * 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 + * + * iwpriv wlan0 fips_test + * + * Supported Feature: FIPS + * + * Usage: Internal + * + * + */ +#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 @@ static const struct ccp_freq_chan_map freq_chan_map[] = { */ #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 @@ static const iw_handler we_private[] = { 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, @@ -13841,6 +13864,12 @@ static const struct iw_priv_args we_private_args[] = { 0, "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,