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 45unsigned 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 50unsigned 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 55unsigned char aes_plaintx[AES_PLAINTX_LEN] = "Single block msg"; 56 57#define AES_CIPHER_LEN 64 58unsigned 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 64static int 65wait_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 */ 96static int 97test_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 */ 126static int 127test_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 */ 149static int 150test_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 */ 181static int 182test_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. */ 262static int 263test_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 282static int 283test_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 345static int 346test_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 */ 364static int 365set_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 381static int 382test_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 393static int 394test_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 425int 426main(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