1*5113495bSYour Name /*
2*5113495bSYour Name * Copyright (c) 2017-2020 The Linux Foundation. All rights reserved.
3*5113495bSYour Name * Copyright (c) 2022 Qualcomm Innovation Center, Inc. All rights reserved.
4*5113495bSYour Name *
5*5113495bSYour Name * Permission to use, copy, modify, and/or distribute this software for
6*5113495bSYour Name * any purpose with or without fee is hereby granted, provided that the
7*5113495bSYour Name * above copyright notice and this permission notice appear in all
8*5113495bSYour Name * copies.
9*5113495bSYour Name *
10*5113495bSYour Name * THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL
11*5113495bSYour Name * WARRANTIES WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED
12*5113495bSYour Name * WARRANTIES OF MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL THE
13*5113495bSYour Name * AUTHOR BE LIABLE FOR ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL
14*5113495bSYour Name * DAMAGES OR ANY DAMAGES WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR
15*5113495bSYour Name * PROFITS, WHETHER IN AN ACTION OF CONTRACT, NEGLIGENCE OR OTHER
16*5113495bSYour Name * TORTIOUS ACTION, ARISING OUT OF OR IN CONNECTION WITH THE USE OR
17*5113495bSYour Name * PERFORMANCE OF THIS SOFTWARE.
18*5113495bSYour Name */
19*5113495bSYour Name
20*5113495bSYour Name /**
21*5113495bSYour Name * DOC: wlan_hdd_fips.c
22*5113495bSYour Name *
23*5113495bSYour Name * WLAN Host Device Driver FIPS Certification Feature
24*5113495bSYour Name */
25*5113495bSYour Name
26*5113495bSYour Name #include "osif_sync.h"
27*5113495bSYour Name #include "wlan_hdd_main.h"
28*5113495bSYour Name #include "wlan_hdd_fips.h"
29*5113495bSYour Name #include "wlan_osif_request_manager.h"
30*5113495bSYour Name #include "qdf_mem.h"
31*5113495bSYour Name #include "sme_api.h"
32*5113495bSYour Name
33*5113495bSYour Name #define WLAN_WAIT_TIME_FIPS 5000
34*5113495bSYour Name
35*5113495bSYour Name /**
36*5113495bSYour Name * struct hdd_fips_context - hdd fips context
37*5113495bSYour Name * @status: status of response. 0: no error, -ENOMEM: unable to allocate
38*5113495bSYour Name * memory for the response payload
39*5113495bSYour Name * @request: fips request
40*5113495bSYour Name * @response: fips response
41*5113495bSYour Name */
42*5113495bSYour Name struct hdd_fips_context {
43*5113495bSYour Name int status;
44*5113495bSYour Name struct fips_params request;
45*5113495bSYour Name struct wmi_host_fips_event_param response;
46*5113495bSYour Name };
47*5113495bSYour Name
48*5113495bSYour Name /**
49*5113495bSYour Name * hdd_fips_event_dup () - duplicate a fips event
50*5113495bSYour Name * @dest: destination event
51*5113495bSYour Name * @src: source event
52*5113495bSYour Name *
53*5113495bSYour Name * Make a "deep" duplicate of a FIPS event
54*5113495bSYour Name *
55*5113495bSYour Name * Return: 0 if the event was duplicated, otherwise an error
56*5113495bSYour Name */
hdd_fips_event_dup(struct wmi_host_fips_event_param * dest,const struct wmi_host_fips_event_param * src)57*5113495bSYour Name static int hdd_fips_event_dup(struct wmi_host_fips_event_param *dest,
58*5113495bSYour Name const struct wmi_host_fips_event_param *src)
59*5113495bSYour Name {
60*5113495bSYour Name *dest = *src;
61*5113495bSYour Name if (dest->data_len) {
62*5113495bSYour Name dest->data = qdf_mem_malloc(dest->data_len);
63*5113495bSYour Name if (!dest->data)
64*5113495bSYour Name return -ENOMEM;
65*5113495bSYour Name
66*5113495bSYour Name qdf_mem_copy(dest->data, src->data, src->data_len);
67*5113495bSYour Name } else {
68*5113495bSYour Name /* make sure we don't have a rogue pointer */
69*5113495bSYour Name dest->data = NULL;
70*5113495bSYour Name }
71*5113495bSYour Name
72*5113495bSYour Name return 0;
73*5113495bSYour Name }
74*5113495bSYour Name
75*5113495bSYour Name /**
76*5113495bSYour Name * hdd_fips_cb () - fips response message handler
77*5113495bSYour Name * @cookie: hdd request cookie
78*5113495bSYour Name * @response: fips response parameters
79*5113495bSYour Name *
80*5113495bSYour Name * Return: none
81*5113495bSYour Name */
hdd_fips_cb(void * cookie,struct wmi_host_fips_event_param * response)82*5113495bSYour Name static void hdd_fips_cb(void *cookie,
83*5113495bSYour Name struct wmi_host_fips_event_param *response)
84*5113495bSYour Name {
85*5113495bSYour Name struct osif_request *request;
86*5113495bSYour Name struct hdd_fips_context *context;
87*5113495bSYour Name
88*5113495bSYour Name hdd_enter();
89*5113495bSYour Name
90*5113495bSYour Name if (!response) {
91*5113495bSYour Name hdd_err("response is NULL");
92*5113495bSYour Name return;
93*5113495bSYour Name }
94*5113495bSYour Name
95*5113495bSYour Name request = osif_request_get(cookie);
96*5113495bSYour Name if (!request) {
97*5113495bSYour Name hdd_debug("Obsolete request");
98*5113495bSYour Name return;
99*5113495bSYour Name }
100*5113495bSYour Name
101*5113495bSYour Name hdd_debug("pdev_id %u, status %u, data_len %u",
102*5113495bSYour Name response->pdev_id,
103*5113495bSYour Name response->error_status,
104*5113495bSYour Name response->data_len);
105*5113495bSYour Name qdf_trace_hex_dump(QDF_MODULE_ID_HDD, QDF_TRACE_LEVEL_DEBUG,
106*5113495bSYour Name response->data, response->data_len);
107*5113495bSYour Name
108*5113495bSYour Name context = osif_request_priv(request);
109*5113495bSYour Name if (response->error_status) {
110*5113495bSYour Name context->status = -ETIMEDOUT;
111*5113495bSYour Name } else {
112*5113495bSYour Name context->status = hdd_fips_event_dup(&context->response,
113*5113495bSYour Name response);
114*5113495bSYour Name }
115*5113495bSYour Name
116*5113495bSYour Name osif_request_complete(request);
117*5113495bSYour Name osif_request_put(request);
118*5113495bSYour Name hdd_exit();
119*5113495bSYour Name }
120*5113495bSYour Name
hdd_fips_context_dealloc(void * priv)121*5113495bSYour Name static void hdd_fips_context_dealloc(void *priv)
122*5113495bSYour Name {
123*5113495bSYour Name struct hdd_fips_context *context = priv;
124*5113495bSYour Name
125*5113495bSYour Name qdf_mem_free(context->response.data);
126*5113495bSYour Name }
127*5113495bSYour Name
128*5113495bSYour Name
hdd_fips_validate_request(struct iw_fips_test_request * user_request,uint32_t request_len)129*5113495bSYour Name static int hdd_fips_validate_request(struct iw_fips_test_request *user_request,
130*5113495bSYour Name uint32_t request_len)
131*5113495bSYour Name {
132*5113495bSYour Name uint32_t expected_data_len;
133*5113495bSYour Name
134*5113495bSYour Name if (request_len < sizeof(*user_request)) {
135*5113495bSYour Name hdd_debug("Request len %u is too small", request_len);
136*5113495bSYour Name return -EINVAL;
137*5113495bSYour Name }
138*5113495bSYour Name
139*5113495bSYour Name if ((user_request->key_len != FIPS_KEY_LENGTH_128) &&
140*5113495bSYour Name (user_request->key_len != FIPS_KEY_LENGTH_256)) {
141*5113495bSYour Name hdd_debug("Invalid key len %u", user_request->key_len);
142*5113495bSYour Name return -EINVAL;
143*5113495bSYour Name }
144*5113495bSYour Name
145*5113495bSYour Name expected_data_len = request_len - sizeof(*user_request);
146*5113495bSYour Name if (expected_data_len != user_request->data_len) {
147*5113495bSYour Name hdd_debug("Unexpected data_len %u for request_len %u",
148*5113495bSYour Name user_request->data_len, request_len);
149*5113495bSYour Name return -EINVAL;
150*5113495bSYour Name }
151*5113495bSYour Name
152*5113495bSYour Name if ((user_request->mode != FIPS_ENGINE_AES_CTR) &&
153*5113495bSYour Name (user_request->mode != FIPS_ENGINE_AES_MIC)) {
154*5113495bSYour Name hdd_debug("Invalid mode %u", user_request->mode);
155*5113495bSYour Name return -EINVAL;
156*5113495bSYour Name }
157*5113495bSYour Name
158*5113495bSYour Name if ((user_request->operation != FIPS_ENCRYPT_CMD) &&
159*5113495bSYour Name (user_request->operation != FIPS_DECRYPT_CMD)) {
160*5113495bSYour Name hdd_debug("Invalid operation %u", user_request->operation);
161*5113495bSYour Name return -EINVAL;
162*5113495bSYour Name }
163*5113495bSYour Name
164*5113495bSYour Name return 0;
165*5113495bSYour Name }
166*5113495bSYour Name
__hdd_fips_test(struct net_device * dev,struct iw_request_info * info,union iwreq_data * wrqu,char * extra)167*5113495bSYour Name static int __hdd_fips_test(struct net_device *dev,
168*5113495bSYour Name struct iw_request_info *info,
169*5113495bSYour Name union iwreq_data *wrqu, char *extra)
170*5113495bSYour Name {
171*5113495bSYour Name struct hdd_adapter *adapter;
172*5113495bSYour Name struct hdd_context *hdd_ctx;
173*5113495bSYour Name struct iw_fips_test_request *user_request;
174*5113495bSYour Name struct iw_fips_test_response *user_response;
175*5113495bSYour Name uint32_t request_len;
176*5113495bSYour Name int ret;
177*5113495bSYour Name QDF_STATUS qdf_status;
178*5113495bSYour Name void *cookie;
179*5113495bSYour Name struct osif_request *request;
180*5113495bSYour Name struct hdd_fips_context *context;
181*5113495bSYour Name struct fips_params *fips_request;
182*5113495bSYour Name struct wmi_host_fips_event_param *fips_response;
183*5113495bSYour Name static const struct osif_request_params params = {
184*5113495bSYour Name .priv_size = sizeof(*context),
185*5113495bSYour Name .timeout_ms = WLAN_WAIT_TIME_FIPS,
186*5113495bSYour Name .dealloc = hdd_fips_context_dealloc,
187*5113495bSYour Name };
188*5113495bSYour Name
189*5113495bSYour Name hdd_enter_dev(dev);
190*5113495bSYour Name
191*5113495bSYour Name adapter = WLAN_HDD_GET_PRIV_PTR(dev);
192*5113495bSYour Name hdd_ctx = WLAN_HDD_GET_CTX(adapter);
193*5113495bSYour Name ret = wlan_hdd_validate_context(hdd_ctx);
194*5113495bSYour Name if (ret)
195*5113495bSYour Name return ret;
196*5113495bSYour Name
197*5113495bSYour Name user_request = (struct iw_fips_test_request *)extra;
198*5113495bSYour Name request_len = wrqu->data.length;
199*5113495bSYour Name ret = hdd_fips_validate_request(user_request, request_len);
200*5113495bSYour Name if (ret)
201*5113495bSYour Name return ret;
202*5113495bSYour Name
203*5113495bSYour Name request = osif_request_alloc(¶ms);
204*5113495bSYour Name if (!request) {
205*5113495bSYour Name hdd_err("Request allocation failure");
206*5113495bSYour Name return -ENOMEM;
207*5113495bSYour Name }
208*5113495bSYour Name context = osif_request_priv(request);
209*5113495bSYour Name fips_request = &context->request;
210*5113495bSYour Name fips_request->key = &user_request->key[0];
211*5113495bSYour Name fips_request->key_len = user_request->key_len;
212*5113495bSYour Name fips_request->data = &user_request->data[0];
213*5113495bSYour Name fips_request->data_len = user_request->data_len;
214*5113495bSYour Name fips_request->mode = user_request->mode;
215*5113495bSYour Name fips_request->op = user_request->operation;
216*5113495bSYour Name fips_request->pdev_id = WMI_PDEV_ID_1ST;
217*5113495bSYour Name
218*5113495bSYour Name cookie = osif_request_cookie(request);
219*5113495bSYour Name qdf_status = sme_fips_request(hdd_ctx->mac_handle, &context->request,
220*5113495bSYour Name hdd_fips_cb, cookie);
221*5113495bSYour Name
222*5113495bSYour Name if (!QDF_IS_STATUS_SUCCESS(qdf_status)) {
223*5113495bSYour Name hdd_err("Unable to post fips message");
224*5113495bSYour Name ret = -EINVAL;
225*5113495bSYour Name goto cleanup;
226*5113495bSYour Name }
227*5113495bSYour Name
228*5113495bSYour Name ret = osif_request_wait_for_response(request);
229*5113495bSYour Name if (ret) {
230*5113495bSYour Name hdd_err("Target response timed out");
231*5113495bSYour Name goto cleanup;
232*5113495bSYour Name }
233*5113495bSYour Name
234*5113495bSYour Name ret = context->status;
235*5113495bSYour Name if (ret) {
236*5113495bSYour Name hdd_err("Target response processing failed");
237*5113495bSYour Name goto cleanup;
238*5113495bSYour Name }
239*5113495bSYour Name
240*5113495bSYour Name fips_response = &context->response;
241*5113495bSYour Name if (fips_response->data_len != fips_request->data_len) {
242*5113495bSYour Name hdd_err("Data length mismatch, got %u, expected %u",
243*5113495bSYour Name fips_response->data_len, fips_request->data_len);
244*5113495bSYour Name ret = -EINVAL;
245*5113495bSYour Name goto cleanup;
246*5113495bSYour Name }
247*5113495bSYour Name user_response = (struct iw_fips_test_response *)extra;
248*5113495bSYour Name user_response->status = context->status;
249*5113495bSYour Name if (user_response->status) {
250*5113495bSYour Name user_response->data_len = 0;
251*5113495bSYour Name } else {
252*5113495bSYour Name user_response->data_len = fips_response->data_len;
253*5113495bSYour Name qdf_mem_copy(user_response->data, fips_response->data,
254*5113495bSYour Name fips_response->data_len);
255*5113495bSYour Name }
256*5113495bSYour Name
257*5113495bSYour Name /*
258*5113495bSYour Name * By default wireless extensions private ioctls have either
259*5113495bSYour Name * SET semantics (even numbered ioctls) or GET semantics (odd
260*5113495bSYour Name * numbered ioctls). This is an even numbered ioctl so the SET
261*5113495bSYour Name * semantics apply. This means the core kernel ioctl code took
262*5113495bSYour Name * care of copying the request parameters from userspace to
263*5113495bSYour Name * kernel space. However this ioctl also needs to return the
264*5113495bSYour Name * response. Since the core kernel ioctl code doesn't support
265*5113495bSYour Name * SET ioctls returning anything other than status, we have to
266*5113495bSYour Name * explicitly copy the result to userspace.
267*5113495bSYour Name */
268*5113495bSYour Name wrqu->data.length = sizeof(*user_response) + user_response->data_len;
269*5113495bSYour Name if (copy_to_user(wrqu->data.pointer, user_response, wrqu->data.length))
270*5113495bSYour Name ret = -EFAULT;
271*5113495bSYour Name
272*5113495bSYour Name cleanup:
273*5113495bSYour Name osif_request_put(request);
274*5113495bSYour Name
275*5113495bSYour Name hdd_exit();
276*5113495bSYour Name return ret;
277*5113495bSYour Name }
278*5113495bSYour Name
hdd_fips_test(struct net_device * dev,struct iw_request_info * info,union iwreq_data * wrqu,char * extra)279*5113495bSYour Name int hdd_fips_test(struct net_device *dev,
280*5113495bSYour Name struct iw_request_info *info,
281*5113495bSYour Name union iwreq_data *wrqu, char *extra)
282*5113495bSYour Name {
283*5113495bSYour Name int errno;
284*5113495bSYour Name struct osif_vdev_sync *vdev_sync;
285*5113495bSYour Name
286*5113495bSYour Name errno = osif_vdev_sync_op_start(dev, &vdev_sync);
287*5113495bSYour Name if (errno)
288*5113495bSYour Name return errno;
289*5113495bSYour Name
290*5113495bSYour Name errno = __hdd_fips_test(dev, info, wrqu, extra);
291*5113495bSYour Name
292*5113495bSYour Name osif_vdev_sync_op_stop(vdev_sync);
293*5113495bSYour Name
294*5113495bSYour Name return errno;
295*5113495bSYour Name }
296