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