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