xref: /illumos-gate/usr/src/cmd/cmd-crypto/cryptoadm/adm_kef_util.c (revision d8d810637042c2a73a269f125dee75e8ed1421d1)
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 2009 Sun Microsystems, Inc.  All rights reserved.
23  * Use is subject to license terms.
24  */
25 
26 #include <errno.h>
27 #include <fcntl.h>
28 #include <stdio.h>
29 #include <stdlib.h>
30 #include <strings.h>
31 #include <time.h>
32 #include <unistd.h>
33 #include <locale.h>
34 #include <sys/types.h>
35 #include <zone.h>
36 #include <sys/stat.h>
37 #include "cryptoadm.h"
38 #include <cryptoutil.h>
39 
40 static int err; /* To store errno which may be overwritten by gettext() */
41 static int build_entrylist(entry_t *, entrylist_t **);
42 static entry_t *dup_entry(entry_t *);
43 static mechlist_t *dup_mechlist(mechlist_t *);
44 static entry_t *getent(char *, entrylist_t *);
45 static int interpret(char *, entry_t **);
46 static int parse_sup_dis_list(char *, entry_t *);
47 
48 
49 /*
50  * Duplicate the mechanism list.  A null pointer is returned if the storage
51  * space available is insufficient or the input argument is NULL.
52  */
53 static mechlist_t *
54 dup_mechlist(mechlist_t *plist)
55 {
56 	mechlist_t	*pres = NULL;
57 	mechlist_t	*pcur;
58 	mechlist_t	*ptmp;
59 	int		rc = SUCCESS;
60 
61 	while (plist != NULL) {
62 		if (!(ptmp = create_mech(plist->name))) {
63 			rc = FAILURE;
64 			break;
65 		}
66 
67 		if (pres == NULL) {
68 			pres = pcur = ptmp;
69 		} else {
70 			pcur->next = ptmp;
71 			pcur = pcur->next;
72 		}
73 		plist = plist->next;
74 	}
75 
76 	if (rc != SUCCESS) {
77 		free_mechlist(pres);
78 		return (NULL);
79 	}
80 
81 	return (pres);
82 }
83 
84 
85 /*
86  * Get the number of mechanisms in the mechanism list.
87  */
88 int
89 get_mech_count(mechlist_t *plist)
90 {
91 	int count = 0;
92 
93 	while (plist != NULL) {
94 		count++;
95 		plist = plist->next;
96 	}
97 	return (count);
98 }
99 
100 /*
101  * Create one item of type entry_t with the provider name.
102  * Return NULL if there's not enough memory or provname is NULL.
103  */
104 entry_t *
105 create_entry(char *provname)
106 {
107 	entry_t		*pent = NULL;
108 
109 	if (provname == NULL) {
110 		return (NULL);
111 	}
112 
113 	pent = calloc(1, sizeof (entry_t));
114 	if (pent == NULL) {
115 		cryptodebug("out of memory.");
116 		return (NULL);
117 	}
118 
119 	(void) strlcpy(pent->name, provname, MAXNAMELEN);
120 	pent->suplist = NULL;
121 	pent->sup_count = 0;
122 	pent->dislist = NULL;
123 	pent->dis_count = 0;
124 	pent->load = B_TRUE;
125 
126 	return (pent);
127 }
128 
129 /*
130  * Duplicate an entry for a provider from kcf.conf.
131  * Return NULL if memory is insufficient or the input argument is NULL.
132  * Called by getent().
133  */
134 static entry_t *
135 dup_entry(entry_t *pent1)
136 {
137 	entry_t	*pent2 = NULL;
138 
139 	if (pent1 == NULL) {
140 		return (NULL);
141 	}
142 
143 	if ((pent2 = create_entry(pent1->name)) == NULL) {
144 		cryptodebug("out of memory.");
145 		return (NULL);
146 	}
147 
148 	pent2->flag_fips_enabled = pent1->flag_fips_enabled;
149 	pent2->sup_count = pent1->sup_count;
150 	pent2->dis_count = pent1->dis_count;
151 	pent2->load = pent1->load;
152 	if (pent1->suplist != NULL) {
153 		pent2->suplist = dup_mechlist(pent1->suplist);
154 		if (pent2->suplist == NULL) {
155 			free_entry(pent2);
156 			return (NULL);
157 		}
158 	}
159 	if (pent1->dislist != NULL) {
160 		pent2->dislist = dup_mechlist(pent1->dislist);
161 		if (pent2->dislist == NULL) {
162 			free_entry(pent2);
163 			return (NULL);
164 		}
165 	}
166 
167 	return (pent2);
168 }
169 
170 
171 /*
172  * This routine parses the disabledlist or the supportedlist of an entry
173  * in the kcf.conf configuration file.
174  *
175  * Arguments:
176  *	buf: an input argument which is a char string with the format of
177  *	     "disabledlist=m1,m2,..." or "supportedlist=m1,m2,..."
178  *	pent: the entry for the disabledlist.  This is an IN/OUT argument.
179  *
180  * Return value: SUCCESS or FAILURE.
181  */
182 static int
183 parse_sup_dis_list(char *buf, entry_t *pent)
184 {
185 	mechlist_t	*pmech = NULL;
186 	mechlist_t	*phead = NULL;
187 	char		*next_token;
188 	char		*value;
189 	int		count;
190 	int		supflag = B_FALSE;
191 	int		disflag = B_FALSE;
192 	int		rc = SUCCESS;
193 
194 	if (strncmp(buf, EF_SUPPORTED, strlen(EF_SUPPORTED)) == 0) {
195 		supflag = B_TRUE;
196 	} else if (strncmp(buf, EF_DISABLED, strlen(EF_DISABLED)) == 0) {
197 		disflag = B_TRUE;
198 	} else {
199 		/* should not come here */
200 		return (FAILURE);
201 	}
202 
203 	if (value = strpbrk(buf, SEP_EQUAL)) {
204 		value++; /* get rid of = */
205 	} else {
206 		cryptodebug("failed to parse the kcf.conf file.");
207 		return (FAILURE);
208 	}
209 
210 	if ((next_token = strtok(value, SEP_COMMA)) == NULL) {
211 		cryptodebug("failed to parse the kcf.conf file.");
212 		return (FAILURE);
213 	}
214 
215 	if ((pmech = create_mech(next_token)) == NULL) {
216 		return (FAILURE);
217 	}
218 
219 	if (supflag) {
220 		pent->suplist = phead = pmech;
221 	} else if (disflag) {
222 		pent->dislist = phead = pmech;
223 	}
224 
225 	count = 1;
226 	while (next_token) {
227 		if (next_token = strtok(NULL, SEP_COMMA)) {
228 			if ((pmech = create_mech(next_token)) == NULL) {
229 				rc = FAILURE;
230 				break;
231 			}
232 			count++;
233 			phead->next = pmech;
234 			phead = phead->next;
235 		}
236 	}
237 
238 	if (rc == SUCCESS) {
239 		if (supflag) {
240 			pent->sup_count = count;
241 		} else if (disflag) {
242 			pent->dis_count = count;
243 		}
244 	} else {
245 		free_mechlist(phead);
246 	}
247 
248 	return (rc);
249 }
250 
251 static int
252 parse_fips(char *buf, entry_t *pent)
253 {
254 	char *value;
255 
256 	if (strncmp(buf, EF_FIPS_STATUS, sizeof (EF_FIPS_STATUS) - 1) == 0) {
257 		if (value = strpbrk(buf, SEP_EQUAL)) {
258 			value++; /* get rid of = */
259 			if (strcmp(value, DISABLED_KEYWORD) == 0) {
260 				pent->flag_fips_enabled = B_FALSE;
261 			} else if (strcmp(value, ENABLED_KEYWORD) == 0) {
262 				pent->flag_fips_enabled = B_TRUE;
263 			} else {
264 				cryptoerror(LOG_ERR, gettext(
265 				    "Failed to parse kcf.conf file.\n"));
266 				return (FAILURE);
267 			}
268 			return (SUCCESS);
269 		} else {
270 			return (FAILURE);
271 		}
272 	} else {
273 		/* should not come here */
274 		cryptoerror(LOG_ERR, gettext(
275 		    "Failed to parse kcf.conf file.\n"));
276 		return (FAILURE);
277 	}
278 
279 }
280 
281 /*
282  * Convert a char string containing a line about a provider
283  * from kcf.conf into an entry_t structure.
284  *
285  * See ent2str(), the reverse of this function, for the format of
286  * kcf.conf lines.
287  */
288 static int
289 interpret(char *buf, entry_t **ppent)
290 {
291 	entry_t	*pent = NULL;
292 	char	*token1;
293 	char	*token2;
294 	char	*token3;
295 	int	rc;
296 
297 	/* Get provider name */
298 	if ((token1 = strtok(buf, SEP_COLON)) == NULL) { /* buf is NULL */
299 		return (FAILURE);
300 	};
301 
302 	pent = create_entry(token1);
303 	if (pent == NULL) {
304 		cryptodebug("out of memory.");
305 		return (FAILURE);
306 	}
307 
308 	if (is_fips(token1)) {
309 		if ((rc = parse_fips(buf + strlen(token1) + 1,
310 		    pent)) != SUCCESS) {
311 			free_entry(pent);
312 		}
313 		*ppent = pent;
314 		return (rc);
315 	}
316 
317 	if ((token2 = strtok(NULL, SEP_SEMICOLON)) == NULL) {
318 		/* The entry contains a provider name only */
319 		free_entry(pent);
320 		return (FAILURE);
321 	}
322 
323 	if (strncmp(token2, EF_UNLOAD, strlen(EF_UNLOAD)) == 0) {
324 		pent->load = B_FALSE; /* cryptoadm unload */
325 		if ((token2 = strtok(NULL, SEP_SEMICOLON)) == NULL) {
326 			/* The entry contains a provider name:unload only */
327 			free_entry(pent);
328 			return (FAILURE);
329 		}
330 	}
331 
332 	/* need to get token3 first to satisfy nested strtok invocations */
333 	token3 = strtok(NULL, SEP_SEMICOLON); /* optional */
334 
335 	/* parse supportedlist (or disabledlist if no supportedlist) */
336 	if ((token2 != NULL) && ((rc = parse_sup_dis_list(token2, pent)) !=
337 	    SUCCESS)) {
338 		free_entry(pent);
339 		return (rc);
340 	}
341 
342 	/* parse disabledlist (if there's a supportedlist) */
343 	if ((token3 != NULL) && ((rc = parse_sup_dis_list(token3, pent)) !=
344 	    SUCCESS)) {
345 		free_entry(pent);
346 		return (rc);
347 	}
348 
349 	*ppent = pent;
350 	return (SUCCESS);
351 }
352 
353 
354 /*
355  * Add an entry about a provider from kcf.conf to the end of an entry list.
356  * If the entry list pplist is NULL, create the linked list with pent as the
357  * first element.
358  */
359 static int
360 build_entrylist(entry_t *pent, entrylist_t **pplist)
361 {
362 	entrylist_t	*pentlist;
363 	entrylist_t	*pcur = NULL;
364 
365 	pentlist = malloc(sizeof (entrylist_t));
366 	if (pentlist == NULL) {
367 		cryptodebug("out of memory.");
368 		return (FAILURE);
369 	}
370 	pentlist->pent = pent;
371 	pentlist->next = NULL;
372 
373 	if (*pplist) {
374 		pcur = *pplist;
375 		while (pcur->next != NULL)
376 			pcur = pcur->next;
377 		pcur->next = pentlist;
378 	} else { /* empty list */
379 		*pplist = pentlist;
380 	}
381 
382 	return (SUCCESS);
383 }
384 
385 
386 
387 /*
388  * Find the entry with the "provname" name from the entry list and duplicate
389  * it.  Called by getent_kef().
390  */
391 static entry_t *
392 getent(char *provname, entrylist_t *entrylist)
393 {
394 	boolean_t	found = B_FALSE;
395 	entry_t		*pent1 = NULL;
396 
397 	if ((provname == NULL) || (entrylist == NULL)) {
398 		return (NULL);
399 	}
400 
401 	while (!found && entrylist) {
402 		if (strcmp(entrylist->pent->name, provname) == 0) {
403 			found = B_TRUE;
404 			pent1 = entrylist->pent;
405 		} else {
406 			entrylist = entrylist->next;
407 		}
408 	}
409 
410 	if (!found) {
411 		return (NULL);
412 	}
413 
414 	/* duplicate the entry to be returned */
415 	return (dup_entry(pent1));
416 }
417 
418 
419 /*
420  * Free memory in entry_t.
421  * That is, the supported and disabled lists for a provider
422  * from kcf.conf.
423  */
424 void
425 free_entry(entry_t  *pent)
426 {
427 	if (pent == NULL) {
428 		return;
429 	} else {
430 		free_mechlist(pent->suplist);
431 		free_mechlist(pent->dislist);
432 		free(pent);
433 	}
434 }
435 
436 
437 /*
438  * Free elements in a entrylist_t linked list,
439  * which lists providers in kcf.conf.
440  */
441 void
442 free_entrylist(entrylist_t *entrylist)
443 {
444 	entrylist_t *pnext;
445 
446 	while (entrylist != NULL) {
447 		pnext = entrylist->next;
448 		free_entry(entrylist->pent);
449 		entrylist = pnext;
450 	}
451 }
452 
453 
454 /*
455  * Convert an entry to a string.  This routine builds a string for the entry
456  * to be inserted in the kcf.conf file.  Based on the content of each entry,
457  * the result string can be one of these 6 forms:
458  *  - name:supportedlist=m1,m2,...,mj
459  *  - name:disabledlist=m1,m2,...,mj
460  *  - name:supportedlist=m1,...,mj;disabledlist=m1,m2,...,mk
461  *
462  *  - name:unload;supportedlist=m1,m2,...,mj
463  *  - name:unload;disabledlist=m1,m2,...,mj
464  *  - name:unload;supportedlist=m1,...,mj;disabledlist=m1,m2,...,mk
465  *
466  * Note that the caller is responsible for freeing the returned string
467  * (with free_entry()).
468  * See interpret() for the reverse of this function: converting a string
469  * to an entry_t.
470  */
471 char *
472 ent2str(entry_t *pent)
473 {
474 	char		*buf;
475 	mechlist_t	*pcur = NULL;
476 	boolean_t	semicolon_separator = B_FALSE;
477 
478 
479 	if (pent == NULL) {
480 		return (NULL);
481 	}
482 
483 	if ((buf = malloc(BUFSIZ)) == NULL) {
484 		return (NULL);
485 	}
486 
487 	/* convert the provider name */
488 	if (strlcpy(buf, pent->name, BUFSIZ) >= BUFSIZ) {
489 		free(buf);
490 		return (NULL);
491 	}
492 
493 	if (!pent->load) { /* add "unload" keyword */
494 		if (strlcat(buf, SEP_COLON, BUFSIZ) >= BUFSIZ) {
495 			free(buf);
496 			return (NULL);
497 		}
498 
499 		if (strlcat(buf, EF_UNLOAD, BUFSIZ) >= BUFSIZ) {
500 			free(buf);
501 			return (NULL);
502 		}
503 
504 		semicolon_separator = B_TRUE;
505 	}
506 
507 	/* convert the supported list if any */
508 	pcur = pent->suplist;
509 	if (pcur != NULL) {
510 		if (strlcat(buf,
511 		    semicolon_separator ? SEP_SEMICOLON : SEP_COLON,
512 		    BUFSIZ) >= BUFSIZ) {
513 			free(buf);
514 			return (NULL);
515 		}
516 
517 		if (strlcat(buf, EF_SUPPORTED, BUFSIZ) >= BUFSIZ) {
518 			free(buf);
519 			return (NULL);
520 		}
521 
522 		while (pcur != NULL) {
523 			if (strlcat(buf, pcur->name, BUFSIZ) >= BUFSIZ) {
524 				free(buf);
525 				return (NULL);
526 			}
527 
528 			pcur = pcur->next;
529 			if (pcur != NULL) {
530 				if (strlcat(buf, SEP_COMMA, BUFSIZ)
531 				    >= BUFSIZ) {
532 					free(buf);
533 					return (NULL);
534 				}
535 			}
536 		}
537 		semicolon_separator = B_TRUE;
538 	}
539 
540 	/* convert the disabled list if any */
541 	pcur = pent->dislist;
542 	if (pcur != NULL) {
543 		if (strlcat(buf,
544 		    semicolon_separator ? SEP_SEMICOLON : SEP_COLON,
545 		    BUFSIZ) >= BUFSIZ) {
546 			free(buf);
547 			return (NULL);
548 		}
549 
550 		if (strlcat(buf, EF_DISABLED, BUFSIZ) >= BUFSIZ) {
551 			free(buf);
552 			return (NULL);
553 		}
554 
555 		while (pcur != NULL) {
556 			if (strlcat(buf, pcur->name, BUFSIZ) >= BUFSIZ) {
557 				free(buf);
558 				return (NULL);
559 			}
560 
561 			pcur = pcur->next;
562 			if (pcur != NULL) {
563 				if (strlcat(buf, SEP_COMMA, BUFSIZ)
564 				    >= BUFSIZ) {
565 					free(buf);
566 					return (NULL);
567 				}
568 			}
569 		}
570 		semicolon_separator = B_TRUE;
571 	}
572 
573 	if (strlcat(buf, "\n", BUFSIZ) >= BUFSIZ) {
574 		free(buf);
575 		return (NULL);
576 	}
577 
578 	return (buf);
579 }
580 
581 
582 /*
583  * Enable the mechanisms for the provider pointed by *ppent.  If allflag is
584  * TRUE, enable all.  Otherwise, enable the mechanisms specified in the 3rd
585  * argument "mlist".  The result will be stored in ppent also.
586  */
587 int
588 enable_mechs(entry_t **ppent, boolean_t allflag, mechlist_t *mlist)
589 {
590 	entry_t		*pent;
591 	mechlist_t	*phead; /* the current and resulting disabled list */
592 	mechlist_t	*ptr = NULL;
593 	mechlist_t	*pcur = NULL;
594 	boolean_t	found;
595 
596 	pent = *ppent;
597 	if (pent == NULL) {
598 		return (FAILURE);
599 	}
600 
601 	if (allflag) {
602 		free_mechlist(pent->dislist);
603 		pent->dis_count = 0;
604 		pent->dislist = NULL;
605 		return (SUCCESS);
606 	}
607 
608 	/*
609 	 * for each mechanism in the to-be-enabled mechanism list,
610 	 * -	check if it is in the current disabled list
611 	 * -	if found, delete it from the disabled list
612 	 *	otherwise, give a warning.
613 	 */
614 	ptr = mlist;
615 	while (ptr != NULL) {
616 		found = B_FALSE;
617 		phead = pcur =  pent->dislist;
618 		while (!found && pcur) {
619 			if (strcmp(pcur->name, ptr->name) == 0) {
620 				found = B_TRUE;
621 			} else {
622 				phead = pcur;
623 				pcur = pcur->next;
624 			}
625 		}
626 
627 		if (found) {
628 			if (phead == pcur) {
629 				pent->dislist = pent->dislist->next;
630 				free(pcur);
631 			} else {
632 				phead->next = pcur->next;
633 				free(pcur);
634 			}
635 			pent->dis_count--;
636 		} else {
637 			cryptoerror(LOG_STDERR, gettext(
638 			    "(Warning) %1$s is either enabled already or not "
639 			    "a valid mechanism for %2$s"), ptr->name,
640 			    pent->name);
641 		}
642 		ptr = ptr->next;
643 	}
644 
645 	if (pent->dis_count == 0) {
646 		pent->dislist = NULL;
647 	}
648 
649 	return (SUCCESS);
650 
651 }
652 
653 
654 /*
655  * Determine if the kernel provider name, path, is a device
656  * (that is, it contains a slash character (e.g., "mca/0").
657  * If so, it is a hardware provider; otherwise it is a software provider.
658  */
659 boolean_t
660 is_device(char *path)
661 {
662 	if (strchr(path, SEP_SLASH) != NULL) {
663 		return (B_TRUE);
664 	} else {
665 		return (B_FALSE);
666 	}
667 }
668 
669 boolean_t
670 is_fips(char *name)
671 {
672 	if (strcmp(name, FIPS_KEYWORD) == 0) {
673 		return (B_TRUE);
674 	} else {
675 		return (B_FALSE);
676 	}
677 }
678 
679 /*
680  * Split a hardware provider name with the "name/inst_num" format into
681  * a name and a number (e.g., split "mca/0" into "mca" instance 0).
682  */
683 int
684 split_hw_provname(char *provname, char *pname, int *inst_num)
685 {
686 	char	name[MAXNAMELEN];
687 	char	*inst_str;
688 
689 	if (provname == NULL) {
690 		return (FAILURE);
691 	}
692 
693 	(void) strlcpy(name, provname, MAXNAMELEN);
694 	if (strtok(name, "/") == NULL) {
695 		return (FAILURE);
696 	}
697 
698 	if ((inst_str = strtok(NULL, "/")) == NULL) {
699 		return (FAILURE);
700 	}
701 
702 	(void) strlcpy(pname, name, MAXNAMELEN);
703 	*inst_num = atoi(inst_str);
704 
705 	return (SUCCESS);
706 }
707 
708 
709 /*
710  * Retrieve information from kcf.conf and build a hardware device entry list
711  * and a software entry list of kernel crypto providers.
712  *
713  * This list is usually incomplete, as kernel crypto providers only have to
714  * be listed in kcf.conf if a mechanism is disabled (by cryptoadm) or
715  * if the kernel provider module is not one of the default kernel providers.
716  *
717  * The kcf.conf file is available only in the global zone.
718  */
719 int
720 get_kcfconf_info(entrylist_t **ppdevlist, entrylist_t **ppsoftlist,
721     entrylist_t **ppfipslist)
722 {
723 	FILE	*pfile = NULL;
724 	char	buffer[BUFSIZ];
725 	int	len;
726 	entry_t	*pent = NULL;
727 	int	rc = SUCCESS;
728 
729 	if ((pfile = fopen(_PATH_KCF_CONF, "r")) == NULL) {
730 		cryptodebug("failed to open the kcf.conf file for read only");
731 		return (FAILURE);
732 	}
733 
734 	*ppdevlist = NULL;
735 	*ppsoftlist = NULL;
736 	*ppfipslist = NULL;
737 
738 	while (fgets(buffer, BUFSIZ, pfile) != NULL) {
739 		if (buffer[0] == '#' || buffer[0] == ' ' ||
740 		    buffer[0] == '\n'|| buffer[0] == '\t') {
741 			continue;   /* ignore comment lines */
742 		}
743 
744 		len = strlen(buffer);
745 		if (buffer[len - 1] == '\n') { /* get rid of trailing '\n' */
746 			len--;
747 		}
748 		buffer[len] = '\0';
749 
750 		if ((rc = interpret(buffer,  &pent)) == SUCCESS) {
751 			if (is_fips(pent->name)) {
752 				if (*ppfipslist != NULL) {
753 					cryptoerror(LOG_STDERR, gettext(
754 					"multiple fips entries."));
755 					rc = FAILURE;
756 				} else {
757 					rc = build_entrylist(pent, ppfipslist);
758 				}
759 			} else if (is_device(pent->name)) {
760 				rc = build_entrylist(pent, ppdevlist);
761 			} else {
762 				rc = build_entrylist(pent, ppsoftlist);
763 			}
764 		} else {
765 			cryptoerror(LOG_STDERR, gettext(
766 			    "failed to parse configuration."));
767 			rc = FAILURE;
768 		}
769 
770 		if (rc != SUCCESS) {
771 			free_entrylist(*ppdevlist);
772 			free_entrylist(*ppsoftlist);
773 			free_entrylist(*ppfipslist);
774 			free_entry(pent);
775 			break;
776 		}
777 	}
778 
779 	(void) fclose(pfile);
780 	return (rc);
781 }
782 
783 /*
784  * Retrieve information from admin device and build a device entry list and
785  * a software entry list.  This is used where there is no kcf.conf, e.g., the
786  * non-global zone.
787  */
788 int
789 get_admindev_info(entrylist_t **ppdevlist, entrylist_t **ppsoftlist)
790 {
791 	crypto_get_dev_list_t	*pdevlist_kernel = NULL;
792 	crypto_get_soft_list_t	*psoftlist_kernel = NULL;
793 	char			*devname;
794 	int			inst_num;
795 	int			mcount;
796 	mechlist_t		*pmech = NULL;
797 	entry_t			*pent_dev = NULL, *pent_soft = NULL;
798 	int			i;
799 	char			*psoftname;
800 	entrylist_t		*tmp_pdev = NULL;
801 	entrylist_t		*tmp_psoft = NULL;
802 	entrylist_t		*phardlist = NULL, *psoftlist = NULL;
803 	entrylist_t		*pfipslist = NULL;
804 
805 	/*
806 	 * Get hardware providers
807 	 */
808 	if (get_dev_list(&pdevlist_kernel) != SUCCESS) {
809 		cryptodebug("failed to get hardware provider list from kernel");
810 		return (FAILURE);
811 	}
812 
813 	for (i = 0; i < pdevlist_kernel->dl_dev_count; i++) {
814 		devname = pdevlist_kernel->dl_devs[i].le_dev_name;
815 		inst_num = pdevlist_kernel->dl_devs[i].le_dev_instance;
816 		mcount = pdevlist_kernel->dl_devs[i].le_mechanism_count;
817 
818 		pmech = NULL;
819 		if (get_dev_info(devname, inst_num, mcount, &pmech) !=
820 		    SUCCESS) {
821 			cryptodebug(
822 			    "failed to retrieve the mechanism list for %s/%d.",
823 			    devname, inst_num);
824 			goto fail_out;
825 		}
826 
827 		if ((pent_dev = create_entry(devname)) == NULL) {
828 			cryptodebug("out of memory.");
829 			free_mechlist(pmech);
830 			goto fail_out;
831 		}
832 		pent_dev->suplist = pmech;
833 		pent_dev->sup_count = mcount;
834 
835 		if (build_entrylist(pent_dev, &tmp_pdev) != SUCCESS) {
836 			goto fail_out;
837 		}
838 	}
839 
840 	free(pdevlist_kernel);
841 	pdevlist_kernel = NULL;
842 
843 	/*
844 	 * Get software providers
845 	 */
846 	if (getzoneid() == GLOBAL_ZONEID) {
847 		if (get_kcfconf_info(&phardlist, &psoftlist, &pfipslist) !=
848 		    SUCCESS) {
849 			goto fail_out;
850 		}
851 	}
852 
853 	if (get_soft_list(&psoftlist_kernel) != SUCCESS) {
854 		cryptodebug("failed to get software provider list from kernel");
855 		goto fail_out;
856 	}
857 
858 	for (i = 0, psoftname = psoftlist_kernel->sl_soft_names;
859 	    i < psoftlist_kernel->sl_soft_count;
860 	    i++, psoftname = psoftname + strlen(psoftname) + 1) {
861 		pmech = NULL;
862 		if (get_soft_info(psoftname, &pmech, phardlist, psoftlist,
863 		    pfipslist) != SUCCESS) {
864 			cryptodebug(
865 			    "failed to retrieve the mechanism list for %s.",
866 			    psoftname);
867 			goto fail_out;
868 		}
869 
870 		if ((pent_soft = create_entry(psoftname)) == NULL) {
871 			cryptodebug("out of memory.");
872 			free_mechlist(pmech);
873 			goto fail_out;
874 		}
875 		pent_soft->suplist = pmech;
876 		pent_soft->sup_count = get_mech_count(pmech);
877 
878 		if (build_entrylist(pent_soft, &tmp_psoft) != SUCCESS) {
879 			goto fail_out;
880 		}
881 	}
882 
883 	free(psoftlist_kernel);
884 	psoftlist_kernel = NULL;
885 
886 	*ppdevlist = tmp_pdev;
887 	*ppsoftlist = tmp_psoft;
888 
889 	return (SUCCESS);
890 
891 fail_out:
892 	if (pent_dev != NULL)
893 		free_entry(pent_dev);
894 	if (pent_soft != NULL)
895 		free_entry(pent_soft);
896 
897 	free_entrylist(tmp_pdev);
898 	free_entrylist(tmp_psoft);
899 
900 	if (pdevlist_kernel != NULL)
901 		free(pdevlist_kernel);
902 	if (psoftlist_kernel != NULL)
903 		free(psoftlist_kernel);
904 
905 	return (FAILURE);
906 }
907 
908 /*
909  * Return configuration information for a kernel provider from kcf.conf.
910  * For kernel software providers return a enabled list and disabled list.
911  * For kernel hardware providers return just a disabled list.
912  *
913  * Parameters phardlist and psoftlist are supplied by get_kcfconf_info().
914  * If NULL, this function calls get_kcfconf_info() internally.
915  */
916 entry_t *
917 getent_kef(char *provname, entrylist_t *phardlist, entrylist_t *psoftlist,
918     entrylist_t *pfipslist)
919 {
920 	entry_t		*pent = NULL;
921 	boolean_t	memory_allocated = B_FALSE;
922 
923 	if ((phardlist == NULL) || (psoftlist == NULL) || (pfipslist == NULL)) {
924 		if (get_kcfconf_info(&phardlist, &psoftlist, &pfipslist) !=
925 		    SUCCESS) {
926 			return (NULL);
927 		}
928 		memory_allocated = B_TRUE;
929 	}
930 
931 	if (is_device(provname)) {
932 		pent = getent(provname, phardlist);
933 	} else if (is_fips(provname)) {
934 		pent = getent(provname, pfipslist);
935 	} else {
936 		pent = getent(provname, psoftlist);
937 	}
938 
939 	if (memory_allocated) {
940 		free_entrylist(phardlist);
941 		free_entrylist(psoftlist);
942 		free_entrylist(pfipslist);
943 	}
944 
945 	return (pent);
946 }
947 
948 /*
949  * Print out the provider name and the mechanism list.
950  */
951 void
952 print_mechlist(char *provname, mechlist_t *pmechlist)
953 {
954 	mechlist_t *ptr = NULL;
955 
956 	if (provname == NULL) {
957 		return;
958 	}
959 
960 	(void) printf("%s: ", provname);
961 	if (pmechlist == NULL) {
962 		(void) printf(gettext("No mechanisms presented.\n"));
963 		return;
964 	}
965 
966 	ptr = pmechlist;
967 	while (ptr != NULL) {
968 		(void) printf("%s", ptr->name);
969 		ptr = ptr->next;
970 		if (ptr == NULL) {
971 			(void) printf("\n");
972 		} else {
973 			(void) printf(",");
974 		}
975 	}
976 }
977 
978 
979 /*
980  * Update the kcf.conf file based on the update mode:
981  * - If update_mode is MODIFY_MODE, modify the entry with the same name.
982  *   If not found, append a new entry to the kcf.conf file.
983  * - If update_mode is DELETE_MODE, delete the entry with the same name.
984  * - If update_mode is ADD_MODE, append a new entry to the kcf.conf file.
985  */
986 int
987 update_kcfconf(entry_t *pent, int update_mode)
988 {
989 	boolean_t	add_it = B_FALSE;
990 	boolean_t	delete_it = B_FALSE;
991 	boolean_t	this_entry_matches = B_FALSE;
992 	boolean_t	found_entry = B_FALSE;
993 	FILE		*pfile = NULL;
994 	FILE		*pfile_tmp = NULL;
995 	char		buffer[BUFSIZ];
996 	char		buffer2[BUFSIZ];
997 	char		tmpfile_name[MAXPATHLEN];
998 	char		*name;
999 	char		*new_str = NULL;
1000 	int		rc = SUCCESS;
1001 
1002 	if (pent == NULL) {
1003 		cryptoerror(LOG_STDERR, gettext("internal error."));
1004 		return (FAILURE);
1005 	}
1006 
1007 	/* Check the update_mode */
1008 	switch (update_mode) {
1009 	case ADD_MODE:
1010 		add_it = B_TRUE;
1011 		/* FALLTHROUGH */
1012 	case MODIFY_MODE:
1013 		/* Convert the entry a string to add to kcf.conf  */
1014 		if ((new_str = ent2str(pent)) == NULL) {
1015 			return (FAILURE);
1016 		}
1017 		break;
1018 	case DELETE_MODE:
1019 		delete_it = B_TRUE;
1020 		break;
1021 	default:
1022 		cryptoerror(LOG_STDERR, gettext("internal error."));
1023 		return (FAILURE);
1024 	}
1025 
1026 	/* Open the kcf.conf file */
1027 	if ((pfile = fopen(_PATH_KCF_CONF, "r+")) == NULL) {
1028 		err = errno;
1029 		cryptoerror(LOG_STDERR,
1030 		    gettext("failed to update the configuration - %s"),
1031 		    strerror(err));
1032 		cryptodebug("failed to open %s for write.", _PATH_KCF_CONF);
1033 		return (FAILURE);
1034 	}
1035 
1036 	/* Lock the kcf.conf file */
1037 	if (lockf(fileno(pfile), F_TLOCK, 0) == -1) {
1038 		err = errno;
1039 		cryptoerror(LOG_STDERR,
1040 		    gettext("failed to update the configuration - %s"),
1041 		    strerror(err));
1042 		(void) fclose(pfile);
1043 		return (FAILURE);
1044 	}
1045 
1046 	/*
1047 	 * Create a temporary file in the /etc/crypto directory to save
1048 	 * updated configuration file first.
1049 	 */
1050 	(void) strlcpy(tmpfile_name, TMPFILE_TEMPLATE, sizeof (tmpfile_name));
1051 	if (mkstemp(tmpfile_name) == -1) {
1052 		err = errno;
1053 		cryptoerror(LOG_STDERR,
1054 		    gettext("failed to create a temporary file - %s"),
1055 		    strerror(err));
1056 		(void) fclose(pfile);
1057 		return (FAILURE);
1058 	}
1059 
1060 	if ((pfile_tmp = fopen(tmpfile_name, "w")) == NULL) {
1061 		err = errno;
1062 		cryptoerror(LOG_STDERR, gettext("failed to open %s - %s"),
1063 		    tmpfile_name, strerror(err));
1064 		(void) fclose(pfile);
1065 		return (FAILURE);
1066 	}
1067 
1068 	/*
1069 	 * Loop thru the entire kcf.conf file, insert, modify or delete
1070 	 * an entry.
1071 	 */
1072 	while (fgets(buffer, BUFSIZ, pfile) != NULL) {
1073 		if (add_it) {
1074 			if (fputs(buffer, pfile_tmp) == EOF) {
1075 				err = errno;
1076 				cryptoerror(LOG_STDERR, gettext(
1077 				    "failed to write to a temp file: %s."),
1078 				    strerror(err));
1079 				rc = FAILURE;
1080 				break;
1081 			}
1082 
1083 		} else { /* modify or delete */
1084 			this_entry_matches = B_FALSE;
1085 
1086 			if (!(buffer[0] == '#' || buffer[0] == ' ' ||
1087 			    buffer[0] == '\n'|| buffer[0] == '\t')) {
1088 				/*
1089 				 * Get the provider name from this line and
1090 				 * check if this is the entry to be updated
1091 				 * or deleted. Note: can not use "buffer"
1092 				 * directly because strtok will change its
1093 				 * value.
1094 				 */
1095 				(void) strlcpy(buffer2, buffer, BUFSIZ);
1096 				if ((name = strtok(buffer2, SEP_COLON)) ==
1097 				    NULL) {
1098 					rc = FAILURE;
1099 					break;
1100 				}
1101 
1102 				if (strcmp(pent->name, name) == 0) {
1103 					this_entry_matches = B_TRUE;
1104 					found_entry = B_TRUE;
1105 				}
1106 			}
1107 
1108 
1109 			if (!this_entry_matches || !delete_it) {
1110 				/* write this entry */
1111 				if (this_entry_matches) {
1112 					/*
1113 					 * Modify this entry: get the
1114 					 * updated string and place into buffer.
1115 					 */
1116 					(void) strlcpy(buffer, new_str, BUFSIZ);
1117 					free(new_str);
1118 				}
1119 				/* write the (unchanged or modified) entry */
1120 				if (fputs(buffer, pfile_tmp) == EOF) {
1121 					err = errno;
1122 					cryptoerror(LOG_STDERR, gettext(
1123 					    "failed to write to a temp file: "
1124 					    "%s."), strerror(err));
1125 					rc = FAILURE;
1126 					break;
1127 				}
1128 			}
1129 		}
1130 	}
1131 
1132 	if ((!delete_it) && (rc != FAILURE)) {
1133 		if (add_it || !found_entry) {
1134 			/* append new entry to end of file */
1135 			if (fputs(new_str, pfile_tmp) == EOF) {
1136 				err = errno;
1137 				cryptoerror(LOG_STDERR, gettext(
1138 				    "failed to write to a temp file: %s."),
1139 				    strerror(err));
1140 				rc = FAILURE;
1141 			}
1142 			free(new_str);
1143 		}
1144 	}
1145 
1146 	(void) fclose(pfile);
1147 	if (fclose(pfile_tmp) != 0) {
1148 		err = errno;
1149 		cryptoerror(LOG_STDERR,
1150 		    gettext("failed to close %s: %s"), tmpfile_name,
1151 		    strerror(err));
1152 		return (FAILURE);
1153 	}
1154 
1155 	/* Copy the temporary file to the kcf.conf file */
1156 	if (rename(tmpfile_name, _PATH_KCF_CONF) == -1) {
1157 		err = errno;
1158 		cryptoerror(LOG_STDERR,
1159 		    gettext("failed to update the configuration - %s"),
1160 		    strerror(err));
1161 		cryptodebug("failed to rename %s to %s: %s", tmpfile,
1162 		    _PATH_KCF_CONF, strerror(err));
1163 		rc = FAILURE;
1164 	} else if (chmod(_PATH_KCF_CONF,
1165 	    S_IRUSR | S_IWUSR | S_IRGRP | S_IROTH) == -1) {
1166 		err = errno;
1167 		cryptoerror(LOG_STDERR,
1168 		    gettext("failed to update the configuration - %s"),
1169 		    strerror(err));
1170 		cryptodebug("failed to chmod to %s: %s", _PATH_KCF_CONF,
1171 		    strerror(err));
1172 		rc = FAILURE;
1173 	} else {
1174 		rc = SUCCESS;
1175 	}
1176 
1177 	if ((rc == FAILURE) && (unlink(tmpfile_name) != 0)) {
1178 		err = errno;
1179 		cryptoerror(LOG_STDERR, gettext(
1180 		    "(Warning) failed to remove %s: %s"),
1181 		    tmpfile_name, strerror(err));
1182 	}
1183 
1184 	return (rc);
1185 }
1186 
1187 
1188 /*
1189  * Disable the mechanisms for the provider pointed by *ppent.  If allflag is
1190  * TRUE, disable all.  Otherwise, disable the mechanisms specified in the
1191  * dislist argument.  The "infolist" argument contains the mechanism list
1192  * supported by this provider.
1193  */
1194 int
1195 disable_mechs(entry_t **ppent, mechlist_t *infolist, boolean_t allflag,
1196 mechlist_t *dislist)
1197 {
1198 	entry_t		*pent;
1199 	mechlist_t	*plist = NULL;
1200 	mechlist_t	*phead = NULL;
1201 	mechlist_t	*pmech = NULL;
1202 	int		rc = SUCCESS;
1203 
1204 	pent = *ppent;
1205 	if (pent == NULL) {
1206 		return (FAILURE);
1207 	}
1208 
1209 	if (allflag) {
1210 		free_mechlist(pent->dislist);
1211 		pent->dis_count = get_mech_count(infolist);
1212 		if (!(pent->dislist = dup_mechlist(infolist))) {
1213 			return (FAILURE);
1214 		} else {
1215 			return (SUCCESS);
1216 		}
1217 	}
1218 
1219 	/*
1220 	 * Not disable all. Now loop thru the mechanisms specified in the
1221 	 * dislist.  If the mechanism is not supported by the provider,
1222 	 * ignore it with a warning.  If the mechanism is disabled already,
1223 	 * do nothing. Otherwise, prepend it to the beginning of the disabled
1224 	 * list of the provider.
1225 	 */
1226 	plist = dislist;
1227 	while (plist != NULL) {
1228 		if (!is_in_list(plist->name, infolist)) {
1229 			cryptoerror(LOG_STDERR, gettext("(Warning) "
1230 			    "%1$s is not a valid mechanism for %2$s."),
1231 			    plist->name, pent->name);
1232 		} else if (!is_in_list(plist->name, pent->dislist)) {
1233 			/* Add this mechanism into the disabled list */
1234 			if ((pmech = create_mech(plist->name)) == NULL) {
1235 				rc = FAILURE;
1236 				break;
1237 			}
1238 
1239 			if (pent->dislist == NULL) {
1240 				pent->dislist = pmech;
1241 			} else {
1242 				phead = pent->dislist;
1243 				pent->dislist = pmech;
1244 				pmech->next = phead;
1245 			}
1246 			pent->dis_count++;
1247 		}
1248 		plist = plist->next;
1249 	}
1250 
1251 	return (rc);
1252 }
1253 
1254 /*
1255  * Remove the mechanism passed, specified by mech, from the list of
1256  * mechanisms, if present in the list. Else, do nothing.
1257  *
1258  * Returns B_TRUE if mechanism is present in the list.
1259  */
1260 boolean_t
1261 filter_mechlist(mechlist_t **pmechlist, const char *mech)
1262 {
1263 	int		cnt = 0;
1264 	mechlist_t	*ptr, *pptr;
1265 	boolean_t	mech_present = B_FALSE;
1266 
1267 	ptr = pptr = *pmechlist;
1268 
1269 	while (ptr != NULL) {
1270 		if (strncmp(ptr->name, mech, sizeof (mech_name_t)) == 0) {
1271 			mech_present = B_TRUE;
1272 			if (ptr == *pmechlist) {
1273 				pptr = *pmechlist = ptr->next;
1274 				free(ptr);
1275 				ptr = pptr;
1276 			} else {
1277 				pptr->next = ptr->next;
1278 				free(ptr);
1279 				ptr = pptr->next;
1280 			}
1281 		} else {
1282 			pptr = ptr;
1283 			ptr = ptr->next;
1284 			cnt++;
1285 		}
1286 	}
1287 
1288 	/* Only one entry is present */
1289 	if (cnt == 0)
1290 		*pmechlist = NULL;
1291 
1292 	return (mech_present);
1293 }
1294 
1295 
1296 
1297 /*
1298  * Print out the mechanism policy for a kernel provider that has an entry
1299  * in the kcf.conf file.
1300  *
1301  * The flag has_random is set to B_TRUE if the provider does random
1302  * numbers. The flag has_mechs is set by the caller to B_TRUE if the provider
1303  * has some mechanisms.
1304  *
1305  * If pent is NULL, the provider doesn't have a kcf.conf entry.
1306  */
1307 void
1308 print_kef_policy(char *provname, entry_t *pent, boolean_t has_random,
1309     boolean_t has_mechs)
1310 {
1311 	mechlist_t	*ptr = NULL;
1312 	boolean_t	rnd_disabled = B_FALSE;
1313 
1314 	if (pent != NULL) {
1315 		rnd_disabled = filter_mechlist(&pent->dislist, RANDOM);
1316 		ptr = pent->dislist;
1317 	}
1318 
1319 	(void) printf("%s:", provname);
1320 
1321 	if (has_mechs == B_TRUE) {
1322 		/*
1323 		 * TRANSLATION_NOTE
1324 		 * This code block may need to be modified a bit to avoid
1325 		 * constructing the text message on the fly.
1326 		 */
1327 		(void) printf(gettext(" all mechanisms are enabled"));
1328 		if (ptr != NULL)
1329 			(void) printf(gettext(", except "));
1330 		while (ptr != NULL) {
1331 			(void) printf("%s", ptr->name);
1332 			ptr = ptr->next;
1333 			if (ptr != NULL)
1334 				(void) printf(",");
1335 		}
1336 		if (ptr == NULL)
1337 			(void) printf(".");
1338 	}
1339 
1340 	/*
1341 	 * TRANSLATION_NOTE
1342 	 * "random" is a keyword and not to be translated.
1343 	 */
1344 	if (rnd_disabled)
1345 		(void) printf(gettext(" %s is disabled."), "random");
1346 	else if (has_random)
1347 		(void) printf(gettext(" %s is enabled."), "random");
1348 	(void) printf("\n");
1349 }
1350 
1351 
1352 /*
1353  * Check if a kernel software provider is in the kernel.
1354  *
1355  * Parameters:
1356  * provname		Provider name
1357  * psoftlist_kernel	Optional software provider list.  If NULL, it will be
1358  *			obtained from get_soft_list().
1359  * in_kernel		Set to B_TRUE if device is in the kernel, else B_FALSE
1360  */
1361 int
1362 check_kernel_for_soft(char *provname, crypto_get_soft_list_t *psoftlist_kernel,
1363     boolean_t *in_kernel)
1364 {
1365 	char		*ptr;
1366 	int		i;
1367 	boolean_t	psoftlist_allocated = B_FALSE;
1368 
1369 	if (provname == NULL) {
1370 		cryptoerror(LOG_STDERR, gettext("internal error."));
1371 		return (FAILURE);
1372 	}
1373 
1374 	if (psoftlist_kernel == NULL) {
1375 		if (get_soft_list(&psoftlist_kernel) == FAILURE) {
1376 			cryptodebug("failed to get the software provider list"
1377 			" from kernel.");
1378 			return (FAILURE);
1379 		}
1380 		psoftlist_allocated = B_TRUE;
1381 	}
1382 
1383 	*in_kernel = B_FALSE;
1384 	ptr = psoftlist_kernel->sl_soft_names;
1385 	for (i = 0; i < psoftlist_kernel->sl_soft_count; i++) {
1386 		if (strcmp(provname, ptr) == 0) {
1387 			*in_kernel = B_TRUE;
1388 			break;
1389 		}
1390 		ptr = ptr + strlen(ptr) + 1;
1391 	}
1392 
1393 	if (psoftlist_allocated)
1394 		free(psoftlist_kernel);
1395 
1396 	return (SUCCESS);
1397 }
1398 
1399 
1400 /*
1401  * Check if a kernel hardware provider is in the kernel.
1402  *
1403  * Parameters:
1404  * provname	Provider name
1405  * pdevlist	Optional Hardware Crypto Device List.  If NULL, it will be
1406  *		obtained from get_dev_list().
1407  * in_kernel	Set to B_TRUE if device is in the kernel, otherwise B_FALSE
1408  */
1409 int
1410 check_kernel_for_hard(char *provname,
1411     crypto_get_dev_list_t *pdevlist, boolean_t *in_kernel)
1412 {
1413 	char		devname[MAXNAMELEN];
1414 	int		inst_num;
1415 	int		i;
1416 	boolean_t	dev_list_allocated = B_FALSE;
1417 
1418 	if (provname == NULL) {
1419 		cryptoerror(LOG_STDERR, gettext("internal error."));
1420 		return (FAILURE);
1421 	}
1422 
1423 	if (split_hw_provname(provname, devname, &inst_num) == FAILURE) {
1424 		return (FAILURE);
1425 	}
1426 
1427 	if (pdevlist == NULL) {
1428 		if (get_dev_list(&pdevlist) == FAILURE) {
1429 			cryptoerror(LOG_STDERR, gettext("internal error."));
1430 			return (FAILURE);
1431 		}
1432 		dev_list_allocated = B_TRUE;
1433 	}
1434 
1435 	*in_kernel = B_FALSE;
1436 	for (i = 0; i < pdevlist->dl_dev_count; i++) {
1437 		if ((strcmp(pdevlist->dl_devs[i].le_dev_name, devname) == 0) &&
1438 		    (pdevlist->dl_devs[i].le_dev_instance == inst_num)) {
1439 			*in_kernel = B_TRUE;
1440 			break;
1441 		}
1442 	}
1443 
1444 	if (dev_list_allocated)
1445 		free(pdevlist);
1446 
1447 	return (SUCCESS);
1448 }
1449