1 /*
2 * CDDL HEADER START
3 *
4 * The contents of this file are subject to the terms of the
5 * Common Development and Distribution License (the "License").
6 * You may not use this file except in compliance with the License.
7 *
8 * You can obtain a copy of the license at usr/src/OPENSOLARIS.LICENSE
9 * or http://www.opensolaris.org/os/licensing.
10 * See the License for the specific language governing permissions
11 * and limitations under the License.
12 *
13 * When distributing Covered Code, include this CDDL HEADER in each
14 * file and include the License file at usr/src/OPENSOLARIS.LICENSE.
15 * If applicable, add the following below this CDDL HEADER, with the
16 * fields enclosed by brackets "[]" replaced with your own identifying
17 * information: Portions Copyright [yyyy] [name of copyright owner]
18 *
19 * CDDL HEADER END
20 */
21 /*
22 * Copyright (c) 2003, 2010, Oracle and/or its affiliates. All rights reserved.
23 */
24 /*
25 * Copyright 2010 Nexenta Systems, Inc. All rights reserved.
26 * Copyright (c) 2018, Joyent, Inc.
27 */
28
29 #include <fcntl.h>
30 #include <stdio.h>
31 #include <stdlib.h>
32 #include <strings.h>
33 #include <unistd.h>
34 #include <locale.h>
35 #include <libgen.h>
36 #include <sys/types.h>
37 #include <sys/varargs.h>
38 #include <zone.h>
39 #include <sys/crypto/ioctladmin.h>
40 #include "cryptoadm.h"
41
42 #define DEFAULT_DEV_NUM 5
43 #define DEFAULT_SOFT_NUM 10
44
45 static crypto_get_soft_info_t *setup_get_soft_info(char *, int);
46
47 /*
48 * Prepare the argument for the LOAD_SOFT_CONFIG ioctl call for the
49 * provider pointed by pent. Return NULL if out of memory.
50 */
51 crypto_load_soft_config_t *
setup_soft_conf(entry_t * pent)52 setup_soft_conf(entry_t *pent)
53 {
54 crypto_load_soft_config_t *pload_soft_conf;
55 mechlist_t *plist;
56 uint_t sup_count;
57 size_t extra_mech_size = 0;
58 int i;
59
60 if (pent == NULL) {
61 return (NULL);
62 }
63
64 sup_count = pent->sup_count;
65 if (sup_count > 1) {
66 extra_mech_size = sizeof (crypto_mech_name_t) *
67 (sup_count - 1);
68 }
69
70 pload_soft_conf = malloc(sizeof (crypto_load_soft_config_t) +
71 extra_mech_size);
72 if (pload_soft_conf == NULL) {
73 cryptodebug("out of memory.");
74 return (NULL);
75 }
76
77 (void) strlcpy(pload_soft_conf->sc_name, pent->name, MAXNAMELEN);
78 pload_soft_conf->sc_count = sup_count;
79
80 i = 0;
81 plist = pent->suplist;
82 while (i < sup_count) {
83 (void) strlcpy(pload_soft_conf->sc_list[i++],
84 plist->name, CRYPTO_MAX_MECH_NAME);
85 plist = plist->next;
86 }
87
88 return (pload_soft_conf);
89 }
90
91
92 /*
93 * Prepare the argument for the LOAD_SOFT_DISABLED ioctl call for the
94 * provider pointed by pent. Return NULL if out of memory.
95 */
96 crypto_load_soft_disabled_t *
setup_soft_dis(entry_t * pent)97 setup_soft_dis(entry_t *pent)
98 {
99 crypto_load_soft_disabled_t *pload_soft_dis = NULL;
100 mechlist_t *plist = NULL;
101 size_t extra_mech_size = 0;
102 uint_t dis_count;
103 int i;
104
105 if (pent == NULL) {
106 return (NULL);
107 }
108
109 dis_count = pent->dis_count;
110 if (dis_count > 1) {
111 extra_mech_size = sizeof (crypto_mech_name_t) *
112 (dis_count - 1);
113 }
114
115 pload_soft_dis = malloc(sizeof (crypto_load_soft_disabled_t) +
116 extra_mech_size);
117 if (pload_soft_dis == NULL) {
118 cryptodebug("out of memory.");
119 return (NULL);
120 }
121
122 (void) strlcpy(pload_soft_dis->sd_name, pent->name, MAXNAMELEN);
123 pload_soft_dis->sd_count = dis_count;
124
125 i = 0;
126 plist = pent->dislist;
127 while (i < dis_count) {
128 (void) strlcpy(pload_soft_dis->sd_list[i++],
129 plist->name, CRYPTO_MAX_MECH_NAME);
130 plist = plist->next;
131 }
132
133 return (pload_soft_dis);
134 }
135
136
137 /*
138 * Prepare the argument for the LOAD_DEV_DISABLED ioctl call for the
139 * provider pointed by pent. Return NULL if out of memory.
140 */
141 crypto_load_dev_disabled_t *
setup_dev_dis(entry_t * pent)142 setup_dev_dis(entry_t *pent)
143 {
144 crypto_load_dev_disabled_t *pload_dev_dis = NULL;
145 mechlist_t *plist = NULL;
146 size_t extra_mech_size = 0;
147 uint_t dis_count;
148 int i;
149 char pname[MAXNAMELEN];
150 int inst_num;
151
152 if (pent == NULL) {
153 return (NULL);
154 }
155
156 /* get the device name and the instance number */
157 if (split_hw_provname(pent->name, pname, &inst_num) == FAILURE) {
158 return (NULL);
159 }
160
161 /* allocate space for pload_dev_des */
162 dis_count = pent->dis_count;
163 if (dis_count > 1) {
164 extra_mech_size = sizeof (crypto_mech_name_t) *
165 (dis_count - 1);
166 }
167
168 pload_dev_dis = malloc(sizeof (crypto_load_dev_disabled_t) +
169 extra_mech_size);
170 if (pload_dev_dis == NULL) {
171 cryptodebug("out of memory.");
172 return (NULL);
173 }
174
175 /* set the values for pload_dev_dis */
176 (void) strlcpy(pload_dev_dis->dd_dev_name, pname, MAXNAMELEN);
177 pload_dev_dis->dd_dev_instance = inst_num;
178 pload_dev_dis->dd_count = dis_count;
179
180 i = 0;
181 plist = pent->dislist;
182 while (i < dis_count) {
183 (void) strlcpy(pload_dev_dis->dd_list[i++],
184 plist->name, CRYPTO_MAX_MECH_NAME);
185 plist = plist->next;
186 }
187
188 return (pload_dev_dis);
189 }
190
191
192 /*
193 * Prepare the calling argument of the UNLOAD_SOFT_MODULE ioctl call for the
194 * provider pointed by pent. Return NULL if out of memory.
195 */
196 crypto_unload_soft_module_t *
setup_unload_soft(entry_t * pent)197 setup_unload_soft(entry_t *pent)
198 {
199 crypto_unload_soft_module_t *punload_soft;
200
201 if (pent == NULL) {
202 return (NULL);
203 }
204
205 punload_soft = malloc(sizeof (crypto_unload_soft_module_t));
206 if (punload_soft == NULL) {
207 cryptodebug("out of memory.");
208 return (NULL);
209 }
210
211 (void) strlcpy(punload_soft->sm_name, pent->name, MAXNAMELEN);
212
213 return (punload_soft);
214 }
215
216
217 /*
218 * Prepare the calling argument for the GET_SOFT_INFO call for the provider
219 * with the number of mechanisms specified in the second argument.
220 *
221 * Called by get_soft_info().
222 */
223 static crypto_get_soft_info_t *
setup_get_soft_info(char * provname,int count)224 setup_get_soft_info(char *provname, int count)
225 {
226 crypto_get_soft_info_t *psoft_info;
227 size_t extra_mech_size = 0;
228
229 if (provname == NULL) {
230 return (NULL);
231 }
232
233 if (count > 1) {
234 extra_mech_size = sizeof (crypto_mech_name_t) * (count - 1);
235 }
236
237 psoft_info = malloc(sizeof (crypto_get_soft_info_t) + extra_mech_size);
238 if (psoft_info == NULL) {
239 cryptodebug("out of memory.");
240 return (NULL);
241 }
242
243 (void) strlcpy(psoft_info->si_name, provname, MAXNAMELEN);
244 psoft_info->si_count = count;
245
246 return (psoft_info);
247 }
248
249
250 /*
251 * Get the device list from kernel.
252 */
253 int
get_dev_list(crypto_get_dev_list_t ** ppdevlist)254 get_dev_list(crypto_get_dev_list_t **ppdevlist)
255 {
256 crypto_get_dev_list_t *pdevlist;
257 int fd = -1;
258 int count = DEFAULT_DEV_NUM;
259
260 pdevlist = malloc(sizeof (crypto_get_dev_list_t) +
261 sizeof (crypto_dev_list_entry_t) * (count - 1));
262 if (pdevlist == NULL) {
263 cryptodebug("out of memory.");
264 return (FAILURE);
265 }
266
267 if ((fd = open(ADMIN_IOCTL_DEVICE, O_RDONLY)) == -1) {
268 cryptoerror(LOG_STDERR, gettext("failed to open %s: %s"),
269 ADMIN_IOCTL_DEVICE, strerror(errno));
270 free(pdevlist);
271 return (FAILURE);
272 }
273
274 pdevlist->dl_dev_count = count;
275 if (ioctl(fd, CRYPTO_GET_DEV_LIST, pdevlist) == -1) {
276 cryptodebug("CRYPTO_GET_DEV_LIST ioctl failed: %s",
277 strerror(errno));
278 free(pdevlist);
279 (void) close(fd);
280 return (FAILURE);
281 }
282
283 /* BUFFER is too small, get the number of devices and retry it. */
284 if (pdevlist->dl_return_value == CRYPTO_BUFFER_TOO_SMALL) {
285 count = pdevlist->dl_dev_count;
286 free(pdevlist);
287 pdevlist = malloc(sizeof (crypto_get_dev_list_t) +
288 sizeof (crypto_dev_list_entry_t) * (count - 1));
289 if (pdevlist == NULL) {
290 cryptodebug("out of memory.");
291 (void) close(fd);
292 return (FAILURE);
293 }
294
295 if (ioctl(fd, CRYPTO_GET_DEV_LIST, pdevlist) == -1) {
296 cryptodebug("CRYPTO_GET_DEV_LIST ioctl failed: %s",
297 strerror(errno));
298 free(pdevlist);
299 (void) close(fd);
300 return (FAILURE);
301 }
302 }
303
304 if (pdevlist->dl_return_value != CRYPTO_SUCCESS) {
305 cryptodebug("CRYPTO_GET_DEV_LIST ioctl failed, "
306 "return_value = %d", pdevlist->dl_return_value);
307 free(pdevlist);
308 (void) close(fd);
309 return (FAILURE);
310 }
311
312 *ppdevlist = pdevlist;
313 (void) close(fd);
314 return (SUCCESS);
315 }
316
317
318 /*
319 * Get all the mechanisms supported by the hardware provider.
320 * The result will be stored in the second argument.
321 */
322 int
get_dev_info(char * devname,int inst_num,int count,mechlist_t ** ppmechlist)323 get_dev_info(char *devname, int inst_num, int count, mechlist_t **ppmechlist)
324 {
325 crypto_get_dev_info_t *dev_info;
326 mechlist_t *phead;
327 mechlist_t *pcur;
328 mechlist_t *pmech;
329 int fd = -1;
330 int i;
331 int rc;
332
333 if (devname == NULL || count < 1) {
334 cryptodebug("get_dev_info(): devname is NULL or bogus count");
335 return (FAILURE);
336 }
337
338 /* Set up the argument for the CRYPTO_GET_DEV_INFO ioctl call */
339 dev_info = malloc(sizeof (crypto_get_dev_info_t) +
340 sizeof (crypto_mech_name_t) * (count - 1));
341 if (dev_info == NULL) {
342 cryptodebug("out of memory.");
343 return (FAILURE);
344 }
345 (void) strlcpy(dev_info->di_dev_name, devname, MAXNAMELEN);
346 dev_info->di_dev_instance = inst_num;
347 dev_info->di_count = count;
348
349 /* Open the ioctl device */
350 if ((fd = open(ADMIN_IOCTL_DEVICE, O_RDONLY)) == -1) {
351 cryptoerror(LOG_STDERR, gettext("failed to open %s: %s"),
352 ADMIN_IOCTL_DEVICE, strerror(errno));
353 free(dev_info);
354 return (FAILURE);
355 }
356
357 if (ioctl(fd, CRYPTO_GET_DEV_INFO, dev_info) == -1) {
358 cryptodebug("CRYPTO_GET_DEV_INFO ioctl failed: %s",
359 strerror(errno));
360 free(dev_info);
361 (void) close(fd);
362 return (FAILURE);
363 }
364
365 if (dev_info->di_return_value != CRYPTO_SUCCESS) {
366 cryptodebug("CRYPTO_GET_DEV_INFO ioctl failed, "
367 "return_value = %d", dev_info->di_return_value);
368 free(dev_info);
369 (void) close(fd);
370 return (FAILURE);
371 }
372
373 phead = pcur = NULL;
374 rc = SUCCESS;
375 for (i = 0; i < dev_info->di_count; i++) {
376 pmech = create_mech(&dev_info->di_list[i][0]);
377 if (pmech == NULL) {
378 rc = FAILURE;
379 break;
380 } else {
381 if (phead == NULL) {
382 phead = pcur = pmech;
383 } else {
384 pcur->next = pmech;
385 pcur = pmech;
386 }
387 }
388 }
389
390 if (rc == SUCCESS) {
391 *ppmechlist = phead;
392 } else {
393 free_mechlist(phead);
394 }
395
396 free(dev_info);
397 (void) close(fd);
398 return (rc);
399 }
400
401
402 /*
403 * Get the supported mechanism list of the software provider from kernel.
404 *
405 * Parameters phardlist and psoftlist are supplied by get_kcfconf_info().
406 * If NULL, this function calls get_kcfconf_info() internally.
407 */
408 int
get_soft_info(char * provname,mechlist_t ** ppmechlist,entrylist_t * phardlist,entrylist_t * psoftlist)409 get_soft_info(char *provname, mechlist_t **ppmechlist,
410 entrylist_t *phardlist, entrylist_t *psoftlist)
411 {
412 boolean_t in_kernel = B_FALSE;
413 crypto_get_soft_info_t *psoft_info;
414 mechlist_t *phead;
415 mechlist_t *pmech;
416 mechlist_t *pcur;
417 entry_t *pent = NULL;
418 int count;
419 int fd = -1;
420 int rc;
421 int i;
422
423 if (provname == NULL) {
424 return (FAILURE);
425 }
426
427 if (getzoneid() == GLOBAL_ZONEID) {
428 /* use kcf.conf for kernel software providers in global zone */
429 if ((pent = getent_kef(provname, phardlist, psoftlist)) ==
430 NULL) {
431
432 /* No kcf.conf entry for this provider */
433 if (check_kernel_for_soft(provname, NULL, &in_kernel)
434 == FAILURE) {
435 return (FAILURE);
436 } else if (in_kernel == B_FALSE) {
437 cryptoerror(LOG_STDERR,
438 gettext("%s does not exist."), provname);
439 return (FAILURE);
440 }
441
442 /*
443 * Set mech count to 1. It will be reset to the
444 * correct value later if the setup buffer is too small.
445 */
446 count = 1;
447 } else {
448 count = pent->sup_count;
449 free_entry(pent);
450 }
451 } else {
452 /*
453 * kcf.conf not there in non-global zone: set mech count to 1.
454 * It will be reset to the correct value later if the setup
455 * buffer is too small.
456 */
457 count = 1;
458 }
459
460 if ((psoft_info = setup_get_soft_info(provname, count)) == NULL) {
461 return (FAILURE);
462 }
463
464 if ((fd = open(ADMIN_IOCTL_DEVICE, O_RDONLY)) == -1) {
465 cryptoerror(LOG_STDERR, gettext("failed to open %s: %s"),
466 ADMIN_IOCTL_DEVICE, strerror(errno));
467 free(psoft_info);
468 return (FAILURE);
469 }
470
471 /* make GET_SOFT_INFO ioctl call */
472 if ((rc = ioctl(fd, CRYPTO_GET_SOFT_INFO, psoft_info)) == -1) {
473 cryptodebug("CRYPTO_GET_SOFT_INFO ioctl failed: %s",
474 strerror(errno));
475 (void) close(fd);
476 free(psoft_info);
477 return (FAILURE);
478 }
479
480 /* BUFFER is too small, get the number of mechanisms and retry it. */
481 if (psoft_info->si_return_value == CRYPTO_BUFFER_TOO_SMALL) {
482 count = psoft_info->si_count;
483 free(psoft_info);
484 if ((psoft_info = setup_get_soft_info(provname, count))
485 == NULL) {
486 (void) close(fd);
487 return (FAILURE);
488 } else {
489 rc = ioctl(fd, CRYPTO_GET_SOFT_INFO, psoft_info);
490 if (rc == -1) {
491 cryptodebug("CRYPTO_GET_SOFT_INFO ioctl "
492 "failed: %s", strerror(errno));
493 (void) close(fd);
494 free(psoft_info);
495 return (FAILURE);
496 }
497 }
498 }
499
500 (void) close(fd);
501 if (psoft_info->si_return_value != CRYPTO_SUCCESS) {
502 cryptodebug("CRYPTO_GET_SOFT_INFO ioctl failed, "
503 "return_value = %d", psoft_info->si_return_value);
504 free(psoft_info);
505 return (FAILURE);
506 }
507
508
509 /* Build the mechanism linked list and return it */
510 rc = SUCCESS;
511 phead = pcur = NULL;
512 for (i = 0; i < psoft_info->si_count; i++) {
513 pmech = create_mech(&psoft_info->si_list[i][0]);
514 if (pmech == NULL) {
515 rc = FAILURE;
516 break;
517 } else {
518 if (phead == NULL) {
519 phead = pcur = pmech;
520 } else {
521 pcur->next = pmech;
522 pcur = pmech;
523 }
524 }
525 }
526
527 if (rc == FAILURE) {
528 free_mechlist(phead);
529 } else {
530 *ppmechlist = phead;
531 }
532
533 free(psoft_info);
534 return (rc);
535 }
536
537
538 /*
539 * Get the kernel software provider list from kernel.
540 */
541 int
get_soft_list(crypto_get_soft_list_t ** ppsoftlist)542 get_soft_list(crypto_get_soft_list_t **ppsoftlist)
543 {
544 crypto_get_soft_list_t *psoftlist = NULL;
545 int count = DEFAULT_SOFT_NUM;
546 int len;
547 int fd = -1;
548
549 if ((fd = open(ADMIN_IOCTL_DEVICE, O_RDONLY)) == -1) {
550 cryptoerror(LOG_STDERR, gettext("failed to open %s: %s"),
551 ADMIN_IOCTL_DEVICE, strerror(errno));
552 return (FAILURE);
553 }
554
555 len = MAXNAMELEN * count;
556 psoftlist = malloc(sizeof (crypto_get_soft_list_t) + len);
557 if (psoftlist == NULL) {
558 cryptodebug("out of memory.");
559 (void) close(fd);
560 return (FAILURE);
561 }
562 psoftlist->sl_soft_names = (caddr_t)(psoftlist + 1);
563 psoftlist->sl_soft_count = count;
564 psoftlist->sl_soft_len = len;
565
566 if (ioctl(fd, CRYPTO_GET_SOFT_LIST, psoftlist) == -1) {
567 cryptodebug("CRYPTO_GET_SOFT_LIST ioctl failed: %s",
568 strerror(errno));
569 free(psoftlist);
570 (void) close(fd);
571 return (FAILURE);
572 }
573
574 /*
575 * if BUFFER is too small, get the number of software providers and
576 * the minimum length needed for names and length and retry it.
577 */
578 if (psoftlist->sl_return_value == CRYPTO_BUFFER_TOO_SMALL) {
579 count = psoftlist->sl_soft_count;
580 len = psoftlist->sl_soft_len;
581 free(psoftlist);
582 psoftlist = malloc(sizeof (crypto_get_soft_list_t) + len);
583 if (psoftlist == NULL) {
584 cryptodebug("out of memory.");
585 (void) close(fd);
586 return (FAILURE);
587 }
588 psoftlist->sl_soft_names = (caddr_t)(psoftlist + 1);
589 psoftlist->sl_soft_count = count;
590 psoftlist->sl_soft_len = len;
591
592 if (ioctl(fd, CRYPTO_GET_SOFT_LIST, psoftlist) == -1) {
593 cryptodebug("CRYPTO_GET_SOFT_LIST ioctl failed:"
594 "%s", strerror(errno));
595 free(psoftlist);
596 (void) close(fd);
597 return (FAILURE);
598 }
599 }
600
601 if (psoftlist->sl_return_value != CRYPTO_SUCCESS) {
602 cryptodebug("CRYPTO_GET_SOFT_LIST ioctl failed, "
603 "return_value = %d", psoftlist->sl_return_value);
604 free(psoftlist);
605 (void) close(fd);
606 return (FAILURE);
607 }
608
609 *ppsoftlist = psoftlist;
610 (void) close(fd);
611 return (SUCCESS);
612 }
613