xref: /titanic_44/usr/src/lib/libkmf/libkmf/common/rdn_parser.c (revision 99ebb4ca412cb0a19d77a3899a87c055b9c30fa8)
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 /*
22  * The contents of this file are subject to the Mozilla Public
23  * License Version 1.1 (the "License"); you may not use this file
24  * except in compliance with the License. You may obtain a copy of
25  * the License at http://www.mozilla.org/MPL/
26  *
27  * Software distributed under the License is distributed on an "AS
28  * IS" basis, WITHOUT WARRANTY OF ANY KIND, either express or
29  * implied. See the License for the specific language governing
30  * rights and limitations under the License.
31  *
32  * The Original Code is the Netscape security libraries.
33  *
34  * The Initial Developer of the Original Code is Netscape
35  * Communications Corporation.  Portions created by Netscape are
36  * Copyright (C) 1994-2000 Netscape Communications Corporation.  All
37  * Rights Reserved.
38  *
39  * Contributor(s):
40  *
41  * Alternatively, the contents of this file may be used under the
42  * terms of the GNU General Public License Version 2 or later (the
43  * "GPL"), in which case the provisions of the GPL are applicable
44  * instead of those above.  If you wish to allow use of your
45  * version of this file only under the terms of the GPL and not to
46  * allow others to use your version of this file under the MPL,
47  * indicate your decision by deleting the provisions above and
48  * replace them with the notice and other provisions required by
49  * the GPL.  If you do not delete the provisions above, a recipient
50  * may use your version of this file under either the MPL or the
51  * GPL.
52  */
53 /*
54  * Copyright 2006 Sun Microsystems, Inc.  All rights reserved.
55  * Use is subject to license terms.
56  *
57  * File: rdn_parser.c
58  */
59 
60 #pragma ident	"%Z%%M%	%I%	%E% SMI"
61 
62 
63 #include <strings.h>
64 #include <stdlib.h>
65 #include <kmfapi.h>
66 #include <kmfapiP.h>
67 #include <ber_der.h>
68 #include <rdn_parser.h>
69 #include <stdio.h>
70 #include <values.h>
71 
72 /*
73  * The order here is important.  The OIDs are arranged in order of
74  * significance.  The CN is the most specific value, the C (country)
75  * is less specific, etc.  Add to this list with care.
76  */
77 static const struct NameToKind name2kinds[] = {
78 { "CN",		OID_AVA_COMMON_NAME,	(KMF_OID *)&KMFOID_CommonName},
79 { "SN",		OID_AVA_SURNAME,	(KMF_OID *)&KMFOID_Surname},
80 { "GN",		OID_AVA_GIVEN_NAME,	(KMF_OID *)&KMFOID_GivenName},
81 { "emailAddress", OID_PKCS9_EMAIL_ADDRESS, (KMF_OID *)&KMFOID_EmailAddress},
82 { "E",		OID_PKCS9_EMAIL_ADDRESS, (KMF_OID *)&KMFOID_EmailAddress},
83 { "MAIL",	OID_RFC1274_MAIL,	(KMF_OID *)&KMFOID_RFC822mailbox},
84 { "STREET",	OID_AVA_STREET_ADDRESS, (KMF_OID *)&KMFOID_StreetAddress},
85 { "UID",	OID_RFC1274_UID,	(KMF_OID *)&KMFOID_userid},
86 { "OU",		OID_AVA_ORGANIZATIONAL_UNIT_NAME,
87 			(KMF_OID *)&KMFOID_OrganizationalUnitName},
88 { "O",		OID_AVA_ORGANIZATION_NAME, (KMF_OID *)&KMFOID_OrganizationName},
89 { "L",		OID_AVA_LOCALITY,	(KMF_OID *)&KMFOID_LocalityName},
90 { "ST",		OID_AVA_STATE_OR_PROVINCE,
91 	(KMF_OID *)&KMFOID_StateProvinceName},
92 { "C",		OID_AVA_COUNTRY_NAME,	(KMF_OID *)&KMFOID_CountryName},
93 { "DC",		OID_AVA_DC,		(KMF_OID *)&KMFOID_domainComponent},
94 { 0,		OID_UNKNOWN, NULL}
95 };
96 
97 static KMF_BOOL
98 IsPrintable(unsigned char *data, unsigned len)
99 {
100 	unsigned char ch, *end;
101 
102 	end = data + len;
103 	while (data < end) {
104 		ch = *data++;
105 		if (!IS_PRINTABLE(ch)) {
106 			return (B_FALSE);
107 		}
108 	}
109 	return (B_TRUE);
110 }
111 
112 static KMF_BOOL
113 Is7Bit(unsigned char *data, unsigned len)
114 {
115 	unsigned char ch, *end;
116 
117 	end = data + len;
118 	while (data < end) {
119 		ch = *data++;
120 		if ((ch & 0x80)) {
121 			return (B_FALSE);
122 		}
123 	}
124 	return (B_TRUE);
125 }
126 
127 static void
128 skipSpace(char **pbp, char *endptr)
129 {
130 	char *bp = *pbp;
131 	while (bp < endptr && OPTIONAL_SPACE(*bp)) {
132 		bp++;
133 	}
134 	*pbp = bp;
135 }
136 
137 static KMF_RETURN
138 scanTag(char **pbp, char *endptr, char *tagBuf, int tagBufSize)
139 {
140 	char *bp, *tagBufp;
141 	int taglen;
142 
143 	if (tagBufSize <= 0)
144 		return (KMF_ERR_INTERNAL);
145 
146 	/* skip optional leading space */
147 	skipSpace(pbp, endptr);
148 	if (*pbp == endptr) {
149 		/* nothing left */
150 		return (KMF_ERR_RDN_PARSER);
151 	}
152 
153 	/* fill tagBuf */
154 	taglen = 0;
155 	bp = *pbp;
156 	tagBufp = tagBuf;
157 	while (bp < endptr && !OPTIONAL_SPACE(*bp) && (*bp != C_EQUAL)) {
158 		if (++taglen >= tagBufSize) {
159 			*pbp = bp;
160 			return (KMF_ERR_RDN_PARSER);
161 		}
162 		*tagBufp++ = *bp++;
163 	}
164 	/* null-terminate tagBuf -- guaranteed at least one space left */
165 	*tagBufp++ = 0;
166 	*pbp = bp;
167 
168 	/*
169 	 * skip trailing spaces till we hit something - should be
170 	 * an equal sign
171 	 */
172 	skipSpace(pbp, endptr);
173 	if (*pbp == endptr) {
174 		/* nothing left */
175 		return (KMF_ERR_RDN_PARSER);
176 	}
177 	if (**pbp != C_EQUAL) {
178 		/* should be an equal sign */
179 		return (KMF_ERR_RDN_PARSER);
180 	}
181 	/* skip over the equal sign */
182 	(*pbp)++;
183 
184 	return (KMF_OK);
185 }
186 
187 static KMF_RETURN
188 scanVal(char **pbp, char *endptr, char *valBuf, int valBufSize)
189 {
190 	char *bp, *valBufp;
191 	int vallen;
192 	boolean_t isQuoted;
193 
194 	if (valBufSize <= 0)
195 		return (KMF_ERR_INTERNAL);
196 
197 	/* skip optional leading space */
198 	skipSpace(pbp, endptr);
199 	if (*pbp == endptr) {
200 		/* nothing left */
201 		return (KMF_ERR_RDN_PARSER);
202 	}
203 
204 	bp = *pbp;
205 
206 	/* quoted? */
207 	if (*bp == C_DOUBLE_QUOTE) {
208 		isQuoted = B_TRUE;
209 		/* skip over it */
210 		bp++;
211 	} else {
212 		isQuoted = B_FALSE;
213 	}
214 
215 	valBufp = valBuf;
216 	vallen = 0;
217 	while (bp < endptr) {
218 		char c = *bp;
219 		if (c == C_BACKSLASH) {
220 			/* escape character */
221 			bp++;
222 			if (bp >= endptr) {
223 				/*
224 				 * escape charater must appear with paired char
225 				 */
226 				*pbp = bp;
227 				return (KMF_ERR_RDN_PARSER);
228 			}
229 		} else if (!isQuoted && SPECIAL_CHAR(c)) {
230 			/* unescaped special and not within quoted value */
231 			break;
232 		} else if (c == C_DOUBLE_QUOTE) {
233 			/* reached unescaped double quote */
234 			break;
235 		}
236 		/* append character */
237 		vallen++;
238 		if (vallen >= valBufSize) {
239 			*pbp = bp;
240 			return (KMF_ERR_RDN_PARSER);
241 		}
242 		*valBufp++ = *bp++;
243 	}
244 
245 	/* stip trailing spaces from unquoted values */
246 	if (!isQuoted) {
247 		if (valBufp > valBuf) {
248 			valBufp--;
249 			while ((valBufp > valBuf) && OPTIONAL_SPACE(*valBufp)) {
250 				valBufp--;
251 			}
252 			valBufp++;
253 		}
254 	}
255 
256 	if (isQuoted) {
257 		/* insist that we stopped on a double quote */
258 		if (*bp != C_DOUBLE_QUOTE) {
259 			*pbp = bp;
260 			return (KMF_ERR_RDN_PARSER);
261 		}
262 		/* skip over the quote and skip optional space */
263 		bp++;
264 		skipSpace(&bp, endptr);
265 	}
266 
267 	*pbp = bp;
268 
269 	if (valBufp == valBuf) {
270 		/* empty value -- not allowed */
271 		return (KMF_ERR_RDN_PARSER);
272 	}
273 
274 	/* null-terminate valBuf -- guaranteed at least one space left */
275 	*valBufp++ = 0;
276 
277 	return (KMF_OK);
278 }
279 
280 static KMF_RETURN
281 CreateRDN(KMF_X509_TYPE_VALUE_PAIR *ava, KMF_X509_RDN *newrdn)
282 {
283 	/* Each RDN has 1 AttrTypeAndValue */
284 	(void) memset(newrdn, 0, sizeof (KMF_X509_RDN));
285 	newrdn->numberOfPairs = 1;
286 	newrdn->AttributeTypeAndValue = ava;
287 
288 	return (KMF_OK);
289 }
290 
291 static KMF_RETURN
292 copy_oid(KMF_OID *dst, KMF_OID *src)
293 {
294 	KMF_RETURN ret = KMF_OK;
295 
296 	if (dst == NULL || src == NULL)
297 		return (KMF_ERR_BAD_PARAMETER);
298 
299 	dst->Data = malloc(src->Length);
300 	if (dst->Data == NULL)
301 		return (KMF_ERR_MEMORY);
302 
303 	dst->Length = src->Length;
304 	(void) memcpy(dst->Data, src->Data, src->Length);
305 
306 	return (ret);
307 }
308 
309 static KMF_RETURN
310 CreateAVA(KMF_OID *oid, int valueType, char *value,
311     KMF_X509_TYPE_VALUE_PAIR **newava)
312 {
313 	int rv = KMF_OK;
314 	KMF_X509_TYPE_VALUE_PAIR *ava = NULL;
315 
316 	*newava = NULL;
317 	ava = (KMF_X509_TYPE_VALUE_PAIR*) malloc(
318 	    sizeof (KMF_X509_TYPE_VALUE_PAIR));
319 	if (ava == NULL) {
320 		return (KMF_ERR_MEMORY);
321 	} else {
322 		(void) memset(ava, 0, sizeof (KMF_X509_TYPE_VALUE_PAIR));
323 		ava->valueType = valueType;
324 		ava->value.Data = malloc(strlen(value));
325 		if (ava->value.Data == NULL) {
326 			free(ava);
327 			return (KMF_ERR_MEMORY);
328 		}
329 		(void) memcpy(ava->value.Data, value, strlen(value));
330 		ava->value.Length = strlen(value);
331 
332 		rv = copy_oid(&ava->type, oid);
333 		if (rv != KMF_OK) {
334 			/* Illegal AVA type */
335 			free(ava->value.Data);
336 			free(ava);
337 			return (rv);
338 		}
339 	}
340 	*newava = ava;
341 
342 	return (rv);
343 }
344 
345 static KMF_RETURN
346 ParseRdnAttribute(char **pbp, char *endptr, boolean_t singleAVA,
347     KMF_X509_TYPE_VALUE_PAIR **a)
348 {
349 	KMF_RETURN rv;
350 	const struct NameToKind *n2k;
351 	int vt;
352 	int valLen;
353 	char *bp;
354 
355 	char tagBuf[32];
356 	char valBuf[384];
357 
358 	rv = scanTag(pbp, endptr, tagBuf, sizeof (tagBuf));
359 	if (rv != KMF_OK)
360 		return (rv);
361 	rv = scanVal(pbp, endptr, valBuf, sizeof (valBuf));
362 	if (rv != KMF_OK)
363 		return (rv);
364 
365 	/* insist that if we haven't finished we've stopped on a separator */
366 	bp = *pbp;
367 	if (bp < endptr) {
368 		if (singleAVA || (*bp != ',' && *bp != ';')) {
369 			*pbp = bp;
370 			return (KMF_ERR_RDN_ATTR);
371 		}
372 		/* ok, skip over separator */
373 		bp++;
374 	}
375 	*pbp = bp;
376 
377 	for (n2k = name2kinds; n2k->name; n2k++) {
378 		if (strcasecmp(n2k->name, tagBuf) == 0) {
379 			valLen = strlen(valBuf);
380 			if (n2k->kind == OID_AVA_COUNTRY_NAME) {
381 				vt = BER_PRINTABLE_STRING;
382 				if (valLen != 2) {
383 					return (KMF_ERR_RDN_ATTR);
384 				}
385 				if (!IsPrintable((unsigned char *) valBuf, 2)) {
386 					return (KMF_ERR_RDN_ATTR);
387 				}
388 			} else if ((n2k->kind == OID_PKCS9_EMAIL_ADDRESS) ||
389 				(n2k->kind == OID_RFC1274_MAIL)) {
390 				vt = BER_IA5STRING;
391 			} else {
392 				/*
393 				 * Hack -- for rationale see X.520
394 				 * DirectoryString defn
395 				 */
396 				if (IsPrintable((unsigned char *)valBuf,
397 				    valLen)) {
398 					vt = BER_PRINTABLE_STRING;
399 				} else if (Is7Bit((unsigned char *)valBuf,
400 				    valLen)) {
401 					vt = BER_T61STRING;
402 				}
403 			}
404 			rv = CreateAVA(n2k->OID,
405 				vt, (char *)valBuf, a);
406 			return (rv);
407 		}
408 	}
409 	/* matched no kind -- invalid tag */
410 	return (KMF_ERR_RDN_ATTR);
411 }
412 
413 static int
414 rdnavcompare(const void *a, const void *b)
415 {
416 	KMF_X509_RDN *r1, *r2;
417 	KMF_X509_TYPE_VALUE_PAIR *av1, *av2;
418 	int i, p1, p2;
419 	const struct NameToKind *n2k;
420 	KMF_OID *oidrec;
421 
422 	r1 = (KMF_X509_RDN *)a;
423 	r2 = (KMF_X509_RDN *)b;
424 
425 	av1 = r1->AttributeTypeAndValue;
426 	av2 = r2->AttributeTypeAndValue;
427 
428 	p1 = p2 = MAXINT;
429 	/*
430 	 * The "Name2Kinds" list is ordered by significance.
431 	 * Compare the "ranking" of each of the OIDs to determine
432 	 * the result.
433 	 */
434 	for (n2k = name2kinds, i = 0;
435 		n2k->name && (p1 == MAXINT || p2 == MAXINT);
436 		n2k++, i++) {
437 		oidrec = n2k->OID;
438 		if (oidrec != NULL) {
439 			if (IsEqualOid(&av1->type, oidrec))
440 				p1 = i;
441 			if (IsEqualOid(&av2->type, oidrec))
442 				p2 = i;
443 		}
444 	}
445 
446 	if (p1 > p2)
447 		return (-1);
448 	else if (p1 < p2)
449 		return (1);
450 	else  /* If equal, treat as if it is less than */
451 		return (1);
452 }
453 
454 KMF_RETURN
455 ParseDistinguishedName(char *buf, int len, KMF_X509_NAME *name)
456 {
457 	KMF_RETURN rv = KMF_OK;
458 	char *bp, *e;
459 	KMF_X509_TYPE_VALUE_PAIR *ava = NULL;
460 	KMF_X509_RDN rdn;
461 
462 	(void) memset(name, 0, sizeof (KMF_X509_NAME));
463 	e = buf + len;
464 	bp = buf;
465 	while (bp < e) {
466 		rv = ParseRdnAttribute(&bp, e, B_FALSE, &ava);
467 		if (rv != KMF_OK) goto loser;
468 		rv = CreateRDN(ava, &rdn);
469 		if (rv != KMF_OK) goto loser;
470 		if (AddRDN(name, &rdn) != KMF_OK) goto loser;
471 		skipSpace(&bp, e);
472 	}
473 
474 	/*
475 	 * Canonicalize the DN by sorting the elements
476 	 * in little-endian order, as per RFC 1485:
477 	 * "The name is presented/input in a little-endian
478 	 * order (most significant component last)."
479 	 */
480 	qsort((void *)name->RelativeDistinguishedName,
481 		name->numberOfRDNs,
482 		sizeof (KMF_X509_RDN),
483 		rdnavcompare);
484 
485 	/* return result */
486 	return (rv);
487 
488 loser:
489 	KMF_FreeDN(name);
490 	return (rv);
491 }
492 
493 static KMF_BOOL
494 IsEqualData(KMF_DATA *d1, KMF_DATA *d2)
495 {
496 	return ((d1->Length == d2->Length) &&
497 	    !memcmp(d1->Data, d2->Data, d1->Length));
498 }
499 
500 /*
501  * Generic routine to compare 2 RDN structures.
502  *
503  * Because the ordering of the AV pairs may not be
504  * the same, we must compare each AV pair individually
505  *
506  * Return 0 if equal, 1 if not.
507  */
508 int
509 KMF_CompareRDNs(KMF_X509_NAME *name1, KMF_X509_NAME *name2)
510 {
511 	int i, j;
512 	boolean_t avfound;
513 	KMF_X509_RDN *r1, *r2;
514 	KMF_X509_TYPE_VALUE_PAIR *av1, *av2;
515 
516 	if (name1 == NULL || name2 == NULL)
517 		return (1);
518 
519 	if (name1->numberOfRDNs != name2->numberOfRDNs)
520 		return (1);
521 
522 	for (i = 0; i < name1->numberOfRDNs; i++) {
523 		r1 = (KMF_X509_RDN *)&name1->RelativeDistinguishedName[i];
524 		av1 = (KMF_X509_TYPE_VALUE_PAIR *)r1->AttributeTypeAndValue;
525 
526 		avfound = FALSE;
527 		for (j = 0; j < name2->numberOfRDNs && !avfound; j++) {
528 			r2 = (KMF_X509_RDN *)
529 				&name2->RelativeDistinguishedName[j];
530 			av2 = (KMF_X509_TYPE_VALUE_PAIR *)
531 				r2->AttributeTypeAndValue;
532 
533 			avfound = (IsEqualOid(&av1->type, &av2->type) &&
534 				    IsEqualData(&av1->value, &av2->value));
535 		}
536 		/*
537 		 * If the current AV from name1 was not found in name2,
538 		 * we are done.
539 		 */
540 		if (!avfound)
541 			return (1);
542 	}
543 
544 	/* If we got this far, it must be a match */
545 	return (0);
546 }
547