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