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