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 * Copyright (c) 2006, 2010, Oracle and/or its affiliates. All rights reserved.
22 *
23 * This file implements the KMF certificate to name mapping framework.
24 */
25#include <stdlib.h>
26#include <string.h>
27#include <dlfcn.h>
28#include <libgen.h>
29#include <kmftypes.h>
30#include <kmfapiP.h>
31
32/* Mappers go in the same dir as plugins. */
33#define	DEFAULT_MAPPER_DIR KMF_PLUGIN_PATH
34
35static void
36cleanup_mapper(KMF_HANDLE_T handle)
37{
38	KMF_MAPPER_RECORD *mapper = &handle->policy->mapper;
39	void (*finalize)(KMF_HANDLE_T);
40
41	if (mapper->curpathname != NULL) {
42		free(mapper->curpathname);
43		mapper->curpathname = NULL;
44	}
45	if (mapper->curoptions != NULL) {
46		free(mapper->curoptions);
47		mapper->curoptions = NULL;
48	}
49	if (mapper->dldesc != NULL) {
50		finalize = (void(*)())dlsym(mapper->dldesc,
51		    MAPPER_FINISH_FUNCTION);
52		/* Optional, not an error if it does not exist. */
53		if (finalize != NULL)
54			finalize(handle);
55
56		(void) dlclose(mapper->dldesc);
57		mapper->dldesc = NULL;
58	}
59}
60
61/* The caller is expected to free the returned string. */
62char *
63get_mapper_pathname(char *name, char *dir)
64{
65	char *pathname = NULL;
66	int len;
67
68	if (name == NULL)
69		return (NULL);
70
71	if (dir == NULL)
72		dir = DEFAULT_MAPPER_DIR;
73
74	/*
75	 * MAPPER_NAME_TEMPLATE has 2 extra characters (%s) which make up for
76	 * the "/" and the terminating NULL when computing the total length.
77	 */
78	len = strlen(name) + strlen(MAPPER_NAME_TEMPLATE) + strlen(dir);
79
80	pathname = malloc(len);
81	if (pathname == NULL)
82		return (NULL);
83	(void) memset(pathname, 0, len);
84	/* Avoid double forward slash if the dir's last character is "/". */
85	(void) snprintf(pathname, len, "%s%s" MAPPER_NAME_TEMPLATE,
86	    dir, dir[strlen(dir) - 1] == '/' ? "" : "/", name);
87
88	return (pathname);
89}
90
91static KMF_RETURN
92open_mapper_library(KMF_MAPPER_RECORD *map)
93{
94	KMF_RETURN ret = KMF_OK;
95
96	map->dldesc = dlopen(map->curpathname, RTLD_LAZY | RTLD_PARENT);
97	if (map->dldesc == NULL)
98		return (KMF_ERR_MAPPER_OPEN);
99
100	return (ret);
101}
102
103/*
104 * The mapping framework uses either attributes or the policy file. Those two
105 * sources are never mixed. We always need a mapper name or a mapper pathname
106 * but these two are mutually exclusive. Directory can be set only if name is
107 * set.
108 */
109KMF_RETURN
110kmf_cert_to_name_mapping_initialize(KMF_HANDLE_T handle, int numattr,
111	KMF_ATTRIBUTE *attrlist)
112{
113	KMF_RETURN ret = KMF_OK;
114	KMF_RETURN (*initialize)(KMF_HANDLE_T, char *);
115	KMF_MAPPER_RECORD *map = NULL;
116	char *dir = NULL;
117	char *name = NULL;
118	char *opts = NULL;
119	char *path = NULL;
120	char *tmppath = NULL;
121	char *old_curpathname = NULL;
122	char *old_curoptions = NULL;
123
124	if (handle == NULL)
125		return (KMF_ERR_BAD_PARAMETER);
126
127	map = &handle->policy->mapper;
128	old_curpathname = map->curpathname;
129	old_curoptions = map->curoptions;
130
131	name = kmf_get_attr_ptr(KMF_MAPPER_NAME_ATTR, attrlist, numattr);
132	dir = kmf_get_attr_ptr(KMF_DIRPATH_ATTR, attrlist, numattr);
133	path = kmf_get_attr_ptr(KMF_MAPPER_PATH_ATTR, attrlist, numattr);
134	opts = kmf_get_attr_ptr(KMF_MAPPER_OPTIONS_ATTR, attrlist, numattr);
135
136	if (path != NULL) {
137		/* Mutually exclusive. */
138		if (name != NULL || dir != NULL)
139			return (KMF_ERR_BAD_PARAMETER);
140		tmppath = strdup(path);
141		if (tmppath == NULL)
142			return (KMF_ERR_MEMORY);
143	/* If we only have a name and possibly a dir, we can find the path. */
144	} else if (name != NULL) {
145		tmppath = get_mapper_pathname(name, dir);
146		/*
147		 * If we were given name but the returned path is still NULL,
148		 * return an error.
149		 */
150		if (tmppath == NULL)
151			return (KMF_ERR_MEMORY);
152	/* Can not exist standalone. */
153	} else if (dir != NULL || opts != NULL) {
154			return (KMF_ERR_BAD_PARAMETER);
155	/* No attributes define the mapper so let's use the policy database. */
156	} else if (map->pathname != NULL) {
157		tmppath = strdup(map->pathname);
158		if (tmppath == NULL)
159			return (KMF_ERR_MEMORY);
160		opts = map->options;
161	} else if (map->mapname != NULL) {
162		tmppath = get_mapper_pathname(map->mapname, map->dir);
163		/*
164		 * If we were given name but the returned path is still NULL,
165		 * return an error.
166		 */
167		if (tmppath == NULL)
168			return (KMF_ERR_MEMORY);
169		opts = map->options;
170	} else {
171		/*
172		 * Either a name or a full pathname must be provided whether
173		 * from attributes or the policy database.
174		 */
175		return (KMF_ERR_BAD_PARAMETER);
176	}
177
178	/*
179	 * Dlopen the mapper specified by the policy. If anything goes wrong
180	 * just return an error. We do not have to worry about resetting
181	 * curpathname and curoptions to the previous values since there was no
182	 * mapper initialized beforehand.
183	 *
184	 * No mapper was open so stored curoptions and curpathname are
185	 * already NULL and need not to be freed.
186	 */
187	if (map->dldesc == NULL) {
188		map->curpathname = tmppath;
189		if (opts != NULL) {
190			map->curoptions = strdup(opts);
191			if (map->curoptions == NULL) {
192				free(map->curpathname);
193				map->curpathname = NULL;
194				return (KMF_ERR_MEMORY);
195			}
196		} else
197			map->curoptions = NULL;
198
199		if ((ret = open_mapper_library(map)) != KMF_OK) {
200			free(map->curpathname);
201			map->curpathname = NULL;
202			if (map->curoptions != NULL) {
203				free(map->curoptions);
204				map->curoptions = NULL;
205			}
206			return (ret);
207		}
208
209		goto end;
210	}
211
212	/*
213	 * We already have an open mapper, let's see if this is a new mapper
214	 * library.
215	 */
216	if (map->curpathname != NULL &&
217	    /* No change in mapper pathname. */
218	    strcmp(map->curpathname, tmppath) == 0) {
219		/* New options are empty while we had some before. */
220		if (map->curoptions != NULL && opts == NULL) {
221			map->curoptions = NULL;
222		/* We have some options now while we had none before. */
223		} else if (map->curoptions == NULL && opts != NULL) {
224			if ((map->curoptions = strdup(opts)) == NULL)
225				goto err_mem;
226		/* We got different options. */
227		} else if (strcmp(map->curoptions, opts) != 0) {
228			if ((map->curoptions = strdup(opts)) == NULL)
229				goto err_mem;
230		} else {
231			/*
232			 * Same options, no free() of current options is
233			 * required.
234			 */
235			old_curoptions = NULL;
236		}
237
238		/* Free old options if applicable. */
239		if (old_curoptions != NULL)
240			free(old_curoptions);
241	} else {
242		/*
243		 * This is a new mapper path, clean up the old data and open the
244		 * new mapper.
245		 */
246		cleanup_mapper(handle);
247		/* These two are no longer valid. */
248		old_curoptions = NULL;
249		old_curpathname = NULL;
250		map->curpathname = tmppath;
251		if (opts != NULL) {
252			map->curoptions = strdup(opts);
253			if (map->curoptions == NULL)
254				goto err_mem;
255		}
256		if ((ret = open_mapper_library(map)) != KMF_OK) {
257			/*
258			 * This will cleanup curoptions and curpathname, and
259			 * ignores the dldesc since it is NULL. Do not free
260			 * tmppath, it will be freed through map->curpathname.
261			 */
262			cleanup_mapper(handle);
263			return (ret);
264		}
265	}
266
267end:
268	initialize = (KMF_RETURN(*)())dlsym(map->dldesc,
269	    MAPPER_INIT_FUNCTION);
270	/* Optional, not an error if it does not exist. */
271	ret = KMF_OK;
272	if (initialize != NULL)
273		ret = initialize(handle, map->curoptions);
274
275	if (ret != KMF_OK)
276		cleanup_mapper(handle);
277
278	return (ret);
279
280err_mem:
281	/*
282	 * Try to put the old curpathname and curoptions back there. In theory,
283	 * the application might be able to continue to use the old mapping
284	 * unless we already called cleanup_mapper(). However, it's neither
285	 * recommended nor officially supported. The app should initialize the
286	 * old mapping again.
287	 */
288	if (tmppath != NULL)
289		free(tmppath);
290	map->curoptions = old_curoptions;
291	map->curpathname = old_curpathname;
292	return (KMF_ERR_MEMORY);
293}
294
295KMF_RETURN
296kmf_cert_to_name_mapping_finalize(KMF_HANDLE_T handle)
297{
298	if (handle == NULL)
299		return (KMF_ERR_BAD_PARAMETER);
300
301	cleanup_mapper(handle);
302
303	return (KMF_OK);
304}
305
306KMF_RETURN
307kmf_map_cert_to_name(KMF_HANDLE_T handle, KMF_DATA *cert, KMF_DATA *name)
308{
309	KMF_MAPPER_RECORD *map = NULL;
310	KMF_RETURN (*cert2name)(KMF_HANDLE *, KMF_DATA *, KMF_DATA *);
311
312	if (handle == NULL)
313		return (KMF_ERR_BAD_PARAMETER);
314
315	map = &handle->policy->mapper;
316	if (map->dldesc == NULL)
317		return (KMF_ERR_MAPPER_NOT_FOUND);
318
319	cert2name = (KMF_RETURN(*)())dlsym(map->dldesc,
320	    MAP_CERT_TO_NAME_FUNCTION);
321	if (cert2name == NULL)
322		return (KMF_ERR_FUNCTION_NOT_FOUND);
323
324	return (cert2name(handle, cert, name));
325}
326
327/*
328 * If mapped_name is non-NULL the caller is later expected to free its Data
329 * after use.
330 */
331KMF_RETURN
332kmf_match_cert_to_name(KMF_HANDLE_T handle, KMF_DATA *cert,
333    KMF_DATA *name_to_match, KMF_DATA *mapped_name)
334{
335	KMF_MAPPER_RECORD *map = NULL;
336	KMF_RETURN (*cert2name)(KMF_HANDLE *, KMF_DATA *, KMF_DATA *,
337	    KMF_DATA *);
338
339	if (handle == NULL)
340		return (KMF_ERR_BAD_PARAMETER);
341
342	map = &handle->policy->mapper;
343
344	if (map->curpathname == NULL || map->dldesc == NULL)
345		return (KMF_ERR_MAPPER_NOT_FOUND);
346
347	cert2name = (KMF_RETURN(*)())dlsym(map->dldesc,
348	    MATCH_CERT_TO_NAME_FUNCTION);
349	if (cert2name == NULL)
350		return (KMF_ERR_FUNCTION_NOT_FOUND);
351
352	return (cert2name(handle, cert, name_to_match, mapped_name));
353}
354
355/*
356 * The caller is responsible for freeing the error string (ie., *errstr) when
357 * done with it.
358 */
359KMF_RETURN
360kmf_get_mapper_error_str(KMF_HANDLE_T handle, char **errstr)
361{
362	KMF_HANDLE *h = NULL;
363	KMF_MAPPER_RECORD *map = NULL;
364	KMF_RETURN (*err2string)(KMF_HANDLE *, char **);
365
366	if (handle == NULL || errstr == NULL)
367		return (KMF_ERR_BAD_PARAMETER);
368
369	h = (KMF_HANDLE *)handle;
370	map = &(h->policy->mapper);
371
372	if (map->curpathname == NULL || map->dldesc == NULL)
373		return (KMF_ERR_MAPPER_NOT_FOUND);
374
375	err2string = (KMF_RETURN(*)())dlsym(map->dldesc,
376	    MAPPER_ERROR_STRING_FUNCTION);
377	if (err2string == NULL)
378		return (KMF_ERR_FUNCTION_NOT_FOUND);
379
380	return (err2string(h, errstr));
381}
382
383void
384kmf_set_mapper_lasterror(KMF_HANDLE_T handle, uint32_t err)
385{
386	handle->mapstate->lastmappererr = err;
387}
388
389uint32_t
390kmf_get_mapper_lasterror(KMF_HANDLE_T handle)
391{
392	return (handle->mapstate->lastmappererr);
393}
394
395void
396kmf_set_mapper_options(KMF_HANDLE_T handle, void *opts)
397{
398	handle->mapstate->options = opts;
399}
400
401void *
402kmf_get_mapper_options(KMF_HANDLE_T handle)
403{
404	return (handle->mapstate->options);
405}
406