/* * 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 (c) 2006, 2010, Oracle and/or its affiliates. All rights reserved. * * This file implements the KMF certificate to name mapping framework. */ #include #include #include #include #include #include /* Mappers go in the same dir as plugins. */ #define DEFAULT_MAPPER_DIR KMF_PLUGIN_PATH static void cleanup_mapper(KMF_HANDLE_T handle) { KMF_MAPPER_RECORD *mapper = &handle->policy->mapper; void (*finalize)(KMF_HANDLE_T); if (mapper->curpathname != NULL) { free(mapper->curpathname); mapper->curpathname = NULL; } if (mapper->curoptions != NULL) { free(mapper->curoptions); mapper->curoptions = NULL; } if (mapper->dldesc != NULL) { finalize = (void(*)())dlsym(mapper->dldesc, MAPPER_FINISH_FUNCTION); /* Optional, not an error if it does not exist. */ if (finalize != NULL) finalize(handle); (void) dlclose(mapper->dldesc); mapper->dldesc = NULL; } } /* The caller is expected to free the returned string. */ char * get_mapper_pathname(char *name, char *dir) { char *pathname = NULL; int len; if (name == NULL) return (NULL); if (dir == NULL) dir = DEFAULT_MAPPER_DIR; /* * MAPPER_NAME_TEMPLATE has 2 extra characters (%s) which make up for * the "/" and the terminating NULL when computing the total length. */ len = strlen(name) + strlen(MAPPER_NAME_TEMPLATE) + strlen(dir); pathname = malloc(len); if (pathname == NULL) return (NULL); (void) memset(pathname, 0, len); /* Avoid double forward slash if the dir's last character is "/". */ (void) snprintf(pathname, len, "%s%s" MAPPER_NAME_TEMPLATE, dir, dir[strlen(dir) - 1] == '/' ? "" : "/", name); return (pathname); } static KMF_RETURN open_mapper_library(KMF_MAPPER_RECORD *map) { KMF_RETURN ret = KMF_OK; map->dldesc = dlopen(map->curpathname, RTLD_LAZY | RTLD_PARENT); if (map->dldesc == NULL) return (KMF_ERR_MAPPER_OPEN); return (ret); } /* * The mapping framework uses either attributes or the policy file. Those two * sources are never mixed. We always need a mapper name or a mapper pathname * but these two are mutually exclusive. Directory can be set only if name is * set. */ KMF_RETURN kmf_cert_to_name_mapping_initialize(KMF_HANDLE_T handle, int numattr, KMF_ATTRIBUTE *attrlist) { KMF_RETURN ret = KMF_OK; KMF_RETURN (*initialize)(KMF_HANDLE_T, char *); KMF_MAPPER_RECORD *map = NULL; char *dir = NULL; char *name = NULL; char *opts = NULL; char *path = NULL; char *tmppath = NULL; char *old_curpathname = NULL; char *old_curoptions = NULL; if (handle == NULL) return (KMF_ERR_BAD_PARAMETER); map = &handle->policy->mapper; old_curpathname = map->curpathname; old_curoptions = map->curoptions; name = kmf_get_attr_ptr(KMF_MAPPER_NAME_ATTR, attrlist, numattr); dir = kmf_get_attr_ptr(KMF_DIRPATH_ATTR, attrlist, numattr); path = kmf_get_attr_ptr(KMF_MAPPER_PATH_ATTR, attrlist, numattr); opts = kmf_get_attr_ptr(KMF_MAPPER_OPTIONS_ATTR, attrlist, numattr); if (path != NULL) { /* Mutually exclusive. */ if (name != NULL || dir != NULL) return (KMF_ERR_BAD_PARAMETER); tmppath = strdup(path); if (tmppath == NULL) return (KMF_ERR_MEMORY); /* If we only have a name and possibly a dir, we can find the path. */ } else if (name != NULL) { tmppath = get_mapper_pathname(name, dir); /* * If we were given name but the returned path is still NULL, * return an error. */ if (tmppath == NULL) return (KMF_ERR_MEMORY); /* Can not exist standalone. */ } else if (dir != NULL || opts != NULL) { return (KMF_ERR_BAD_PARAMETER); /* No attributes define the mapper so let's use the policy database. */ } else if (map->pathname != NULL) { tmppath = strdup(map->pathname); if (tmppath == NULL) return (KMF_ERR_MEMORY); opts = map->options; } else if (map->mapname != NULL) { tmppath = get_mapper_pathname(map->mapname, map->dir); /* * If we were given name but the returned path is still NULL, * return an error. */ if (tmppath == NULL) return (KMF_ERR_MEMORY); opts = map->options; } else { /* * Either a name or a full pathname must be provided whether * from attributes or the policy database. */ return (KMF_ERR_BAD_PARAMETER); } /* * Dlopen the mapper specified by the policy. If anything goes wrong * just return an error. We do not have to worry about resetting * curpathname and curoptions to the previous values since there was no * mapper initialized beforehand. * * No mapper was open so stored curoptions and curpathname are * already NULL and need not to be freed. */ if (map->dldesc == NULL) { map->curpathname = tmppath; if (opts != NULL) { map->curoptions = strdup(opts); if (map->curoptions == NULL) { free(map->curpathname); map->curpathname = NULL; return (KMF_ERR_MEMORY); } } else map->curoptions = NULL; if ((ret = open_mapper_library(map)) != KMF_OK) { free(map->curpathname); map->curpathname = NULL; if (map->curoptions != NULL) { free(map->curoptions); map->curoptions = NULL; } return (ret); } goto end; } /* * We already have an open mapper, let's see if this is a new mapper * library. */ if (map->curpathname != NULL && /* No change in mapper pathname. */ strcmp(map->curpathname, tmppath) == 0) { /* New options are empty while we had some before. */ if (map->curoptions != NULL && opts == NULL) { map->curoptions = NULL; /* We have some options now while we had none before. */ } else if (map->curoptions == NULL && opts != NULL) { if ((map->curoptions = strdup(opts)) == NULL) goto err_mem; /* We got different options. */ } else if (strcmp(map->curoptions, opts) != 0) { if ((map->curoptions = strdup(opts)) == NULL) goto err_mem; } else { /* * Same options, no free() of current options is * required. */ old_curoptions = NULL; } /* Free old options if applicable. */ if (old_curoptions != NULL) free(old_curoptions); } else { /* * This is a new mapper path, clean up the old data and open the * new mapper. */ cleanup_mapper(handle); /* These two are no longer valid. */ old_curoptions = NULL; old_curpathname = NULL; map->curpathname = tmppath; if (opts != NULL) { map->curoptions = strdup(opts); if (map->curoptions == NULL) goto err_mem; } if ((ret = open_mapper_library(map)) != KMF_OK) { /* * This will cleanup curoptions and curpathname, and * ignores the dldesc since it is NULL. Do not free * tmppath, it will be freed through map->curpathname. */ cleanup_mapper(handle); return (ret); } } end: initialize = (KMF_RETURN(*)())dlsym(map->dldesc, MAPPER_INIT_FUNCTION); /* Optional, not an error if it does not exist. */ ret = KMF_OK; if (initialize != NULL) ret = initialize(handle, map->curoptions); if (ret != KMF_OK) cleanup_mapper(handle); return (ret); err_mem: /* * Try to put the old curpathname and curoptions back there. In theory, * the application might be able to continue to use the old mapping * unless we already called cleanup_mapper(). However, it's neither * recommended nor officially supported. The app should initialize the * old mapping again. */ if (tmppath != NULL) free(tmppath); map->curoptions = old_curoptions; map->curpathname = old_curpathname; return (KMF_ERR_MEMORY); } KMF_RETURN kmf_cert_to_name_mapping_finalize(KMF_HANDLE_T handle) { if (handle == NULL) return (KMF_ERR_BAD_PARAMETER); cleanup_mapper(handle); return (KMF_OK); } KMF_RETURN kmf_map_cert_to_name(KMF_HANDLE_T handle, KMF_DATA *cert, KMF_DATA *name) { KMF_MAPPER_RECORD *map = NULL; KMF_RETURN (*cert2name)(KMF_HANDLE *, KMF_DATA *, KMF_DATA *); if (handle == NULL) return (KMF_ERR_BAD_PARAMETER); map = &handle->policy->mapper; if (map->dldesc == NULL) return (KMF_ERR_MAPPER_NOT_FOUND); cert2name = (KMF_RETURN(*)())dlsym(map->dldesc, MAP_CERT_TO_NAME_FUNCTION); if (cert2name == NULL) return (KMF_ERR_FUNCTION_NOT_FOUND); return (cert2name(handle, cert, name)); } /* * If mapped_name is non-NULL the caller is later expected to free its Data * after use. */ KMF_RETURN kmf_match_cert_to_name(KMF_HANDLE_T handle, KMF_DATA *cert, KMF_DATA *name_to_match, KMF_DATA *mapped_name) { KMF_MAPPER_RECORD *map = NULL; KMF_RETURN (*cert2name)(KMF_HANDLE *, KMF_DATA *, KMF_DATA *, KMF_DATA *); if (handle == NULL) return (KMF_ERR_BAD_PARAMETER); map = &handle->policy->mapper; if (map->curpathname == NULL || map->dldesc == NULL) return (KMF_ERR_MAPPER_NOT_FOUND); cert2name = (KMF_RETURN(*)())dlsym(map->dldesc, MATCH_CERT_TO_NAME_FUNCTION); if (cert2name == NULL) return (KMF_ERR_FUNCTION_NOT_FOUND); return (cert2name(handle, cert, name_to_match, mapped_name)); } /* * The caller is responsible for freeing the error string (ie., *errstr) when * done with it. */ KMF_RETURN kmf_get_mapper_error_str(KMF_HANDLE_T handle, char **errstr) { KMF_HANDLE *h = NULL; KMF_MAPPER_RECORD *map = NULL; KMF_RETURN (*err2string)(KMF_HANDLE *, char **); if (handle == NULL || errstr == NULL) return (KMF_ERR_BAD_PARAMETER); h = (KMF_HANDLE *)handle; map = &(h->policy->mapper); if (map->curpathname == NULL || map->dldesc == NULL) return (KMF_ERR_MAPPER_NOT_FOUND); err2string = (KMF_RETURN(*)())dlsym(map->dldesc, MAPPER_ERROR_STRING_FUNCTION); if (err2string == NULL) return (KMF_ERR_FUNCTION_NOT_FOUND); return (err2string(h, errstr)); } void kmf_set_mapper_lasterror(KMF_HANDLE_T handle, uint32_t err) { handle->mapstate->lastmappererr = err; } uint32_t kmf_get_mapper_lasterror(KMF_HANDLE_T handle) { return (handle->mapstate->lastmappererr); } void kmf_set_mapper_options(KMF_HANDLE_T handle, void *opts) { handle->mapstate->options = opts; } void * kmf_get_mapper_options(KMF_HANDLE_T handle) { return (handle->mapstate->options); }