xref: /wlan-driver/qcacld-3.0/core/hdd/src/wlan_hdd_fips.c (revision 5113495b16420b49004c444715d2daae2066e7dc)
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(&params);
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