h_ioctl.c revision 1.5 1 /* $NetBSD: h_ioctl.c,v 1.5 2022/05/21 20:38:34 riastradh Exp $ */
2
3 /*-
4 * Copyright (c) 2017 Internet Initiative Japan Inc.
5 * All rights reserved.
6 *
7 * Redistribution and use in source and binary forms, with or without
8 * modification, are permitted provided that the following conditions
9 * are met:
10 * 1. Redistributions of source code must retain the above copyright
11 * notice, this list of conditions and the following disclaimer.
12 * 2. Redistributions in binary form must reproduce the above copyright
13 * notice, this list of conditions and the following disclaimer in the
14 * documentation and/or other materials provided with the distribution.
15 *
16 * THIS SOFTWARE IS PROVIDED BY THE NETBSD FOUNDATION, INC. AND CONTRIBUTORS
17 * ``AS IS'' AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED
18 * TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR
19 * PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE FOUNDATION OR CONTRIBUTORS
20 * BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR
21 * CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF
22 * SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS
23 * INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN
24 * CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE)
25 * ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE
26 * POSSIBILITY OF SUCH DAMAGE.
27 */
28
29 #include <err.h>
30 #include <errno.h>
31 #include <fcntl.h>
32 #include <poll.h>
33 #include <stdio.h>
34 #include <string.h>
35
36 #include <sys/errno.h>
37 #include <sys/ioctl.h>
38 #include <sys/sysctl.h>
39
40 #include <crypto/cryptodev.h>
41
42 /* copy from h_aescbc.c */
43 #define AES_KEY_LEN 16
44 unsigned char aes_key[AES_KEY_LEN] =
45 { 0x06, 0xa9, 0x21, 0x40, 0x36, 0xb8, 0xa1, 0x5b,
46 0x51, 0x2e, 0x03, 0xd5, 0x34, 0x12, 0x00, 0x06, };
47
48 #define AES_IV_LEN 16
49 unsigned char aes_iv[AES_IV_LEN] =
50 { 0x3d, 0xaf, 0xba, 0x42, 0x9d, 0x9e, 0xb4, 0x30,
51 0xb4, 0x22, 0xda, 0x80, 0x2c, 0x9f, 0xac, 0x41, };
52
53 #define AES_PLAINTX_LEN 64
54 unsigned char aes_plaintx[AES_PLAINTX_LEN] = "Single block msg";
55
56 #define AES_CIPHER_LEN 64
57 unsigned char aes_cipher[AES_CIPHER_LEN] =
58 { 0xe3, 0x53, 0x77, 0x9c, 0x10, 0x79, 0xae, 0xb8,
59 0x27, 0x08, 0x94, 0x2d, 0xbe, 0x77, 0x18, 0x1a, };
60
61 #define COUNT 2
62
63 static int
64 wait_for_read(int fd)
65 {
66 struct pollfd pfd = { .fd = fd, .events = POLLIN };
67 int nfd;
68
69 nfd = poll(&pfd, 1, 5000);
70 if (nfd == -1) {
71 warn("failed: poll");
72 return -1;
73 }
74 if (nfd == 0) {
75 warnx("failed: timeout");
76 errno = ETIMEDOUT;
77 return -1;
78 }
79 if (nfd != 1 || (pfd.revents & POLLIN) == 0) {
80 warnx("failed: invalid poll: %d", nfd);
81 errno = EIO;
82 return -1;
83 }
84 return 0;
85 }
86
87 /*
88 * CRIOGET is deprecated.
89 */
90
91 /*
92 * CIOCNGSESSION
93 * Hmm, who uses? (1)
94 */
95 static int
96 test_ngsession(int fd)
97 {
98 int ret;
99 struct crypt_sgop sg;
100 struct session_n_op css[COUNT];
101
102 for (size_t i = 0; i < COUNT; i++) {
103 struct session_n_op *cs = &css[i];
104
105 memset(cs, 0, sizeof(*cs));
106 cs->cipher = CRYPTO_AES_CBC;
107 cs->keylen = AES_KEY_LEN;
108 cs->key = __UNCONST(&aes_key);
109 }
110 memset(&sg, 0, sizeof(sg));
111 sg.count = COUNT;
112 sg.sessions = css;
113
114 ret = ioctl(fd, CIOCNGSESSION, &sg);
115 if (ret < 0)
116 warn("failed: CIOCNGSESSION");
117
118 return ret;
119 }
120
121 /*
122 * CIOCNFSESSION
123 * Hmm, who uses? (2)
124 */
125 static int
126 test_nfsession(int fd)
127 {
128 int ret;
129 struct crypt_sfop sf;
130 u_int32_t sids[COUNT];
131
132 memset(sids, 0, sizeof(sids));
133 memset(&sf, 0, sizeof(sf));
134 sf.count = COUNT;
135 sf.sesid = sids;
136
137 ret = ioctl(fd, CIOCNFSESSION, &sf);
138 if (ret < 0)
139 warn("failed: CIOCNFSESSION");
140
141 return ret;
142 }
143
144 /*
145 * CIOCNCRYPTM
146 * Hmm, who uses? (3)
147 */
148 static int
149 test_ncryptm(int fd)
150 {
151 int ret;
152 struct crypt_mop mop;
153 struct crypt_n_op css[COUNT];
154
155 for (size_t i = 0; i < COUNT; i++) {
156 struct crypt_n_op *cs;
157 cs = &css[i];
158
159 memset(cs, 0, sizeof(*cs));
160 cs->ses = 0; /* session id */
161 cs->op = COP_ENCRYPT;
162 /* XXX */
163 }
164
165 memset(&mop, 0, sizeof(mop));
166 mop.count = COUNT;
167 mop.reqs = css;
168
169 ret = ioctl(fd, CIOCNCRYPTM, &mop);
170 if (ret < 0)
171 warn("failed: CIOCNCRYPTM");
172
173 return ret;
174 }
175
176 /*
177 * CIOCNCRYPTRETM
178 * Hmm, who uses? (4)
179 */
180 static int
181 test_ncryptretm(int fd)
182 {
183 int ret;
184 struct session_op cs;
185
186 struct crypt_mop mop;
187 struct crypt_n_op cnos[COUNT];
188 unsigned char cno_dst[COUNT][AES_CIPHER_LEN];
189 struct cryptret cret;
190 struct crypt_result crs[COUNT];
191
192 memset(&cs, 0, sizeof(cs));
193 cs.cipher = CRYPTO_AES_CBC;
194 cs.keylen = AES_KEY_LEN;
195 cs.key = __UNCONST(&aes_key);
196 ret = ioctl(fd, CIOCGSESSION, &cs);
197 if (ret < 0) {
198 warn("failed: CIOCGSESSION");
199 return ret;
200 }
201
202 for (size_t i = 0; i < COUNT; i++) {
203 struct crypt_n_op *cno = &cnos[i];
204
205 memset(cno, 0, sizeof(*cno));
206 cno->ses = cs.ses;
207 cno->op = COP_ENCRYPT;
208 cno->len = AES_PLAINTX_LEN;
209 cno->src = aes_plaintx;
210 cno->dst_len = AES_CIPHER_LEN;
211 cno->dst = cno_dst[i];
212 }
213
214 memset(&mop, 0, sizeof(mop));
215 mop.count = COUNT;
216 mop.reqs = cnos;
217 ret = ioctl(fd, CIOCNCRYPTM, &mop);
218 if (ret < 0) {
219 warn("failed: CIOCNCRYPTM");
220 return ret;
221 }
222
223 for (size_t i = 0; i < COUNT; i++) {
224 struct crypt_result *cr = &crs[i];
225
226 memset(cr, 0, sizeof(*cr));
227 cr->reqid = cnos[i].reqid;
228 }
229
230 memset(&cret, 0, sizeof(cret));
231 cret.count = COUNT;
232 cret.results = crs;
233 ret = ioctl(fd, CIOCNCRYPTRETM, &cret);
234 if (ret < 0) {
235 if (errno != EINPROGRESS) {
236 warn("failed: CIOCNCRYPTRETM");
237 return ret;
238 }
239
240 ret = wait_for_read(fd);
241 if (ret < 0)
242 return ret;
243
244 cret.count = COUNT;
245 cret.results = crs;
246 ret = ioctl(fd, CIOCNCRYPTRETM, &cret);
247 if (ret < 0) {
248 warn("failed: CIOCNCRYPTRET");
249 return ret;
250 }
251 }
252
253 return ret;
254 }
255
256 /*
257 * CIOCNCRYPTRET
258 * Hmm, who uses? (5)
259 */
260 /* test when it does not request yet. */
261 static int
262 test_ncryptret_noent(int fd)
263 {
264 int ret;
265 struct crypt_result cr;
266
267 memset(&cr, 0, sizeof(cr));
268
269 ret = ioctl(fd, CIOCNCRYPTRET, &cr);
270 if (ret == 0) {
271 warn("failed: CIOCNCRYPTRET unexpected success when no entry");
272 ret = -1;
273 } else if (errno == EINPROGRESS) {
274 /* expected fail */
275 ret = 0;
276 }
277
278 return ret;
279 }
280
281 static int
282 test_ncryptret_ent(int fd)
283 {
284 int ret;
285 struct session_op cs;
286
287 struct crypt_mop mop;
288 struct crypt_n_op cno;
289 unsigned char cno_dst[AES_CIPHER_LEN];
290
291 struct crypt_result cr;
292
293 memset(&cs, 0, sizeof(cs));
294 cs.cipher = CRYPTO_AES_CBC;
295 cs.keylen = AES_KEY_LEN;
296 cs.key = __UNCONST(&aes_key);
297 ret = ioctl(fd, CIOCGSESSION, &cs);
298 if (ret < 0) {
299 warn("failed: CIOCGSESSION");
300 return ret;
301 }
302
303 memset(&cno, 0, sizeof(cno));
304 cno.ses = cs.ses;
305 cno.op = COP_ENCRYPT;
306 cno.len = AES_PLAINTX_LEN;
307 cno.src = aes_plaintx;
308 cno.dst_len = AES_CIPHER_LEN;
309 cno.dst = cno_dst;
310
311 memset(&mop, 0, sizeof(mop));
312 mop.count = 1;
313 mop.reqs = &cno;
314 ret = ioctl(fd, CIOCNCRYPTM, &mop);
315 if (ret < 0) {
316 warn("failed: CIOCNCRYPTM");
317 return ret;
318 }
319
320 memset(&cr, 0, sizeof(cr));
321 cr.reqid = cno.reqid;
322
323 ret = ioctl(fd, CIOCNCRYPTRET, &cr);
324 if (ret < 0) {
325 if (errno != EINPROGRESS) {
326 warn("failed: CIOCNCRYPTRET");
327 return ret;
328 }
329
330 ret = wait_for_read(fd);
331 if (ret < 0)
332 return ret;
333 ret = ioctl(fd, CIOCNCRYPTRET, &cr);
334 if (ret < 0) {
335 warn("failed: CIOCNCRYPTRET");
336 return ret;
337 }
338 return 0;
339 }
340
341 return ret;
342 }
343
344 static int
345 test_ncryptret(int fd)
346 {
347 int ret;
348
349 ret = test_ncryptret_noent(fd);
350 if (ret < 0)
351 return ret;
352
353 ret = test_ncryptret_ent(fd);
354 if (ret < 0)
355 return ret;
356
357 return ret;
358 }
359
360 /*
361 * CIOCASYMFEAT
362 */
363 static int
364 set_userasymcrypto(int new, int *old)
365 {
366 int ret;
367
368 ret = sysctlbyname("kern.userasymcrypto", NULL, NULL, &new, sizeof(new));
369 if (ret < 0) {
370 warn("failed: kern.userasymcrypto=%d", new);
371 return ret;
372 }
373
374 if (old != NULL)
375 *old = new;
376
377 return ret;
378 }
379
380 static int
381 test_asymfeat_each(int fd, u_int32_t *asymfeat, int userasym)
382 {
383 int ret;
384
385 ret = ioctl(fd, CIOCASYMFEAT, asymfeat);
386 if (ret < 0)
387 warn("failed: CIOCASYMFEAT when userasym=%d", userasym);
388
389 return ret;
390 }
391
392 static int
393 test_asymfeat(int fd)
394 {
395 int ret, new, orig;
396 u_int32_t asymfeat = 0;
397
398 /* test for kern.userasymcrypto=1 */
399 new = 1;
400 ret = set_userasymcrypto(new, &orig);
401 if (ret < 0)
402 return ret;
403 ret = test_asymfeat_each(fd, &asymfeat, new);
404 if (ret < 0)
405 return ret;
406
407 /* test for kern.userasymcrypto=0 */
408 new = 0;
409 ret = set_userasymcrypto(new, NULL);
410 if (ret < 0)
411 return ret;
412 ret = test_asymfeat_each(fd, &asymfeat, new);
413 if (ret < 0)
414 return ret;
415
416 /* cleanup */
417 ret = set_userasymcrypto(orig, NULL);
418 if (ret < 0)
419 warnx("failed: cleanup kern.userasymcrypto");
420
421 return ret;
422 }
423
424 int
425 main(void)
426 {
427 int fd, ret;
428
429 fd = open("/dev/crypto", O_RDWR, 0);
430 if (fd < 0)
431 err(1, "open");
432
433 ret = test_ngsession(fd);
434 if (ret < 0)
435 err(1, "test_ngsession");
436
437 ret = test_nfsession(fd);
438 if (ret < 0)
439 err(1, "test_ngsession");
440
441 ret = test_ncryptm(fd);
442 if (ret < 0)
443 err(1, "test_ncryptm");
444
445 test_ncryptretm(fd);
446 if (ret < 0)
447 err(1, "test_ncryptretm");
448
449 ret = test_ncryptret(fd);
450 if (ret < 0)
451 err(1, "test_ncryptret");
452
453 ret = test_asymfeat(fd);
454 if (ret < 0)
455 err(1, "test_asymfeat");
456
457 return 0;
458 }
459