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