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 
48 typedef struct cooked_opts {
49 	int casesensitive;
50 } cooked_opts;
51 
52 KMF_RETURN
mapper_initialize(KMF_HANDLE_T h,char * options)53 mapper_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 
73 void
mapper_finalize(KMF_HANDLE_T h)74 mapper_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  */
87 KMF_RETURN
mapper_map_cert_to_name(KMF_HANDLE_T h,KMF_DATA * cert,KMF_DATA * name)88 mapper_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 
131 finished:
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  */
146 KMF_RETURN
mapper_match_cert_to_name(KMF_HANDLE_T h,KMF_DATA * cert,KMF_DATA * name_to_match,KMF_DATA * mapped_name)147 mapper_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. */
200 KMF_RETURN
mapper_get_error_str(KMF_HANDLE_T h,char ** errstr)201 mapper_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