1 /* $NetBSD: h_ioctl.c,v 1.6 2023/08/05 13:29:57 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 #include <unistd.h>
36
37 #include <sys/errno.h>
38 #include <sys/ioctl.h>
39 #include <sys/sysctl.h>
40
41 #include <crypto/cryptodev.h>
42
43 /* copy from h_aescbc.c */
44 #define AES_KEY_LEN 16
45 unsigned char aes_key[AES_KEY_LEN] =
46 { 0x06, 0xa9, 0x21, 0x40, 0x36, 0xb8, 0xa1, 0x5b,
47 0x51, 0x2e, 0x03, 0xd5, 0x34, 0x12, 0x00, 0x06, };
48
49 #define AES_IV_LEN 16
50 unsigned char aes_iv[AES_IV_LEN] =
51 { 0x3d, 0xaf, 0xba, 0x42, 0x9d, 0x9e, 0xb4, 0x30,
52 0xb4, 0x22, 0xda, 0x80, 0x2c, 0x9f, 0xac, 0x41, };
53
54 #define AES_PLAINTX_LEN 64
55 unsigned char aes_plaintx[AES_PLAINTX_LEN] = "Single block msg";
56
57 #define AES_CIPHER_LEN 64
58 unsigned char aes_cipher[AES_CIPHER_LEN] =
59 { 0xe3, 0x53, 0x77, 0x9c, 0x10, 0x79, 0xae, 0xb8,
60 0x27, 0x08, 0x94, 0x2d, 0xbe, 0x77, 0x18, 0x1a, };
61
62 #define COUNT 2
63
64 static int
wait_for_read(int fd)65 wait_for_read(int fd)
66 {
67 struct pollfd pfd = { .fd = fd, .events = POLLIN };
68 int nfd;
69
70 nfd = poll(&pfd, 1, 5000);
71 if (nfd == -1) {
72 warn("failed: poll");
73 return -1;
74 }
75 if (nfd == 0) {
76 warnx("failed: timeout");
77 errno = ETIMEDOUT;
78 return -1;
79 }
80 if (nfd != 1 || (pfd.revents & POLLIN) == 0) {
81 warnx("failed: invalid poll: %d", nfd);
82 errno = EIO;
83 return -1;
84 }
85 return 0;
86 }
87
88 /*
89 * CRIOGET is deprecated.
90 */
91
92 /*
93 * CIOCNGSESSION
94 * Hmm, who uses? (1)
95 */
96 static int
test_ngsession(int fd)97 test_ngsession(int fd)
98 {
99 int ret;
100 struct crypt_sgop sg;
101 struct session_n_op css[COUNT];
102
103 for (size_t i = 0; i < COUNT; i++) {
104 struct session_n_op *cs = &css[i];
105
106 memset(cs, 0, sizeof(*cs));
107 cs->cipher = CRYPTO_AES_CBC;
108 cs->keylen = AES_KEY_LEN;
109 cs->key = __UNCONST(&aes_key);
110 }
111 memset(&sg, 0, sizeof(sg));
112 sg.count = COUNT;
113 sg.sessions = css;
114
115 ret = ioctl(fd, CIOCNGSESSION, &sg);
116 if (ret < 0)
117 warn("failed: CIOCNGSESSION");
118
119 return ret;
120 }
121
122 /*
123 * CIOCNFSESSION
124 * Hmm, who uses? (2)
125 */
126 static int
test_nfsession(int fd)127 test_nfsession(int fd)
128 {
129 int ret;
130 struct crypt_sfop sf;
131 u_int32_t sids[COUNT];
132
133 memset(sids, 0, sizeof(sids));
134 memset(&sf, 0, sizeof(sf));
135 sf.count = COUNT;
136 sf.sesid = sids;
137
138 ret = ioctl(fd, CIOCNFSESSION, &sf);
139 if (ret < 0)
140 warn("failed: CIOCNFSESSION");
141
142 return ret;
143 }
144
145 /*
146 * CIOCNCRYPTM
147 * Hmm, who uses? (3)
148 */
149 static int
test_ncryptm(int fd)150 test_ncryptm(int fd)
151 {
152 int ret;
153 struct crypt_mop mop;
154 struct crypt_n_op css[COUNT];
155
156 for (size_t i = 0; i < COUNT; i++) {
157 struct crypt_n_op *cs;
158 cs = &css[i];
159
160 memset(cs, 0, sizeof(*cs));
161 cs->ses = 0; /* session id */
162 cs->op = COP_ENCRYPT;
163 /* XXX */
164 }
165
166 memset(&mop, 0, sizeof(mop));
167 mop.count = COUNT;
168 mop.reqs = css;
169
170 ret = ioctl(fd, CIOCNCRYPTM, &mop);
171 if (ret < 0)
172 warn("failed: CIOCNCRYPTM");
173
174 return ret;
175 }
176
177 /*
178 * CIOCNCRYPTRETM
179 * Hmm, who uses? (4)
180 */
181 static int
test_ncryptretm(int fd)182 test_ncryptretm(int fd)
183 {
184 int ret;
185 struct session_op cs;
186
187 struct crypt_mop mop;
188 struct crypt_n_op cnos[COUNT];
189 unsigned char cno_dst[COUNT][AES_CIPHER_LEN];
190 struct cryptret cret;
191 struct crypt_result crs[COUNT];
192
193 memset(&cs, 0, sizeof(cs));
194 cs.cipher = CRYPTO_AES_CBC;
195 cs.keylen = AES_KEY_LEN;
196 cs.key = __UNCONST(&aes_key);
197 ret = ioctl(fd, CIOCGSESSION, &cs);
198 if (ret < 0) {
199 warn("failed: CIOCGSESSION");
200 return ret;
201 }
202
203 for (size_t i = 0; i < COUNT; i++) {
204 struct crypt_n_op *cno = &cnos[i];
205
206 memset(cno, 0, sizeof(*cno));
207 cno->ses = cs.ses;
208 cno->op = COP_ENCRYPT;
209 cno->len = AES_PLAINTX_LEN;
210 cno->src = aes_plaintx;
211 cno->dst_len = AES_CIPHER_LEN;
212 cno->dst = cno_dst[i];
213 }
214
215 memset(&mop, 0, sizeof(mop));
216 mop.count = COUNT;
217 mop.reqs = cnos;
218 ret = ioctl(fd, CIOCNCRYPTM, &mop);
219 if (ret < 0) {
220 warn("failed: CIOCNCRYPTM");
221 return ret;
222 }
223
224 for (size_t i = 0; i < COUNT; i++) {
225 struct crypt_result *cr = &crs[i];
226
227 memset(cr, 0, sizeof(*cr));
228 cr->reqid = cnos[i].reqid;
229 }
230
231 memset(&cret, 0, sizeof(cret));
232 cret.count = COUNT;
233 cret.results = crs;
234 ret = ioctl(fd, CIOCNCRYPTRETM, &cret);
235 if (ret < 0) {
236 if (errno != EINPROGRESS) {
237 warn("failed: CIOCNCRYPTRETM");
238 return ret;
239 }
240
241 ret = wait_for_read(fd);
242 if (ret < 0)
243 return ret;
244
245 cret.count = COUNT;
246 cret.results = crs;
247 ret = ioctl(fd, CIOCNCRYPTRETM, &cret);
248 if (ret < 0) {
249 warn("failed: CIOCNCRYPTRET");
250 return ret;
251 }
252 }
253
254 return ret;
255 }
256
257 /*
258 * CIOCNCRYPTRET
259 * Hmm, who uses? (5)
260 */
261 /* test when it does not request yet. */
262 static int
test_ncryptret_noent(int fd)263 test_ncryptret_noent(int fd)
264 {
265 int ret;
266 struct crypt_result cr;
267
268 memset(&cr, 0, sizeof(cr));
269
270 ret = ioctl(fd, CIOCNCRYPTRET, &cr);
271 if (ret == 0) {
272 warn("failed: CIOCNCRYPTRET unexpected success when no entry");
273 ret = -1;
274 } else if (errno == EINPROGRESS) {
275 /* expected fail */
276 ret = 0;
277 }
278
279 return ret;
280 }
281
282 static int
test_ncryptret_ent(int fd)283 test_ncryptret_ent(int fd)
284 {
285 int ret;
286 struct session_op cs;
287
288 struct crypt_mop mop;
289 struct crypt_n_op cno;
290 unsigned char cno_dst[AES_CIPHER_LEN];
291
292 struct crypt_result cr;
293
294 memset(&cs, 0, sizeof(cs));
295 cs.cipher = CRYPTO_AES_CBC;
296 cs.keylen = AES_KEY_LEN;
297 cs.key = __UNCONST(&aes_key);
298 ret = ioctl(fd, CIOCGSESSION, &cs);
299 if (ret < 0) {
300 warn("failed: CIOCGSESSION");
301 return ret;
302 }
303
304 memset(&cno, 0, sizeof(cno));
305 cno.ses = cs.ses;
306 cno.op = COP_ENCRYPT;
307 cno.len = AES_PLAINTX_LEN;
308 cno.src = aes_plaintx;
309 cno.dst_len = AES_CIPHER_LEN;
310 cno.dst = cno_dst;
311
312 memset(&mop, 0, sizeof(mop));
313 mop.count = 1;
314 mop.reqs = &cno;
315 ret = ioctl(fd, CIOCNCRYPTM, &mop);
316 if (ret < 0) {
317 warn("failed: CIOCNCRYPTM");
318 return ret;
319 }
320
321 memset(&cr, 0, sizeof(cr));
322 cr.reqid = cno.reqid;
323
324 ret = ioctl(fd, CIOCNCRYPTRET, &cr);
325 if (ret < 0) {
326 if (errno != EINPROGRESS) {
327 warn("failed: CIOCNCRYPTRET");
328 return ret;
329 }
330
331 ret = wait_for_read(fd);
332 if (ret < 0)
333 return ret;
334 ret = ioctl(fd, CIOCNCRYPTRET, &cr);
335 if (ret < 0) {
336 warn("failed: CIOCNCRYPTRET");
337 return ret;
338 }
339 return 0;
340 }
341
342 return ret;
343 }
344
345 static int
test_ncryptret(int fd)346 test_ncryptret(int fd)
347 {
348 int ret;
349
350 ret = test_ncryptret_noent(fd);
351 if (ret < 0)
352 return ret;
353
354 ret = test_ncryptret_ent(fd);
355 if (ret < 0)
356 return ret;
357
358 return ret;
359 }
360
361 /*
362 * CIOCASYMFEAT
363 */
364 static int
set_userasymcrypto(int new,int * old)365 set_userasymcrypto(int new, int *old)
366 {
367 int ret;
368
369 ret = sysctlbyname("kern.userasymcrypto", NULL, NULL, &new, sizeof(new));
370 if (ret < 0) {
371 warn("failed: kern.userasymcrypto=%d", new);
372 return ret;
373 }
374
375 if (old != NULL)
376 *old = new;
377
378 return ret;
379 }
380
381 static int
test_asymfeat_each(int fd,u_int32_t * asymfeat,int userasym)382 test_asymfeat_each(int fd, u_int32_t *asymfeat, int userasym)
383 {
384 int ret;
385
386 ret = ioctl(fd, CIOCASYMFEAT, asymfeat);
387 if (ret < 0)
388 warn("failed: CIOCASYMFEAT when userasym=%d", userasym);
389
390 return ret;
391 }
392
393 static int
test_asymfeat(int fd)394 test_asymfeat(int fd)
395 {
396 int ret, new, orig;
397 u_int32_t asymfeat = 0;
398
399 /* test for kern.userasymcrypto=1 */
400 new = 1;
401 ret = set_userasymcrypto(new, &orig);
402 if (ret < 0)
403 return ret;
404 ret = test_asymfeat_each(fd, &asymfeat, new);
405 if (ret < 0)
406 return ret;
407
408 /* test for kern.userasymcrypto=0 */
409 new = 0;
410 ret = set_userasymcrypto(new, NULL);
411 if (ret < 0)
412 return ret;
413 ret = test_asymfeat_each(fd, &asymfeat, new);
414 if (ret < 0)
415 return ret;
416
417 /* cleanup */
418 ret = set_userasymcrypto(orig, NULL);
419 if (ret < 0)
420 warnx("failed: cleanup kern.userasymcrypto");
421
422 return ret;
423 }
424
425 int
main(void)426 main(void)
427 {
428 int fd, ret;
429
430 fd = open("/dev/crypto", O_RDWR, 0);
431 if (fd < 0)
432 err(1, "open");
433
434 ret = test_ngsession(fd);
435 if (ret < 0)
436 err(1, "test_ngsession");
437
438 ret = test_nfsession(fd);
439 if (ret < 0)
440 err(1, "test_ngsession");
441
442 ret = test_ncryptm(fd);
443 if (ret < 0)
444 err(1, "test_ncryptm");
445
446 test_ncryptretm(fd);
447 if (ret < 0)
448 err(1, "test_ncryptretm");
449
450 ret = test_ncryptret(fd);
451 if (ret < 0)
452 err(1, "test_ncryptret");
453
454 if (getuid() == 0) {
455 ret = test_asymfeat(fd);
456 if (ret < 0)
457 err(1, "test_asymfeat");
458 }
459
460 return 0;
461 }
462