xref: /illumos-gate/usr/src/cmd/cmd-crypto/cryptoadm/adm_kef_ioctl.c (revision b6805bf78d2bbbeeaea8909a05623587b42d58b3)
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 *
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 *
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 *
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 *
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 *
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
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
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
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
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