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