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) 2010, Oracle and/or its affiliates. All rights reserved.
22 */
23
24/*
25 * KMF CN certificate-to-name mapper.
26 */
27
28#include <kmftypes.h>
29#include <kmfapi.h>
30#include <fcntl.h>
31
32/*
33 * KMF uses long identifiers for RDN processing which makes it hard to keep
34 * cstyle cleanliness without using some auxiliary macros. Parameter 'x' is of
35 * the KMF_X509_NAME type.
36 */
37#define	RDN_VALUE(x, i) \
38	(&x.RelativeDistinguishedName[i].AttributeTypeAndValue->value)
39
40#define	RDN_OID(x, i) \
41	(&x.RelativeDistinguishedName[i].AttributeTypeAndValue->type)
42
43#define	RDN_NPAIRS(x, i) (x.RelativeDistinguishedName[i].numberOfPairs)
44
45/* Error codes specific to this mapper. */
46#define	CN_MAPPER_CN_RDN_NOT_PRESENT	1
47
48typedef struct cooked_opts {
49	int casesensitive;
50} cooked_opts;
51
52KMF_RETURN
53mapper_initialize(KMF_HANDLE_T h, char *options)
54{
55	cooked_opts *opts;
56
57	if ((opts = malloc(sizeof (cooked_opts))) == NULL)
58		return (KMF_ERR_MEMORY);
59
60	/* This is the default. */
61	opts->casesensitive = B_FALSE;
62
63	if (options != NULL) {
64		if (strcmp(options, "casesensitive") == 0)
65			opts->casesensitive = B_TRUE;
66	}
67
68	kmf_set_mapper_options(h, opts);
69
70	return (KMF_OK);
71}
72
73void
74mapper_finalize(KMF_HANDLE_T h)
75{
76	void *opts;
77
78	if ((opts = kmf_get_mapper_options(h)) != NULL)
79		free(opts);
80	kmf_set_mapper_options(h, NULL);
81}
82
83/*
84 * The CN string returned in name.Data will be NULL-terminated. The caller is
85 * expected to free name->Data after use.
86 */
87KMF_RETURN
88mapper_map_cert_to_name(KMF_HANDLE_T h, KMF_DATA *cert, KMF_DATA *name)
89{
90	int i, j;
91	char *dn;
92	KMF_RETURN rv;
93	uchar_t *cn = NULL;
94	KMF_X509_NAME x509name;
95
96	kmf_set_mapper_lasterror(h, KMF_OK);
97
98	if ((rv = kmf_get_cert_subject_str(h, cert, &dn)) != KMF_OK)
99		return (rv);
100
101	if ((rv = kmf_dn_parser(dn, &x509name)) != KMF_OK)
102		return (rv);
103
104	/* Go through the list of RDNs and look for the CN. */
105	for (i = 0; i < x509name.numberOfRDNs; ++i) {
106		for (j = 0; j < RDN_NPAIRS(x509name, i); ++j) {
107			KMF_OID *oid = RDN_OID(x509name, i);
108			KMF_DATA *data = RDN_VALUE(x509name, i);
109
110			if (oid == NULL)
111				continue;
112
113			/* Is this RDN a Common Name? */
114			if (oid->Length == KMFOID_CommonName.Length &&
115			    memcmp(oid->Data, KMFOID_CommonName.Data,
116			    oid->Length) == 0) {
117				if ((cn = malloc(data->Length + 1)) == NULL) {
118					kmf_free_dn(&x509name);
119					return (KMF_ERR_MEMORY);
120				}
121				(void) memcpy(cn, data->Data, data->Length);
122				/* Terminate the string. */
123				cn[data->Length] = '\0';
124				name->Length = data->Length + 1;
125				name->Data = cn;
126				goto finished;
127			}
128		}
129	}
130
131finished:
132	kmf_free_dn(&x509name);
133	if (cn != NULL)
134		return (KMF_OK);
135	else {
136		kmf_set_mapper_lasterror(h, CN_MAPPER_CN_RDN_NOT_PRESENT);
137		return (KMF_ERR_INTERNAL);
138	}
139}
140
141/*
142 * Note that name_to_match->Data might or might not be NULL terminated. If
143 * mapped_name->Length returned is greater than zero the caller is expected to
144 * free mapped_name->Data after use.
145 */
146KMF_RETURN
147mapper_match_cert_to_name(KMF_HANDLE_T h, KMF_DATA *cert,
148    KMF_DATA *name_to_match, KMF_DATA *mapped_name)
149{
150	int ret;
151	KMF_RETURN rv;
152	KMF_DATA get_name;
153	cooked_opts *opts = NULL;
154
155	opts = (cooked_opts *)kmf_get_mapper_options(h);
156
157	/* Initialize the output parameter. */
158	if (mapped_name != NULL) {
159		mapped_name->Length = 0;
160		mapped_name->Data = NULL;
161	}
162
163	if ((rv = mapper_map_cert_to_name(h, cert, &get_name)) != KMF_OK)
164		return (rv);
165
166	/*
167	 * If name_to_match->Data is not NULL terminated, check that we have the
168	 * same number of characters.
169	 */
170	if (name_to_match->Data[name_to_match->Length - 1] != '\0')
171		/* We know that get_name.Data is NULL terminated. */
172		if (name_to_match->Length != get_name.Length - 1)
173			return (KMF_ERR_NAME_NOT_MATCHED);
174
175	/*
176	 * Compare the strings. We must use name_to_match->Length in case
177	 * name_to_match->Data was not NULL terminated. If we used
178	 * get_name.Length we could overrun name_to_match->Data by one byte.
179	 */
180	if (opts->casesensitive == B_TRUE)
181		ret = strncmp((char *)name_to_match->Data,
182		    (char *)get_name.Data, name_to_match->Length);
183	else
184		ret = strncasecmp((char *)name_to_match->Data,
185		    (char *)get_name.Data, name_to_match->Length);
186
187	if (mapped_name != NULL) {
188		mapped_name->Length = get_name.Length;
189		mapped_name->Data = get_name.Data;
190	} else
191		kmf_free_data(&get_name);
192
193	if (ret == 0)
194		return (KMF_OK);
195	else
196		return (KMF_ERR_NAME_NOT_MATCHED);
197}
198
199/* The caller is responsible for freeing the error string when done with it. */
200KMF_RETURN
201mapper_get_error_str(KMF_HANDLE_T h, char **errstr)
202{
203	uint32_t lasterr;
204
205	lasterr = kmf_get_mapper_lasterror(h);
206	*errstr = NULL;
207	if (lasterr == 0)
208		return (KMF_ERR_MISSING_ERRCODE);
209
210	switch (lasterr) {
211	case CN_MAPPER_CN_RDN_NOT_PRESENT:
212		*errstr = (char *)strdup("CN_MAPPER_CN_RDN_NOT_PRESENT");
213		break;
214	default:
215		*errstr = (char *)strdup("KMF_ERR_MISSING_MAPPER_ERRCODE");
216	}
217
218	if (*errstr == NULL)
219		return (KMF_ERR_MEMORY);
220
221	return (KMF_OK);
222}
223