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