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(¶ms);
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