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