/* * CDDL HEADER START * * The contents of this file are subject to the terms of the * Common Development and Distribution License (the "License"). * You may not use this file except in compliance with the License. * * You can obtain a copy of the license at usr/src/OPENSOLARIS.LICENSE * or http://www.opensolaris.org/os/licensing. * See the License for the specific language governing permissions * and limitations under the License. * * When distributing Covered Code, include this CDDL HEADER in each * file and include the License file at usr/src/OPENSOLARIS.LICENSE. * If applicable, add the following below this CDDL HEADER, with the * fields enclosed by brackets "[]" replaced with your own identifying * information: Portions Copyright [yyyy] [name of copyright owner] * * CDDL HEADER END */ /* * Copyright 2009 Sun Microsystems, Inc. All rights reserved. * Use is subject to license terms. */ #include #include #include #include #include #include #include #include #include #include #include #include "cryptoadm.h" #include static int err; /* To store errno which may be overwritten by gettext() */ static int build_entrylist(entry_t *, entrylist_t **); static entry_t *dup_entry(entry_t *); static mechlist_t *dup_mechlist(mechlist_t *); static entry_t *getent(char *, entrylist_t *); static int interpret(char *, entry_t **); static int parse_sup_dis_list(char *, entry_t *); /* * Duplicate the mechanism list. A null pointer is returned if the storage * space available is insufficient or the input argument is NULL. */ static mechlist_t * dup_mechlist(mechlist_t *plist) { mechlist_t *pres = NULL; mechlist_t *pcur; mechlist_t *ptmp; int rc = SUCCESS; while (plist != NULL) { if (!(ptmp = create_mech(plist->name))) { rc = FAILURE; break; } if (pres == NULL) { pres = pcur = ptmp; } else { pcur->next = ptmp; pcur = pcur->next; } plist = plist->next; } if (rc != SUCCESS) { free_mechlist(pres); return (NULL); } return (pres); } /* * Get the number of mechanisms in the mechanism list. */ int get_mech_count(mechlist_t *plist) { int count = 0; while (plist != NULL) { count++; plist = plist->next; } return (count); } /* * Create one item of type entry_t with the provider name. * Return NULL if there's not enough memory or provname is NULL. */ entry_t * create_entry(char *provname) { entry_t *pent = NULL; if (provname == NULL) { return (NULL); } pent = calloc(1, sizeof (entry_t)); if (pent == NULL) { cryptodebug("out of memory."); return (NULL); } (void) strlcpy(pent->name, provname, MAXNAMELEN); pent->suplist = NULL; pent->sup_count = 0; pent->dislist = NULL; pent->dis_count = 0; pent->load = B_TRUE; return (pent); } /* * Duplicate an entry for a provider from kcf.conf. * Return NULL if memory is insufficient or the input argument is NULL. * Called by getent(). */ static entry_t * dup_entry(entry_t *pent1) { entry_t *pent2 = NULL; if (pent1 == NULL) { return (NULL); } if ((pent2 = create_entry(pent1->name)) == NULL) { cryptodebug("out of memory."); return (NULL); } pent2->flag_fips_enabled = pent1->flag_fips_enabled; pent2->sup_count = pent1->sup_count; pent2->dis_count = pent1->dis_count; pent2->load = pent1->load; if (pent1->suplist != NULL) { pent2->suplist = dup_mechlist(pent1->suplist); if (pent2->suplist == NULL) { free_entry(pent2); return (NULL); } } if (pent1->dislist != NULL) { pent2->dislist = dup_mechlist(pent1->dislist); if (pent2->dislist == NULL) { free_entry(pent2); return (NULL); } } return (pent2); } /* * This routine parses the disabledlist or the supportedlist of an entry * in the kcf.conf configuration file. * * Arguments: * buf: an input argument which is a char string with the format of * "disabledlist=m1,m2,..." or "supportedlist=m1,m2,..." * pent: the entry for the disabledlist. This is an IN/OUT argument. * * Return value: SUCCESS or FAILURE. */ static int parse_sup_dis_list(char *buf, entry_t *pent) { mechlist_t *pmech = NULL; mechlist_t *phead = NULL; char *next_token; char *value; int count; int supflag = B_FALSE; int disflag = B_FALSE; int rc = SUCCESS; if (strncmp(buf, EF_SUPPORTED, strlen(EF_SUPPORTED)) == 0) { supflag = B_TRUE; } else if (strncmp(buf, EF_DISABLED, strlen(EF_DISABLED)) == 0) { disflag = B_TRUE; } else { /* should not come here */ return (FAILURE); } if (value = strpbrk(buf, SEP_EQUAL)) { value++; /* get rid of = */ } else { cryptodebug("failed to parse the kcf.conf file."); return (FAILURE); } if ((next_token = strtok(value, SEP_COMMA)) == NULL) { cryptodebug("failed to parse the kcf.conf file."); return (FAILURE); } if ((pmech = create_mech(next_token)) == NULL) { return (FAILURE); } if (supflag) { pent->suplist = phead = pmech; } else if (disflag) { pent->dislist = phead = pmech; } count = 1; while (next_token) { if (next_token = strtok(NULL, SEP_COMMA)) { if ((pmech = create_mech(next_token)) == NULL) { rc = FAILURE; break; } count++; phead->next = pmech; phead = phead->next; } } if (rc == SUCCESS) { if (supflag) { pent->sup_count = count; } else if (disflag) { pent->dis_count = count; } } else { free_mechlist(phead); } return (rc); } static int parse_fips(char *buf, entry_t *pent) { char *value; if (strncmp(buf, EF_FIPS_STATUS, sizeof (EF_FIPS_STATUS) - 1) == 0) { if (value = strpbrk(buf, SEP_EQUAL)) { value++; /* get rid of = */ if (strcmp(value, DISABLED_KEYWORD) == 0) { pent->flag_fips_enabled = B_FALSE; } else if (strcmp(value, ENABLED_KEYWORD) == 0) { pent->flag_fips_enabled = B_TRUE; } else { cryptoerror(LOG_ERR, gettext( "Failed to parse kcf.conf file.\n")); return (FAILURE); } return (SUCCESS); } else { return (FAILURE); } } else { /* should not come here */ cryptoerror(LOG_ERR, gettext( "Failed to parse kcf.conf file.\n")); return (FAILURE); } } /* * Convert a char string containing a line about a provider * from kcf.conf into an entry_t structure. * * See ent2str(), the reverse of this function, for the format of * kcf.conf lines. */ static int interpret(char *buf, entry_t **ppent) { entry_t *pent = NULL; char *token1; char *token2; char *token3; int rc; /* Get provider name */ if ((token1 = strtok(buf, SEP_COLON)) == NULL) { /* buf is NULL */ return (FAILURE); }; pent = create_entry(token1); if (pent == NULL) { cryptodebug("out of memory."); return (FAILURE); } if (is_fips(token1)) { if ((rc = parse_fips(buf + strlen(token1) + 1, pent)) != SUCCESS) { free_entry(pent); } *ppent = pent; return (rc); } if ((token2 = strtok(NULL, SEP_SEMICOLON)) == NULL) { /* The entry contains a provider name only */ free_entry(pent); return (FAILURE); } if (strncmp(token2, EF_UNLOAD, strlen(EF_UNLOAD)) == 0) { pent->load = B_FALSE; /* cryptoadm unload */ if ((token2 = strtok(NULL, SEP_SEMICOLON)) == NULL) { /* The entry contains a provider name:unload only */ free_entry(pent); return (FAILURE); } } /* need to get token3 first to satisfy nested strtok invocations */ token3 = strtok(NULL, SEP_SEMICOLON); /* optional */ /* parse supportedlist (or disabledlist if no supportedlist) */ if ((token2 != NULL) && ((rc = parse_sup_dis_list(token2, pent)) != SUCCESS)) { free_entry(pent); return (rc); } /* parse disabledlist (if there's a supportedlist) */ if ((token3 != NULL) && ((rc = parse_sup_dis_list(token3, pent)) != SUCCESS)) { free_entry(pent); return (rc); } *ppent = pent; return (SUCCESS); } /* * Add an entry about a provider from kcf.conf to the end of an entry list. * If the entry list pplist is NULL, create the linked list with pent as the * first element. */ static int build_entrylist(entry_t *pent, entrylist_t **pplist) { entrylist_t *pentlist; entrylist_t *pcur = NULL; pentlist = malloc(sizeof (entrylist_t)); if (pentlist == NULL) { cryptodebug("out of memory."); return (FAILURE); } pentlist->pent = pent; pentlist->next = NULL; if (*pplist) { pcur = *pplist; while (pcur->next != NULL) pcur = pcur->next; pcur->next = pentlist; } else { /* empty list */ *pplist = pentlist; } return (SUCCESS); } /* * Find the entry with the "provname" name from the entry list and duplicate * it. Called by getent_kef(). */ static entry_t * getent(char *provname, entrylist_t *entrylist) { boolean_t found = B_FALSE; entry_t *pent1 = NULL; if ((provname == NULL) || (entrylist == NULL)) { return (NULL); } while (!found && entrylist) { if (strcmp(entrylist->pent->name, provname) == 0) { found = B_TRUE; pent1 = entrylist->pent; } else { entrylist = entrylist->next; } } if (!found) { return (NULL); } /* duplicate the entry to be returned */ return (dup_entry(pent1)); } /* * Free memory in entry_t. * That is, the supported and disabled lists for a provider * from kcf.conf. */ void free_entry(entry_t *pent) { if (pent == NULL) { return; } else { free_mechlist(pent->suplist); free_mechlist(pent->dislist); free(pent); } } /* * Free elements in a entrylist_t linked list, * which lists providers in kcf.conf. */ void free_entrylist(entrylist_t *entrylist) { entrylist_t *pnext; while (entrylist != NULL) { pnext = entrylist->next; free_entry(entrylist->pent); entrylist = pnext; } } /* * Convert an entry to a string. This routine builds a string for the entry * to be inserted in the kcf.conf file. Based on the content of each entry, * the result string can be one of these 6 forms: * - name:supportedlist=m1,m2,...,mj * - name:disabledlist=m1,m2,...,mj * - name:supportedlist=m1,...,mj;disabledlist=m1,m2,...,mk * * - name:unload;supportedlist=m1,m2,...,mj * - name:unload;disabledlist=m1,m2,...,mj * - name:unload;supportedlist=m1,...,mj;disabledlist=m1,m2,...,mk * * Note that the caller is responsible for freeing the returned string * (with free_entry()). * See interpret() for the reverse of this function: converting a string * to an entry_t. */ char * ent2str(entry_t *pent) { char *buf; mechlist_t *pcur = NULL; boolean_t semicolon_separator = B_FALSE; if (pent == NULL) { return (NULL); } if ((buf = malloc(BUFSIZ)) == NULL) { return (NULL); } /* convert the provider name */ if (strlcpy(buf, pent->name, BUFSIZ) >= BUFSIZ) { free(buf); return (NULL); } if (!pent->load) { /* add "unload" keyword */ if (strlcat(buf, SEP_COLON, BUFSIZ) >= BUFSIZ) { free(buf); return (NULL); } if (strlcat(buf, EF_UNLOAD, BUFSIZ) >= BUFSIZ) { free(buf); return (NULL); } semicolon_separator = B_TRUE; } /* convert the supported list if any */ pcur = pent->suplist; if (pcur != NULL) { if (strlcat(buf, semicolon_separator ? SEP_SEMICOLON : SEP_COLON, BUFSIZ) >= BUFSIZ) { free(buf); return (NULL); } if (strlcat(buf, EF_SUPPORTED, BUFSIZ) >= BUFSIZ) { free(buf); return (NULL); } while (pcur != NULL) { if (strlcat(buf, pcur->name, BUFSIZ) >= BUFSIZ) { free(buf); return (NULL); } pcur = pcur->next; if (pcur != NULL) { if (strlcat(buf, SEP_COMMA, BUFSIZ) >= BUFSIZ) { free(buf); return (NULL); } } } semicolon_separator = B_TRUE; } /* convert the disabled list if any */ pcur = pent->dislist; if (pcur != NULL) { if (strlcat(buf, semicolon_separator ? SEP_SEMICOLON : SEP_COLON, BUFSIZ) >= BUFSIZ) { free(buf); return (NULL); } if (strlcat(buf, EF_DISABLED, BUFSIZ) >= BUFSIZ) { free(buf); return (NULL); } while (pcur != NULL) { if (strlcat(buf, pcur->name, BUFSIZ) >= BUFSIZ) { free(buf); return (NULL); } pcur = pcur->next; if (pcur != NULL) { if (strlcat(buf, SEP_COMMA, BUFSIZ) >= BUFSIZ) { free(buf); return (NULL); } } } semicolon_separator = B_TRUE; } if (strlcat(buf, "\n", BUFSIZ) >= BUFSIZ) { free(buf); return (NULL); } return (buf); } /* * Enable the mechanisms for the provider pointed by *ppent. If allflag is * TRUE, enable all. Otherwise, enable the mechanisms specified in the 3rd * argument "mlist". The result will be stored in ppent also. */ int enable_mechs(entry_t **ppent, boolean_t allflag, mechlist_t *mlist) { entry_t *pent; mechlist_t *phead; /* the current and resulting disabled list */ mechlist_t *ptr = NULL; mechlist_t *pcur = NULL; boolean_t found; pent = *ppent; if (pent == NULL) { return (FAILURE); } if (allflag) { free_mechlist(pent->dislist); pent->dis_count = 0; pent->dislist = NULL; return (SUCCESS); } /* * for each mechanism in the to-be-enabled mechanism list, * - check if it is in the current disabled list * - if found, delete it from the disabled list * otherwise, give a warning. */ ptr = mlist; while (ptr != NULL) { found = B_FALSE; phead = pcur = pent->dislist; while (!found && pcur) { if (strcmp(pcur->name, ptr->name) == 0) { found = B_TRUE; } else { phead = pcur; pcur = pcur->next; } } if (found) { if (phead == pcur) { pent->dislist = pent->dislist->next; free(pcur); } else { phead->next = pcur->next; free(pcur); } pent->dis_count--; } else { cryptoerror(LOG_STDERR, gettext( "(Warning) %1$s is either enabled already or not " "a valid mechanism for %2$s"), ptr->name, pent->name); } ptr = ptr->next; } if (pent->dis_count == 0) { pent->dislist = NULL; } return (SUCCESS); } /* * Determine if the kernel provider name, path, is a device * (that is, it contains a slash character (e.g., "mca/0"). * If so, it is a hardware provider; otherwise it is a software provider. */ boolean_t is_device(char *path) { if (strchr(path, SEP_SLASH) != NULL) { return (B_TRUE); } else { return (B_FALSE); } } boolean_t is_fips(char *name) { if (strcmp(name, FIPS_KEYWORD) == 0) { return (B_TRUE); } else { return (B_FALSE); } } /* * Split a hardware provider name with the "name/inst_num" format into * a name and a number (e.g., split "mca/0" into "mca" instance 0). */ int split_hw_provname(char *provname, char *pname, int *inst_num) { char name[MAXNAMELEN]; char *inst_str; if (provname == NULL) { return (FAILURE); } (void) strlcpy(name, provname, MAXNAMELEN); if (strtok(name, "/") == NULL) { return (FAILURE); } if ((inst_str = strtok(NULL, "/")) == NULL) { return (FAILURE); } (void) strlcpy(pname, name, MAXNAMELEN); *inst_num = atoi(inst_str); return (SUCCESS); } /* * Retrieve information from kcf.conf and build a hardware device entry list * and a software entry list of kernel crypto providers. * * This list is usually incomplete, as kernel crypto providers only have to * be listed in kcf.conf if a mechanism is disabled (by cryptoadm) or * if the kernel provider module is not one of the default kernel providers. * * The kcf.conf file is available only in the global zone. */ int get_kcfconf_info(entrylist_t **ppdevlist, entrylist_t **ppsoftlist, entrylist_t **ppfipslist) { FILE *pfile = NULL; char buffer[BUFSIZ]; int len; entry_t *pent = NULL; int rc = SUCCESS; if ((pfile = fopen(_PATH_KCF_CONF, "r")) == NULL) { cryptodebug("failed to open the kcf.conf file for read only"); return (FAILURE); } *ppdevlist = NULL; *ppsoftlist = NULL; *ppfipslist = NULL; while (fgets(buffer, BUFSIZ, pfile) != NULL) { if (buffer[0] == '#' || buffer[0] == ' ' || buffer[0] == '\n'|| buffer[0] == '\t') { continue; /* ignore comment lines */ } len = strlen(buffer); if (buffer[len - 1] == '\n') { /* get rid of trailing '\n' */ len--; } buffer[len] = '\0'; if ((rc = interpret(buffer, &pent)) == SUCCESS) { if (is_fips(pent->name)) { if (*ppfipslist != NULL) { cryptoerror(LOG_STDERR, gettext( "multiple fips entries.")); rc = FAILURE; } else { rc = build_entrylist(pent, ppfipslist); } } else if (is_device(pent->name)) { rc = build_entrylist(pent, ppdevlist); } else { rc = build_entrylist(pent, ppsoftlist); } } else { cryptoerror(LOG_STDERR, gettext( "failed to parse configuration.")); rc = FAILURE; } if (rc != SUCCESS) { free_entrylist(*ppdevlist); free_entrylist(*ppsoftlist); free_entrylist(*ppfipslist); free_entry(pent); break; } } (void) fclose(pfile); return (rc); } /* * Retrieve information from admin device and build a device entry list and * a software entry list. This is used where there is no kcf.conf, e.g., the * non-global zone. */ int get_admindev_info(entrylist_t **ppdevlist, entrylist_t **ppsoftlist) { crypto_get_dev_list_t *pdevlist_kernel = NULL; crypto_get_soft_list_t *psoftlist_kernel = NULL; char *devname; int inst_num; int mcount; mechlist_t *pmech = NULL; entry_t *pent_dev = NULL, *pent_soft = NULL; int i; char *psoftname; entrylist_t *tmp_pdev = NULL; entrylist_t *tmp_psoft = NULL; entrylist_t *phardlist = NULL, *psoftlist = NULL; entrylist_t *pfipslist = NULL; /* * Get hardware providers */ if (get_dev_list(&pdevlist_kernel) != SUCCESS) { cryptodebug("failed to get hardware provider list from kernel"); return (FAILURE); } for (i = 0; i < pdevlist_kernel->dl_dev_count; i++) { devname = pdevlist_kernel->dl_devs[i].le_dev_name; inst_num = pdevlist_kernel->dl_devs[i].le_dev_instance; mcount = pdevlist_kernel->dl_devs[i].le_mechanism_count; pmech = NULL; if (get_dev_info(devname, inst_num, mcount, &pmech) != SUCCESS) { cryptodebug( "failed to retrieve the mechanism list for %s/%d.", devname, inst_num); goto fail_out; } if ((pent_dev = create_entry(devname)) == NULL) { cryptodebug("out of memory."); free_mechlist(pmech); goto fail_out; } pent_dev->suplist = pmech; pent_dev->sup_count = mcount; if (build_entrylist(pent_dev, &tmp_pdev) != SUCCESS) { goto fail_out; } } free(pdevlist_kernel); pdevlist_kernel = NULL; /* * Get software providers */ if (getzoneid() == GLOBAL_ZONEID) { if (get_kcfconf_info(&phardlist, &psoftlist, &pfipslist) != SUCCESS) { goto fail_out; } } if (get_soft_list(&psoftlist_kernel) != SUCCESS) { cryptodebug("failed to get software provider list from kernel"); goto fail_out; } for (i = 0, psoftname = psoftlist_kernel->sl_soft_names; i < psoftlist_kernel->sl_soft_count; i++, psoftname = psoftname + strlen(psoftname) + 1) { pmech = NULL; if (get_soft_info(psoftname, &pmech, phardlist, psoftlist, pfipslist) != SUCCESS) { cryptodebug( "failed to retrieve the mechanism list for %s.", psoftname); goto fail_out; } if ((pent_soft = create_entry(psoftname)) == NULL) { cryptodebug("out of memory."); free_mechlist(pmech); goto fail_out; } pent_soft->suplist = pmech; pent_soft->sup_count = get_mech_count(pmech); if (build_entrylist(pent_soft, &tmp_psoft) != SUCCESS) { goto fail_out; } } free(psoftlist_kernel); psoftlist_kernel = NULL; *ppdevlist = tmp_pdev; *ppsoftlist = tmp_psoft; return (SUCCESS); fail_out: if (pent_dev != NULL) free_entry(pent_dev); if (pent_soft != NULL) free_entry(pent_soft); free_entrylist(tmp_pdev); free_entrylist(tmp_psoft); if (pdevlist_kernel != NULL) free(pdevlist_kernel); if (psoftlist_kernel != NULL) free(psoftlist_kernel); return (FAILURE); } /* * Return configuration information for a kernel provider from kcf.conf. * For kernel software providers return a enabled list and disabled list. * For kernel hardware providers return just a disabled list. * * Parameters phardlist and psoftlist are supplied by get_kcfconf_info(). * If NULL, this function calls get_kcfconf_info() internally. */ entry_t * getent_kef(char *provname, entrylist_t *phardlist, entrylist_t *psoftlist, entrylist_t *pfipslist) { entry_t *pent = NULL; boolean_t memory_allocated = B_FALSE; if ((phardlist == NULL) || (psoftlist == NULL) || (pfipslist == NULL)) { if (get_kcfconf_info(&phardlist, &psoftlist, &pfipslist) != SUCCESS) { return (NULL); } memory_allocated = B_TRUE; } if (is_device(provname)) { pent = getent(provname, phardlist); } else if (is_fips(provname)) { pent = getent(provname, pfipslist); } else { pent = getent(provname, psoftlist); } if (memory_allocated) { free_entrylist(phardlist); free_entrylist(psoftlist); free_entrylist(pfipslist); } return (pent); } /* * Print out the provider name and the mechanism list. */ void print_mechlist(char *provname, mechlist_t *pmechlist) { mechlist_t *ptr = NULL; if (provname == NULL) { return; } (void) printf("%s: ", provname); if (pmechlist == NULL) { (void) printf(gettext("No mechanisms presented.\n")); return; } ptr = pmechlist; while (ptr != NULL) { (void) printf("%s", ptr->name); ptr = ptr->next; if (ptr == NULL) { (void) printf("\n"); } else { (void) printf(","); } } } /* * Update the kcf.conf file based on the update mode: * - If update_mode is MODIFY_MODE, modify the entry with the same name. * If not found, append a new entry to the kcf.conf file. * - If update_mode is DELETE_MODE, delete the entry with the same name. * - If update_mode is ADD_MODE, append a new entry to the kcf.conf file. */ int update_kcfconf(entry_t *pent, int update_mode) { boolean_t add_it = B_FALSE; boolean_t delete_it = B_FALSE; boolean_t this_entry_matches = B_FALSE; boolean_t found_entry = B_FALSE; FILE *pfile = NULL; FILE *pfile_tmp = NULL; char buffer[BUFSIZ]; char buffer2[BUFSIZ]; char tmpfile_name[MAXPATHLEN]; char *name; char *new_str = NULL; int rc = SUCCESS; if (pent == NULL) { cryptoerror(LOG_STDERR, gettext("internal error.")); return (FAILURE); } /* Check the update_mode */ switch (update_mode) { case ADD_MODE: add_it = B_TRUE; /* FALLTHROUGH */ case MODIFY_MODE: /* Convert the entry a string to add to kcf.conf */ if ((new_str = ent2str(pent)) == NULL) { return (FAILURE); } break; case DELETE_MODE: delete_it = B_TRUE; break; default: cryptoerror(LOG_STDERR, gettext("internal error.")); return (FAILURE); } /* Open the kcf.conf file */ if ((pfile = fopen(_PATH_KCF_CONF, "r+")) == NULL) { err = errno; cryptoerror(LOG_STDERR, gettext("failed to update the configuration - %s"), strerror(err)); cryptodebug("failed to open %s for write.", _PATH_KCF_CONF); return (FAILURE); } /* Lock the kcf.conf file */ if (lockf(fileno(pfile), F_TLOCK, 0) == -1) { err = errno; cryptoerror(LOG_STDERR, gettext("failed to update the configuration - %s"), strerror(err)); (void) fclose(pfile); return (FAILURE); } /* * Create a temporary file in the /etc/crypto directory to save * updated configuration file first. */ (void) strlcpy(tmpfile_name, TMPFILE_TEMPLATE, sizeof (tmpfile_name)); if (mkstemp(tmpfile_name) == -1) { err = errno; cryptoerror(LOG_STDERR, gettext("failed to create a temporary file - %s"), strerror(err)); (void) fclose(pfile); return (FAILURE); } if ((pfile_tmp = fopen(tmpfile_name, "w")) == NULL) { err = errno; cryptoerror(LOG_STDERR, gettext("failed to open %s - %s"), tmpfile_name, strerror(err)); (void) fclose(pfile); return (FAILURE); } /* * Loop thru the entire kcf.conf file, insert, modify or delete * an entry. */ while (fgets(buffer, BUFSIZ, pfile) != NULL) { if (add_it) { if (fputs(buffer, pfile_tmp) == EOF) { err = errno; cryptoerror(LOG_STDERR, gettext( "failed to write to a temp file: %s."), strerror(err)); rc = FAILURE; break; } } else { /* modify or delete */ this_entry_matches = B_FALSE; if (!(buffer[0] == '#' || buffer[0] == ' ' || buffer[0] == '\n'|| buffer[0] == '\t')) { /* * Get the provider name from this line and * check if this is the entry to be updated * or deleted. Note: can not use "buffer" * directly because strtok will change its * value. */ (void) strlcpy(buffer2, buffer, BUFSIZ); if ((name = strtok(buffer2, SEP_COLON)) == NULL) { rc = FAILURE; break; } if (strcmp(pent->name, name) == 0) { this_entry_matches = B_TRUE; found_entry = B_TRUE; } } if (!this_entry_matches || !delete_it) { /* write this entry */ if (this_entry_matches) { /* * Modify this entry: get the * updated string and place into buffer. */ (void) strlcpy(buffer, new_str, BUFSIZ); free(new_str); } /* write the (unchanged or modified) entry */ if (fputs(buffer, pfile_tmp) == EOF) { err = errno; cryptoerror(LOG_STDERR, gettext( "failed to write to a temp file: " "%s."), strerror(err)); rc = FAILURE; break; } } } } if ((!delete_it) && (rc != FAILURE)) { if (add_it || !found_entry) { /* append new entry to end of file */ if (fputs(new_str, pfile_tmp) == EOF) { err = errno; cryptoerror(LOG_STDERR, gettext( "failed to write to a temp file: %s."), strerror(err)); rc = FAILURE; } free(new_str); } } (void) fclose(pfile); if (fclose(pfile_tmp) != 0) { err = errno; cryptoerror(LOG_STDERR, gettext("failed to close %s: %s"), tmpfile_name, strerror(err)); return (FAILURE); } /* Copy the temporary file to the kcf.conf file */ if (rename(tmpfile_name, _PATH_KCF_CONF) == -1) { err = errno; cryptoerror(LOG_STDERR, gettext("failed to update the configuration - %s"), strerror(err)); cryptodebug("failed to rename %s to %s: %s", tmpfile, _PATH_KCF_CONF, strerror(err)); rc = FAILURE; } else if (chmod(_PATH_KCF_CONF, S_IRUSR | S_IWUSR | S_IRGRP | S_IROTH) == -1) { err = errno; cryptoerror(LOG_STDERR, gettext("failed to update the configuration - %s"), strerror(err)); cryptodebug("failed to chmod to %s: %s", _PATH_KCF_CONF, strerror(err)); rc = FAILURE; } else { rc = SUCCESS; } if ((rc == FAILURE) && (unlink(tmpfile_name) != 0)) { err = errno; cryptoerror(LOG_STDERR, gettext( "(Warning) failed to remove %s: %s"), tmpfile_name, strerror(err)); } return (rc); } /* * Disable the mechanisms for the provider pointed by *ppent. If allflag is * TRUE, disable all. Otherwise, disable the mechanisms specified in the * dislist argument. The "infolist" argument contains the mechanism list * supported by this provider. */ int disable_mechs(entry_t **ppent, mechlist_t *infolist, boolean_t allflag, mechlist_t *dislist) { entry_t *pent; mechlist_t *plist = NULL; mechlist_t *phead = NULL; mechlist_t *pmech = NULL; int rc = SUCCESS; pent = *ppent; if (pent == NULL) { return (FAILURE); } if (allflag) { free_mechlist(pent->dislist); pent->dis_count = get_mech_count(infolist); if (!(pent->dislist = dup_mechlist(infolist))) { return (FAILURE); } else { return (SUCCESS); } } /* * Not disable all. Now loop thru the mechanisms specified in the * dislist. If the mechanism is not supported by the provider, * ignore it with a warning. If the mechanism is disabled already, * do nothing. Otherwise, prepend it to the beginning of the disabled * list of the provider. */ plist = dislist; while (plist != NULL) { if (!is_in_list(plist->name, infolist)) { cryptoerror(LOG_STDERR, gettext("(Warning) " "%1$s is not a valid mechanism for %2$s."), plist->name, pent->name); } else if (!is_in_list(plist->name, pent->dislist)) { /* Add this mechanism into the disabled list */ if ((pmech = create_mech(plist->name)) == NULL) { rc = FAILURE; break; } if (pent->dislist == NULL) { pent->dislist = pmech; } else { phead = pent->dislist; pent->dislist = pmech; pmech->next = phead; } pent->dis_count++; } plist = plist->next; } return (rc); } /* * Remove the mechanism passed, specified by mech, from the list of * mechanisms, if present in the list. Else, do nothing. * * Returns B_TRUE if mechanism is present in the list. */ boolean_t filter_mechlist(mechlist_t **pmechlist, const char *mech) { int cnt = 0; mechlist_t *ptr, *pptr; boolean_t mech_present = B_FALSE; ptr = pptr = *pmechlist; while (ptr != NULL) { if (strncmp(ptr->name, mech, sizeof (mech_name_t)) == 0) { mech_present = B_TRUE; if (ptr == *pmechlist) { pptr = *pmechlist = ptr->next; free(ptr); ptr = pptr; } else { pptr->next = ptr->next; free(ptr); ptr = pptr->next; } } else { pptr = ptr; ptr = ptr->next; cnt++; } } /* Only one entry is present */ if (cnt == 0) *pmechlist = NULL; return (mech_present); } /* * Print out the mechanism policy for a kernel provider that has an entry * in the kcf.conf file. * * The flag has_random is set to B_TRUE if the provider does random * numbers. The flag has_mechs is set by the caller to B_TRUE if the provider * has some mechanisms. * * If pent is NULL, the provider doesn't have a kcf.conf entry. */ void print_kef_policy(char *provname, entry_t *pent, boolean_t has_random, boolean_t has_mechs) { mechlist_t *ptr = NULL; boolean_t rnd_disabled = B_FALSE; if (pent != NULL) { rnd_disabled = filter_mechlist(&pent->dislist, RANDOM); ptr = pent->dislist; } (void) printf("%s:", provname); if (has_mechs == B_TRUE) { /* * TRANSLATION_NOTE * This code block may need to be modified a bit to avoid * constructing the text message on the fly. */ (void) printf(gettext(" all mechanisms are enabled")); if (ptr != NULL) (void) printf(gettext(", except ")); while (ptr != NULL) { (void) printf("%s", ptr->name); ptr = ptr->next; if (ptr != NULL) (void) printf(","); } if (ptr == NULL) (void) printf("."); } /* * TRANSLATION_NOTE * "random" is a keyword and not to be translated. */ if (rnd_disabled) (void) printf(gettext(" %s is disabled."), "random"); else if (has_random) (void) printf(gettext(" %s is enabled."), "random"); (void) printf("\n"); } /* * Check if a kernel software provider is in the kernel. * * Parameters: * provname Provider name * psoftlist_kernel Optional software provider list. If NULL, it will be * obtained from get_soft_list(). * in_kernel Set to B_TRUE if device is in the kernel, else B_FALSE */ int check_kernel_for_soft(char *provname, crypto_get_soft_list_t *psoftlist_kernel, boolean_t *in_kernel) { char *ptr; int i; boolean_t psoftlist_allocated = B_FALSE; if (provname == NULL) { cryptoerror(LOG_STDERR, gettext("internal error.")); return (FAILURE); } if (psoftlist_kernel == NULL) { if (get_soft_list(&psoftlist_kernel) == FAILURE) { cryptodebug("failed to get the software provider list" " from kernel."); return (FAILURE); } psoftlist_allocated = B_TRUE; } *in_kernel = B_FALSE; ptr = psoftlist_kernel->sl_soft_names; for (i = 0; i < psoftlist_kernel->sl_soft_count; i++) { if (strcmp(provname, ptr) == 0) { *in_kernel = B_TRUE; break; } ptr = ptr + strlen(ptr) + 1; } if (psoftlist_allocated) free(psoftlist_kernel); return (SUCCESS); } /* * Check if a kernel hardware provider is in the kernel. * * Parameters: * provname Provider name * pdevlist Optional Hardware Crypto Device List. If NULL, it will be * obtained from get_dev_list(). * in_kernel Set to B_TRUE if device is in the kernel, otherwise B_FALSE */ int check_kernel_for_hard(char *provname, crypto_get_dev_list_t *pdevlist, boolean_t *in_kernel) { char devname[MAXNAMELEN]; int inst_num; int i; boolean_t dev_list_allocated = B_FALSE; if (provname == NULL) { cryptoerror(LOG_STDERR, gettext("internal error.")); return (FAILURE); } if (split_hw_provname(provname, devname, &inst_num) == FAILURE) { return (FAILURE); } if (pdevlist == NULL) { if (get_dev_list(&pdevlist) == FAILURE) { cryptoerror(LOG_STDERR, gettext("internal error.")); return (FAILURE); } dev_list_allocated = B_TRUE; } *in_kernel = B_FALSE; for (i = 0; i < pdevlist->dl_dev_count; i++) { if ((strcmp(pdevlist->dl_devs[i].le_dev_name, devname) == 0) && (pdevlist->dl_devs[i].le_dev_instance == inst_num)) { *in_kernel = B_TRUE; break; } } if (dev_list_allocated) free(pdevlist); return (SUCCESS); }