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